You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/12/03 12:48:44 UTC
[isis] branch master updated: ISIS-2464: prepare removal of Clock
(in favor of VirtualClock)
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 72eddd8 ISIS-2464: prepare removal of Clock (in favor of VirtualClock)
72eddd8 is described below
commit 72eddd8b171eb14a1004b7631dfd7364312179b8
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Dec 3 13:48:24 2020 +0100
ISIS-2464: prepare removal of Clock (in favor of VirtualClock)
---
.../java/org/apache/isis/applib/clock/Clock.java | 2 +-
.../org/apache/isis/applib/clock/VirtualClock.java | 118 +++++++++++++++++++++
.../isis/applib/services/user/UserMemento.java | 51 ++++-----
.../isis/commons/internal/collections/_Lists.java | 6 ++
.../runtimeservices/user/UserServiceDefault.java | 2 +-
.../authentication/AuthenticationSession.java | 18 +++-
.../AuthenticationSessionAbstract.java | 80 +++++++-------
.../authentication/health/HealthAuthSession.java | 3 +-
.../authentication/standard/SimpleSession.java | 3 +-
9 files changed, 209 insertions(+), 74 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/clock/Clock.java b/api/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
index 37ec6a4..e29f459 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/clock/Clock.java
@@ -36,7 +36,7 @@ import lombok.val;
*
* <p>
* The clock is used primarily by the temporal value classes, and is accessed by
- * the NOF as a singleton. The actual implementation used can be configured at
+ * the framework as a singleton. The actual implementation used can be configured at
* startup, but once specified the clock instance cannot be changed.
*
* <p>
diff --git a/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java b/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java
new file mode 100644
index 0000000..7635625
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.applib.clock;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import org.apache.isis.applib.services.iactn.Interaction;
+
+import lombok.NonNull;
+
+/**
+ * Works in connection with {@link InteractionFactory}, such that it allows an {@link Interaction}
+ * to run with its own simulated (or actual) time.
+ * <p>
+ * Relates to {@link VirtualContext}
+ *
+ * @since 2.0
+ *
+ */
+@FunctionalInterface
+public interface VirtualClock extends Serializable {
+
+ // -- INTERFACE
+
+ /**
+ * Returns the (virtual) time as an {@link Instant}.
+ *
+ * @apiNote This is a universal time difference, that does not depend on
+ * where you are (eg. your current timezone), just on when you are.
+ *
+ * @see {@link Instant}
+ */
+ Instant now();
+
+ // -- FACTORIES
+
+ static VirtualClock system() {
+ return Instant::now;
+ }
+
+ // -- UTILITY
+
+ /**
+ * Returns the (virtual) time as the number of milliseconds since the epoch start.
+ *
+ * @apiNote This is a universal time difference, that does not depend on
+ * where you are (eg. your current timezone), just on when you are.
+ *
+ * @see {@link Instant}
+ */
+ default long getEpochMillis() {
+ return now().toEpochMilli();
+ }
+
+ /**
+ * Returns the (virtual) time as {@link LocalDate}, using the {@link ZoneId} timezone.
+ * @param zoneId - the time-zone, which may be an offset, not null
+ */
+ default LocalDate getTimeAsLocalDate(final @NonNull ZoneId zoneId) {
+ return getTimeAsLocalDateTime(zoneId).toLocalDate();
+ }
+
+ /**
+ * Returns the (virtual) time as {@link LocalDateTime}, using the {@link ZoneId} timezone.
+ * @param zoneId - the time-zone, which may be an offset, not null
+ */
+ default LocalDateTime getTimeAsLocalDateTime(final @NonNull ZoneId zoneId) {
+ return LocalDateTime.ofInstant(now(), zoneId);
+ }
+
+ /**
+ * Returns the (virtual) time as {@link OffsetDateTime}, using the {@link ZoneId} timezone.
+ * @param zoneId - the time-zone, which may be an offset, not null
+ */
+ default OffsetDateTime getTimeAsOffsetDateTime(final @NonNull ZoneId zoneId) {
+ return OffsetDateTime.ofInstant(now(), zoneId);
+ }
+
+ default Timestamp getTimeAsJavaSqlTimestamp() {
+ return new java.sql.Timestamp(getEpochMillis());
+ }
+
+ /**
+ * Returns the time as a Joda {@link DateTime},
+ * using the {@link ZoneId#systemDefault() system default} timezone.
+ * @deprecated please migrate to java.time.* TODO provide a compatibility layer?
+ */
+ @Deprecated
+ default DateTime getTimeAsJodaDateTime() {
+ final ZoneId zoneId = ZoneId.systemDefault();
+ return new DateTime(getEpochMillis(), DateTimeZone.forID(zoneId.getId()));
+ }
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
index d90ea91..607fa44 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
@@ -30,7 +30,7 @@ import lombok.Getter;
import lombok.experimental.UtilityClass;
/**
- * Details, obtained from the container, about the user and his roles.
+ * Details about a user and his roles.
* Read-only.
*/
// tag::refguide[]
@@ -59,7 +59,7 @@ public final class UserMemento {
throw new IllegalArgumentException("Name not specified");
}
this.name = name;
- this.roles.addAll(roles);
+ this.roles = Collections.unmodifiableList(new ArrayList<RoleMemento>(roles));
}
public String title() {
@@ -79,10 +79,10 @@ public final class UserMemento {
* The roles associated with this user.
*/
@MemberOrder(sequence = "1.1")
- private final List<RoleMemento> roles = new ArrayList<RoleMemento>();
+ private final List<RoleMemento> roles;
// tag::refguide[]
public List<RoleMemento> getRoles() {
- return Collections.unmodifiableList(roles);
+ return roles;
}
// end::refguide[]
/**
@@ -99,27 +99,28 @@ public final class UserMemento {
return name.equals(userName);
}
- /**
- * Determines if the user fulfills the specified role.
- *
- * @param role the role to search for, regular expressions are allowed
- */
- public boolean hasRole(final RoleMemento role) {
- return hasRole(role.getName());
- }
-
- /**
- * Determines if the user fulfills the specified role. Roles are compared
- * lexically by role name.
- */
- public boolean hasRole(final String roleName) {
- for (final RoleMemento role : roles) {
- if (role.getName().matches(roleName)) {
- return true;
- }
- }
- return false;
- }
+//XXX implemented as regex match, java-doc is not specific about what these methods actually do; so if in doubt, rather remove
+// /**
+// * Determines if the user fulfills the specified role.
+// *
+// * @param role the role to search for, regular expressions are allowed
+// */
+// public boolean hasRole(final RoleMemento role) {
+// return hasRole(role.getName());
+// }
+//
+// /**
+// * Determines if the user fulfills the specified role. Roles are compared
+// * lexically by role name.
+// */
+// public boolean hasRole(final String roleName) {
+// for (final RoleMemento role : roles) {
+// if (role.getName().matches(roleName)) {
+// return true;
+// }
+// }
+// return false;
+// }
@Override
public String toString() {
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
index 0f5d8f0..3d08f03 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
@@ -117,6 +117,10 @@ public final class _Lists {
public static <T> ArrayList<T> newArrayList() {
return new ArrayList<T>();
}
+
+ public static <T> ArrayList<T> newArrayList(final int initialSize) {
+ return new ArrayList<T>(initialSize);
+ }
public static <T> ArrayList<T> newArrayList(@Nullable Collection<T> collection) {
if(collection==null) {
@@ -196,4 +200,6 @@ public final class _Lists {
return toUnmodifiable(ArrayList::new);
}
+
+
}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
index f9fa51d..38565b8 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
@@ -97,7 +97,7 @@ public class UserServiceDefault implements UserService {
} else {
return isisInteractionTracker.currentAuthenticationSession()
- .map(AuthenticationSession::createUserMemento)
+ .map(AuthenticationSession::getUser)
.orElseThrow(()->_Exceptions.illegalState("need an AuthenticationSession to create a UserMemento"));
}
}
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSession.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSession.java
index afdb374..dc1a174 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSession.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSession.java
@@ -21,6 +21,8 @@ package org.apache.isis.core.security.authentication;
import java.io.Serializable;
+import org.apache.isis.applib.clock.VirtualClock;
+import org.apache.isis.applib.services.iactn.Interaction;
import org.apache.isis.applib.services.user.UserMemento;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
@@ -75,7 +77,21 @@ public interface AuthenticationSession extends Serializable {
*/
MessageBroker getMessageBroker();
- UserMemento createUserMemento();
+ /**
+ * The (programmatically) simulated (or actual) user, belonging to this session.
+ *
+ * @apiNote immutable, allows an {@link Interaction} to (logically) run with its
+ * own simulated (or actual) user
+ */
+ UserMemento getUser();
+
+ /**
+ * The (programmatically) simulated (or actual) clock, belonging to this session.
+ *
+ * @apiNote immutable, allows an {@link Interaction} to (logically) run with its
+ * own simulated (or actual) clock
+ */
+ VirtualClock getClock();
/**
* To support external security mechanisms such as keycloak, where the validity of the session is defined by
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSessionAbstract.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSessionAbstract.java
index e897a96..ad3ad69 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSessionAbstract.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationSessionAbstract.java
@@ -21,53 +21,64 @@ package org.apache.isis.core.security.authentication;
import java.io.Serializable;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.stream.Stream;
+import org.apache.isis.applib.clock.VirtualClock;
import org.apache.isis.applib.services.user.RoleMemento;
import org.apache.isis.applib.services.user.UserMemento;
import org.apache.isis.applib.util.ToString;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.collections._Sets;
import lombok.Getter;
import lombok.NonNull;
+import lombok.val;
-public abstract class AuthenticationSessionAbstract implements AuthenticationSession, Serializable {
+public abstract class AuthenticationSessionAbstract
+implements AuthenticationSession, Serializable {
private static final long serialVersionUID = 1L;
// -- Constructor, fields
- @Getter private final String userName;
- private final Set<String> roles = _Sets.newHashSet();
- private transient Can<String> rolesImmutable;
+ @Getter(onMethod_ = {@Override}) @NonNull
+ private final VirtualClock clock;
+
+ @Getter(onMethod_ = {@Override}) @NonNull
+ private final String userName;
+
+ @Getter(onMethod_ = {@Override}) @NonNull
+ private final Can<String> roles;
+
+ @Getter(onMethod_ = {@Override}) @NonNull
private final String validationCode;
- private final Map<String, Object> attributeByName = new HashMap<String, Object>();
-
+ @Getter(onMethod_ = {@Override}) @NonNull
private final MessageBroker messageBroker;
+
+ private final Map<String, Object> attributeByName = new HashMap<String, Object>();
+
public AuthenticationSessionAbstract(
@NonNull final String name,
@NonNull final String validationCode) {
- this(name, Stream.empty(), validationCode);
+ this(VirtualClock.system(), name, Stream.empty(), validationCode);
}
public AuthenticationSessionAbstract(
+ @NonNull final VirtualClock clock,
@NonNull final String userName,
@NonNull final Stream<String> roles,
@NonNull final String validationCode) {
- this.userName = userName;
- roles
- .filter(_Strings::isNotEmpty)
- .forEach(this.roles::add);
+ this.clock = clock;
+ this.userName = userName;
+ this.roles = Can.ofStream(roles
+ .filter(_Strings::isNotEmpty)
+ .distinct());
this.validationCode = validationCode;
this.messageBroker = new MessageBroker();
@@ -84,27 +95,10 @@ public abstract class AuthenticationSessionAbstract implements AuthenticationSes
// -- Roles
@Override
- public Can<String> getRoles() {
- if(rolesImmutable==null) {
- // lazy in support of serialization,
- // its also (effectively) thread-safe without doing any synchronization here
- rolesImmutable = Can.ofCollection(roles);
- }
- return rolesImmutable;
- }
-
- @Override
public boolean hasRole(String role) {
return roles.contains(role);
}
- // -- Validation Code
-
- @Override
- public String getValidationCode() {
- return validationCode;
- }
-
// -- Attributes
@Override
@@ -117,22 +111,20 @@ public abstract class AuthenticationSessionAbstract implements AuthenticationSes
attributeByName.put(attributeName, attribute);
}
- // -- MessageBroker
-
- @Override
- public MessageBroker getMessageBroker() {
- return messageBroker;
- }
-
- // -- createUserMemento
+ // -- UserMemento
+ private transient UserMemento user;
+
@Override
- public UserMemento createUserMemento() {
- final List<RoleMemento> roles = _Lists.newArrayList();
- for (final String roleName : this.roles) {
- roles.add(new RoleMemento(roleName));
+ public UserMemento getUser() {
+ if(user==null) {
+ val roleMementos = _Lists.<RoleMemento>newArrayList(roles.size());
+ for (final String roleName : roles) {
+ roleMementos.add(new RoleMemento(roleName));
+ }
+ user = new UserMemento(getUserName(), roleMementos);
}
- return new UserMemento(getUserName(), roles);
+ return user;
}
// -- toString
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/health/HealthAuthSession.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/health/HealthAuthSession.java
index 6d8c257..854b2ed 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/authentication/health/HealthAuthSession.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/health/HealthAuthSession.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.security.authentication.health;
import java.util.stream.Stream;
+import org.apache.isis.applib.clock.VirtualClock;
import org.apache.isis.core.security.authentication.AuthenticationSessionAbstract;
public class HealthAuthSession extends AuthenticationSessionAbstract {
@@ -32,7 +33,7 @@ public class HealthAuthSession extends AuthenticationSessionAbstract {
private static final String CODE = "";
public HealthAuthSession() {
- super(USER_NAME, Stream.of(ROLE), CODE);
+ super(VirtualClock.system(), USER_NAME, Stream.of(ROLE), CODE);
}
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/standard/SimpleSession.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/standard/SimpleSession.java
index 6a1dcb9..c40b310 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/authentication/standard/SimpleSession.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/standard/SimpleSession.java
@@ -22,6 +22,7 @@ package org.apache.isis.core.security.authentication.standard;
import java.util.Collection;
import java.util.stream.Stream;
+import org.apache.isis.applib.clock.VirtualClock;
import org.apache.isis.core.security.authentication.AuthenticationSessionAbstract;
import static org.apache.isis.commons.internal.base._NullSafe.stream;
@@ -46,7 +47,7 @@ public class SimpleSession extends AuthenticationSessionAbstract {
}
public SimpleSession(final String userName, final Stream<String> roles, final String code) {
- super(userName, roles, code);
+ super(VirtualClock.system(), userName, roles, code);
}
@Getter @Setter