karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gno...@apache.org
Subject [08/11] git commit: [KARAF-2805] Rework service tracking to be more robust under high load
Date Tue, 01 Apr 2014 11:43:41 GMT
[KARAF-2805] Rework service tracking to be more robust under high load

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/77d5f064
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/77d5f064
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/77d5f064

Branch: refs/heads/master
Commit: 77d5f064bf9434b66cab0eb502255ea178be07a7
Parents: 75eb098
Author: Guillaume Nodet <gnodet@gmail.com>
Authored: Tue Apr 1 10:35:03 2014 +0200
Committer: Guillaume Nodet <gnodet@gmail.com>
Committed: Tue Apr 1 11:34:27 2014 +0200

----------------------------------------------------------------------
 .../shell/impl/action/command/ManagerImpl.java  |   2 +-
 .../action/osgi/AggregateServiceTracker.java    | 123 +++++++++-------
 .../shell/impl/action/osgi/CommandExtender.java |   1 +
 .../impl/action/osgi/CommandExtension.java      | 141 +++++++++----------
 .../impl/action/osgi/MultiServiceTracker.java   |  96 ++++++-------
 .../shell/impl/action/osgi/Satisfiable.java     |  30 ----
 .../impl/action/osgi/SingleServiceTracker.java  |  86 +++++------
 7 files changed, 214 insertions(+), 265 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
index 5498d2d..6aa0d32 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
@@ -87,7 +87,7 @@ public class ManagerImpl implements Manager {
                         }
                     }
                     if (!allowCustomServices && value == null) {
-                        throw new RuntimeException("No service matching " + field.getType().getName());
+                        throw new IllegalStateException("No service matching " + field.getType().getName());
                     }
                     field.setAccessible(true);
                     field.set(instance, value);

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
index e36b260..dad5ab2 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
@@ -19,67 +19,71 @@
 package org.apache.karaf.shell.impl.action.osgi;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.osgi.framework.BundleContext;
 
 /**
  * Track multiple services by their type
  */
-public class AggregateServiceTracker implements Satisfiable {
+public abstract class AggregateServiceTracker {
 
     private final BundleContext bundleContext;
-    private final Satisfiable satisfiable;
     private final ConcurrentMap<Class, SingleServiceTracker> singleTrackers = new ConcurrentHashMap<Class,
SingleServiceTracker>();
     private final ConcurrentMap<Class, MultiServiceTracker> multiTrackers = new ConcurrentHashMap<Class,
MultiServiceTracker>();
-    private final AtomicInteger count = new AtomicInteger(-1);
+    private volatile State state = new State();
+    private volatile boolean opening = true;
 
-    public AggregateServiceTracker(BundleContext bundleContext, Satisfiable satisfiable)
{
+    public AggregateServiceTracker(BundleContext bundleContext) {
         this.bundleContext = bundleContext;
-        this.satisfiable = satisfiable;
     }
 
     @SuppressWarnings("unchecked")
-    public void track(Class service, boolean multiple) {
+    public <T> void track(final Class<T> service, final boolean multiple) {
         if (multiple) {
             if (multiTrackers.get(service) == null) {
-                MultiServiceTracker tracker = new MultiServiceTracker(bundleContext, service,
this);
+                MultiServiceTracker<T> tracker = new MultiServiceTracker<T>(bundleContext,
service) {
+                    @Override
+                    public void updateState(List<T> services) {
+                        updateStateMulti(service, services);
+                    }
+                };
                 multiTrackers.put(service, tracker);
             }
         } else {
             if (singleTrackers.get(service) == null) {
-                SingleServiceTracker tracker = new SingleServiceTracker(bundleContext, service,
this);
+                SingleServiceTracker<T> tracker = new SingleServiceTracker<T>(bundleContext,
service) {
+                    @Override
+                    public void updateState(T oldSvc, T newSvc) {
+                        updateStateSingle(service, newSvc);
+                    }
+                };
                 singleTrackers.putIfAbsent(service, tracker);
             }
         }
     }
 
-    public <T> T getService(Class<T> clazz) {
-        SingleServiceTracker tracker = singleTrackers.get(clazz);
-        return tracker != null ? clazz.cast(tracker.getService()) : null;
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> List<T> getServices(Class<T> clazz) {
-        MultiServiceTracker tracker = multiTrackers.get(clazz);
-        return tracker != null ? tracker.getServices() : null;
-    }
-
-    public void open() {
+    public State open() {
         for (SingleServiceTracker tracker : singleTrackers.values()) {
             tracker.open();
         }
         for (MultiServiceTracker tracker : multiTrackers.values()) {
             tracker.open();
         }
-        found();
+        State state;
+        synchronized (this) {
+            state = this.state;
+            this.opening = false;
+        }
+        return state;
     }
 
     public void close() {
-        lost();
+        updateState(null);
         for (MultiServiceTracker tracker : multiTrackers.values()) {
             tracker.close();
         }
@@ -88,44 +92,65 @@ public class AggregateServiceTracker implements Satisfiable {
         }
     }
 
-    public boolean isSatisfied() {
-        return count.get() == singleTrackers.size() + multiTrackers.size();
+    protected abstract void updateState(State state);
+
+    private <T> void updateStateMulti(Class<T> serviceClass, List<T> services)
{
+        State newState = new State();
+        synchronized (this) {
+            newState.multi.putAll(state.multi);
+            newState.single.putAll(state.single);
+            newState.multi.put(serviceClass, services);
+            this.state = newState;
+        }
+        updateState(newState);
     }
 
-    public List<String> getMissingServices() {
-        List<String> missing = new ArrayList<String>();
-        for (SingleServiceTracker tracker : singleTrackers.values()) {
-            if (!tracker.isSatisfied()) {
-                missing.add(tracker.getClassName());
+    private <T> void updateStateSingle(Class<T> serviceClass, T service) {
+        State newState = new State();
+        boolean opening;
+        synchronized (this) {
+            newState.multi.putAll(state.multi);
+            newState.single.putAll(state.single);
+            if (service != null) {
+                newState.single.put(serviceClass, service);
+            } else {
+                newState.single.remove(serviceClass);
             }
+            this.state = newState;
+            opening = this.opening;
         }
-        for (MultiServiceTracker tracker : multiTrackers.values()) {
-            if (!tracker.isSatisfied()) {
-                missing.add("List<" + tracker.getClassName() + ">");
-            }
+        if (!opening) {
+            updateState(newState);
         }
-        return missing;
     }
 
-    @Override
-    public void found() {
-        if (count.incrementAndGet() == singleTrackers.size() + multiTrackers.size()) {
-            satisfiable.found();
+    public class State {
+
+        private final Map<Class, List> multi = new HashMap<Class, List>();
+        private final Map<Class, Object> single = new HashMap<Class, Object>();
+
+        public boolean isSatisfied() {
+            return single.size() == singleTrackers.size();
         }
-    }
 
-    @Override
-    public void updated() {
-        if (count.get() == singleTrackers.size() + multiTrackers.size()) {
-            satisfiable.updated();
+        public Map<Class, Object> getSingleServices() {
+            return single;
         }
-    }
 
-    @Override
-    public void lost() {
-        if (count.getAndDecrement() == singleTrackers.size() + multiTrackers.size()) {
-            satisfiable.lost();
+        public Map<Class, List> getMultiServices() {
+            return multi;
         }
+
+        public List<String> getMissingServices() {
+            List<String> missing = new ArrayList<String>();
+            for (SingleServiceTracker tracker : singleTrackers.values()) {
+                if (single.containsKey(tracker.getTrackedClass())) {
+                    missing.add(tracker.getTrackedClass().getName());
+                }
+            }
+            return missing;
+        }
+
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtender.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtender.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtender.java
index 25bcae7..cb2fb07 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtender.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtender.java
@@ -42,6 +42,7 @@ public class CommandExtender extends AbstractExtender {
     private Registry registry;
 
     public CommandExtender(Registry registry) {
+        setSynchronous(true);
         this.registry = registry;
         this.registry.register(new ManagerImpl(this.registry, this.registry));
     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
index 7f9e3ee..80f9d21 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
@@ -22,6 +22,7 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.felix.utils.extender.Extension;
 import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.manifest.Parser;
+import org.apache.karaf.shell.api.action.lifecycle.Manager;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.apache.karaf.shell.api.console.History;
@@ -47,50 +49,28 @@ import org.slf4j.LoggerFactory;
 /**
  * Commands extension
  */
-public class CommandExtension implements Extension, Satisfiable {
+public class CommandExtension implements Extension {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(CommandExtension.class);
 
     private final Bundle bundle;
-    private final ManagerImpl manager;
     private final Registry registry;
     private final CountDownLatch started;
     private final AggregateServiceTracker tracker;
-    private final List<Satisfiable> satisfiables = new ArrayList<Satisfiable>();
+    private final List<Class> classes = new ArrayList<Class>();
+    private Manager manager;
 
 
     public CommandExtension(Bundle bundle, Registry registry) {
         this.bundle = bundle;
-        this.registry = new RegistryImpl(registry);
-        this.registry.register(bundle.getBundleContext());
-        this.manager = new ManagerImpl(this.registry, registry);
-        this.registry.register(this.manager);
+        this.registry = registry;
         this.started = new CountDownLatch(1);
-        this.tracker = new AggregateServiceTracker(bundle.getBundleContext(), this);
-    }
-
-    @Override
-    public void found() {
-        LOGGER.info("Registering commands for bundle {}/{}",
-                bundle.getSymbolicName(),
-                bundle.getVersion());
-        for (Satisfiable s : satisfiables) {
-            s.found();
-        }
-    }
-
-    @Override
-    public void updated() {
-        for (Satisfiable s : satisfiables) {
-            s.updated();
-        }
-    }
-
-    @Override
-    public void lost() {
-        for (Satisfiable s : satisfiables) {
-            s.lost();
-        }
+        this.tracker = new AggregateServiceTracker(bundle.getBundleContext()) {
+            @Override
+            protected void updateState(State state) {
+                CommandExtension.this.updateState(state);
+            }
+        };
     }
 
     public void start() throws Exception {
@@ -118,12 +98,14 @@ public class CommandExtension implements Extension, Satisfiable {
                     inspectClass(bundle.loadClass(className));
                 }
             }
-            tracker.open();
-            if (!tracker.isSatisfied()) {
+            AggregateServiceTracker.State state = tracker.open();
+            if (!state.isSatisfied()) {
                 LOGGER.info("Command registration delayed for bundle {}/{}. Missing dependencies:
{}",
                         bundle.getSymbolicName(),
                         bundle.getVersion(),
-                        tracker.getMissingServices());
+                        state.getMissingServices());
+            } else {
+                updateState(state);
             }
         } finally {
             started.countDown();
@@ -139,6 +121,51 @@ public class CommandExtension implements Extension, Satisfiable {
         tracker.close();
     }
 
+    private synchronized void updateState(AggregateServiceTracker.State state) {
+        boolean wasSatisfied = manager != null;
+        boolean isSatisfied = state != null && state.isSatisfied();
+        String action;
+        if (wasSatisfied && isSatisfied) {
+            action = "Updating";
+        } else if (wasSatisfied) {
+            action = "Unregistering";
+        } else if (isSatisfied) {
+            action = "Registering";
+        } else {
+            action = null;
+        }
+        LOGGER.info("{} commands for bundle {}/{}",
+                action,
+                bundle.getSymbolicName(),
+                bundle.getVersion());
+        if (wasSatisfied) {
+            for (Class clazz : classes) {
+                manager.unregister(clazz);
+            }
+            manager = null;
+        }
+        if (isSatisfied) {
+            Registry reg = new RegistryImpl(registry);
+            manager = new ManagerImpl(reg, registry);
+            reg.register(bundle.getBundleContext());
+            reg.register(manager);
+            for (Map.Entry<Class, Object> entry : state.getSingleServices().entrySet())
{
+                reg.register(entry.getValue());
+            }
+            for (final Map.Entry<Class, List> entry : state.getMultiServices().entrySet())
{
+                reg.register(new Callable() {
+                    @Override
+                    public Object call() throws Exception {
+                        return entry.getValue();
+                    }
+                }, entry.getKey());
+            }
+            for (Class clazz : classes) {
+                manager.register(clazz);
+            }
+        }
+    }
+
     private void inspectClass(final Class<?> clazz) throws Exception {
         Service reg = clazz.getAnnotation(Service.class);
         if (reg == null) {
@@ -162,59 +189,17 @@ public class CommandExtension implements Extension, Satisfiable {
                 }
             }
         }
-        satisfiables.add(new AutoRegister(clazz));
+        classes.add(clazz);
     }
 
     protected void track(final GenericType type) {
         if (type.getRawClass() == List.class) {
             final Class clazzRef = type.getActualTypeArgument(0).getRawClass();
             tracker.track(clazzRef, true);
-            registry.register(new Callable() {
-                @Override
-                public Object call() throws Exception {
-                    return tracker.getServices(clazzRef);
-                }
-            }, clazzRef);
         } else {
             final Class clazzRef = type.getRawClass();
             tracker.track(clazzRef, false);
-            registry.register(new Callable() {
-                @Override
-                public Object call() throws Exception {
-                    return tracker.getService(clazzRef);
-                }
-            }, clazzRef);
         }
     }
 
-    public class AutoRegister implements Satisfiable {
-
-        private final Class<?> clazz;
-
-        public AutoRegister(Class<?> clazz) {
-            this.clazz = clazz;
-        }
-
-        @Override
-        public void found() {
-            try {
-                manager.register(clazz);
-            } catch (Exception e) {
-                throw new RuntimeException("Unable to create service " + clazz.getName(),
e);
-            }
-        }
-
-        @Override
-        public void updated() {
-            lost();
-            found();
-        }
-
-        @Override
-        public void lost() {
-            manager.unregister(clazz);
-        }
-
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/MultiServiceTracker.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/MultiServiceTracker.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/MultiServiceTracker.java
index 1b918b9..38c0a14 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/MultiServiceTracker.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/MultiServiceTracker.java
@@ -19,13 +19,14 @@
 package org.apache.karaf.shell.impl.action.osgi;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
@@ -33,84 +34,54 @@ import org.osgi.framework.ServiceReference;
 
 /**
  * Track multiple service by its type.
+ * When tracking multiple services, the dependency is always considered optional.
  *
  * @param <T>
  */
-public final class MultiServiceTracker<T> {
+public abstract class MultiServiceTracker<T> {
 
     private final BundleContext ctx;
-    private final String className;
-    private final List<T> services = new CopyOnWriteArrayList<T>();
-    private final List<ServiceReference> refs = new CopyOnWriteArrayList<ServiceReference>();
+    private final Class<T> clazz;
+    private final Map<ServiceReference<T>, T> refs = new HashMap<ServiceReference<T>,
T>();
     private final AtomicBoolean open = new AtomicBoolean(false);
-    private final Satisfiable serviceListener;
-    private Filter filter;
 
     private final ServiceListener listener = new ServiceListener() {
+        @SuppressWarnings("unchecked")
         public void serviceChanged(ServiceEvent event) {
             if (open.get()) {
                 if (event.getType() == ServiceEvent.UNREGISTERING) {
-                    removeRef(event.getServiceReference());
+                    removeRef((ServiceReference<T>) event.getServiceReference());
                 } else if (event.getType() == ServiceEvent.REGISTERED) {
-                    addRef(event.getServiceReference());
+                    addRef((ServiceReference<T>) event.getServiceReference());
                 }
+                updateState();
             }
         }
     };
 
-    public MultiServiceTracker(BundleContext context, Class<T> clazz, Satisfiable sl)
{
+    public MultiServiceTracker(BundleContext context, Class<T> clazz) {
         ctx = context;
-        this.className = clazz.getName();
-        serviceListener = sl;
+        this.clazz = clazz;
     }
 
-    public List<T> getServices() {
-        return services;
-    }
-
-    public List<ServiceReference> getServiceReferences() {
-        return refs;
-    }
+    protected abstract void updateState(List<T> services);
 
     public void open() {
         if (open.compareAndSet(false, true)) {
             try {
-                String filterString = '(' + Constants.OBJECTCLASS + '=' + className + ')';
-                if (filter != null) filterString = "(&" + filterString + filter + ')';
+                String filterString = '(' + Constants.OBJECTCLASS + '=' + clazz.getName()
+ ')';
                 ctx.addServiceListener(listener, filterString);
-                ServiceReference[] refs = ctx.getServiceReferences(className, filter != null
? filter.toString() : null);
+                Collection<ServiceReference<T>> refs = ctx.getServiceReferences(clazz,
null);
                 if (refs != null) {
-                    for (ServiceReference ref : refs) {
+                    for (ServiceReference<T> ref : refs) {
                         addRef(ref);
                     }
                 }
             } catch (InvalidSyntaxException e) {
                 // this can never happen. (famous last words :)
             }
-            serviceListener.found();
-        }
-    }
-
-    private void addRef(ServiceReference ref) {
-        T service = (T) ctx.getService(ref);
-        synchronized (refs) {
-            if (refs.add(ref)) {
-                services.add(service);
-                return;
-            }
+            updateState();
         }
-        ctx.ungetService(ref);
-        serviceListener.updated();
-    }
-
-    private void removeRef(ServiceReference ref) {
-        synchronized (refs) {
-            if (!refs.remove(ref)) {
-                return;
-            }
-        }
-        ctx.ungetService(ref);
-        serviceListener.updated();
     }
 
     public void close() {
@@ -119,9 +90,8 @@ public final class MultiServiceTracker<T> {
 
             List<ServiceReference> oldRefs;
             synchronized (refs) {
-                oldRefs = new ArrayList<ServiceReference>(refs);
+                oldRefs = new ArrayList<ServiceReference>(refs.keySet());
                 refs.clear();
-                services.clear();
             }
             for (ServiceReference ref : oldRefs) {
                 ctx.ungetService(ref);
@@ -129,12 +99,32 @@ public final class MultiServiceTracker<T> {
         }
     }
 
-    public boolean isSatisfied() {
-        return true;
+    private void updateState() {
+        List<T> svcs = new ArrayList<T>();
+        synchronized (refs) {
+            svcs.addAll(refs.values());
+        }
+        updateState(svcs);
     }
 
-    public String getClassName() {
-        return className;
+    private void addRef(ServiceReference<T> ref) {
+        T service = ctx.getService(ref);
+        synchronized (refs) {
+            if (!refs.containsKey(ref)) {
+                refs.put(ref, service);
+                return;
+            }
+        }
+        ctx.ungetService(ref);
+    }
+
+    private void removeRef(ServiceReference<T> ref) {
+        synchronized (refs) {
+            if (refs.remove(ref) == null) {
+                return;
+            }
+        }
+        ctx.ungetService(ref);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/Satisfiable.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/Satisfiable.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/Satisfiable.java
deleted file mode 100644
index 90545aa..0000000
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/Satisfiable.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.karaf.shell.impl.action.osgi;
-
-/**
- * Interface to be called with a boolean satisfaction status.
- */
-public interface Satisfiable {
-
-    void found();
-    void updated();
-    void lost();
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/77d5f064/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/SingleServiceTracker.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/SingleServiceTracker.java
b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/SingleServiceTracker.java
index 039fe48..d007382 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/SingleServiceTracker.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/SingleServiceTracker.java
@@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
@@ -31,24 +30,24 @@ import org.osgi.framework.ServiceReference;
 
 /**
  * Track a single service by its type.
+ * When tracking a single service, the dependency is always considered mandatory.
  *
  * @param <T>
  */
-public final class SingleServiceTracker<T> {
+public abstract class SingleServiceTracker<T> {
 
     private final BundleContext ctx;
-    private final String className;
+    private final Class<T> clazz;
     private final AtomicReference<T> service = new AtomicReference<T>();
-    private final AtomicReference<ServiceReference> ref = new AtomicReference<ServiceReference>();
+    private final AtomicReference<ServiceReference<T>> ref = new AtomicReference<ServiceReference<T>>();
     private final AtomicBoolean open = new AtomicBoolean(false);
-    private final Satisfiable serviceListener;
-    private Filter filter;
 
     private final ServiceListener listener = new ServiceListener() {
+        @SuppressWarnings("unchecked")
         public void serviceChanged(ServiceEvent event) {
             if (open.get()) {
                 if (event.getType() == ServiceEvent.UNREGISTERING) {
-                    ServiceReference deadRef = event.getServiceReference();
+                    ServiceReference<T> deadRef = (ServiceReference<T>) event.getServiceReference();
                     if (deadRef.equals(ref.get())) {
                         findMatchingReference(deadRef);
                     }
@@ -59,25 +58,17 @@ public final class SingleServiceTracker<T> {
         }
     };
 
-    public SingleServiceTracker(BundleContext context, Class<T> clazz, Satisfiable
sl) {
+    public SingleServiceTracker(BundleContext context, Class<T> clazz) {
         ctx = context;
-        this.className = clazz.getName();
-        serviceListener = sl;
+        this.clazz = clazz;
     }
 
-    public T getService() {
-        return service.get();
-    }
-
-    public ServiceReference getServiceReference() {
-        return ref.get();
-    }
+    protected abstract void updateState(T oldSvc, T newSvc);
 
     public void open() {
         if (open.compareAndSet(false, true)) {
             try {
-                String filterString = '(' + Constants.OBJECTCLASS + '=' + className + ')';
-                if (filter != null) filterString = "(&" + filterString + filter + ')';
+                String filterString = '(' + Constants.OBJECTCLASS + '=' + clazz.getName()
+ ')';
                 ctx.addServiceListener(listener, filterString);
                 findMatchingReference(null);
             } catch (InvalidSyntaxException e) {
@@ -86,12 +77,24 @@ public final class SingleServiceTracker<T> {
         }
     }
 
-    private void findMatchingReference(ServiceReference original) {
+    public void close() {
+        if (open.compareAndSet(true, false)) {
+            ctx.removeServiceListener(listener);
+
+            synchronized (this) {
+                ServiceReference<T> deadRef = ref.getAndSet(null);
+                service.set(null);
+                if (deadRef != null) ctx.ungetService(deadRef);
+            }
+        }
+    }
+
+    private void findMatchingReference(ServiceReference<T> original) {
         boolean clear = true;
-        ServiceReference ref = ctx.getServiceReference(className);
-        if (ref != null && (filter == null || filter.match(ref))) {
+        ServiceReference<T> ref = ctx.getServiceReference(clazz);
+        if (ref != null) {
             @SuppressWarnings("unchecked")
-            T service = (T) ctx.getService(ref);
+            T service = ctx.getService(ref);
             if (service != null) {
                 clear = false;
 
@@ -109,60 +112,35 @@ public final class SingleServiceTracker<T> {
         }
     }
 
-    private boolean update(ServiceReference deadRef, ServiceReference newRef, T service)
{
+    private boolean update(ServiceReference<T> deadRef, ServiceReference<T> newRef,
T service) {
         boolean result = false;
-        int foundLostReplaced = -1;
 
         // Make sure we don't try to get a lock on null
         Object lock;
-
         // we have to choose our lock.
         if (newRef != null) lock = newRef;
         else if (deadRef != null) lock = deadRef;
         else lock = this;
 
+        T old = null;
         // This lock is here to ensure that no two threads can set the ref and service
         // at the same time.
         synchronized (lock) {
             if (open.get()) {
                 result = this.ref.compareAndSet(deadRef, newRef);
                 if (result) {
-                    this.service.set(service);
-
-                    if (deadRef == null && newRef != null) foundLostReplaced = 0;
-                    if (deadRef != null && newRef == null) foundLostReplaced = 1;
-                    if (deadRef != null && newRef != null) foundLostReplaced = 2;
+                    old = this.service.getAndSet(service);
                 }
             }
         }
 
-        if (serviceListener != null) {
-            if (foundLostReplaced == 0) serviceListener.found();
-            else if (foundLostReplaced == 1) serviceListener.lost();
-            else if (foundLostReplaced == 2) serviceListener.updated();
-        }
+        updateState(old, service);
 
         return result;
     }
 
-    public void close() {
-        if (open.compareAndSet(true, false)) {
-            ctx.removeServiceListener(listener);
-
-            synchronized (this) {
-                ServiceReference deadRef = ref.getAndSet(null);
-                service.set(null);
-                if (deadRef != null) ctx.ungetService(deadRef);
-            }
-        }
-    }
-
-    public boolean isSatisfied() {
-        return service.get() != null;
-    }
-
-    public String getClassName() {
-        return className;
+    public Class getTrackedClass() {
+        return clazz;
     }
 
 }


Mime
View raw message