From 01dc63f774c931f92f7c6e28c406b0f478ac4a9f Mon Sep 17 00:00:00 2001 From: "elena.parovyshnaya" Date: Thu, 5 Dec 2019 15:56:11 +0300 Subject: [PATCH] # 553807 - revise permission observatory API - Observatory is renamed to Pool - GuardedObservatory is renamed to Observatory - schedule semantics is formed to a separate class - which is, in turn, covered with tests Signed-off-by: elena.parovyshnaya --- .../META-INF/MANIFEST.MF | 2 +- .../permission/BasePermissionObservatory.java | 13 +-- .../base/permission/LimitedPermission.java | 4 +- .../permission/PermissionObservatory.java | 4 +- .../permission/observatory/CheckSchedule.java | 82 ++++++++++++++ .../base/permission/observatory/Guard.java | 8 +- .../observatory/GuardedObservatory.java | 92 ---------------- .../permission/observatory/Observatory.java | 101 ++++++++++++++---- .../base/permission/observatory/Pool.java | 43 ++++++++ .../access/EquinoxPermissionObservatory.java | 5 +- .../observatory/CheckScheduleTest.java | 39 +++++++ .../observatory/ObservatoryTest.java | 14 ++- 12 files changed, 276 insertions(+), 131 deletions(-) create mode 100644 bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckSchedule.java delete mode 100644 bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/GuardedObservatory.java create mode 100644 bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Pool.java create mode 100644 tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckScheduleTest.java diff --git a/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF b/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF index ab19a27af..d74ed2cbe 100644 --- a/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Automatic-Module-Name: org.eclipse.passage.lic.api.base Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.passage.lic.base -Bundle-Version: 0.6.0.qualifier +Bundle-Version: 0.6.1.qualifier Bundle-Name: %Bundle-Name Bundle-Vendor: %Bundle-Vendor Bundle-Copyright: %Bundle-Copyright diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/BasePermissionObservatory.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/BasePermissionObservatory.java index e20436344..8f6aa21f7 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/BasePermissionObservatory.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/BasePermissionObservatory.java @@ -17,24 +17,25 @@ import java.util.stream.StreamSupport; import org.eclipse.passage.lic.api.access.FeaturePermission; -import org.eclipse.passage.lic.internal.base.permission.observatory.GuardedObservatory; +import org.eclipse.passage.lic.internal.base.permission.observatory.CheckSchedule; +import org.eclipse.passage.lic.internal.base.permission.observatory.Observatory; /** *

* Base implementation for {@linkplain PermissionObservatory} component. *

*

- * Covers {@linkplain GuardedObservatory} tuned for permission tracking and is - * it's representation for the domain specific environment. + * Covers {@linkplain Observatory} tuned for permission tracking and is it's + * representation for the domain specific environment. *

* * @since 0.6 */ public final class BasePermissionObservatory implements PermissionObservatory { - private final GuardedObservatory observatory; + private final Observatory observatory; - public BasePermissionObservatory(int seconds, Consumer> farewell) { - this.observatory = new GuardedObservatory(seconds, farewell); + public BasePermissionObservatory(CheckSchedule schedule, Consumer> farewell) { + this.observatory = new Observatory(schedule, farewell); } public void watch(Iterable permissions) { diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/LimitedPermission.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/LimitedPermission.java index 98db467ae..47e6b01de 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/LimitedPermission.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/LimitedPermission.java @@ -15,12 +15,12 @@ import java.util.Date; import org.eclipse.passage.lic.api.access.FeaturePermission; -import org.eclipse.passage.lic.internal.base.permission.observatory.GuardedObservatory; +import org.eclipse.passage.lic.internal.base.permission.observatory.Observatory; import org.eclipse.passage.lic.internal.base.permission.observatory.Limited; /** * Adapter for {@linkplain FeaturePermission} to implement {@linkplain Limited} - * interface to be properly tracked by {@linkplain GuardedObservatory} + * interface to be properly tracked by {@linkplain Observatory} * * @since 0.6 */ diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/PermissionObservatory.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/PermissionObservatory.java index 341a97373..11d7eb7f1 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/PermissionObservatory.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/PermissionObservatory.java @@ -12,10 +12,10 @@ *******************************************************************************/ package org.eclipse.passage.lic.internal.base.permission; -import org.eclipse.passage.lic.internal.base.permission.observatory.GuardedObservatory; +import org.eclipse.passage.lic.internal.base.permission.observatory.Observatory; /** - * Marker interface for a component that holds {@linkplain GuardedObservatory} + * Marker interface for a component that holds {@linkplain Observatory} * for permission tracking. * * @since 0.6 diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckSchedule.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckSchedule.java new file mode 100644 index 000000000..7eb6169a1 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckSchedule.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2019 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.permission.observatory; + +import java.time.temporal.ChronoUnit; + +/** + *

+ * Schedule for observatory checks. + *

+ * + *

+ * Been defined for 5 minutes, causes the observatory to check for new + * expired entries each 5 minutes (or so). + *

+ * + * @since 0.6 + */ +public class CheckSchedule { + + private final int amount; + private final ChronoUnit unit; + + /** + *

+ * Use desired units to define the most suitable check schedule. + *

+ * + *

+ * For example, {@code new CheckSchedule(2, ChronoUnit.DAYS)}, been given to an + * observatory, will cause the closest expiration check to happen only in two + * days since the observatory opening. + *

+ * + * @param amount number of units + * @unit {@linkplain ChronoUnit} constant to measure {@code amount} + * @since 0.6 + */ + public CheckSchedule(int amount, ChronoUnit unit) { + this.amount = amount; + this.unit = unit; + } + + /** + * Default schedules are measured in minutes. + * + * @param minutes number of minutes + * @since 0.6 + */ + public CheckSchedule(int minutes) { + this(minutes, ChronoUnit.MINUTES); + } + + /** + * Default schedule is 10 minutes. + * + * @since 0.6 + */ + public CheckSchedule() { + this(10, ChronoUnit.MINUTES); + } + + /** + * Reports the scheduled period duration in seconds. + * + * @since 0.6 + */ + public long seconds() { + return unit.getDuration().getSeconds() * amount; + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Guard.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Guard.java index 7d47f670c..bd6cdaf56 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Guard.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Guard.java @@ -19,8 +19,8 @@ final class Guard implements Runnable { - private final int period; - private final Observatory pool; + private final long period; + private final Pool pool; private final Consumer> onExpire; private final Executor executor; @@ -28,8 +28,8 @@ final class Guard implements Runnable { * @param onExpire never gets {@code null} or empty set. The callback is * expected to complete shortly. */ - Guard(int seconds, Observatory pool, Consumer> onExpire) { - this.period = seconds; + Guard(CheckSchedule schedule, Pool pool, Consumer> onExpire) { + this.period = schedule.seconds(); this.pool = pool; this.onExpire = onExpire; executor = Executors.newSingleThreadExecutor(); diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/GuardedObservatory.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/GuardedObservatory.java deleted file mode 100644 index 309dbbd3d..000000000 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/GuardedObservatory.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 ArSysOp - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * ArSysOp - initial API and implementation - *******************************************************************************/ -package org.eclipse.passage.lic.internal.base.permission.observatory; - -import java.util.Set; -import java.util.function.Consumer; - -/** - *

- * This observatory keeps an eye on any captured entry, that can say in a moment - * of time whether it is expired already or not yet. - *

- * - *

- * Ones in a period of time the observatory checks all watched entries if they - * are expired already. And for the ones who do, a configured action is called. - *

- * - *

- * An entry can be put under the observatoryControl and removed from it - * preliminary, prior this is done by TTL. - *

- * - * @since 0.6 - */ -public final class GuardedObservatory { - private final Observatory observatory; - private final Guard guard; - - /** - *

- * When the time comes and an entry expires, {@code farewell} action is called - * for it. - *

- * - *

- * The observatory does the brushing checks each {@code schedule} seconds, so an - * entry cannot be processed precisely in a moment it expires, but some time - * like {@code schedule} seconds after. - *

- * - * @param schedule period (in seconds) between checks - * @param farewell handler to be notified of expired entries. Is not intended to - * work long. Never bothered for naught: never gets {@code null} - * or empty set. - * @since 0.6 - */ - public GuardedObservatory(int schedule, Consumer> farewell) { - observatory = new Observatory(); - guard = new Guard(schedule, observatory, farewell); - } - - /** - * The observatory can accept entries to watch and dismiss them after, but - * checks start only after it is open. - * - * @since 0.6 - */ - public void open() { - new Thread(guard).start(); - } - - /** - * Put a {@link Limited} entry under the observatory control. - * - * @since 0.6 - */ - public void watch(T limited) { - observatory.watch(limited); - } - - /** - * Remove a {@link Limited} entry from the observatory control intentionally - * prior it's expiration. - * - * @since 0.6 - */ - public void forget(T limited) { - observatory.forget(limited); - } - -} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Observatory.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Observatory.java index 7d2b76c88..3add49d41 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Observatory.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Observatory.java @@ -12,30 +12,95 @@ *******************************************************************************/ package org.eclipse.passage.lic.internal.base.permission.observatory; -import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; +import java.util.function.Consumer; -final class Observatory { - private final Set watched = new HashSet<>(); +/** + *

+ * This observatory keeps an eye on any captured entry, that can say in a moment + * of time whether it is expired already or not yet. + *

+ * + *

+ * Ones in a period of time the observatory checks all watched entries if they + * are expired already. And for the ones who do, a configured action is called. + * Since this moment an expired entry in not watched anymore. + *

+ * + *

+ * There is no check on an observatory opening. The closest one takes place only + * after a scheduled period of time is over. + *

+ * + *

+ * An entry can be put under the observatory control and removed from it + * preliminary, prior this is done by TTL. + *

+ * + *

+ * To configure an observatory, you need only define + *

    + *
  • how often it will check expiration and
  • + *
  • what will it do for expired entries
  • + *
+ *

+ * + * @since 0.6 + */ +public final class Observatory { + private final Pool pool; + private final Guard guard; - void watch(T target) { - synchronized (watched) { - watched.add(target); - } + /** + *

+ * When the time comes and an entry expires, {@code farewell} action is called + * for it. + *

+ * + *

+ * The observatory does the brushing checks each {@code schedule} seconds, so an + * entry cannot be processed precisely in a moment it expires, but some time + * like {@code schedule} seconds after. + *

+ * + * @param schedule period (in seconds) between checks + * @param farewell handler to be notified of expired entries. Is not intended to + * work long. Never bothered for naught: never gets {@code null} + * or empty set. + * @since 0.6 + */ + public Observatory(CheckSchedule schedule, Consumer> farewell) { + pool = new Pool(); + guard = new Guard(schedule, pool, farewell); } - void forget(T target) { - synchronized (watched) { - watched.remove(target); - } + /** + * The observatory can accept entries to watch and dismiss them after, but + * checks start only after it is open. + * + * @since 0.6 + */ + public void open() { + new Thread(guard).start(); } - Expired popExpired() { - synchronized (watched) { - Set expired = watched.stream().filter(Limited::expired).collect(Collectors.toSet()); - watched.removeAll(expired); - return new Expired(expired); - } + /** + * Put a {@link Limited} entry under the observatory control. + * + * @since 0.6 + */ + public void watch(T limited) { + pool.watch(limited); } + + /** + * Remove a {@link Limited} entry from the observatory control intentionally + * prior it's expiration. + * + * @since 0.6 + */ + public void forget(T limited) { + pool.forget(limited); + } + } diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Pool.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Pool.java new file mode 100644 index 000000000..bc3da3af7 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/permission/observatory/Pool.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2019 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.permission.observatory; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +final class Pool { + private final Set watched = new HashSet<>(); + + void watch(T target) { + synchronized (watched) { + watched.add(target); + } + } + + void forget(T target) { + synchronized (watched) { + watched.remove(target); + } + } + + Expired popExpired() { + synchronized (watched) { + Set expired = watched.stream() // + .filter(Limited::expired)// + .collect(Collectors.toSet()); + watched.removeAll(expired); + return new Expired(expired); + } + } +} diff --git a/bundles/org.eclipse.passage.lic.equinox/src/org/eclipse/passage/lic/internal/equinox/access/EquinoxPermissionObservatory.java b/bundles/org.eclipse.passage.lic.equinox/src/org/eclipse/passage/lic/internal/equinox/access/EquinoxPermissionObservatory.java index 2b4ee8d3d..da6bacd97 100644 --- a/bundles/org.eclipse.passage.lic.equinox/src/org/eclipse/passage/lic/internal/equinox/access/EquinoxPermissionObservatory.java +++ b/bundles/org.eclipse.passage.lic.equinox/src/org/eclipse/passage/lic/internal/equinox/access/EquinoxPermissionObservatory.java @@ -14,6 +14,7 @@ import static org.eclipse.passage.lic.base.LicensingResults.createEvent; +import java.time.temporal.ChronoUnit; import java.util.Map; import java.util.Set; @@ -24,6 +25,7 @@ import org.eclipse.passage.lic.internal.base.permission.BasePermissionObservatory; import org.eclipse.passage.lic.internal.base.permission.LimitedPermission; import org.eclipse.passage.lic.internal.base.permission.PermissionObservatory; +import org.eclipse.passage.lic.internal.base.permission.observatory.CheckSchedule; import org.eclipse.passage.lic.internal.equinox.EquinoxEvents; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -50,7 +52,8 @@ public final class EquinoxPermissionObservatory implements EventHandler, Permiss @Activate public void activate(Map config) { int schedule = (Integer) config.get("observatory.schedule"); //$NON-NLS-1$ - observatory = new BasePermissionObservatory(schedule, this::fireExpiration); + observatory = new BasePermissionObservatory(new CheckSchedule(schedule, ChronoUnit.SECONDS), + this::fireExpiration); observatory.open(); } diff --git a/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckScheduleTest.java b/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckScheduleTest.java new file mode 100644 index 000000000..5bea00077 --- /dev/null +++ b/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/CheckScheduleTest.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2019 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.permission.observatory; + +import static org.junit.Assert.assertEquals; + +import java.time.temporal.ChronoUnit; + +import org.junit.Test; + +@SuppressWarnings("restriction") +public class CheckScheduleTest { + + @Test + public void testDefaultSchedule() { + assertEquals(10 * 60, new CheckSchedule().seconds()); + } + + @Test + public void testMinutesSchedule() { + assertEquals(5 * 60, new CheckSchedule(5).seconds()); + } + + @Test + public void testAlternativechedule() { + assertEquals(3 * 24 * 60 * 60, new CheckSchedule(3, ChronoUnit.DAYS).seconds()); + } + +} diff --git a/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/ObservatoryTest.java b/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/ObservatoryTest.java index c7209faeb..5365759b4 100644 --- a/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/ObservatoryTest.java +++ b/tests/org.eclipse.passage.lic.base.tests/src/org/eclipse/passage/lic/internal/base/permission/observatory/ObservatoryTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.fail; +import java.time.temporal.ChronoUnit; import java.util.Set; import java.util.function.Consumer; @@ -21,8 +22,8 @@ import org.junit.Test; /** - * Test suite for {@linkplain GuardedObservatory}, which mostly intended to keep - * an eye on {@linkplain Limited} instances, given under it's watch and fire + * Test suite for {@linkplain Observatory}, which mostly intended to keep an eye + * on {@linkplain Limited} instances, given under it's watch and fire * {@code expired} action when a {@linkplain Limited} expires. * * @since 0.6 @@ -46,7 +47,7 @@ public class ObservatoryTest { @Test public void twoSeconds() { Countdown countdown = new Countdown(2); - GuardedObservatory observatory = new GuardedObservatory(1, countdown); + Observatory observatory = new Observatory(buzy(), countdown); observatory.open(); observatory.watch(new TimeLimited(1)); observatory.watch(new TimeLimited(1)); @@ -60,7 +61,7 @@ public void twoSeconds() { @Test public void forget() { Countdown countdown = new Countdown(1); // watch two entries, but then forget one of them - GuardedObservatory observatory = new GuardedObservatory(1, countdown); + Observatory observatory = new Observatory(buzy(), countdown); observatory.open(); observatory.watch(new TimeLimited(1)); TimeLimited toBeForgotten = new TimeLimited(1); @@ -86,7 +87,7 @@ public void guardIsHighlyReliable() { private void testGuardReliability(Consumer> sabotage) { Countdown countdown = new Countdown(4); - GuardedObservatory observatory = new GuardedObservatory(1, countdown.andThen(sabotage)); + Observatory observatory = new Observatory(buzy(), countdown.andThen(sabotage)); observatory.open(); observatory.watch(new TimeLimited(1)); observatory.watch(new TimeLimited(1)); @@ -118,6 +119,9 @@ private void assertCountdownIsComplete(Countdown countdown, int delay) { fail("Some expired Limited are still active."); //$NON-NLS-1$ } + private CheckSchedule buzy() { + return new CheckSchedule(1, ChronoUnit.SECONDS); + } // multithreaded usage // failing in Limited::expire implementation // negative schedule