Commit the new iPOJO version (0.7.6).
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@642265 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/composite/pom.xml b/ipojo/composite/pom.xml
new file mode 100644
index 0000000..222dc6d
--- /dev/null
+++ b/ipojo/composite/pom.xml
@@ -0,0 +1,120 @@
+<!--
+ 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.
+-->
+<project>
+ <parent>
+ <artifactId>iPOJO</artifactId>
+ <groupId>org.apache.felix</groupId>
+ <version>0.7.6-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix iPOJO Composite</name>
+ <artifactId>org.apache.felix.ipojo.composite</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.apache.felix.ipojo.metadata</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>iPOJO Composite</Bundle-Name>
+ <Bundle-Vendor>Clement ESCOFFIER</Bundle-Vendor>
+ <Bundle-Description>
+ iPOJO Composititon Framework
+ </Bundle-Description>
+ <Import-Package>
+ org.apache.felix.ipojo,
+ org.apache.felix.ipojo.architecture,
+ org.apache.felix.ipojo.context,
+ org.apache.felix.ipojo.metadata,
+ org.apache.felix.ipojo.parser,
+ org.apache.felix.ipojo.util,
+ org.osgi.framework
+ </Import-Package>
+ <Private-Package>
+ org.apache.felix.ipojo.manipulation,
+ org.apache.felix.ipojo.composite.architecture,
+ org.apache.felix.ipojo.composite.service*,
+ org.apache.felix.ipojo.composite.instance,
+ org.apache.felix.ipojo.composite.util,
+ !org.objectweb.asm.xml*,
+ org.objectweb.asm*;-split-package:=merge-first
+ </Private-Package>
+ <Export-Package>
+ org.apache.felix.ipojo.composite;
+ version="0.7.6"
+ </Export-Package>
+ <IPOJO-Extension>
+ composite:org.apache.felix.ipojo.composite.CompositeFactory
+ </IPOJO-Extension>
+ <_donotcopy>
+ (CVS|.svn|.+.bak|~.+|metadata.xml)
+ </_donotcopy>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-ipojo-plugin</artifactId>
+ <version>${pom.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>ipojo-bundle</goal>
+ </goals>
+ <configuration>
+ <metadata>metadata.xml</metadata>
+ <ignoreAnnotations>true</ignoreAnnotations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java
new file mode 100644
index 0000000..9c980a1
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeFactory.java
@@ -0,0 +1,178 @@
+/*
+ * 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.composite;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.Logger;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * The component factory manages component instance objects. This management
+ * consist in creating and managing component instance build with the component
+ * factory. This class could export Factory and ManagedServiceFactory services.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeFactory extends ComponentFactory implements TrackerCustomizer {
+
+ /**
+ * Tracker used to track required handler factories.
+ */
+ protected Tracker m_tracker;
+
+ /**
+ * Create a composite factory.
+ * @param context : bundle context
+ * @param metadata : metadata of the component to create
+ * @throws ConfigurationException occurs when the element describing the factory is malformed.
+ */
+ public CompositeFactory(BundleContext context, Element metadata) throws ConfigurationException {
+ super(context, metadata);
+ }
+
+ /**
+ * Check if the metadata are well formed.
+ * @param metadata : metadata
+ * @throws ConfigurationException occurs when the element describing the factory is malformed.
+ * @see org.apache.felix.ipojo.ComponentFactory#check(org.apache.felix.ipojo.metadata.Element)
+ */
+ public void check(Element metadata) throws ConfigurationException {
+ String name = metadata.getAttribute("name");
+ if (name == null) {
+ throw new ConfigurationException("A composite needs a name : " + metadata);
+ }
+ }
+
+ public String getClassName() { return "composite"; }
+
+
+ /**
+ * Compute required handlers.
+ * @return the list of required handler.
+ */
+ public List getRequiredHandlerList() {
+ List list = new ArrayList();
+ Element[] elems = m_componentMetadata.getElements();
+ for (int i = 0; i < elems.length; i++) {
+ Element current = elems[i];
+ RequiredHandler req = new RequiredHandler(current.getName(), current.getNameSpace());
+ if (! list.contains(req)) { list.add(req); }
+ }
+
+ // Add architecture if architecture != 'false'
+ String arch = m_componentMetadata.getAttribute("architecture");
+ if (arch == null || arch.equalsIgnoreCase("true")) {
+ RequiredHandler req = new RequiredHandler("architecture", null);
+ if (! list.contains(req)) { list.add(req); }
+ }
+
+ return list;
+ }
+
+ /**
+ * Stop all the instance managers.
+ */
+ public synchronized void stopping() {
+ if (m_tracker != null) {
+ m_tracker.close();
+ }
+ m_tracker = null;
+ }
+
+ /**
+ * Start all the instance managers.
+ */
+ public synchronized void starting() {
+ if (m_requiredHandlers.size() != 0) {
+ try {
+ String filter = "(&(" + Constants.OBJECTCLASS + "=" + Factory.class.getName() + ")"
+ + "(" + Handler.HANDLER_TYPE_PROPERTY + "=" + CompositeHandler.HANDLER_TYPE + ")"
+ + "(factory.state=1)"
+ + ")";
+ m_tracker = new Tracker(m_context, m_context.createFilter(filter), this);
+ m_tracker.open();
+ } catch (InvalidSyntaxException e) {
+ m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage());
+ stop();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Create an instance from the current factory.
+ * @param configuration : instance configuration
+ * @param context : bundle context to inject in the instance manager
+ * @param handlers : array of handler object to attached on the instance
+ * @return the created instance
+ * @throws ConfigurationException either the instance configuration or the instance starting has failed
+ * @see org.apache.felix.ipojo.ComponentFactory#createInstance(java.util.Dictionary, org.apache.felix.ipojo.IPojoContext, org.apache.felix.ipojo.HandlerManager[])
+ */
+ public ComponentInstance createInstance(Dictionary configuration, IPojoContext context, HandlerManager[] handlers) throws ConfigurationException {
+ CompositeManager inst = new CompositeManager(this, context, handlers);
+ inst.configure(m_componentMetadata, configuration);
+ inst.start();
+ return inst;
+ }
+
+ /**
+ * Reconfigure an existing instance.
+ * @param properties : the new configuration to push.
+ * @throws UnacceptableConfiguration : occurs if the new configuration is
+ * not consistent with the component type.
+ * @throws MissingHandlerException : occurs when an handler is unavailable when creating the instance.
+ * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
+ */
+ public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException {
+ if (properties == null || properties.get("name") == null) {
+ throw new UnacceptableConfiguration("The configuration does not contains the \"name\" property");
+ }
+ String name = (String) properties.get("name");
+
+ ComponentInstance instance = (CompositeManager) m_componentInstances.get(name);
+
+ if (instance == null) {
+ return; // The instance does not exist.
+ }
+
+ instance.reconfigure(properties); // re-configure the component
+ }
+
+ public String getFactoryName() {
+ return m_componentMetadata.getAttribute("name");
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java
new file mode 100644
index 0000000..4194fed
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.composite;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.Handler;
+import org.apache.felix.ipojo.util.Logger;
+
+
+/**
+ * Composite Handler Abstract Class. An composite handler need implements these
+ * method to be notified of lifecycle change...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class CompositeHandler extends Handler {
+
+ /**
+ * Composite Handler type.
+ */
+ public static final String HANDLER_TYPE = "composite";
+
+ /**
+ * Reference on the composite manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Composite Factory.
+ */
+ private CompositeFactory m_factory;
+
+ /**
+ * Set the manager.
+ * This method me be called only once time.
+ * @param instance : the composite manager.
+ */
+ protected final void attach(ComponentInstance instance) {
+ m_manager = (CompositeManager) instance;
+ }
+
+ public final void setFactory(Factory factory) {
+ m_factory = (CompositeFactory) factory;
+ }
+
+ public final Logger getLogger() {
+ return m_factory.getLogger();
+ }
+
+ public final CompositeManager getCompositeManager() {
+ return m_manager;
+ }
+
+ /**
+ * Get a plugged handler of the same container.
+ * This method must be call only in the start method (or after).
+ * In the configure method, this method can not return a consistent
+ * result as all handlers are not plugged.
+ * @param name : name of the handler to find (class name).
+ * @return the composite handler object or null if the handler is not found.
+ */
+ public final Handler getHandler(String name) {
+ return m_manager.getCompositeHandler(name);
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java
new file mode 100644
index 0000000..00537ea
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeManager.java
@@ -0,0 +1,442 @@
+/*
+ * 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.composite;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.InstanceStateListener;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * iPOJO Composite manager. The composite manager class manages one instance of
+ * a component type which is a composition. It manages component lifecycle, and
+ * handlers...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeManager implements ComponentInstance, InstanceStateListener {
+
+ /**
+ * The context of the component.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Parent factory (ComponentFactory).
+ */
+ private CompositeFactory m_factory;
+
+ /**
+ * Composite Handler list.
+ */
+ private HandlerManager[] m_handlers = new HandlerManager[0];
+
+ /**
+ * Instance State Listener List.
+ */
+ private List m_listeners = new ArrayList();
+
+ /**
+ * Internal service context of the composition.
+ */
+ private CompositeServiceContext m_internalContext;
+
+ /**
+ * Name of the component instance.
+ */
+ private String m_name;
+
+ /**
+ * Component state (STOPPED at the beginning).
+ */
+ private int m_state = STOPPED;
+
+ /**
+ * Construct a new Component Manager.
+ * @param factory : the factory managing the instance manager
+ * @param context : the bundle context to give to the instance
+ * @param handlers : the handlers to plug
+ */
+ public CompositeManager(CompositeFactory factory, BundleContext context, HandlerManager[] handlers) {
+ m_factory = factory;
+ m_context = context;
+ // Initialize the service context.
+ m_internalContext = new CompositeServiceContext(m_context, this);
+ m_handlers = handlers;
+ }
+
+ /**
+ * Plug the given handler to the current container.
+ * @param handler : the handler to plug.
+ */
+ public synchronized void addCompositeHandler(HandlerManager handler) {
+ if (m_handlers.length > 0) {
+ HandlerManager[] newInstances = new HandlerManager[m_handlers.length + 1];
+ System.arraycopy(m_handlers, 0, newInstances, 0, m_handlers.length);
+ newInstances[m_handlers.length] = handler;
+ m_handlers = newInstances;
+ } else {
+ m_handlers = new HandlerManager[] { handler };
+ }
+ }
+
+ /**
+ * Add an instance to the created instance list.
+ * @param listener : the instance state listener to add.
+ * @see org.apache.felix.ipojo.ComponentInstance#addInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public void addInstanceStateListener(InstanceStateListener listener) {
+ synchronized (m_listeners) {
+ m_listeners.add(listener);
+ }
+ }
+
+ /**
+ * Configure the instance manager. Stop the existing handler, clear the
+ * handler list, change the metadata, recreate the handler
+ *
+ * @param metadata : the component type metadata
+ * @param configuration : the configuration of the instance
+ * @throws ConfigurationException : occurs when the component type are incorrect.
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ // Add the name
+ m_name = (String) configuration.get("name");
+
+ // Create the standard handlers and add these handlers to the list
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].init(this, metadata, configuration);
+ }
+ }
+
+ /**
+ * Dispose the instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#dispose()
+ */
+ public void dispose() {
+ if (m_state > STOPPED) { stop(); }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, DISPOSED);
+ }
+
+ m_factory.disposed(this);
+
+ // Cleaning
+ m_state = DISPOSED;
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].dispose();
+ }
+ m_handlers = new HandlerManager[0];
+ m_listeners.clear();
+ }
+
+ /**
+ * Return a specified handler.
+ * @param name : class name of the handler to find
+ * @return : the handler, or null if not found
+ */
+ public CompositeHandler getCompositeHandler(String name) {
+ for (int i = 0; i < m_handlers.length; i++) {
+ HandlerFactory fact = (HandlerFactory) m_handlers[i].getFactory();
+ if (fact.getHandlerName().equals(name) || fact.getComponentDescription().getClassName().equals(name)) {
+ return (CompositeHandler) m_handlers[i].getHandler();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the bundle context used by this instance.
+ * @return the parent context of the instance.
+ * @see org.apache.felix.ipojo.ComponentInstance#getContext()
+ */
+ public BundleContext getContext() {
+ return m_context;
+ }
+
+ /**
+ * Get the factory which create this instance.
+ * @return the factory of the component
+ * @see org.apache.felix.ipojo.ComponentInstance#getFactory()
+ */
+ public ComponentFactory getFactory() {
+ return m_factory;
+ }
+
+ /**
+ * Get the global bundle context.
+ * @return the global bundle context.
+ */
+ public BundleContext getGlobalContext() {
+ IPojoContext context = (IPojoContext) m_context;
+ return context.getGlobalContext();
+ }
+
+ /**
+ * Return the instance description of this instance.
+ * @return the instance description.
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ InstanceDescription desc = new InstanceDescription(m_name, m_state, getContext().getBundle().getBundleId(), m_factory.getComponentDescription());
+ CompositeHandler[] handlers = getRegistredCompositeHandlers();
+ for (int i = 0; i < handlers.length; i++) {
+ desc.addHandler(handlers[i].getDescription());
+ }
+
+ // Get instances description of internal instance
+ ServiceReference[] refs;
+ try {
+ refs = m_internalContext.getServiceReferences(Architecture.class.getName(), null);
+ if (refs != null) {
+ for (int i = 0; i < refs.length; i++) {
+ Architecture arch = (Architecture) m_internalContext.getService(refs[i]);
+ desc.addInstance(arch.getInstanceDescription());
+ m_internalContext.ungetService(refs[i]);
+ }
+ }
+ } catch (InvalidSyntaxException e) {
+ // Cannot happen
+ }
+ return desc;
+ }
+
+ /**
+ * Get the instance name.
+ * @return the instance name
+ * @see org.apache.felix.ipojo.ComponentInstance#getInstanceName()
+ */
+ public String getInstanceName() {
+ return m_name;
+ }
+
+ /**
+ * Get the parent service context.
+ * @return the parent service context.
+ */
+ public ServiceContext getParentServiceContext() {
+ IPojoContext context = (IPojoContext) m_context;
+ return context.getServiceContext();
+ }
+
+ /**
+ * REturn the list of handlers plugged on this instance.
+ * @return the list of the registered handlers.
+ */
+ public CompositeHandler[] getRegistredCompositeHandlers() {
+ CompositeHandler[] handler = new CompositeHandler[m_handlers.length];
+ for (int i = 0; i < m_handlers.length; i++) {
+ handler[i] = (CompositeHandler) m_handlers[i].getHandler();
+ }
+ return handler;
+ }
+
+ /**
+ * Get the internal service context of this instance.
+ * @return the internal service context.
+ */
+ public ServiceContext getServiceContext() {
+ return m_internalContext;
+ }
+
+ /**
+ * Get the actual state of the instance.
+ * @return the actual state of the instance
+ * @see org.apache.felix.ipojo.ComponentInstance#getState()
+ */
+ public int getState() {
+ return m_state;
+ }
+
+ /**
+ * Check if the instance is started.
+ * @return true if the instance is started.
+ * @see org.apache.felix.ipojo.ComponentInstance#isStarted()
+ */
+ public boolean isStarted() {
+ return m_state > STOPPED;
+ }
+
+ /**
+ * Reconfigure the current instance.
+ * @param configuration : the new instance configuration.
+ * @see org.apache.felix.ipojo.ComponentInstance#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary configuration) {
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].reconfigure(configuration);
+ }
+ }
+
+ /**
+ * Remove an instance state listener.
+ * @param listener : the listener to remove
+ * @see org.apache.felix.ipojo.ComponentInstance#removeInstanceStateListener(org.apache.felix.ipojo.InstanceStateListener)
+ */
+ public void removeInstanceStateListener(InstanceStateListener listener) {
+ synchronized (m_listeners) {
+ m_listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Set the state of the component.
+ * if the state changed call the stateChanged(int) method on the handlers.
+ * @param state : new state
+ */
+ public void setState(int state) {
+ if (m_state != state) {
+ if (state > m_state) {
+ // The state increases (Stopped = > IV, IV => V) => invoke handlers from the higher priority to the lower
+ m_state = state;
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ } else {
+ // The state decreases (V => IV, IV = > Stopped, Stopped => Disposed)
+ m_state = state;
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].getHandler().stateChanged(state);
+ }
+ }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, state);
+ }
+ }
+ }
+
+ /**
+ * Start the instance manager.
+ */
+ public synchronized void start() {
+ if (m_state > STOPPED) {
+ return;
+ } // Instance already started
+
+
+ // The new state of the component is UNRESOLVED
+ m_state = INVALID;
+
+ m_internalContext.start(); // Turn on the factory tracking
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ m_handlers[i].start();
+ m_handlers[i].addInstanceStateListener(this);
+ }
+
+ for (int i = 0; i < m_handlers.length; i++) {
+ if (m_handlers[i].getState() != VALID) {
+ setState(INVALID);
+ return;
+ }
+ }
+ setState(VALID);
+
+ }
+
+ /**
+ * State Change listener callback.
+ * This method is notified at each time a plugged handler becomes invalid.
+ * @param instance : changing instance
+ * @param newState : new state
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public synchronized void stateChanged(ComponentInstance instance, int newState) {
+ if (m_state <= STOPPED) { return; }
+
+ // Update the component state if necessary
+ if (newState == INVALID && m_state == VALID) {
+ // Need to update the state to UNRESOLVED
+ setState(INVALID);
+ return;
+ }
+ if (newState == VALID && m_state == INVALID) {
+ // An handler becomes valid => check if all handlers are valid
+ boolean isValid = true;
+ for (int i = 0; i < m_handlers.length; i++) {
+ isValid = isValid && m_handlers[i].getState() == VALID;
+ }
+
+ if (isValid) { setState(VALID); }
+ }
+ if (newState == DISPOSED) {
+ kill();
+ }
+ }
+
+ /**
+ * Stop the instance manager.
+ */
+ public synchronized void stop() {
+ if (m_state <= STOPPED) {
+ return;
+ } // Instance already stopped
+
+ setState(INVALID);
+ // Stop all the handlers
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].removeInstanceStateListener(this);
+ m_handlers[i].stop();
+ }
+
+ m_internalContext.stop(); // Turn off the factory tracking
+ m_state = STOPPED;
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, STOPPED);
+ }
+ }
+
+ /**
+ * Kill the current instance.
+ * Only the factory of this instance can call this method.
+ */
+ protected synchronized void kill() {
+ if (m_state > STOPPED) { stop(); }
+
+ for (int i = 0; i < m_listeners.size(); i++) {
+ ((InstanceStateListener) m_listeners.get(i)).stateChanged(this, DISPOSED);
+ }
+
+ // Cleaning
+ m_state = DISPOSED;
+
+ for (int i = m_handlers.length - 1; i > -1; i--) {
+ m_handlers[i].dispose();
+ }
+ m_handlers = new HandlerManager[0];
+ m_listeners.clear();
+ }
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java
new file mode 100644
index 0000000..1b0ebfc
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/CompositeServiceContext.java
@@ -0,0 +1,482 @@
+/*
+ * 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.composite;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.IPojoContext;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.context.ServiceRegistry;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * CompositeServiceContext Class. This class provides an implementation of the
+ * service context for composite.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeServiceContext implements ServiceContext, TrackerCustomizer {
+
+ /**
+ * Structure storing the reference, the factory and the registration.
+ */
+ private class Record {
+ /**
+ * Reference of the represented factory from the external context.
+ */
+ private ServiceReference m_ref;
+ /**
+ * Registration of the factory in the internal context.
+ */
+ private ServiceRegistration m_reg;
+ /**
+ * Represented Factory.
+ */
+ private FactoryProxy m_fact;
+ }
+
+ /**
+ * List of imported factories.
+ */
+ private List m_factories = new ArrayList();
+ /**
+ * Internal service registry.
+ */
+ private ServiceRegistry m_registry;
+
+ /**
+ * Component Instance who creates this registry.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Global service context.
+ */
+ private BundleContext m_global;
+
+ /**
+ * Tracker tracking Factories to import.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * Constructor. This constructor instantiate a service registry with the
+ * given bundle context.
+ *
+ * @param context : the bundle context
+ */
+ public CompositeServiceContext(BundleContext context) {
+ m_registry = new ServiceRegistry(context);
+ if (context instanceof IPojoContext) {
+ m_global = ((IPojoContext) context).getGlobalContext();
+ } else {
+ m_global = context; // the parent context is the global context
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context : the bundle context
+ * @param instance : the component instance owning this context
+ */
+ public CompositeServiceContext(BundleContext context, ComponentInstance instance) {
+ this(context);
+ m_instance = instance;
+ }
+
+ /**
+ * Add a service listener.
+ * @param arg0 : The service listener to add
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void addServiceListener(ServiceListener arg0) {
+ m_registry.addServiceListener(arg0);
+ }
+
+ /**
+ * Add a filtered service listener.
+ * @param arg0 : the service listener object to add
+ * @param arg1 : the LDAP filter for this listener
+ * @throws InvalidSyntaxException : occurs if the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener,
+ * java.lang.String)
+ */
+ public void addServiceListener(ServiceListener arg0, String arg1) throws InvalidSyntaxException {
+ m_registry.addServiceListener(arg0, arg1);
+ }
+
+ /**
+ * Get all service references.
+ * @param arg0 : The required service interface.
+ * @param arg1 : LDAP filter
+ * @return the list of all service reference matching with the query
+ * @throws InvalidSyntaxException : occurs when the given filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getAllServiceReferences(java.lang.String,
+ * java.lang.String)
+ */
+ public ServiceReference[] getAllServiceReferences(String arg0, String arg1) throws InvalidSyntaxException {
+ return m_registry.getAllServiceReferences(arg0, arg1);
+ }
+
+ /**
+ * Get a service object for the given service reference.
+ * @param arg0 : the service reference
+ * @return the service object or null if the reference is no more valid or if the object is not accessible
+ * @see org.apache.felix.ipojo.ServiceContext#getService(org.osgi.framework.ServiceReference)
+ */
+ public Object getService(ServiceReference arg0) {
+ return m_registry.getService(m_instance, arg0);
+ }
+
+
+ /**
+ * Get a service reference for the required interface.
+ * @param arg0 : the required interface name
+ * @return the service reference or null if no available provider
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference getServiceReference(String arg0) {
+ return m_registry.getServiceReference(arg0);
+ }
+
+ /**
+ * Get all accessible service reference for the given query.
+ * @param clazz : required interface
+ * @param filter : LDAP filter
+ * @return the list (array) of service reference matching with the query.
+ * @throws InvalidSyntaxException : occurs when the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ return m_registry.getServiceReferences(clazz, filter);
+ }
+
+
+ /**
+ * Register a service inside the composite context.
+ * @param arg0 : list of interfaces to register.
+ * @param arg1 : service object
+ * @param arg2 : properties list
+ * @return the service registration
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String[] arg0, Object arg1, Dictionary arg2) {
+ return m_registry.registerService(m_instance, arg0, arg1, arg2);
+ }
+
+ /**
+ * Register a service inside the composite context.
+ * @param arg0 : interface to register.
+ * @param arg1 : service object
+ * @param arg2 : properties list
+ * @return the service registration
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String arg0, Object arg1, Dictionary arg2) {
+ return m_registry.registerService(m_instance, arg0, arg1, arg2);
+ }
+
+ /**
+ * Remove a service listener.
+ * @param arg0 : the service listener to remove
+ * @see org.apache.felix.ipojo.ServiceContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void removeServiceListener(ServiceListener arg0) {
+ m_registry.removeServiceListener(arg0);
+ }
+
+ /**
+ * Unget a service.
+ * @param arg0 the service reference to unget
+ * @return true
+ * @see org.apache.felix.ipojo.ServiceContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ public boolean ungetService(ServiceReference arg0) {
+ return m_registry.ungetService(m_instance, arg0);
+ }
+
+ /**
+ * Import a factory form the parent to the internal registry.
+ *
+ * @param ref : the reference of the factory to import.
+ */
+ private void importFactory(ServiceReference ref) {
+ Record rec = new Record();
+ m_factories.add(rec);
+ Dictionary dict = new Properties();
+ for (int j = 0; j < ref.getPropertyKeys().length; j++) {
+ dict.put(ref.getPropertyKeys()[j], ref.getProperty(ref.getPropertyKeys()[j]));
+ }
+ rec.m_fact = new FactoryProxy((Factory) m_tracker.getService(ref), this);
+ rec.m_reg = registerService(Factory.class.getName(), rec.m_fact, dict);
+ rec.m_ref = ref;
+ }
+
+ /**
+ * Remove a factory of the available factory list.
+ *
+ * @param ref : the reference on the factory to remove.
+ */
+ private void removeFactory(ServiceReference ref) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == ref) {
+ if (rec.m_reg != null) {
+ rec.m_reg.unregister();
+ rec.m_fact = null;
+ }
+ m_tracker.ungetService(rec.m_ref);
+ m_factories.remove(rec);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Start the registry management.
+ */
+ public void start() {
+ m_tracker = new Tracker(m_global, Factory.class.getName(), this);
+ m_tracker.open();
+ }
+
+ /**
+ * Stop the registry management.
+ */
+ public synchronized void stop() {
+ m_tracker.close();
+ m_registry.reset();
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ removeFactory(rec.m_ref);
+ }
+ m_tracker = null;
+ }
+
+ /**
+ * Check if the factory list contain the given reference.
+ *
+ * @param ref : the reference to find.
+ * @return true if the list contains the given reference.
+ */
+ private boolean containsRef(ServiceReference ref) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == ref) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a bundle listener.
+ * Delegate on the global bundle context.
+ * @param arg0 : bundle listener to add
+ * @see org.osgi.framework.BundleContext#addBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void addBundleListener(BundleListener arg0) {
+ m_global.addBundleListener(arg0);
+ }
+
+ /**
+ * Add a framework listener.
+ * Delegate on the global bundle context.
+ * @param arg0 : framework listener to add.
+ * @see org.osgi.framework.BundleContext#addFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void addFrameworkListener(FrameworkListener arg0) {
+ m_global.addFrameworkListener(arg0);
+ }
+
+ /**
+ * Create a LDAP filter.
+ * @param arg0 : String-form of the filter
+ * @return the created filter object
+ * @throws InvalidSyntaxException : if the given argument is not a valid against the LDAP grammar.
+ * @see org.osgi.framework.BundleContext#createFilter(java.lang.String)
+ */
+ public Filter createFilter(String arg0) throws InvalidSyntaxException {
+ return m_global.createFilter(arg0);
+ }
+
+ /**
+ * Get the current bundle.
+ * @return the current bundle
+ * @see org.osgi.framework.BundleContext#getBundle()
+ */
+ public Bundle getBundle() {
+ return m_global.getBundle();
+ }
+
+ /**
+ * Get the bundle object with the given id.
+ * @param bundleId : bundle id
+ * @return the bundle object
+ * @see org.osgi.framework.BundleContext#getBundle(long)
+ */
+ public Bundle getBundle(long bundleId) {
+ return m_global.getBundle(bundleId);
+ }
+
+ /**
+ * Get installed bundles.
+ * @return the list of installed bundles
+ * @see org.osgi.framework.BundleContext#getBundles()
+ */
+ public Bundle[] getBundles() {
+ return m_global.getBundles();
+ }
+
+
+ /**
+ * Get a data file.
+ * @param filename : File name.
+ * @return the File object
+ * @see org.osgi.framework.BundleContext#getDataFile(java.lang.String)
+ */
+ public File getDataFile(String filename) {
+ return m_global.getDataFile(filename);
+ }
+
+ /**
+ * Get a property value.
+ * @param key : key of the asked property
+ * @return the property value (object) or null if no property are associated with the given key
+ * @see org.osgi.framework.BundleContext#getProperty(java.lang.String)
+ */
+ public String getProperty(String key) {
+ return m_global.getProperty(key);
+ }
+
+ /**
+ * Install a bundle.
+ * @param location : URL of the bundle to install
+ * @return the installed bundle
+ * @throws BundleException : if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String)
+ */
+ public Bundle installBundle(String location) throws BundleException {
+ return m_global.installBundle(location);
+ }
+
+ /**
+ * Install a bundle.
+ * @param location : URL of the bundle to install
+ * @param input :
+ * @return the installed bundle
+ * @throws BundleException : if the bundle cannot be installed correctly
+ * @see org.osgi.framework.BundleContext#installBundle(java.lang.String, java.io.InputStream)
+ */
+ public Bundle installBundle(String location, InputStream input) throws BundleException {
+ return m_global.installBundle(location, input);
+ }
+
+ /**
+ * Remove a bundle listener.
+ * @param listener : the listener to remove
+ * @see org.osgi.framework.BundleContext#removeBundleListener(org.osgi.framework.BundleListener)
+ */
+ public void removeBundleListener(BundleListener listener) {
+ m_global.removeBundleListener(listener);
+ }
+
+ /**
+ * Remove a framework listener.
+ * @param listener : the listener to remove
+ * @see org.osgi.framework.BundleContext#removeFrameworkListener(org.osgi.framework.FrameworkListener)
+ */
+ public void removeFrameworkListener(FrameworkListener listener) {
+ m_global.removeFrameworkListener(listener);
+ }
+
+ /**
+ * A new factory is detected.
+ * @param reference : service reference
+ * @return true if not already imported.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference reference) {
+ if (!containsRef(reference)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A matching reference has been added. The import factory can now be imported.
+ * @param reference : the added reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ importFactory(reference);
+ }
+
+ /**
+ * An imported factory is modified.
+ * @param reference : modified reference
+ * @param service : factory object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ for (int i = 0; i < m_factories.size(); i++) {
+ Record rec = (Record) m_factories.get(i);
+ if (rec.m_ref == reference) {
+ Dictionary dict = new Properties();
+ for (int j = 0; j < reference.getPropertyKeys().length; j++) {
+ dict.put(reference.getPropertyKeys()[j], reference.getProperty(reference.getPropertyKeys()[j]));
+ }
+ rec.m_reg.setProperties(dict);
+ return;
+ }
+ }
+ }
+
+ /**
+ * An imported factory disappears.
+ * @param reference : reference
+ * @param service : factory object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ if (containsRef(reference)) {
+ removeFactory(reference);
+ }
+
+ }
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
new file mode 100644
index 0000000..a2a67ba
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/FactoryProxy.java
@@ -0,0 +1,173 @@
+/*
+ * 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.composite;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.FactoryStateListener;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Bridge representing a Factory inside a composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryProxy implements Factory {
+
+ /**
+ * Delegated factory.
+ */
+ private Factory m_delegate;
+
+ /**
+ * Destination context.
+ */
+ private ServiceContext m_context;
+
+ /**
+ * Constructor.
+ * @param fact : the targeted factory.
+ * @param svcContext : the service context to target.
+ */
+ public FactoryProxy(Factory fact, ServiceContext svcContext) {
+ m_delegate = fact;
+ m_context = svcContext;
+ }
+
+ /**
+ * Create an instance manager (i.e. component type instance).
+ * @param configuration : the configuration properties for this component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration : when a given configuration is not valid.
+ * @throws MissingHandlerException : occurs when the creation failed due to a missing handler (the factory should be invalid)
+ * @throws ConfigurationException : occurs when the creation failed due to a configuration issue
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary)
+ */
+ public ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException,
+ ConfigurationException {
+ return m_delegate.createComponentInstance(configuration, m_context);
+ }
+
+ /**
+ * Create an instance manager (i.e. component type instance). This has these service interaction in the scope given in argument.
+ * @param configuration : the configuration properties for this component.
+ * @param serviceContext : the service context of the component.
+ * @return the created instance manager.
+ * @throws UnacceptableConfiguration : when the given configuration is not valid.
+ * @throws MissingHandlerException : when at least one handler is missing.
+ * @throws ConfigurationException : when an issue occurs during the oconfiguration of the instance.
+ * @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary, org.apache.felix.ipojo.ServiceContext)
+ */
+ public ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration,
+ MissingHandlerException, ConfigurationException {
+ return m_delegate.createComponentInstance(configuration, serviceContext);
+ }
+
+ /**
+ * Get the component type information containing provided service, configuration properties ...
+ * @return the component type information.
+ * @see org.apache.felix.ipojo.Factory#getDescription()
+ */
+ public Element getDescription() {
+ return m_delegate.getDescription();
+ }
+
+ /**
+ * Return the factory name.
+ * @return the name of the factory.
+ * @see org.apache.felix.ipojo.Factory#getName()
+ */
+ public String getName() {
+ return m_delegate.getName();
+ }
+
+ /**
+ * Check if the given configuration is acceptable as a configuration of a component instance.
+ * @param conf : the configuration to test
+ * @return true if the configuration is acceptable
+ * @see org.apache.felix.ipojo.Factory#isAcceptable(java.util.Dictionary)
+ */
+ public boolean isAcceptable(Dictionary conf) {
+ return m_delegate.isAcceptable(conf);
+ }
+
+ /**
+ * Reconfigure an instance already created. This configuration need to have the name property to identify the instance.
+ * @param conf : the configuration to reconfigure the instance.
+ * @throws UnacceptableConfiguration : if the given configuration is not consistent for the targeted instance.
+ * @throws MissingHandlerException : when at least one handler is missing
+ * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
+ */
+ public void reconfigure(Dictionary conf) throws UnacceptableConfiguration, MissingHandlerException {
+ m_delegate.reconfigure(conf);
+ }
+
+ /**
+ * Add a factory listener.
+ * @param listener : the listener to add.
+ * @see org.apache.felix.ipojo.Factory#addFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void addFactoryStateListener(FactoryStateListener listener) {
+ m_delegate.addFactoryStateListener(listener);
+
+ }
+
+ public List getMissingHandlers() {
+ return m_delegate.getMissingHandlers();
+ }
+
+ public List getRequiredHandlers() {
+ return m_delegate.getRequiredHandlers();
+ }
+
+ /**
+ * Remove a service listener.
+ * @param listener : the listener to remove
+ * @see org.apache.felix.ipojo.Factory#removeFactoryStateListener(org.apache.felix.ipojo.FactoryStateListener)
+ */
+ public void removeFactoryStateListener(FactoryStateListener listener) {
+ m_delegate.removeFactoryStateListener(listener);
+
+ }
+
+ public ComponentTypeDescription getComponentDescription() {
+ return m_delegate.getComponentDescription();
+ }
+
+ public String getClassName() {
+ return m_delegate.getClassName();
+ }
+
+ public int getState() {
+ return m_delegate.getState();
+ }
+
+ public BundleContext getBundleContext() {
+ return m_delegate.getBundleContext();
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java
new file mode 100644
index 0000000..e5554b1
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/architecture/ArchitectureHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.composite.architecture;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.InstanceDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Composite Architecture Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ArchitectureHandler extends CompositeHandler implements Architecture {
+
+ /**
+ * Name of the component.
+ */
+ private String m_name;
+
+ /**
+ * Configure the handler.
+ *
+ * @param metadata : the metadata of the component
+ * @param configuration : the instance configuration
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager,
+ * org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) {
+ m_name = (String) configuration.get("name");
+ }
+
+ /**
+ * Stop the handler.
+ * @see org.apache.felix.ipojo.Handler#stop()
+ */
+ public void stop() {
+ // Nothing to do.
+ }
+
+ /**
+ * Start the handler.
+ * @see org.apache.felix.ipojo.Handler#start()
+ */
+ public void start() {
+ info("Start composite architecture handler with " + m_name + " name");
+ }
+
+ /**
+ * Get the instance description.
+ * @return the instance description
+ * @see org.apache.felix.ipojo.architecture.Architecture#getDescription()
+ */
+ public InstanceDescription getInstanceDescription() {
+ return getCompositeManager().getInstanceDescription();
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
new file mode 100644
index 0000000..3443073
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandler.java
@@ -0,0 +1,402 @@
+/*
+ * 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.composite.instance;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.InstanceStateListener;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseException;
+
+/**
+ * Composite Instance Handler.
+ * This handler allows creating an instance inside a composite.
+ * This instance is determine by its type and a configuration.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceHandler extends CompositeHandler implements InstanceStateListener {
+
+ /**
+ * Internal context.
+ */
+ private ServiceContext m_scope;
+
+ /**
+ * Available factories.
+ */
+ private Factory[] m_factories;
+
+
+ /**
+ * This structure aims to manage a configuration. It stores all necessary
+ * information to create an instance and to track the factory.
+ */
+ class ManagedConfiguration {
+ /**
+ * Configuration of the instance to create.
+ */
+ private Dictionary m_configuration;
+
+ /**
+ * Factory name.
+ */
+ private String m_factoryName;
+
+ /**
+ * Created instance.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Desired Factory (can be the classname).
+ */
+ private String m_desiredFactory;
+
+ /**
+ * Constructor.
+ *
+ * @param conf : the configuration to create.
+ */
+ ManagedConfiguration(Dictionary conf) {
+ m_configuration = conf;
+ m_desiredFactory = (String) conf.get("component");
+ }
+
+ /**
+ * Return the managed configuration.
+ * @return the configuration.
+ */
+ protected Dictionary getConfiguration() {
+ return m_configuration;
+ }
+
+ /**
+ * Return the used factory name.
+ * @return the factory name
+ */
+ protected String getFactory() {
+ return m_factoryName;
+ }
+
+ protected String getNeededFactoryName() {
+ return m_desiredFactory;
+ }
+
+ /**
+ * Return the created instance.
+ * @return the instance (or null if no instance are created).
+ */
+ protected ComponentInstance getInstance() {
+ return m_instance;
+ }
+
+ /**
+ * Set the factory name.
+ *
+ * @param name : the factory name.
+ */
+ protected void setFactory(String name) {
+ m_factoryName = name;
+ }
+
+ /**
+ * Set the instance object.
+ *
+ * @param instance : the instance
+ */
+ protected void setInstance(ComponentInstance instance) {
+ m_instance = instance;
+ }
+ }
+
+ /**
+ * Configurations to create and maintains.
+ */
+ private ManagedConfiguration[] m_configurations = new ManagedConfiguration[0];
+
+ /**
+ * Create an instance using the given factory and the given configuration.
+ *
+ * @param fact : the factory name to used.
+ * @param config : the configuration.
+ */
+ private void createInstance(Factory fact, ManagedConfiguration config) {
+ Dictionary conf = config.getConfiguration();
+ try {
+ config.setInstance(fact.createComponentInstance(conf, m_scope));
+ config.setFactory(fact.getName());
+ config.getInstance().addInstanceStateListener(this);
+ } catch (UnacceptableConfiguration e) {
+ error("A factory is available for the configuration but the configuration is not acceptable", e);
+ } catch (MissingHandlerException e) {
+ error("The instance creation has failed, at least one handler is missing", e);
+ } catch (ConfigurationException e) {
+ error("The instance creation has failed, an error during the configuration has occured", e);
+ }
+ }
+
+ /**
+ * A new valid factory appears.
+ * @param factory : factory.
+ */
+ public void bindFactory(Factory factory) {
+ boolean implicated = false;
+ String factName = factory.getName();
+ String className = factory.getComponentDescription().getClassName();
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) {
+ createInstance(factory, m_configurations[i]);
+ implicated = true;
+ }
+ }
+ if (implicated && ! getValidity()) {
+ checkValidity();
+ }
+ }
+
+ /**
+ * An existing factory disappears or becomes invalid.
+ * @param factory : factory
+ */
+ public void unbindFactory(Factory factory) {
+ boolean implicated = false;
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null && m_configurations[i].getFactory().equals(factory.getName())) {
+ m_configurations[i].setInstance(null);
+ m_configurations[i].setFactory(null);
+ implicated = true;
+ }
+ }
+ if (implicated && getValidity()) {
+ checkValidity();
+ }
+ }
+
+ /**
+ * Stop all created instances.
+ */
+ public synchronized void stop() {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null) {
+ m_configurations[i].getInstance().removeInstanceStateListener(this);
+ if (m_configurations[i].getInstance().getState() != ComponentInstance.DISPOSED) {
+ m_configurations[i].getInstance().dispose();
+ }
+ }
+ m_configurations[i].setInstance(null);
+ m_configurations[i].setFactory(null);
+ }
+ m_configurations = new ManagedConfiguration[0];
+ }
+
+ /**
+ * Configure method.
+ * @param metadata : component type metadata.
+ * @param configuration : instance configuration.
+ * @throws ConfigurationException : occurs an instance cannot be parsed correctly.
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_scope = getCompositeManager().getServiceContext();
+ Element[] instances = metadata.getElements("instance");
+ m_configurations = new ManagedConfiguration[instances.length];
+ for (int i = 0; i < instances.length; i++) {
+ Dictionary conf = null;
+ try {
+ conf = parseInstance(instances[i]);
+ } catch (ParseException e) {
+ error("An instance cannot be parsed correctly", e);
+ throw new ConfigurationException("An instance cannot be parsed correctly : " + e.getMessage());
+ }
+ m_configurations[i] = new ManagedConfiguration(conf);
+ }
+ }
+
+ /**
+ * Parse an Element to get a dictionary.
+ * @param instance : the Element describing an instance.
+ * @return : the resulting dictionary
+ * @throws ParseException : occurs when a configuration cannot be parse correctly.
+ */
+ public static Dictionary parseInstance(Element instance) throws ParseException {
+ Dictionary dict = new Properties();
+ String name = instance.getAttribute("name");
+ if (name != null) {
+ dict.put("name", name);
+ }
+
+ String comp = instance.getAttribute("component");
+ if (comp == null) {
+ throw new ParseException("An instance does not have the 'component' attribute");
+ } else {
+ dict.put("component", comp);
+ }
+
+ Element[] props = instance.getElements("property");
+ for (int i = 0; props != null && i < props.length; i++) {
+ parseProperty(props[i], dict);
+ }
+
+ return dict;
+ }
+
+ /**
+ * Parse a property.
+ * @param prop : the current element to parse
+ * @param dict : the dictionary to populate
+ * @throws ParseException : occurs if the property cannot be parsed correctly
+ */
+ public static void parseProperty(Element prop, Dictionary dict) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name == null) { throw new ParseException("A property does not have the 'name' attribute"); }
+ // Final case : the property element has a 'value' attribute
+ if (value == null) {
+ // Recursive case
+ // Check if there is 'property' element
+ Element[] subProps = prop.getElements("property");
+ if (subProps == null) { throw new ParseException("A complex property must have at least one 'property' sub-element"); }
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ dict.put(prop.getAttribute("name"), dict2);
+ }
+ } else {
+ dict.put(name, value);
+ }
+ }
+
+ /**
+ * Start method.
+ * @see org.apache.felix.ipojo.CompositeHandler#start()
+ */
+ public void start() {
+ for (int j = 0; j < m_factories.length; j++) {
+ String factName = m_factories[j].getName();
+ String className = m_factories[j].getClassName();
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) {
+ createInstance(m_factories[j], m_configurations[i]);
+ }
+ }
+ }
+ checkValidity();
+ }
+
+ /**
+ * Check handler validity.
+ * The method update the validaity of the handler.
+ */
+ private void checkValidity() {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() == null || m_configurations[i].getInstance().getState() != ComponentInstance.VALID) {
+ setValidity(false);
+ return;
+ }
+ }
+ setValidity(true);
+ }
+
+ /**
+ * Instance state listener.
+ * This method listens when managed instance states change.
+ * @param instance : instance
+ * @param newState : the now state of the given instance
+ * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int)
+ */
+ public void stateChanged(ComponentInstance instance, int newState) {
+ switch (newState) {
+ case ComponentInstance.DISPOSED:
+ case ComponentInstance.STOPPED:
+ break; // Should not happen
+ case ComponentInstance.VALID:
+ if (!getValidity()) {
+ checkValidity();
+ }
+ break;
+ case ComponentInstance.INVALID:
+ if (getValidity()) {
+ checkValidity();
+ }
+ break;
+ default:
+ break;
+
+ }
+ }
+
+ /**
+ * Method returning an instance object of the given component type.
+ * This method must be called only on 'primitive' type.
+ * @param type : type.
+ * @return an instance object or null if not found.
+ */
+ public Object getObjectFromInstance(String type) {
+ for (int i = 0; i < m_configurations.length; i++) {
+ if (m_configurations[i].getInstance() != null && type.equals(m_configurations[i].getFactory())) {
+ if (m_configurations[i].getInstance().getState() == ComponentInstance.VALID) {
+ return ((InstanceManager) m_configurations[i].getInstance()).getPojoObject();
+ } else {
+ error("An object cannot be get from the instance of the type " + type + ": invalid instance" + m_configurations[i].getInstance().getInstanceDescription().getDescription());
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the handler description, i.e. the state of created instances.
+ * @return the handler description.
+ * @see org.apache.felix.ipojo.CompositeHandler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_configurations.length; i++) {
+ list.add(m_configurations[i]);
+ }
+ return new InstanceHandlerDescription(this, list);
+ }
+
+ /**
+ * Get the list of used component type.
+ * @return the list containing the used component type
+ */
+ public List getUsedType() {
+ List result = new ArrayList();
+ for (int i = 0; i < m_configurations.length; i++) {
+ result.add(m_configurations[i].getConfiguration().get("component"));
+ }
+ return result;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java
new file mode 100644
index 0000000..2be1122
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/instance/InstanceHandlerDescription.java
@@ -0,0 +1,90 @@
+/*
+ * 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.composite.instance;
+
+import java.util.List;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler.ManagedConfiguration;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * Description of the Instance Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InstanceHandlerDescription extends HandlerDescription {
+
+ /**
+ * List of managed instances.
+ */
+ private List m_instances;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : handler
+ * @param insts : list of component instances
+ */
+ public InstanceHandlerDescription(CompositeHandler handler, List insts) {
+ super(handler);
+ m_instances = insts;
+ }
+
+ /**
+ * Build handler description.
+ * @return the handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element instances = super.getHandlerInfo();
+ for (int i = 0; i < m_instances.size(); i++) {
+ ManagedConfiguration inst = (ManagedConfiguration) m_instances.get(i);
+ Element instance = new Element("Instance", "");
+ if (inst.getInstance() == null) {
+ instance.addAttribute(new Attribute("Factory", inst.getConfiguration().get("component").toString()));
+ instance.addAttribute(new Attribute("State", "Not Available"));
+ } else {
+ instance.addAttribute(new Attribute("Factory", inst.getFactory()));
+ instance.addAttribute(new Attribute("Name", inst.getInstance().getInstanceName()));
+ String state = null;
+ switch(inst.getInstance().getState()) {
+ case ComponentInstance.DISPOSED :
+ state = "disposed"; break;
+ case ComponentInstance.STOPPED :
+ state = "stopped"; break;
+ case ComponentInstance.VALID :
+ state = "valid"; break;
+ case ComponentInstance.INVALID :
+ state = "invalid"; break;
+ default :
+ break;
+ }
+ instance.addAttribute(new Attribute("State", state));
+ instance.addElement(inst.getInstance().getInstanceDescription().getDescription());
+ }
+ instances.addElement(instance);
+ }
+ return instances;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
new file mode 100644
index 0000000..76f912c
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceDependencyHandler.java
@@ -0,0 +1,371 @@
+/*
+ * 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.composite.service.instantiator;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.composite.util.SourceManager;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.ParseException;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Service Instantiator Class. This handler allows to instantiate service
+ * instance inside the composition.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyHandler extends CompositeHandler implements DependencyStateListener {
+
+ /**
+ * List of instances to manage.
+ */
+ private List/* <SvcInstance> */m_instances = new ArrayList();
+
+ /**
+ * List of importers.
+ */
+ private List/* <ServiceImporter> */ m_importers = new ArrayList();
+
+ /**
+ * Flag indicating if the handler has already finished the start method.
+ */
+ private boolean m_isStarted;
+
+ /**
+ * Source Managers.
+ */
+ private List m_sources;
+
+
+ /**
+ * Create a Service instance object form the given Element.
+ * This method parse the given element and configure the service instance object.
+ * @param service : the Element describing the service instance
+ * @throws ConfigurationException : the service instance cannot be created correctly
+ */
+ private void createServiceInstance(Element service) throws ConfigurationException {
+ String spec = service.getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed service : the specification attribute is mandatory");
+ }
+ String filter = "(&(!(factory.name=" + getCompositeManager().getFactory().getComponentDescription().getName() + "))(factory.state=1))"; // Cannot reinstantiate yourself
+ String givenFilter = service.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ Filter fil;
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("Malformed filter " + filter + " : " + e.getMessage());
+ }
+
+ Properties prop = new Properties();
+ Element[] props = service.getElements("property");
+ for (int k = 0; props != null && k < props.length; k++) {
+ try {
+ InstanceHandler.parseProperty(props[k], prop);
+ } catch (ParseException e) {
+ throw new ConfigurationException("An instance configuration is invalid : " + e.getMessage());
+ }
+ }
+
+ String aggregate = service.getAttribute("aggregate");
+ boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
+
+ String optional = service.getAttribute("optional");
+ boolean opt = optional != null && optional.equalsIgnoreCase("true");
+
+ int policy = DependencyModel.getPolicy(service);
+
+ Comparator cmp = DependencyModel.getComparator(service, getCompositeManager().getGlobalContext());
+
+ SvcInstance inst = new SvcInstance(this, spec, prop, agg, opt, fil, cmp, policy);
+ m_instances.add(inst);
+
+ String sources = service.getAttribute("context-source");
+ if (sources != null) {
+ SourceManager source = new SourceManager(sources, filter, inst, getCompositeManager());
+ if (m_sources == null) {
+ m_sources = new ArrayList(1);
+ }
+ m_sources.add(source);
+ }
+ }
+
+ /**
+ * Create a Service importer object from the given Element.
+ * This method parse the given element and configure the service importer object.
+ * @param imp : Element describing the import
+ * @param confFilter : instance filter customization
+ * @throws ConfigurationException : the service importer cannot be created correctly
+ */
+ private void createServiceImport(Element imp, Dictionary confFilter) throws ConfigurationException {
+ boolean optional = false;
+ boolean aggregate = false;
+ String specification = imp.getAttribute("specification");
+
+ if (specification == null) {
+ // Malformed import
+ error("Malformed imports : the specification attribute is mandatory");
+ throw new ConfigurationException("Malformed imports : the specification attribute is mandatory");
+ } else {
+ String opt = imp.getAttribute("optional");
+ optional = opt != null && opt.equalsIgnoreCase("true");
+
+ String agg = imp.getAttribute("aggregate");
+ aggregate = agg != null && agg.equalsIgnoreCase("true");
+
+ String original = "(&(objectClass=" + specification + ")(!(instance.name=" + getCompositeManager().getInstanceName() + ")))"; // Cannot import yourself
+ String filter = original;
+ String givenFilter = imp.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ String identitity = imp.getAttribute("id");
+
+ String scope = imp.getAttribute("scope");
+ BundleContext context = getCompositeManager().getGlobalContext(); // Get the default bundle context.
+ if (scope != null) {
+ if (scope.equalsIgnoreCase("global")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.GLOBAL);
+ } else if (scope.equalsIgnoreCase("composite")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL);
+ } else if (scope.equalsIgnoreCase("composite+global")) {
+ context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL_AND_GLOBAL);
+ }
+ }
+
+ // Configure instance filter if available
+ if (confFilter != null && identitity != null && confFilter.get(identitity) != null) {
+ filter = "(&" + original + (String) confFilter.get(identitity) + ")";
+ }
+
+ Filter fil = null;
+ if (filter != null) {
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A required filter " + filter + " is malformed : " + e.getMessage());
+ }
+ }
+
+ Comparator cmp = DependencyModel.getComparator(imp, getCompositeManager().getGlobalContext());
+ Class spec = DependencyModel.loadSpecification(specification, getCompositeManager().getGlobalContext());
+ int policy = DependencyModel.getPolicy(imp);
+
+ ServiceImporter importer = new ServiceImporter(spec, fil, aggregate, optional, cmp, policy, context, identitity, this);
+ m_importers.add(importer);
+
+ String sources = imp.getAttribute("context-source");
+ if (sources != null) {
+ SourceManager source = new SourceManager(sources, filter, importer, getCompositeManager());
+ if (m_sources == null) {
+ m_sources = new ArrayList(1);
+ }
+ m_sources.add(source);
+ }
+
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param metadata : the metadata of the component
+ * @param conf : the instance configuration
+ * @throws ConfigurationException : the specification attribute is missing
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary conf) throws ConfigurationException {
+ Element[] services = metadata.getElements("subservice");
+ // Get instance filters
+ Dictionary confFilter = null;
+ if (conf.get("requires.filters") != null) {
+ confFilter = (Dictionary) conf.get("requires.filters");
+ }
+
+ for (int i = 0; i < services.length; i++) {
+ String action = services[i].getAttribute("action");
+ if (action == null) {
+ throw new ConfigurationException("The action attribute must be set to 'instantiate' or 'import'");
+ } else if ("instantiate".equalsIgnoreCase(action)) {
+ createServiceInstance(services[i]);
+ } else if ("import".equalsIgnoreCase(action)) {
+ createServiceImport(services[i], confFilter);
+ } else {
+ throw new ConfigurationException("Unknown action : " + action);
+ }
+
+
+
+ }
+ }
+
+ /**
+ * Start the service instantiator handler.
+ * Start all created service instance.
+ * @see org.apache.felix.ipojo.CompositeHandler#start()
+ */
+ public void start() {
+ for (int i = 0; m_sources != null && i < m_sources.size(); i++) {
+ SourceManager source = (SourceManager) m_sources.get(i);
+ source.start();
+ }
+
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ imp.start();
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ inst.start();
+ }
+
+ isHandlerValid();
+ m_isStarted = true;
+ }
+
+ /**
+ * Check the handler validity.
+ * @see org.apache.felix.ipojo.CompositeHandler#isValid()
+ */
+ private void isHandlerValid() {
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ if (imp.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ if (inst.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ setValidity(true);
+ }
+
+ /**
+ * Handler stop method.
+ * Stop all created service instance.
+ * @see org.apache.felix.ipojo.CompositeHandler#stop()
+ */
+ public void stop() {
+ for (int i = 0; m_sources != null && i < m_sources.size(); i++) {
+ SourceManager source = (SourceManager) m_sources.get(i);
+ source.stop();
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ inst.stop();
+ }
+
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ imp.stop();
+ }
+
+ m_isStarted = false;
+ }
+
+ /**
+ * State change callback.
+ * This method is used to freeze the set of used provider if the static binding policy is used.
+ * @param newState : the new state of the underlying instance
+ * @see org.apache.felix.ipojo.Handler#stateChanged(int)
+ */
+ public void stateChanged(int newState) {
+ // If we are becoming valid and started, check if we need to freeze importers.
+ if (m_isStarted && newState == ComponentInstance.VALID) {
+ for (int i = 0; i < m_importers.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_importers.get(i);
+ if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
+ imp.freeze();
+ }
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance imp = (SvcInstance) m_instances.get(i);
+ if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) {
+ imp.freeze();
+ }
+ }
+ }
+ }
+
+ /**
+ * An service instance becomes valid.
+ * @param dep : dependency becoming valid.
+ */
+ public void validate(DependencyModel dep) {
+ if (!getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * A service instance becomes invalid.
+ * @param dep : dependency becoming valid.
+ */
+ public void invalidate(DependencyModel dep) {
+ if (getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * Get the service instantiator handler description.
+ * @return the description
+ * @see org.apache.felix.ipojo.CompositeHandler#getDescription()
+ */
+ public HandlerDescription getDescription() {
+ return new ServiceInstantiatorDescription(this, m_instances, m_importers);
+ }
+
+ public List getInstances() {
+ return m_instances;
+ }
+
+ public List getRequirements() {
+ return m_importers;
+ }
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java
new file mode 100644
index 0000000..019ebb0
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceImporter.java
@@ -0,0 +1,319 @@
+/*
+ * 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.composite.service.instantiator;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Import a service form the parent to the internal service registry.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceImporter extends DependencyModel {
+
+ /**
+ * Reference on the handler.
+ */
+ private ServiceDependencyHandler m_handler;
+
+ private final class Record {
+ /**
+ * External Reference.
+ */
+ private ServiceReference m_ref;
+
+ /**
+ * Internal Registration.
+ */
+ private ServiceRegistration m_reg;
+
+ /**
+ * Exposed Object.
+ */
+ private Object m_svcObject;
+
+ /**
+ * Constructor.
+ * @param ref : service reference.
+ */
+ protected Record(ServiceReference ref) {
+ m_ref = ref;
+ }
+
+ /**
+ * Register the current import.
+ */
+ private void register() {
+ if (m_reg != null) {
+ m_reg.unregister();
+ }
+ m_svcObject = getService(m_ref);
+ m_reg = m_handler.getCompositeManager().getServiceContext().registerService(getSpecification().getName(), m_svcObject, getProps(m_ref));
+ }
+
+ /**
+ * Update the current import.
+ */
+ private void update() {
+ if (m_reg != null) {
+ m_reg.setProperties(getProps(m_ref));
+ }
+ }
+
+ /**
+ * Unregister and release the current import.
+ */
+ private void dispose() {
+ if (m_reg != null) {
+ m_reg.unregister();
+ m_svcObject = null;
+ m_reg = null;
+ }
+ m_ref = null;
+ }
+
+ /**
+ * Test object equality.
+ * @param object : object to confront against the current object.
+ * @return true if the two objects are equals (same service reference).
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object object) {
+ if (object instanceof Record) {
+ Record rec = (Record) object;
+ return rec.m_ref == m_ref;
+ }
+ return false;
+ }
+
+ /**
+ * Hash code method.
+ * @return the hash code by calling the parent method.
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+
+ /**
+ * List of managed records.
+ */
+ private List/*<Record>*/m_records = new ArrayList()/* <Record> */;
+
+ /**
+ * Requirement Id.
+ */
+ private String m_id;
+
+ /**
+ * Is this requirement attached to a service-level requirement.
+ */
+ private boolean m_specLevelReq;
+
+ /**
+ * Is the set of used provider frozen ?
+ */
+ private boolean m_isFrozen;
+
+ /**
+ * Constructor.
+ *
+ * @param specification : targeted specification
+ * @param filter : LDAP filter
+ * @param multiple : should the importer imports several services ?
+ * @param optional : is the import optional ?
+ * @param cmp : comparator to use for the tracking
+ * @param policy : resolving policy
+ * @param context : bundle context to use for the tracking (can be a servie context)
+ * @param identitity : requirement id (may be null)
+ * @param handler : handler
+ */
+ public ServiceImporter(Class specification, Filter filter, boolean multiple, boolean optional, Comparator cmp, int policy, BundleContext context, String identitity
+ , ServiceDependencyHandler handler) {
+ super(specification, multiple, optional, filter, cmp, policy, context, handler);
+
+ this.m_handler = handler;
+
+ if (m_id == null) {
+ m_id = super.getSpecification().getName();
+ } else {
+ m_id = identitity;
+ }
+
+ }
+
+ /**
+ * Get the properties for the exposed service from the given reference.
+ *
+ * @param ref : the reference.
+ * @return the property dictionary
+ */
+ private static Dictionary getProps(ServiceReference ref) {
+ Properties prop = new Properties();
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ prop.put(keys[i], ref.getProperty(keys[i]));
+ }
+ return prop;
+ }
+
+ /**
+ * Freeze the set of used provider.
+ * This method allow to fix the set of provider when the static binding policy is used.
+ */
+ public void freeze() {
+ m_isFrozen = true;
+ }
+
+ public boolean isFrozen() {
+ return m_isFrozen;
+ }
+
+ /**
+ * Stop the management of the import.
+ */
+ public void stop() {
+
+ super.stop();
+
+ for (int i = 0; i < m_records.size(); i++) {
+ Record rec = (Record) m_records.get(i);
+ rec.dispose();
+ }
+
+ m_records.clear();
+
+ }
+
+ /**
+ * Get the record list using the given reference.
+ *
+ * @param ref : the reference
+ * @return the list containing all record using the given reference
+ */
+ private List/* <Record> */getRecordsByRef(ServiceReference ref) {
+ List list = new ArrayList();
+ for (int i = 0; i < m_records.size(); i++) {
+ Record rec = (Record) m_records.get(i);
+ if (rec.m_ref == ref) {
+ list.add(rec);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Build the list of imported service provider.
+ * @return the list of all imported services.
+ */
+ public List getProviders() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_records.size(); i++) {
+ list.add((((Record) m_records.get(i)).m_ref).getProperty("instance.name"));
+ }
+ return list;
+ }
+
+ /**
+ * Set that this dependency is a service level dependency.
+ * This forces the scoping policy to be STRICT.
+ * @param b
+ */
+ public void setServiceLevelDependency() {
+ m_specLevelReq = true;
+ PolicyServiceContext context = new PolicyServiceContext(m_handler.getCompositeManager().getGlobalContext(), m_handler.getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL);
+ setBundleContext(context);
+ }
+
+ public String getId() {
+ return m_id;
+ }
+
+ public boolean isServiceLevelRequirement() {
+ return m_specLevelReq;
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ for (int i = 0; departs != null && i < departs.length; i++) {
+ onServiceDeparture(departs[i]);
+ }
+
+ for (int i = 0; arrivals != null && i < arrivals.length; i++) {
+ onServiceArrival(arrivals[i]);
+ }
+ }
+
+ /**
+ * A new service is injected by the tracker.
+ * This method create a 'Record' and register it.
+ * @param ref : new service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference ref) {
+ Record rec = new Record(ref);
+ m_records.add(rec);
+ // Always register the reference, as the method is call only when needed.
+ rec.register();
+ }
+
+ /**
+ * A used service disappears.
+ * This method find the implicated 'Record', dispose it and remove it from the list.
+ * @param ref : leaving service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceDeparture(ServiceReference ref) {
+ List list = getRecordsByRef(ref);
+ for (int i = 0; i < list.size(); i++) { // Stop the implied record
+ Record rec = (Record) list.get(i);
+ rec.dispose();
+ }
+ m_records.removeAll(list);
+ }
+
+ /**
+ * A used service is modified.
+ * @param ref : modified service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceModification(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceModification(ServiceReference ref) {
+ List list = getRecordsByRef(ref);
+ for (int i = 0; i < list.size(); i++) { // Stop the implied record
+ Record rec = (Record) list.get(i);
+ rec.update();
+ }
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java
new file mode 100644
index 0000000..2125153
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/ServiceInstantiatorDescription.java
@@ -0,0 +1,117 @@
+/*
+ * 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.composite.service.instantiator;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Description of the Service Creator Handler.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceInstantiatorDescription extends HandlerDescription {
+
+ /**
+ * List of managed service instances.
+ */
+ private List m_instances;
+
+ /**
+ * List of exports.
+ */
+ private List m_imports;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : composite handler
+ * @param insts : list of service instances
+ * @param imps : list of service importers
+ */
+ public ServiceInstantiatorDescription(CompositeHandler handler, List insts, List imps) {
+ super(handler);
+ m_instances = insts;
+ m_imports = imps;
+ }
+
+ /**
+ * Build service instantiator handler description.
+ * @return the handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element services = super.getHandlerInfo();
+ for (int i = 0; i < m_imports.size(); i++) {
+ ServiceImporter imp = (ServiceImporter) m_imports.get(i);
+ Element impo = new Element("Requires", "");
+ impo.addAttribute(new Attribute("Specification", imp.getSpecification().getName()));
+ if (imp.getFilter() != null) {
+ impo.addAttribute(new Attribute("Filter", imp.getFilter()));
+ }
+ if (imp.getState() == DependencyModel.RESOLVED) {
+ impo.addAttribute(new Attribute("State", "resolved"));
+ for (int j = 0; j < imp.getProviders().size(); j++) {
+ Element prov = new Element("Provider", "");
+ prov.addAttribute(new Attribute("name", (String) imp.getProviders().get(j)));
+ impo.addElement(prov);
+ }
+ } else {
+ impo.addAttribute(new Attribute("State", "unresolved"));
+ }
+ services.addElement(impo);
+ }
+
+ for (int i = 0; i < m_instances.size(); i++) {
+ SvcInstance inst = (SvcInstance) m_instances.get(i);
+ Element service = new Element("Service", "");
+ service.addAttribute(new Attribute("Specification", inst.getServiceSpecification()));
+ String state = "unresolved";
+ if (inst.getState() == DependencyModel.RESOLVED) {
+ state = "resolved";
+ }
+ service.addAttribute(new Attribute("State", state));
+ Map map = inst.getMatchingFactories();
+ Set keys = map.keySet();
+ Iterator iterator = keys.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Object object = map.get(ref);
+ if (object != null) {
+ Element fact = new Element("Factory", "");
+ fact.addAttribute(new Attribute("Name", ((ComponentInstance) object).getFactory().getName()));
+ service.addElement(fact);
+ }
+ }
+ services.addElement(service);
+ }
+ return services;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java
new file mode 100644
index 0000000..94c0a09
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/instantiator/SvcInstance.java
@@ -0,0 +1,267 @@
+/*
+ * 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.composite.service.instantiator;
+
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Manage a service instantiation. This service create component instance providing the required service specification.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SvcInstance extends DependencyModel {
+
+ /**
+ * Configuration to push to the instance.
+ */
+ private Dictionary m_configuration;
+
+ /**
+ * Handler creating the service instance.
+ */
+ private ServiceDependencyHandler m_handler;
+
+ /**
+ * Map of matching factories Service Reference => instance or null (null if the service reference is not actually used).
+ */
+ private Map /* <ServiceReference, Instance> */m_factories = new HashMap();
+
+ /**
+ * Required specification.
+ */
+ private String m_specification;
+
+ /**
+ * Is the service provider frozen ? (Is used for static biding policy)
+ */
+ private boolean m_isFrozen;
+
+ /**
+ * Constructor.
+ * @param handler : the handler.
+ * @param spec : required specification.
+ * @param conf : instance configuration.
+ * @param isAgg : is the service instance an aggregate service ?
+ * @param isOpt : is the service instance optional ?
+ * @param filt : LDAP filter
+ * @param cmp : comparator to use for the tracking
+ * @param policy : binding policy
+ * @throws ConfigurationException : an attribute cannot be parsed correctly, or is incorrect.
+ */
+ public SvcInstance(ServiceDependencyHandler handler, String spec, Dictionary conf, boolean isAgg, boolean isOpt, Filter filt, Comparator cmp, int policy) throws ConfigurationException {
+ super(Factory.class, isAgg, isOpt, filt, cmp, policy, null, handler);
+
+ m_specification = spec;
+
+ m_handler = handler;
+ setBundleContext(m_handler.getCompositeManager().getServiceContext());
+
+ m_configuration = conf;
+ }
+
+ /**
+ * Stop the service instance.
+ */
+ public void stop() {
+ super.stop();
+
+ Set keys = m_factories.keySet();
+ Iterator iterator = keys.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Object object = m_factories.get(ref);
+ if (object != null) {
+ ((ComponentInstance) object).dispose();
+ }
+ }
+
+ m_factories.clear();
+
+ }
+
+ public boolean isFrozen() {
+ return m_isFrozen;
+ }
+
+ /**
+ * Freeze the set of used provider.
+ * This method is when the static binding policy is applied.
+ */
+ public void freeze() {
+ m_isFrozen = true;
+ }
+
+ /**
+ * Create an instance for the given reference. The instance is not added inside the map.
+ * @param factory : the factory from which we need to create the instance.
+ * @return the created component instance.
+ * @throws ConfigurationException : the instance cannot be configured correctly.
+ * @throws MissingHandlerException : the factory is invalid.
+ * @throws UnacceptableConfiguration : the given configuration is invalid for the given factory.
+ */
+ private ComponentInstance createInstance(Factory factory) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException {
+ // Recreate the configuration to avoid sharing.
+ Properties props = new Properties();
+ Enumeration keys = m_configuration.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ props.put(key, m_configuration.get(key));
+ }
+ ComponentInstance instance = null;
+ instance = factory.createComponentInstance(props);
+ return instance;
+ }
+
+ /**
+ * Does the service instance match with the given factory ?
+ * @param fact : the factory to test.
+ * @return true if the factory match, false otherwise.
+ */
+ public boolean match(ServiceReference fact) {
+ // Check if the factory can provide the specification
+ ComponentTypeDescription desc = (ComponentTypeDescription) fact.getProperty("component.description");
+ if (desc == null) {
+ return false; // No component type description.
+ }
+
+ String[] provides = desc.getprovidedServiceSpecification();
+ for (int i = 0; provides != null && i < provides.length; i++) {
+ if (provides[i].equals(m_specification)) {
+ // Check that the factory needs every properties contained in
+ // the configuration
+ PropertyDescription[] props = desc.getProperties();
+ Properties conf = new Properties();
+ Enumeration keys = m_configuration.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (!containsProperty(key, props)) { return false; }
+ conf.put(key, m_configuration.get(key));
+ }
+
+ Factory factory = (Factory) getService(fact);
+ return factory.isAcceptable(conf);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Does the factory support the given property ? This method check if the property is contained in the given property description array.
+ * @param name : name of the property
+ * @param props : list of property description
+ * @return true if the factory support this property
+ */
+ private boolean containsProperty(String name, org.apache.felix.ipojo.architecture.PropertyDescription[] props) {
+ for (int i = 0; props != null && i < props.length; i++) {
+ if (props[i].getName().equalsIgnoreCase(name)) { return true; }
+ }
+ if (name.equalsIgnoreCase("name")) { return true; } // Skip the name property
+ return false;
+ }
+
+ /**
+ * Get the required specification.
+ * @return the required specification.
+ */
+ public String getServiceSpecification() {
+ return m_specification;
+ }
+
+ /**
+ * Get the map of used references [reference, component instance].
+ * @return the map of used references.
+ */
+ protected Map getMatchingFactories() {
+ return m_factories;
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ for (int i = 0; departs != null && i < departs.length; i++) {
+ onServiceDeparture(departs[i]);
+ }
+
+ for (int i = 0; arrivals != null && i < arrivals.length; i++) {
+ onServiceArrival(arrivals[i]);
+ }
+ }
+
+ /**
+ * A new service is injected.
+ * This method create the sub-service instance in the composite.
+ * @param ref : service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference ref) {
+ // The given factory matches.
+ try {
+ Factory fact = (Factory) getService(ref);
+ ComponentInstance instance = createInstance(fact);
+ m_factories.put(ref, instance);
+ } catch (UnacceptableConfiguration e) {
+ m_handler.error("A matching factory refuse the actual configuration : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ } catch (MissingHandlerException e) {
+ m_handler.error("A matching factory is no more valid : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ } catch (ConfigurationException e) {
+ m_handler.error("A matching configuration is refuse by the instance : " + e.getMessage());
+ m_handler.getCompositeManager().stop();
+ }
+
+ }
+
+
+ /**
+ * A used service is leaving.
+ * This method dispose the created instance.
+ * @param ref : leaving service reference.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceDeparture(ServiceReference ref) {
+ // Remove the reference is contained
+ Object instance = m_factories.remove(ref);
+ if (instance != null) {
+ ((ComponentInstance) instance).dispose();
+ }
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.java
new file mode 100644
index 0000000..9423d3c
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionException.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.composite.service.provides;
+
+/**
+ * Exception occurs when a composition error occurs.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionException extends Exception {
+ //TODO consider removing this class to use configuration exception.
+ /**
+ * serialVersionUID.
+ */
+ private static final long serialVersionUID = -3063353267573738105L;
+
+ /**
+ * Constructor.
+ * @param message : a message.
+ */
+ public CompositionException(String message) {
+ super(message);
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
new file mode 100644
index 0000000..8ccf749
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
@@ -0,0 +1,359 @@
+/*
+ * 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.composite.service.provides;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.manipulation.Manipulator;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Check and build a composition, i.e. a POJO containing the composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositionMetadata {
+
+ /**
+ * Implemented composition.
+ */
+ private SpecificationMetadata m_specification;
+
+ /**
+ * Name of the composition.
+ */
+ private String m_name;
+
+ /**
+ * Bundle Context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Manipulation Metadata.
+ */
+ private Element m_manipulation;
+
+ /**
+ * Reference on the handler.
+ */
+ private ProvidedServiceHandler m_handler;
+
+ /**
+ * List of Mappings.
+ */
+ private List m_mappings = new ArrayList();
+
+ /**
+ * Constructor.
+ * @param context : bundle context
+ * @param description : 'provides' element
+ * @param psh : parent handler
+ * @param name : name of the composition.
+ */
+ public CompositionMetadata(BundleContext context, Element description, ProvidedServiceHandler psh, String name) {
+ m_context = context;
+ m_handler = psh;
+ // Get the composition name
+ m_name = description.getAttribute("specification") + name;
+
+ // Get implemented service specification
+ String spec = description.getAttribute("specification");
+ m_specification = new SpecificationMetadata(spec, m_context, false, false, m_handler);
+
+ Element[] mappings = description.getElements("delegation");
+ for (int i = 0; mappings != null && i < mappings.length; i++) {
+ String methodName = mappings[i].getAttribute("method");
+ MethodMetadata method = m_specification.getMethodByName(methodName);
+ if (method == null) {
+ m_handler.error("The method " + methodName + " does not exist in the specicifation " + spec);
+ return;
+ }
+
+ if (mappings[i].getAttribute("policy").equalsIgnoreCase("All")) {
+ method.setAllPolicy();
+ }
+ }
+ }
+
+ protected BundleContext getBundleContext() {
+ return m_context;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public SpecificationMetadata getSpecificationMetadata() {
+ return m_specification;
+ }
+
+ /**
+ * Build Available Mappings.
+ * @throws CompositionException : a factory is not available, the composition cannot be checked.
+ */
+ private void buildAvailableMappingList() throws CompositionException {
+ int index = 0;
+
+ for (int i = 0; i < m_handler.getInstanceType().size(); i++) {
+ String type = (String) m_handler.getInstanceType().get(i);
+ try {
+ ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), "(factory.name=" + type + ")");
+ if (refs == null) {
+ m_handler.error("The factory " + type + " is not available, cannot check the composition");
+ throw new CompositionException("The factory " + type + " needs to be available to check the composition");
+ } else {
+ String className = (String) refs[0].getProperty("component.class");
+ Class impl = m_context.getBundle().loadClass(className);
+ SpecificationMetadata spec = new SpecificationMetadata(impl, type, m_handler);
+ FieldMetadata field = new FieldMetadata(spec);
+ field.setName("_field" + index);
+ Mapping map = new Mapping(spec, field);
+ m_mappings.add(map);
+ index++;
+ }
+ } catch (InvalidSyntaxException e) {
+ m_handler.error("A LDAP filter is not valid : " + e.getMessage());
+ } catch (ClassNotFoundException e) {
+ m_handler.error("The implementation class of a component cannot be loaded : " + e.getMessage());
+ }
+ }
+
+ for (int i = 0; i < m_handler.getSpecifications().size(); i++) {
+ SpecificationMetadata spec = (SpecificationMetadata) m_handler.getSpecifications().get(i);
+ FieldMetadata field = new FieldMetadata(spec);
+ field.setName("_field" + index);
+ if (spec.isOptional()) {
+ field.setOptional(true);
+ }
+ if (spec.isAggregate()) {
+ field.setAggregate(true);
+ }
+ Mapping map = new Mapping(spec, field);
+ m_mappings.add(map);
+ index++;
+ }
+ }
+
+ /**
+ * Build the delegation mapping.
+ * @throws CompositionException : occurs when the mapping cannot be inferred correctly
+ */
+ protected void buildMapping() throws CompositionException {
+ buildAvailableMappingList();
+
+ // Dependency closure is OK, now look for method delegation
+ Map/* <MethodMetadata, Mapping> */svcMethods = new HashMap();
+ Map/* <MethodMetadata, Mapping> */instMethods = new HashMap();
+
+ for (int i = 0; i < m_mappings.size(); i++) {
+ Mapping map = (Mapping) m_mappings.get(i);
+ SpecificationMetadata spec = map.getSpecification();
+ for (int j = 0; j < spec.getMethods().size(); j++) {
+ MethodMetadata method = (MethodMetadata) spec.getMethods().get(j);
+ if (spec.isInterface()) {
+ svcMethods.put(method, map);
+ } else {
+ instMethods.put(method, map);
+ }
+ }
+ }
+
+ // For each needed method, search if available and store the mapping
+ for (int j = 0; j < m_specification.getMethods().size(); j++) {
+ MethodMetadata method = (MethodMetadata) m_specification.getMethods().get(j);
+ Set keys = instMethods.keySet(); // Look first in methods contained in the glue code.
+ Iterator iterator = keys.iterator();
+ boolean found = false;
+ while (iterator.hasNext() & !found) {
+ MethodMetadata met = (MethodMetadata) iterator.next();
+ if (met.equals(method)) {
+ found = true;
+ FieldMetadata field = ((Mapping) instMethods.get(met)).getField();
+ field.setUseful(true);
+ method.setDelegation(field);
+ }
+ }
+ if (!found) { // If not found looks inside method contained in services.
+ keys = svcMethods.keySet(); // Look first in methods contained in the glue code
+ iterator = keys.iterator();
+ while (!found && iterator.hasNext()) {
+ MethodMetadata met = (MethodMetadata) iterator.next();
+ if (met.equals(method)) {
+ found = true;
+ FieldMetadata field = ((Mapping) svcMethods.get(met)).getField();
+ field.setUseful(true);
+ method.setDelegation(field);
+ // Test optional
+ if (field.isOptional() && !method.throwsUnsupportedOperationException()) {
+ m_handler.warn("The method " + method.getMethod().getName() + " could not be provided correctly : the specification " + field.getSpecification().getName() + " is optional");
+ }
+ }
+ }
+ }
+ if (!found) { throw new CompositionException("Inconsistent composition - the method " + method.getMethod() + " could not be delegated"); }
+ }
+ }
+
+ /**
+ * Build a service implementation.
+ * @return the byte[] of the POJO.
+ */
+ protected byte[] buildPOJO() {
+ Class clazz = null;
+ try {
+ clazz = getBundleContext().getBundle().loadClass(m_specification.getName());
+ } catch (ClassNotFoundException e1) {
+ // The class has already be loaded.
+ return null;
+ }
+ byte[] pojo = POJOWriter.dump(clazz, m_name, getFieldList(), getMethodList());
+ Manipulator manipulator = new Manipulator();
+ try {
+ byte[] newclazz = manipulator.manipulate(pojo);
+ m_manipulation = manipulator.getManipulationMetadata();
+ return newclazz;
+ } catch (IOException e) {
+ m_handler.error("An error occurs during the composite implementation creation : " + e.getMessage(), e);
+ }
+ return null;
+ }
+
+ /**
+ * Build service implementation metadata.
+ * @param name : name of the future instance (used to avoid cycle)
+ * @return Component Type metadata.
+ */
+ protected Element buildMetadata(String name) {
+ Element elem = new Element("component", "");
+ Attribute className = new Attribute("className", m_name);
+ Attribute factory = new Attribute("factory", "false");
+ elem.addAttribute(className);
+ elem.addAttribute(factory);
+
+ // Add architecture for debug
+ elem.addAttribute(new Attribute("architecture", "true"));
+
+ // Provides
+ Element provides = new Element("provides", "");
+ provides.addAttribute(new Attribute("specification", m_specification.getName()));
+ elem.addElement(provides);
+
+ // Dependencies
+ List fields = getFieldList();
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && field.getSpecification().isInterface()) {
+ Element dep = new Element("requires", "");
+ dep.addAttribute(new Attribute("field", field.getName()));
+ dep.addAttribute(new Attribute("scope", "composite"));
+ if (field.getSpecification().isOptional()) {
+ dep.addAttribute(new Attribute("optional", "true"));
+ }
+ dep.addAttribute(new Attribute("filter", "(!(instance.name=" + name + "))"));
+ elem.addElement(dep);
+ }
+ }
+
+ Element properties = new Element("properties", "");
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && !field.getSpecification().isInterface()) {
+ Element prop = new Element("Property", "");
+ prop.addAttribute(new Attribute("field", field.getName()));
+ properties.addElement(prop);
+ }
+ }
+ if (properties.getElements().length != 0) {
+ elem.addElement(properties);
+ }
+
+ // Insert information to metadata
+ elem.addElement(m_manipulation);
+
+ return elem;
+ }
+
+ /**
+ * Get the field list to use for the delegation.
+ * @return the field list.
+ */
+ public List getFieldList() {
+ List list = new ArrayList();
+ for (int i = 0; i < m_mappings.size(); i++) {
+ Mapping map = (Mapping) m_mappings.get(i);
+ list.add(map.getField());
+ }
+ return list;
+ }
+
+ /**
+ * Get the method list contained in the implemented specification.
+ * @return the List of implemented method.
+ */
+ private List getMethodList() {
+ return m_specification.getMethods();
+ }
+
+ /**
+ * Store links between Field and pointed Specification.
+ */
+ private class Mapping {
+
+ /**
+ * Specification.
+ */
+ private SpecificationMetadata m_spec;
+
+ /**
+ * Field.
+ */
+ private FieldMetadata m_field;
+
+ /**
+ * Constructor.
+ * @param spec : specification metadata.
+ * @param field : the field.
+ */
+ public Mapping(SpecificationMetadata spec, FieldMetadata field) {
+ m_spec = spec;
+ m_field = field;
+ }
+
+ public SpecificationMetadata getSpecification() {
+ return m_spec;
+ }
+
+ public FieldMetadata getField() {
+ return m_field;
+ }
+
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java
new file mode 100644
index 0000000..4df41a8
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/FieldMetadata.java
@@ -0,0 +1,105 @@
+/*
+ * 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.composite.service.provides;
+
+/**
+ * Field used inside a composition.
+ * This class contains all information useful for the generation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FieldMetadata {
+
+ /**
+ * Name of the field.
+ */
+ private String m_name;
+
+ /**
+ * Is the field an array?
+ */
+ private boolean m_isAggregate = false;
+
+ /**
+ * Interface of the field.
+ */
+ private SpecificationMetadata m_specification;
+
+ /**
+ * Is the field useful in this composition.
+ */
+ private boolean m_isUseful;
+
+ /**
+ * Is the dependency for this field optional.
+ */
+ private boolean m_isOptional = false;
+
+ /**
+ * Constructor.
+ * @param specification : interface of the field.
+ */
+ public FieldMetadata(SpecificationMetadata specification) {
+ super();
+ this.m_specification = specification;
+ if (m_specification.isAggregate()) {
+ m_isAggregate = true;
+ }
+ }
+
+ public boolean isAggregate() {
+ return m_isAggregate;
+ }
+
+ public void setAggregate(boolean aggregate) {
+ m_isAggregate = aggregate;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public void setName(String name) {
+ this.m_name = name;
+ }
+
+ public SpecificationMetadata getSpecification() {
+ return m_specification;
+ }
+
+ public void setSpecification(SpecificationMetadata specification) {
+ this.m_specification = specification;
+ }
+
+ public boolean isUseful() {
+ return m_isUseful;
+ }
+
+ public void setUseful(boolean useful) {
+ m_isUseful = useful;
+ }
+
+ public boolean isOptional() {
+ return m_isOptional;
+ }
+
+ public void setOptional(boolean opt) {
+ m_isOptional = opt;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java
new file mode 100644
index 0000000..510514e
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/MethodMetadata.java
@@ -0,0 +1,138 @@
+/*
+ * 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.composite.service.provides;
+
+import java.lang.reflect.Method;
+
+/**
+ * Information on Method for the composition.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MethodMetadata {
+
+ /**
+ * ONE POLICY.
+ */
+ public static final int ONE_POLICY = 1;
+
+ /**
+ * ALL POLICY.
+ */
+ public static final int ALL_POLICY = 2;
+
+ /**
+ * Method Object.
+ */
+ private Method m_method;
+
+ /**
+ * Delegation field.
+ */
+ private FieldMetadata m_delegation;
+
+ /**
+ * Delegation policy (default = ONE).
+ */
+ private int m_policy = ONE_POLICY;
+
+ /**
+ * Constructor.
+ * @param method : method object.
+ */
+ public MethodMetadata(Method method) {
+ m_method = method;
+ }
+
+ public Method getMethod() {
+ return m_method;
+ }
+
+ public void setDelegation(FieldMetadata field) {
+ m_delegation = field;
+ }
+
+ public FieldMetadata getDelegation() {
+ return m_delegation;
+ }
+
+ /**
+ * Equals method.
+ * This method check if two MethodMetadata are equals or if the current MemethodMetadata is equals with a Method object.
+ * @param object : object.
+ * @return true if the current object and the given object are equals.
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object object) {
+ if (object instanceof MethodMetadata) {
+ Method met = ((MethodMetadata) object).getMethod();
+ return equals(met);
+ }
+
+ if (object instanceof Method) {
+ Method met = (Method) object;
+ if (! met.getName().equals(m_method.getName()) || met.getParameterTypes().length != m_method.getParameterTypes().length) {
+ return false;
+ }
+
+ for (int i = 0; i < m_method.getParameterTypes().length; i++) {
+ if (!m_method.getParameterTypes()[i].getName().equals(met.getParameterTypes()[i].getName())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Hash code method.
+ * @return the parent hash code.
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public int getPolicy() {
+ return m_policy;
+ }
+
+ /**
+ * Activate the all policy for this method.
+ */
+ public void setAllPolicy() {
+ m_policy = ALL_POLICY;
+ }
+
+ /**
+ * Check if the method can throw UnsupportedOperationException.
+ * @return true if the method has declared the UnsupportedOperationException.
+ */
+ protected boolean throwsUnsupportedOperationException() {
+ for (int i = 0; i < m_method.getExceptionTypes().length; i++) {
+ if (m_method.getExceptionTypes()[i].getName().equals(UnsupportedOperationException.class.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java
new file mode 100644
index 0000000..91ed8cc
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/POJOWriter.java
@@ -0,0 +1,377 @@
+/*
+ * 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.composite.service.provides;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Create the Proxy class.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class POJOWriter implements Opcodes {
+
+ //TODO : consider using getOpCode
+ //TODO : fix bug on double-space
+ //TODO : use a logger
+ //TODO : merge this class with another class only static method.
+
+ /**
+ * Create a class.
+ * @param cw : class writer
+ * @param className : class name
+ * @param spec : implemented specification
+ */
+ private static void createClass(ClassWriter cw, String className, String spec) {
+ // Create the class
+ cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", new String[] { spec.replace('.', '/') });
+ }
+
+ /**
+ * Inject field in the current class.
+ * @param cw : class writer.
+ * @param fields : list of field to inject.
+ */
+ private static void injectFields(ClassWriter cw, List fields) {
+ // Inject fields
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful()) {
+ SpecificationMetadata spec = field.getSpecification();
+ String fieldName = field.getName();
+ String desc = "";
+ if (field.isAggregate()) {
+ desc = "[L" + spec.getName().replace('.', '/') + ";";
+ } else {
+ desc = "L" + spec.getName().replace('.', '/') + ";";
+ }
+
+ cw.visitField(Opcodes.ACC_PRIVATE, fieldName, desc, null, null);
+ }
+ }
+ }
+
+ /**
+ * Return the proxy 'classname' for the contract 'contractname' by delegating on available service.
+ * @param clazz : Specification class
+ * @param className : The class name to create
+ * @param fields : the list of fields on which delegate
+ * @param methods : the list of method on which delegate
+ * @return byte[] : the build class
+ */
+ public static byte[] dump(Class clazz, String className, List fields, List methods) {
+ Method[] itfmethods = clazz.getMethods();
+
+ // Create the class
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ className = className.replace('.', '/');
+ createClass(cw, className, clazz.getName());
+
+ // Inject fields inside the POJO
+ injectFields(cw, fields);
+
+ // Inject a constructor <INIT>()V
+ MethodVisitor cst = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ cst.visitVarInsn(ALOAD, 0);
+ cst.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+ cst.visitInsn(RETURN);
+ cst.visitMaxs(0, 0);
+ cst.visitEnd();
+
+ for (int i = 0; i < itfmethods.length; ++i) {
+ Method method = itfmethods[i];
+
+ // Get the field for this method
+ // 1) find the MethodMetadata
+ FieldMetadata delegator = null; // field to delegate
+ MethodMetadata methodDelegator = null; // field to delegate
+ for (int j = 0; j < methods.size(); j++) {
+ MethodMetadata methodMeta = (MethodMetadata) methods.get(j);
+ if (methodMeta.equals(method)) {
+ delegator = methodMeta.getDelegation();
+ methodDelegator = methodMeta;
+ }
+ }
+
+ generateOneMethod(cw, className, methodDelegator, method, delegator);
+
+ }
+
+ // End process
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ /**
+ * Generate on method.
+ * @param cw : class writer
+ * @param className : the current class name
+ * @param method : the method to generate
+ * @param sign : method signature to generate
+ * @param delegator : the field on which delegate
+ */
+ private static void generateOneMethod(ClassWriter cw, String className, MethodMetadata method, Method sign, FieldMetadata delegator) {
+ String desc = Type.getMethodDescriptor(sign);
+ String name = sign.getName();
+ String[] exc = new String[sign.getExceptionTypes().length];
+ for (int i = 0; i < sign.getExceptionTypes().length; i++) {
+ exc[i] = Type.getType(sign.getExceptionTypes()[i]).getInternalName();
+ }
+
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, desc, null, exc);
+
+ if (delegator.isOptional()) {
+ if (!delegator.isAggregate()) {
+ generateOptionalCase(mv, delegator, className);
+ }
+ if (delegator.isAggregate() /*&& method.getPolicy() == MethodMetadata.ONE_POLICY*/) {
+ generateOptionalAggregateCase(mv, delegator, className);
+ }
+ }
+
+ if (delegator.isAggregate()) {
+ if (method.getPolicy() == MethodMetadata.ONE_POLICY) {
+ // Aggregate and One Policy
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ICONST_0); // Take the first one
+ mv.visitInsn(AALOAD);
+
+ // Loads args
+ Type[] args = Type.getArgumentTypes(desc);
+ for (int i = 0; i < args.length; i++) {
+ writeLoad(args[i], i + 1, mv);
+ }
+
+ // Invoke
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+
+ // Return
+ writeReturn(Type.getReturnType(desc), mv);
+ } else { // All policy
+ if (Type.getReturnType(desc).getSort() != Type.VOID) {
+ //TODO use logger.
+ System.err.println("All policy cannot be used on method which does not return void");
+ }
+
+ Type[] args = Type.getArgumentTypes(desc);
+ int index = args.length + 1;
+
+ // Init
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ISTORE, index);
+ Label l1b = new Label();
+ mv.visitLabel(l1b);
+ Label l2b = new Label();
+ mv.visitJumpInsn(GOTO, l2b);
+
+ // Loop
+ Label l3b = new Label();
+ mv.visitLabel(l3b);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitVarInsn(ILOAD, index);
+ mv.visitInsn(AALOAD);
+
+ // Loads args
+ for (int i = 0; i < args.length; i++) {
+ writeLoad(args[i], i + 1, mv);
+ }
+
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+
+ Label l4b = new Label();
+ mv.visitLabel(l4b);
+ mv.visitIincInsn(index, 1); // i++;
+
+ // Condition
+ mv.visitLabel(l2b);
+ mv.visitVarInsn(ILOAD, index);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ARRAYLENGTH);
+ mv.visitJumpInsn(IF_ICMPLT, l3b);
+
+ Label l5b = new Label();
+ mv.visitLabel(l5b);
+ mv.visitInsn(RETURN);
+ }
+ } else {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+
+ // Loads args
+ Type[] args = Type.getArgumentTypes(desc);
+ for (int i = 0; i < args.length; i++) {
+ writeLoad(args[i], i + 1, mv);
+ }
+
+ // Invoke
+ if (delegator.getSpecification().isInterface()) {
+ mv.visitMethodInsn(INVOKEINTERFACE, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+ } else {
+ mv.visitMethodInsn(INVOKEVIRTUAL, delegator.getSpecification().getName().replace('.', '/'), name, desc);
+ }
+
+ // Return
+ writeReturn(Type.getReturnType(desc), mv);
+ }
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Generate Optional Case for aggregate field.
+ * @param mv : method visitor
+ * @param delegator : Field on which delegate
+ * @param className : current class name
+ */
+ private static void generateOptionalAggregateCase(MethodVisitor mv, FieldMetadata delegator, String className) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "[L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitInsn(ARRAYLENGTH);
+ Label l1a = new Label();
+ mv.visitJumpInsn(IFNE, l1a);
+ Label l2a = new Label();
+ mv.visitLabel(l2a);
+ mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn("Operation not supported");
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V");
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(l1a);
+ }
+
+ /**
+ * Generate Optional case for non aggregate fields.
+ *
+ * @param mv : the method visitor
+ * @param delegator : the field on which delegate.
+ * @param className : the name of the current class.
+ */
+ private static void generateOptionalCase(MethodVisitor mv, FieldMetadata delegator, String className) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, className, delegator.getName(), "L" + delegator.getSpecification().getName().replace('.', '/') + ";");
+ mv.visitTypeInsn(INSTANCEOF, "org/apache/felix/ipojo/Nullable");
+ Label end = new Label();
+ mv.visitJumpInsn(IFEQ, end);
+ Label begin = new Label();
+ mv.visitLabel(begin);
+ mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn("Operation not supported");
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V");
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(end);
+ }
+
+ /**
+ * Write a return instruction according to the given type.
+ * @param t : the type
+ * @param mv : the method visitor
+ */
+ private static void writeReturn(Type t, MethodVisitor mv) {
+ switch (t.getSort()) {
+ case Type.BOOLEAN:
+ case Type.INT:
+ case Type.BYTE:
+ case Type.CHAR:
+ case Type.SHORT:
+ // Integer or Boolean : return 0 ( false)
+ mv.visitInsn(IRETURN);
+ break;
+ case Type.LONG:
+ // mv.visitInsn(LCONST_0);
+ mv.visitInsn(LRETURN);
+ break;
+ case Type.DOUBLE:
+ // Double : return 0.0
+ // mv.visitInsn(DCONST_0);
+ mv.visitInsn(DRETURN);
+ break;
+ case Type.FLOAT:
+ // Double : return 0.0
+ // mv.visitInsn(DCONST_0);
+ mv.visitInsn(FRETURN);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ // Return always null for array and object
+ // mv.visitInsn(ACONST_NULL);
+ mv.visitInsn(ARETURN);
+ break;
+ case Type.VOID:
+ mv.visitInsn(RETURN);
+ break;
+ default:
+ System.err.println("Type not yet managed : " + t);
+ break;
+ }
+ }
+
+ /**
+ * Write a load instruction according to the given type.
+ * @param t : the type
+ * @param mv : the method visitor
+ * @param index : variable name (index)
+ */
+ private static void writeLoad(Type t, int index, MethodVisitor mv) {
+ switch (t.getSort()) {
+ case Type.BOOLEAN:
+ case Type.INT:
+ case Type.BYTE:
+ case Type.CHAR:
+ case Type.SHORT:
+ // Integer or Boolean : return 0 ( false)
+ mv.visitVarInsn(ILOAD, index);
+ break;
+ case Type.LONG:
+ // mv.visitInsn(LCONST_0);
+ mv.visitVarInsn(LLOAD, index);
+ break;
+ case Type.FLOAT:
+ // mv.visitInsn(LCONST_0);
+ mv.visitVarInsn(FLOAD, index);
+ break;
+ case Type.DOUBLE:
+ // Double : return 0.0
+ // mv.visitInsn(DCONST_0);
+ mv.visitVarInsn(DLOAD, index);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ // Return always null for array and object
+ // mv.visitInsn(ACONST_NULL);
+ mv.visitVarInsn(ALOAD, index);
+ break;
+ default:
+ System.err.println("Type not yet managed : " + t);
+ break;
+ }
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java
new file mode 100644
index 0000000..f76aea1
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedService.java
@@ -0,0 +1,235 @@
+/*
+ * 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.composite.service.provides;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentFactory;
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.apache.felix.ipojo.util.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Composite Provided Service.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedService implements DependencyStateListener {
+
+ /**
+ * Composite Manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Composition Model.
+ */
+ private CompositionMetadata m_composition;
+
+ /**
+ * Internal context.
+ */
+ private ServiceContext m_scope;
+
+ /**
+ * External context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Created Factory.
+ */
+ private ComponentFactory m_factory;
+
+ /**
+ * Created Instance.
+ */
+ private ComponentInstance m_instance;
+
+ /**
+ * Exporter.
+ */
+ private ServiceExporter m_exports;
+
+ /**
+ * Created instance name.
+ */
+ private String m_instanceName;
+
+ /**
+ * Constructor.
+ * The delegation mapping is infers in this method.
+ * @param handler : the handler.
+ * @param element : 'provides' element.
+ * @param name : name of this provided service.
+ */
+ public ProvidedService(ProvidedServiceHandler handler, Element element, String name) {
+ m_manager = handler.getCompositeManager();
+ m_scope = m_manager.getServiceContext();
+ m_context = m_manager.getContext();
+ m_composition = new CompositionMetadata(m_manager.getContext(), element, handler, name);
+ }
+
+ /**
+ * Start method.
+ * Build service implementation type, factory and instance.
+ * @throws CompositionException if a consistent mapping cannot be discovered.
+ */
+ public void start() throws CompositionException {
+ m_composition.buildMapping();
+
+ m_instanceName = m_composition.getSpecificationMetadata().getName() + "Provider-Gen";
+ byte[] clazz = m_composition.buildPOJO();
+ Element metadata = m_composition.buildMetadata(m_instanceName);
+
+ // Create the factory
+ try {
+ m_factory = new ComponentFactory(m_context, clazz, metadata);
+ } catch (ConfigurationException e) {
+ // Should not happen.
+ }
+ m_factory.start();
+
+ try {
+ Class spec = DependencyModel.loadSpecification(m_composition.getSpecificationMetadata().getName(), m_context);
+ Filter filter = m_context.createFilter("(instance.name=" + m_instanceName + ")");
+ // Create the exports
+ m_exports = new ServiceExporter(spec, filter, false, false, null, DependencyModel.DYNAMIC_BINDING_POLICY, m_scope, m_context, this, m_manager);
+ } catch (InvalidSyntaxException e) {
+ throw new CompositionException("A provided service filter is invalid : " + e.getMessage());
+ } catch (ConfigurationException e) {
+ throw new CompositionException("The class " + m_composition.getSpecificationMetadata().getName() + " cannot be loaded : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Stop the provided service.
+ * Kill the exporter, the instance and the factory.
+ */
+ public void stop() {
+ if (m_exports != null) {
+ m_exports.stop();
+ m_exports = null;
+ }
+ if (m_instance != null) {
+ m_instance.dispose();
+ m_instance = null;
+ }
+ if (m_factory != null) {
+ m_factory.stop();
+ m_factory = null;
+ }
+ }
+
+ protected CompositeManager getManager() {
+ return m_manager;
+ }
+
+ /**
+ * The exporter becomes valid.
+ * @param exporter : the exporter
+ */
+ public void validate(DependencyModel exporter) {
+ // Nothing to do.
+ }
+
+ /**
+ * The exporter becomes invalid.
+ * @param exporter : the exporter
+ */
+ public void invalidate(DependencyModel exporter) {
+ // Nothing to do.
+ }
+
+ /**
+ * Get an object from the given type.
+ * @param type : type
+ * @return an object from an instance of this type or null
+ */
+ private Object getObjectByType(String type) {
+ InstanceHandler handler = (InstanceHandler) m_manager.getCompositeHandler("org.apache.felix.ipojo.composite.instance.InstanceHandler");
+ Object pojo = handler.getObjectFromInstance(type);
+ if (pojo == null) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "An instance object cannot be found for the type : " + type);
+ }
+ return pojo;
+ }
+
+ public String getSpecification() {
+ return m_composition.getSpecificationMetadata().getName();
+ }
+
+ /**
+ * Unregister the exposed service.
+ */
+ public void unregister() {
+ m_exports.stop();
+ }
+
+ /**
+ * Register the exposed service.
+ */
+ public void register() {
+ Properties props = new Properties();
+ props.put("name", m_instanceName);
+ List fields = m_composition.getFieldList();
+ for (int i = 0; i < fields.size(); i++) {
+ FieldMetadata field = (FieldMetadata) fields.get(i);
+ if (field.isUseful() && !field.getSpecification().isInterface()) {
+ String type = field.getSpecification().getComponentType();
+ Object pojo = getObjectByType(type);
+ props.put(field.getName(), pojo);
+ }
+ }
+
+ if (m_instance == null) {
+ // Else we have to create the instance
+ try {
+ m_instance = m_factory.createComponentInstance(props, m_manager.getServiceContext());
+ } catch (UnacceptableConfiguration e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ } catch (MissingHandlerException e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ } catch (ConfigurationException e) {
+ throw new IllegalStateException("Cannot create the service implementation : " + e.getMessage());
+ }
+ } else {
+ // We have to reconfigure the instance in order to inject up to date glue component instance.
+ m_instance.reconfigure(props);
+ }
+
+ m_exports.start();
+ }
+
+ public boolean isRegistered() {
+ return m_exports.getState() == DependencyModel.RESOLVED;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
new file mode 100644
index 0000000..fcbec3c
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
@@ -0,0 +1,496 @@
+/*
+ * 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.composite.service.provides;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.HandlerFactory;
+import org.apache.felix.ipojo.HandlerManager;
+import org.apache.felix.ipojo.MissingHandlerException;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.UnacceptableConfiguration;
+import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.composite.instance.InstanceHandler;
+import org.apache.felix.ipojo.composite.service.instantiator.ServiceDependencyHandler;
+import org.apache.felix.ipojo.composite.service.instantiator.ServiceImporter;
+import org.apache.felix.ipojo.composite.service.instantiator.SvcInstance;
+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.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Composite Provided Service Handler.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandler extends CompositeHandler implements DependencyStateListener {
+
+ /**
+ * External context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * List of "available" services in the internal context.
+ */
+ private List m_services = new ArrayList();
+
+ /**
+ * List of exporters.
+ */
+ private List m_exporters = new ArrayList();
+
+ /**
+ * List of managed services.
+ */
+ private List m_managedServices = new ArrayList();
+
+ /**
+ * List of component type.
+ */
+ private List m_types;
+
+ /**
+ * Initialize the component type.
+ * @param desc : component type description to populate.
+ * @param metadata : component type metadata.
+ * @throws ConfigurationException : metadata are incorrect.
+ * @see org.apache.felix.ipojo.Handler#initializeComponentFactory(org.apache.felix.ipojo.architecture.ComponentTypeDescription, org.apache.felix.ipojo.metadata.Element)
+ */
+ public void initializeComponentFactory(ComponentTypeDescription desc, Element metadata) throws ConfigurationException {
+ Element[] provides = metadata.getElements("provides");
+ for (int i = 0; i < provides.length; i++) {
+ String action = provides[i].getAttribute("action");
+ if (action == null) {
+ throw new ConfigurationException("Invalid composition service providing : no specified action");
+ } else if (action.equalsIgnoreCase("implement")) {
+ String spec = provides[i].getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed provides : the specification attribute is mandatory");
+ } else {
+ desc.addProvidedServiceSpecification(spec);
+ }
+ } else if (action.equalsIgnoreCase("export")) {
+ String spec = provides[i].getAttribute("specification");
+ if (spec == null) {
+ throw new ConfigurationException("Malformed exports - Missing the specification attribute");
+ } else {
+ desc.addProvidedServiceSpecification(spec);
+ }
+ } else {
+ throw new ConfigurationException("Invalid composition service providing : unknown action " + action);
+ }
+ }
+ }
+
+ /**
+ * Configure the handler.
+ * @param metadata : the metadata of the component
+ * @param configuration : the instance configuration
+ * @throws ConfigurationException : the exporter cannot be created
+ * @see org.apache.felix.ipojo.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary)
+ */
+ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException {
+ m_context = getCompositeManager().getContext();
+
+ Element[] provides = metadata.getElements("provides", "");
+ for (int i = 0; i < provides.length; i++) {
+ String action = provides[i].getAttribute("action");
+ if (action.equalsIgnoreCase("implement")) {
+ ProvidedService svc = new ProvidedService(this, provides[i], Integer.toString(i));
+ m_managedServices.add(svc);
+ } else if (action.equalsIgnoreCase("export")) {
+ boolean optional = false;
+ boolean aggregate = false;
+ String specification = provides[i].getAttribute("specification");
+
+ String filter = "(objectClass=" + specification + ")";
+
+ String opt = provides[i].getAttribute("optional");
+ optional = opt != null && opt.equalsIgnoreCase("true");
+
+ String agg = provides[i].getAttribute("aggregate");
+ aggregate = agg != null && agg.equalsIgnoreCase("true");
+
+ String givenFilter = provides[i].getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ Filter fil = null;
+ try {
+ fil = m_context.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("An exporter filter is invalid " + filter + " : " + e.getMessage());
+ }
+
+ Comparator cmp = DependencyModel.getComparator(provides[i], m_context);
+ int policy = DependencyModel.getPolicy(provides[i]);
+ Class spec = DependencyModel.loadSpecification(specification, m_context);
+
+ ServiceExporter imp = new ServiceExporter(spec, fil, aggregate, optional, cmp, policy, getCompositeManager().getServiceContext(), m_context, this, getCompositeManager());
+ m_exporters.add(imp);
+ } // Others case cannot happen. The test was already made during the factory initialization.
+ }
+
+ }
+
+ /**
+ * Start method.
+ * Start all managed provided service.
+ * @see org.apache.felix.ipojo.CompositeHandler#start()
+ */
+ public void start() {
+ // Compute imports and instances
+ computeAvailableServices();
+ computeAvailableTypes();
+
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ try {
+ checkServiceSpecification(svc);
+ svc.start();
+ } catch (CompositionException e) {
+ error("Cannot start the provided service handler", e);
+ setValidity(false);
+ return;
+ }
+ }
+
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ exp.start();
+ }
+
+ isHandlerValid();
+ }
+
+ /**
+ * Stop method.
+ * Stop all managed provided service.
+ * @see org.apache.felix.ipojo.CompositeHandler#stop()
+ */
+ public void stop() {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.stop();
+ }
+
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ exp.stop();
+ }
+ }
+
+ /**
+ * Check the handler validity.
+ * @see org.apache.felix.ipojo.CompositeHandler#isValid()
+ */
+ private void isHandlerValid() {
+ for (int i = 0; i < m_exporters.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exporters.get(i);
+ if (exp.getState() != DependencyModel.RESOLVED) {
+ setValidity(false);
+ return;
+ }
+ }
+
+ setValidity(true);
+ }
+
+ /**
+ * Handler state changed.
+ * @param state : the new instance state.
+ * @see org.apache.felix.ipojo.CompositeHandler#stateChanged(int)
+ */
+ public void stateChanged(int state) {
+ if (state == ComponentInstance.INVALID) {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.unregister();
+ }
+ return;
+ }
+
+ // If the new state is VALID => register all the services
+ if (state == ComponentInstance.VALID) {
+ for (int i = 0; i < m_managedServices.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_managedServices.get(i);
+ svc.register();
+ }
+ return;
+ }
+ }
+
+ /**
+ * Notify the handler that an exporter becomes invalid.
+ *
+ * @param exporter : the implicated exporter.
+ */
+ public void invalidate(DependencyModel exporter) {
+ // An export is no more valid
+ if (getValidity()) {
+ setValidity(false);
+ }
+
+ }
+
+ /**
+ * Notify the handler that an exporter becomes valid.
+ *
+ * @param exporter : the implicated exporter.
+ */
+ public void validate(DependencyModel exporter) {
+ // An import becomes valid
+ if (!getValidity()) {
+ isHandlerValid();
+ }
+ }
+
+ /**
+ * Build the list of available specification.
+ * @return the list of available specification.
+ */
+ protected List getSpecifications() {
+ return m_services;
+ }
+
+ /**
+ * Build the list of available specifications.
+ */
+ private void computeAvailableServices() {
+ // Get instantiated services :
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+
+ for (int i = 0; handler != null && i < handler.getInstances().size(); i++) {
+ SvcInstance svc = (SvcInstance) handler.getInstances().get(i);
+ String itf = svc.getServiceSpecification();
+ boolean agg = svc.isAggregate();
+ boolean opt = svc.isOptional();
+
+ SpecificationMetadata specMeta = new SpecificationMetadata(itf, m_context, agg, opt, this);
+ m_services.add(specMeta);
+ }
+
+ for (int i = 0; handler != null && i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ String itf = imp.getSpecification().getName();
+ boolean agg = imp.isAggregate();
+ boolean opt = imp.isOptional();
+
+ SpecificationMetadata specMeta = new SpecificationMetadata(itf, m_context, agg, opt, this);
+ m_services.add(specMeta);
+ }
+ }
+
+ /**
+ * Check composite requirement against service specification requirement is available.
+ * @param svc : the provided service to check
+ * @throws CompositionException : occurs if the specification field of the service specification cannot be analyzed correctly.
+ */
+ private void checkServiceSpecification(ProvidedService svc) throws CompositionException {
+ try {
+ Class spec = m_context.getBundle().loadClass(svc.getSpecification());
+ Field specField = spec.getField("specification");
+ Object object = specField.get(null);
+ if (object instanceof String) {
+ Element specification = ManifestMetadataParser.parse((String) object);
+ Element[] reqs = specification.getElements("requires");
+ for (int j = 0; reqs != null && j < reqs.length; j++) {
+ ServiceImporter imp = getAttachedRequirement(reqs[j]);
+ if (imp != null) {
+ // Fix service-level dependency flag
+ imp.setServiceLevelDependency();
+ }
+ checkRequirement(imp, reqs[j]);
+ }
+ } else {
+ error("[" + getCompositeManager().getInstanceName() + "] The specification field of the service specification " + svc.getSpecification() + " need to be a String");
+ throw new CompositionException("Service Specification checking failed : The specification field of the service specification " + svc.getSpecification() + " need to be a String");
+ }
+ } catch (NoSuchFieldException e) {
+ return; // No specification field
+ } catch (ClassNotFoundException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The service specification " + svc.getSpecification() + " cannot be load");
+ throw new CompositionException("The service specification " + svc.getSpecification() + " cannot be load : " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " is not accessible : " + e.getMessage());
+ } catch (ParseException e) {
+ error("[" + getCompositeManager().getInstanceName() + "] The field 'specification' of the service specification " + svc.getSpecification() + " does not contain a valid String : " + e.getMessage());
+ throw new CompositionException("The field 'specification' of the service specification " + svc.getSpecification() + " does not contain a valid String : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Look for the implementation (i.e. composite) requirement for the given service-level requirement metadata.
+ * @param element : the service-level requirement metadata
+ * @return the ServiceImporter object, null if not found or if the DependencyHandler is not plugged to the instance
+ */
+ private ServiceImporter getAttachedRequirement(Element element) {
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+ if (handler == null) { return null; }
+
+ String identity = element.getAttribute("id");
+ if (identity != null) {
+ // Look for dependency Id
+ for (int i = 0; i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ if (imp.getId().equals(identity)) { return imp; }
+ }
+ }
+
+ // If not found or no id, look for a dependency with the same specification
+ String requirement = element.getAttribute("specification");
+ for (int i = 0; i < handler.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) handler.getRequirements().get(i);
+ if (imp.getId().equals(requirement) || imp.getSpecification().getName().equals(requirement)) { return imp; }
+ }
+ return null;
+ }
+
+ /**
+ * Check the correctness of the composite requirement against the service level dependency.
+ * @param imp : requirement to check
+ * @param elem : service-level dependency metadata
+ * @throws CompositionException : occurs if the requirement does not match with service-level specification requirement
+ */
+ private void checkRequirement(ServiceImporter imp, Element elem) throws CompositionException {
+ String optional = elem.getAttribute("optional");
+ boolean opt = optional != null && optional.equalsIgnoreCase("true");
+
+ String aggregate = elem.getAttribute("aggregate");
+ boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true");
+
+ if (imp == null) {
+ // Add the missing requirement
+ ServiceDependencyHandler handler = (ServiceDependencyHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":subservice");
+ if (handler == null) {
+ // Look for the ServiceDependencyHandler factory
+ HandlerManager handlerManager = null;
+ try {
+ ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), "(&(handler.name=subservice)(handler.namespace=" + HandlerFactory.IPOJO_NAMESPACE + ")(handler.type=composite))");
+ Factory factory = (Factory) m_context.getService(refs[0]);
+ handlerManager = (HandlerManager) factory.createComponentInstance(null, getCompositeManager().getServiceContext());
+ } catch (InvalidSyntaxException e) {
+ // Should not happen
+ } catch (UnacceptableConfiguration e) {
+ // Should not happen
+ } catch (MissingHandlerException e) {
+ // Should not happen
+ } catch (ConfigurationException e) {
+ // Should not happen
+ }
+
+ // Add the required handler
+ try {
+ handlerManager.init(getCompositeManager(), new Element("composite", ""), new Properties());
+ } catch (ConfigurationException e) {
+ error("Internal error : cannot configure the Import Handler : " + e.getMessage());
+ throw new CompositionException("Internal error : cannot configure the Import Handler : " + e.getMessage());
+ }
+ handler = (ServiceDependencyHandler) handlerManager.getHandler();
+ getCompositeManager().addCompositeHandler(handlerManager);
+ }
+
+ String spec = elem.getAttribute("specification");
+ String filter = "(&(objectClass=" + spec + ")(!(instance.name=" + getCompositeManager().getInstanceName() + ")))"; // Cannot import yourself
+ String givenFilter = elem.getAttribute("filter");
+ if (givenFilter != null) {
+ filter = "(&" + filter + givenFilter + ")"; //NOPMD
+ }
+
+ BundleContext context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.GLOBAL);
+
+ Filter fil = null;
+ try {
+ fil = getCompositeManager().getGlobalContext().createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new CompositionException("A required filter " + filter + " is malformed : " + e.getMessage());
+ }
+
+ Class specToImport = null;
+ try {
+ specToImport = getCompositeManager().getGlobalContext().getBundle().loadClass(spec);
+ } catch (ClassNotFoundException e) {
+ throw new CompositionException("A required specification cannot be loaded : " + spec);
+ }
+
+ ServiceImporter importer = new ServiceImporter(specToImport, fil, agg, opt, null, DependencyModel.DYNAMIC_BINDING_POLICY, context, null, handler);
+
+ handler.getRequirements().add(importer);
+ SpecificationMetadata specMeta = new SpecificationMetadata(spec, m_context, agg, opt, this);
+ m_services.add(specMeta); // Update the available types
+ return;
+ }
+
+ if (imp.isAggregate() && !agg) {
+ error("[" + getCompositeManager().getInstanceName() + "] The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ throw new CompositionException("The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ }
+
+ String filter = elem.getAttribute("filter");
+ if (filter != null) {
+ String filter2 = imp.getFilter();
+ if (filter2 == null || !filter2.equalsIgnoreCase(filter)) {
+ error("[" + getCompositeManager().getInstanceName() + "] The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ throw new CompositionException("The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ }
+ }
+ }
+
+ public HandlerDescription getDescription() {
+ return new ProvidedServiceHandlerDescription(this, m_managedServices, m_exporters);
+ }
+
+ /**
+ * Build available instance types.
+ */
+ private void computeAvailableTypes() {
+ InstanceHandler handler = (InstanceHandler) getHandler(HandlerFactory.IPOJO_NAMESPACE + ":instance");
+ if (handler == null) {
+ m_types = new ArrayList();
+ } else {
+ m_types = handler.getUsedType();
+ }
+ }
+
+ public List getInstanceType() {
+ return m_types;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.java
new file mode 100644
index 0000000..5c22246
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandlerDescription.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.composite.service.provides;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.CompositeHandler;
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.util.DependencyModel;
+
+/**
+ * Provided Service Handler Description for composite.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ProvidedServiceHandlerDescription extends HandlerDescription {
+
+ /**
+ * Provided Service Description list.
+ */
+ private List m_services = new ArrayList();
+
+ /**
+ * List of exports.
+ */
+ private List m_exports;
+
+ /**
+ * Constructor.
+ *
+ * @param handler : composite handler.
+ * @param services : The list of Provided Service.
+ * @param exporters : list of managed exports
+ */
+ public ProvidedServiceHandlerDescription(CompositeHandler handler, List services, List exporters) {
+ super(handler);
+ m_services = services;
+ m_exports = exporters;
+ }
+
+ /**
+ * Get the handler description.
+ * @return the provided service handler description
+ * @see org.apache.felix.ipojo.architecture.HandlerDescription#getHandlerInfo()
+ */
+ public Element getHandlerInfo() {
+ Element services = super.getHandlerInfo();
+ for (int i = 0; i < m_services.size(); i++) {
+ ProvidedService svc = (ProvidedService) m_services.get(i);
+ Element service = new Element("service", "");
+ String state = "unregistered";
+ if (svc.isRegistered()) {
+ state = "registered";
+ }
+ String spec = "[" + svc.getSpecification() + "]";
+ service.addAttribute(new Attribute("Specification", spec));
+ service.addAttribute(new Attribute("State", state));
+ services.addElement(service);
+ }
+
+ for (int i = 0; i < m_exports.size(); i++) {
+ ServiceExporter exp = (ServiceExporter) m_exports.get(i);
+ Element expo = new Element("Exports", "");
+ expo.addAttribute(new Attribute("Specification", exp.getSpecification().getName()));
+ expo.addAttribute(new Attribute("Filter", exp.getFilter()));
+ if (exp.getState() == DependencyModel.RESOLVED) {
+ expo.addAttribute(new Attribute("State", "resolved"));
+ } else {
+ expo.addAttribute(new Attribute("State", "unresolved"));
+ }
+ services.addElement(expo);
+ }
+
+ return services;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java
new file mode 100644
index 0000000..38a790f
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/ServiceExporter.java
@@ -0,0 +1,163 @@
+/*
+ * 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.composite.service.provides;
+
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.DependencyStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Export an service from the scope to the parent context.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceExporter extends DependencyModel {
+
+ /**
+ * Destination context.
+ */
+ private BundleContext m_destination;
+
+ /**
+ * Composite Manager.
+ */
+ private CompositeManager m_manager;
+
+ /**
+ * Map of service reference - service registration storing exported services.
+ */
+ private Map/*<ServiceReference, ServiceRegistration>*/m_registrations = new HashMap();
+
+ /**
+ * Constructor.
+ *
+ * @param specification : exported service specification.
+ * @param filter : LDAP filter
+ * @param multiple : is the export an aggregate export?
+ * @param optional : is the export optional?
+ * @param cmp : comparator to use in the dependency
+ * @param policy : binding policy.
+ * @param from : internal service context
+ * @param dest : parent bundle context
+ * @param listener : dependency lifecycle listener to notify when the dependency state change.
+ * @param manager : composite manager
+ */
+ public ServiceExporter(Class specification, Filter filter, boolean multiple, boolean optional, Comparator cmp, int policy, ServiceContext from, BundleContext dest, DependencyStateListener listener, CompositeManager manager) {
+ super(specification, multiple, optional, filter, cmp, policy, from, listener);
+
+ m_destination = dest;
+
+ m_manager = manager;
+
+ }
+
+ /**
+ * Transform service reference property in a dictionary.
+ * instance.name and factory.name are injected too.
+ * @param ref : the service reference.
+ * @return the dictionary containing all property of the given service reference.
+ */
+ private Dictionary getProps(ServiceReference ref) {
+ Properties prop = new Properties();
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ prop.put(keys[i], ref.getProperty(keys[i]));
+ }
+
+ prop.put("instance.name", m_manager.getInstanceName());
+ prop.put("factory.name", m_manager.getFactory().getName());
+
+ return prop;
+ }
+
+ /**
+ * Stop an exporter.
+ * Remove the service listener
+ * Unregister all exported services.
+ */
+ public void stop() {
+ super.stop();
+ Set refs = m_registrations.keySet();
+ Iterator iterator = refs.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(ref);
+ reg.unregister();
+ }
+ m_registrations.clear();
+ }
+
+ /**
+ * A service has been injected. Register it.
+ * @param reference : the new reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void onServiceArrival(ServiceReference reference) {
+ Object svc = getService(reference);
+ ServiceRegistration reg = m_destination.registerService(getSpecification().getName(), svc, getProps(reference));
+ m_registrations.put(reference, reg);
+ }
+
+ /**
+ * An exported service was modified.
+ * @param reference : modified reference
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void onServiceModification(ServiceReference reference) {
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(reference);
+ if (reg != null) {
+ reg.setProperties(getProps(reference));
+ }
+ }
+
+ /**
+ * An exported service disappears.
+ * @param reference : service reference
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void onServiceDeparture(ServiceReference reference) {
+ ServiceRegistration reg = (ServiceRegistration) m_registrations.get(reference);
+ if (reg != null) {
+ reg.unregister();
+ }
+ m_registrations.remove(reference);
+ }
+
+ /**
+ * On Dependency Reconfiguration notification method.
+ * @param departs : leaving service references.
+ * @param arrivals : new injected service references.
+ * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[])
+ */
+ public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) {
+ throw new UnsupportedOperationException("Dynamic dependency reconfiguration is not supported by service exporter");
+ }
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java
new file mode 100644
index 0000000..392de87
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/service/provides/SpecificationMetadata.java
@@ -0,0 +1,158 @@
+/*
+ * 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.composite.service.provides;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Represent a service specification.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SpecificationMetadata {
+
+ /**
+ * Name of the specification, i.e. name of the interface.
+ */
+ private String m_name;
+
+ /**
+ * List of the method contained in the specification.
+ */
+ private List/* <MethodMetadata> */m_methods = new ArrayList/* <MethodMetadata> */();
+
+ /**
+ * Is the specification an aggregate?
+ */
+ private boolean m_isAggregate;
+
+ /**
+ * Is the specification optional?
+ */
+ private boolean m_isOptional = false;
+
+ /**
+ * Is the specification an interface?
+ */
+ private boolean m_isInterface = true;
+
+ /**
+ * Component Type.
+ */
+ private String m_componentType = null;
+
+ /**
+ * Reference on the handler.
+ */
+ private ProvidedServiceHandler m_handler;
+
+ /**
+ * Constructor.
+ * @param name : specification name.
+ * @param context : bundle context.
+ * @param isAggregate : is the specification aggregate.
+ * @param isOptional : is the specification optional.
+ * @param psd : the handler.
+ */
+ public SpecificationMetadata(String name, BundleContext context, boolean isAggregate, boolean isOptional, ProvidedServiceHandler psd) {
+ m_name = name;
+ m_handler = psd;
+
+ // Populate methods :
+ try {
+ Class clazz = context.getBundle().loadClass(name);
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ MethodMetadata method = new MethodMetadata(methods[i]);
+ m_methods.add(method);
+ }
+ } catch (ClassNotFoundException e) {
+ m_handler.error("Cannot open " + name + " : " + e.getMessage());
+ return;
+ }
+
+ m_isAggregate = isAggregate;
+ m_isOptional = isOptional;
+ }
+
+ /**
+ * Constructor.
+ * @param clazz : class
+ * @param type : component type
+ * @param psd : the parent handler
+ */
+ public SpecificationMetadata(Class clazz, String type, ProvidedServiceHandler psd) {
+ m_handler = psd;
+ m_isAggregate = false;
+ m_isOptional = false;
+ m_componentType = type;
+ m_name = clazz.getName();
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ MethodMetadata method = new MethodMetadata(methods[i]);
+ m_methods.add(method);
+ }
+ m_isInterface = false;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public List/* <MethodMetadata> */getMethods() {
+ return m_methods;
+ }
+
+ /**
+ * Get a method by its name.
+ * @param name : method name
+ * @return the method metadata contained in the current specification with the given name. Null if the method is not found.
+ */
+ public MethodMetadata getMethodByName(String name) {
+ for (int i = 0; i < m_methods.size(); i++) {
+ MethodMetadata met = (MethodMetadata) m_methods.get(i);
+ if (met.getMethod().getName().equals(name)) { return met; }
+ }
+ return null;
+ }
+
+ public boolean isAggregate() {
+ return m_isAggregate;
+ }
+
+ public boolean isOptional() {
+ return m_isOptional;
+ }
+
+ public boolean isInterface() {
+ return m_isInterface;
+ }
+
+ public void setIsOptional(boolean optional) {
+ m_isOptional = optional;
+ }
+
+ public String getComponentType() {
+ return m_componentType;
+ }
+
+}
diff --git a/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java
new file mode 100644
index 0000000..d7f227a
--- /dev/null
+++ b/ipojo/composite/src/main/java/org/apache/felix/ipojo/composite/util/SourceManager.java
@@ -0,0 +1,376 @@
+/*
+ * 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.composite.util;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.ipojo.ConfigurationException;
+import org.apache.felix.ipojo.ContextListener;
+import org.apache.felix.ipojo.ContextSource;
+import org.apache.felix.ipojo.composite.CompositeManager;
+import org.apache.felix.ipojo.parser.ParseUtils;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.apache.felix.ipojo.util.Tracker;
+import org.apache.felix.ipojo.util.TrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class manages context-source management.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SourceManager implements ContextListener {
+
+ /**
+ * Source Name service property.
+ */
+ public static final String SOURCE_NAME = "source.name";
+
+ /**
+ * Managed dependency.
+ */
+ private DependencyModel m_dependency;
+
+ /**
+ * List of monitored context sources.
+ */
+ private List/* <ContextSource> */m_sources = new ArrayList(1);
+
+ /**
+ * PRoperties contained in the original filter.
+ */
+ private String[] m_properties;
+
+ /**
+ * Original filter (containing variables).
+ */
+ private String m_filter;
+
+ /**
+ * Bundle context.
+ */
+ private BundleContext m_context;
+
+ /**
+ * Service Tracker List.
+ */
+ private List/*<SourceTracker>*/m_trackers = new ArrayList(1);
+
+ /**
+ * Constructor.
+ * @param sources : context-source attribute from the dependency metadata
+ * @param depfilter : original dependency filter
+ * @param dependency : dependency object
+ * @param manager : composite manager
+ * @throws ConfigurationException : the sources are incorrect.
+ */
+ public SourceManager(String sources, String depfilter, DependencyModel dependency, CompositeManager manager) throws ConfigurationException {
+ m_filter = depfilter;
+ m_properties = getProperties(depfilter);
+ m_dependency = dependency;
+ m_context = manager.getGlobalContext();
+ if (manager.getParentServiceContext() == null) {
+ // The parent is the global context
+ parseSources(sources, manager.getGlobalContext(), manager.getGlobalContext(), manager.getServiceContext());
+ } else {
+ parseSources(sources, manager.getGlobalContext(), manager.getParentServiceContext(), manager.getServiceContext());
+ }
+ }
+
+ /**
+ * Start the context management.
+ */
+ public void start() {
+ for (int i = 0; i < m_trackers.size(); i++) {
+ ((SourceTracker) m_trackers.get(i)).open();
+ }
+ computeFilter();
+ }
+
+ /**
+ * Stop the context management.
+ */
+ public void stop() {
+ for (int i = 0; i < m_trackers.size(); i++) {
+ ((SourceTracker) m_trackers.get(i)).close();
+ }
+ setFilter(m_filter);
+ m_sources.clear();
+ }
+
+ /**
+ * Get the state of this source manager.
+ * @return the state of this source manager.
+ */
+ public int getState() {
+ if (m_sources.isEmpty()) {
+ return DependencyModel.UNRESOLVED;
+ } else {
+ return DependencyModel.RESOLVED;
+ }
+ }
+
+ /**
+ * Set the filter of the managed dependency.
+ * @param filter : the new filter to apply
+ */
+ private void setFilter(String filter) {
+ if (!filter.equals(m_dependency.getFilter())) {
+ // Reconfigure
+ try {
+ m_dependency.setFilter(m_context.createFilter(filter));
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("A context filter is invalid : " + filter);
+ }
+ }
+ }
+
+ /**
+ * Compute the new filter.
+ */
+ private void computeFilter() {
+ String fil = m_filter;
+ synchronized (this) {
+ for (int i = 0; i < m_sources.size(); i++) {
+ Dictionary props = ((ContextSource) m_sources.get(i)).getContext();
+ fil = substitute(fil, props); //NOPMD
+ }
+ }
+ if (!fil.equals(m_dependency.getFilter())) {
+ setFilter(fil);
+ }
+ }
+
+ /**
+ * This method substitute ${var} substring by values stored in a map.
+ * @param str : string with variables
+ * @param values : dictionary containing the variable name and the value.
+ * @return resulted string
+ */
+ public static String substitute(String str, Dictionary values) {
+ int len = str.length();
+ StringBuffer buffer = new StringBuffer(len);
+
+ int prev = 0;
+ int start = str.indexOf("${");
+ int end = str.indexOf('}', start);
+ while (start != -1 && end != -1) {
+ String key = str.substring(start + 2, end);
+ Object value = values.get(key);
+ if (value == null) {
+ buffer.append(str.substring(prev, end + 1));
+ } else {
+ buffer.append(str.substring(prev, start));
+ buffer.append(value);
+ }
+ prev = end + 1;
+ if (prev >= str.length()) {
+ break;
+ }
+
+ start = str.indexOf("${", prev);
+ if (start != -1) {
+ end = str.indexOf('}', start);
+ }
+ }
+
+ buffer.append(str.substring(prev));
+
+ return buffer.toString();
+ }
+
+ /**
+ * Compute the properties (${name}) from the given filter.
+ * @param str : string form of the filter.
+ * @return the list of found properties.
+ */
+ public static String[] getProperties(String str) {
+ List list = new ArrayList();
+ int prev = 0;
+ int start = str.indexOf("${");
+ int end = str.indexOf('}', start);
+ while (start != -1 && end != -1) {
+ String key = str.substring(start + 2, end);
+ list.add(key);
+ prev = end + 1;
+ if (prev >= str.length()) {
+ break;
+ }
+
+ start = str.indexOf("${", prev);
+ if (start != -1) {
+ end = str.indexOf('}', start);
+ }
+ }
+
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * A context source has modified a monitored property.
+ * @param source : source
+ * @param property : modified property
+ * @param value : new value.
+ * @see org.apache.felix.ipojo.ContextListener#update(org.apache.felix.ipojo.ContextSource, java.lang.String, java.lang.Object)
+ */
+ public synchronized void update(ContextSource source, String property, Object value) {
+ computeFilter();
+ }
+
+ /**
+ * Parse the context-source attribute in order to create source tracker object.
+ * @param sourceAtt : context-source attribute.
+ * @param global : global bundle context.
+ * @param parent : parent bundle context.
+ * @param local : local bundle context.
+ * @throws ConfigurationException : the context-source attribute is invalid.
+ */
+ private void parseSources(String sourceAtt, BundleContext global, BundleContext parent, BundleContext local) throws ConfigurationException {
+ String[] sources = ParseUtils.split(sourceAtt, ",");
+ for (int i = 0; i < sources.length; i++) {
+ String[] srcs = ParseUtils.split(sources[i], ":");
+ if (srcs.length == 1) {
+ // No prefix use local. //TODO choose default case.
+ SourceTracker tracker = new SourceTracker(srcs[0], local);
+ m_trackers.add(tracker);
+ } else if (srcs.length == 2) {
+ // According to prefix add the source in the good list.
+ if (srcs[0].equalsIgnoreCase("parent")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], parent);
+ m_trackers.add(tracker);
+ } else if (srcs[0].equalsIgnoreCase("local")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], local);
+ m_trackers.add(tracker);
+ } else if (srcs[0].equalsIgnoreCase("global")) {
+ SourceTracker tracker = new SourceTracker(srcs[1], global);
+ m_trackers.add(tracker);
+ } else {
+ throw new ConfigurationException("Unknowns context scope : " + srcs[0]);
+ }
+ } else {
+ throw new ConfigurationException("Malformed context source : " + sources[i]);
+ }
+ }
+ }
+
+ /**
+ * A context source appears.
+ * @param source : new context source.
+ */
+ private void addContextSource(ContextSource source) {
+ m_sources.add(source);
+ computeFilter();
+ source.registerContextListener(this, m_properties);
+ }
+
+ /**
+ * A context source disappears.
+ * @param source : leaving context source.
+ */
+ private void removeContextSource(ContextSource source) {
+ m_sources.remove(source);
+ computeFilter();
+ }
+
+ private class SourceTracker implements TrackerCustomizer {
+
+ /**
+ * Service tracker.
+ */
+ private Tracker m_tracker;
+
+ /**
+ * Constructor.
+ * @param name : name of the required context-source.
+ * @param countext : bundle context to use.
+ * @throws ConfigurationException : the context-source name is invalid.
+ */
+ public SourceTracker(String name, BundleContext countext) throws ConfigurationException {
+ String fil = "(&(" + Constants.OBJECTCLASS + "=" + ContextSource.class.getName() + ")(" + SOURCE_NAME + "=" + name + "))";
+ try {
+ Filter filter = countext.createFilter(fil);
+ m_tracker = new Tracker(countext, filter, this);
+ } catch (InvalidSyntaxException e) {
+ throw new ConfigurationException("A Context source filter is invalid " + fil + " : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Open the tracker.
+ */
+ public void open() {
+ m_tracker.open();
+ }
+
+ /**
+ * Close the tracker.
+ */
+ public void close() {
+ m_tracker.close();
+ }
+
+ /**
+ * A new context-source was added.
+ * This method inject the context-source object in the source manager.
+ * @param reference : service reference.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
+ */
+ public void addedService(ServiceReference reference) {
+ addContextSource((ContextSource) m_tracker.getService(reference));
+ }
+
+ /**
+ * A new context-source is adding in the tracker..
+ * @param reference : service reference
+ * @return true.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public boolean addingService(ServiceReference reference) {
+ return true;
+ }
+
+ /**
+ * A used context-source is modified.
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void modifiedService(ServiceReference reference, Object service) {
+ // Nothing to do.
+ }
+
+ /**
+ * A used context-source disappears.
+ * This method notify the Source Manager in order to manage this departure.
+ * @param reference : service reference.
+ * @param service : service object.
+ * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ removeContextSource((ContextSource) service);
+ }
+
+ }
+
+}
diff --git a/ipojo/composite/src/main/resources/metadata.xml b/ipojo/composite/src/main/resources/metadata.xml
new file mode 100644
index 0000000..cb71d71
--- /dev/null
+++ b/ipojo/composite/src/main/resources/metadata.xml
@@ -0,0 +1,27 @@
+<ipojo>
+ <!-- Composite Handler -->
+ <handler
+ classname="org.apache.felix.ipojo.composite.instance.InstanceHandler"
+ name="instance" type="composite" architecture="false" level="1">
+ <requires filter="(factory.state=1)" field="m_factories"
+ optional="true">
+ <callback type="bind" method="bindFactory" />
+ <callback type="unbind" method="unbindFactory" />
+ </requires>
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.service.instantiator.ServiceDependencyHandler"
+ name="subservice" type="composite" architecture="false">
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.service.provides.ProvidedServiceHandler"
+ name="provides" type="composite" architecture="false" level="3">
+ </handler>
+ <handler
+ classname="org.apache.felix.ipojo.composite.architecture.ArchitectureHandler"
+ name="architecture" type="composite" architecture="false">
+ <provides>
+ <property field="m_name" name="instance.name" value="" />
+ </provides>
+ </handler>
+</ipojo>
\ No newline at end of file