Start working around components
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / osgi / util / OnServiceRegistration.java
diff --git a/org.argeo.enterprise/src/org/argeo/osgi/util/OnServiceRegistration.java b/org.argeo.enterprise/src/org/argeo/osgi/util/OnServiceRegistration.java
new file mode 100644 (file)
index 0000000..5a6760e
--- /dev/null
@@ -0,0 +1,98 @@
+package org.argeo.osgi.util;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class OnServiceRegistration<R> implements Future<R> {
+       private BundleContext ownBundleContext = FrameworkUtil.getBundle(OnServiceRegistration.class).getBundleContext();
+
+       private ServiceTracker<?, ?> st;
+
+       private R result;
+       private boolean cancelled = false;
+       private Throwable exception;
+
+       public <T> OnServiceRegistration(Class<T> clss, Function<T, R> function) {
+               this(null, clss, function);
+       }
+
+       public <T> OnServiceRegistration(BundleContext bundleContext, Class<T> clss, Function<T, R> function) {
+               st = new ServiceTracker<T, T>(bundleContext != null ? bundleContext : ownBundleContext, clss, null) {
+
+                       @Override
+                       public T addingService(ServiceReference<T> reference) {
+                               T service = super.addingService(reference);
+                               try {
+                                       if (result != null)// we only want the first one
+                                               return service;
+                                       result = function.apply(service);
+                                       return service;
+                               } catch (Exception e) {
+                                       exception = e;
+                                       return service;
+                               } finally {
+                                       close();
+                               }
+                       }
+               };
+               st.open(bundleContext == null);
+       }
+
+       @Override
+       public boolean cancel(boolean mayInterruptIfRunning) {
+               if (result != null || exception != null || cancelled)
+                       return false;
+               st.close();
+               cancelled = true;
+               return true;
+       }
+
+       @Override
+       public boolean isCancelled() {
+               return cancelled;
+       }
+
+       @Override
+       public boolean isDone() {
+               return result != null || cancelled;
+       }
+
+       @Override
+       public R get() throws InterruptedException, ExecutionException {
+               st.waitForService(0);
+               return tryGetResult();
+       }
+
+       @Override
+       public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+               st.waitForService(TimeUnit.MILLISECONDS.convert(timeout, unit));
+               if (result == null)
+                       throw new TimeoutException("No result after " + timeout + " " + unit);
+               return tryGetResult();
+       }
+
+       protected R tryGetResult() throws ExecutionException, CancellationException {
+               if (cancelled)
+                       throw new CancellationException();
+               if (exception != null)
+                       throw new ExecutionException(exception);
+               if (result == null)// this should not happen
+                       try {
+                               throw new IllegalStateException("No result available");
+                       } catch (Exception e) {
+                               exception = e;
+                               throw new ExecutionException(e);
+                       }
+               return result;
+       }
+
+}