Fix https://issues.apache.org/jira/browse/FELIX-3948
Provide the new extender model.
This work was mostly done by Guillaume Sauthier.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1453391 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/arch-gogo/pom.xml b/ipojo/arch-gogo/pom.xml
index afaad27..8d45776 100644
--- a/ipojo/arch-gogo/pom.xml
+++ b/ipojo/arch-gogo/pom.xml
@@ -40,7 +40,7 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.ipojo</artifactId>
- <version>1.6.2</version>
+ <version>1.9.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
diff --git a/ipojo/arch-gogo/src/main/java/org/apache/felix/ipojo/arch/gogo/Arch.java b/ipojo/arch-gogo/src/main/java/org/apache/felix/ipojo/arch/gogo/Arch.java
index ee75edd..6fb6d5e 100644
--- a/ipojo/arch-gogo/src/main/java/org/apache/felix/ipojo/arch/gogo/Arch.java
+++ b/ipojo/arch-gogo/src/main/java/org/apache/felix/ipojo/arch/gogo/Arch.java
@@ -19,6 +19,7 @@
package org.apache.felix.ipojo.arch.gogo;
import java.io.PrintStream;
+import java.util.Dictionary;
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.Factory;
@@ -30,6 +31,9 @@
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.apache.felix.ipojo.architecture.Architecture;
import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.apache.felix.ipojo.extender.TypeDeclaration;
import org.apache.felix.service.command.Descriptor;
/**
* iPOJO Arch command giving information about the current
@@ -56,7 +60,8 @@
"instance",
"factory",
"factories",
- "handlers"
+ "handlers",
+ "extensions"
};
/**
@@ -76,7 +81,16 @@
*/
@Requires(optional = true)
private HandlerFactory[] m_handlers;
-
+
+ @Requires(optional = true)
+ private InstanceDeclaration[] m_instances;
+
+ @Requires(optional = true)
+ private TypeDeclaration[] m_types;
+
+ @Requires(optional = true)
+ private ExtensionDeclaration[] m_extensions;
+
/**
* Displays iPOJO instances.
*/
@@ -95,6 +109,15 @@
buffer.append("Instance " + instance.getName() + " -> stopped \n");
}
}
+
+ for (InstanceDeclaration instance : m_instances) {
+ // Only print unbound instances (others already printed above)
+ if (!instance.getStatus().isBound()) {
+ buffer.append("Instance " + name(instance.getConfiguration()) + " of type " + instance.getConfiguration().get("component") + " is not bound.\n");
+ buffer.append(" Reason: " + instance.getStatus().getMessage());
+ buffer.append("\n");
+ }
+ }
if (buffer.length() == 0) {
buffer.append("No instances \n");
@@ -102,7 +125,15 @@
System.out.println(buffer.toString());
}
-
+
+ private String name(Dictionary<String, Object> configuration) {
+ String name = (String) configuration.get("instance.name");
+ if (name == null) {
+ name = "unnamed";
+ }
+ return name;
+ }
+
/**
* Displays the architecture of a specific instance.
* @param instance the instance name
@@ -116,6 +147,17 @@
return;
}
}
+
+ for (InstanceDeclaration instanceDeclaration : m_instances) {
+ if (!instanceDeclaration.getStatus().isBound()) {
+ if (instance.equals(name(instanceDeclaration.getConfiguration()))) {
+ System.out.println("Instance " + instance + " not bound to its factory");
+ System.out.println(" -> " + instanceDeclaration.getStatus().getMessage());
+ return;
+ }
+ }
+ }
+
System.err.println("Instance " + instance + " not found");
}
@@ -140,7 +182,17 @@
found = true;
}
}
-
+
+
+ for (TypeDeclaration type : m_types) {
+ if (!type.getStatus().isBound()) {
+ if (factory.equals(type.getComponentName())) {
+ System.out.println("Factory " + factory + " not bound");
+ System.out.println(" -> " + type.getStatus().getMessage());
+ found = true;
+ }
+ }
+ }
if (! found) {
System.err.println("Factory " + factory + " not found");
}
@@ -159,6 +211,14 @@
buffer.append("Factory " + m_factories[i].getName() + " (INVALID : " + m_factories[i].getMissingHandlers() + ") \n");
}
}
+
+ for (TypeDeclaration type : m_types) {
+ if (!type.getStatus().isBound()) {
+ buffer.append("Factory " + type.getComponentName() + " is not bound\n");
+ buffer.append(" Reason: " + type.getStatus().getMessage());
+ buffer.append("\n");
+ }
+ }
if (buffer.length() == 0) {
buffer.append("No factories \n");
@@ -166,7 +226,7 @@
System.out.println(buffer.toString());
}
-
+
/**
* Displays the list of available handlers.
*/
@@ -184,6 +244,25 @@
out.println("Handler " + name + " (INVALID : " + m_handlers[i].getMissingHandlers() + ")");
}
}
+
+ for (TypeDeclaration type : m_types) {
+ if (!type.getStatus().isBound()) {
+ out.println("HandlerFactory " + type.getComponentName() + " is not bound");
+ out.println(" Reason: " + type.getStatus().getMessage());
+ }
+ }
+ }
+
+ /**
+ * Displays the list of available extensions.
+ */
+ @Descriptor("Display iPOJO extensions")
+ public void extensions() {
+ PrintStream out = System.out;
+ out.println("Available extensions:");
+ for (ExtensionDeclaration extension : m_extensions) {
+ out.println(" * " + extension.getExtensionName());
+ }
}
}
diff --git a/ipojo/runtime/composite-it/src/it/ipojo-composite-import-export-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java b/ipojo/runtime/composite-it/src/it/ipojo-composite-import-export-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
index b242001..1c93fa4 100644
--- a/ipojo/runtime/composite-it/src/it/ipojo-composite-import-export-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
+++ b/ipojo/runtime/composite-it/src/it/ipojo-composite-import-export-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
@@ -21,8 +21,10 @@
import org.ops4j.pax.exam.spi.reactors.PerMethod;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.ow2.chameleon.testing.helpers.IPOJOHelper;
import org.ow2.chameleon.testing.helpers.OSGiHelper;
import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
@@ -129,6 +131,8 @@
}
String version = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION);
System.out.println("OSGi Framework : " + vendor + " - " + version);
+
+ waitForStability(bc);
}
@After
@@ -221,5 +225,75 @@
fail("Assertion failed : " + s);
}
+ /**
+ * Waits for stability:
+ * <ul>
+ * <li>all bundles are activated
+ * <li>service count is stable
+ * </ul>
+ * If the stability can't be reached after a specified time,
+ * the method throws a {@link IllegalStateException}.
+ * @param context the bundle context
+ * @throws IllegalStateException when the stability can't be reach after a several attempts.
+ */
+ private void waitForStability(BundleContext context) throws IllegalStateException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while (!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ System.err.println("Bundle stability isn't reached after 500 tries");
+ throw new IllegalStateException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0;
+ int count2 = 0;
+ while (! serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences((String) null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences((String) null, null);
+ count2 = refs.length;
+ serviceStability = count1 == count2;
+ } catch (Exception e) {
+ System.err.println(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ System.err.println("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ throw new IllegalStateException("Cannot reach the service stability");
+ }
+ }
+
+ /**
+ * Are bundle stables.
+ * @param bc the bundle context
+ * @return <code>true</code> if every bundles are activated.
+ */
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
}
diff --git a/ipojo/runtime/composite-it/src/it/ipojo-composite-instance-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java b/ipojo/runtime/composite-it/src/it/ipojo-composite-instance-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
index b242001..1c93fa4 100644
--- a/ipojo/runtime/composite-it/src/it/ipojo-composite-instance-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
+++ b/ipojo/runtime/composite-it/src/it/ipojo-composite-instance-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
@@ -21,8 +21,10 @@
import org.ops4j.pax.exam.spi.reactors.PerMethod;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.ow2.chameleon.testing.helpers.IPOJOHelper;
import org.ow2.chameleon.testing.helpers.OSGiHelper;
import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
@@ -129,6 +131,8 @@
}
String version = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION);
System.out.println("OSGi Framework : " + vendor + " - " + version);
+
+ waitForStability(bc);
}
@After
@@ -221,5 +225,75 @@
fail("Assertion failed : " + s);
}
+ /**
+ * Waits for stability:
+ * <ul>
+ * <li>all bundles are activated
+ * <li>service count is stable
+ * </ul>
+ * If the stability can't be reached after a specified time,
+ * the method throws a {@link IllegalStateException}.
+ * @param context the bundle context
+ * @throws IllegalStateException when the stability can't be reach after a several attempts.
+ */
+ private void waitForStability(BundleContext context) throws IllegalStateException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while (!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ System.err.println("Bundle stability isn't reached after 500 tries");
+ throw new IllegalStateException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0;
+ int count2 = 0;
+ while (! serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences((String) null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences((String) null, null);
+ count2 = refs.length;
+ serviceStability = count1 == count2;
+ } catch (Exception e) {
+ System.err.println(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ System.err.println("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ throw new IllegalStateException("Cannot reach the service stability");
+ }
+ }
+
+ /**
+ * Are bundle stables.
+ * @param bc the bundle context
+ * @return <code>true</code> if every bundles are activated.
+ */
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-annotations-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/annotations/Common.java b/ipojo/runtime/core-it/src/it/ipojo-core-annotations-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/annotations/Common.java
index a4c431b..c83ad2c 100644
--- a/ipojo/runtime/core-it/src/it/ipojo-core-annotations-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/annotations/Common.java
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-annotations-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/annotations/Common.java
@@ -19,6 +19,7 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.ow2.chameleon.testing.helpers.IPOJOHelper;
import org.ow2.chameleon.testing.helpers.OSGiHelper;
import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
@@ -79,6 +80,8 @@
}
String version = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION);
System.out.println("OSGi Framework : " + vendor + " - " + version);
+
+ waitForStability(bc);
}
@After
@@ -152,4 +155,74 @@
}
}
+ /**
+ * Waits for stability:
+ * <ul>
+ * <li>all bundles are activated
+ * <li>service count is stable
+ * </ul>
+ * If the stability can't be reached after a specified time,
+ * the method throws a {@link IllegalStateException}.
+ * @param context the bundle context
+ * @throws IllegalStateException when the stability can't be reach after a several attempts.
+ */
+ private void waitForStability(BundleContext context) throws IllegalStateException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while (!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ System.err.println("Bundle stability isn't reached after 500 tries");
+ throw new IllegalStateException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0;
+ int count2 = 0;
+ while (! serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences((String) null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences((String) null, null);
+ count2 = refs.length;
+ serviceStability = count1 == count2;
+ } catch (Exception e) {
+ System.err.println(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ System.err.println("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ throw new IllegalStateException("Cannot reach the service stability");
+ }
+ }
+
+ /**
+ * Are bundle stables.
+ * @param bc the bundle context
+ * @return <code>true</code> if every bundles are activated.
+ */
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
}
diff --git a/ipojo/runtime/core-it/src/it/ipojo-core-service-providing-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java b/ipojo/runtime/core-it/src/it/ipojo-core-service-providing-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
index d99d2c6..905b56f 100644
--- a/ipojo/runtime/core-it/src/it/ipojo-core-service-providing-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
+++ b/ipojo/runtime/core-it/src/it/ipojo-core-service-providing-test/src/test/java/org/apache/felix/ipojo/runtime/core/Common.java
@@ -18,8 +18,10 @@
import org.ops4j.pax.exam.spi.reactors.PerMethod;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.ow2.chameleon.testing.helpers.IPOJOHelper;
import org.ow2.chameleon.testing.helpers.OSGiHelper;
import org.ow2.chameleon.testing.tinybundles.ipojo.IPOJOStrategy;
@@ -126,6 +128,8 @@
}
String version = (String) osgiHelper.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION);
System.out.println("OSGi Framework : " + vendor + " - " + version);
+
+ waitForStability(bc);
}
@After
@@ -212,5 +216,75 @@
fail("Assertion failed : " + s);
}
+ /**
+ * Waits for stability:
+ * <ul>
+ * <li>all bundles are activated
+ * <li>service count is stable
+ * </ul>
+ * If the stability can't be reached after a specified time,
+ * the method throws a {@link IllegalStateException}.
+ * @param context the bundle context
+ * @throws IllegalStateException when the stability can't be reach after a several attempts.
+ */
+ private void waitForStability(BundleContext context) throws IllegalStateException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while (!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ System.err.println("Bundle stability isn't reached after 500 tries");
+ throw new IllegalStateException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0;
+ int count2 = 0;
+ while (! serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences((String) null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences((String) null, null);
+ count2 = refs.length;
+ serviceStability = count1 == count2;
+ } catch (Exception e) {
+ System.err.println(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ System.err.println("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ throw new IllegalStateException("Cannot reach the service stability");
+ }
+ }
+
+ /**
+ * Are bundle stables.
+ * @param bc the bundle context
+ * @return <code>true</code> if every bundles are activated.
+ */
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
}
diff --git a/ipojo/runtime/core/pom.xml b/ipojo/runtime/core/pom.xml
index caf523b..90f7a48 100644
--- a/ipojo/runtime/core/pom.xml
+++ b/ipojo/runtime/core/pom.xml
@@ -84,15 +84,24 @@
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <version>3.0</version>
<configuration>
<target>1.5</target>
<source>1.5</source>
+ <testTarget>1.5</testTarget>
+ <testSource>1.5</testSource>
</configuration>
</plugin>
<plugin>
@@ -106,8 +115,7 @@
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
<Bundle-SymbolicName>org.apache.felix.ipojo;singleton:=true</Bundle-SymbolicName>
<Bundle-Description>iPOJO Core Framework</Bundle-Description>
- <Bundle-Activator>org.apache.felix.ipojo.Extender
- </Bundle-Activator>
+ <Bundle-Activator>org.apache.felix.ipojo.extender.internal.Extender</Bundle-Activator>
<Bundle-DocURL>
http://felix.apache.org/site/apache-felix-ipojo.html
</Bundle-DocURL>
@@ -119,6 +127,7 @@
org.osgi.framework;version=1.3, <!-- To support KF 2 -->
org.osgi.service.cm,
org.osgi.service.log,
+ org.osgi.util.tracker;version=1.3,
!sun.io,
!net.sourceforge.cobertura.*, <!-- To support code coverage -->
@@ -136,6 +145,7 @@
org.apache.felix.ipojo.handlers.architecture,
org.apache.felix.ipojo.handlers.lifecycle.callback,
org.apache.felix.ipojo.handlers.lifecycle.controller,
+ org.apache.felix.ipojo.extender.internal*,
META-INF.services.*,
<!-- ASM (Manipulator dependencies) -->
org.objectweb.asm.commons,
@@ -147,6 +157,9 @@
org.apache.felix.ipojo; version="${ipojo.package.version}",
org.apache.felix.ipojo.metadata; version="${ipojo.package.version}",
org.apache.felix.ipojo.architecture; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.extender; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.extender.builder; version="${ipojo.package.version}",
+ org.apache.felix.ipojo.extender.queue; version="${ipojo.package.version}",
org.apache.felix.ipojo.parser; version="${ipojo.package.version}",
org.apache.felix.ipojo.util; version="${ipojo.package.version}",
org.apache.felix.ipojo.handlers.dependency; version="${ipojo.package.version}",
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java
deleted file mode 100644
index ed899a9..0000000
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Extender.java
+++ /dev/null
@@ -1,785 +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.felix.ipojo;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.ipojo.metadata.Element;
-import org.apache.felix.ipojo.parser.ManifestMetadataParser;
-import org.apache.felix.ipojo.parser.ParseException;
-import org.apache.felix.ipojo.parser.ParseUtils;
-import org.apache.felix.ipojo.util.Logger;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.SynchronousBundleListener;
-
-/**
- * iPOJO Extender.
- * This class listens bundle arrivals and departures in order to detect and manage
- * iPOJO powered bundles. This class creates factories and ask for instance creation.
- * @see SynchronousBundleListener
- * @see BundleActivator
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-public class Extender implements SynchronousBundleListener, BundleActivator {
-
- /**
- * Enables the iPOJO internal dispatcher.
- * This internal dispatcher helps the OSGi framework to support large
- * scale applications. The internal dispatcher is disabled by default.
- */
- static boolean DISPATCHER_ENABLED = true;
-
- /**
- * Disables the iPOJO asynchronous processing.
- * When set to false, the bundles are processed in the listener thread
- * making iPOJO usable on Google App Engine. By default, the processing
- * is asynchronous.
- */
- static boolean SYNCHRONOUS_PROCESSING_ENABLED = false;
-
- /**
- * Property allowing to set if the internal dispatcher is enabled or disabled.
- * Possible value are either <code>true</code> or <code>false</code>.
- */
- private static final String ENABLING_DISPATCHER = "ipojo.internal.dispatcher";
-
- /**
- * Property allowing to disable the asynchronous process (and so enables the
- * synchronous processing).
- * Possible value are either <code>true</code> or <code>false</code>.
- */
- private static final String SYNCHRONOUS_PROCESSING = "ipojo.processing.synchronous";
-
- /**
- * iPOJO Component Type and Instance declaration header.
- */
- private static final String IPOJO_HEADER = "iPOJO-Components";
-
- /**
- * iPOJO Component Type and Instance declaration header
- * (alternative).
- * This header was introduced because of BND supporting only header
- * starting with an uppercase.
- */
- private static final String IPOJO_HEADER_ALT = "IPOJO-Components";
-
- /**
- * iPOJO Extension declaration header.
- */
- private static final String IPOJO_EXTENSION = "IPOJO-Extension";
-
- /**
- * The Bundle Context of the iPOJO Core bundle.
- */
- private static BundleContext m_context;
-
- /**
- * The iPOJO Extender logger.
- */
- private Logger m_logger;
-
- /**
- * The instance creator used to create instances.
- * (Singleton)
- */
- private InstanceCreator m_creator;
-
- /**
- * The iPOJO Bundle.
- */
- private Bundle m_bundle;
-
- /**
- * The list of factory types.
- */
- private List m_factoryTypes = new ArrayList();
-
- /**
- * The list of unbound types.
- * A type is unbound if the matching extension is not deployed.
- */
- private final List m_unboundTypes = new ArrayList();
-
- /**
- * The processor analyzing arriving bundles and creating iPOJO contributions.
- */
- private final CreatorThread m_processor = new CreatorThread();
-
- /**
- * Bundle Listener Notification.
- * @param event the bundle event.
- * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
- */
- public void bundleChanged(final BundleEvent event) {
- if (event.getBundle() == m_bundle) { return; }
-
- switch (event.getType()) {
- case BundleEvent.STARTED:
- // Put the bundle in the queue
- m_processor.addBundle(event.getBundle());
- break;
- case BundleEvent.STOPPING:
- m_processor.removeBundle(event.getBundle());
- //TODO Should be done in another thread in the asynchronous case.
- closeManagementFor(event.getBundle());
- break;
- default:
- break;
- }
-
- }
-
- /**
- * Ends the iPOJO Management for the given bundle.
- * Generally the bundle is leaving. This method
- * stops every factories declared is the bundle and
- * disposed every declared instances.
- * @param bundle the bundle.
- */
- 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);
-
- // Look for component type created from this bundle.
- if (mft.m_created != null) {
- List cfs = (List) mft.m_created.remove(bundle);
- for (int i = 0; cfs != null && i < cfs.size(); i++) {
- IPojoFactory factory = (IPojoFactory) cfs.get(i);
- m_creator.removeFactory(factory);
- factory.stop();
- }
- }
-
- // If the leaving bundle has declared mft : destroy all created factories.
- if (mft.m_bundle == bundle) {
- if (mft.m_created != null) {
- Iterator iterator = mft.m_created.keySet().iterator();
- while (iterator.hasNext()) {
- Bundle key = (Bundle) iterator.next();
- List list = (List) mft.m_created.get(key);
- for (int i = 0; i < list.size(); i++) {
- IPojoFactory factory = (IPojoFactory) list.get(i);
- factory.stop();
- m_unboundTypes.add(new UnboundComponentType(mft.m_type, factory.m_componentMetadata, factory.getBundleContext()
- .getBundle()));
- }
- }
- }
- toRemove.add(mft);
- }
- }
-
- for (int i = 0; i < toRemove.size(); i++) {
- ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) toRemove.get(i);
- m_logger.log(Logger.INFO, "The factory type: " + mft.m_type + " is no more available");
- mft.m_bundle = null;
- mft.m_clazz = null;
- mft.m_created = null;
- mft.m_type = null;
- m_factoryTypes.remove(mft);
- }
- }
-
- /**
- * Checks if the given bundle is an iPOJO bundle, and begin
- * the iPOJO management is true.
- * @param bundle the bundle to check.
- */
- private void startManagementFor(Bundle bundle) {
- Dictionary dict = bundle.getHeaders();
- // Check for abstract factory type
- String typeHeader = (String) dict.get(IPOJO_EXTENSION);
- if (typeHeader != null) {
- parseAbstractFactoryType(bundle, typeHeader);
- }
-
- // Check bundle
- String header = (String) dict.get(IPOJO_HEADER);
- // Check the alternative header
- if (header == null) {
- header = (String) dict.get(IPOJO_HEADER_ALT);
- }
-
- if (header != null) {
- try {
- parse(bundle, header);
- } catch (IOException e) {
- m_logger.log(Logger.ERROR, "An exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
- } catch (ParseException e) {
- m_logger.log(Logger.ERROR, "A parse exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
- }
- }
- }
-
- /**
- * Parses an IPOJO-Extension manifest header and then creates
- * iPOJO extensions (factory types).
- * @param bundle the bundle containing the header.
- * @param header the header to parse.
- */
- private void parseAbstractFactoryType(Bundle bundle, String header) {
- String[] arr = ParseUtils.split(header, ",");
- for (int i = 0; arr != null && i < arr.length; i++) {
- String[] arr2 = ParseUtils.split(arr[i], ":");
-
- /*
- * Get the fully qualified type name.
- * type = [namespace] name
- */
- String[] nameparts = ParseUtils.split(arr2[0].trim(), " \t");
- String type = nameparts.length == 1 ? nameparts[0] : nameparts[0]+":"+nameparts[1];
-
- Class clazz;
- try {
- clazz = bundle.loadClass(arr2[1]);
- } catch (ClassNotFoundException e) {
- m_logger.log(Logger.ERROR, "Cannot load the extension " + type, e);
- return;
- }
- ManagedAbstractFactoryType mft = new ManagedAbstractFactoryType(clazz, type, bundle);
- m_factoryTypes.add(mft);
- m_logger.log(Logger.DEBUG, "New factory type available: " + type);
-
- for (int j = m_unboundTypes.size() - 1; j >= 0; j--) {
- UnboundComponentType unbound = (UnboundComponentType) m_unboundTypes.get(j);
- if (unbound.m_type.equals(type)) {
- createAbstractFactory(unbound.m_bundle, unbound.m_description);
- m_unboundTypes.remove(unbound);
- }
- }
- }
- }
-
- /**
- * Parses the internal metadata (from the manifest
- * (in the iPOJO-Components property)). This methods
- * creates factories and add instances to the instance creator.
- * @param bundle the owner bundle.
- * @param components The iPOJO Header String.
- * @throws IOException if the manifest can not be found
- * @throws ParseException if the parsing process failed
- */
- private void parse(Bundle bundle, String components) throws IOException, ParseException {
- ManifestMetadataParser parser = new ManifestMetadataParser();
- parser.parseHeader(components);
-
- // Get the component type declaration
- Element[] metadata = parser.getComponentsMetadata();
- for (int i = 0; i < metadata.length; i++) {
- createAbstractFactory(bundle, metadata[i]);
- }
-
- Dictionary[] instances = parser.getInstances();
- for (int i = 0; instances != null && i < instances.length; i++) {
- m_creator.addInstance(instances[i], bundle.getBundleId());
- }
- }
-
- /**
- * iPOJO Start method.
- * @param context the iPOJO bundle context.
- * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
- */
- public void start(BundleContext context) {
- m_context = context;
- m_bundle = context.getBundle();
- m_creator = new InstanceCreator(context);
-
- m_logger = new Logger(m_context, "IPOJO-Extender");
-
- enablingDispatcher(context, m_logger);
- enablingSynchronousProcessing(context, m_logger);
-
- // Create the dispatcher only if required.
- if (DISPATCHER_ENABLED) {
- EventDispatcher.create(context);
- }
-
- // Begin by initializing core handlers
- startManagementFor(m_bundle);
-
- if (! SYNCHRONOUS_PROCESSING_ENABLED) {
- new Thread(m_processor).start();
- }
-
- synchronized (this) {
- // listen to any changes in bundles.
- m_context.addBundleListener(this);
- // compute already started bundles.
- for (int i = 0; i < context.getBundles().length; i++) {
- if (context.getBundles()[i].getState() == Bundle.ACTIVE) {
- m_processor.addBundle(context.getBundles()[i]); // Bundles are processed in another thread.
- }
- }
- }
-
- m_logger.log(Logger.INFO, "iPOJO Runtime started");
- }
-
- /**
- * Stops the iPOJO Bundle.
- * @param context the bundle context.
- * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
- */
- public void stop(BundleContext context) {
- m_processor.stop(); // Stop the thread processing bundles.
- m_context.removeBundleListener(this);
-
- if (DISPATCHER_ENABLED) {
- EventDispatcher.dispose();
- }
-
- for (int k = 0; k < m_factoryTypes.size(); k++) {
- ManagedAbstractFactoryType mft = (ManagedAbstractFactoryType) m_factoryTypes.get(k);
-
- if (mft.m_created != null) {
- Iterator iterator = mft.m_created.keySet().iterator();
- while (iterator.hasNext()) {
- Bundle key = (Bundle) iterator.next();
- List list = (List) mft.m_created.get(key);
- for (int i = 0; i < list.size(); i++) {
- IPojoFactory factory = (IPojoFactory) list.get(i);
- m_creator.removeFactory(factory);
- factory.dispose();
- }
- }
- }
- }
-
- m_factoryTypes = null;
- m_creator = null;
-
- m_logger.log(Logger.INFO, "iPOJO Runtime stopped");
- m_context = null;
- }
-
- /**
- * Gets iPOJO bundle context.
- * @return the iPOJO Bundle Context
- */
- public static BundleContext getIPOJOBundleContext() {
- return m_context;
- }
-
- /**
- * Enables or disables the internal dispatcher, so sets the
- * {@link Extender#DISPATCHER_ENABLED} flag.
- * This method checks if the {@link Extender#ENABLING_DISPATCHER}
- * property is set to <code>true</code>. Otherwise, the internal
- * dispatcher is disabled. The property can be set as a system
- * property (<code>ipojo.internal.dispatcher</code>) or inside the
- * iPOJO bundle manifest (<code>ipojo-internal-dispatcher</code>).
- * @param context the bundle context.
- * @param logger the logger to indicates if the internal dispatcher is set.
- */
- private static void enablingDispatcher(BundleContext context, Logger logger) {
- // First check in the framework and in the system properties
- String flag = context.getProperty(ENABLING_DISPATCHER);
-
- // If null, look in bundle manifest
- if (flag == null) {
- String key = ENABLING_DISPATCHER.replace('.', '-');
- flag = (String) context.getBundle().getHeaders().get(key);
- }
-
- if (flag != null) {
- if (flag.equalsIgnoreCase("true")) {
- Extender.DISPATCHER_ENABLED = true;
- logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher enables");
- return;
- }
- }
-
- // Either l is null, or the specified value was false
- Extender.DISPATCHER_ENABLED = false;
- logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher disables");
-
- }
-
- /**
- * Enables or disables the asynchronous processing, so sets the
- * {@link Extender#SYNCHRONOUS_PROCESSING_ENABLED} flag.
- * Disabling asynchronous processing avoids iPOJO to create a new
- * thread to process bundles. So, iPOJO can be used on the
- * Google App Engine.
- * This method checks if the {@link Extender#SYNCHRONOUS_PROCESSING}
- * property is set to <code>true</code>. Otherwise, asynchronous processing
- * is used (default). The property can be set as a system
- * property (<code>ipojo.processing.synchronous</code>) or inside the
- * iPOJO bundle manifest.
- * @param context the bundle context.
- * @param logger the logger to indicates if the internal dispatcher is set.
- */
- private static void enablingSynchronousProcessing(BundleContext context, Logger logger) {
- String flag = context.getProperty(SYNCHRONOUS_PROCESSING);
-
- // If null, look in bundle manifest
- if (flag == null) {
- String key = SYNCHRONOUS_PROCESSING.replace('.', '-');
- flag = (String) context.getBundle().getHeaders().get(key);
- }
-
- if (flag != null) {
- if (flag.equalsIgnoreCase("true")) {
- Extender.SYNCHRONOUS_PROCESSING_ENABLED = true;
- logger.log(Logger.INFO, "iPOJO Asynchronous processing disabled");
- return;
- }
- }
-
- // Either l is null, or the specified value was false
- Extender.SYNCHRONOUS_PROCESSING_ENABLED = false;
- logger.log(Logger.INFO, "iPOJO synchrnous processing disables");
-
- }
-
- /**
- * Adds a component factory to the factory list.
- * @param metadata the new component metadata.
- * @param bundle the bundle.
- */
- private void createAbstractFactory(Bundle bundle, Element metadata) {
- ManagedAbstractFactoryType factoryType = null;
- // First, look for factory-type (component, handler, composite ...)
-
- // TODO : Should Element.getQualifiedName() be public ?
- String typeName = metadata.getNameSpace() == null ? metadata.getName() : metadata.getNameSpace()+":"+metadata.getName();
-
- for (int i = 0; i < m_factoryTypes.size(); i++) {
- ManagedAbstractFactoryType type = (ManagedAbstractFactoryType) m_factoryTypes.get(i);
- if (type.m_type.equals(typeName)) {
- factoryType = type;
- break;
- }
- }
-
- // If not found, return. It will wait for a new component type factory.
- if (factoryType == null) {
- m_logger.log(Logger.WARNING, "Type of component not available: " + typeName);
- m_unboundTypes.add(new UnboundComponentType(typeName, metadata, bundle));
- return;
- }
-
- // Once found, we invoke the AbstractFactory constructor to create the component factory.
- Class clazz = factoryType.m_clazz;
- try {
- // Look for the constructor, and invoke it.
- Constructor cst = clazz.getConstructor(new Class[] { BundleContext.class, Element.class });
- IPojoFactory factory = (IPojoFactory) cst.newInstance(new Object[] { getBundleContext(bundle), metadata });
-
- // Add the created factory in the m_createdFactories map.
- if (factoryType.m_created == null) {
- factoryType.m_created = new HashMap();
- List list = new ArrayList();
- list.add(factory);
- factoryType.m_created.put(bundle, list);
- } else {
- List list = (List) factoryType.m_created.get(bundle);
- if (list == null) {
- list = new ArrayList();
- list.add(factory);
- factoryType.m_created.put(bundle, list);
- } else {
- list.add(factory);
- }
- }
-
- // Start the created factory.
- factory.start();
- // Then add the factory to the instance creator.
- m_creator.addFactory(factory);
-
- } catch (SecurityException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
- } catch (NoSuchMethodException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName() + ": the given class constructor cannot be found");
- } catch (IllegalArgumentException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
- } catch (InstantiationException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
- } catch (IllegalAccessException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
- } catch (InvocationTargetException e) {
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e.getTargetException());
- } catch(Throwable e) {
- // Intercepts everything else to avoid complete crash.
- m_logger.log(Logger.ERROR, "Cannot instantiate an abstract factory from " + clazz.getName(), e);
- }
- }
-
- /**
- * Structure storing an iPOJO extension.
- */
- private static final class ManagedAbstractFactoryType {
- /**
- * The type (i.e.) name of the extension.
- */
- String m_type;
-
- /**
- * The abstract Factory class.
- */
- Class m_clazz;
-
- /**
- * The bundle object containing the declaration of the extension.
- */
- Bundle m_bundle;
-
- /**
- * The factories created by this extension.
- */
- private Map m_created;
-
- /**
- * Creates a ManagedAbstractFactoryType.
- * @param factory the abstract factory class.
- * @param type the name of the extension.
- * @param bundle the bundle declaring the extension.
- */
- protected ManagedAbstractFactoryType(Class factory, String type, Bundle bundle) {
- m_bundle = bundle;
- m_clazz = factory;
- m_type = type;
- }
- }
-
- /**
- * Structure storing unbound component type declarations.
- * Unbound means that there is no extension able to manage the extension.
- */
- private static final class UnboundComponentType {
- /**
- * The component type description.
- */
- private final Element m_description;
-
- /**
- * The bundle declaring this type.
- */
- private final Bundle m_bundle;
-
- /**
- * The required extension name.
- */
- private final String m_type;
-
- /**
- * Creates a UnboundComponentType.
- * @param description the description of the component type.
- * @param bundle the bundle declaring this type.
- * @param type the required extension name.
- */
- protected UnboundComponentType(String type, Element description, Bundle bundle) {
- m_type = type;
- m_description = description;
- m_bundle = bundle;
- }
- }
-
- /**
- * Computes the bundle context from the bundle class by introspection.
- * @param bundle the bundle.
- * @return the bundle context object or <code>null</code> if not found.
- */
- public BundleContext getBundleContext(Bundle bundle) {
- if (bundle == null) { return null; }
-
- // getBundleContext (OSGi 4.1)
- Method meth = null;
- try {
- meth = bundle.getClass().getMethod("getBundleContext", new Class[0]); // This method is public and is specified in the Bundle interface.
- } catch (SecurityException e) {
- // Nothing do to, will try the Equinox method
- } catch (NoSuchMethodException e) {
- // Nothing do to, will try the Equinox method
- }
-
- // try Equinox getContext if not found.
- if (meth == null) {
- try {
- meth = bundle.getClass().getMethod("getContext", new Class[0]);
- } catch (SecurityException e) {
- // Nothing do to, will try field inspection
- } catch (NoSuchMethodException e) {
- // Nothing do to, will try field inspection
- }
- }
-
- if (meth != null) {
- if (! meth.isAccessible()) {
- // If not accessible, try to set the accessibility.
- meth.setAccessible(true);
- }
- try {
- return (BundleContext) meth.invoke(bundle, new Object[0]);
- } catch (IllegalArgumentException e) {
- m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e);
- return null;
- } catch (IllegalAccessException e) {
- m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e);
- return null;
- } catch (InvocationTargetException e) {
- m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + meth.getName(), e.getTargetException());
- return null;
- }
- }
-
- // Else : Field inspection (KF and Prosyst)
- Field[] fields = bundle.getClass().getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- if (BundleContext.class.isAssignableFrom(fields[i].getType())) {
- if (!fields[i].isAccessible()) {
- fields[i].setAccessible(true);
- }
- try {
- return (BundleContext) fields[i].get(bundle);
- } catch (IllegalArgumentException e) {
- m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + fields[i].getName(), e);
- return null;
- } catch (IllegalAccessException e) {
- m_logger.log(Logger.ERROR, "Cannot get the BundleContext by invoking " + fields[i].getName(), e);
- return null;
- }
- }
- }
- m_logger.log(Logger.ERROR, "Cannot find the BundleContext for " + bundle.getSymbolicName(), null);
- return null;
- }
-
-
- /**
- * The creator thread analyzes arriving bundles to create iPOJO contribution.
- */
- private class CreatorThread implements Runnable {
-
- /**
- * Is the creator thread started?
- */
- private boolean m_started = true;
-
- /**
- * The list of bundle that are going to be analyzed.
- */
- private List m_bundles = new ArrayList();
-
- /**
- * A bundle is arriving.
- * This method is synchronized to avoid concurrent modification of the waiting list.
- * @param bundle the new bundle
- */
- public synchronized void addBundle(Bundle bundle) {
- if (SYNCHRONOUS_PROCESSING_ENABLED) {
- m_logger.log(Logger.DEBUG, "Analyzing " + bundle.getBundleId());
- startManagementFor(bundle);
- } else {
- // Asynchronous case, we add the bundle to the queue
- m_bundles.add(bundle);
- notifyAll(); // Notify the thread to force the process.
- m_logger.log(Logger.DEBUG, "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);
- }
-
- /**
- * Stops the creator thread.
- */
- public synchronized void stop() {
- m_started = false;
- m_bundles.clear();
- notifyAll();
- }
-
- /**
- * Creator thread's run method.
- * While the list is not empty, the thread launches the bundle analyzing on the next bundle.
- * When the list is empty, the thread sleeps until the arrival of a new bundle
- * or until iPOJO stops.
- * @see java.lang.Runnable#run()
- */
- public void run() {
- m_logger.log(Logger.DEBUG, "Creator thread is starting");
- boolean started;
- synchronized (this) {
- started = m_started;
- }
- while (started) {
- Bundle bundle;
- synchronized (this) {
- while (m_started && m_bundles.isEmpty()) {
- try {
- m_logger.log(Logger.DEBUG, "Creator thread is waiting - Nothing to do");
- wait();
- } catch (InterruptedException e) {
- // Interruption, re-check the condition
- }
- }
- if (!m_started) {
- m_logger.log(Logger.DEBUG, "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.DEBUG, "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 error occurs when analyzing the content or starting the management of " + bundle.getBundleId(), e);
- }
- synchronized (this) {
- started = m_started;
- }
- }
- }
-
- }
-
-}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
index f18c1d3..8ffc9de 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/Factory.java
@@ -44,6 +44,17 @@
*/
int INVALID = 0;
+
+ /**
+ * Instance configuration can set the instance name using this property.
+ */
+ String INSTANCE_NAME_PROPERTY = "instance.name";
+
+ /**
+ * Instance configuration can set the factory version they target using this property.
+ */
+ String FACTORY_VERSION_PROPERTY = "factory.version";
+
/**
* Creates an instance manager (i.e. component type instance).
* @param configuration the configuration properties for this component.
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
index ade4045..0d13551 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/IPojoFactory.java
@@ -18,18 +18,9 @@
*/
package org.apache.felix.ipojo;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.extender.internal.Extender;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.util.Logger;
import org.apache.felix.ipojo.util.SecurityHelper;
@@ -39,6 +30,8 @@
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ManagedServiceFactory;
+import java.util.*;
+
/**
* This class defines common mechanisms of iPOJO component factories
* (i.e. component type).
@@ -276,12 +269,12 @@
// Find name in the configuration
String name;
- if (configuration.get("instance.name") == null && configuration.get("name") == null) {
+ if (configuration.get(Factory.INSTANCE_NAME_PROPERTY) == null && configuration.get("name") == null) {
// No name provided
name = null;
} else {
// Support both instance.name & name
- name = (String) configuration.get("instance.name");
+ name = (String) configuration.get(Factory.INSTANCE_NAME_PROPERTY);
if (name == null) {
name = (String) configuration.get("name");
getLogger().log(Logger.WARNING, "The 'name' (" + name + ") attribute, used as the instance name, is deprecated, please use the 'instance.name' attribute");
@@ -292,9 +285,9 @@
// Generate a unique name if required and verify uniqueness
// We extract the version from the configuration because it may help to compute a unique name by appending
// the version to the given name.
- String version = (String) configuration.get("factory.version");
+ String version = (String) configuration.get(Factory.FACTORY_VERSION_PROPERTY);
name = m_generator.generate(name, version);
- configuration.put("instance.name", name);
+ configuration.put(Factory.INSTANCE_NAME_PROPERTY, name);
// Here we are sure to be valid until the end of the method.
HandlerManager[] handlers = new HandlerManager[m_requiredHandlers.size()];
@@ -478,11 +471,11 @@
* @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
*/
public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException {
- if (properties == null || (properties.get("instance.name") == null && properties.get("name") == null)) { // Support both instance.name and name
+ if (properties == null || (properties.get(Factory.INSTANCE_NAME_PROPERTY) == null && properties.get("name") == null)) { // Support both instance.name and name
throw new UnacceptableConfiguration("The configuration does not contains the \"instance.name\" property");
}
- String name = (String) properties.get("instance.name");
+ String name = (String) properties.get(Factory.INSTANCE_NAME_PROPERTY);
if (name == null) {
name = (String) properties.get("name");
}
@@ -653,7 +646,7 @@
if (instance == null) {
try {
- properties.put("instance.name", name); // Add the name in the configuration
+ properties.put(Factory.INSTANCE_NAME_PROPERTY, name); // Add the name in the configuration
// If an instance with this name was created before, this creation will failed.
createComponentInstance(properties);
} catch (UnacceptableConfiguration e) {
@@ -668,7 +661,7 @@
}
} else {
try {
- properties.put("instance.name", name); // Add the name in the configuration
+ properties.put(Factory.INSTANCE_NAME_PROPERTY, name); // Add the name in the configuration
reconfigure(properties); // re-configure the component
} catch (UnacceptableConfiguration e) {
m_logger.log(Logger.ERROR, "The configuration is not acceptable : " + e.getMessage());
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
deleted file mode 100644
index d66e301..0000000
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceCreator.java
+++ /dev/null
@@ -1,381 +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.felix.ipojo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.ipojo.util.Logger;
-import org.osgi.framework.BundleContext;
-
-/**
- * The instance creator creates instances and tracks their factories.
- * It allows creating instances from external factories.
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-public class InstanceCreator implements FactoryStateListener {
-
- /**
- * The logger to log messages if errors occur.
- */
- private Logger m_logger;
-
- /**
- * The configurations to create and to maintain.
- */
- private List m_idle = new ArrayList();
-
- /**
- * The map storing created instances.
- * This map contains [AbstractFactory, List [ManagedInstance]] couples.
- */
- private Map m_attached = new HashMap();
-
- /**
- * The abstract factory list.
- */
- private List m_factories = new ArrayList();
-
- /**
- * Creates the instance creator.
- * This object is generally a singleton.
- * @param context the bundle context of the iPOJO bundle.
- */
- public InstanceCreator(BundleContext context) {
- m_logger = new Logger(context, "iPOJO Instance Creator");
- }
-
- /**
- * Adds an instance to manage.
- * @param instance the instance configuration
- * @param bundle the bundle id declaring the instance
- */
- synchronized void addInstance(Dictionary instance, long bundle) {
- if (instance.get("factory.version") != null) {
- m_logger.log(Logger.DEBUG, "New instance to managed, looking for " + instance.get("component") + "-" + instance.get("factory.version"));
- } else {
- m_logger.log(Logger.DEBUG, "New instance to managed, looking for " + instance.get("component"));
- }
-
- ManagedInstance managed = new ManagedInstance(instance, bundle);
- for (int i = 0; i < m_factories.size(); i++) {
- IPojoFactory factory = (IPojoFactory) m_factories.get(i);
- if (managed.matchNameAndVersion(factory)) {
- // Subscribe to the factory state change
- m_logger.log(Logger.DEBUG, "Listen factory " + factory.getName() + " events");
- factory.addFactoryStateListener(this);
- if (factory.getState() == Factory.VALID && managed.match(factory)) {
- managed.create(factory);
- List list = (List) m_attached.get(factory);
- if (list == null) {
- list = new ArrayList();
- list.add(managed);
- m_attached.put(factory, list);
- } else {
- list.add(managed);
- }
- return;
- }
- }
- }
- // If there is no matching factory, add the instance to the idle list
- m_idle.add(managed);
- }
-
- /**
- * Disposes all instances declared by the given (leaving) bundle.
- * @param bundle the bundle.
- */
- void removeInstancesFromBundle(long bundle) {
- // Disposes instance from attached instances
- Map copy = null;
-
- // To avoid concurrent modification exception, we create a copy.
- synchronized (this) {
- copy = new HashMap(m_attached);
- }
-
- Collection col = copy.keySet();
- Iterator iterator = col.iterator();
- List instanceToRemove = new ArrayList();
- List factoryToRemove = new ArrayList();
- while (iterator.hasNext()) {
- IPojoFactory factory = (IPojoFactory) iterator.next();
- List list = (List) copy.get(factory);
- for (int i = 0; i < list.size(); i++) {
- ManagedInstance managed = (ManagedInstance) list.get(i);
- if (managed.m_bundleId == bundle) {
- managed.dispose();
- instanceToRemove.add(managed);
- }
- }
- if (!instanceToRemove.isEmpty()) {
- list.removeAll(instanceToRemove);
- if (list.isEmpty()) {
- factory.removeFactoryStateListener(this);
- factoryToRemove.add(factory);
- }
- }
- }
-
- // We remove from the original map
- for (int i = 0; i < factoryToRemove.size(); i++) {
- m_attached.remove(factoryToRemove.get(i));
- }
-
- // Delete idle instances
- List list = null;
- synchronized (this) {
- list = new ArrayList(m_idle);
- }
- instanceToRemove.clear();
- for (int i = 0; i < list.size(); i++) {
- ManagedInstance managed = (ManagedInstance) list.get(i);
- if (managed.m_bundleId == bundle) {
- instanceToRemove.add(managed);
- }
- }
- m_idle.removeAll(instanceToRemove);
- }
-
- /**
- * This method is called when a factory appears.
- * @param factory the new factory.
- */
- public synchronized void addFactory(IPojoFactory factory) {
- List createdInstances = new ArrayList(1);
- m_logger.log(Logger.DEBUG, "Add the factory " + factory.getName());
- m_factories.add(factory);
- for (int i = 0; i < m_idle.size(); i++) {
- ManagedInstance managed = (ManagedInstance) m_idle.get(i);
- if (managed.matchNameAndVersion(factory)) {
- // We have to subscribe to the factory.
- factory.addFactoryStateListener(this);
- if (factory.getState() == Factory.VALID && managed.match(factory)) {
- managed.create(factory);
- List list = (List) m_attached.get(factory);
- if (list == null) {
- list = new ArrayList();
- list.add(managed);
- m_attached.put(factory, list);
- } else {
- list.add(managed);
- }
- createdInstances.add(managed);
- }
- }
- }
- if (!createdInstances.isEmpty()) {
- m_idle.removeAll(createdInstances);
- }
- }
-
- /**
- * This method is called when a factory is leaving.
- * @param factory the leaving factory
- */
- void removeFactory(IPojoFactory factory) {
- factory.removeFactoryStateListener(this);
- m_factories.remove(factory);
- onInvalidation(factory);
- m_attached.remove(factory);
- }
-
- /**
- * This method is called when the given factory becomes valid.
- * @param factory the factory becoming valid.
- */
- private void onValidation(IPojoFactory factory) {
- List toRemove = new ArrayList();
- for (int i = 0; i < m_idle.size(); i++) {
- ManagedInstance managed = (ManagedInstance) m_idle.get(i);
- if (managed.match(factory)) {
- managed.create(factory);
- List list = (List) m_attached.get(factory);
- if (list == null) {
- list = new ArrayList();
- list.add(managed);
- m_attached.put(factory, list);
- } else {
- list.add(managed);
- }
- toRemove.add(managed);
- }
- }
- if (!toRemove.isEmpty()) {
- m_idle.removeAll(toRemove);
- }
- }
-
- /**
- * This method is called when the given factory becomes invalid.
- * @param factory the factory becoming invalid.
- */
- private void onInvalidation(IPojoFactory factory) {
- List instances = (List) m_attached.remove(factory);
- if (instances != null) {
- for (int i = 0; i < instances.size(); i++) {
- ManagedInstance managed = (ManagedInstance) instances.get(i);
- managed.dispose();
- m_idle.add(managed);
- }
- }
- }
-
- /**
- * This method is called when the state of a factory changes.
- * @param factory the factory.
- * @param newState the new state.
- * @see org.apache.felix.ipojo.FactoryStateListener#stateChanged(org.apache.felix.ipojo.Factory, int)
- */
- public void stateChanged(Factory factory, int newState) {
- if (newState == Factory.VALID) {
- m_logger.log(Logger.DEBUG, "A factory is becoming valid : " + factory.getName());
- onValidation((IPojoFactory) factory);
- } else {
- m_logger.log(Logger.DEBUG, "A factory is becoming invalid : " + factory.getName());
- onInvalidation((IPojoFactory) factory);
- }
- }
-
- /**
- * This structure aims to manage a configuration.
- * It stores all necessary information to create an instance
- * and to track the factory.
- */
- private class ManagedInstance {
- /**
- * The configuration of the instance to create.
- */
- private Dictionary m_configuration;
-
- /**
- * The bundle which creates the instance.
- */
- private long m_bundleId;
-
- /**
- * The factory used to create the instance.
- */
- private IPojoFactory m_factory;
-
- /**
- * The created instance.
- */
- private ComponentInstance m_instance;
-
- /**
- * Creates a ManagedInstance.
- * @param conf the configuration to create.
- * @param bundle the bundle in which the instance is declared.
- */
- ManagedInstance(Dictionary conf, long bundle) {
- m_configuration = conf;
- m_bundleId = bundle;
- }
-
- /**
- * Checks if the required factory name match with the given factory.
- * This methods checks only the name, and not the configuration.
- * @param factory the factory to test
- * @return <code>true</code> if the factory name and the version (if set) match, <code>false</code>
- * otherwise.
- */
- public boolean matchNameAndVersion(IPojoFactory factory) {
- String component = (String) m_configuration.get("component");
- String v = (String) m_configuration.get("factory.version");
- if (v == null) {
- return factory.getName().equals(component) || factory.getClassName().equalsIgnoreCase(component);
- } else {
- return (factory.getName().equals(component) || factory.getClassName().equalsIgnoreCase(component))
- && v.equals(factory.getVersion());
- }
- }
-
- /**
- * Checks if the given factory match with the factory
- * required by this instance. A factory matches if its
- * name or its class name is equals to the 'component'
- * property of the instance. Then the acceptability of
- * the configuration is checked.
- * @param factory the factory to confront against the current instance.
- * @return <code>true</code> if the factory matches.
- */
- public boolean match(IPojoFactory factory) {
- // Test factory name (and classname)
- if (matchNameAndVersion(factory)) {
- // Test factory accessibility
- if (factory.m_isPublic || factory.getBundleContext().getBundle().getBundleId() == m_bundleId) {
- // Test the configuration validity.
- try {
- factory.checkAcceptability(m_configuration);
- return true;
- } catch (UnacceptableConfiguration e) {
- m_logger.log(Logger.ERROR, "An instance can be bound to a matching factory, however the configuration seems unacceptable : "
- + e.getMessage());
- return false;
- } catch (MissingHandlerException e) {
- m_logger.log(Logger.ERROR, "An instance can be bound to a matching factory, but this factory cannot be used : "
- + e.getMessage());
- return false;
- }
- }
- }
- return false;
- }
-
- /**
- * Creates the instance by using the given factory.
- * @param factory the factory to use to create the instance. The factory must match.
- */
- public void create(IPojoFactory factory) {
- try {
- m_factory = factory;
- m_instance = m_factory.createComponentInstance(m_configuration);
- m_logger.log(Logger.INFO, "Instance created");
- } catch (UnacceptableConfiguration e) {
- m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
- + e.getMessage());
- } catch (MissingHandlerException e) {
- m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
- + e.getMessage());
- } catch (ConfigurationException e) {
- m_logger.log(Logger.ERROR, "A matching factory was found for " + m_configuration + ", but the instantiation failed : "
- + e.getMessage());
- }
- }
-
- /**
- * Disposes the current instance if not <code>null</code>.
- */
- public void dispose() {
- if (m_instance != null) {
- m_instance.dispose();
- }
- m_instance = null;
- m_factory = null;
- }
- }
-
-}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index 1ea6cdc..23f8c31 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -186,7 +186,7 @@
m_className = metadata.getAttribute("classname");
// Add the name
- m_name = (String) configuration.get("instance.name");
+ m_name = (String) configuration.get(Factory.INSTANCE_NAME_PROPERTY);
// Check if an object is injected in the instance
Object obj = configuration.get("instance.object");
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java
index 2a7dc4a..a2d02d6 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/ComponentTypeDescription.java
@@ -214,7 +214,7 @@
// Add the version if set
String v = getVersion();
if (v != null) {
- props.put("factory.version", v);
+ props.put(Factory.FACTORY_VERSION_PROPERTY, v);
}
props.put("component.providedServiceSpecifications", m_providedServiceSpecification);
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Declaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Declaration.java
new file mode 100644
index 0000000..e22a1d2
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Declaration.java
@@ -0,0 +1,57 @@
+/*
+ * 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.felix.ipojo.extender;
+
+/**
+ * A declaration is a creation instruction of an entity (Component type, Factory, Instance...).
+ * All declarations are exposed as services. <em>Processors</em> are tracking the adequate declaration type and create
+ * the entity.
+ * <p/>
+ * Declaration can be <em>bound</em> or <em>unbound</em> whether they are fulfilled. When they are unbound,
+ * a message or/and an error can be set.
+ */
+public interface Declaration {
+ /**
+ * Gets the declaration status.
+ *
+ * @return the current status. As Status are immutable, it returns a new object every time.
+ */
+ Status getStatus();
+
+ /**
+ * Marks the declaration bound.
+ */
+ void bind();
+
+ /**
+ * Unbinds the declaration.
+ *
+ * @param message an explanation
+ */
+ void unbind(String message);
+
+ /**
+ * Unbinds the declaration
+ *
+ * @param message an explanation
+ * @param throwable an error
+ */
+ void unbind(String message, Throwable throwable);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ExtensionDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ExtensionDeclaration.java
new file mode 100644
index 0000000..2a1f5c3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/ExtensionDeclaration.java
@@ -0,0 +1,48 @@
+/*
+ * 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.felix.ipojo.extender;
+
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+
+/**
+ * iPOJO's extension declaration.
+ * This service interface is published to instruct the extender to create a new iPOJO extension (like composite or
+ * handler).
+ */
+public interface ExtensionDeclaration extends Declaration {
+ /**
+ * The service property specifying the extension name.
+ */
+ String EXTENSION_NAME_PROPERTY = "ipojo.extension.name";
+
+ /**
+ * Gets the factory builder to use to create the factories bound to this extension.
+ *
+ * @return the factory builder.
+ */
+ FactoryBuilder getFactoryBuilder();
+
+ /**
+ * Gets the extension name. This name must be unique.
+ *
+ * @return the extension name.
+ */
+ String getExtensionName();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceDeclaration.java
new file mode 100644
index 0000000..1d804a0
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/InstanceDeclaration.java
@@ -0,0 +1,71 @@
+/*
+ * 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.felix.ipojo.extender;
+
+import java.util.Dictionary;
+
+/**
+ * Service published to instruct an instance creation.
+ */
+public interface InstanceDeclaration extends Declaration {
+ /**
+ * Service property specifying the component type's name.
+ */
+ String COMPONENT_NAME_PROPERTY = "ipojo.component.name";
+
+ /**
+ * Service property specifying the component type's version.
+ */
+ String COMPONENT_VERSION_PROPERTY = "ipojo.component.version";
+
+ /**
+ * Service property specifying the instance name.
+ */
+ String INSTANCE_NAME = "ipojo.instance.name";
+
+ /**
+ * Value used when an instance configuration does not declare its name.
+ */
+ String UNNAMED_INSTANCE = "unnamed";
+
+ /**
+ * The instance configuration.
+ *
+ * @return the instance configuration
+ */
+ Dictionary<String, Object> getConfiguration();
+
+ /**
+ * @return the component type's name.
+ */
+ String getComponentName();
+
+ /**
+ * @return the component type's version, {@literal null} if not set.
+ */
+ String getComponentVersion();
+
+ /**
+ * Gets the instance name.
+ *
+ * @return the instance name, {@literal unnamed} if not specified.
+ */
+ String getInstanceName();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Status.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Status.java
new file mode 100644
index 0000000..c9a98db
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/Status.java
@@ -0,0 +1,49 @@
+/*
+ * 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.felix.ipojo.extender;
+
+/**
+ * The declaration status.
+ * A declaration may be fulfilled or not (bound or not).
+ * When the declaration is unbound, a message can be given to explain the reason.
+ * Implementation are immutable.
+ */
+public interface Status {
+ /**
+ * Is the declaration fulfilled ?
+ *
+ * @return {@literal true} if the declaration is bound, {@literal false} otherwise.
+ */
+ boolean isBound();
+
+ /**
+ * Gets the unbound message if any.
+ *
+ * @return the unbound message, <code>null</code> if no message.
+ */
+ String getMessage();
+
+ /**
+ * Gets the unbound error if any.
+ *
+ * @return the unbound error, <code>null</code> if no error were set.
+ */
+ Throwable getThrowable();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/TypeDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/TypeDeclaration.java
new file mode 100644
index 0000000..167aa2a
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/TypeDeclaration.java
@@ -0,0 +1,63 @@
+/*
+ * 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.felix.ipojo.extender;
+
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Service exposed to instruct a factory creation.
+ */
+public interface TypeDeclaration extends Declaration {
+
+ /**
+ * Get the component metadata description.
+ *
+ * @return the component metadata description.
+ */
+ Element getComponentMetadata();
+
+ /**
+ * Returns {@literal true} if the type is public
+ *
+ * @return {@literal true} if the type is public
+ */
+ boolean isPublic();
+
+ /**
+ * Gets the component type's name.
+ *
+ * @return the component type's name.
+ */
+ String getComponentName();
+
+ /**
+ * Gets the component type's version.
+ *
+ * @return the component type's version
+ */
+ String getComponentVersion();
+
+ /**
+ * Gets the targeted iPOJO Extension (primitive, composite, handler...)
+ *
+ * @return the targeted extension
+ */
+ String getExtension();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilder.java
new file mode 100644
index 0000000..167187b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.felix.ipojo.extender.builder;
+
+import org.apache.felix.ipojo.IPojoFactory;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Interface defining the method used to build {@link IPojoFactory} instances.
+ * As each type of iPOJO factories can be different, factory builder are the common facade of all those types.
+ */
+public interface FactoryBuilder {
+
+ /**
+ * Creates an iPOJO Factory.
+ *
+ * @param bundleContext the bundle context of the bundle declaring the component type
+ * @param metadata the metadata of the component type (<code>component</code> element).
+ * @return the iPOJO Factory instance.
+ * @throws FactoryBuilderException if the factory cannot be created.
+ */
+ IPojoFactory build(BundleContext bundleContext, Element metadata) throws FactoryBuilderException;
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilderException.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilderException.java
new file mode 100644
index 0000000..bec872b
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/builder/FactoryBuilderException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.felix.ipojo.extender.builder;
+
+/**
+ * Exception thrown by {@link FactoryBuilder} when a {@link org.apache.felix.ipojo.IPojoFactory} instance cannot be
+ * created correctly.
+ */
+public class FactoryBuilderException extends Exception {
+
+ /**
+ * Creates the exception instance with the given message.
+ *
+ * @param message the message
+ */
+ public FactoryBuilderException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates the exception instance with the given message and cause.
+ *
+ * @param message the message
+ * @param cause the cause
+ */
+ public FactoryBuilderException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
new file mode 100644
index 0000000..1fa48f0
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
@@ -0,0 +1,86 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import java.util.Dictionary;
+
+/**
+ * Common code wrapping an OSGi service.
+ */
+public abstract class AbstractService implements Lifecycle {
+
+ /**
+ * The bundle context.
+ */
+ private final BundleContext m_bundleContext;
+
+ /**
+ * The service specification.
+ */
+ private final Class<?> m_type;
+
+ /**
+ * The service registration.
+ */
+ private ServiceRegistration<?> m_registration;
+
+
+ /**
+ * Constructor.
+ * This constructor checks that the current class and the service specification are compatible.
+ *
+ * @param bundleContext the bundle context
+ * @param type the specification
+ */
+ protected AbstractService(BundleContext bundleContext, Class<?> type) {
+ m_bundleContext = bundleContext;
+ if (!type.isAssignableFrom(getClass())) {
+ throw new IllegalArgumentException("This object is not an instance of " + type.getName());
+ }
+ m_type = type;
+ }
+
+ /**
+ * On start, registers the service.
+ */
+ public void start() {
+ m_registration = m_bundleContext.registerService(m_type.getName(), this, getServiceProperties());
+ }
+
+ /**
+ * On stop, un-registers the service.
+ */
+ public void stop() {
+ if (m_registration != null) {
+ m_registration.unregister();
+ m_registration = null;
+ }
+ }
+
+ /**
+ * @return the service properties, {@literal null} by default.
+ */
+ protected Dictionary<String, ?> getServiceProperties() {
+ return null;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/BundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/BundleProcessor.java
new file mode 100644
index 0000000..740b401
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/BundleProcessor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Main Processor interface.
+ * A bundle processor is an extender fragment, it analyzes the bundle content and creates the entities.
+ * <p/>
+ * Notice the difference between the <code>activate / deactivate </code> methods called when a bundle is starting
+ * and stopping, and <code>start / stop</code> called when the iPOJO bundle is started and stopped.
+ */
+public interface BundleProcessor extends Lifecycle {
+ /**
+ * A bundle is started.
+ *
+ * @param bundle the bundle
+ */
+ void activate(Bundle bundle);
+
+ /**
+ * A bundle is stopping. This call is made during the stopping phase.
+ *
+ * @param bundle the bundle
+ */
+ void deactivate(Bundle bundle);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
new file mode 100644
index 0000000..7fc4427
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
@@ -0,0 +1,292 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+import org.apache.felix.ipojo.EventDispatcher;
+import org.apache.felix.ipojo.extender.internal.linker.DeclarationLinker;
+import org.apache.felix.ipojo.extender.internal.processor.ChainedBundleProcessor;
+import org.apache.felix.ipojo.extender.internal.processor.ComponentsBundleProcessor;
+import org.apache.felix.ipojo.extender.internal.processor.ExtensionBundleProcessor;
+import org.apache.felix.ipojo.extender.internal.processor.QueuingActivationProcessor;
+import org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory;
+import org.apache.felix.ipojo.extender.internal.queue.SynchronousQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.pref.HeaderPreferenceSelection;
+import org.apache.felix.ipojo.extender.internal.queue.pref.Preference;
+import org.apache.felix.ipojo.extender.internal.queue.pref.PreferenceQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.pref.enforce.EnforcedQueueService;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.*;
+
+/**
+ * iPOJO main activator.
+ */
+public class Extender implements BundleActivator, SynchronousBundleListener {
+ /**
+ * Enables the iPOJO internal dispatcher.
+ * This internal dispatcher helps the OSGi framework to support large
+ * scale applications. The internal dispatcher is disabled by default.
+ */
+ static boolean DISPATCHER_ENABLED = true;
+
+ /**
+ * Disables the iPOJO asynchronous processing.
+ * When set to false, the bundles are processed in the listener thread
+ * making iPOJO usable on Google App Engine. By default, the processing
+ * is asynchronous.
+ */
+ static boolean SYNCHRONOUS_PROCESSING_ENABLED = false;
+
+ /**
+ * Property allowing to set if the internal dispatcher is enabled or disabled.
+ * Possible value are either {@literal true} or {@literal false}.
+ */
+ private static final String ENABLING_DISPATCHER = "ipojo.internal.dispatcher";
+
+ /**
+ * Property allowing to disable the asynchronous process (and so enables the
+ * synchronous processing).
+ * Possible value are either {@literal true} or {@literal false}.
+ */
+ private static final String SYNCHRONOUS_PROCESSING = "ipojo.processing.synchronous";
+
+ /**
+ * The Bundle Context of the iPOJO Core bundle.
+ */
+ private static BundleContext m_context;
+
+ /**
+ * The iPOJO Extender logger.
+ */
+ private Logger m_logger;
+
+ /**
+ * The iPOJO Bundle.
+ */
+ private Bundle m_bundle;
+
+ /**
+ * The chained processor containing all the true bundle processor.
+ */
+ private ChainedBundleProcessor m_processor;
+
+ /**
+ * Binds Instances to Factories to Extensions.
+ */
+ private DeclarationLinker m_linker;
+
+ private LifecycleQueueService m_queueService;
+
+ /**
+ * The iPOJO bundle is starting.
+ * This method configures the iPOJO system (internal dispatcher and bundle processing). Then it initiates the
+ * bundle processing.
+ * <p/>
+ * To optimize the processing, we process the iPOJO bundle first.
+ *
+ * @param context the iPOJO's bundle bundle context
+ * @throws Exception something terrible happen during startup
+ */
+ public void start(BundleContext context) throws Exception {
+ m_context = context;
+ m_bundle = context.getBundle();
+
+ m_logger = new Logger(m_context, "IPOJO-Main-Extender");
+
+ enablingDispatcher(context, m_logger);
+ enablingSynchronousProcessing(context, m_logger);
+
+ // Create the dispatcher only if required.
+ if (DISPATCHER_ENABLED) {
+ EventDispatcher.create(context);
+ }
+
+ BundleProcessor extensionBundleProcessor = new ExtensionBundleProcessor(m_logger);
+ BundleProcessor componentsProcessor = new ComponentsBundleProcessor(m_logger);
+ if (SYNCHRONOUS_PROCESSING_ENABLED) {
+ m_queueService = new EnforcedQueueService(
+ new HeaderPreferenceSelection(),
+ new SynchronousQueueService(context),
+ Preference.SYNC,
+ m_logger);
+ } else {
+ SynchronousQueueService sync = new SynchronousQueueService(context);
+ ExecutorQueueService async = new ExecutorQueueService(context, 1, new PrefixedThreadFactory("[iPOJO] "));
+ m_queueService = new PreferenceQueueService(new HeaderPreferenceSelection(), sync, async);
+
+ extensionBundleProcessor = new QueuingActivationProcessor(extensionBundleProcessor, m_queueService);
+ componentsProcessor = new QueuingActivationProcessor(componentsProcessor, m_queueService);
+ }
+ m_queueService.start();
+
+ // Start linking
+ m_linker = new DeclarationLinker(context, m_queueService);
+ m_linker.start();
+
+ m_processor = ChainedBundleProcessor.create(extensionBundleProcessor, componentsProcessor);
+
+ m_processor.start();
+
+ // Begin by initializing core handlers
+ m_processor.activate(m_bundle);
+
+ synchronized (this) {
+ // listen to any changes in bundles.
+ m_context.addBundleListener(this);
+ // compute already started bundles.
+ for (int i = 0; i < context.getBundles().length; i++) {
+ if (context.getBundles()[i].getState() == Bundle.ACTIVE) {
+ m_processor.activate(context.getBundles()[i]);
+ }
+ }
+ }
+
+ m_logger.log(Logger.INFO, "iPOJO Main Extender started");
+ }
+
+ /**
+ * The iPOJO bundle is stopping.
+ *
+ * @param context the bundle context
+ * @throws Exception something terrible happen
+ */
+ public void stop(BundleContext context) throws Exception {
+ context.removeBundleListener(this);
+
+ m_processor.stop();
+
+ if (DISPATCHER_ENABLED) {
+ EventDispatcher.dispose();
+ }
+
+ m_linker.stop();
+ m_queueService.stop();
+
+ m_logger.log(Logger.INFO, "iPOJO Main Extender stopped");
+ m_context = null;
+ }
+
+ /**
+ * A bundle event was caught.
+ *
+ * @param event the event
+ */
+ public void bundleChanged(BundleEvent event) {
+ if (m_bundle.getBundleId() != (event.getBundle().getBundleId())) {
+ // Do not process our-self (already done)
+ switch (event.getType()) {
+ case BundleEvent.STARTED:
+ // Put the bundle in the queue
+ m_processor.activate(event.getBundle());
+ break;
+ case BundleEvent.STOPPING:
+ m_processor.deactivate(event.getBundle());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Gets iPOJO bundle context.
+ *
+ * @return the iPOJO Bundle Context
+ */
+ public static BundleContext getIPOJOBundleContext() {
+ return m_context;
+ }
+
+ /**
+ * Enables or disables the internal dispatcher, so sets the
+ * {@link Extender#DISPATCHER_ENABLED} flag.
+ * This method checks if the {@link Extender#ENABLING_DISPATCHER}
+ * property is set to {@literal true}. Otherwise, the internal
+ * dispatcher is disabled. The property can be set as a system
+ * property ({@literal ipojo.internal.dispatcher}) or inside the
+ * iPOJO bundle manifest ({@literal ipojo-internal-dispatcher}).
+ *
+ * @param context the bundle context.
+ * @param logger the logger to indicates if the internal dispatcher is set.
+ */
+ private static void enablingDispatcher(BundleContext context, Logger logger) {
+ // First check in the framework and in the system properties
+ String flag = context.getProperty(ENABLING_DISPATCHER);
+
+ // If null, look in bundle manifest
+ if (flag == null) {
+ String key = ENABLING_DISPATCHER.replace('.', '-');
+ flag = (String) context.getBundle().getHeaders().get(key);
+ }
+
+ if (flag != null) {
+ if (flag.equalsIgnoreCase("true")) {
+ Extender.DISPATCHER_ENABLED = true;
+ logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher enables");
+ return;
+ }
+ }
+
+ // Either l is null, or the specified value was false
+ Extender.DISPATCHER_ENABLED = false;
+ logger.log(Logger.INFO, "iPOJO Internal Event Dispatcher disables");
+
+ }
+
+ /**
+ * Enables or disables the asynchronous processing, so sets the
+ * {@link Extender#SYNCHRONOUS_PROCESSING_ENABLED} flag.
+ * Disabling asynchronous processing avoids iPOJO to create a new
+ * thread to process bundles. So, iPOJO can be used on the
+ * Google App Engine.
+ * This method checks if the {@link Extender#SYNCHRONOUS_PROCESSING}
+ * property is set to {@literal true}. Otherwise, asynchronous processing
+ * is used (default). The property can be set as a system
+ * property ({@literal ipojo.processing.synchronous}) or inside the
+ * iPOJO bundle manifest.
+ *
+ * @param context the bundle context.
+ * @param logger the logger to indicates if the internal dispatcher is set.
+ */
+ private static void enablingSynchronousProcessing(BundleContext context, Logger logger) {
+ String flag = context.getProperty(SYNCHRONOUS_PROCESSING);
+
+ // If null, look in bundle manifest
+ if (flag == null) {
+ String key = SYNCHRONOUS_PROCESSING.replace('.', '-');
+ flag = (String) context.getBundle().getHeaders().get(key);
+ }
+
+ if (flag != null) {
+ if (flag.equalsIgnoreCase("true")) {
+ Extender.SYNCHRONOUS_PROCESSING_ENABLED = true;
+ logger.log(Logger.INFO, "iPOJO Asynchronous processing disabled");
+ return;
+ }
+ }
+
+ // Either l is null, or the specified value was false
+ Extender.SYNCHRONOUS_PROCESSING_ENABLED = false;
+ logger.log(Logger.INFO, "iPOJO synchronous processing disabled");
+
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Lifecycle.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Lifecycle.java
new file mode 100644
index 0000000..2706ca1
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Lifecycle.java
@@ -0,0 +1,36 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+/**
+ * Simple start/stop interface.
+ */
+public interface Lifecycle {
+
+ /**
+ * Start the service.
+ */
+ void start();
+
+ /**
+ * Stop the service.
+ */
+ void stop();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/LifecycleQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/LifecycleQueueService.java
new file mode 100644
index 0000000..97024f3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/LifecycleQueueService.java
@@ -0,0 +1,29 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+import org.apache.felix.ipojo.extender.queue.QueueService;
+
+/**
+ * An interface composing {@link QueueService} and {@link Lifecycle}.
+ */
+public interface LifecycleQueueService extends QueueService, Lifecycle {
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/ReferenceableCallable.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/ReferenceableCallable.java
new file mode 100644
index 0000000..1c0dece
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/ReferenceableCallable.java
@@ -0,0 +1,56 @@
+/*
+ * 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.felix.ipojo.extender.internal;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A callable object implementing Bundle Reference.
+ * It makes the Bundle object accessible by the processing job.
+ * This class is intended to be extended.
+ */
+public abstract class ReferenceableCallable<T> implements Callable<T>, BundleReference {
+ /**
+ * The bundle object.
+ */
+ private final Bundle m_bundle;
+
+ /**
+ * Creates the ReferenceableCallable instance.
+ *
+ * @param bundle the associated bundle
+ */
+ protected ReferenceableCallable(Bundle bundle) {
+ m_bundle = bundle;
+ }
+
+ /**
+ * Gets the bundle object.
+ *
+ * @return the bundle
+ */
+ public Bundle getBundle() {
+ return m_bundle;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/builder/ReflectiveFactoryBuilder.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/builder/ReflectiveFactoryBuilder.java
new file mode 100644
index 0000000..c7b0f55
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/builder/ReflectiveFactoryBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.felix.ipojo.extender.internal.builder;
+
+import org.apache.felix.ipojo.IPojoFactory;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilderException;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * An Factory builder using a reflective call to build the factory.
+ * <p/>
+ * This builder is compatible with the original iPOJO method consisting in calling a constructor receiving the bundle
+ * context and the metadata as parameters.
+ * <p/>
+ * This factory builder need the constructor to be called.
+ */
+public class ReflectiveFactoryBuilder implements FactoryBuilder {
+
+ /**
+ * The constructor to call.
+ */
+ private final Constructor<? extends IPojoFactory> m_constructor;
+
+ /**
+ * Creates the factory builder.
+ *
+ * @param constructor the constructor that will be called when a new component factory will be created.
+ */
+ public ReflectiveFactoryBuilder(Constructor<? extends IPojoFactory> constructor) {
+ m_constructor = constructor;
+ }
+
+ /**
+ * Calls the wrapped constructor to create an iPOJO factory.
+ *
+ * @param bundleContext the bundle context of the bundle declaring the component type
+ * @param metadata the metadata of the component type (<code>component</code> element).
+ * @return the created iPOJO factory
+ * @throws FactoryBuilderException if the constructor cannot be called or throws an error.
+ */
+ public IPojoFactory build(BundleContext bundleContext, Element metadata) throws FactoryBuilderException {
+ try {
+ return m_constructor.newInstance(bundleContext, metadata);
+ } catch (InstantiationException e) {
+ throw new FactoryBuilderException("Cannot create instance of " + m_constructor.getDeclaringClass(), e);
+ } catch (IllegalAccessException e) {
+ throw new FactoryBuilderException(m_constructor.getDeclaringClass() + " constructor is not " +
+ "accessible (not public)", e);
+ } catch (InvocationTargetException e) {
+ throw new FactoryBuilderException("Cannot create instance of " + m_constructor.getDeclaringClass(), e);
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java
new file mode 100644
index 0000000..acca635
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/AbstractDeclaration.java
@@ -0,0 +1,135 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import org.apache.felix.ipojo.extender.Declaration;
+import org.apache.felix.ipojo.extender.Status;
+import org.apache.felix.ipojo.extender.internal.AbstractService;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Common code to all Declaration objects.
+ */
+public abstract class AbstractDeclaration extends AbstractService implements Declaration, Status {
+
+ /**
+ * The message used when a declaration is bound.
+ */
+
+ public static final String DECLARATION_BOUND_MESSAGE = "Declaration bound";
+ /**
+ * Is the declaration bound (i.e. successfully fulfilled), or not.
+ */
+ private boolean m_bound = false;
+
+ /**
+ * When not bound, the reason, otherwise {@link #DECLARATION_BOUND_MESSAGE}
+ */
+ private String m_message;
+
+ /**
+ * If an error was thrown during the binding, the error.
+ */
+ private Throwable m_throwable;
+
+ protected AbstractDeclaration(BundleContext bundleContext, Class<?> type) {
+ super(bundleContext, type);
+ }
+
+ /**
+ * @return whether the declaration is bound or not.
+ */
+ public boolean isBound() {
+ return m_bound;
+ }
+
+ /**
+ * @return the message. The message contains the not-bound reason.
+ */
+ public String getMessage() {
+ return m_message;
+ }
+
+ /**
+ * @return the error if something went wrong during the binding process.
+ */
+ public Throwable getThrowable() {
+ return m_throwable;
+ }
+
+ /**
+ * Gets the status of the declaration. This method returns an immutable object.
+ *
+ * @return the declaration status.
+ */
+ public Status getStatus() {
+ // We return an immutable object, created on the fly.
+ return new Status() {
+ final boolean m_bound = AbstractDeclaration.this.m_bound;
+ final String m_message = AbstractDeclaration.this.m_message;
+ final Throwable m_throwable = AbstractDeclaration.this.m_throwable;
+
+ public boolean isBound() {
+ return this.m_bound;
+ }
+
+ public String getMessage() {
+ return this.m_message;
+ }
+
+ public Throwable getThrowable() {
+ return this.m_throwable;
+ }
+ };
+ }
+
+ /**
+ * Binds the declaration.
+ * This method just set the status, message and error.
+ */
+ public void bind() {
+ m_bound = true;
+ m_message = DECLARATION_BOUND_MESSAGE;
+ m_throwable = null;
+ }
+
+ /**
+ * Unbinds the declaration.
+ *
+ * @param message an explanation
+ * @see #unbind(String, Throwable)
+ */
+ public void unbind(String message) {
+ unbind(message, null);
+ }
+
+ /**
+ * Unbinds the declaration.
+ * The flag, message and error are set.
+ *
+ * @param message an explanation
+ * @param throwable an error
+ */
+ public void unbind(String message, Throwable throwable) {
+ m_bound = false;
+ m_message = message;
+ m_throwable = throwable;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclaration.java
new file mode 100644
index 0000000..2d91ef7
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclaration.java
@@ -0,0 +1,64 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.osgi.framework.BundleContext;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+/**
+ * Default implementation of the iPOJO Extension Declaration.
+ */
+public class DefaultExtensionDeclaration extends AbstractDeclaration implements ExtensionDeclaration {
+
+ private final FactoryBuilder m_factoryBuilder;
+ private final String m_type;
+
+ public DefaultExtensionDeclaration(BundleContext bundleContext, FactoryBuilder factoryBuilder, String type) {
+ super(bundleContext, ExtensionDeclaration.class);
+ m_factoryBuilder = factoryBuilder;
+ m_type = type;
+ }
+
+ public FactoryBuilder getFactoryBuilder() {
+ return m_factoryBuilder;
+ }
+
+ public String getExtensionName() {
+ return m_type;
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ bind();
+ }
+
+ @Override
+ protected Dictionary<String, ?> getServiceProperties() {
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ properties.put(ExtensionDeclaration.EXTENSION_NAME_PROPERTY, m_type);
+ return properties;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclaration.java
new file mode 100644
index 0000000..18d8922
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclaration.java
@@ -0,0 +1,96 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.osgi.framework.BundleContext;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+/**
+ * Default implementation of the instance declaration.
+ */
+public class DefaultInstanceDeclaration extends AbstractDeclaration implements InstanceDeclaration {
+
+ private static final Dictionary<String, Object> EMPTY_DICTIONARY = new Hashtable<String, Object>();
+
+ private final String m_componentName;
+ private final Dictionary<String, Object> m_configuration;
+ private final String m_componentVersion;
+ private final String m_instanceName;
+
+ public DefaultInstanceDeclaration(BundleContext bundleContext, String componentName) {
+ this(bundleContext, componentName, EMPTY_DICTIONARY);
+ }
+
+ public DefaultInstanceDeclaration(BundleContext bundleContext, String componentName, Dictionary<String, Object> configuration) {
+ super(bundleContext, InstanceDeclaration.class);
+ m_componentName = componentName;
+ m_configuration = configuration;
+ m_componentVersion = initComponentVersion();
+ m_instanceName = initInstanceName();
+ }
+
+ private String initInstanceName() {
+ String name = (String) m_configuration.get(Factory.INSTANCE_NAME_PROPERTY);
+ if (name == null) {
+ name = UNNAMED_INSTANCE;
+ }
+ return name;
+ }
+
+ private String initComponentVersion() {
+ return (String) m_configuration.get(Factory.FACTORY_VERSION_PROPERTY);
+ }
+
+ public Dictionary<String, Object> getConfiguration() {
+ return m_configuration;
+ }
+
+ public String getComponentName() {
+ return m_componentName;
+ }
+
+ public String getComponentVersion() {
+ return m_componentVersion;
+ }
+
+ public String getInstanceName() {
+ return m_instanceName;
+ }
+
+ @Override
+ protected Dictionary<String, ?> getServiceProperties() {
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ properties.put(InstanceDeclaration.COMPONENT_NAME_PROPERTY, m_componentName);
+
+ String version = getComponentVersion();
+ if (version != null) {
+ properties.put(InstanceDeclaration.COMPONENT_VERSION_PROPERTY, version);
+ }
+
+ properties.put(InstanceDeclaration.INSTANCE_NAME, m_instanceName);
+
+ return properties;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclaration.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclaration.java
new file mode 100644
index 0000000..42b1958
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclaration.java
@@ -0,0 +1,96 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import org.apache.felix.ipojo.extender.TypeDeclaration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Default implementation of the component type declaration.
+ */
+public class DefaultTypeDeclaration extends AbstractDeclaration implements TypeDeclaration {
+
+ private final Element m_componentMetadata;
+ private final String m_componentName;
+ private final String m_componentVersion;
+ private final String m_extension;
+ private boolean visible = true;
+
+ public DefaultTypeDeclaration(BundleContext bundleContext, Element componentMetadata) {
+ super(bundleContext, TypeDeclaration.class);
+ m_componentMetadata = componentMetadata;
+ visible = initVisible();
+ m_componentName = initComponentName();
+ m_componentVersion = initComponentVersion(bundleContext);
+ m_extension = initExtension();
+ }
+
+ private String initExtension() {
+ if (m_componentMetadata.getNameSpace() == null) {
+ return m_componentMetadata.getName();
+ }
+ return m_componentMetadata.getNameSpace() + ":" + m_componentMetadata.getName();
+ }
+
+ private String initComponentVersion(BundleContext bundleContext) {
+ String version = m_componentMetadata.getAttribute("version");
+ if (version != null) {
+ if ("bundle".equalsIgnoreCase(version)) {
+ return bundleContext.getBundle().getHeaders().get(Constants.BUNDLE_VERSION);
+ }
+ }
+ return version;
+ }
+
+ private String initComponentName() {
+ String name = m_componentMetadata.getAttribute("name");
+ if (name == null) {
+ name = m_componentMetadata.getAttribute("classname");
+ }
+ return name;
+ }
+
+ private boolean initVisible() {
+ String publicAttribute = m_componentMetadata.getAttribute("public");
+ return (publicAttribute == null) || !publicAttribute.equalsIgnoreCase("false");
+ }
+
+ public String getComponentName() {
+ return m_componentName;
+ }
+
+ public String getComponentVersion() {
+ return m_componentVersion;
+ }
+
+ public String getExtension() {
+ return m_extension;
+ }
+
+ public Element getComponentMetadata() {
+ return m_componentMetadata;
+ }
+
+ public boolean isPublic() {
+ return visible;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinker.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinker.java
new file mode 100644
index 0000000..7630cea
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinker.java
@@ -0,0 +1,109 @@
+/*
+ * 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.felix.ipojo.extender.internal.linker;
+
+import org.apache.felix.ipojo.extender.TypeDeclaration;
+import org.apache.felix.ipojo.extender.internal.Lifecycle;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * The linker is responsible to bind extension declaration to type declaration.
+ * It tracks TypeDeclaration, and reifies them as factory.
+ */
+public class DeclarationLinker implements ServiceTrackerCustomizer, Lifecycle {
+ /**
+ * The bundle context. It uses the iPOJO bundle context.
+ */
+ private final BundleContext m_bundleContext;
+
+ /**
+ * The queue service on which we delegate the binding process.
+ */
+ private final QueueService m_queueService;
+
+ /**
+ * The service tracker looking for TypeDeclaration.
+ */
+ private final ServiceTracker m_typeTracker;
+
+ /**
+ * Creates the linker.
+ *
+ * @param bundleContext the bundle context
+ * @param queueService the queue service
+ */
+ public DeclarationLinker(BundleContext bundleContext, QueueService queueService) {
+ m_bundleContext = bundleContext;
+ m_queueService = queueService;
+ m_typeTracker = new ServiceTracker(m_bundleContext, TypeDeclaration.class.getName(), this);
+ }
+
+ /**
+ * When the iPOJO management starts, we look for type declaration.
+ */
+ public void start() {
+ m_typeTracker.open(true);
+ }
+
+ /**
+ * When iPOJO stops, we close the tracker.
+ */
+ public void stop() {
+ m_typeTracker.close();
+ }
+
+ /**
+ * A new type declaration was published.
+ *
+ * @param reference the service reference of the type declaration
+ * @return the managed type object wrapping the service object.
+ */
+ public Object addingService(ServiceReference reference) {
+ TypeDeclaration declaration = (TypeDeclaration) m_bundleContext.getService(reference);
+ ManagedType managedType = new ManagedType(reference.getBundle().getBundleContext(), m_queueService, declaration);
+ managedType.start();
+ return managedType;
+ }
+
+ /**
+ * Type declaration cannot be modified.
+ *
+ * @param reference the reference
+ * @param service the object returned by {@link #addingService(org.osgi.framework.ServiceReference)}
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Ignored
+ }
+
+ /**
+ * A type declaration service was withdrawn from the service registry.
+ *
+ * @param reference the leaving reference
+ * @param service the object returned by {@link #addingService(org.osgi.framework.ServiceReference)}
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ ManagedType managedType = (ManagedType) service;
+ managedType.stop();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/ManagedType.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/ManagedType.java
new file mode 100644
index 0000000..bc40b7e
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/linker/ManagedType.java
@@ -0,0 +1,340 @@
+/*
+ * 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.felix.ipojo.extender.internal.linker;
+
+import org.apache.felix.ipojo.*;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.apache.felix.ipojo.extender.TypeDeclaration;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilderException;
+import org.apache.felix.ipojo.extender.internal.Lifecycle;
+import org.apache.felix.ipojo.extender.internal.ReferenceableCallable;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * This class is responsible to create the factory for a given type declaration. It also instructs the factories to
+ * create
+ * the instance for each instance declaration targeting the managed factory.
+ */
+public class ManagedType implements FactoryStateListener, Lifecycle {
+ /**
+ * The bundle context
+ */
+ private final BundleContext m_bundleContext;
+ /**
+ * The queue service used for the creation.
+ */
+ private final QueueService m_queueService;
+ /**
+ * The type declaration that we have to handle.
+ */
+ private final TypeDeclaration m_declaration;
+ /**
+ * The service tracker tracking {@link ExtensionDeclaration} services.
+ */
+ private ServiceTracker m_extensionTracker;
+
+ /**
+ * The service tracker tracking the {@link InstanceDeclaration} services.
+ */
+ private ServiceTracker m_instanceTracker;
+
+ /**
+ * The job used to instantiate the factory.
+ */
+ private Future<IPojoFactory> m_future;
+
+ /**
+ * If the Managed Type cannot be initializes, sets this flag to true and no links will be created.
+ */
+ private boolean m_frozen;
+
+ /**
+ * Constructs a Managed Type object for the given type declaration.
+ *
+ * @param bundleContext the bundle context
+ * @param queueService the queue service
+ * @param declaration the declaration
+ */
+ public ManagedType(BundleContext bundleContext, QueueService queueService, TypeDeclaration declaration) {
+ m_bundleContext = bundleContext;
+ m_queueService = queueService;
+ m_declaration = declaration;
+ try {
+ initExtensionTracker();
+ initInstanceTracker();
+ } catch (InvalidSyntaxException e) {
+ // Error during filter creation, freeze the declaration and add a meaningful message
+ m_frozen = true;
+ m_declaration.unbind("Filter creation error", e);
+ }
+ }
+
+ /**
+ * Initializes the extension declaration tracker.
+ *
+ * @throws InvalidSyntaxException cannot happen
+ */
+ private void initExtensionTracker() throws InvalidSyntaxException {
+ String filter = String.format(
+ "(&(objectclass=%s)(%s=%s))",
+ ExtensionDeclaration.class.getName(),
+ ExtensionDeclaration.EXTENSION_NAME_PROPERTY,
+ m_declaration.getExtension()
+ );
+ m_extensionTracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter(filter), new ExtensionSupport());
+ }
+
+ /**
+ * Initializes the instance declaration tracker.
+ *
+ * @throws InvalidSyntaxException cannot happen
+ */
+ private void initInstanceTracker() throws InvalidSyntaxException {
+
+ String filter;
+ String version = m_declaration.getComponentVersion();
+ if (version != null) {
+ // Track instance for:
+ // * this component AND
+ // * this component's version OR no version
+ filter = String.format(
+ "(&(objectClass=%s)(%s=%s)(|(%s=%s)(!(%s=*))))",
+ InstanceDeclaration.class.getName(),
+ InstanceDeclaration.COMPONENT_NAME_PROPERTY,
+ m_declaration.getComponentName(),
+ InstanceDeclaration.COMPONENT_VERSION_PROPERTY,
+ version,
+ InstanceDeclaration.COMPONENT_VERSION_PROPERTY
+ );
+ } else {
+ // Track instance for:
+ // * this component AND no version
+ filter = String.format(
+ "(&(objectClass=%s)(%s=%s)(!(%s=*)))",
+ InstanceDeclaration.class.getName(),
+ InstanceDeclaration.COMPONENT_NAME_PROPERTY,
+ m_declaration.getComponentName(),
+ InstanceDeclaration.COMPONENT_VERSION_PROPERTY
+ );
+ }
+ m_instanceTracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter(filter), new InstanceSupport());
+ }
+
+ /**
+ * Starting the management.
+ * We open only the extension tracker.
+ */
+ public void start() {
+ if (!m_frozen) {
+ m_extensionTracker.open(true);
+ }
+ }
+
+ /**
+ * Stopping the management.
+ */
+ public void stop() {
+ m_instanceTracker.close();
+ m_extensionTracker.close();
+ }
+
+ /**
+ * The factory we have built has a state in his change.
+ *
+ * @param factory the changing factory
+ * @param newState the new factory state
+ */
+ public void stateChanged(Factory factory, int newState) {
+ if (Factory.VALID == newState) {
+ // Start tracking instances
+ m_instanceTracker.open(true);
+ } else {
+ // Un-track all instances
+ m_instanceTracker.close();
+ }
+ }
+
+ /**
+ * The service tracker customizer for extension declaration.
+ * It submits a factory building job when an extension matching our type declaration is found.
+ */
+ private class ExtensionSupport implements ServiceTrackerCustomizer {
+ public Object addingService(ServiceReference reference) {
+ // TODO Check if we can cast the instance
+ final Object service = m_bundleContext.getService(reference);
+ if (service instanceof ExtensionDeclaration) {
+ m_future = m_queueService.submit(new ReferenceableCallable<IPojoFactory>(reference.getBundle()) {
+
+ /**
+ * The factory creation job.
+ * @return the IPojoFactory
+ * @throws Exception the factory cannot be created
+ */
+ public IPojoFactory call() throws Exception {
+ ExtensionDeclaration declaration = (ExtensionDeclaration) service;
+ try {
+ // Build and start the factory instance
+ IPojoFactory factory = declaration.getFactoryBuilder().build(m_bundleContext, m_declaration.getComponentMetadata());
+ factory.addFactoryStateListener(ManagedType.this);
+ factory.start();
+
+ // Change the status
+ m_declaration.bind();
+
+ return factory;
+ } catch (FactoryBuilderException e) {
+ m_declaration.unbind(String.format("Cannot build '%s' factory instance", m_declaration.getExtension()), e);
+ } catch (Throwable t) {
+ m_declaration.unbind(String.format("Error during '%s' factory instance creation", m_declaration.getExtension()), t);
+ }
+
+ return null;
+ }
+ });
+ }
+
+ return null;
+ }
+
+ public void modifiedService(ServiceReference reference, Object o) {
+ }
+
+ public void removedService(ServiceReference reference, Object o) {
+
+ // Then stop the factory
+ try {
+ IPojoFactory factory = m_future.get();
+ // It is possible that the factory couldn't be created
+ if (factory != null) {
+ factory.stop();
+ factory.removeFactoryStateListener(ManagedType.this);
+ m_declaration.unbind("Extension '%s' is missing");
+ }
+ } catch (InterruptedException e) {
+ m_declaration.unbind("Could not create Factory", e);
+ } catch (ExecutionException e) {
+ m_declaration.unbind("Factory creation throw an Exception", e);
+ }
+ m_future = null;
+ }
+ }
+
+ private class InstanceSupport implements ServiceTrackerCustomizer {
+ public Object addingService(final ServiceReference reference) {
+ // TODO Check if we can cast the instance
+ Object service = m_bundleContext.getService(reference);
+ if (service instanceof InstanceDeclaration) {
+ final InstanceDeclaration instanceDeclaration = (InstanceDeclaration) service;
+
+ // Check that instance is not already bound
+ if (instanceDeclaration.getStatus().isBound()) {
+ return null;
+ }
+
+ // Handle visibility (private/public factories)
+ if (!m_declaration.isPublic()) {
+ if (!reference.getBundle().equals(m_bundleContext.getBundle())) {
+ Bundle origin = m_bundleContext.getBundle();
+ instanceDeclaration.unbind(
+ String.format("Component '%s/%s' is private. It only accept instances " +
+ "from bundle %s/%s [%d] (instance bundle origin: %d)",
+ m_declaration.getComponentName(),
+ m_declaration.getComponentVersion(),
+ origin.getSymbolicName(),
+ origin.getVersion(),
+ origin.getBundleId(),
+ reference.getBundle().getBundleId())
+ );
+ return null;
+ }
+ }
+
+ return m_queueService.submit(new ReferenceableCallable<ComponentInstance>(reference.getBundle()) {
+ public ComponentInstance call() throws Exception {
+ try {
+ // Create the component's instance
+ // It is automatically started
+ // Future.get should never be null since this tracker is started when the factory has been created
+ ComponentInstance instance = m_future.get().createComponentInstance(instanceDeclaration.getConfiguration());
+
+ // Notify the declaration that everything is fine
+ instanceDeclaration.bind();
+
+ return instance;
+ } catch (UnacceptableConfiguration c) {
+ m_declaration.unbind(String.format("Instance configuration is invalid (component:%s/%s, bundle:%d)",
+ m_declaration.getComponentName(),
+ m_declaration.getComponentVersion(),
+ reference.getBundle().getBundleId()),
+ c);
+ } catch (MissingHandlerException e) {
+ m_declaration.unbind(String.format("Component '%s/%s' is missing some handlers", m_declaration.getComponentName(), m_declaration.getComponentVersion()), e);
+ } catch (ConfigurationException e) {
+ m_declaration.unbind(String.format("Component '%s/%s' is incorrect", m_declaration.getComponentName(), m_declaration.getComponentVersion()), e);
+ }
+
+ return null;
+ }
+ });
+ }
+
+ return null;
+ }
+
+ public void modifiedService(ServiceReference reference, Object o) {
+ }
+
+ public void removedService(ServiceReference reference, Object o) {
+ InstanceDeclaration instanceDeclaration = (InstanceDeclaration) m_bundleContext.getService(reference);
+ Future<ComponentInstance> future = (Future<ComponentInstance>) o;
+ ComponentInstance instance = null;
+ try {
+ instance = future.get();
+ // It is possible that the instance couldn't be created
+ if (instance != null) {
+ String message = String.format("Factory for Component '%s/%s' is missing",
+ instance.getFactory().getName(),
+ m_declaration.getComponentVersion());
+ instanceDeclaration.unbind(message);
+
+ instance.stop();
+ instance.dispose();
+ }
+
+ } catch (InterruptedException e) {
+ instanceDeclaration.unbind("Could not create ComponentInstance", e);
+ } catch (ExecutionException e) {
+ instanceDeclaration.unbind("ComponentInstance creation throw an Exception", e);
+ }
+ }
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessor.java
new file mode 100644
index 0000000..1139928
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessor.java
@@ -0,0 +1,127 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.osgi.framework.Bundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A bundle processor chaining others processor.
+ * It composes the <em>extender</em> mechanism.
+ * <p/>
+ * Instance must be created using the {@link #create(org.apache.felix.ipojo.extender.internal.BundleProcessor...)}
+ * method.
+ */
+public class ChainedBundleProcessor implements BundleProcessor {
+
+ /**
+ * The list of processors.
+ * Be aware that the order if important, as processors will be called in the insertion order.
+ * <p/>
+ * Once the chained bundle processor is created, this list cannot be modified.
+ */
+ private final List<BundleProcessor> m_processors = new ArrayList<BundleProcessor>();
+
+ private ChainedBundleProcessor() {
+ // Just here to avoid direct creation.
+ }
+
+ /**
+ * Creates a new chained bundle processor.
+ *
+ * @param processors the set of processor to chain. Cannot be <code>null</code> or empty.
+ * @return the created bundle processor
+ * @throws IllegalArgumentException if the given processor list is <code>null</code> or empty.
+ */
+ public static ChainedBundleProcessor create(BundleProcessor... processors) {
+ if (processors == null || processors.length == 0) {
+ throw new IllegalArgumentException("Chained processor cannot be created without processors");
+ }
+ ChainedBundleProcessor chain = new ChainedBundleProcessor();
+ Collections.addAll(chain.m_processors, processors);
+ return chain;
+ }
+
+ /**
+ * Gets the list of processors.
+ * This method returns a copy of the list of processor.
+ *
+ * @return a copy of the processor list
+ */
+ public List<BundleProcessor> getProcessors() {
+ List<BundleProcessor> list = new ArrayList<BundleProcessor>();
+ list.addAll(m_processors);
+ return list;
+ }
+
+ /**
+ * A bundle is starting.
+ * Call the {@link BundleProcessor#activate(org.osgi.framework.Bundle)} method on all chained processors.
+ *
+ * @param bundle the bundle
+ */
+ public void activate(Bundle bundle) {
+ for (BundleProcessor processor : m_processors) {
+ processor.activate(bundle);
+ }
+ }
+
+ /**
+ * A bundle is stopping.
+ * Call the {@link BundleProcessor#deactivate(org.osgi.framework.Bundle)} method on all chained processors.
+ *
+ * @param bundle the bundle
+ */
+ public void deactivate(Bundle bundle) {
+ List<BundleProcessor> reverse = new ArrayList<BundleProcessor>(m_processors);
+ Collections.reverse(reverse);
+ for (BundleProcessor processor : reverse) {
+ processor.deactivate(bundle);
+ }
+ }
+
+ /**
+ * The iPOJO bundle is starting.
+ * Call the {@link org.apache.felix.ipojo.extender.internal.BundleProcessor#start()} method on all chained
+ * processors.
+ */
+ public void start() {
+ for (BundleProcessor processor : m_processors) {
+ processor.start();
+ }
+ }
+
+ /**
+ * The iPOJO bundle is stopping.
+ * Call the {@link org.apache.felix.ipojo.extender.internal.BundleProcessor#stop()} method on all chained
+ * processors.
+ */
+ public void stop() {
+ List<BundleProcessor> reverse = new ArrayList<BundleProcessor>(m_processors);
+ Collections.reverse(reverse);
+ for (BundleProcessor processor : reverse) {
+ processor.stop();
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ComponentsBundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ComponentsBundleProcessor.java
new file mode 100644
index 0000000..066ce81
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ComponentsBundleProcessor.java
@@ -0,0 +1,231 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.util.Log;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.Bundle;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Processor handling the {@link #IPOJO_HEADER} and {@link #IPOJO_HEADER_ALT}
+ * header from the bundle manifest.
+ */
+public class ComponentsBundleProcessor implements BundleProcessor {
+
+ /**
+ * iPOJO Component Type and Instance declaration header.
+ */
+ public static final String IPOJO_HEADER = "iPOJO-Components";
+
+ /**
+ * iPOJO Component Type and Instance declaration header
+ * (alternative).
+ * This header was introduced because of BND supporting only header
+ * starting with an uppercase.
+ */
+ public static final String IPOJO_HEADER_ALT = "IPOJO-Components";
+
+ /**
+ * The attribute used in instance configuration specifying the targeted component (i.e. factory).
+ */
+ public static final String COMPONENT_INSTANCE_ATTRIBUTE = "component";
+
+ /**
+ * The logger.
+ */
+ private final Log m_logger;
+
+ /**
+ * Registry storing the bundle to components and instances declared within this bundle.
+ */
+ private final Map<Bundle, ComponentsAndInstances> m_registry = new HashMap<Bundle, ComponentsAndInstances>();
+
+ /**
+ * Creates the component bundle processor.
+ *
+ * @param logger the logger.
+ */
+ public ComponentsBundleProcessor(Log logger) {
+ m_logger = logger;
+ }
+
+ /**
+ * A bundle is starting.
+ *
+ * @param bundle the bundle
+ */
+ public void activate(Bundle bundle) {
+ Dictionary dict = bundle.getHeaders();
+ // Check bundle
+ String header = (String) dict.get(IPOJO_HEADER);
+ // Check the alternative header
+ if (header == null) {
+ header = (String) dict.get(IPOJO_HEADER_ALT);
+ }
+
+ if (header != null) {
+ try {
+ parse(bundle, header);
+ } catch (IOException e) {
+ m_logger.log(Logger.ERROR, "An exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
+ } catch (ParseException e) {
+ m_logger.log(Logger.ERROR, "A parse exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
+ }
+ }
+
+ }
+
+ /**
+ * A bundle is stopping.
+ *
+ * @param bundle the bundle
+ */
+ public void deactivate(Bundle bundle) {
+ ComponentsAndInstances cai = m_registry.remove(bundle);
+ if (cai != null) {
+ cai.stop();
+ }
+ }
+
+ /**
+ * {@inheritDoc BundleProcessor#start}
+ */
+ public void start() {
+ // Nothing to do
+ }
+
+ /**
+ * {@inheritDoc BundleProcessor#stop}
+ * <p/>
+ * This method cleans up all created factories and instances.
+ */
+ public void stop() {
+ // Ignored, for a simple ordered shutdown, use ReverseBundleProcessor
+ }
+
+ /**
+ * Parses the internal metadata (from the manifest
+ * (in the iPOJO-Components property)). This methods
+ * creates factories and add instances to the instance creator.
+ *
+ * @param bundle the owner bundle.
+ * @param components The iPOJO Header String.
+ * @throws IOException if the manifest can not be found
+ * @throws ParseException if the parsing process failed
+ */
+ private void parse(Bundle bundle, String components) throws IOException, ParseException {
+ ManifestMetadataParser parser = new ManifestMetadataParser();
+ parser.parseHeader(components);
+
+ // Get the component type declaration
+ Element[] metadata = parser.getComponentsMetadata();
+ for (int i = 0; i < metadata.length; i++) {
+ handleTypeDeclaration(bundle, metadata[i]);
+ }
+
+ Dictionary[] instances = parser.getInstances();
+ for (int i = 0; instances != null && i < instances.length; i++) {
+ handleInstanceDeclaration(bundle, instances[i]);
+ }
+ }
+
+ /**
+ * Extracts and builds the declaration attached to an instance.
+ *
+ * @param bundle the bundle declaring the instance
+ * @param instance the instance configuration (parsed from the header)
+ */
+ private void handleInstanceDeclaration(Bundle bundle, Dictionary instance) {
+
+ String component = (String) instance.get(COMPONENT_INSTANCE_ATTRIBUTE);
+ //String v = (String) instance.get(Factory.FACTORY_VERSION_PROPERTY); //TODO CES to GSA, why this is commented ?
+
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(bundle.getBundleContext(),
+ component, instance);
+ declaration.start();
+
+ getComponentsAndInstances(bundle).m_instances.add(declaration);
+
+ }
+
+ /**
+ * Adds a component factory to the factory list.
+ *
+ * @param metadata the new component metadata.
+ * @param bundle the bundle.
+ */
+ private void handleTypeDeclaration(Bundle bundle, Element metadata) {
+
+ DefaultTypeDeclaration declaration = new DefaultTypeDeclaration(bundle.getBundleContext(), metadata);
+ declaration.start();
+
+ getComponentsAndInstances(bundle).m_types.add(declaration);
+
+ }
+
+ /**
+ * Gets the {@link ComponentsAndInstances} declared by the given bundle.
+ *
+ * @param bundle the bundle
+ * @return the set of component and instances declared by the bundle, <code>null</code> otherwise
+ */
+ private ComponentsAndInstances getComponentsAndInstances(Bundle bundle) {
+ ComponentsAndInstances cai = m_registry.get(bundle);
+ if (cai == null) {
+ cai = new ComponentsAndInstances();
+ m_registry.put(bundle, cai);
+ }
+ return cai;
+ }
+
+ /**
+ * Container storing the components and instances declared by a bundle.
+ * This class is not intended to be used outside from the current processor.
+ */
+ private static class ComponentsAndInstances {
+ List<DefaultTypeDeclaration> m_types = new ArrayList<DefaultTypeDeclaration>();
+ List<DefaultInstanceDeclaration> m_instances = new ArrayList<DefaultInstanceDeclaration>();
+
+ /**
+ * Stops all declarations.
+ */
+ void stop() {
+ for (DefaultInstanceDeclaration instance : m_instances) {
+ instance.stop();
+ }
+ for (DefaultTypeDeclaration declaration : m_types) {
+ declaration.stop();
+ }
+ m_instances.clear();
+ m_types.clear();
+ }
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessor.java
new file mode 100644
index 0000000..e48584c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessor.java
@@ -0,0 +1,169 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.apache.felix.ipojo.extender.internal.builder.ReflectiveFactoryBuilder;
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultExtensionDeclaration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.util.Log;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import java.util.*;
+
+/**
+ * Bundle processor handling the {@link #IPOJO_EXTENSION} header.
+ */
+public class ExtensionBundleProcessor implements BundleProcessor {
+
+ /**
+ * iPOJO Extension declaration header.
+ */
+ public static final String IPOJO_EXTENSION = "IPOJO-Extension";
+
+ /**
+ * Logger.
+ */
+ private final Log m_logger;
+
+ /**
+ * The map storing the association between bundles and the list of extension declaration.
+ */
+ private Map<Bundle, List<DefaultExtensionDeclaration>> m_extensions = new HashMap<Bundle, List<DefaultExtensionDeclaration>>();
+
+ /**
+ * Creates the processor.
+ *
+ * @param logger the logger
+ */
+ public ExtensionBundleProcessor(Log logger) {
+ m_logger = logger;
+ }
+
+ /**
+ * A bundle is starting.
+ *
+ * @param bundle the bundle
+ */
+ public void activate(Bundle bundle) {
+ Dictionary dict = bundle.getHeaders();
+ // Check for abstract factory type
+ String extension = (String) dict.get(IPOJO_EXTENSION);
+ if (extension != null) {
+ activateExtensions(bundle, extension);
+ }
+ }
+
+ /**
+ * A bundle is stopping.
+ *
+ * @param bundle the bundle
+ */
+ public void deactivate(Bundle bundle) {
+ List<DefaultExtensionDeclaration> declarations = m_extensions.get(bundle);
+ if (declarations != null) {
+ for (DefaultExtensionDeclaration declaration : declarations) {
+ declaration.stop();
+ }
+ m_extensions.remove(bundle);
+ }
+ }
+
+ /**
+ * iPOJO is starting.
+ * Nothing to do.
+ */
+ public void start() {
+ // Nothing to do
+ }
+
+ /**
+ * iPOJO is stopping.
+ * We clean up all extension in the reverse order of their installation.
+ */
+ public void stop() {
+ // Construct a new instance to avoid ConcurrentModificationException since deactivate also change the extensions
+ // list
+ // Ignored, for a simple ordered shutdown, use ReverseBundleProcessor
+ }
+
+ /**
+ * Parses an IPOJO-Extension manifest header and then creates
+ * iPOJO extensions (factory types).
+ *
+ * @param bundle the bundle containing the header.
+ * @param header the header to parse.
+ */
+ private void activateExtensions(Bundle bundle, String header) {
+ String[] extensions = ParseUtils.split(header, ",");
+ for (int i = 0; extensions != null && i < extensions.length; i++) {
+ String[] segments = ParseUtils.split(extensions[i], ":");
+
+ /*
+ * Get the fully qualified type name.
+ * type = [namespace] name
+ */
+ String[] nameparts = ParseUtils.split(segments[0].trim(), " \t");
+ String type = nameparts.length == 1 ? nameparts[0] : nameparts[0] + ":" + nameparts[1];
+
+ Class clazz;
+ try {
+ clazz = bundle.loadClass(segments[1]);
+ } catch (ClassNotFoundException e) {
+ m_logger.log(Logger.ERROR, "Cannot load the extension " + type, e);
+ return;
+ }
+
+ try {
+ ReflectiveFactoryBuilder builder = new ReflectiveFactoryBuilder(clazz.getConstructor(BundleContext.class, Element.class));
+ DefaultExtensionDeclaration declaration = new DefaultExtensionDeclaration(bundle.getBundleContext(), builder, type);
+
+ getBundleDeclarations(bundle).add(declaration);
+
+ declaration.start();
+
+ m_logger.log(Logger.DEBUG, "New factory type available: " + type);
+ } catch (NoSuchMethodException e) {
+ m_logger.log(Logger.ERROR,
+ String.format("Extension '%s' is missing the required (BundleContext, Element) public " +
+ "constructor", clazz.getName()));
+ }
+ }
+ }
+
+ /**
+ * Gets the list of declaration for the given method.
+ *
+ * @param bundle the bundle
+ * @return the list of extension declaration associated to the given bundle, <code>null</code> otherwise.
+ */
+ private List<DefaultExtensionDeclaration> getBundleDeclarations(Bundle bundle) {
+ List<DefaultExtensionDeclaration> declarations = m_extensions.get(bundle);
+ if (declarations == null) {
+ declarations = new ArrayList<DefaultExtensionDeclaration>();
+ m_extensions.put(bundle, declarations);
+ }
+ return declarations;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ForwardingBundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ForwardingBundleProcessor.java
new file mode 100644
index 0000000..f11d7e1
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ForwardingBundleProcessor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.osgi.framework.Bundle;
+
+/**
+ * A bundle processor delegating to a wrapped processor.
+ * Implementation defined how we retrieve the delegate.
+ */
+public abstract class ForwardingBundleProcessor implements BundleProcessor {
+ /**
+ * Implementation must implement this method to retrieve the wrapped bundle processor.
+ *
+ * @return the wrapped bundle processor on which we delegate all calls.
+ */
+ protected abstract BundleProcessor delegate();
+
+ public void activate(Bundle bundle) {
+ delegate().activate(bundle);
+ }
+
+ public void deactivate(Bundle bundle) {
+ delegate().deactivate(bundle);
+ }
+
+ public void start() {
+ delegate().start();
+ }
+
+ public void stop() {
+ delegate().stop();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/QueuingActivationProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/QueuingActivationProcessor.java
new file mode 100644
index 0000000..13561a3
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/QueuingActivationProcessor.java
@@ -0,0 +1,73 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.apache.felix.ipojo.extender.internal.ReferenceableCallable;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.Bundle;
+
+/**
+ * A bundle processor submitting the activating job to the queue service.
+ * The submitted job relies on a delegated bundle processor.
+ */
+public class QueuingActivationProcessor extends ForwardingBundleProcessor {
+ /**
+ * The wrapped bundle processor used by the job.
+ */
+ private final BundleProcessor m_delegate;
+
+ /**
+ * The queue service.
+ */
+ private final QueueService m_queueService;
+
+ /**
+ * Creates an instance of the queuing bundle processor
+ *
+ * @param delegate the bundle processor used by the submitted job
+ * @param queueService the used queue service
+ */
+ public QueuingActivationProcessor(BundleProcessor delegate, QueueService queueService) {
+ m_delegate = delegate;
+ m_queueService = queueService;
+ }
+
+ @Override
+ protected BundleProcessor delegate() {
+ return m_delegate;
+ }
+
+ /**
+ * A bundle is starting.
+ * The processing of the bundle is wrapped in a job submitted to the queue service.
+ *
+ * @param bundle the bundle
+ */
+ public void activate(final Bundle bundle) {
+ m_queueService.submit(new ReferenceableCallable<Boolean>(bundle) {
+ public Boolean call() throws Exception {
+ QueuingActivationProcessor.super.activate(bundle);
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessor.java
new file mode 100644
index 0000000..90d0ec0
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.osgi.framework.Bundle;
+
+import java.util.LinkedList;
+
+/**
+ * A bundle processor delegating a wrapped bundle processor. On stop, the bundles are processed in reverse.
+ */
+public class ReverseBundleProcessor extends ForwardingBundleProcessor {
+
+ /**
+ * The wrapped bundle processor.
+ */
+ private final BundleProcessor m_delegate;
+
+ /**
+ * A list of bundle to process.
+ */
+ private LinkedList<Bundle> m_bundles = new LinkedList<Bundle>();
+
+ /**
+ * Creates the processor.
+ *
+ * @param delegate the processor on which call are delegated
+ */
+ public ReverseBundleProcessor(BundleProcessor delegate) {
+ m_delegate = delegate;
+ }
+
+ /**
+ * @return the wrapped processor.
+ */
+ @Override
+ protected BundleProcessor delegate() {
+ return m_delegate;
+ }
+
+ /**
+ * A bundle is starting.
+ * The bundle is added to the list, and the processing delegated to the wrapped processor.
+ *
+ * @param bundle the bundle
+ */
+ @Override
+ public void activate(Bundle bundle) {
+ m_bundles.addLast(bundle);
+ super.activate(bundle);
+ }
+
+ /**
+ * A bundle is stopping.
+ * The bundle is removed from the list and the processing delegated to the wrapped processor.
+ *
+ * @param bundle the bundle
+ */
+ @Override
+ public void deactivate(Bundle bundle) {
+ m_bundles.remove(bundle);
+ super.deactivate(bundle);
+ }
+
+ /**
+ * iPOJO is stopping.
+ * The bundle that are still in the list are processed in the <strong>reverse</strong> order by the wrapped
+ * processor.
+ */
+ @Override
+ public void stop() {
+ // deactivate in reverse order
+ while (!m_bundles.isEmpty()) {
+ super.deactivate(m_bundles.pollLast());
+ }
+ super.stop();
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java
new file mode 100644
index 0000000..0da4057
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java
@@ -0,0 +1,151 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import org.apache.felix.ipojo.extender.internal.AbstractService;
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.BundleContext;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * An asynchronous implementation of the queue service. This implementation relies on an executor service.
+ */
+public class ExecutorQueueService extends AbstractService implements LifecycleQueueService {
+
+ /**
+ * The default thread pool size (3).
+ */
+ private final static int DEFAULT_QUEUE_SIZE = 3;
+
+ /**
+ * The executor service.
+ */
+ private final ExecutorService m_executorService;
+
+ /**
+ * The statistics populated by this queue service.
+ */
+ private final Statistic m_statistic = new Statistic();
+
+ /**
+ * Creates the queue service using the default pool size.
+ *
+ * @param bundleContext the bundle context.
+ */
+ public ExecutorQueueService(BundleContext bundleContext) {
+ this(bundleContext, DEFAULT_QUEUE_SIZE);
+ }
+
+ /**
+ * Creates the queue service.
+ *
+ * @param bundleContext the bundle context.
+ * @param size the thread pool size.
+ */
+ public ExecutorQueueService(BundleContext bundleContext, int size) {
+ this(bundleContext, Executors.newFixedThreadPool(size));
+ }
+
+ /**
+ * Creates the queue service.
+ *
+ * @param bundleContext the bundle context.
+ * @param size the thread pool size
+ * @param threadFactory the thread factory
+ */
+ public ExecutorQueueService(BundleContext bundleContext, int size, ThreadFactory threadFactory) {
+ this(bundleContext, Executors.newFixedThreadPool(size, threadFactory));
+ }
+
+
+ /**
+ * Creates the queue service.
+ * All others constructors delegates to this one.
+ *
+ * @param bundleContext the bundle context
+ * @param executorService the executor service we have to use
+ */
+ private ExecutorQueueService(BundleContext bundleContext, ExecutorService executorService) {
+ super(bundleContext, QueueService.class);
+ m_executorService = executorService;
+ }
+
+ /**
+ * Stops the service.
+ */
+ public void stop() {
+ m_executorService.shutdown();
+ super.stop();
+ }
+
+ @Override
+ protected Dictionary<String, ?> getServiceProperties() {
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ properties.put(QueueService.QUEUE_MODE_PROPERTY, QueueService.ASYNCHRONOUS_QUEUE_MODE);
+ return properties;
+ }
+
+ public int getFinished() {
+ return m_statistic.getFinishedCounter().get();
+ }
+
+ public int getWaiters() {
+ return m_statistic.getWaiters().size();
+ }
+
+ public int getCurrents() {
+ return m_statistic.getCurrentsCounter().get();
+ }
+
+ public List<JobInfo> getWaitersInfo() {
+ List<JobInfo> snapshot;
+ synchronized (m_statistic.getWaiters()) {
+ snapshot = new ArrayList<JobInfo>(m_statistic.getWaiters());
+ }
+ return Collections.unmodifiableList(snapshot);
+ }
+
+ /**
+ * Submits a job to the queue. The submitted job is wrapped into a {@link JobInfoCallable} to collect the
+ * statistics.
+ *
+ * @param callable the job
+ * @param callback callback called when the job is processed
+ * @param description a description of the job
+ * @return the reference on the submitted job
+ */
+ public <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description) {
+ return m_executorService.submit(new JobInfoCallable<T>(m_statistic, callable, callback, description));
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, String description) {
+ return submit(callable, null, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable) {
+ return submit(callable, "No description");
+ }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallable.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallable.java
new file mode 100644
index 0000000..2b98ca9
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallable.java
@@ -0,0 +1,172 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A callable computing job statistics. The job is given as another callable.
+ * The statistics are global, so must be used carefully.
+ */
+public class JobInfoCallable<T> implements Callable<T>, JobInfo {
+
+ /**
+ * The statistic object.
+ */
+ private final Statistic m_statistic;
+
+ /**
+ * The genuine job.
+ */
+ private final Callable<T> m_delegate;
+
+ /**
+ * A callback notified when the job is processed.
+ */
+ private final Callback<T> m_callback;
+
+ /**
+ * The job description.
+ */
+ private final String m_description;
+
+ /**
+ * The date (in milli) when this object is created.
+ */
+ private long enlistmentTime = System.currentTimeMillis();
+
+ /**
+ * The date when the job processing started.
+ */
+ private long startTime = -1;
+
+ /**
+ * The date when the job processing is completed.
+ */
+ private long endTime = -1;
+
+ /**
+ * Creates the job info callable.
+ *
+ * @param statistic the statistics that will be populated
+ * @param delegate the real job
+ * @param callback the callback notified when the job is completed
+ * @param description the job description
+ */
+ public JobInfoCallable(Statistic statistic,
+ Callable<T> delegate,
+ Callback<T> callback,
+ String description) {
+ m_statistic = statistic;
+ m_delegate = delegate;
+ m_callback = callback;
+ m_description = description;
+ m_statistic.getWaiters().add(this);
+ }
+
+ /**
+ * Executes the job.
+ * This method updates the statistics.
+ *
+ * @return the job result
+ * @throws Exception the job execution failed
+ */
+ public T call() throws Exception {
+ m_statistic.getWaiters().remove(this);
+ startTime = System.currentTimeMillis();
+ m_statistic.getCurrentsCounter().incrementAndGet();
+ T result = null;
+ try {
+ result = m_delegate.call();
+ return result;
+ } catch (Exception e) {
+ if (m_callback != null) {
+ m_callback.error(this, e);
+ }
+ throw e;
+ } finally {
+ m_statistic.getCurrentsCounter().decrementAndGet();
+ m_statistic.getFinishedCounter().incrementAndGet();
+ endTime = System.currentTimeMillis();
+ if (m_callback != null) {
+ m_callback.success(this, result);
+ }
+ }
+ }
+
+ /**
+ * @return the enlistment date.
+ */
+ public long getEnlistmentTime() {
+ return enlistmentTime;
+ }
+
+ /**
+ * @return the job start date.
+ */
+ public long getStartTime() {
+ return startTime;
+ }
+
+ /**
+ * @return the job completion date.
+ */
+ public long getEndTime() {
+ return endTime;
+ }
+
+ /**
+ * Computes the time spent in the waiting queue
+ *
+ * @return the waited time, if the job is still waiting, gets the current waited time
+ */
+ public long getWaitDuration() {
+ long end = startTime;
+ if (end == -1) {
+ // Not yet started
+ // Still waiting
+ end = System.currentTimeMillis();
+ }
+ return end - enlistmentTime;
+ }
+
+ /**
+ * Computes the time spent to execute the job (this does not include the waiting).
+ * If the job is not executed yet, or is still executing, {@literal -1} is returned
+ *
+ * @return the execution duration, or {@literal -1}.
+ */
+ public long getExecutionDuration() {
+ if ((startTime == -1) || (endTime == -1)) {
+ return -1;
+ }
+ return endTime - startTime;
+ }
+
+ /**
+ * @return the job description
+ */
+ public String getDescription() {
+ return m_description;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactory.java
new file mode 100644
index 0000000..2c0dc46
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactory.java
@@ -0,0 +1,74 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * A thread factory setting the name of the created thread.
+ * This thread factory delegates the thread creation on another factory, it just set the thread name (actually just
+ * set a prefix).
+ */
+public class PrefixedThreadFactory implements ThreadFactory {
+
+ /**
+ * The wrapped thread factory on which creation is delegated.
+ */
+ private final ThreadFactory m_threadFactory;
+
+ /**
+ * The prefix.
+ */
+ private final String m_prefix;
+
+ /**
+ * Creates the object using the default thread factory.
+ *
+ * @param prefix the prefix
+ */
+ public PrefixedThreadFactory(String prefix) {
+ this(Executors.defaultThreadFactory(), prefix);
+ }
+
+ /**
+ * Creates the object delegating to the given thread factory.
+ *
+ * @param threadFactory the thread factory
+ * @param prefix the prefix
+ */
+ public PrefixedThreadFactory(ThreadFactory threadFactory, String prefix) {
+ m_threadFactory = threadFactory;
+ m_prefix = prefix;
+ }
+
+ /**
+ * Creates a new thread.
+ * Prepend the prefix to the thread name
+ *
+ * @param r the runnable
+ * @return the thread object
+ */
+ public Thread newThread(Runnable r) {
+ Thread thread = m_threadFactory.newThread(r);
+ thread.setName(m_prefix + thread.getName());
+ return thread;
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/Statistic.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/Statistic.java
new file mode 100644
index 0000000..52e232a
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/Statistic.java
@@ -0,0 +1,70 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Objects wrapping the {@link org.apache.felix.ipojo.extender.queue.QueueService} statistics.
+ */
+public class Statistic {
+ /**
+ * The synchronized list of waiting jobs.
+ */
+ private final List<JobInfo> m_waiters = Collections.synchronizedList(new ArrayList<JobInfo>());
+
+ /**
+ * The number of completed jobs.
+ */
+ private final AtomicInteger m_finished = new AtomicInteger(0);
+
+ /**
+ * The number of job being processed.
+ */
+ private final AtomicInteger m_currents = new AtomicInteger(0);
+
+ /**
+ * @return the number of completed jobs.
+ */
+ public AtomicInteger getFinishedCounter() {
+ return m_finished;
+ }
+
+ /**
+ * @return the list of waiting jobs.
+ */
+ public List<JobInfo> getWaiters() {
+ return m_waiters;
+ }
+
+ /**
+ * @return the number of jobs under processing.
+ */
+ public AtomicInteger getCurrentsCounter() {
+ return m_currents;
+ }
+
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueService.java
new file mode 100644
index 0000000..6800b29
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueService.java
@@ -0,0 +1,129 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import org.apache.felix.ipojo.extender.internal.AbstractService;
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.BundleContext;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.*;
+
+/**
+ * An implementation of the Lifecycle Queue Service for synchronous processing.
+ */
+public class SynchronousQueueService extends AbstractService implements LifecycleQueueService {
+
+ private final Statistic m_statistic = new Statistic();
+
+ public SynchronousQueueService(BundleContext bundleContext) {
+ super(bundleContext, QueueService.class);
+ }
+
+ @Override
+ protected Dictionary<String, ?> getServiceProperties() {
+ Hashtable<String, Object> properties = new Hashtable<String, Object>();
+ properties.put(QueueService.QUEUE_MODE_PROPERTY, QueueService.SYNCHRONOUS_QUEUE_MODE);
+ return properties;
+ }
+
+ public int getFinished() {
+ return m_statistic.getFinishedCounter().get();
+ }
+
+ public int getWaiters() {
+ return 0;
+ }
+
+ public int getCurrents() {
+ return m_statistic.getCurrentsCounter().get();
+ }
+
+ public List<JobInfo> getWaitersInfo() {
+ return Collections.emptyList();
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description) {
+ JobInfoCallable<T> exec = new JobInfoCallable<T>(m_statistic, callable, callback, description);
+ try {
+ return new ImmediateFuture<T>(exec.call());
+ } catch (Exception e) {
+ return new ExceptionFuture<T>(e);
+ }
+
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, String description) {
+ return submit(callable, null, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable) {
+ return submit(callable, "No description");
+ }
+
+ private class ImmediateFuture<T> implements Future<T> {
+ private T m_result;
+
+ public ImmediateFuture(T result) {
+ m_result = result;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public boolean isDone() {
+ return true;
+ }
+
+ public T get() throws InterruptedException, ExecutionException {
+ return m_result;
+ }
+
+ public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return get();
+ }
+ }
+
+ private class ExceptionFuture<T> extends ImmediateFuture<T> {
+ private ExecutionException m_exception;
+
+ public ExceptionFuture(Exception e) {
+ super(null);
+ m_exception = new ExecutionException(e);
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ throw m_exception;
+ }
+
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelection.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelection.java
new file mode 100644
index 0000000..c7cb3d7
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelection.java
@@ -0,0 +1,55 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * A preference selection strategy based on a manifest header.
+ * By default, the {@literal IPOJO-Queue-Preference} header is used.
+ */
+public class HeaderPreferenceSelection implements PreferenceSelection {
+
+ private final String name;
+
+ public HeaderPreferenceSelection() {
+ this("IPOJO-Queue-Preference");
+ }
+
+ public HeaderPreferenceSelection(String name) {
+ this.name = name;
+ }
+
+ public Preference select(Bundle source) {
+ String header = source.getHeaders().get(name);
+
+ // No preference specified, return default
+ if (header == null) {
+ return Preference.DEFAULT;
+ }
+
+ header = header.trim();
+ try {
+ return Preference.valueOf(header.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return Preference.DEFAULT;
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/Preference.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/Preference.java
new file mode 100644
index 0000000..3a245f8
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/Preference.java
@@ -0,0 +1,36 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref;
+
+/**
+ * The processing preference.
+ * <p/>
+ * Each bundle can set the desired processing strategy, however the system can override this choice.
+ * <p/>
+ * 3 strategies are possible:
+ * <ul>
+ * <li>SYNC: for synchronous processing</li>
+ * <li>ASYNC: for asynchronous processing</li>
+ * <li>DEFAULT: using the default behavior</li>
+ * </ul>
+ */
+public enum Preference {
+ SYNC, ASYNC, DEFAULT
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceQueueService.java
new file mode 100644
index 0000000..b1317cc
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceQueueService.java
@@ -0,0 +1,161 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref;
+
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+/**
+ * An implementation of the queue service delegating on the synchronous and asynchronous implementations according to
+ * the processing preference.
+ */
+public class PreferenceQueueService implements LifecycleQueueService {
+
+ /**
+ * The preference selection strategy.
+ */
+ private final PreferenceSelection m_strategy;
+
+ /**
+ * The synchronous queue service.
+ */
+ private final LifecycleQueueService m_syncQueue;
+
+ /**
+ * The asynchronous queue service.
+ */
+ private final LifecycleQueueService m_asyncQueue;
+
+ /**
+ * The default queue service (chosen using the global preference).
+ */
+ private QueueService m_defaultQueue;
+
+ /**
+ * Creates the preference queue service.
+ *
+ * @param strategy the preference strategy
+ * @param syncQueue the synchronous queue service
+ * @param asyncQueue the asynchronous queue service
+ */
+ public PreferenceQueueService(PreferenceSelection strategy, LifecycleQueueService syncQueue, LifecycleQueueService asyncQueue) {
+ m_strategy = strategy;
+ m_syncQueue = syncQueue;
+ m_asyncQueue = asyncQueue;
+
+ // By default, system queue is asynchronous
+ m_defaultQueue = asyncQueue;
+ }
+
+ /**
+ * Starting queues.
+ */
+ public void start() {
+ m_syncQueue.start();
+ m_asyncQueue.start();
+ }
+
+ /**
+ * Stopping queues.
+ */
+ public void stop() {
+ m_syncQueue.stop();
+ m_asyncQueue.stop();
+ }
+
+ /**
+ * @return the number of completed jobs.
+ */
+ public int getFinished() {
+ return m_syncQueue.getFinished() + m_asyncQueue.getFinished();
+ }
+
+ /**
+ * @return the number of waiting jobs.
+ */
+ public int getWaiters() {
+ return m_syncQueue.getWaiters() + m_asyncQueue.getWaiters();
+ }
+
+ /**
+ * @return the number of jobs being processed.
+ */
+ public int getCurrents() {
+ return m_syncQueue.getCurrents() + m_asyncQueue.getCurrents();
+ }
+
+ /**
+ * Gets the number of waiting job. Notice that the synchronous queue does not have a waiting queue.
+ *
+ * @return the number of waiting job.
+ */
+ public List<JobInfo> getWaitersInfo() {
+ // synchronous queue as no waiters, so snapshot is always empty and can be ignored
+ return m_asyncQueue.getWaitersInfo();
+ }
+
+ /**
+ * Submits a job to the right queue.
+ * The queue selection works as follow:
+ * If the bundle submitting the queue has a preference, use this preference, otherwise use the default preference.
+ *
+ * @param callable the job
+ * @param callback callback called when the job is processed
+ * @param description a description of the job
+ * @return the reference of the submitted job
+ */
+ public <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description) {
+ // Argghhh, how can I choose between the 2 QueueService ?
+ // I was expecting to have the source Bundle to make a decision
+ Preference preference = Preference.DEFAULT;
+ if (callable instanceof BundleReference) {
+ Bundle bundle = ((BundleReference) callable).getBundle();
+ preference = m_strategy.select(bundle);
+ }
+
+ QueueService selected = m_defaultQueue;
+ switch (preference) {
+ case ASYNC:
+ selected = m_asyncQueue;
+ break;
+ case SYNC:
+ selected = m_syncQueue;
+ break;
+ }
+
+ return selected.submit(callable, callback, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, String description) {
+ return submit(callable, null, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable) {
+ return submit(callable, "No description");
+ }
+}
\ No newline at end of file
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceSelection.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceSelection.java
new file mode 100644
index 0000000..e07f451
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/PreferenceSelection.java
@@ -0,0 +1,29 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * An interface to choose the processing preference.
+ */
+public interface PreferenceSelection {
+ Preference select(Bundle source);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueService.java
new file mode 100644
index 0000000..ffbb26d
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueService.java
@@ -0,0 +1,130 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref.enforce;
+
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.pref.Preference;
+import org.apache.felix.ipojo.extender.internal.queue.pref.PreferenceSelection;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.util.Log;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+/**
+ * A queue service implementation enforcing the processing preference choice for the bundle.
+ */
+public class EnforcedQueueService extends ForwardingQueueService {
+
+ /**
+ * The preference strategy selection.
+ */
+ private final PreferenceSelection m_strategy;
+
+ /**
+ * The lifecycle queue service on which the job are delegated.
+ */
+ private final LifecycleQueueService m_queueService;
+
+ /**
+ * The preference.
+ */
+ private final Preference m_enforced;
+
+ /**
+ * The logger.
+ */
+ private final Log m_logger;
+
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy
+ * @param queueService the queue service
+ * @param enforced the preference we want to enforce
+ * @param logger the logger
+ */
+ public EnforcedQueueService(PreferenceSelection strategy, LifecycleQueueService queueService, Preference enforced, Log logger) {
+ m_strategy = strategy;
+ m_queueService = queueService;
+ m_enforced = enforced;
+ m_logger = logger;
+ }
+
+ @Override
+ protected LifecycleQueueService delegate() {
+ return m_queueService;
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description) {
+ checkBundlePreference(callable);
+ return super.submit(callable, callback, description);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> callable, String description) {
+ checkBundlePreference(callable);
+ return super.submit(callable, description);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> callable) {
+ checkBundlePreference(callable);
+ return super.submit(callable);
+ }
+
+ /**
+ * Checks the bundle processing preference and compare with the enforced preference.
+ *
+ * @param callable the callable
+ */
+ private void checkBundlePreference(Callable<?> callable) {
+ if (callable instanceof BundleReference) {
+ Bundle bundle = ((BundleReference) callable).getBundle();
+ Preference preference = m_strategy.select(bundle);
+
+ if (!isCompatible(preference)) {
+ // Log a warning, Bundle asked for a synchronous processing,
+ // but we will enforce parametrised processing
+ String message = String.format(
+ "Enforcing %s mode for Bundle %s/%s [%d] (asking for %s)",
+ m_enforced.name(),
+ bundle.getSymbolicName(),
+ bundle.getVersion(),
+ bundle.getBundleId(),
+ preference
+ );
+ m_logger.log(Logger.WARNING, message);
+ }
+ }
+ }
+
+ /**
+ * @param preference the preference.
+ * @return is the given preference compatible with the enforced one.
+ */
+ private boolean isCompatible(Preference preference) {
+ return ((preference == m_enforced) || (preference == Preference.DEFAULT));
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/ForwardingQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/ForwardingQueueService.java
new file mode 100644
index 0000000..33139ae
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/ForwardingQueueService.java
@@ -0,0 +1,72 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref.enforce;
+
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+/**
+ * A queue service implementation delegating to a {@link LifecycleQueueService}.
+ */
+public abstract class ForwardingQueueService implements LifecycleQueueService {
+
+ protected abstract LifecycleQueueService delegate();
+
+ public void start() {
+ delegate().start();
+ }
+
+ public void stop() {
+ delegate().stop();
+ }
+
+ public int getFinished() {
+ return delegate().getFinished();
+ }
+
+ public int getWaiters() {
+ return delegate().getWaiters();
+ }
+
+ public int getCurrents() {
+ return delegate().getCurrents();
+ }
+
+ public List<JobInfo> getWaitersInfo() {
+ return delegate().getWaitersInfo();
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description) {
+ return delegate().submit(callable, callback, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable, String description) {
+ return delegate().submit(callable, description);
+ }
+
+ public <T> Future<T> submit(Callable<T> callable) {
+ return delegate().submit(callable);
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/Callback.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/Callback.java
new file mode 100644
index 0000000..5a8dded
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/Callback.java
@@ -0,0 +1,42 @@
+/*
+ * 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.felix.ipojo.extender.queue;
+
+/**
+ * When submitting a processing job, a callback object can be passed to the submission process. This callback is
+ * notified when the job processing is completed.
+ */
+public interface Callback<T> {
+ /**
+ * The job was completed successfully.
+ *
+ * @param info the job info
+ * @param result the job result
+ */
+ void success(JobInfo info, T result);
+
+ /**
+ * The job was not completed successfully.
+ *
+ * @param info the job info
+ * @param exception the thrown exception
+ */
+ void error(JobInfo info, Exception exception);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/JobInfo.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/JobInfo.java
new file mode 100644
index 0000000..3c78db8
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/JobInfo.java
@@ -0,0 +1,68 @@
+/*
+ * 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.felix.ipojo.extender.queue;
+
+/**
+ * Interface to retrieve information about the job execution.
+ */
+public interface JobInfo {
+
+ /**
+ * Gets the submission time of the job.
+ *
+ * @return the submission time
+ */
+ long getEnlistmentTime();
+
+ /**
+ * Gets the starting time. This is the date when the job execution starts.
+ *
+ * @return the start time
+ */
+ long getStartTime();
+
+ /**
+ * Gets the completion time. This is the date when the job execution ends.
+ *
+ * @return the end time
+ */
+ long getEndTime();
+
+ /**
+ * Gets the time spent in the waiting queue.
+ *
+ * @return the waited time
+ */
+ long getWaitDuration();
+
+ /**
+ * Gets the execution duration.
+ *
+ * @return the execution duration, {@literal -1} is this duration cannot be computed
+ */
+ long getExecutionDuration();
+
+ /**
+ * Gets the job description
+ *
+ * @return the description
+ */
+ String getDescription();
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/QueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/QueueService.java
new file mode 100644
index 0000000..e66ba47
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/queue/QueueService.java
@@ -0,0 +1,118 @@
+/*
+ * 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.felix.ipojo.extender.queue;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+/**
+ * Definition of the queue service.
+ * The queue service is used to enqueue the bundle processing job.
+ * The processing order is depending of the implementation.
+ */
+public interface QueueService {
+ /**
+ * The service property specifying the queue mode (sync/async/pref).
+ */
+ String QUEUE_MODE_PROPERTY = "ipojo.queue.mode";
+
+ /**
+ * Synchronous queue mode.
+ */
+ String SYNCHRONOUS_QUEUE_MODE = "sync";
+
+ /**
+ * Asynchronous queue mode.
+ */
+ String ASYNCHRONOUS_QUEUE_MODE = "async";
+
+ /**
+ * Preference queue mode.
+ */
+ String PREFERENCE_QUEUE_MODE = "pref";
+
+ /**
+ * The service property specifying the queue scope (global/...).
+ */
+ String QUEUE_SCOPE_PROPERTY = "ipojo.queue.scope";
+
+ /**
+ * Global queue scope.
+ */
+ String GLOABL_QUEUE_SCOPE = "global";
+
+ /**
+ * @return the number of jobs that have been executed entirely
+ * (including successful and erroneous jobs).
+ */
+ int getFinished();
+
+ /**
+ * @return the number of jobs scheduled but not yet started.
+ */
+ int getWaiters();
+
+ /**
+ * @return the number of jobs currently executed (started but not finished).
+ */
+ int getCurrents();
+
+ /**
+ * @return a snapshot of the currently waiting jobs.
+ */
+ List<JobInfo> getWaitersInfo();
+
+ // Note: I don't want us to store error reports there
+ // Maybe we should use EventAdmin to send notifications ?
+ // getErrors
+
+ /**
+ * Submits a job to the queue service.
+ *
+ * @param callable the job
+ * @param callback callback called when the job is processed
+ * @param description a description of the job
+ * @return the future object to retrieve the result
+ */
+ <T> Future<T> submit(Callable<T> callable, Callback<T> callback, String description);
+
+ /**
+ * Submits a job to the queue service.
+ *
+ * @param callable the job
+ * @param description a description of the job
+ * @return the future object to retrieve the result
+ */
+ <T> Future<T> submit(Callable<T> callable, String description);
+
+ /**
+ * Submits a job to the queue service.
+ *
+ * @param callable the job
+ * @return the future object to retrieve the result
+ */
+ <T> Future<T> submit(Callable<T> callable);
+
+
+ // TODO Add a way to add global callbacks
+ //<T> void addGlobalCallback(Callback<T> callback);
+ // <T> void removeGlobalCallback(Callback<T> callback);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java
index a5f6c7e..8d3dbe1 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/architecture/ArchitectureHandler.java
@@ -20,6 +20,7 @@
import java.util.Dictionary;
+import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.PrimitiveHandler;
import org.apache.felix.ipojo.architecture.Architecture;
import org.apache.felix.ipojo.architecture.InstanceDescription;
@@ -43,7 +44,7 @@
* @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
*/
public void configure(Element metadata, Dictionary configuration) {
- m_name = (String) configuration.get("instance.name");
+ m_name = (String) configuration.get(Factory.INSTANCE_NAME_PROPERTY);
}
/**
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
index 37e1b83..fc3955d 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
@@ -21,6 +21,7 @@
import java.util.*;
import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.HandlerFactory;
import org.apache.felix.ipojo.PrimitiveHandler;
import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
@@ -352,7 +353,7 @@
if (m_managedServicePID != null && m_sr == null) {
Properties props = new Properties();
props.put(Constants.SERVICE_PID, m_managedServicePID);
- props.put("instance.name", getInstanceManager().getInstanceName());
+ props.put(Factory.INSTANCE_NAME_PROPERTY, getInstanceManager().getInstanceName());
props.put("factory.name", getInstanceManager().getFactory().getFactoryName());
// Security Check
@@ -562,7 +563,7 @@
Enumeration e = m_propagatedFromCA.keys();
while (e.hasMoreElements()) {
String k = (String) e.nextElement();
- if (! k.equals("instance.name")) {
+ if (! k.equals(Factory.INSTANCE_NAME_PROPERTY)) {
props.put(k, m_propagatedFromCA.get(k));
}
}
@@ -572,7 +573,7 @@
Enumeration e = m_propagatedFromInstance.keys();
while (e.hasMoreElements()) {
String k = (String) e.nextElement();
- if (! k.equals("instance.name")) { // Skip instance.name
+ if (! k.equals(Factory.INSTANCE_NAME_PROPERTY)) { // Skip instance.name
props.put(k, m_propagatedFromInstance.get(k));
}
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
index 0e6d77d..daa50cc 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
@@ -21,6 +21,7 @@
import java.util.Iterator;
import java.util.List;
+import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
@@ -148,10 +149,10 @@
while (iterator.hasNext()) {
Element use = new Element("Uses", "");
ServiceReference ref = (ServiceReference) iterator.next();
- use.addAttribute(new Attribute("service.id", ref.getProperty(Constants.SERVICE_ID).toString()));
- String instance = (String) ref.getProperty("instance.name");
+ use.addAttribute(new Attribute(Constants.SERVICE_ID, ref.getProperty(Constants.SERVICE_ID).toString()));
+ String instance = (String) ref.getProperty(Factory.INSTANCE_NAME_PROPERTY);
if (instance != null) {
- use.addAttribute(new Attribute("instance.name", instance));
+ use.addAttribute(new Attribute(Factory.INSTANCE_NAME_PROPERTY, instance));
}
dep.addElement(use);
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
index a539984..62d53b2 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
@@ -32,10 +32,7 @@
import java.util.Map;
import java.util.Properties;
-import org.apache.felix.ipojo.ComponentInstance;
-import org.apache.felix.ipojo.ConfigurationException;
-import org.apache.felix.ipojo.IPOJOServiceFactory;
-import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.*;
import org.apache.felix.ipojo.util.Callback;
import org.apache.felix.ipojo.util.Property;
import org.apache.felix.ipojo.util.SecurityHelper;
@@ -152,11 +149,11 @@
// Add instance name, factory name and factory version is set.
try {
- addProperty(new Property("instance.name", null, null, handler.getInstanceManager().getInstanceName(), String.class.getName(), handler.getInstanceManager(), handler));
+ addProperty(new Property(Factory.INSTANCE_NAME_PROPERTY, null, null, handler.getInstanceManager().getInstanceName(), String.class.getName(), handler.getInstanceManager(), handler));
addProperty(new Property("factory.name", null, null, handler.getInstanceManager().getFactory().getFactoryName(), String.class.getName(), handler.getInstanceManager(), handler));
if (handler.getInstanceManager().getFactory().getVersion() != null) {
- addProperty(new Property("factory.version", null, null, handler.getInstanceManager().getFactory().getVersion(), String.class.getName(), handler.getInstanceManager(), handler));
+ addProperty(new Property(Factory.FACTORY_VERSION_PROPERTY, null, null, handler.getInstanceManager().getFactory().getVersion(), String.class.getName(), handler.getInstanceManager(), handler));
}
// Add the service.* if defined
@@ -485,8 +482,8 @@
Dictionary newProps = (Dictionary) (updated.clone());
// Remove keys that must not be compared
- newProps.remove("instance.name");
- oldProps.remove("instance.name");
+ newProps.remove(Factory.INSTANCE_NAME_PROPERTY);
+ oldProps.remove(Factory.INSTANCE_NAME_PROPERTY);
newProps.remove(Constants.SERVICE_ID);
oldProps.remove(Constants.SERVICE_ID);
newProps.remove(Constants.SERVICE_PID);
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
index 1acbfe1..e5bd89e 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
@@ -25,6 +25,7 @@
import java.util.Map;
import java.util.Properties;
+import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
@@ -91,7 +92,7 @@
String version = instance.getAttribute("version");
if (name != null) {
- dict.put("instance.name", instance.getAttribute("name"));
+ dict.put(Factory.INSTANCE_NAME_PROPERTY, instance.getAttribute("name"));
}
if (comp == null) {
@@ -101,7 +102,7 @@
dict.put("component", comp);
if (version != null) {
- dict.put("factory.version", version);
+ dict.put(Factory.FACTORY_VERSION_PROPERTY, version);
}
Element[] props = instance.getElements("property");
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Log.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Log.java
new file mode 100644
index 0000000..5730053
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Log.java
@@ -0,0 +1,46 @@
+/*
+ * 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.felix.ipojo.util;
+
+/**
+ * API for the iPOJO internal Log system.
+ */
+public interface Log {
+ /**
+ * The Log Level ERROR.
+ */
+ int ERROR = 1;
+ /**
+ * The Log Level WARNING.
+ */
+ int WARNING = 2;
+ /**
+ * The Log Level INFO.
+ */
+ int INFO = 3;
+ /**
+ * The Log Level DEBUG.
+ */
+ int DEBUG = 4;
+
+ void log(int level, String msg);
+
+ void log(int level, String msg, Throwable exception);
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
index e28009b..b7d6760 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Logger.java
@@ -20,7 +20,7 @@
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.ErrorHandler;
-import org.apache.felix.ipojo.Extender;
+import org.apache.felix.ipojo.extender.internal.Extender;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
@@ -32,7 +32,7 @@
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public class Logger {
+public class Logger implements Log {
/**
* The iPOJO default log level property.
@@ -50,22 +50,22 @@
/**
* The Log Level ERROR.
*/
- public static final int ERROR = 1;
+ public static final int ERROR = Log.ERROR;
/**
* The Log Level WARNING.
*/
- public static final int WARNING = 2;
+ public static final int WARNING = Log.WARNING;
/**
* The Log Level INFO.
*/
- public static final int INFO = 3;
+ public static final int INFO = Log.INFO;
/**
* The Log Level DEBUG.
*/
- public static final int DEBUG = 4;
+ public static final int DEBUG = Log.DEBUG;
/**
* The Bundle Context used to get the
@@ -332,7 +332,6 @@
handler.onWarning(m_instance, msg, error);
} // The others case are not supported
m_context.ungetService(ref);
- return;
} // Else do nothing...
} catch (IllegalStateException e) {
// Ignore
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclarationTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclarationTestCase.java
new file mode 100644
index 0000000..61e29ee
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultExtensionDeclarationTestCase.java
@@ -0,0 +1,66 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.Hashtable;
+
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the behavior of the devault extension declaration.
+ */
+public class DefaultExtensionDeclarationTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testRegistration() throws Exception {
+ m_bundleContext = mock(BundleContext.class);
+ DefaultExtensionDeclaration declaration = new DefaultExtensionDeclaration(m_bundleContext, null, "component");
+
+ // Before start, declaration is not bound
+ assertFalse(declaration.getStatus().isBound());
+ declaration.start();
+
+ // After start, declaration is bound
+ assertTrue(declaration.getStatus().isBound());
+
+ // Verify service registration
+ ArgumentCaptor<Hashtable> argument = ArgumentCaptor.forClass(Hashtable.class);
+ verify(m_bundleContext).registerService(eq(ExtensionDeclaration.class.getName()), eq(declaration), argument.capture());
+ assertEquals(argument.getValue().get(ExtensionDeclaration.EXTENSION_NAME_PROPERTY), "component");
+
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java
new file mode 100644
index 0000000..e372f00
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultInstanceDeclarationTestCase.java
@@ -0,0 +1,85 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import java.util.Hashtable;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.extender.InstanceDeclaration;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the behavior of the default instance declaration.
+ */
+public class DefaultInstanceDeclarationTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Captor
+ private ArgumentCaptor<Hashtable<String, Object>> argument;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testRegistrationWithEmptyConfiguration() throws Exception {
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello");
+ declaration.start();
+
+ // Declaration is not bound
+ assertFalse(declaration.getStatus().isBound());
+
+ // Verify service registration
+ verify(m_bundleContext).registerService(eq(InstanceDeclaration.class.getName()), eq(declaration), argument.capture());
+ assertEquals(argument.getValue().get(InstanceDeclaration.COMPONENT_NAME_PROPERTY), "component.Hello");
+ assertNull(argument.getValue().get(InstanceDeclaration.COMPONENT_VERSION_PROPERTY));
+
+ }
+
+ public void testRegistrationWithVersionedConfiguration() throws Exception {
+ Hashtable<String, Object> configuration = new Hashtable<String, Object>();
+ configuration.put(Factory.FACTORY_VERSION_PROPERTY, "1.0.0");
+ DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(m_bundleContext, "component.Hello", configuration);
+
+ declaration.start();
+
+ // Declaration is not bound
+ assertFalse(declaration.getStatus().isBound());
+
+ // Verify service registration
+ verify(m_bundleContext).registerService(eq(InstanceDeclaration.class.getName()), eq(declaration), argument.capture());
+ assertEquals(argument.getValue().get(InstanceDeclaration.COMPONENT_NAME_PROPERTY), "component.Hello");
+ assertEquals(argument.getValue().get(InstanceDeclaration.COMPONENT_VERSION_PROPERTY), "1.0.0");
+
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclarationTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclarationTestCase.java
new file mode 100644
index 0000000..acf123b
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/declaration/DefaultTypeDeclarationTestCase.java
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.ipojo.extender.internal.declaration;
+
+import junit.framework.TestCase;
+import org.apache.felix.ipojo.IPojoFactory;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.extender.TypeDeclaration;
+import org.apache.felix.ipojo.extender.builder.FactoryBuilder;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Checks the behavior of {@link TypeDeclaration}.
+ */
+public class DefaultTypeDeclarationTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+ @Mock
+ private Filter filter;
+ @Mock
+ private ServiceReference extensionReference;
+ @Mock
+ private ExtensionDeclaration m_extension;
+ @Mock
+ private FactoryBuilder m_builder;
+ @Mock
+ private IPojoFactory factory;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testRegistration() throws Exception {
+ when(m_bundleContext.createFilter(anyString())).thenReturn(filter);
+
+ DefaultTypeDeclaration declaration = new DefaultTypeDeclaration(m_bundleContext, element("component", "component.Hello"));
+ declaration.start();
+
+ // Declaration is not bound
+ assertFalse(declaration.getStatus().isBound());
+
+ // Verify service registration
+ verify(m_bundleContext).registerService(TypeDeclaration.class.getName(), declaration, null);
+
+ }
+
+ private Element element(String type, String name) {
+ Element root = new Element(type, null);
+ root.addAttribute(new Attribute("name", name));
+ return root;
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinkerTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinkerTestCase.java
new file mode 100644
index 0000000..5845772
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/linker/DeclarationLinkerTestCase.java
@@ -0,0 +1,116 @@
+/*
+ * 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.felix.ipojo.extender.internal.linker;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the linker behavior.
+ */
+public class DeclarationLinkerTestCase extends TestCase {
+ @Mock
+ private Bundle m_bundle;
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Mock
+ private Filter filter;
+
+ @Mock
+ private ServiceReference m_reference;
+
+ @Mock
+ private QueueService m_queueService;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testActivationDeactivation() throws Exception {
+/*
+ when(filter.match(m_reference)).thenReturn(true);
+ when(m_bundleContext.getService(m_reference)).thenReturn(m_extension);
+ when(m_extension.getFactoryBuilder()).thenReturn(m_builder);
+ when(m_builder.build(any(BundleContext.class), any(Element.class))).thenReturn(factory);
+*/
+ when(m_bundleContext.getService(m_reference)).thenReturn(new DefaultTypeDeclaration(m_bundleContext, element("component", "test.Hello")));
+ when(m_bundleContext.createFilter(anyString())).thenReturn(filter);
+ when(m_reference.getBundle()).thenReturn(m_bundle);
+ when(m_bundle.getBundleContext()).thenReturn(m_bundleContext);
+
+ DeclarationLinker linker = new DeclarationLinker(m_bundleContext, m_queueService);
+ assertNotNull(linker.addingService(m_reference));
+
+/*
+ DefaultTypeDeclaration declaration = new DefaultTypeDeclaration(m_bundleContext, element("component", "component.Hello"));
+ declaration.start();
+
+ // Declaration is not bound
+ assertFalse(declaration.getStatus().isBound());
+
+ verify(m_bundleContext).addServiceListener(captor.capture(), anyString());
+
+ ServiceListener listener = captor.getValue();
+ ServiceEvent e = new ServiceEvent(ServiceEvent.REGISTERED, m_reference);
+ listener.serviceChanged(e);
+
+ verify(factory).addFactoryStateListener(fslCaptor.capture());
+ FactoryStateListener fsl = fslCaptor.getValue();
+ fsl.stateChanged(factory, Factory.VALID);
+
+ assertTrue(declaration.getStatus().isBound());
+
+ // The 2nd tracker should have registered its own listener
+ verify(m_bundleContext, times(2)).addServiceListener(captor.capture(), anyString());
+ ServiceListener listener2 = captor.getValue();
+ assertNotSame(listener, listener2);
+
+ ServiceEvent e2 = new ServiceEvent(ServiceEvent.UNREGISTERING, m_reference);
+ listener.serviceChanged(e2);
+
+ // After extension removal, the declaration should be unbound
+ assertFalse(declaration.getStatus().isBound());
+*/
+ }
+
+
+ private Element element(String type, String name) {
+ Element root = new Element(type, null);
+ root.addAttribute(new Attribute("name", name));
+ return root;
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessorTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessorTestCase.java
new file mode 100644
index 0000000..0ba895c
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ChainedBundleProcessorTestCase.java
@@ -0,0 +1,76 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import static org.mockito.Mockito.inOrder;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the behavior of the chained bundle processor.
+ */
+public class ChainedBundleProcessorTestCase extends TestCase {
+
+ @Mock
+ private BundleProcessor m_delegate1;
+
+ @Mock
+ private BundleProcessor m_delegate2;
+
+ @Mock
+ private Bundle m_bundle;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testActivationThenDeactivateAreCalledWithReversedProcessorsList() throws Exception {
+ ChainedBundleProcessor chain = ChainedBundleProcessor.create(m_delegate1, m_delegate2);
+
+ chain.activate(m_bundle);
+ chain.deactivate(m_bundle);
+
+ InOrder order = inOrder(m_delegate1, m_delegate2);
+ order.verify(m_delegate1).activate(m_bundle);
+ order.verify(m_delegate2).activate(m_bundle);
+ order.verify(m_delegate2).deactivate(m_bundle);
+ order.verify(m_delegate1).deactivate(m_bundle);
+ }
+
+ public void testStartStopIsCalledWithReversedProcessorsList() throws Exception {
+ ChainedBundleProcessor chain = ChainedBundleProcessor.create(m_delegate1, m_delegate2);
+
+ chain.start();
+ chain.stop();
+
+ InOrder order = inOrder(m_delegate1, m_delegate2);
+ order.verify(m_delegate1).start();
+ order.verify(m_delegate2).start();
+ order.verify(m_delegate2).stop();
+ order.verify(m_delegate1).stop();
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessorTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessorTestCase.java
new file mode 100644
index 0000000..b87f8c0
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ExtensionBundleProcessorTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import junit.framework.TestCase;
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.extender.ExtensionDeclaration;
+import org.apache.felix.ipojo.util.Logger;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests the extension bundle processor.
+ */
+public class ExtensionBundleProcessorTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+ @Mock
+ private Logger m_logger;
+ @Mock
+ private Bundle m_bundle;
+
+ @Override
+ public void setUp() throws Exception {
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ MockitoAnnotations.initMocks(this);
+
+ when(m_bundle.getBundleContext()).thenReturn(m_bundleContext);
+ when(m_bundleContext.getBundle()).thenReturn(m_bundle);
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ }
+
+ public void testEmptyExtensionBundle() throws Exception {
+ ExtensionBundleProcessor processor = new ExtensionBundleProcessor(m_logger);
+ processor.activate(m_bundle);
+ verify(m_bundleContext, never()).registerService((Class<?>) null, null, null);
+ }
+
+ public void testSimpleExtensionBundle() throws Exception {
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put(ExtensionBundleProcessor.IPOJO_EXTENSION, "component:" + ComponentFactory.class.getName());
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ Mockito.<Class<?>>when(m_bundle.loadClass(anyString())).thenReturn(ComponentFactory.class);
+
+ ExtensionBundleProcessor processor = new ExtensionBundleProcessor(m_logger);
+ processor.activate(m_bundle);
+ verify(m_bundleContext).registerService(eq(ExtensionDeclaration.class.getName()), any(ExtensionDeclaration.class), any(Dictionary.class));
+ }
+
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessorTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessorTestCase.java
new file mode 100644
index 0000000..7a7ded2
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/processor/ReverseBundleProcessorTestCase.java
@@ -0,0 +1,84 @@
+/*
+ * 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.felix.ipojo.extender.internal.processor;
+
+import org.apache.felix.ipojo.extender.internal.BundleProcessor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the behavior of the reverse bundle processor.
+ */
+public class ReverseBundleProcessorTestCase extends TestCase {
+
+ @Mock
+ private Bundle m_bundle1;
+
+ @Mock
+ private Bundle m_bundle2;
+
+ @Mock
+ private Bundle m_bundle3;
+
+ @Mock
+ private BundleProcessor m_delegate;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testReverseOrderWhenStopped() throws Exception {
+ ReverseBundleProcessor reverse = new ReverseBundleProcessor(m_delegate);
+ reverse.activate(m_bundle1);
+ reverse.activate(m_bundle2);
+ reverse.activate(m_bundle3);
+
+ reverse.stop();
+
+ InOrder order = Mockito.inOrder(m_delegate);
+ order.verify(m_delegate).deactivate(m_bundle3);
+ order.verify(m_delegate).deactivate(m_bundle2);
+ order.verify(m_delegate).deactivate(m_bundle1);
+
+ }
+
+ public void testReverseOrderWhenStoppedAndRemovedElements() throws Exception {
+ ReverseBundleProcessor reverse = new ReverseBundleProcessor(m_delegate);
+ reverse.activate(m_bundle1);
+ reverse.activate(m_bundle2);
+ reverse.activate(m_bundle3);
+
+ reverse.deactivate(m_bundle2);
+
+ reverse.stop();
+
+ InOrder order = Mockito.inOrder(m_delegate);
+ order.verify(m_delegate).deactivate(m_bundle2);
+ order.verify(m_delegate).deactivate(m_bundle3);
+ order.verify(m_delegate).deactivate(m_bundle1);
+
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java
new file mode 100644
index 0000000..363eaab
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java
@@ -0,0 +1,119 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.util.Dictionary;
+import java.util.concurrent.Future;
+
+import org.apache.felix.ipojo.extender.internal.queue.callable.SleepingCallable;
+import org.apache.felix.ipojo.extender.internal.queue.callable.StringCallable;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the Executor Queue Service.
+ */
+public class ExecutorQueueServiceTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Mock
+ private ServiceRegistration<?> m_registration;
+
+ @Mock
+ private Callback<String> m_callback;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testQueueServiceRegistration() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext);
+
+ Mockito.<ServiceRegistration<?>>when(m_bundleContext.registerService(eq(QueueService.class.getName()), eq(queueService), any(Dictionary.class))).thenReturn(m_registration);
+
+ queueService.start();
+
+ verify(m_bundleContext).registerService(eq(QueueService.class.getName()), eq(queueService), any(Dictionary.class));
+
+ queueService.stop();
+
+ verify(m_registration).unregister();
+
+ }
+
+ public void testCallbackIsInvoked() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext);
+ queueService.start();
+
+ Future<String> future = queueService.submit(new StringCallable(), m_callback, "hello");
+
+ // Wait for callable to finish
+ assertEquals("hello", future.get());
+ verify(m_callback).success(any(JobInfo.class), eq("hello"));
+ verify(m_callback, never()).error(any(JobInfo.class), any(Exception.class));
+
+ queueService.stop();
+ }
+
+ public void testStatistics() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext, 2);
+ queueService.start();
+
+ // We create 4 job, so that we have the 2 first execution while the 2 others are waiting
+ Future<String> one = queueService.submit(new SleepingCallable(50, "1"), m_callback, "First");
+ Future<String> two = queueService.submit(new SleepingCallable(50, "2"), m_callback, "Second");
+ Future<String> three = queueService.submit(new SleepingCallable(50, "3"), m_callback, "Third");
+ Future<String> four = queueService.submit(new SleepingCallable(50, "4"), m_callback, "Fourth");
+
+ // Wait for callable to finish
+ one.get();
+ two.get();
+ assertEquals(2, queueService.getFinished());
+ assertEquals(2, queueService.getCurrents());
+
+ three.get();
+ four.get();
+
+ assertEquals(4, queueService.getFinished());
+ assertEquals(0, queueService.getCurrents());
+ assertEquals(0, queueService.getWaiters());
+
+ queueService.stop();
+ }
+
+}
+
+
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallableTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallableTestCase.java
new file mode 100644
index 0000000..1b1541a
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/JobInfoCallableTestCase.java
@@ -0,0 +1,53 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import org.apache.felix.ipojo.extender.internal.queue.callable.StringCallable;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the job info callable.
+ */
+public class JobInfoCallableTestCase extends TestCase {
+ public void testCall() throws Exception {
+ Statistic stat = new Statistic();
+ long mark = System.currentTimeMillis();
+ JobInfoCallable<String> info = new JobInfoCallable<String>(stat, new StringCallable(), null, null);
+
+ // Before execution
+ assertTrue(info.getEnlistmentTime() >= mark);
+ assertEquals(-1, info.getExecutionDuration());
+ assertTrue(info.getWaitDuration() <= (System.currentTimeMillis() - mark));
+
+ assertTrue(stat.getWaiters().contains(info));
+ assertEquals(0, stat.getCurrentsCounter().get());
+ assertEquals(0, stat.getFinishedCounter().get());
+
+ info.call();
+
+ assertTrue(info.getExecutionDuration() != -1);
+
+ assertTrue(stat.getWaiters().isEmpty());
+ assertEquals(0, stat.getCurrentsCounter().get());
+ assertEquals(1, stat.getFinishedCounter().get());
+
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java
new file mode 100644
index 0000000..ec00530
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java
@@ -0,0 +1,50 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the behavior of the {@link PrefixedThreadFactory}.
+ */
+public class PrefixedThreadFactoryTestCase extends TestCase {
+ public void testNewThread() throws Exception {
+ PrefixedThreadFactory factory = new PrefixedThreadFactory("test ");
+ Thread t = factory.newThread(mock(Runnable.class));
+ assertTrue(t.getName().startsWith("test "));
+ }
+ public void testNewThreadDelegation() throws Exception {
+ ThreadFactory delegate = mock(ThreadFactory.class);
+ when(delegate.newThread(any(Runnable.class))).thenReturn(new Thread("thread"));
+ PrefixedThreadFactory factory = new PrefixedThreadFactory(delegate, "test ");
+ Thread t = factory.newThread(mock(Runnable.class));
+ verify(delegate).newThread(any(Runnable.class));
+ assertEquals(t.getName(), "test thread");
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueServiceTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueServiceTestCase.java
new file mode 100644
index 0000000..cb2bf37
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/SynchronousQueueServiceTestCase.java
@@ -0,0 +1,96 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.extender.internal.queue.callable.StringCallable;
+import org.apache.felix.ipojo.extender.queue.Callback;
+import org.apache.felix.ipojo.extender.queue.JobInfo;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the synchronous queue behavior
+ */
+public class SynchronousQueueServiceTestCase extends TestCase {
+
+ @Mock
+ private BundleContext m_bundleContext;
+
+ @Mock
+ private ServiceRegistration<?> m_registration;
+
+ @Mock
+ private Callback<String> m_callback;
+
+ @Captor
+ private ArgumentCaptor<JobInfo> infos;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testRegistration() throws Exception {
+ SynchronousQueueService queueService = new SynchronousQueueService(m_bundleContext);
+
+ Mockito.<ServiceRegistration<?>>when(m_bundleContext.registerService(eq(QueueService.class.getName()), eq(queueService), any(Dictionary.class))).thenReturn(m_registration);
+
+ queueService.start();
+
+ verify(m_bundleContext).registerService(eq(QueueService.class.getName()), eq(queueService), any(Dictionary.class));
+
+ queueService.stop();
+
+ verify(m_registration).unregister();
+ }
+
+ public void testSubmitsAreSequential() throws Exception {
+
+ SynchronousQueueService queueService = new SynchronousQueueService(m_bundleContext);
+
+ queueService.submit(new StringCallable("one"), m_callback, null);
+ queueService.submit(new StringCallable("two"), m_callback, null);
+
+ InOrder order = inOrder(m_callback);
+ order.verify(m_callback).success(infos.capture(), eq("one"));
+ order.verify(m_callback).success(infos.capture(), eq("two"));
+
+ JobInfo one = infos.getAllValues().get(0);
+ JobInfo two = infos.getAllValues().get(1);
+
+ assertTrue(two.getEnlistmentTime() >= one.getEndTime());
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/SleepingCallable.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/SleepingCallable.java
new file mode 100644
index 0000000..95bed29
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/SleepingCallable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.callable;
+
+import java.util.concurrent.Callable;
+
+/**
+* A dummy job taking some time to complete....
+*/
+public class SleepingCallable implements Callable<String> {
+ private int m_time;
+ private String m_value;
+
+ public SleepingCallable(int time, String value) {
+ m_time = time;
+ m_value = value;
+ }
+
+ public String call() throws Exception {
+ Thread.sleep(m_time);
+ return m_value;
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/StringCallable.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/StringCallable.java
new file mode 100644
index 0000000..6940a01
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/callable/StringCallable.java
@@ -0,0 +1,42 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.callable;
+
+import java.util.concurrent.Callable;
+
+/**
+* A dummy job.
+*/
+public class StringCallable implements Callable<String> {
+
+ private final String m_hello;
+
+ public StringCallable() {
+ this("hello");
+ }
+
+ public StringCallable(String hello) {
+ m_hello = hello;
+ }
+
+ public String call() throws Exception {
+ return m_hello;
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelectionTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelectionTestCase.java
new file mode 100644
index 0000000..c187928
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/HeaderPreferenceSelectionTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref;
+
+import static org.mockito.Mockito.when;
+
+import java.util.Hashtable;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the selection of the job processing preference from the bundle manifest.
+ */
+public class HeaderPreferenceSelectionTestCase extends TestCase {
+
+ public static final String HEADER = "Header";
+
+ @Mock
+ private Bundle m_bundle;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testMissingHeader() throws Exception {
+ Hashtable<String, String> headers = new Hashtable<String, String>();
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ HeaderPreferenceSelection selection = new HeaderPreferenceSelection(HEADER);
+ assertEquals(Preference.DEFAULT, selection.select(m_bundle));
+ }
+
+ public void testUnrecognizedHeader() throws Exception {
+ Hashtable<String, String> headers = new Hashtable<String, String>();
+ headers.put(HEADER, "invalid");
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ HeaderPreferenceSelection selection = new HeaderPreferenceSelection(HEADER);
+ assertEquals(Preference.DEFAULT, selection.select(m_bundle));
+ }
+
+ public void testSyncHeader() throws Exception {
+ Hashtable<String, String> headers = new Hashtable<String, String>();
+ // We should ignore case
+ headers.put(HEADER, "SyNc");
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ HeaderPreferenceSelection selection = new HeaderPreferenceSelection(HEADER);
+ assertEquals(Preference.SYNC, selection.select(m_bundle));
+ }
+
+ public void testAsyncHeader() throws Exception {
+ Hashtable<String, String> headers = new Hashtable<String, String>();
+ // We should ignore case
+ headers.put(HEADER, "aSyNc");
+ when(m_bundle.getHeaders()).thenReturn(headers);
+ HeaderPreferenceSelection selection = new HeaderPreferenceSelection(HEADER);
+ assertEquals(Preference.ASYNC, selection.select(m_bundle));
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueServiceTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueServiceTestCase.java
new file mode 100644
index 0000000..21a3bb7
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/pref/enforce/EnforcedQueueServiceTestCase.java
@@ -0,0 +1,102 @@
+/*
+ * 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.felix.ipojo.extender.internal.queue.pref.enforce;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.Callable;
+
+import org.apache.felix.ipojo.extender.internal.LifecycleQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.callable.StringCallable;
+import org.apache.felix.ipojo.extender.internal.queue.pref.Preference;
+import org.apache.felix.ipojo.extender.internal.queue.pref.PreferenceSelection;
+import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.apache.felix.ipojo.util.Log;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks the enforced queue service.
+ */
+public class EnforcedQueueServiceTestCase extends TestCase {
+
+ @Mock
+ private LifecycleQueueService delegate;
+
+ @Mock
+ private Bundle m_bundle;
+
+ @Mock
+ private Log m_log;
+
+ @Mock
+ private PreferenceSelection m_selection;
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testNoEnforcement() throws Exception {
+ when(m_selection.select(m_bundle)).thenReturn(Preference.DEFAULT);
+ EnforcedQueueService queueService = new EnforcedQueueService(m_selection, delegate, Preference.ASYNC, m_log);
+ queueService.submit(new ReferenceCallable());
+ verifyZeroInteractions(m_log);
+ }
+
+ public void testNoEnforcementBecauseNoBundleReference() throws Exception {
+ EnforcedQueueService queueService = new EnforcedQueueService(m_selection, delegate, Preference.ASYNC, m_log);
+ queueService.submit(new StringCallable());
+ verifyZeroInteractions(m_log);
+ }
+
+ public void testIncompatibleEnforcement() throws Exception {
+ when(m_selection.select(m_bundle)).thenReturn(Preference.SYNC);
+ EnforcedQueueService queueService = new EnforcedQueueService(m_selection, delegate, Preference.ASYNC, m_log);
+ queueService.submit(new ReferenceCallable());
+
+ verify(m_log).log(eq(Log.WARNING), anyString());
+ }
+
+ public void testCompatibleEnforcement() throws Exception {
+ when(m_selection.select(m_bundle)).thenReturn(Preference.ASYNC);
+ EnforcedQueueService queueService = new EnforcedQueueService(m_selection, delegate, Preference.ASYNC, m_log);
+ queueService.submit(new ReferenceCallable());
+ verifyZeroInteractions(m_log);
+ }
+
+ private class ReferenceCallable implements Callable<String>, BundleReference {
+ public String call() throws Exception {
+ return "hello";
+ }
+
+ public Bundle getBundle() {
+ return m_bundle;
+ }
+ }
+}