1 package org
.argeo
.util
.register
;
3 import java
.util
.Collections
;
4 import java
.util
.HashMap
;
5 import java
.util
.HashSet
;
8 import java
.util
.concurrent
.CompletableFuture
;
9 import java
.util
.concurrent
.CompletionStage
;
10 import java
.util
.function
.Consumer
;
11 import java
.util
.function
.Supplier
;
14 * A wrapper for an object, whose dependencies and life cycle can be managed.
16 public class Component
<I
> implements Supplier
<I
>, Comparable
<Component
<?
>> {
18 private final I instance
;
20 private final Runnable init
;
21 private final Runnable close
;
23 private final Map
<Class
<?
super I
>, PublishedType
<?
super I
>> types
;
24 private final Set
<Dependency
<?
>> dependencies
;
25 private final Map
<String
, Object
> properties
;
27 private CompletableFuture
<Void
> activationStarted
= null;
28 private CompletableFuture
<Void
> activated
= null;
30 private CompletableFuture
<Void
> deactivationStarted
= null;
31 private CompletableFuture
<Void
> deactivated
= null;
34 private Set
<Dependency
<?
>> dependants
= new HashSet
<>();
36 private RankingKey rankingKey
;
38 Component(ComponentRegister register
, I instance
, Runnable init
, Runnable close
, Set
<Dependency
<?
>> dependencies
,
39 Set
<Class
<?
super I
>> classes
, Map
<String
, Object
> properties
) {
40 assert instance
!= null;
43 assert dependencies
!= null;
44 assert classes
!= null;
46 this.instance
= instance
;
51 Map
<Class
<?
super I
>, PublishedType
<?
super I
>> types
= new HashMap
<>(classes
.size());
52 for (Class
<?
super I
> clss
: classes
) {
53 // if (!clss.isAssignableFrom(instance.getClass()))
54 // throw new IllegalArgumentException(
55 // "Type " + clss.getName() + " is not compatible with " + instance.getClass().getName());
56 types
.put(clss
, new PublishedType
<>(this, clss
));
58 this.types
= Collections
.unmodifiableMap(types
);
61 this.dependencies
= Collections
.unmodifiableSet(new HashSet
<>(dependencies
));
62 for (Dependency
<?
> dependency
: this.dependencies
) {
63 dependency
.setDependantComponent(this);
66 // deactivated by default
67 deactivated
= CompletableFuture
.completedFuture(null);
68 deactivationStarted
= CompletableFuture
.completedFuture(null);
70 // TODO check whether context is active, so that we start right away
71 prepareNextActivation();
73 long serviceId
= register
.register(this);
74 Map
<String
, Object
> props
= new HashMap
<>(properties
);
75 props
.put(RankingKey
.SERVICE_ID
, serviceId
);
76 this.properties
= Collections
.unmodifiableMap(props
);
77 rankingKey
= new RankingKey(properties
);
80 private void prepareNextActivation() {
81 activationStarted
= new CompletableFuture
<Void
>();
82 activated
= activationStarted
//
83 .thenComposeAsync(this::dependenciesActivated
) //
84 .thenRun(this.init
) //
85 .thenRun(() -> prepareNextDeactivation());
88 private void prepareNextDeactivation() {
89 deactivationStarted
= new CompletableFuture
<Void
>();
90 deactivated
= deactivationStarted
//
91 .thenComposeAsync(this::dependantsDeactivated
) //
92 .thenRun(this.close
) //
93 .thenRun(() -> prepareNextActivation());
96 CompletableFuture
<Void
> getActivated() {
100 CompletableFuture
<Void
> getDeactivated() {
104 void startActivating() {
105 if (activated
.isDone() || activationStarted
.isDone())
107 activationStarted
.complete(null);
110 void startDeactivating() {
111 if (deactivated
.isDone() || deactivationStarted
.isDone())
113 deactivationStarted
.complete(null);
116 CompletableFuture
<Void
> dependenciesActivated(Void v
) {
117 Set
<CompletableFuture
<?
>> constraints
= new HashSet
<>(this.dependencies
.size());
118 for (Dependency
<?
> dependency
: this.dependencies
) {
119 CompletableFuture
<Void
> dependencyActivated
= dependency
.publisherActivated() //
120 .thenCompose(dependency
::set
);
121 constraints
.add(dependencyActivated
);
123 return CompletableFuture
.allOf(constraints
.toArray(new CompletableFuture
[constraints
.size()]));
126 CompletableFuture
<Void
> dependantsDeactivated(Void v
) {
127 Set
<CompletableFuture
<?
>> constraints
= new HashSet
<>(this.dependants
.size());
128 for (Dependency
<?
> dependant
: this.dependants
) {
129 CompletableFuture
<Void
> dependantDeactivated
= dependant
.dependantDeactivated() //
130 .thenCompose(dependant
::unset
);
131 constraints
.add(dependantDeactivated
);
133 CompletableFuture
<Void
> dependantsDeactivated
= CompletableFuture
134 .allOf(constraints
.toArray(new CompletableFuture
[constraints
.size()]));
135 return dependantsDeactivated
;
139 void addDependant(Dependency
<?
> dependant
) {
140 dependants
.add(dependant
);
148 @SuppressWarnings("unchecked")
149 public <T
> PublishedType
<T
> getType(Class
<T
> clss
) {
150 if (!types
.containsKey(clss
))
151 throw new IllegalArgumentException(clss
.getName() + " is not a type published by this component");
152 return (PublishedType
<T
>) types
.get(clss
);
155 public <T
> boolean isPublishedType(Class
<T
> clss
) {
156 return types
.containsKey(clss
);
159 public Map
<String
, Object
> getProperties() {
164 public int compareTo(Component
<?
> o
) {
165 return rankingKey
.compareTo(rankingKey
);
168 /** A type which has been explicitly exposed by a component. */
169 public static class PublishedType
<T
> {
170 private Component
<?
extends T
> component
;
171 private Class
<T
> clss
;
173 private CompletableFuture
<T
> value
;
175 public PublishedType(Component
<?
extends T
> component
, Class
<T
> clss
) {
177 this.component
= component
;
178 value
= CompletableFuture
.completedFuture((T
) component
.instance
);
181 public Component
<?
> getPublisher() {
185 public Class
<T
> getType() {
189 public CompletionStage
<T
> getValue() {
190 return value
.minimalCompletionStage();
194 /** Builds a {@link Component}. */
195 public static class Builder
<I
> implements Supplier
<I
> {
196 private final I instance
;
198 private Runnable init
;
199 private Runnable close
;
201 private Set
<Dependency
<?
>> dependencies
= new HashSet
<>();
202 private Set
<Class
<?
super I
>> types
= new HashSet
<>();
203 private final Map
<String
, Object
> properties
= new HashMap
<>();
205 public Builder(I instance
) {
206 this.instance
= instance
;
209 public Component
<I
> build(ComponentRegister register
) {
211 if (types
.isEmpty()) {
212 types
.add(getInstanceClass());
223 Component
<I
> component
= new Component
<I
>(register
, instance
, init
, close
, dependencies
, types
, properties
);
224 for (Dependency
<?
> dependency
: dependencies
) {
225 dependency
.type
.getPublisher().addDependant(dependency
);
230 public Builder
<I
> addType(Class
<?
super I
> clss
) {
235 public Builder
<I
> addActivation(Runnable init
) {
236 if (this.init
!= null)
237 throw new IllegalArgumentException("init method is already set");
242 public Builder
<I
> addDeactivation(Runnable close
) {
243 if (this.close
!= null)
244 throw new IllegalArgumentException("close method is already set");
249 public <D
> Builder
<I
> addDependency(PublishedType
<D
> type
, Consumer
<D
> set
, Consumer
<D
> unset
) {
250 dependencies
.add(new Dependency
<D
>(type
, set
, unset
));
254 public void addProperty(String key
, Object value
) {
255 if (properties
.containsKey(key
))
256 throw new IllegalStateException("Key " + key
+ " is already set.");
257 properties
.put(key
, value
);
265 @SuppressWarnings("unchecked")
266 private Class
<I
> getInstanceClass() {
267 return (Class
<I
>) instance
.getClass();
272 static class Dependency
<D
> {
273 private PublishedType
<D
> type
;
274 private Consumer
<D
> set
;
275 private Consumer
<D
> unset
;
278 Component
<?
> dependantComponent
;
279 CompletableFuture
<Void
> setStage
;
280 CompletableFuture
<Void
> unsetStage
;
282 public Dependency(PublishedType
<D
> types
, Consumer
<D
> set
, Consumer
<D
> unset
) {
285 this.set
= set
!= null ? set
: t
-> {
287 this.unset
= unset
!= null ? unset
: t
-> {
292 void setDependantComponent(Component
<?
> component
) {
293 this.dependantComponent
= component
;
296 CompletableFuture
<Void
> publisherActivated() {
297 return type
.getPublisher().activated
.copy();
300 CompletableFuture
<Void
> dependantDeactivated() {
301 return dependantComponent
.deactivated
.copy();
304 CompletableFuture
<Void
> set(Void v
) {
305 return type
.value
.thenAccept(set
);
308 CompletableFuture
<Void
> unset(Void v
) {
309 return type
.value
.thenAccept(unset
);