From: Mathieu Baudier Date: Tue, 4 Jan 2022 04:54:58 +0000 (+0100) Subject: Refine first component implementation X-Git-Tag: argeo-commons-2.3.5~108 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=df60db390a109c60ac76980ab918f4bdbec1364b;p=lgpl%2Fargeo-commons.git Refine first component implementation --- diff --git a/org.argeo.enterprise/src/org/argeo/util/register/Component.java b/org.argeo.enterprise/src/org/argeo/util/register/Component.java index e3f3c2f72..dccb77d38 100644 --- a/org.argeo.enterprise/src/org/argeo/util/register/Component.java +++ b/org.argeo.enterprise/src/org/argeo/util/register/Component.java @@ -1,70 +1,36 @@ package org.argeo.util.register; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; -import java.util.function.Predicate; - -public class Component { - private final static AtomicBoolean started = new AtomicBoolean(false); - private final static IdentityHashMap components = new IdentityHashMap<>(); - - private static synchronized void registerComponent(Component component) { - if (started.get()) // TODO make it rellay dynamic - throw new IllegalStateException("Already activated"); - if (components.containsKey(component.instance)) - throw new IllegalArgumentException("Already registered as component"); - components.put(component.instance, component); - } - - static synchronized Component get(Object instance) { - if (!components.containsKey(instance)) - throw new IllegalArgumentException("Not registered as component"); - return components.get(instance); - } - public synchronized static void activate() { - if (started.get()) - throw new IllegalStateException("Already activated"); - for (Component component : components.values()) { - component.activationStarted.complete(null); - } - started.set(true); - } - - public synchronized static void deactivate() { - if (!started.get()) - throw new IllegalStateException("Not activated"); - for (Component component : components.values()) { - component.deactivationStarted.complete(null); - } - started.set(false); - } +/** + * A wrapper for an object, whose dependencies and life cycle can be managed. + */ +public class Component { - private final Object instance; + private final I instance; - private Runnable init; - private Runnable close; + private final Runnable init; + private final Runnable close; - private final Map, PublishedType> types; + private final Map, PublishedType> types; private final Set> dependencies; - private CompletableFuture activationStarted = new CompletableFuture(); - private CompletableFuture activated = new CompletableFuture(); + private CompletableFuture activationStarted = null; + private CompletableFuture activated = null; - private CompletableFuture deactivationStarted = new CompletableFuture(); - private CompletableFuture deactivated = new CompletableFuture(); + private CompletableFuture deactivationStarted = null; + private CompletableFuture deactivated = null; private Set> dependants = new HashSet<>(); - Component(Object instance, Runnable init, Runnable close, Set> dependencies, Set> classes) { + Component(I instance, Runnable init, Runnable close, Set> dependencies, + Set> classes) { assert instance != null; assert init != null; assert close != null; @@ -76,12 +42,12 @@ public class Component { this.close = close; // types - Map, PublishedType> types = new HashMap<>(classes.size()); - for (Class clss : classes) { + Map, PublishedType> types = new HashMap<>(classes.size()); + for (Class clss : classes) { if (!clss.isAssignableFrom(instance.getClass())) throw new IllegalArgumentException( "Type " + clss.getName() + " is not compatible with " + instance.getClass().getName()); - types.put(clss, new PublishedType<>(clss)); + types.put(clss, new PublishedType<>(this, clss)); } this.types = Collections.unmodifiableMap(types); @@ -91,17 +57,50 @@ public class Component { dependency.setDependantComponent(this); } - // future activation + // deactivated by default + deactivated = CompletableFuture.completedFuture(null); + deactivationStarted = CompletableFuture.completedFuture(null); + + // TODO check whether context is active, so that we start right away + prepareNextActivation(); + + StaticRegister.registerComponent(this); + } + + private void prepareNextActivation() { + activationStarted = new CompletableFuture(); activated = activationStarted // - .thenCompose(this::dependenciesActivated) // - .thenRun(this.init); + .thenComposeAsync(this::dependenciesActivated) // + .thenRun(this.init) // + .thenRun(() -> prepareNextDeactivation()); + } - // future deactivation + private void prepareNextDeactivation() { + deactivationStarted = new CompletableFuture(); deactivated = deactivationStarted // - .thenCompose(this::dependantsDeactivated) // - .thenRun(this.close); + .thenComposeAsync(this::dependantsDeactivated) // + .thenRun(this.close) // + .thenRun(() -> prepareNextActivation()); + } - registerComponent(this); + public CompletableFuture getActivated() { + return activated; + } + + public CompletableFuture getDeactivated() { + return deactivated; + } + + void startActivating() { + if (activated.isDone() || activationStarted.isDone()) + return; + activationStarted.complete(null); + } + + void startDeactivating() { + if (deactivated.isDone() || deactivationStarted.isDone()) + return; + deactivationStarted.complete(null); } CompletableFuture dependenciesActivated(Void v) { @@ -131,25 +130,31 @@ public class Component { dependants.add(dependant); } - public PublishedType getType(Class clss) { + I getInstance() { + return instance; + } + + @SuppressWarnings("unchecked") + PublishedType getType(Class clss) { if (!types.containsKey(clss)) throw new IllegalArgumentException(clss.getName() + " is not a type published by this component"); return (PublishedType) types.get(clss); } - public class PublishedType { + public static class PublishedType { + private Component component; private Class clss; private CompletableFuture value; - public PublishedType(Class clss) { + public PublishedType(Component component, Class clss) { this.clss = clss; - - value = CompletableFuture.completedFuture((T) Component.this.instance); + this.component = component; + value = CompletableFuture.completedFuture((T) component.instance); } - Component getPublisher() { - return Component.this; + Component getPublisher() { + return component; } Class getType() { @@ -164,15 +169,16 @@ public class Component { private Runnable close; private Set> dependencies = new HashSet<>(); - private Set> types = new HashSet<>(); + private Set> types = new HashSet<>(); public Builder(I instance) { this.instance = instance; } - public Component build() { + public Component build() { + // default values if (types.isEmpty()) { - types.add(instance.getClass()); + types.add(getInstanceClass()); } if (init == null) @@ -182,15 +188,16 @@ public class Component { close = () -> { }; - Component component = new Component(instance, init, close, dependencies, types); + // instantiation + Component component = new Component(instance, init, close, dependencies, types); for (Dependency dependency : dependencies) { dependency.type.getPublisher().addDependant(dependency); } return component; } - public Builder addType(Class... classes) { - types.addAll(Arrays.asList(classes)); + public Builder addType(Class clss) { + types.add(clss); return this; } @@ -208,9 +215,8 @@ public class Component { return this; } - public Builder addDependency(PublishedType type, Predicate filter, Consumer set, - Consumer unset) { - dependencies.add(new Dependency(type, filter, set, unset)); + public Builder addDependency(PublishedType type, Consumer set, Consumer unset) { + dependencies.add(new Dependency(type, set, unset)); return this; } @@ -218,37 +224,40 @@ public class Component { return instance; } + @SuppressWarnings("unchecked") + private Class getInstanceClass() { + return (Class) instance.getClass(); + } + } static class Dependency { private PublishedType type; - private Predicate filter; private Consumer set; private Consumer unset; // live - Component dependantComponent; + Component dependantComponent; CompletableFuture setStage; CompletableFuture unsetStage; - public Dependency(PublishedType types, Predicate filter, Consumer set, Consumer unset) { + public Dependency(PublishedType types, Consumer set, Consumer unset) { super(); this.type = types; - this.filter = filter; this.set = set; this.unset = unset != null ? unset : (v) -> set.accept(null); } // live - void setDependantComponent(Component component) { + void setDependantComponent(Component component) { this.dependantComponent = component; } - Component getPublisher() { + Component getPublisher() { return type.getPublisher(); } - Component getDependantComponent() { + Component getDependantComponent() { return dependantComponent; } @@ -262,4 +271,3 @@ public class Component { } } - diff --git a/org.argeo.enterprise/src/org/argeo/util/register/StaticRegister.java b/org.argeo.enterprise/src/org/argeo/util/register/StaticRegister.java new file mode 100644 index 000000000..0c55bddb2 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/util/register/StaticRegister.java @@ -0,0 +1,65 @@ +package org.argeo.util.register; + +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +/** A minimal component register. */ +public class StaticRegister { + private final static AtomicBoolean started = new AtomicBoolean(false); + private final static IdentityHashMap> components = new IdentityHashMap<>(); + + static synchronized void registerComponent(Component component) { + if (started.get()) // TODO make it really dynamic + throw new IllegalStateException("Already activated"); + if (components.containsKey(component.getInstance())) + throw new IllegalArgumentException("Already registered as component"); + components.put(component.getInstance(), component); + } + + static synchronized Component get(Object instance) { + if (!components.containsKey(instance)) + throw new IllegalArgumentException("Not registered as component"); + return components.get(instance); + } + + synchronized static void activate() { + if (started.get()) + throw new IllegalStateException("Already activated"); + Set> constraints = new HashSet<>(); + for (Component component : components.values()) { + component.startActivating(); + constraints.add(component.getActivated()); + } + + // wait + try { + CompletableFuture.allOf(constraints.toArray(new CompletableFuture[0])).thenRun(() -> started.set(true)) + .get(); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + synchronized static void deactivate() { + if (!started.get()) + throw new IllegalStateException("Not activated"); + Set> constraints = new HashSet<>(); + for (Component component : components.values()) { + component.startDeactivating(); + constraints.add(component.getDeactivated()); + } + + // wait + try { + CompletableFuture.allOf(constraints.toArray(new CompletableFuture[0])).thenRun(() -> started.set(false)) + .get(); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + } + +}