1 package org
.argeo
.util
.register
;
3 import java
.util
.Arrays
;
4 import java
.util
.Collections
;
5 import java
.util
.HashMap
;
6 import java
.util
.HashSet
;
7 import java
.util
.IdentityHashMap
;
10 import java
.util
.concurrent
.CompletableFuture
;
11 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
12 import java
.util
.function
.Consumer
;
13 import java
.util
.function
.Predicate
;
15 public class Component
{
16 private final static AtomicBoolean started
= new AtomicBoolean(false);
17 private final static IdentityHashMap
<Object
, Component
> components
= new IdentityHashMap
<>();
19 private static synchronized void registerComponent(Component component
) {
20 if (started
.get()) // TODO make it rellay dynamic
21 throw new IllegalStateException("Already activated");
22 if (components
.containsKey(component
.instance
))
23 throw new IllegalArgumentException("Already registered as component");
24 components
.put(component
.instance
, component
);
27 static synchronized Component
get(Object instance
) {
28 if (!components
.containsKey(instance
))
29 throw new IllegalArgumentException("Not registered as component");
30 return components
.get(instance
);
33 public synchronized static void activate() {
35 throw new IllegalStateException("Already activated");
36 for (Component component
: components
.values()) {
37 component
.activationStarted
.complete(null);
42 public synchronized static void deactivate() {
44 throw new IllegalStateException("Not activated");
45 for (Component component
: components
.values()) {
46 component
.deactivationStarted
.complete(null);
51 private final Object instance
;
53 private Runnable init
;
54 private Runnable close
;
56 private final Map
<Class
<?
>, PublishedType
<?
>> types
;
57 private final Set
<Dependency
<?
>> dependencies
;
59 private CompletableFuture
<Void
> activationStarted
= new CompletableFuture
<Void
>();
60 private CompletableFuture
<Void
> activated
= new CompletableFuture
<Void
>();
62 private CompletableFuture
<Void
> deactivationStarted
= new CompletableFuture
<Void
>();
63 private CompletableFuture
<Void
> deactivated
= new CompletableFuture
<Void
>();
65 private Set
<Dependency
<?
>> dependants
= new HashSet
<>();
67 Component(Object instance
, Runnable init
, Runnable close
, Set
<Dependency
<?
>> dependencies
, Set
<Class
<?
>> classes
) {
68 assert instance
!= null;
71 assert dependencies
!= null;
72 assert classes
!= null;
74 this.instance
= instance
;
79 Map
<Class
<?
>, PublishedType
<?
>> types
= new HashMap
<>(classes
.size());
80 for (Class
<?
> clss
: classes
) {
81 if (!clss
.isAssignableFrom(instance
.getClass()))
82 throw new IllegalArgumentException(
83 "Type " + clss
.getName() + " is not compatible with " + instance
.getClass().getName());
84 types
.put(clss
, new PublishedType
<>(clss
));
86 this.types
= Collections
.unmodifiableMap(types
);
89 this.dependencies
= Collections
.unmodifiableSet(new HashSet
<>(dependencies
));
90 for (Dependency
<?
> dependency
: this.dependencies
) {
91 dependency
.setDependantComponent(this);
95 activated
= activationStarted
//
96 .thenCompose(this::dependenciesActivated
) //
99 // future deactivation
100 deactivated
= deactivationStarted
//
101 .thenCompose(this::dependantsDeactivated
) //
102 .thenRun(this.close
);
104 registerComponent(this);
107 CompletableFuture
<Void
> dependenciesActivated(Void v
) {
108 Set
<CompletableFuture
<?
>> constraints
= new HashSet
<>(this.dependencies
.size());
109 for (Dependency
<?
> dependency
: this.dependencies
) {
110 CompletableFuture
<Void
> dependencyActivated
= dependency
.getPublisher().activated
//
111 .thenCompose(dependency
::set
);
112 constraints
.add(dependencyActivated
);
114 return CompletableFuture
.allOf(constraints
.toArray(new CompletableFuture
[constraints
.size()]));
117 CompletableFuture
<Void
> dependantsDeactivated(Void v
) {
118 Set
<CompletableFuture
<?
>> constraints
= new HashSet
<>(this.dependants
.size());
119 for (Dependency
<?
> dependant
: this.dependants
) {
120 CompletableFuture
<Void
> dependantDeactivated
= dependant
.getDependantComponent().deactivated
//
121 .thenCompose(dependant
::unset
);
122 constraints
.add(dependantDeactivated
);
124 CompletableFuture
<Void
> dependantsDeactivated
= CompletableFuture
125 .allOf(constraints
.toArray(new CompletableFuture
[constraints
.size()]));
126 return dependantsDeactivated
;
130 void addDependant(Dependency
<?
> dependant
) {
131 dependants
.add(dependant
);
134 public <T
> PublishedType
<T
> getType(Class
<T
> clss
) {
135 if (!types
.containsKey(clss
))
136 throw new IllegalArgumentException(clss
.getName() + " is not a type published by this component");
137 return (PublishedType
<T
>) types
.get(clss
);
140 public class PublishedType
<T
> {
141 private Class
<T
> clss
;
143 private CompletableFuture
<T
> value
;
145 public PublishedType(Class
<T
> clss
) {
148 value
= CompletableFuture
.completedFuture((T
) Component
.this.instance
);
151 Component
getPublisher() {
152 return Component
.this;
160 public static class Builder
<I
> {
161 private final I instance
;
163 private Runnable init
;
164 private Runnable close
;
166 private Set
<Dependency
<?
>> dependencies
= new HashSet
<>();
167 private Set
<Class
<?
>> types
= new HashSet
<>();
169 public Builder(I instance
) {
170 this.instance
= instance
;
173 public Component
build() {
174 if (types
.isEmpty()) {
175 types
.add(instance
.getClass());
185 Component component
= new Component(instance
, init
, close
, dependencies
, types
);
186 for (Dependency
<?
> dependency
: dependencies
) {
187 dependency
.type
.getPublisher().addDependant(dependency
);
192 public Builder
<I
> addType(Class
<?
>... classes
) {
193 types
.addAll(Arrays
.asList(classes
));
197 public Builder
<I
> addInit(Runnable init
) {
198 if (this.init
!= null)
199 throw new IllegalArgumentException("init method is already set");
204 public Builder
<I
> addClose(Runnable close
) {
205 if (this.close
!= null)
206 throw new IllegalArgumentException("close method is already set");
211 public <D
> Builder
<I
> addDependency(PublishedType
<D
> type
, Predicate
<?
> filter
, Consumer
<D
> set
,
213 dependencies
.add(new Dependency
<D
>(type
, filter
, set
, unset
));
223 static class Dependency
<D
> {
224 private PublishedType
<D
> type
;
225 private Predicate
<?
> filter
;
226 private Consumer
<D
> set
;
227 private Consumer
<D
> unset
;
230 Component dependantComponent
;
231 CompletableFuture
<Void
> setStage
;
232 CompletableFuture
<Void
> unsetStage
;
234 public Dependency(PublishedType
<D
> types
, Predicate
<?
> filter
, Consumer
<D
> set
, Consumer
<D
> unset
) {
237 this.filter
= filter
;
239 this.unset
= unset
!= null ? unset
: (v
) -> set
.accept(null);
243 void setDependantComponent(Component component
) {
244 this.dependantComponent
= component
;
247 Component
getPublisher() {
248 return type
.getPublisher();
251 Component
getDependantComponent() {
252 return dependantComponent
;
255 CompletableFuture
<Void
> set(Void v
) {
256 return type
.value
.thenAccept(set
);
259 CompletableFuture
<Void
> unset(Void v
) {
260 return type
.value
.thenAccept(unset
);