Initial upload
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@395940 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.scr/changelog.txt b/org.apache.felix.scr/changelog.txt
new file mode 100644
index 0000000..c4c3f89
--- /dev/null
+++ b/org.apache.felix.scr/changelog.txt
@@ -0,0 +1,5 @@
+04/21/2006
+
+This version of the declarative services is derived from the service binder's original source.
+At this point, it supports the declaration of components that provide services and references.
+The components may be immediate or delayed.
\ No newline at end of file
diff --git a/org.apache.felix.scr/pom.xml b/org.apache.felix.scr/pom.xml
new file mode 100644
index 0000000..67b40b6
--- /dev/null
+++ b/org.apache.felix.scr/pom.xml
@@ -0,0 +1,58 @@
+<project>
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix</artifactId>
+ <version>0.8.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>osgi-bundle</packaging>
+ <name>Apache Felix Declarative Services</name>
+ <artifactId>org.apache.felix.scr</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>${pom.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>${pom.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>kxml2</groupId>
+ <artifactId>kxml2</artifactId>
+ <version>2.2.2</version>
+ </dependency> </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix.plugins</groupId>
+ <artifactId>maven-osgi-plugin</artifactId>
+ <version>${pom.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <osgiManifest>
+ <bundleName>Service Component Runtime</bundleName>
+ <bundleVendor>Apache Software Foundation</bundleVendor>
+ <bundleDescription>
+ Implementation of the Declarative Services specification.
+ </bundleDescription>
+ <bundleActivator>
+ org.apache.felix.scr.Activator
+ </bundleActivator>
+ <bundleSymbolicName>org.apache.felix.scr</bundleSymbolicName>
+ <exportPackage>
+ org.apache.felix.scr
+ </exportPackage>
+ <importPackage>
+ org.osgi.service.component, org.osgi.framework
+ </importPackage>
+ </osgiManifest>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXHandler.java b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXHandler.java
new file mode 100644
index 0000000..6d32e75
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.declarativeservices.scr;
+
+import java.util.Properties;
+
+/**
+ * Interface for SAX handler with kXML
+ *
+ * @author Didier Donsez (didier.donsez@imag.fr)
+ */
+public interface KXml2SAXHandler {
+
+ /**
+ * Method called when parsing text
+ *
+ * @param ch
+ * @param offset
+ * @param length
+ * @exception SAXException
+ */
+ public void characters(char[] ch, int offset, int length) throws Exception;
+
+ /**
+ * Method called when a tag opens
+ *
+ * @param uri
+ * @param localName
+ * @param qName
+ * @param attrib
+ * @exception SAXException
+ **/
+ public void startElement(
+ String uri,
+ String localName,
+ String qName,
+ Properties attrib)
+ throws Exception;
+ /**
+ * Method called when a tag closes
+ *
+ * @param uri
+ * @param localName
+ * @param qName
+ * @exception SAXException
+ */
+ public void endElement(
+ java.lang.String uri,
+ java.lang.String localName,
+ java.lang.String qName)
+ throws Exception;
+
+ public void processingInstruction(String target,
+ String data)
+ throws Exception;
+
+ public void setLineNumber(int lineNumber);
+
+ public void setColumnNumber(int columnNumber);
+}
\ No newline at end of file
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXParser.java b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXParser.java
new file mode 100644
index 0000000..99a6ea3
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/KXml2SAXParser.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.declarativeservices.scr;
+
+import java.io.Reader;
+import java.util.Properties;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * The KXml2SAXParser extends the XmlParser from kxml. This is a very
+ * simple parser that does not take into account the DTD
+ *
+ */
+public class KXml2SAXParser extends KXmlParser {
+
+ public String uri="uri";
+
+ private Reader reader;
+
+ /**
+ * The constructor for a parser, it receives a java.io.Reader.
+ *
+ * @param reader The reader
+ * @throws XmlPullParserException
+ */
+ public KXml2SAXParser(Reader reader) throws XmlPullParserException {
+ super();
+ this.reader=reader;
+ setInput(reader);
+ }
+
+ /**
+ * Parser from the reader provided in the constructor, and call
+ * the startElement and endElement in a KxmlHandler
+ *
+ * @param reader The reader
+ * @exception Exception thrown by the superclass
+ */
+ public void parseXML(KXml2SAXHandler handler) throws Exception {
+
+ while (next() != XmlPullParser.END_DOCUMENT) {
+ handler.setLineNumber(getLineNumber());
+ handler.setColumnNumber(getColumnNumber());
+ if (getEventType() == XmlPullParser.START_TAG) {
+ Properties props = new Properties();
+ for (int i = 0; i < getAttributeCount(); i++) {
+ props.put(getAttributeName(i), getAttributeValue(i));
+ }
+ handler.startElement(
+ getNamespace(),
+ getName(),
+ getName(),
+ props);
+ } else if (getEventType() == XmlPullParser.END_TAG) {
+ handler.endElement(getNamespace(), getName(), getName());
+ } else if (getEventType() == XmlPullParser.TEXT) {
+ String text = getText();
+ handler.characters(text.toCharArray(),0,text.length());
+ } else if (getEventType() == XmlPullParser.PROCESSING_INSTRUCTION) {
+ // TODO extract the target from the evt.getText()
+ handler.processingInstruction(null,getText());
+ } else {
+ // do nothing
+ }
+ }
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/ParseException.java b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/ParseException.java
new file mode 100644
index 0000000..41a8a85
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/declarativeservices/scr/ParseException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.declarativeservices.scr;
+
+public class ParseException extends Exception
+{
+ Exception m_originalException;
+
+ public ParseException(String msg, Exception originalException) {
+ super(msg);
+ m_originalException = originalException;
+ }
+
+ public Exception getException() {
+ return m_originalException;
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/Activator.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/Activator.java
new file mode 100644
index 0000000..112f376
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/Activator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This activator is used to cover requirement described in section 112.8.1
+ * When SCR is implemented as a bundle, any component configurations
+ * activated by SCR must be deactivated when the SCR bundle is stopped. When
+ * the SCR bundle is started, it must process any components that are declared
+ * in active bundles.
+ *
+ */
+public class Activator implements BundleActivator {
+
+ public void start(BundleContext arg0) throws Exception {
+ //TODO: pending
+ }
+
+ public void stop(BundleContext arg0) throws Exception {
+ // TODO: pending
+ }
+
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManager.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManager.java
new file mode 100644
index 0000000..e7696a6
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+/**
+ * This interface is provided so that there can be multiple implementations of
+ * managers that are responsible for managing component's lifecycle.
+ *
+ */
+public interface ComponentManager {
+
+ /**
+ * Enable the component
+ *
+ * @return true if it was succesfully enabled, false otherwise
+ */
+ public boolean enable();
+
+ /**
+ * Dispose the component
+ *
+ */
+ public void dispose();
+
+ /**
+ * Get the component information
+ *
+ * @return a ComponentMetadata object
+ */
+ public ComponentMetadata getComponentMetadata();
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManagerImpl.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManagerImpl.java
new file mode 100644
index 0000000..a8779bb
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentManagerImpl.java
@@ -0,0 +1,1041 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import java.util.Dictionary;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Bundle;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentInstance;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * The default ComponentManager. Objects of this class are responsible for managing
+ * implementation object's lifecycle.
+ *
+ */
+public class ComponentManagerImpl implements ComponentManager, ComponentInstance
+{
+ // States of the instance manager
+ static final int INSTANCE_CREATING = 0;
+ static final int INSTANCE_CREATED = 1;
+ static final int INSTANCE_VALIDATING = 2;
+ static final int INSTANCE_VALID = 3;
+ static final int INSTANCE_INVALIDATING = 4;
+ static final int INSTANCE_INVALID = 5;
+ static final int INSTANCE_DESTROYING = 6;
+ static final int INSTANCE_DESTROYED = 7;
+
+
+ static final String m_states[]={"CREATING","CREATED",
+ "VALIDATING","VALID",
+ "INVALIDATING","INVALID",
+ "DESTROYING","DESTROYED"
+ };
+
+ // The state of this instance manager
+ private int m_state = INSTANCE_CREATING;
+
+ // The metadata
+ private ComponentMetadata m_componentMetadata;
+
+ // The object that implements the service and that is bound to other services
+ private Object m_implementationObject = null;
+
+ // The dependency managers that manage every dependency
+ private List m_dependencyManagers = new ArrayList();
+
+ // The ServiceRegistration
+ private ServiceRegistration m_serviceRegistration = null;
+
+ // A reference to the GenericActivator
+ private GenericActivator m_activator = null;
+
+ // The context that will be passed to the implementationObject
+ private ComponentContext m_componentContext = null;
+
+ // In case of a delayed component, this holds a reference to the factory
+ private DelayedComponentServiceFactory m_delayedComponentServiceFactory;
+
+ /**
+ * The constructor receives both the activator and the metadata
+ *
+ * @param activator
+ * @param metadata
+ */
+ ComponentManagerImpl(GenericActivator activator,ComponentMetadata metadata)
+ {
+ // Store the activator reference
+ m_activator = activator;
+
+ // Store the metadata reference
+ m_componentMetadata = metadata;
+ }
+
+ /**
+ * Enable this component
+ *
+ * @return true if enabling was successful
+ */
+ public boolean enable() {
+
+ GenericActivator.trace("Enabling component", m_componentMetadata);
+
+ try
+ {
+ // If this component has got dependencies, create dependency managers for each one of them.
+ if (m_componentMetadata.getDependencies().size() != 0)
+ {
+ Iterator dependencyit = m_componentMetadata.getDependencies().iterator();
+
+ while(dependencyit.hasNext())
+ {
+ ReferenceMetadata currentdependency = (ReferenceMetadata)dependencyit.next();
+
+ DependencyManager depmanager = new DependencyManager(currentdependency);
+
+ m_dependencyManagers.add(depmanager);
+
+ // Register the dependency managers as listeners to service events so that they begin
+ // to manage the dependency autonomously
+ m_activator.getBundleContext().addServiceListener(depmanager,depmanager.m_dependencyMetadata.getTarget());
+ }
+ }
+
+ // TODO: create the context
+ //m_sbcontext = new ServiceBinderContextImpl(this);
+
+ // Add this instance manager to the Generic activator list
+ //m_activator.addInstanceManager(this);
+
+
+ setState(INSTANCE_CREATED);
+
+ activate();
+
+ return true;
+ }
+ catch(Exception ex)
+ {
+ // TODO: Log this error
+ return false;
+ }
+ }
+
+ /**
+ * Activate this Instance manager.
+ *
+ * 112.5.6 Activating a component configuration consists of the following steps
+ * 1. Load the component implementation class
+ * 2. Create the component instance and component context
+ * 3. Bind the target services
+ * 4. Call the activate method, if present
+ * [5. Register provided services]
+ */
+ synchronized private void activate()
+ {
+ // CONCURRENCY NOTE: This method is called either by the enable() method or by the dependency
+ // managers.
+ if (m_state != INSTANCE_INVALID && m_state !=INSTANCE_CREATED) {
+ // This state can only be entered from the CREATED or INVALID states
+ return;
+ }
+
+ setState(INSTANCE_VALIDATING);
+
+ // Before creating the implementation object, we are going to
+ // test if all the mandatory dependencies are satisfied
+ Iterator it = m_dependencyManagers.iterator();
+
+ while (it.hasNext())
+ {
+ // It is not possible to call the isValid method yet in the DependencyManagers
+ // since they have not been initialized yet, but they can't be initialized yet
+ // because the implementationObject has not been created yet.
+ // This test is necessary, because we don't want to instantiate
+ // the implementationObject if the dependency managers aren't valid.
+ DependencyManager dm = (DependencyManager)it.next();
+ if (dm.getRequiredServiceRefs() == null && dm.m_dependencyMetadata.isOptional() == false)
+ {
+ setState(INSTANCE_INVALID);
+ return;
+ }
+ }
+
+ // 1. Load the component implementation class
+ // 2. Create the component instance and component context
+ // If the component is not immediate, this is not done at this moment
+ if( m_componentMetadata.isImmediate() == true )
+ {
+ //GenericActivator.trace("Loading implementation class and creating instance for component '"+m_componentMetadata.getName()+"'");
+ try
+ {
+ // 112.4.4 The class is retrieved with the loadClass method of the component's bundle
+ Class c = m_activator.getBundleContext().getBundle().loadClass(m_componentMetadata.getImplementationClassName());
+
+ // 112.4.4 The class must be public and have a public constructor without arguments so component instances
+ // may be created by the SCR with the newInstance method on Class
+ m_componentContext = new ComponentContextImpl();
+ m_implementationObject = c.newInstance();
+ }
+ catch (Exception ex)
+ {
+ // TODO: manage this exception when implementation object cannot be created
+ GenericActivator.exception("Error during instantiation", m_componentMetadata, ex);
+ deactivate();
+ //invalidate();
+ return;
+ }
+ } else {
+ m_delayedComponentServiceFactory = new DelayedComponentServiceFactory();
+ }
+
+ // 3. Bind the target services
+ it = m_dependencyManagers.iterator();
+
+ //GenericActivator.trace("Binding target services for component '"+m_componentMetadata.getName()+"'");
+
+ while (it.hasNext())
+ {
+ DependencyManager dm = (DependencyManager)it.next();
+
+ // if any of the dependency managers is unable to bind (it is invalid), the component is deactivated
+ if (dm.bind() == false)
+ {
+ deactivate();
+ return;
+ }
+ }
+
+ //GenericActivator.trace("Calling activate for component '"+m_componentMetadata.getName()+"'");
+
+ // 4. Call the activate method, if present
+ // We need to check if we are still validating because it is possible that when we
+ // registered the service above our thread causes an instance to become valid which
+ // then registered a service that then generated an event that we needed that
+ // caused validate() to be called again, thus if we are not still VALIDATING, it
+ // means we are already VALID.
+ if ( m_componentMetadata.isImmediate() == true && m_state == INSTANCE_VALIDATING)
+ {
+ // Search for the activate method
+ try {
+ Method activateMethod = m_implementationObject.getClass().getMethod("activate", new Class[]{ComponentContext.class});
+ activateMethod.invoke(m_implementationObject, new Object[]{m_componentContext});
+ }
+ catch(NoSuchMethodException ex) {
+ // We can safely ignore this one
+ GenericActivator.trace("activate() method not implemented", m_componentMetadata);
+ }
+ catch(IllegalAccessException ex) {
+ // TODO: Log this exception?
+ GenericActivator.trace("activate() method cannot be called", m_componentMetadata);
+ }
+ catch(InvocationTargetException ex) {
+ // TODO: 112.5.8 If the activate method throws an exception, SCR must log an error message
+ // containing the exception with the Log Service
+ GenericActivator.exception("The activate method has thrown an exception", m_componentMetadata, ex.getTargetException());
+ }
+ }
+
+ // Validation occurs before the services are provided, otherwhise the service provider's service may be called
+ // by a service requester while it is still VALIDATING
+ setState(INSTANCE_VALID);
+
+ // 5. Register provided services
+ if(m_componentMetadata.getServiceMetadata() != null)
+ {
+ GenericActivator.trace("registering services", m_componentMetadata);
+
+ if( m_componentMetadata.isImmediate() == true ) {
+ // In the case the component is immediate, the implementation object is registered
+ m_serviceRegistration = m_activator.getBundleContext().registerService(m_componentMetadata.getServiceMetadata().getProvides(), m_implementationObject, m_componentMetadata.getProperties());
+ }else {
+ // In the case the component is delayed, a factory is registered
+ m_serviceRegistration = m_activator.getBundleContext().registerService(m_componentMetadata.getServiceMetadata().getProvides(), m_delayedComponentServiceFactory, m_componentMetadata.getProperties());
+ }
+ }
+ }
+
+ /**
+ * This method deactivates the manager, performing the following steps
+ *
+ * [0. Remove published services from the registry]
+ * 1. Call the deactivate() method, if present
+ * 2. Unbind any bound services
+ * 3. Release references to the component instance and component context
+ **/
+ synchronized private void deactivate()
+ {
+ // CONCURRENCY NOTE: This method may be called either from application code or by the dependency managers
+ if (m_state != INSTANCE_VALID && m_state != INSTANCE_VALIDATING && m_state != INSTANCE_DESTROYING) {
+ return;
+ }
+
+ // In case the instance is valid when this is called, the manager is set to an invalidating state
+ if (m_state != INSTANCE_DESTROYING)
+ {
+ setState(INSTANCE_INVALIDATING);
+ }
+
+ // 0.- Remove published services from the registry
+ if(m_serviceRegistration != null)
+ {
+ m_serviceRegistration.unregister();
+ m_serviceRegistration = null;
+
+ GenericActivator.trace("unregistering the services", m_componentMetadata);
+ }
+
+ // 1.- Call the deactivate method, if present
+ // Search the deactivate method
+ try {
+ // It is necessary to check that the implementation Object is not null. This may happen if the component
+ // is delayed and its service was never requested.
+ if(m_implementationObject != null)
+ {
+ Method activateMethod = m_implementationObject.getClass().getMethod("deactivate", new Class[]{ComponentContext.class});
+ activateMethod.invoke(m_implementationObject, new Object[]{m_componentContext});
+ }
+ }
+ catch(NoSuchMethodException ex) {
+ // We can safely ignore this one
+ GenericActivator.trace("deactivate() method is not implemented", m_componentMetadata);
+ }
+ catch(IllegalAccessException ex) {
+ // Ignored, but should it be logged?
+ GenericActivator.trace("deactivate() method cannot be called", m_componentMetadata);
+ }
+ catch(InvocationTargetException ex) {
+ // TODO: 112.5.12 If the deactivate method throws an exception, SCR must log an error message
+ // containing the exception with the Log Service
+ GenericActivator.exception("The deactivate method has thrown and exception", m_componentMetadata, ex);
+ }
+
+ // 2. Unbind any bound services
+ Iterator it = m_dependencyManagers.iterator();
+
+ while (it.hasNext())
+ {
+ DependencyManager dm = (DependencyManager)it.next();
+ dm.unbind();
+ }
+
+ // 3. Release references to the component instance and component context
+ m_implementationObject = null;
+ m_componentContext = null;
+ m_delayedComponentServiceFactory = null;
+
+ //GenericActivator.trace("InstanceManager from bundle ["+ m_activator.getBundleContext().getBundle().getBundleId() + "] was invalidated.");
+
+ if (m_state != INSTANCE_DESTROYING)
+ {
+ setState(INSTANCE_INVALID);
+ }
+ }
+
+ /**
+ *
+ */
+ public synchronized void dispose()
+ {
+ // CONCURRENCY NOTE: This method is only called from the GenericActivator or by application logic
+ // but not by the dependency managers
+
+ // Theoretically this should never be in any state other than VALID or INVALID,
+ // because validate is called right after creation.
+ if (m_state != INSTANCE_VALID && m_state != INSTANCE_INVALID)
+ {
+ return;
+ }
+
+ boolean deactivationRequired = (m_state == INSTANCE_VALID);
+
+ setState(INSTANCE_DESTROYING);
+
+ // Stop the dependency managers to listen to events...
+ Iterator it = m_dependencyManagers.iterator();
+
+ while (it.hasNext())
+ {
+ DependencyManager dm = (DependencyManager)it.next();
+ m_activator.getBundleContext().removeServiceListener(dm);
+ }
+
+ // in case the component is disposed when it was VALID, it is necessary to deactivate it first.
+ if (deactivationRequired)
+ {
+ deactivate();
+ }
+
+ m_dependencyManagers.clear();
+
+ setState(INSTANCE_DESTROYED);
+
+ m_activator = null;
+ }
+
+ //**********************************************************************************************************
+
+ /**
+ * Get the object that is implementing this descriptor
+ *
+ * @return the object that implements the services
+ */
+ public Object getInstance() {
+ return m_implementationObject;
+ }
+
+ /**
+ *
+ */
+ public ComponentMetadata getComponentMetadata() {
+ return m_componentMetadata;
+ }
+
+ /**
+ * sets the state of the manager
+ **/
+ private synchronized void setState(int newState) {
+ GenericActivator.trace("State transition : "+m_states[m_state]+" -> "+m_states[newState], m_componentMetadata);
+
+ m_state = newState;
+
+
+
+ if(m_state == INSTANCE_CREATED || m_state == INSTANCE_VALID || m_state == INSTANCE_INVALID || m_state == INSTANCE_DESTROYED)
+ {
+ //m_activator.fireInstanceChangeEvent(new InstanceChangeEvent(this,m_instanceMetadata,m_state));
+ }
+ }
+
+/**
+ * The DependencyManager task is to listen to service events and to call the
* bind/unbind methods on a given object. It is also responsible for requesting
* the unregistration of a service in case a dependency is broken.
+ */
+ class DependencyManager implements ServiceListener
+ {
+ // Reference to the metadata
+ private ReferenceMetadata m_dependencyMetadata;
+
+ // The bound services <ServiceReference>
+ private Set m_boundServicesRefs = new HashSet();
+
+ // A flag that denotes if the dependency is satisfied at any given moment
+ private boolean m_isValid;
+
+ // A flag that defines if the bind method receives a ServiceReference
+ private boolean m_bindUsesServiceReference = false;
+
+ /**
+ * Constructor that receives several parameters.
+ *
+ * @param dependency An object that contains data about the dependency
+ **/
+ private DependencyManager(ReferenceMetadata dependency) throws ClassNotFoundException, NoSuchMethodException
+ {
+ m_dependencyMetadata = dependency;
+ m_isValid = false;
+
+ //m_bindMethod = getTargetMethod(m_dependencyMetadata.getBind(), m_componentMetadata.getImplementationClassName(),m_dependencyMetadata.getInterface());
+ //m_unbindMethod = getTargetMethod(m_dependencyMetadata.getUnbind(), m_componentMetadata.getImplementationClassName(),m_dependencyMetadata.getInterface());
+ }
+
+ /**
+ * initializes a dependency. This method binds all of the service occurrences to the instance object
+ *
+ * @return true if the operation was successful, false otherwise
+ **/
+ private boolean bind()
+ {
+ /*
+ if(getInstance() == null)
+ {
+ return false;
+ }*/
+
+ // Get service references
+ ServiceReference refs[] = getRequiredServiceRefs();
+
+ // If no references were received, we have to check if the dependency
+ // is optional, if it is not then the dependency is invalid
+ if (refs == null && m_dependencyMetadata.isOptional() == false)
+ {
+ m_isValid = false;
+ return m_isValid;
+ }
+
+ m_isValid = true;
+
+ // refs can be null if the dependency is optional
+ if (refs != null)
+ {
+ int max = 1;
+ boolean retval = true;
+
+ if (m_dependencyMetadata.isMultiple() == true)
+ {
+ max = refs.length;
+ }
+
+ for (int index = 0; index < max; index++)
+ {
+ retval = invokeBindMethod(refs[index]);
+ if(retval == false && (max == 1))
+ {
+ // There was an exception when calling the bind method
+ GenericActivator.error("Dependency Manager: Possible exception in the bind method during initialize()");
+ m_isValid = false;
+ //setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
+ return m_isValid;
+ }
+ }
+ }
+
+ return m_isValid;
+ }
+
+ /**
+ * Revoke all bindings. This method cannot throw an exception since it must try
+ * to complete all that it can
+ *
+ **/
+ private void unbind()
+ {
+ Object []allrefs = m_boundServicesRefs.toArray();
+
+ if (allrefs == null)
+ return;
+
+ for (int i = 0; i < allrefs.length; i++)
+ {
+ invokeUnbindMethod((ServiceReference)allrefs[i]);
+ }
+ }
+
+ /**
+ *
+ * Returns an array containing the service references that are pertinent to the
+ * dependency managed by this object. This method filters out services that
+ * belong to bundles that are being (or are actually) shutdown. This is an issue
+ * since is not clearly specified in the OSGi specification if a getServiceReference
+ * call should return the services that belong to bundles that are stopping.
+ *
+ * @return an array of ServiceReferences valid in the context of this dependency
+ **/
+ private ServiceReference [] getRequiredServiceRefs()
+ {
+ try
+ {
+ ArrayList list=new ArrayList();
+
+ ServiceReference temprefs[] = m_activator.getBundleContext().getServiceReferences(m_dependencyMetadata.getInterface(), m_dependencyMetadata.getTarget());
+
+ if (temprefs == null)
+ {
+ return null;
+ }
+
+ for (int i = 0; i < temprefs.length; i++)
+ {
+ if (temprefs[i].getBundle().getState() == Bundle.ACTIVE
+ || temprefs[i].getBundle().getState() == Bundle.STARTING)
+ {
+ list.add(temprefs[i]);
+ }
+ }
+
+ return (ServiceReference []) list.toArray(new ServiceReference [temprefs.length]);
+
+ }
+ catch (Exception e)
+ {
+ GenericActivator.error("DependencyManager: exception while getting references :"+e);
+ return null;
+ }
+ }
+
+ /**
+ * Gets a bind or unbind method according to the policies described in the specification
+ *
+ * @param methodname The name of the method
+ * @param targetClass the class to which the method belongs to
+ * @param parameterClassName the name of the class of the parameter that is passed to the method
+ * @return the method or null
+ * @throws java.lang.ClassNotFoundException if the class was not found
+ **/
+ private Method getBindingMethod(String methodname, Class targetClass, String parameterClassName)
+ {
+ Method method = null;
+
+ Class parameterClass = null;
+
+ // 112.3.1 The method is searched for using the following priority
+ // 1. The method's parameter type is org.osgi.framework.ServiceReference
+ // 2. The method's parameter type is the type specified by the reference's interface attribute
+ // 3. The method's parameter type is assignable from the type specified by the reference's interface attribute
+ try{
+ // Case 1
+
+ method = targetClass.getMethod(methodname, new Class[]{ServiceReference.class});
+
+ m_bindUsesServiceReference = true;
+ }
+ catch(NoSuchMethodException ex){
+
+ try {
+ // Case2
+
+ m_bindUsesServiceReference = false;
+
+ parameterClass = m_activator.getBundleContext().getBundle().loadClass(parameterClassName);
+
+ method = targetClass.getMethod(methodname, new Class[]{parameterClass});
+ }
+ catch(NoSuchMethodException ex2) {
+
+ // Case 3
+ method = null;
+
+ // Get all potential bind methods
+ Method candidateBindMethods[] = targetClass.getMethods();
+
+ // Iterate over them
+ for(int i = 0; i < candidateBindMethods.length; i++) {
+ Method currentMethod = candidateBindMethods[i];
+
+ // Get the parameters for the current method
+ Class[] parameters = currentMethod.getParameterTypes();
+
+ // Select only the methods that receive a single parameter
+ if(parameters.length == 1) {
+
+ // Get the parameter type
+ Class theParameter = parameters[0];
+
+ // Check if the parameter type is assignable from the type specified by the reference's interface attribute
+ if(theParameter.isAssignableFrom(parameterClass)) {
+ method = currentMethod;
+ }
+ }
+ }
+ }
+ catch(ClassNotFoundException ex2) {
+ GenericActivator.exception("Cannot load class used as parameter "+parameterClassName,m_componentMetadata,ex2);
+ }
+
+ }
+
+ return method;
+ }
+
+ /**
+ * Call the bind method. In case there is an exception while calling the bind method, the service
+ * is not considered to be bound to the instance object
+ *
+ * @param ref A ServiceReference with the service that will be bound to the instance object
+ * @param storeRef A boolean that indicates if the reference must be stored (this is used for the delayed components)
+ * @return true if the call was successful, false otherwise
+ **/
+ private boolean invokeBindMethod(ServiceReference ref) {
+ // The bind method is only invoked if the implementation object is not null. This is valid
+ // for both immediate and delayed components
+ if(m_implementationObject != null) {
+
+ try {
+ // Get the bind method
+ Method bindMethod = getBindingMethod(m_dependencyMetadata.getBind(), getInstance().getClass(), m_dependencyMetadata.getInterface());
+
+ if(bindMethod == null){
+ // 112.3.1 If the method is not found , SCR must log an error
+ // message with the log service, if present, and ignore the method
+ // TODO: log error message
+ GenericActivator.trace("bind() method not found", m_componentMetadata);
+ return false;
+ }
+
+ // Get the parameter
+ Object parameter;
+
+ if(m_bindUsesServiceReference == false) {
+ parameter = m_activator.getBundleContext().getService(ref);
+ }
+ else {
+ parameter = ref;
+ }
+
+ // Invoke the method
+ bindMethod.invoke(getInstance(),new Object[] {parameter});
+
+ // Store the reference
+ m_boundServicesRefs.add(ref);
+
+ return true;
+ }
+ catch(IllegalAccessException ex)
+ {
+ // 112.3.1 If the method is not is not declared protected or public, SCR must log an error
+ // message with the log service, if present, and ignore the method
+ // TODO: log error message
+ return false;
+ }
+ catch(InvocationTargetException ex)
+ {
+ GenericActivator.exception("DependencyManager : exception while invoking "+m_dependencyMetadata.getBind()+"()", m_componentMetadata, ex);
+ return false;
+ }
+ } else if( m_implementationObject == null && m_componentMetadata.isImmediate() == false) {
+ // In the case the implementation object is null and the component is delayed
+ // then we still have to store the object that is passed to the bind methods
+ // so that it can be used once the implementation object is created.
+ m_boundServicesRefs.add(ref);
+ return true;
+ } else {
+ // TODO: assert false : this theoretically never happens...
+ return false;
+ }
+ }
+
+ /**
+ * Call the unbind method
+ *
+ * @param ref A service reference corresponding to the service that will be unbound
+ * @return true if the call was successful, false otherwise
+ **/
+ private boolean invokeUnbindMethod(ServiceReference ref) {
+ // TODO: assert m_boundServices.contains(ref) == true : "DependencyManager : callUnbindMethod UNBINDING UNKNOWN SERVICE !!!!";
+
+ // The unbind method is only invoked if the implementation object is not null. This is valid
+ // for both immediate and delayed components
+ if ( m_implementationObject != null ) {
+ try
+ {
+ // TODO: me quede aqui por que el unbind method no funciona
+ GenericActivator.trace("getting unbind: "+m_dependencyMetadata.getUnbind(), m_componentMetadata);
+ Method unbindMethod = getBindingMethod(m_dependencyMetadata.getUnbind(), getInstance().getClass(), m_dependencyMetadata.getInterface());
+
+ // Recover the object that is bound from the map.
+ //Object parameter = m_boundServices.get(ref);
+ Object parameter = null;
+
+ if(m_bindUsesServiceReference == true) {
+ parameter = ref;
+ } else {
+ parameter = m_activator.getBundleContext().getService(ref);
+ }
+
+ if(unbindMethod == null){
+ // 112.3.1 If the method is not found , SCR must log an error
+ // message with the log service, if present, and ignore the method
+ // TODO: log error message
+ GenericActivator.trace("unbind() method not found", m_componentMetadata);
+ return false;
+ }
+
+ unbindMethod.invoke(getInstance(),new Object [] {parameter});
+
+ m_boundServicesRefs.remove(ref);
+
+ m_activator.getBundleContext().ungetService(ref);
+
+ return true;
+ }
+ catch (IllegalAccessException ex) {
+ // 112.3.1 If the method is not is not declared protected or public, SCR must log an error
+ // message with the log service, if present, and ignore the method
+ // TODO: log error message
+ return false;
+ }
+ catch (InvocationTargetException ex) {
+ GenericActivator.exception("DependencyManager : exception while invoking "+m_dependencyMetadata.getUnbind()+"()", m_componentMetadata, ex);
+ return false;
+ }
+
+ } else if( m_implementationObject == null && m_componentMetadata.isImmediate() == false) {
+ // In the case the implementation object is null and the component is delayed
+ // then we still have to store the object that is passed to the bind methods
+ // so that it can be used once the implementation object is created.
+ m_boundServicesRefs.remove(ref);
+ return true;
+ } else {
+ // TODO: assert false : this theoretically never happens...
+ return false;
+ }
+ }
+
+ /**
+ * Called upon a service event. This method is responsible for calling the
+ * binding and unbinding methods and also to request the eventual unregistering
+ * of a service when a dependency breaks
+ *
+ * @param evt The ServiceEvent
+ **/
+ public void serviceChanged(ServiceEvent evt)
+ {
+ synchronized (ComponentManagerImpl.this)
+ {
+ // If the object is being created or destroyed, we can safely ignore events.
+ if (m_state == INSTANCE_DESTROYING || m_state == INSTANCE_DESTROYED || m_state == INSTANCE_CREATING || m_state == INSTANCE_CREATED)
+ {
+ return;
+ }
+
+ // If we are in the process of invalidating, it is not necessary to pass
+ // unregistration events, since we are unbinding everything anyway.
+ else if (m_state == INSTANCE_INVALIDATING && evt.getType() == ServiceEvent.UNREGISTERING)
+ {
+ return;
+ }
+
+ // We do not have an entry for VALIDATING because it is reentrant.
+
+ // A service is unregistering
+ if (evt.getType() == ServiceEvent.UNREGISTERING)
+ {
+ if (m_boundServicesRefs.contains(evt.getServiceReference()) == true)
+ {
+ // A static dependency is broken the instance manager will be invalidated
+ if (m_dependencyMetadata.isStatic())
+ {
+ m_isValid = false;
+ //setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
+ try
+ {
+ GenericActivator.trace("Dependency Manager: Static dependency is broken", m_componentMetadata);
+ deactivate();
+ GenericActivator.trace("Dependency Manager: RECREATING", m_componentMetadata);
+ activate();
+ }
+ catch(Exception ex)
+ {
+ GenericActivator.exception("Exception while recreating dependency ",m_componentMetadata, ex);
+ }
+ }
+ // dynamic dependency
+ else
+ {
+ // Release references to the service, call unbinder method
+ // and eventually request service unregistration
+
+ invokeUnbindMethod(evt.getServiceReference());
+
+ // The only thing we need to do here is check if we can reinitialize
+ // once the bound services becomes zero. This tries to repair dynamic
+ // 1..1 or rebind 0..1, since replacement services may be available.
+ // In the case of aggregates, this will only invalidate them since they
+ // can't be repaired.
+ if (m_boundServicesRefs.size() == 0)
+ {
+ // try to reinitialize
+ if (!bind())
+ {
+ if (!m_dependencyMetadata.isOptional())
+ {
+ GenericActivator.trace("Dependency Manager: Mandatory dependency not fullfilled and no replacements available... unregistering service...", m_componentMetadata);
+ deactivate();
+ GenericActivator.trace("Dependency Manager: Recreating", m_componentMetadata);
+ activate();
+ }
+ }
+ }
+ }
+ }
+ }
+ // A service is registering.
+ else if (evt.getType() == ServiceEvent.REGISTERED)
+ {
+ if (m_boundServicesRefs.contains(evt.getServiceReference()) == true)
+ {
+ // This is a duplicate
+ GenericActivator.trace("DependencyManager : ignoring REGISTERED ServiceEvent (already bound)", m_componentMetadata);
+ }
+ else
+ {
+ m_isValid = true;
+ //setStateDependency(DependencyChangeEvent.DEPENDENCY_VALID);
+
+ // If the InstanceManager is invalid, a call to validate is made
+ // which will fix everything.
+ if (ComponentManagerImpl.this.m_state != INSTANCE_VALID)
+ {
+ activate();
+ }
+ // Otherwise, this checks for dynamic 0..1, 0..N, and 1..N it never
+ // checks for 1..1 dynamic which is done above by the validate()
+ else if (!m_dependencyMetadata.isStatic())
+ {
+ // For dependency that are aggregates, always bind the service
+ // Otherwise only bind if bind services is zero, which captures the 0..1 case
+ if (m_dependencyMetadata.isMultiple() || m_boundServicesRefs.size() == 0)
+ {
+ invokeBindMethod(evt.getServiceReference());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation for the ComponentContext interface
+ *
+ */
+ class ComponentContextImpl implements ComponentContext {
+
+ public Dictionary getProperties() {
+ //TODO: 112.11.3.5 The Dictionary is read-only and cannot be modified
+ return m_componentMetadata.getProperties();
+ }
+
+ public Object locateService(String arg0) {
+ // TODO implement this method
+ return null;
+ }
+
+ public Object locateService(String arg0, ServiceReference arg1) {
+ // TODO implement this method
+ return null;
+ }
+
+ public Object[] locateServices(String arg0) {
+ // TODO implement this method
+ return null;
+ }
+
+ public BundleContext getBundleContext() {
+ return m_activator.getBundleContext();
+ }
+
+ public Bundle getUsingBundle() {
+ // TODO implement this method
+ return null;
+ }
+
+ public ComponentInstance getComponentInstance() {
+ return ComponentManagerImpl.this;
+ }
+
+ public void enableComponent(String arg0) {
+ // TODO implement this method
+
+ }
+
+ public void disableComponent(String arg0) {
+ // TODO implement this method
+
+ }
+
+ public ServiceReference getServiceReference() {
+ if(m_serviceRegistration != null) {
+ return m_serviceRegistration.getReference();
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * This class is a ServiceFactory that is used when a delayed component is created
+ *
+ */
+ class DelayedComponentServiceFactory implements ServiceFactory {
+
+ public Object getService(Bundle arg0, ServiceRegistration arg1) {
+
+ GenericActivator.trace("DelayedComponentServiceFactory.getService()", m_componentMetadata);
+ // When the getServiceMethod is called, the implementation object must be created
+
+ // 1. Load the component implementation class
+ // 2. Create the component instance and component context
+ // If the component is not immediate, this is not done at this moment
+ try
+ {
+ // 112.4.4 The class is retrieved with the loadClass method of the component's bundle
+ Class c = m_activator.getBundleContext().getBundle().loadClass(m_componentMetadata.getImplementationClassName());
+
+ // 112.4.4 The class must be public and have a public constructor without arguments so component instances
+ // may be created by the SCR with the newInstance method on Class
+ m_componentContext = new ComponentContextImpl();
+ m_implementationObject = c.newInstance();
+ }
+ catch (Exception ex)
+ {
+ // TODO: manage this exception when implementation object cannot be created
+ GenericActivator.exception("Error during instantiation of the implementation object",m_componentMetadata,ex);
+ deactivate();
+ //invalidate();
+ return null;
+ }
+
+
+ // 3. Bind the target services
+ Iterator it = m_dependencyManagers.iterator();
+
+ while ( it.hasNext() )
+ {
+ DependencyManager dm = (DependencyManager)it.next();
+ Iterator bound = dm.m_boundServicesRefs.iterator();
+ while ( bound.hasNext() ) {
+ ServiceReference nextRef = (ServiceReference) bound.next();
+ dm.invokeBindMethod(nextRef);
+ }
+ }
+
+ // 4. Call the activate method, if present
+ // Search for the activate method
+ try {
+ Method activateMethod = m_implementationObject.getClass().getMethod("activate", new Class[]{ComponentContext.class});
+ activateMethod.invoke(m_implementationObject, new Object[]{m_componentContext});
+ }
+ catch(NoSuchMethodException ex) {
+ // We can safely ignore this one
+ }
+ catch(IllegalAccessException ex) {
+ // TODO: Log this exception?
+
+ }
+ catch(InvocationTargetException ex) {
+ // TODO: 112.5.8 If the activate method throws an exception, SCR must log an error message
+ // containing the exception with the Log Service
+ }
+
+ return m_implementationObject;
+ }
+
+ public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) {
+ // TODO Auto-generated method stub
+
+ }
+ }
+
+
+
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentMetadata.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentMetadata.java
new file mode 100644
index 0000000..e8c010b
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ComponentMetadata.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.osgi.service.component.ComponentException;
+
+/**
+ * This class holds the information associated to a component in the descriptor
*
*/
+public class ComponentMetadata {
+ // 112.4.3: A Globally unique component name (required)
+ private String m_name;
+
+ // 112.4.3: Controls whether the component is enabled when the bundle is started. (optional, default is true).
+ private boolean m_enabled = true;
+
+ // 112.4.3: Factory identified. If set to a non empty string, it indicates that the component is a factory component (optional).
+ private String m_factory = null;
+
+ // 112.4.3: Controls whether component configurations must be immediately activated after becoming
+ // satisfied or whether activation should be delayed. (optional, default value is false).
+ private boolean m_immediate = false;
+
+ // 112.4.4 Implementation Element (required)
+ private String m_implementationClassName = null;
+
+ // Associated properties (0..*)
+ private Properties m_properties = new Properties();
+
+ // Provided services (0..1)
+ private ServiceMetadata m_service = null;
+
+ // List of service references, (required services 0..*)
+ private List m_references = new ArrayList();
+
+ // Flag that is set once the component is verified (its properties cannot be changed)
+ private boolean m_validated = false;
+
+
+ /////////////////////////////////////////// SETTERS //////////////////////////////////////
+
+ /**
+ * Setter for the name
+ *
+ * @param name
+ */
+ public void setName(String name) {
+ if(m_validated) {
+ return;
+ }
+ m_name = name;
+ }
+
+ /**
+ * Setter for the enabled property
+ *
+ * @param enabled
+ */
+ public void setEnabled(boolean enabled) {
+ if(m_validated) {
+ return;
+ }
+ m_enabled = enabled;
+ }
+
+ /**
+ *
+ * @param factoryIdentifier
+ */
+ public void setFactoryIdentifier(String factoryIdentifier) {
+ if(m_validated) {
+ return;
+ }
+ m_factory = factoryIdentifier;
+ }
+
+ /**
+ * Setter for the immediate property
+ *
+ * @param immediate
+ */
+ public void setImmediate(boolean immediate) {
+ if(m_validated) {
+ return;
+ }
+ m_immediate = immediate;
+ }
+
+ /**
+ * Sets the name of the implementation class
+ *
+ * @param implementationClassName a class name
+ */
+ public void setImplementationClassName(String implementationClassName) {
+ if(m_validated) {
+ return;
+ }
+ m_implementationClassName = implementationClassName;
+ }
+
+ /**
+ * Used to add a property to the instance
+ *
+ * @param newProperty a property metadata object
+ */
+ public void addProperty(PropertyMetadata newProperty) {
+ if(m_validated) {
+ return;
+ }
+ if(newProperty == null) {
+ throw new IllegalArgumentException ("Cannot add a null property");
+ }
+ String key = newProperty.getName();
+ Object value = newProperty.getValue();
+ m_properties.put(key,value);
+ }
+
+ /**
+ * Used to set a ServiceMetadata object.
+ *
+ * @param service a ServiceMetadata
+ */
+ public void setService(ServiceMetadata service) {
+ if(m_validated) {
+ return;
+ }
+ m_service = service;
+ }
+
+ /**
+ * Used to add a reference metadata to the component
+ *
+ * @param newReference a new ReferenceMetadata to be added
+ */
+ public void addDependency(ReferenceMetadata newReference) {
+ if(newReference == null) {
+ throw new IllegalArgumentException ("Cannot add a null ReferenceMetadata");
+ }
+ m_references.add(newReference);
+ }
+
+
+ /////////////////////////////////////////// GETTERS //////////////////////////////////////
+
+ /**
+ * Returns the name of the component
+ *
+ * @return A string containing the name of the component
+ */
+ public String getName() {
+ return m_name;
+ }
+
+ /**
+ * Returns the value of the enabled flag
+ *
+ * @return a boolean containing the value of the enabled flag
+ */
+ public boolean isEnabled() {
+ return m_enabled;
+ }
+
+ /**
+ * Returns the factory identifier
+ *
+ * @return A string containing a factory identifier or null
+ */
+ public String getFactoryIdentifier() {
+ return m_factory;
+ }
+
+ /**
+ * Returns the flag that defines the activation policy for the component
+ *
+ * @return a boolean that defines the activation policy
+ */
+ public boolean isImmediate() {
+ return m_immediate;
+ }
+
+ /**
+ * Returns the name of the implementation class
+ *
+ * @return the name of the implementation class
+ */
+ public String getImplementationClassName() {
+ return m_implementationClassName;
+ }
+
+ /**
+ * Returns the associated ServiceMetadata
+ *
+ * @return a ServiceMetadata object or null if the Component does not provide any service
+ */
+ public ServiceMetadata getServiceMetadata() {
+ return m_service;
+ }
+
+ /**
+ * Returns the property descriptors
+ *
+ * @return the property descriptors as a Collection
+ */
+ public Properties getProperties() {
+ return m_properties;
+ }
+
+ /**
+ * Returns the dependency descriptors
+ *
+ * @return a Collection of dependency descriptors
+ */
+ public List getDependencies() {
+ return m_references;
+ }
+
+ /**
+ * Test to see if this service is a factory
+ *
+ * @return true if it is a factory, false otherwise
+ */
+ public boolean isFactory() {
+ return m_factory != null;
+ }
+
+ /**
+ * Method used to verify if the semantics of this metadata are correct
+ */
+ void validate() {
+
+ // First check if the properties are valid
+ Iterator propertyIterator = m_properties.keySet().iterator();
+ while ( propertyIterator.hasNext() ) {
+ ((PropertyMetadata)propertyIterator.next()).validate();
+ }
+
+ // Check that the provided services are valid too
+ if(m_service != null) {
+ m_service.validate();
+ }
+
+ // Check that the references are ok
+ Iterator referenceIterator = m_references.iterator();
+ while ( referenceIterator.hasNext() ) {
+ ((ReferenceMetadata)referenceIterator.next()).validate();
+ }
+
+ // 112.10 The name of the component is required
+ if( m_name == null ) {
+ throw new ComponentException("The component name has not been set");
+ }
+
+ // 112.10 There must be one implementation element and the class atribute is required
+ if ( m_implementationClassName == null ) {
+ throw new ComponentException("The implementation class name has not been set for this component");
+ }
+
+ // 112.2.3 A delayed component specifies a service, is not specified to be a factory component
+ // and does not have the immediate attribute of the component element set to true.
+ if(m_immediate == false && m_service == null) {
+ throw new ComponentException("Component '"+m_name+"' is specified as being delayed but does not provide any service.");
+ }
+
+ if ( m_factory != null && m_immediate == false) {
+ throw new ComponentException("A factory cannot be a delayed component");
+ }
+
+ // TODO: 112.4.6 The serviceFactory attribute (of a provided service) must not be true if
+ // the component is a factory component or an immediate component
+
+
+ m_validated = true;
+ // TODO: put a similar flag on the references and the services
+ }
+
+}
+
+
+
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/GenericActivator.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/GenericActivator.java
new file mode 100644
index 0000000..2c22574
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/GenericActivator.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import java.io.InputStream;
+
+import org.apache.felix.declarativeservices.scr.KXml2SAXParser;
+import org.apache.felix.declarativeservices.scr.ParseException;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentException;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * The GenericActivator is the main startup class. It will read information from the metadata.xml file
+ * descriptors and create the corresponding managers.
+ *
+ */
+public class GenericActivator implements BundleActivator
+{
+ // The bundle context
+ private BundleContext m_context = null;
+
+ // This is a list of component instance managers that belong to a particular bundle
+ private List m_managers = new ArrayList();
+
+ // Flag that sets tracing messages
+ private static boolean m_trace = true;
+
+ // Flag that sets error messages
+ private static boolean m_error = true;
+
+ // A string containing the version number
+ private static String m_version = "1.0.0 (12012006)";
+
+ // Registry of component names
+ static Set m_componentNames = new HashSet();
+
+ // Static initializations based on system properties
+ static {
+ // Get system properties to see if traces or errors need to be displayed
+ String result = System.getProperty("ds.showtrace");
+ if(result != null && result.equals("true"))
+ {
+ m_trace = true;
+ }
+ result = System.getProperty("ds.showerrors");
+ if(result != null && result.equals("false"))
+ {
+ m_error = false;
+ }
+ result = System.getProperty("ds.showversion");
+ if(result != null && result.equals("true"))
+ {
+ System.out.println("[ Version = "+m_version+" ]\n");
+ }
+ }
+
+ /**
+ * Called upon starting of the bundle. This method invokes initialize() which
+ * parses the metadata and creates the instance managers
+ *
+ * @param context The bundle context passed by the framework
+ * @exception Exception any exception thrown from initialize
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ // Stores the context
+ m_context = context;
+
+ try
+ {
+ initialize();
+ }
+ catch (Exception e)
+ {
+ GenericActivator.error("GenericActivator : in bundle ["
+ + context.getBundle().getBundleId() + "] : " + e);
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ /**
+ * Gets the MetaData location, parses the meta data and requests the processing
+ * of binder instances
+ *
+ * @throws FileNotFoundException if the metadata file is not found
+ * @throws ParseException if the metadata file is not found
+ */
+ private void initialize() throws ParseException {
+ // Get the Metadata-Location value from the manifest
+ String descriptorLocations = (String) m_context.getBundle().getHeaders().get("Service-Component");
+
+ if (descriptorLocations == null)
+ {
+ throw new ComponentException("Service-Component entry not found in the manifest");
+ }
+
+ // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
+ StringTokenizer st = new StringTokenizer(descriptorLocations, ", ");
+
+ while (st.hasMoreTokens()) {
+ String descriptorLocation = st.nextToken();
+
+ try {
+ InputStream stream = m_context.getBundle().getResource(descriptorLocation).openStream();
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(stream));
+ XmlHandler handler = new XmlHandler();
+ KXml2SAXParser parser;
+
+ parser = new KXml2SAXParser(in);
+
+ parser.parseXML(handler);
+
+ // 112.4.2 Component descriptors may contain a single, root component element
+ // or one or more component elements embedded in a larger document
+ Iterator i = handler.getComponentMetadataList().iterator();
+ while (i.hasNext()) {
+ try {
+ ComponentMetadata metadata = (ComponentMetadata) i.next();
+
+ validate(metadata);
+
+ // Request creation of the component manager
+ ComponentManager manager = ManagerFactory.createManager(this,metadata);
+
+ if(metadata.isFactory()) {
+ // 112.2.4 SCR must register a Component Factory service on behalf ot the component
+ // as soon as the component factory is satisfied
+ }
+ else if(metadata.isEnabled()) {
+ // enable the component
+ manager.enable();
+ m_managers.add(manager);
+ }
+ } catch (Exception e) {
+ // There is a problem with this particular component, we'll log the error
+ // and proceed to the next one
+ // TODO: log the error
+ e.printStackTrace();
+ }
+ }
+
+ stream.close();
+
+ }
+ catch ( IOException ex ) {
+ // 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
+ // fragments, SCR must log an error message with the Log Service, if present, and continue.
+
+ error("Component descriptor entry '" + descriptorLocation + "' not found");
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Stop method that destroys all the instance managers
+ *
+ * @param context The Bundle Context passed by the framework
+ * @exception Exception any exception thrown during destruction of the instance managers
+ */
+ public void stop(BundleContext context) throws java.lang.Exception
+ {
+ //GenericActivator.trace("GenericActivator : Bundle ["+context.getBundle().getBundleId()+"] will destroy "+m_managers.size()+" instances");
+
+ while (m_managers.size() !=0 )
+ {
+ ComponentManager manager = (ComponentManager)m_managers.get(0);
+ try {
+ manager.dispose();
+ m_managers.remove(manager);
+ }
+ catch(Exception e) {
+ GenericActivator.error("GenericActivator : Exception during invalidate : "+e);
+ e.printStackTrace();
+ }
+ finally {
+ m_componentNames.remove(manager.getComponentMetadata().getName());
+ }
+
+ }
+
+ m_context = null;
+
+ //GenericActivator.trace("GenericActivator : Bundle ["+context.getBundle().getBundleId()+"] STOPPED");
+ }
+
+ /**
+ * Returns the list of instance references currently associated to this activator
+ *
+ * @return the list of instance references
+ */
+ protected List getInstanceReferences()
+ {
+ return m_managers;
+ }
+
+ /**
+ * Returns the BundleContext
+ *
+ * @return the BundleContext
+ */
+ protected BundleContext getBundleContext()
+ {
+ return m_context;
+ }
+
+ /**
+ * Method to display traces
+ *
+ * @param message a string to be displayed
+ * @param metadata ComponentMetadata associated to the message (can be null)
+ **/
+ static void trace(String message, ComponentMetadata metadata)
+ {
+ if(m_trace)
+ {
+ System.out.print("--- ");
+ if(metadata != null) {
+ System.out.print("[");
+ System.out.print(metadata.getName());
+ System.out.print("] ");
+ System.out.println(message);
+ }
+ }
+ }
+
+ /**
+ * Method to display errors
+ *
+ * @param s a string to be displayed
+ **/
+ static void error(String s) {
+ if(m_error) {
+ System.err.println("### "+s);
+ }
+ }
+
+ /**
+ * Method to display exceptions
+ *
+ * @param ex an exception
+ **/
+ static void exception(String message, ComponentMetadata metadata, Throwable ex) {
+ if(m_error) {
+ System.out.println("--- Exception in component "+metadata.getName()+" : "+message+" ---");
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * This method is used to validate that the component. This method verifies multiple things:
+ *
+ * 1.- That the name attribute is set and is globally unique
+ * 2.- That an implementation class name has been set
+ * 3.- That a delayed component provides a service and is not specified to be a factory
+ * - That the serviceFactory attribute for the provided service is not true if the component is a factory or immediate
+ *
+ * If the component is valid, its name is registered
+ *
+ * @throws A ComponentException if something is not right
+ */
+ void validate(ComponentMetadata component) throws ComponentException {
+
+ if(m_componentNames.contains(component.getName()))
+ {
+ throw new ComponentException("The component name '"+component.getName()+"' has already been registered.");
+ }
+
+ m_componentNames.add(component.getName());
+
+ component.validate();
+
+ trace("Validated and registered component",component);
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ManagerFactory.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ManagerFactory.java
new file mode 100644
index 0000000..6ca71b6
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ManagerFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+/**
+ * This factory allows other types of ComponentManagers to be provided.
+ *
+ *
+ */
+public class ManagerFactory {
+
+ static ComponentManager createManager(GenericActivator activator, ComponentMetadata metadata) {
+ GenericActivator.trace("ManagerFactory.createManager", metadata);
+ return new ComponentManagerImpl(activator,metadata);
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/PropertyMetadata.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/PropertyMetadata.java
new file mode 100644
index 0000000..28d028a
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/PropertyMetadata.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import org.osgi.service.component.ComponentException;
+
+/**
+ * A property descriptor that contains the information for properties
+ * defined in the descriptor
+ *
+ */
+public class PropertyMetadata {
+
+ // Name of the property (required)
+ private String m_name;
+
+ // Type of the property (optional)
+ private String m_type = "String";
+
+ // Value of the type (optional)
+ private Object m_value;
+
+ // Flag that indicates if this PropertyMetadata has been validated and thus has become immutable
+ private boolean m_validated = false;
+
+ /**
+ * Set the name
+ *
+ * @param name
+ */
+ public void setName(String name) {
+ if (m_validated == true) {
+ return;
+ }
+
+ m_name = name;
+ }
+
+
+ /**
+ * Set the type
+ *
+ * @param type
+ */
+ public void setType(String type) {
+ if (m_validated == true) {
+ return;
+ }
+ m_type = type;
+ }
+
+ /**
+ * Set the value
+ *
+ * @param value
+ */
+ public void setValue(String value) {
+ if (m_validated == true) {
+ return;
+ }
+
+ // 112.4.5 Parsing of the value is done by the valueOf(String) method (P. 291)
+ // Should the type accept lowercase too?
+ if(m_type.equals("String")) {
+ m_value = String.valueOf(value);
+ }
+ else if(m_type.equals("Long")) {
+ m_value = Long.valueOf(value);
+ }
+ else if(m_type.equals("Double")) {
+ m_value = Double.valueOf(value);
+ }
+ else if(m_type.equals("Float")) {
+ m_value = Float.valueOf(value);
+ }
+ else if(m_type.equals("Integer")) {
+ m_value = Integer.valueOf(value);
+ }
+ else if(m_type.equals("Byte")) {
+ m_value = Byte.valueOf(value);
+ }
+ else if(m_type.equals("Char")) {
+ //TODO: verify if this is adequate for Characters
+ m_value = Byte.valueOf(value);
+ }
+ else if(m_type.equals("Boolean")) {
+ m_value = Boolean.valueOf(value);
+ }
+ else if(m_type.equals("Short")) {
+ m_value = Short.valueOf(value);
+ }
+ else {
+ throw new IllegalArgumentException("Undefined property type '"+m_type+"'");
+ }
+ }
+
+ /**
+ * Get the name of the property
+ *
+ * @return the name of the property
+ */
+ public String getName() {
+ return m_name;
+ }
+
+ /**
+ * Get the type of the property
+ *
+ * @return the type of the property
+ */
+ public String getType() {
+ return m_type;
+ }
+
+ /**
+ * Get the value of the property
+ *
+ * @return the value of the property as an Object
+ */
+ public Object getValue() {
+ return m_value;
+ }
+
+ /**
+ * Method used to verify if the semantics of this metadata are correct
+ */
+ public void validate(){
+ if(m_name == null)
+ {
+ throw new ComponentException("Property name attribute is mandatory");
+ }
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java
new file mode 100644
index 0000000..8b61eca
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import org.osgi.service.component.ComponentException;
+
+/**
+ * Information associated to a dependency
+ *
+ */
+public class ReferenceMetadata {
+ // Name for the reference (required)
+ private String m_name = null;
+
+ // Interface name (required)
+ private String m_interface = null;
+
+ // Cardinality (optional, default="1..1")
+ private String m_cardinality = "1..1";
+
+ // Target (optional)
+ private String m_target;
+
+ // Name of the bind method (optional)
+ private String m_bind = null;
+
+ // Name of the unbind method (optional)
+ private String m_unbind = null;
+
+ // Policy attribute (optional, default = static)
+ private String m_policy = "static";
+
+ // Flag that is set once the component is verified (its properties cannot be changed)
+ private boolean m_validated = false;
+
+ // Flags that store the values passed as strings
+ private boolean m_isStatic = true;
+ private boolean m_isOptional = false;
+ private boolean m_isMultiple = false;
+
+ /////////////////////////////////////////////// setters ///////////////////////////////////
+
+ /**
+ * Setter for the name attribute
+ *
+ * @param name
+ */
+ public void setName(String name) {
+ if(m_validated) {
+ return;
+ }
+
+ m_name = name;
+ }
+
+ /**
+ * Setter for the interfaceName attribute
+ *
+ * @param interfaceName
+ */
+ public void setInterface(String interfaceName) {
+ if(m_validated) {
+ return;
+ }
+
+ m_interface = interfaceName;
+
+ }
+
+ /**
+ * Setter for the cardinality attribute
+ *
+ * @param cardinality
+ */
+ public void setCardinality(String cardinality) {
+ if(m_validated) {
+ return;
+ }
+
+ if(!m_cardinality.equals("0..1") && !m_cardinality.equals("0..n") && !m_cardinality.equals("1..1") && !m_cardinality.equals("1..n")) {
+ throw new IllegalArgumentException ("Cardinality should take one of the following values: 0..1, 0..n, 1..1, 1..n");
+ }
+ if(m_cardinality.equals("0..1") || m_cardinality.equals("0..n")) {
+ m_isOptional = true;
+ }
+ if(m_cardinality.equals("0..n") || m_cardinality.equals("1..n")) {
+ m_isMultiple = true;
+ }
+
+ m_cardinality = cardinality;
+ }
+
+ /**
+ * Setter for the policy attribute
+ *
+ * @param policy
+ */
+ public void setPolicy(String policy) {
+ if(m_validated) {
+ return;
+ }
+
+ if(!m_policy.equals("static") && !m_policy.equals("dynamic")) {
+ throw new IllegalArgumentException ("Policy must be either 'static' or 'dynamic'");
+ }
+ if(policy.equals("static") == false) {
+ m_isStatic = false;
+ }
+
+ m_policy = policy;
+ }
+
+ /**
+ * Setter for the target attribute (filter)
+ *
+ * @param target
+ */
+ public void setTarget(String target) {
+ if(m_validated) {
+ return;
+ }
+
+ //TODO: check if we really need to extend the filter to limit seaches to a particular interface
+ String classnamefilter = "(objectClass="+m_interface+")";
+ if(target != null) {
+ m_target = "(&"+classnamefilter+target+")";
+ }
+ else {
+ m_target = classnamefilter;
+ }
+ }
+
+ /**
+ * Setter for the bind method attribute
+ *
+ * @param bind
+ */
+ public void setBind(String bind) {
+ if(m_validated) {
+ return;
+ }
+
+ m_bind = bind;
+ }
+
+ /**
+ * Setter for the unbind method attribute
+ *
+ * @param bind
+ */
+ public void setUnbind(String unbind) {
+ if(m_validated) {
+ return;
+ }
+
+ m_unbind = unbind;
+ }
+
+
+ /////////////////////////////////////////////// getters ///////////////////////////////////
+
+ /**
+ * Returns the name of the reference
+ *
+ * @return A string containing the reference's name
+ **/
+ public String getName() {
+ return m_name;
+ }
+
+ /**
+ * Returns the fully qualified name of the class that is used by the component to access the service
+ *
+ * @return A string containing a fully qualified name
+ **/
+ public String getInterface() {
+ return m_interface;
+ }
+
+ /**
+ * Get the cardinality as a string
+ *
+ * @return A string with the cardinality
+ **/
+ public String getCardinality() {
+ return m_cardinality;
+ }
+
+ /**
+ * Get the policy as a string
+ *
+ * @return A string with the policy
+ **/
+ public String getPolicy() {
+ return m_policy;
+ }
+
+ /**
+ * Returns the filter expression that further constrains the set of target services
+ *
+ * @return A string with a filter
+ **/
+ public String getTarget() {
+ return m_target;
+ }
+
+ /**
+ * Get the name of a method in the component implementation class that is used to notify that
+ * a service is bound to the component configuration
+ *
+ * @return a String with the name of the bind method
+ **/
+ public String getBind() {
+ return m_bind;
+ }
+
+ /**
+ * Get the name of a method in the component implementation class that is used to notify that
+ * a service is unbound from the component configuration
+ *
+ * @return a String with the name of the unbind method
+ **/
+ public String getUnbind() {
+ return m_unbind;
+ }
+
+ // Getters for boolean values that determine both policy and cardinality
+
+ /**
+ * Test if dependency's binding policy is static
+ *
+ * @return true if static
+ **/
+ public boolean isStatic() {
+ return m_isStatic;
+ }
+
+ /**
+ * Test if dependency is optional (0..1 or 0..n)
+ *
+ * @return true if the dependency is optional
+ **/
+ public boolean isOptional() {
+ return m_isOptional;
+ }
+
+ /**
+ * Test if dependency is multiple (0..n or 1..n)
+ *
+ * @return true if the dependency is multiple
+ **/
+ public boolean isMultiple() {
+ return m_isMultiple;
+ }
+
+ /**
+ * Method used to verify if the semantics of this metadata are correct
+ *
+ */
+ void validate() {
+ if (m_name == null) {
+ throw new ComponentException("the name for the reference must be set");
+ }
+
+ if (m_interface == null) {
+ throw new ComponentException("the interface for the reference must be set");
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ServiceMetadata.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ServiceMetadata.java
new file mode 100644
index 0000000..9b20d4e
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/ServiceMetadata.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.osgi.service.component.ComponentException;
+
+/**
+ * This class contains the metadata associated to a service that is provided
+ * by a component
+ *
+ */
+public class ServiceMetadata {
+
+ // 112.4.6 Flag that indicates if the service is a ServiceFactory
+ private boolean m_serviceFactory = false;
+
+ // List of provided interfaces
+ private List m_provides = new ArrayList();
+
+ // Flag that indicates if this metadata has been validated and has become immutable
+ private boolean m_validated = false;
+
+ /**
+ * Setter for the servicefactory attribute of the service element
+ *
+ * @param serviceFactory
+ */
+ public void setServiceFactory(boolean serviceFactory) {
+ if(m_validated) {
+ return;
+ }
+
+ m_serviceFactory = serviceFactory;
+ }
+
+ /**
+ * Add a provided interface to this service
+ *
+ * @param provide a String containing the name of the provided interface
+ */
+ public void addProvide(String provide) {
+ if(m_validated) {
+ return;
+ }
+
+ m_provides.add(provide);
+ }
+
+ /**
+ * Return the flag that defines if it is a service factory or not
+ *
+ * @return a boolean flag
+ */
+ public boolean isServiceFactory() {
+ return m_serviceFactory;
+ }
+
+ /**
+ * Returns the implemented interfaces
+ *
+ * @return the implemented interfaces as a string array
+ */
+ public String [] getProvides() {
+ String provides[] = new String[m_provides.size()];
+ Iterator it = m_provides.iterator();
+ int count = 0;
+ while (it.hasNext())
+ {
+ provides[count++] = it.next().toString();
+ }
+ return provides;
+ }
+
+ /**
+ * Verify if the semantics of this metadata are correct
+ *
+ */
+ void validate() {
+ if(m_provides.size() == 0) {
+ throw new ComponentException("At least one provided interface must be given");
+ }
+ }
+}
diff --git a/org.apache.felix.scr/src/main/java/org/apache/felix/scr/XmlHandler.java b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/XmlHandler.java
new file mode 100644
index 0000000..0f0b81f
--- /dev/null
+++ b/org.apache.felix.scr/src/main/java/org/apache/felix/scr/XmlHandler.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.scr;
+
+import java.util.Properties;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.declarativeservices.scr.KXml2SAXHandler;
+import org.apache.felix.declarativeservices.scr.ParseException;
+
+/**
+ *
+ *
+ */
+public class XmlHandler implements KXml2SAXHandler {
+
+ // A reference to the current component
+ private ComponentMetadata m_currentComponent = null;
+
+ // The current service
+ private ServiceMetadata m_currentService = null;
+
+ // A list of component descriptors contained in the file
+ private List m_components = new ArrayList();
+
+ /**
+ * Method called when a tag opens
+ *
+ * @param uri
+ * @param localName
+ * @param qName
+ * @param attrib
+ * @exception ParseException
+ **/
+ public void startElement(String uri,String localName,String qName,Properties attrib) throws ParseException
+ {
+ try {
+
+ // 112.4.3 Component Element
+ if (qName.equals("component")) {
+
+ // Create a new ComponentMetadata
+ m_currentComponent = new ComponentMetadata();
+
+ // name attribute is mandatory
+ m_currentComponent.setName(attrib.getProperty("name"));
+
+ // enabled attribute is optional
+ if(attrib.getProperty("enabled") != null) {
+ m_currentComponent.setEnabled(attrib.getProperty("enabled").equals("true"));
+ }
+
+ // immediate attribute is optional
+ if(attrib.getProperty("immediate") != null) {
+ m_currentComponent.setImmediate(attrib.getProperty("immediate").equals("true"));
+ }
+
+ // factory attribute is optional
+ if(attrib.getProperty("factory") != null) {
+ m_currentComponent.setFactoryIdentifier(attrib.getProperty("factory"));
+ }
+
+ // Add this component to the list
+ m_components.add(m_currentComponent);
+ }
+
+ // 112.4.4 Implementation
+ else if (qName.equals("implementation"))
+ {
+ // Set the implementation class name (mandatory)
+ m_currentComponent.setImplementationClassName(attrib.getProperty("class"));
+ }
+ // 112.4.5 Properties and Property Elements
+ else if (qName.equals("property")) {
+ // 112.4.5: If the value attribute is specified, the body of the element is ignored.
+ if( attrib.getProperty("value") != null) {
+ PropertyMetadata prop = new PropertyMetadata();
+
+ // name attribute is mandatory
+ prop.setName(attrib.getProperty("name"));
+
+ // type attribute is optional
+ if(attrib.getProperty("type") != null) {
+ prop.setType(attrib.getProperty("type"));
+ }
+
+ // value attribute is optional
+ if(attrib.getProperty("value") != null) {
+ prop.setValue(attrib.getProperty("value"));
+ }
+ m_currentComponent.addProperty(prop);
+ }
+ else {
+ // TODO: treat the case where property value is not specified (p. 291)
+ }
+ // TODO: treat the case where a properties file name is provided (p. 292)
+ }
+ else if(qName.equals("properties")) {
+ // TODO: implement the properties tag
+ }
+ // 112.4.6 Service Element
+ else if (qName.equals("service")) {
+
+ m_currentService = new ServiceMetadata();
+
+ // servicefactory attribute is optional
+ if(attrib.getProperty("servicefactory") != null) {
+ m_currentService.setServiceFactory(attrib.getProperty("servicefactory").equals("true"));
+ }
+
+ m_currentComponent.setService(m_currentService);
+ }
+ else if (qName.equals("provide")) {
+ m_currentService.addProvide(attrib.getProperty("interface"));
+ }
+
+ // 112.4.7 Reference element
+ else if (qName.equals("reference")) {
+ ReferenceMetadata ref=new ReferenceMetadata ();
+ ref.setName(attrib.getProperty("name"));
+ ref.setInterface(attrib.getProperty("interface"));
+
+ // Cardinality
+ if(attrib.getProperty("cardinality")!= null) {
+ ref.setCardinality(attrib.getProperty("cardinality"));
+ }
+
+ if(attrib.getProperty("policy") != null) {
+ ref.setPolicy(attrib.getProperty("policy"));
+ }
+
+ //if
+ ref.setTarget(attrib.getProperty("target"));
+ ref.setBind(attrib.getProperty("bind"));
+ ref.setUnbind(attrib.getProperty("unbind"));
+
+ m_currentComponent.addDependency(ref);
+ }
+ }
+ catch(Exception ex) {
+ ex.printStackTrace();
+ throw new ParseException("Exception during parsing",ex);
+ }
+ }
+
+ /**
+ * Method called when a tag closes
+ *
+ * @param uri
+ * @param localName
+ * @param qName
+ * @exception ParseException
+ */
+ public void endElement(java.lang.String uri,java.lang.String localName,java.lang.String qName) throws ParseException
+ {
+ if (qName.equals("component"))
+ {
+ // When the closing tag for a component is found, the component is validated to check if
+ // the implementation class has been set
+ m_currentComponent.validate();
+ }
+ }
+
+ /**
+ * Called to retrieve the service descriptors
+ *
+ * @return A list of service descriptors
+ */
+ List getComponentMetadataList()
+ {
+ return m_components;
+ }
+
+ public void characters(char[] ch, int offset, int length) throws Exception {
+ // Not used
+
+ }
+
+ public void processingInstruction(String target, String data) throws Exception {
+ // Not used
+
+ }
+
+ public void setLineNumber(int lineNumber) {
+ // Not used
+
+ }
+
+ public void setColumnNumber(int columnNumber) {
+ // Not used
+
+ }
+}
+
diff --git a/org.apache.felix.scr/todolist.txt b/org.apache.felix.scr/todolist.txt
new file mode 100644
index 0000000..90935b5
--- /dev/null
+++ b/org.apache.felix.scr/todolist.txt
@@ -0,0 +1,66 @@
+Component Description
+
+112.4.1 Service Component Header
+[ ] Process multi-entry (comma separated) header
+[ ] Support fragments
+[ ] Log error with log service in case header cannot be located
+
+112.4.2 XML Document
+[ ] Use document namespace
+
+112.4.3 Component Element
+[ ] Verify that the name of a component is globally unique
+[ OK ] Enabled set to true when not present
+[ OK ] Support null factory attribute
+[ OK ] Immediate set to false by default
+
+112.4.4 Implementation Element
+[ OK ] Check that the implementation element is present
+
+112.4.5 Properties and Property Elements
+[ OK ] Support property
+[ ] Support Configuration Admin's service
+[ ] Element body with multiple lines
+[ ] Support properties (file)
+
+112.4.6 Service Element
+[ OK ] Support for optional declaration
+[ OK ] Support for provides
+
+112.4.7 Reference Element
+[ OK ] Name of the reference
+[ OK ] Interface
+[ OK ] Cardinality
+[ OK ] Policy
+[ OK ] Target
+[ OK ] bind
+[ OK ] unbind
+
+Component Lifecycle
+
+[ OK ] 112.5.1 Enabled
+[ OK ] 112.5.2 Satisfied
+[ OK ] 112.5.3 Immediate
+[ OK ] 112.5.4 Delayed
+[ ] 112.5.5 Factory
+[ OK ] 112.5.6 Activation
+[ OK ] 112.5.7 Binding Services
+[ OK ] 112.5.8 Activate Method
+[ ] 112.5.9 Component Context
+[ ] 112.5.10 Bound Service Replacement
+[ ] 112.5.11 Deactivation
+[ OK ] 112.5.12 Deactivate Method
+[ OK ] 112.5.13 Unbinding
+
+[ ] 112.6 Component Properties
+[ ] 112.7 Deployment
+
+Service Component Runtime
+
+[ ] 112.8.1 Relationship to OSGi Framework
+[ ] 112.8.2 Starting and Stopping SCR
+
+Security
+
+[ ] 112.9.1 Service Permissions
+[ ] 112.9.2 Using hasPermission
diff --git a/pom.xml b/pom.xml
index 95b71d9..85decaa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,6 +35,7 @@
<module>org.apache.felix.upnp.tester</module>
<module>org.apache.felix.upnp.sample.tv</module>
<module>org.apache.felix.http.jetty</module>
+ <module>org.apache.felix.scr</module>
</modules>
<repositories>