Author: gnodet
Date: Wed Sep 23 18:07:38 2009
New Revision: 818182

URL: http://svn.apache.org/viewvc?rev=818182&view=rev
Log:
The current activator use excessive synchronization which leads to deadlocks in 
osgi

Added:
    
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/
    
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/AbstractTracked.java
    
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTracker.java
    
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTrackerCustomizer.java
Modified:
    
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/Activator.java

Modified: 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/Activator.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/Activator.java?rev=818182&r1=818181&r2=818182&view=diff
==============================================================================
--- 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/Activator.java
 (original)
+++ 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/Activator.java
 Wed Sep 23 18:07:38 2009
@@ -23,15 +23,17 @@
 import java.net.URL;
 import java.util.Collection;
 import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.camel.Component;
 import org.apache.camel.TypeConverter;
+import org.apache.camel.osgi.tracker.BundleTracker;
+import org.apache.camel.osgi.tracker.BundleTrackerCustomizer;
 import org.apache.camel.spi.Language;
 import org.apache.camel.spi.LanguageResolver;
 import org.apache.camel.util.ObjectHelper;
@@ -44,19 +46,21 @@
 import org.osgi.framework.SynchronousBundleListener;
 import org.springframework.osgi.util.BundleDelegatingClassLoader;
 
-public class Activator implements BundleActivator, SynchronousBundleListener {
+public class Activator implements BundleActivator, BundleTrackerCustomizer {
     public static final String META_INF_TYPE_CONVERTER = 
"META-INF/services/org/apache/camel/TypeConverter";
     public static final String META_INF_COMPONENT = 
"META-INF/services/org/apache/camel/component/";
     public static final String META_INF_LANGUAGE = 
"META-INF/services/org/apache/camel/language/";
     public static final String META_INF_LANGUAGE_RESOLVER = 
"META-INF/services/org/apache/camel/language/resolver/";
     
     private static final transient Log LOG = 
LogFactory.getLog(Activator.class);    
-    private static final Map<String, ComponentEntry> COMPONENTS = new 
HashMap<String, ComponentEntry>();
-    private static final Map<URL, TypeConverterEntry> TYPE_CONVERTERS = new 
HashMap<URL, TypeConverterEntry>();
-    private static final Map<String, ComponentEntry> LANGUAGES = new 
HashMap<String, ComponentEntry>();
-    private static final Map<String, ComponentEntry> LANGUAGE_RESOLVERS = new 
HashMap<String, ComponentEntry>();
+    private static final Map<String, ComponentEntry> COMPONENTS = new 
ConcurrentHashMap<String, ComponentEntry>();
+    private static final Map<URL, TypeConverterEntry> TYPE_CONVERTERS = new 
ConcurrentHashMap<URL, TypeConverterEntry>();
+    private static final Map<String, ComponentEntry> LANGUAGES = new 
ConcurrentHashMap<String, ComponentEntry>();
+    private static final Map<String, ComponentEntry> LANGUAGE_RESOLVERS = new 
ConcurrentHashMap<String, ComponentEntry>();
     private static Bundle bundle;
     
+    private BundleTracker tracker;
+
     private class ComponentEntry {
         Bundle bundle;
         String path;
@@ -70,32 +74,28 @@
         Set<String> converterPackages;
     }
     
-    public void bundleChanged(BundleEvent event) {
-        if (LOG.isTraceEnabled()) {
-            LOG.trace("Bundle changed: " + event);
+       public Object addingBundle(Bundle bundle, BundleEvent event) {
+               modifiedBundle(bundle, event, null);
+               return bundle;
+       }
+
+       public void modifiedBundle(Bundle bundle, BundleEvent event, Object 
object) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Bundle started: " + bundle.getSymbolicName());
         }
+        mayBeAddComponentAndLanguageFor(bundle);                
+        mayBeAddTypeConverterFor(bundle);
+       }
 
-        try {
-            Bundle bundle = event.getBundle();
-            if (event.getType() == BundleEvent.RESOLVED) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Bundle resolved: " + bundle.getSymbolicName());
-                }
-                mayBeAddComponentAndLanguageFor(bundle);                
-                mayBeAddTypeConverterFor(bundle);
-            } else if (event.getType() == BundleEvent.UNRESOLVED) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Bundle unresolved: " + 
bundle.getSymbolicName());
-                }
-                mayBeRemoveComponentAndLanguageFor(bundle);                
-                mayBeRemoveTypeConverterFor(bundle);
-            }
-        } catch (Throwable e) {
-            LOG.fatal("Exception occured during bundleChanged for event: " + 
event, e);
+       public void removedBundle(Bundle bundle, BundleEvent event, Object 
object) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Bundle stopped: " + bundle.getSymbolicName());
         }
-    }
+        mayBeRemoveComponentAndLanguageFor(bundle);                
+        mayBeRemoveTypeConverterFor(bundle);
+       }
 
-    protected synchronized void addComponentEntry(String entryPath, Bundle 
bundle, Map<String, ComponentEntry> entries, Class clazz) {
+    protected void addComponentEntry(String entryPath, Bundle bundle, 
Map<String, ComponentEntry> entries, Class clazz) {
         // Check bundle compatibility
         try {
             if (bundle.loadClass(clazz.getName()) != clazz) {
@@ -127,7 +127,7 @@
         addComponentEntry(META_INF_LANGUAGE_RESOLVER, bundle, 
LANGUAGE_RESOLVERS, LanguageResolver.class);
     }
     
-    protected synchronized void mayBeAddTypeConverterFor(Bundle bundle) {
+    protected void mayBeAddTypeConverterFor(Bundle bundle) {
         // Check bundle compatibility
         try {
             Class clazz = TypeConverter.class;
@@ -175,7 +175,7 @@
         }        
     }
     
-    protected synchronized void mayBeRemoveTypeConverterFor(Bundle bundle) {
+    protected void mayBeRemoveTypeConverterFor(Bundle bundle) {
         TypeConverterEntry[] entriesArray = 
TYPE_CONVERTERS.values().toArray(new TypeConverterEntry[0]);
         for (TypeConverterEntry entry : entriesArray) {
             if (entry.bundle == bundle) {
@@ -194,19 +194,9 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Using bundle: " + bundle);
         }
-        context.addBundleListener(this);
-
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Checking existing bundles for Camel components, 
languages and type converters");
-        }
-        for (Bundle bundle : context.getBundles()) {
-            if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == 
Bundle.STARTING
-                || bundle.getState() == Bundle.ACTIVE || bundle.getState() == 
Bundle.STOPPING) {
-                mayBeAddComponentAndLanguageFor(bundle);
-                mayBeAddTypeConverterFor(bundle);
-            }
-        }
 
+        tracker = new BundleTracker(context, Bundle.ACTIVE, this);
+        tracker.open();
         LOG.info("Camel activator started");
     }
 
@@ -216,14 +206,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Removing Camel bundles");
         }
-        for (Bundle bundle : context.getBundles()) {
-            if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == 
Bundle.STARTING 
-                || bundle.getState() == Bundle.ACTIVE || bundle.getState() == 
Bundle.STOPPING) {
-                mayBeRemoveComponentAndLanguageFor(bundle);
-                mayBeRemoveTypeConverterFor(bundle);
-            }
-        }
-
+        tracker.close();
         LOG.info("Camel activator stopped");
     }
     
@@ -269,27 +252,27 @@
         return bundle;
     }
     
-    protected static synchronized TypeConverterEntry[] 
getTypeConverterEntries() {
+    protected static TypeConverterEntry[] getTypeConverterEntries() {
         Collection<TypeConverterEntry> entries = TYPE_CONVERTERS.values();
         return entries.toArray(new TypeConverterEntry[entries.size()]);
     }
         
-    public static synchronized Class getComponent(String name) throws 
Exception {
+    public static Class getComponent(String name) throws Exception {
         LOG.trace("Finding Component: " + name);
         return getClassFromEntries(name, COMPONENTS);
     }
     
-    public static synchronized Class getLanguage(String name) throws Exception 
{
+    public static Class getLanguage(String name) throws Exception {
         LOG.trace("Finding Language: " + name);
         return getClassFromEntries(name, LANGUAGES);
     }
     
-    public static synchronized Class getLanguageResolver(String name) throws 
Exception {
+    public static Class getLanguageResolver(String name) throws Exception {
         LOG.trace("Finding LanguageResolver: " + name);
         return getClassFromEntries(name, LANGUAGE_RESOLVERS);
     }
     
-    protected static synchronized Class getClassFromEntries(String name, 
Map<String, ComponentEntry> entries) throws Exception {
+    protected static Class getClassFromEntries(String name, Map<String, 
ComponentEntry> entries) throws Exception {
         ComponentEntry entry = entries.get(name);
         if (entry == null) {
             return null;

Added: 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/AbstractTracked.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/AbstractTracked.java?rev=818182&view=auto
==============================================================================
--- 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/AbstractTracked.java
 (added)
+++ 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/AbstractTracked.java
 Wed Sep 23 18:07:38 2009
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed 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.camel.osgi.tracker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract class to track items. If a Tracker is reused (closed then 
reopened),
+ * then a new AbstractTracked object is used. This class acts a map of tracked
+ * item -> customized object. Subclasses of this class will act as the listener
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked items. This is not a public class. It is only for use by the
+ * implementation of the Tracker class.
+ * 
+ * @ThreadSafe
+ * @version $Revision$
+ * @since 1.4
+ */
+abstract class AbstractTracked {
+       /* set this to true to compile in debug messages */
+       static final boolean            DEBUG   = false;
+
+       /**
+        * Map of tracked items to customized objects.
+        * 
+        * @GuardedBy this
+        */
+       private final Map                       tracked;
+
+       /**
+        * Modification count. This field is initialized to zero and 
incremented by
+        * modified.
+        * 
+        * @GuardedBy this
+        */
+       private int                                     trackingCount;
+
+       /**
+        * List of items in the process of being added. This is used to deal 
with
+        * nesting of events. Since events may be synchronously delivered, 
events
+        * can be nested. For example, when processing the adding of a service 
and
+        * the customizer causes the service to be unregistered, notification 
to the
+        * nested call to untrack that the service was unregistered can be made 
to
+        * the track method.
+        * 
+        * Since the ArrayList implementation is not synchronized, all access to
+        * this list must be protected by the same synchronized object for
+        * thread-safety.
+        * 
+        * @GuardedBy this
+        */
+       private final List                      adding;
+
+       /**
+        * true if the tracked object is closed.
+        * 
+        * This field is volatile because it is set by one thread and read by
+        * another.
+        */
+       volatile boolean                        closed;
+
+       /**
+        * Initial list of items for the tracker. This is used to correctly 
process
+        * the initial items which could be modified before they are tracked. 
This
+        * is necessary since the initial set of tracked items are not 
"announced"
+        * by events and therefore the event which makes the item untracked 
could be
+        * delivered before we track the item.
+        * 
+        * An item must not be in both the initial and adding lists at the same
+        * time. An item must be moved from the initial list to the adding list
+        * "atomically" before we begin tracking it.
+        * 
+        * Since the LinkedList implementation is not synchronized, all access 
to
+        * this list must be protected by the same synchronized object for
+        * thread-safety.
+        * 
+        * @GuardedBy this
+        */
+       private final LinkedList        initial;
+
+       /**
+        * AbstractTracked constructor.
+        */
+       AbstractTracked() {
+               tracked = new HashMap();
+               trackingCount = 0;
+               adding = new ArrayList(6);
+               initial = new LinkedList();
+               closed = false;
+       }
+
+       /**
+        * Set initial list of items into tracker before events begin to be
+        * received.
+        * 
+        * This method must be called from Tracker's open method while 
synchronized
+        * on this object in the same synchronized block as the add listener 
call.
+        * 
+        * @param list The initial list of items to be tracked. 
<code>null</code>
+        *        entries in the list are ignored.
+        * @GuardedBy this
+        */
+       void setInitial(Object[] list) {
+               if (list == null) {
+                       return;
+               }
+               int size = list.length;
+               for (int i = 0; i < size; i++) {
+                       Object item = list[i];
+                       if (item == null) {
+                               continue;
+                       }
+                       if (DEBUG) {
+                               System.out.println("AbstractTracked.setInitial: 
" + item); //$NON-NLS-1$
+                       }
+                       initial.add(item);
+               }
+       }
+
+       /**
+        * Track the initial list of items. This is called after events can 
begin to
+        * be received.
+        * 
+        * This method must be called from Tracker's open method while not
+        * synchronized on this object after the add listener call.
+        * 
+        */
+       void trackInitial() {
+               while (true) {
+                       Object item;
+                       synchronized (this) {
+                               if (closed || (initial.size() == 0)) {
+                                       /*
+                                        * if there are no more initial items
+                                        */
+                                       return; /* we are done */
+                               }
+                               /*
+                                * move the first item from the initial list to 
the adding list
+                                * within this synchronized block.
+                                */
+                               item = initial.removeFirst();
+                               if (tracked.get(item) != null) {
+                                       /* if we are already tracking this item 
*/
+                                       if (DEBUG) {
+                                               System.out
+                                                               
.println("AbstractTracked.trackInitial[already tracked]: " + item); 
//$NON-NLS-1$
+                                       }
+                                       continue; /* skip this item */
+                               }
+                               if (adding.contains(item)) {
+                                       /*
+                                        * if this item is already in the 
process of being added.
+                                        */
+                                       if (DEBUG) {
+                                               System.out
+                                                               
.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+                                       }
+                                       continue; /* skip this item */
+                               }
+                               adding.add(item);
+                       }
+                       if (DEBUG) {
+                               
System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+                       }
+                       trackAdding(item, null); /*
+                                                                        * 
Begin tracking it. We call trackAdding
+                                                                        * 
since we have already put the item in the
+                                                                        * 
adding list.
+                                                                        */
+               }
+       }
+
+       /**
+        * Called by the owning Tracker object when it is closed.
+        */
+       void close() {
+               closed = true;
+       }
+
+       /**
+        * Begin to track an item.
+        * 
+        * @param item Item to be tracked.
+        * @param related Action related object.
+        */
+       void track(final Object item, final Object related) {
+               final Object object;
+               synchronized (this) {
+                       if (closed) {
+                               return;
+                       }
+                       object = tracked.get(item);
+                       if (object == null) { /* we are not tracking the item */
+                               if (adding.contains(item)) {
+                                       /* if this item is already in the 
process of being added. */
+                                       if (DEBUG) {
+                                               System.out
+                                                               
.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+                                       }
+                                       return;
+                               }
+                               adding.add(item); /* mark this item is being 
added */
+                       }
+                       else { /* we are currently tracking this item */
+                               if (DEBUG) {
+                                       System.out
+                                                       
.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+                               }
+                               modified(); /* increment modification count */
+                       }
+               }
+
+               if (object == null) { /* we are not tracking the item */
+                       trackAdding(item, related);
+               }
+               else {
+                       /* Call customizer outside of synchronized region */
+                       customizerModified(item, related, object);
+                       /*
+                        * If the customizer throws an unchecked exception, it 
is safe to
+                        * let it propagate
+                        */
+               }
+       }
+
+       /**
+        * Common logic to add an item to the tracker used by track and
+        * trackInitial. The specified item must have been placed in the adding 
list
+        * before calling this method.
+        * 
+        * @param item Item to be tracked.
+        * @param related Action related object.
+        */
+       private void trackAdding(final Object item, final Object related) {
+               if (DEBUG) {
+                       System.out.println("AbstractTracked.trackAdding: " + 
item); //$NON-NLS-1$
+               }
+               Object object = null;
+               boolean becameUntracked = false;
+               /* Call customizer outside of synchronized region */
+               try {
+                       object = customizerAdding(item, related);
+                       /*
+                        * If the customizer throws an unchecked exception, it 
will
+                        * propagate after the finally
+                        */
+               }
+               finally {
+                       synchronized (this) {
+                               if (adding.remove(item) && !closed) {
+                                       /*
+                                        * if the item was not untracked during 
the customizer
+                                        * callback
+                                        */
+                                       if (object != null) {
+                                               tracked.put(item, object);
+                                               modified(); /* increment 
modification count */
+                                               notifyAll(); /* notify any 
waiters */
+                                       }
+                               }
+                               else {
+                                       becameUntracked = true;
+                               }
+                       }
+               }
+               /*
+                * The item became untracked during the customizer callback.
+                */
+               if (becameUntracked && (object != null)) {
+                       if (DEBUG) {
+                               System.out
+                                               
.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+                       }
+                       /* Call customizer outside of synchronized region */
+                       customizerRemoved(item, related, object);
+                       /*
+                        * If the customizer throws an unchecked exception, it 
is safe to
+                        * let it propagate
+                        */
+               }
+       }
+
+       /**
+        * Discontinue tracking the item.
+        * 
+        * @param item Item to be untracked.
+        * @param related Action related object.
+        */
+       void untrack(final Object item, final Object related) {
+               final Object object;
+               synchronized (this) {
+                       if (initial.remove(item)) { /*
+                                                                               
 * if this item is already in the list
+                                                                               
 * of initial references to process
+                                                                               
 */
+                               if (DEBUG) {
+                                       System.out
+                                                       
.println("AbstractTracked.untrack[removed from initial]: " + item); 
//$NON-NLS-1$
+                               }
+                               return; /*
+                                                * we have removed it from the 
list and it will not be
+                                                * processed
+                                                */
+                       }
+
+                       if (adding.remove(item)) { /*
+                                                                               
 * if the item is in the process of
+                                                                               
 * being added
+                                                                               
 */
+                               if (DEBUG) {
+                                       System.out
+                                                       
.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+                               }
+                               return; /*
+                                                * in case the item is 
untracked while in the process of
+                                                * adding
+                                                */
+                       }
+                       object = tracked.remove(item); /*
+                                                                               
         * must remove from tracker before
+                                                                               
         * calling customizer callback
+                                                                               
         */
+                       if (object == null) { /* are we actually tracking the 
item */
+                               return;
+                       }
+                       modified(); /* increment modification count */
+               }
+               if (DEBUG) {
+                       System.out.println("AbstractTracked.untrack[removed]: " 
+ item); //$NON-NLS-1$
+               }
+               /* Call customizer outside of synchronized region */
+               customizerRemoved(item, related, object);
+               /*
+                * If the customizer throws an unchecked exception, it is safe 
to let it
+                * propagate
+                */
+       }
+
+       /**
+        * Returns the number of tracked items.
+        * 
+        * @return The number of tracked items.
+        * 
+        * @GuardedBy this
+        */
+       int size() {
+               return tracked.size();
+       }
+
+       /**
+        * Return the customized object for the specified item
+        * 
+        * @param item The item to lookup in the map
+        * @return The customized object for the specified item.
+        * 
+        * @GuardedBy this
+        */
+       Object getCustomizedObject(final Object item) {
+               return tracked.get(item);
+       }
+
+       /**
+        * Return the list of tracked items.
+        * 
+        * @param list An array to contain the tracked items.
+        * @return The specified list if it is large enough to hold the tracked
+        *         items or a new array large enough to hold the tracked items.
+        * @GuardedBy this
+        */
+       Object[] getTracked(final Object[] list) {
+               return tracked.keySet().toArray(list);
+       }
+
+       /**
+        * Increment the modification count. If this method is overridden, the
+        * overriding method MUST call this method to increment the tracking 
count.
+        * 
+        * @GuardedBy this
+        */
+       void modified() {
+               trackingCount++;
+       }
+
+       /**
+        * Returns the tracking count for this <code>ServiceTracker</code> 
object.
+        * 
+        * The tracking count is initialized to 0 when this object is opened. 
Every
+        * time an item is added, modified or removed from this object the 
tracking
+        * count is incremented.
+        * 
+        * @GuardedBy this
+        * @return The tracking count for this object.
+        */
+       int getTrackingCount() {
+               return trackingCount;
+       }
+
+       /**
+        * Call the specific customizer adding method. This method must not be
+        * called while synchronized on this object.
+        * 
+        * @param item Item to be tracked.
+        * @param related Action related object.
+        * @return Customized object for the tracked item or <code>null</code> 
if
+        *         the item is not to be tracked.
+        */
+       abstract Object customizerAdding(final Object item, final Object 
related);
+
+       /**
+        * Call the specific customizer modified method. This method must not be
+        * called while synchronized on this object.
+        * 
+        * @param item Tracked item.
+        * @param related Action related object.
+        * @param object Customized object for the tracked item.
+        */
+       abstract void customizerModified(final Object item, final Object 
related,
+                       final Object object);
+
+       /**
+        * Call the specific customizer removed method. This method must not be
+        * called while synchronized on this object.
+        * 
+        * @param item Tracked item.
+        * @param related Action related object.
+        * @param object Customized object for the tracked item.
+        */
+       abstract void customizerRemoved(final Object item, final Object related,
+                       final Object object);
+}

Added: 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTracker.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTracker.java?rev=818182&view=auto
==============================================================================
--- 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTracker.java
 (added)
+++ 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTracker.java
 Wed Sep 23 18:07:38 2009
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed 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.camel.osgi.tracker;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * The <code>BundleTracker</code> class simplifies tracking bundles much like
+ * the <code>ServiceTracker</code> simplifies tracking services.
+ * <p>
+ * A <code>BundleTracker</code> is constructed with state criteria and a
+ * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> 
can
+ * use the <code>BundleTrackerCustomizer</code> to select which bundles are
+ * tracked and to create a customized object to be tracked with the bundle. The
+ * <code>BundleTracker</code> can then be opened to begin tracking all bundles
+ * whose state matches the specified state criteria.
+ * <p>
+ * The <code>getBundles</code> method can be called to get the
+ * <code>Bundle</code> objects of the bundles being tracked. The
+ * <code>getObject</code> method can be called to get the customized object for
+ * a tracked bundle.
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision$
+ * @since 1.4
+ */
+public class BundleTracker implements BundleTrackerCustomizer {
+       /* set this to true to compile in debug messages */
+       static final boolean                    DEBUG   = false;
+
+       /**
+        * The Bundle Context used by this <code>BundleTracker</code>.
+        */
+       protected final BundleContext   context;
+
+       /**
+        * The <code>BundleTrackerCustomizer</code> object for this tracker.
+        */
+       final BundleTrackerCustomizer   customizer;
+
+       /**
+        * Tracked bundles: <code>Bundle</code> object -> customized Object and
+        * <code>BundleListener</code> object
+        */
+       private volatile Tracked                tracked;
+
+       /**
+        * Accessor method for the current Tracked object. This method is only
+        * intended to be used by the unsynchronized methods which do not 
modify the
+        * tracked field.
+        * 
+        * @return The current Tracked object.
+        */
+       private Tracked tracked() {
+               return tracked;
+       }
+
+       /**
+        * State mask for bundles being tracked. This field contains the ORed 
values
+        * of the bundle states being tracked.
+        */
+       final int                                               mask;
+
+       /**
+        * Create a <code>BundleTracker</code> for bundles whose state is 
present in
+        * the specified state mask.
+        * 
+        * <p>
+        * Bundles whose state is present on the specified state mask will be
+        * tracked by this <code>BundleTracker</code>.
+        * 
+        * @param context The <code>BundleContext</code> against which the 
tracking
+        *        is done.
+        * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
+        *        states to be tracked.
+        * @param customizer The customizer object to call when bundles are 
added,
+        *        modified, or removed in this <code>BundleTracker</code>. If
+        *        customizer is <code>null</code>, then this
+        *        <code>BundleTracker</code> will be used as the
+        *        <code>BundleTrackerCustomizer</code> and this
+        *        <code>BundleTracker</code> will call the
+        *        <code>BundleTrackerCustomizer</code> methods on itself.
+        * @see Bundle#getState()
+        */
+       public BundleTracker(BundleContext context, int stateMask,
+                       BundleTrackerCustomizer customizer) {
+               this.context = context;
+               this.mask = stateMask;
+               this.customizer = (customizer == null) ? this : customizer;
+       }
+
+       /**
+        * Open this <code>BundleTracker</code> and begin tracking bundles.
+        * 
+        * <p>
+        * Bundle which match the state criteria specified when this
+        * <code>BundleTracker</code> was created are now tracked by this
+        * <code>BundleTracker</code>.
+        * 
+        * @throws java.lang.IllegalStateException If the 
<code>BundleContext</code>
+        *         with which this <code>BundleTracker</code> was created is no
+        *         longer valid.
+        * @throws java.lang.SecurityException If the caller and this class do 
not
+        *         have the appropriate
+        *         <code>AdminPermission[context bundle,LISTENER]</code>, and 
the
+        *         Java Runtime Environment supports permissions.
+        */
+       public void open() {
+               final Tracked t;
+               synchronized (this) {
+                       if (tracked != null) {
+                               return;
+                       }
+                       if (DEBUG) {
+                               System.out.println("BundleTracker.open"); 
//$NON-NLS-1$
+                       }
+                       t = new Tracked();
+                       synchronized (t) {
+                               context.addBundleListener(t);
+                               Bundle[] bundles = context.getBundles();
+                               if (bundles != null) {
+                                       int length = bundles.length;
+                                       for (int i = 0; i < length; i++) {
+                                               int state = 
bundles[i].getState();
+                                               if ((state & mask) == 0) {
+                                                       /* null out bundles 
whose states are not interesting */
+                                                       bundles[i] = null;
+                                               }
+                                       }
+                                       /* set tracked with the initial bundles 
*/
+                                       t.setInitial(bundles); 
+                               }
+                       }
+                       tracked = t;
+               }
+               /* Call tracked outside of synchronized region */
+               t.trackInitial(); /* process the initial references */
+       }
+
+       /**
+        * Close this <code>BundleTracker</code>.
+        * 
+        * <p>
+        * This method should be called when this <code>BundleTracker</code> 
should
+        * end the tracking of bundles.
+        * 
+        * <p>
+        * This implementation calls {...@link #getBundles()} to get the list of
+        * tracked bundles to remove.
+        */
+       public void close() {
+               final Bundle[] bundles;
+               final Tracked outgoing;
+               synchronized (this) {
+                       outgoing = tracked;
+                       if (outgoing == null) {
+                               return;
+                       }
+                       if (DEBUG) {
+                               System.out.println("BundleTracker.close"); 
//$NON-NLS-1$
+                       }
+                       outgoing.close();
+                       bundles = getBundles();
+                       tracked = null;
+                       try {
+                               context.removeBundleListener(outgoing);
+                       }
+                       catch (IllegalStateException e) {
+                               /* In case the context was stopped. */
+                       }
+               }
+               if (bundles != null) {
+                       for (int i = 0; i < bundles.length; i++) {
+                               outgoing.untrack(bundles[i], null);
+                       }
+               }
+       }
+
+       /**
+        * Default implementation of the
+        * <code>BundleTrackerCustomizer.addingBundle</code> method.
+        * 
+        * <p>
+        * This method is only called when this <code>BundleTracker</code> has 
been
+        * constructed with a <code>null BundleTrackerCustomizer</code> 
argument.
+        * 
+        * <p>
+        * This implementation simply returns the specified <code>Bundle</code>.
+        * 
+        * <p>
+        * This method can be overridden in a subclass to customize the object 
to be
+        * tracked for the bundle being added.
+        * 
+        * @param bundle The <code>Bundle</code> being added to this
+        *        <code>BundleTracker</code> object.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @return The specified bundle.
+        * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
+        */
+       public Object addingBundle(Bundle bundle, BundleEvent event) {
+               return bundle;
+       }
+
+       /**
+        * Default implementation of the
+        * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
+        * 
+        * <p>
+        * This method is only called when this <code>BundleTracker</code> has 
been
+        * constructed with a <code>null BundleTrackerCustomizer</code> 
argument.
+        * 
+        * <p>
+        * This implementation does nothing.
+        * 
+        * @param bundle The <code>Bundle</code> whose state has been modified.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @param object The customized object for the specified Bundle.
+        * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, 
Object)
+        */
+       public void modifiedBundle(Bundle bundle, BundleEvent event, Object 
object) {
+               /* do nothing */
+       }
+
+       /**
+        * Default implementation of the
+        * <code>BundleTrackerCustomizer.removedBundle</code> method.
+        * 
+        * <p>
+        * This method is only called when this <code>BundleTracker</code> has 
been
+        * constructed with a <code>null BundleTrackerCustomizer</code> 
argument.
+        * 
+        * <p>
+        * This implementation does nothing.
+        * 
+        * @param bundle The <code>Bundle</code> being removed.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @param object The customized object for the specified bundle.
+        * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, 
Object)
+        */
+       public void removedBundle(Bundle bundle, BundleEvent event, Object 
object) {
+               /* do nothing */
+       }
+
+       /**
+        * Return an array of <code>Bundle</code>s for all bundles being 
tracked by
+        * this <code>BundleTracker</code>.
+        * 
+        * @return An array of <code>Bundle</code>s or <code>null</code> if no
+        *         bundles are being tracked.
+        */
+       public Bundle[] getBundles() {
+               final Tracked t = tracked();
+               if (t == null) { /* if BundleTracker is not open */
+                       return null;
+               }
+               synchronized (t) {
+                       int length = t.size();
+                       if (length == 0) {
+                               return null;
+                       }
+                       return (Bundle[]) t.getTracked(new Bundle[length]);
+               }
+       }
+
+       /**
+        * Returns the customized object for the specified <code>Bundle</code> 
if
+        * the specified bundle is being tracked by this 
<code>BundleTracker</code>.
+        * 
+        * @param bundle The <code>Bundle</code> being tracked.
+        * @return The customized object for the specified <code>Bundle</code> 
or
+        *         <code>null</code> if the specified <code>Bundle</code> is not
+        *         being tracked.
+        */
+       public Object getObject(Bundle bundle) {
+               final Tracked t = tracked();
+               if (t == null) { /* if BundleTracker is not open */
+                       return null;
+               }
+               synchronized (t) {
+                       return t.getCustomizedObject(bundle);
+               }
+       }
+
+       /**
+        * Remove a bundle from this <code>BundleTracker</code>.
+        * 
+        * The specified bundle will be removed from this 
<code>BundleTracker</code>
+        * . If the specified bundle was being tracked then the
+        * <code>BundleTrackerCustomizer.removedBundle</code> method will be 
called
+        * for that bundle.
+        * 
+        * @param bundle The <code>Bundle</code> to be removed.
+        */
+       public void remove(Bundle bundle) {
+               final Tracked t = tracked();
+               if (t == null) { /* if BundleTracker is not open */
+                       return;
+               }
+               t.untrack(bundle, null);
+       }
+
+       /**
+        * Return the number of bundles being tracked by this
+        * <code>BundleTracker</code>.
+        * 
+        * @return The number of bundles being tracked.
+        */
+       public int size() {
+               final Tracked t = tracked();
+               if (t == null) { /* if BundleTracker is not open */
+                       return 0;
+               }
+               synchronized (t) {
+                       return t.size();
+               }
+       }
+
+       /**
+        * Returns the tracking count for this <code>BundleTracker</code>.
+        * 
+        * The tracking count is initialized to 0 when this
+        * <code>BundleTracker</code> is opened. Every time a bundle is added,
+        * modified or removed from this <code>BundleTracker</code> the tracking
+        * count is incremented.
+        * 
+        * <p>
+        * The tracking count can be used to determine if this
+        * <code>BundleTracker</code> has added, modified or removed a bundle by
+        * comparing a tracking count value previously collected with the 
current
+        * tracking count value. If the value has not changed, then no bundle 
has
+        * been added, modified or removed from this <code>BundleTracker</code>
+        * since the previous tracking count was collected.
+        * 
+        * @return The tracking count for this <code>BundleTracker</code> or -1 
if
+        *         this <code>BundleTracker</code> is not open.
+        */
+       public int getTrackingCount() {
+               final Tracked t = tracked();
+               if (t == null) { /* if BundleTracker is not open */
+                       return -1;
+               }
+               synchronized (t) {
+                       return t.getTrackingCount();
+               }
+       }
+
+       /**
+        * Inner class which subclasses AbstractTracked. This class is the
+        * <code>SynchronousBundleListener</code> object for the tracker.
+        * 
+        * @ThreadSafe
+        * @since 1.4
+        */
+       class Tracked extends AbstractTracked implements 
SynchronousBundleListener {
+               /**
+                * Tracked constructor.
+                */
+               Tracked() {
+                       super();
+               }
+
+               /**
+                * <code>BundleListener</code> method for the 
<code>BundleTracker</code>
+                * class. This method must NOT be synchronized to avoid deadlock
+                * potential.
+                * 
+                * @param event <code>BundleEvent</code> object from the 
framework.
+                */
+               public void bundleChanged(final BundleEvent event) {
+                       /*
+                        * Check if we had a delayed call (which could happen 
when we
+                        * close).
+                        */
+                       if (closed) {
+                               return;
+                       }
+                       final Bundle bundle = event.getBundle();
+                       final int state = bundle.getState();
+                       if (DEBUG) {
+                               System.out
+                                               
.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); 
//$NON-NLS-1$ //$NON-NLS-2$
+                       }
+
+                       if ((state & mask) != 0) {
+                               track(bundle, event);
+                               /*
+                                * If the customizer throws an unchecked 
exception, it is safe
+                                * to let it propagate
+                                */
+                       }
+                       else {
+                               untrack(bundle, event);
+                               /*
+                                * If the customizer throws an unchecked 
exception, it is safe
+                                * to let it propagate
+                                */
+                       }
+               }
+
+               /**
+                * Call the specific customizer adding method. This method must 
not be
+                * called while synchronized on this object.
+                * 
+                * @param item Item to be tracked.
+                * @param related Action related object.
+                * @return Customized object for the tracked item or 
<code>null</code>
+                *         if the item is not to be tracked.
+                */
+               Object customizerAdding(final Object item,
+                               final Object related) {
+                       return customizer
+                                       .addingBundle((Bundle) item, 
(BundleEvent) related);
+               }
+
+               /**
+                * Call the specific customizer modified method. This method 
must not be
+                * called while synchronized on this object.
+                * 
+                * @param item Tracked item.
+                * @param related Action related object.
+                * @param object Customized object for the tracked item.
+                */
+               void customizerModified(final Object item,
+                               final Object related, final Object object) {
+                       customizer.modifiedBundle((Bundle) item, (BundleEvent) 
related,
+                                       object);
+               }
+
+               /**
+                * Call the specific customizer removed method. This method 
must not be
+                * called while synchronized on this object.
+                * 
+                * @param item Tracked item.
+                * @param related Action related object.
+                * @param object Customized object for the tracked item.
+                */
+               void customizerRemoved(final Object item,
+                               final Object related, final Object object) {
+                       customizer.removedBundle((Bundle) item, (BundleEvent) 
related,
+                                       object);
+               }
+       }
+}

Added: 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTrackerCustomizer.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTrackerCustomizer.java?rev=818182&view=auto
==============================================================================
--- 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTrackerCustomizer.java
 (added)
+++ 
camel/trunk/components/camel-osgi/src/main/java/org/apache/camel/osgi/tracker/BundleTrackerCustomizer.java
 Wed Sep 23 18:07:38 2009
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed 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.camel.osgi.tracker;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * The <code>BundleTrackerCustomizer</code> interface allows a
+ * <code>BundleTracker</code> to customize the <code>Bundle</code>s that are
+ * tracked. A <code>BundleTrackerCustomizer</code> is called when a bundle is
+ * being added to a <code>BundleTracker</code>. The
+ * <code>BundleTrackerCustomizer</code> can then return an object for the
+ * tracked bundle. A <code>BundleTrackerCustomizer</code> is also called when a
+ * tracked bundle is modified or has been removed from a
+ * <code>BundleTracker</code>.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * <code>BundleEvent</code> being received by a <code>BundleTracker</code>.
+ * Since <code>BundleEvent</code>s are received synchronously by the
+ * <code>BundleTracker</code>, it is highly recommended that implementations of
+ * these methods do not alter bundle states while being synchronized on any
+ * object.
+ * 
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision$
+ * @since 1.4
+ */
+public interface BundleTrackerCustomizer {
+       /**
+        * A bundle is being added to the <code>BundleTracker</code>.
+        * 
+        * <p>
+        * This method is called before a bundle which matched the search 
parameters
+        * of the <code>BundleTracker</code> is added to the
+        * <code>BundleTracker</code>. This method should return the object to 
be
+        * tracked for the specified <code>Bundle</code>. The returned object is
+        * stored in the <code>BundleTracker</code> and is available from the
+        * {...@link BundleTracker#getObject(Bundle) getObject} method.
+        * 
+        * @param bundle The <code>Bundle</code> being added to the
+        *        <code>BundleTracker</code>.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @return The object to be tracked for the specified 
<code>Bundle</code>
+        *         object or <code>null</code> if the specified 
<code>Bundle</code>
+        *         object should not be tracked.
+        */
+       public Object addingBundle(Bundle bundle, BundleEvent event);
+
+       /**
+        * A bundle tracked by the <code>BundleTracker</code> has been modified.
+        * 
+        * <p>
+        * This method is called when a bundle being tracked by the
+        * <code>BundleTracker</code> has had its state modified.
+        * 
+        * @param bundle The <code>Bundle</code> whose state has been modified.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @param object The tracked object for the specified bundle.
+        */
+       public void modifiedBundle(Bundle bundle, BundleEvent event,
+                       Object object);
+
+       /**
+        * A bundle tracked by the <code>BundleTracker</code> has been removed.
+        * 
+        * <p>
+        * This method is called after a bundle is no longer being tracked by 
the
+        * <code>BundleTracker</code>.
+        * 
+        * @param bundle The <code>Bundle</code> that has been removed.
+        * @param event The bundle event which caused this customizer method to 
be
+        *        called or <code>null</code> if there is no bundle event 
associated
+        *        with the call to this method.
+        * @param object The tracked object for the specified bundle.
+        */
+       public void removedBundle(Bundle bundle, BundleEvent event,
+                       Object object);
+}


Reply via email to