Massive package refactoring
[lgpl/argeo-commons.git] / org.argeo.api.register / src / org / argeo / api / register / SimpleRegister.java
diff --git a/org.argeo.api.register/src/org/argeo/api/register/SimpleRegister.java b/org.argeo.api.register/src/org/argeo/api/register/SimpleRegister.java
new file mode 100644 (file)
index 0000000..9ed7e76
--- /dev/null
@@ -0,0 +1,124 @@
+package org.argeo.api.register;
+
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Predicate;
+
+/** A minimal component register. */
+public class SimpleRegister implements ComponentRegister {
+       private final AtomicBoolean started = new AtomicBoolean(false);
+       private final IdentityHashMap<Object, Component<?>> components = new IdentityHashMap<>();
+       private final AtomicLong nextServiceId = new AtomicLong(0l);
+
+       @Override
+       public long register(Component<?> component) {
+               return registerComponent(component);
+       }
+
+       @SuppressWarnings({ "unchecked" })
+       @Override
+       public synchronized <T> SortedSet<Component<? extends T>> find(Class<T> clss,
+                       Predicate<Map<String, Object>> filter) {
+               SortedSet<Component<? extends T>> result = new TreeSet<>();
+               instances: for (Object instance : components.keySet()) {
+                       if (!clss.isAssignableFrom(instance.getClass()))
+                               continue instances;
+                       Component<? extends T> component = (Component<? extends T>) components.get(instance);
+
+                       if (component.isPublishedType(clss)) {
+                               if (filter != null) {
+                                       filter.test(component.getProperties());
+                               }
+                               result.add(component);
+                       }
+               }
+               if (result.isEmpty())
+                       return null;
+               return result;
+
+       }
+
+       synchronized long registerComponent(Component<?> component) {
+               if (started.get()) // TODO make it really dynamic
+                       throw new IllegalStateException("Already activated");
+               if (components.containsKey(component.get()))
+                       throw new IllegalArgumentException("Already registered as component");
+               components.put(component.get(), component);
+               return nextServiceId.incrementAndGet();
+       }
+
+       @Override
+       public synchronized Component<?> get(Object instance) {
+               if (!components.containsKey(instance))
+                       throw new IllegalArgumentException("Not registered as component");
+               return components.get(instance);
+       }
+
+       @Override
+       public synchronized void activate() {
+               if (started.get())
+                       throw new IllegalStateException("Already activated");
+               Set<CompletableFuture<?>> 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 (InterruptedException e) {
+                       throw new RuntimeException("Register activation has been interrupted", e);
+               } catch (ExecutionException e) {
+                       if (RuntimeException.class.isAssignableFrom(e.getCause().getClass())) {
+                               throw (RuntimeException) e.getCause();
+                       } else {
+                               throw new IllegalStateException("Cannot activate register", e.getCause());
+                       }
+               }
+       }
+
+       @Override
+       public synchronized void deactivate() {
+               if (!started.get())
+                       throw new IllegalStateException("Not activated");
+               Set<CompletableFuture<?>> 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 (InterruptedException e) {
+                       throw new RuntimeException("Register deactivation has been interrupted", e);
+               } catch (ExecutionException e) {
+                       if (RuntimeException.class.isAssignableFrom(e.getCause().getClass())) {
+                               throw (RuntimeException) e.getCause();
+                       } else {
+                               throw new IllegalStateException("Cannot deactivate register", e.getCause());
+                       }
+               }
+       }
+
+       @Override
+       public synchronized boolean isActive() {
+               return started.get();
+       }
+
+       @Override
+       public synchronized void clear() {
+               components.clear();
+       }
+}