Fix issue Felix-632:
Improves immediate component detection in order to avoid to re-set a component as immediate when it is already immediate.

Fix issue Felix-633:
Uses another thread to create factories and instances. Then once a bundle is analyzed, the factory and instance creation is delayed and delegate to another thread.

Fix issue Felix-634:
Catches Throwable when a POJO instance is created in order to catch "unexpected" errors.

Fix issue Felix-635:
Simplifies factory name computation. Be aware that the factory attribute is no more supported and is replaced by the public attribute or the name attribute. The factory name is now computed as following:
if name not null use this, otherwise use the classname.
The factory advertising is now set with the public attribute (true by default).




git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@676672 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
index 4e56b55..5cb5500 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
@@ -242,11 +242,8 @@
      */
     public String getFactoryName() {
         String name = m_componentMetadata.getAttribute("name");
-        if (name == null) { // No factory name, try with factory attribute
-            name = m_componentMetadata.getAttribute("factory");
-            if (name == null || name.equalsIgnoreCase("true") || name.equalsIgnoreCase("false")) { // Avoid boolean case
+        if (name == null) { // No factory name, use the classname (mandatory attribute)
                 name = m_componentMetadata.getAttribute("classname");
-            }
         }
         return name;
     }
@@ -281,7 +278,7 @@
         // and does not specified that the component is not immediate.
         if (m_componentMetadata.getElements("provides") == null) {
             String imm = m_componentMetadata.getAttribute("immediate");
-            if (imm == null || !imm.equalsIgnoreCase("false")) {
+            if (imm == null) { // immediate not specified, set the immediate attribute to true
                 getLogger().log(
                         Logger.WARNING,
                         "The component " + getFactoryName()
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/Extender.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/Extender.java
index c4a027f..e3f4bc6 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/Extender.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/Extender.java
@@ -87,6 +87,11 @@
      * List of unbound types.

      */

     private final List m_unboundTypes = new ArrayList();

+    

+    /**

+     * Thread analyzing arriving bundles and creating iPOJO contributions.

+     */

+    private final CreatorThread m_thread = new CreatorThread();

 

     /**

      * Bundle Listener Notification.

@@ -98,9 +103,11 @@
 

         switch (event.getType()) {

             case BundleEvent.STARTED:

-                startManagementFor(event.getBundle());

+                // Put the bundle in the queue

+                m_thread.addBundle(event.getBundle());

                 break;

             case BundleEvent.STOPPING:

+                m_thread.removeBundle(event.getBundle());

                 closeManagementFor(event.getBundle());

                 break;

             default:

@@ -115,12 +122,11 @@
      */

     private void closeManagementFor(Bundle bundle) {

         List toRemove = new ArrayList();

+        // Delete instances declared in the leaving bundle.

+        m_creator.removeInstancesFromBundle(bundle.getBundleId());

         for (int k = 0; k < m_factoryTypes.size(); k++) {

             ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) m_factoryTypes.get(k);

 

-            // Delete instances declared in the leaving bundle.

-            m_creator.removeInstancesFromBundle(bundle.getBundleId());

-

             // Look for component type created from this bundle.

             if (mft.m_created != null) {

                 List cfs = (List) mft.m_created.remove(bundle);

@@ -249,10 +255,12 @@
         m_bundle = context.getBundle();

         m_creator = new InstanceCreator(context);

 

-        m_logger = new Logger(m_context, "IPOJO Extender");

+        m_logger = new Logger(m_context, "IPOJO-Extender");

 

         // Begin by initializing core handlers

         startManagementFor(m_bundle);

+        

+        new Thread(m_thread).start();

 

         synchronized (this) {

             // listen to any changes in bundles.

@@ -272,6 +280,7 @@
      * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)

      */

     public void stop(BundleContext context) {

+        m_thread.stop(); // Stop the thread processing bundles.

         m_context.removeBundleListener(this);

 

         for (int k = 0; k < m_factoryTypes.size(); k++) {

@@ -367,7 +376,7 @@
      */

     private final class ManagedAbstractFactoryType {

         /**

-         * TYpe (i.e.) name of the extension.

+         * Type (i.e.) name of the extension.

          */

         String m_type;

 

@@ -501,5 +510,95 @@
         m_logger.log(Logger.ERROR, "Cannot find the BundleContext for " + bundle.getSymbolicName(), null);

         return null;

     }

+    

+

+    /**

+     * The creator thread analyze arriving bundle to create iPOJO contribution.

+     */

+    private class CreatorThread implements Runnable {

+

+        /**

+         * Is the creator thread started?

+         */

+        private boolean m_started = true;

+        

+        /**

+         * List of bundle that are going to be analyzed.

+         */

+        private List m_bundles = new ArrayList();

+        

+        /**

+         * A bundle is arrived.

+         * This method is synchronized to avoid concurrent modification of the waiting list.

+         * @param bundle : new bundle

+         */

+        public synchronized void addBundle(Bundle bundle) {

+            m_bundles.add(bundle);

+            notifyAll(); // Notify the thread to force the process.

+            m_logger.log(Logger.INFO, "Creator thread is going to analyze the bundle " + bundle.getBundleId() + " List : " + m_bundles);

+        }

+        

+        /**

+         * A bundle is leaving.

+         * If the bundle was not already processed, the bundle is remove from the waiting list.

+         * This method is synchronized to avoid concurrent modification of the waiting list.

+         * @param bundle : the leaving bundle.

+         */

+        public synchronized void removeBundle(Bundle bundle) {

+            m_bundles.remove(bundle);

+        }

+        

+        /**

+         * Stop the creator thread.

+         */

+        public synchronized void stop() {

+            m_started = false;

+            m_bundles.clear();

+            notifyAll();

+        }

+

+        /**

+         * Creator thread run method.

+         * While the waiting list is not empty, the thread launch the bundle analyzing.

+         * When the list is empty, the thread sleeps until the arrival of a new bundle 

+         * or that iPOJO stops.

+         * @see java.lang.Runnable#run()

+         */

+        public void run() {

+            m_logger.log(Logger.INFO, "Creator thread is starting");

+            while (m_started) {

+                Bundle bundle;

+                synchronized (this) {

+                    while (m_started && m_bundles.isEmpty()) {

+                        try {

+                            m_logger.log(Logger.INFO, "Creator thread is waiting - Nothing to do");

+                            wait();

+                        } catch (InterruptedException e) {

+                            // Interruption, re-check the condition

+                        }

+                    }

+                    if (!m_started) {

+                        m_logger.log(Logger.INFO, "Creator thread is stopping");

+                        return; // The thread must be stopped immediately.

+                    } else {

+                        // The bundle list is not empty, get the bundle.

+                        // The bundle object is collected inside the synchronized block to avoid

+                        // concurrent modification. However the real process is made outside the

+                        // mutual exclusion area

+                        bundle = (Bundle) m_bundles.remove(0);

+                    }

+                }

+                // Process ...

+                m_logger.log(Logger.INFO, "Creator thread is processing " + bundle.getBundleId());

+                try {

+                    startManagementFor(bundle);

+                } catch (Throwable e) {

+                    // To be sure to not kill the thread, we catch all exceptions and errors

+                    m_logger.log(Logger.ERROR, "An errors occurs when analyzing the content or starting the management of " + bundle.getBundleId());

+                }

+            }

+        }

+

+    }

 

 }

diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
index a967e88..03402c3 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
@@ -127,7 +127,7 @@
         m_context = context;

         m_componentMetadata = metadata;

         m_factoryName = getFactoryName();

-        String fac = metadata.getAttribute("factory");

+        String fac = metadata.getAttribute("public");

         m_isPublic = fac == null || !fac.equalsIgnoreCase("false");

         m_logger = new Logger(m_context, m_factoryName);

         m_requiredHandlers = getRequiredHandlerList(); // Call sub-class to get the list of required handlers.

diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index 173f2fa..e006eb9 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -520,7 +520,7 @@
     }
 
     /**
-     * Create an instance of the component. This method need to be called one time only for singleton provided service
+     * Create an instance of the component. This method need to be called one time only for singleton provided service.
      * @return a new instance
      */
     public Object createPojoObject() {
@@ -560,6 +560,7 @@
                 m_factory.getLogger().log(Logger.ERROR,
                                           "[" + m_name + "] createInstance -> The POJO constructor is not accessible : " + e.getMessage());
                 stop();
+                return null;
             } catch (SecurityException e) {
                 m_factory.getLogger().log(
                                           Logger.ERROR,
@@ -568,27 +569,28 @@
                                                   + "] createInstance -> The Component Instance is not accessible (security reason) : "
                                                   + e.getMessage());
                 stop();
+                return null;
             } catch (InvocationTargetException e) {
                 m_factory.getLogger().log(
                                           Logger.ERROR,
                                           "["
                                                   + m_name
-                                                  + "] createInstance -> Cannot invoke the constructor method (illegal target) : "
+                                                  + "] createInstance -> Cannot invoke the constructor method - the constructor throws an exception : "
                                                   + e.getTargetException().getMessage());
                 onError(null, m_className, e.getTargetException());
                 stop();
+                return null;
             } catch (NoSuchMethodException e) {
                 m_factory.getLogger().log(Logger.ERROR,
                                           "[" + m_name + "] createInstance -> Cannot invoke the constructor (method not found) : " + e.getMessage());
                 stop();
-            } catch (IllegalArgumentException e) {
+                return null;
+            } catch (Throwable e) {
+                // Catch every other possible error and runtime exception.
                 m_factory.getLogger().log(Logger.ERROR,
-                                          "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage());
+                        "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage());
                 stop();
-            } catch (InstantiationException e) {
-                m_factory.getLogger().log(Logger.ERROR,
-                                          "[" + m_name + "] createInstance -> The POJO constructor invocation failed : " + e.getMessage());
-                stop();
+                return null;
             }
         } else {
             try {
@@ -622,6 +624,7 @@
                                                           + "] createInstance -> Cannot invoke the factory-method (method not found) : "
                                                           + e2.getMessage());
                         stop();
+                        return null;
                     }
                 }
 
@@ -633,24 +636,13 @@
                 method.invoke(instance, new Object[] { this });
                 onExit(null, m_className, instance);
 
-            } catch (SecurityException e) {
-                // Error : invocation failed
-                m_factory.getLogger().log(Logger.ERROR, "[" + m_name + "] createInstance -> Cannot invoke the factory-method : " + e.getMessage());
-                stop();
-            } catch (IllegalArgumentException e) {
-                // Error : arguments mismatch
-                m_factory.getLogger().log(Logger.ERROR, "[" + m_name + "] createInstance -> Cannot invoke the factory-method : " + e.getMessage());
-                stop();
-            } catch (IllegalAccessException e) {
-                // Error : illegal access
-                m_factory.getLogger().log(Logger.ERROR, "[" + m_name + "] createInstance -> Cannot invoke the factory-method : " + e.getMessage());
-                stop();
             } catch (InvocationTargetException e) {
                 // Error : invocation failed
                 m_factory.getLogger().log(Logger.ERROR,
-                                          "[" + m_name + "] createInstance -> The factory-method returns an exception : " + e.getTargetException());
+                                          "[" + m_name + "] createInstance -> The factory-method throws an exception : " + e.getTargetException());
                 onError(null, m_className, e.getTargetException());
                 stop();
+                return null;
             } catch (NoSuchMethodException e) {
                 // Error : _setInstanceManager method is missing
                 m_factory.getLogger()
@@ -661,11 +653,15 @@
                                      + "] createInstance -> Cannot invoke the factory-method (the _setInstanceManager method does not exist) : "
                                      + e.getMessage());
                 stop();
+                return null;
+            } catch (Throwable e) {
+                // Catch every other possible error and runtime exception.
+                m_factory.getLogger().log(Logger.ERROR,
+                        "[" + m_name + "] createInstance -> The factory-method invocation failed : " + e.getMessage());
+                stop();
+                return null;
             }
-
         }
-        
-        
 
         // Add the new instance in the instance list.
         synchronized (this) {
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index 9020820..8d0f2ba 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -310,8 +310,10 @@
                         // A NoClassDefFoundError is thrown if the specification uses a class not accessible by the actual instance.
                         // It generally comes from a missing import.
                         throw new IllegalStateException("Cannot create the Nullable object, a referenced class cannot be loaded: " + e.getMessage());
+                    } catch (Throwable e) { // Catch any other exception that can occurs
+                        throw new IllegalStateException("Cannot create the Nullable object, an unexpected error occurs: " + e.getMessage());
                     }
-                }
+                 }
             } else {
                 // Create the default-implementation object.
                 try {
@@ -323,6 +325,8 @@
                     throw new IllegalStateException("Cannot load the default-implementation " + m_di + " : " + e.getMessage());
                 } catch (ClassNotFoundException e) {
                     throw new IllegalStateException("Cannot load the default-implementation " + m_di + " : " + e.getMessage());
+                } catch(Throwable e) { // Catch any other exception
+                    throw new IllegalStateException("Cannot load the default-implementation (unexpected exception) " + m_di + " : " + e.getMessage());
                 }
             }
         }