Add scoping features for service requirement resolving.
This feature can be used both for primitive component requirement and for composite requirement. A requirement can now be solved in the global (i.e. OSGi) service registry instead of inside the composite.
(Felix-311)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@562430 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
index ea39c20..7b655df 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/ComponentFactory.java
@@ -91,7 +91,7 @@
private FactoryClassloader m_classLoader = null;
/**
- * Component Type provided by this factory. //TODO Should be keep this
+ * Component Type provided by this factory. //TODO Should we keep this ?
* reference ?
*/
private Element m_componentMetadata;
@@ -347,13 +347,13 @@
// create a ghost component
if (!m_isComposite) {
- final InstanceManager ghost = new InstanceManager(this, m_context);
+ final InstanceManager ghost = new InstanceManager(this, new IPojoContext(m_context));
final Properties p = new Properties();
p.put("name", "ghost");
ghost.configure(m_componentMetadata, p);
m_componentDesc = ghost.getComponentDescription();
} else {
- final CompositeManager ghost = new CompositeManager(this, m_context);
+ final CompositeManager ghost = new CompositeManager(this, new IPojoContext(m_context));
final Properties p = new Properties();
p.put("name", "ghost");
ghost.configure(m_componentMetadata, p);
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/CompositeManager.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/CompositeManager.java
index 6672f6a..7802473 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/CompositeManager.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/CompositeManager.java
@@ -521,4 +521,22 @@
public ServiceContext getServiceContext() {
return m_internalContext;
}
+
+ /**
+ * Get the global bundle context.
+ * @return the global bundle context.
+ */
+ public BundleContext getGlobalContext() {
+ IPojoContext c = (IPojoContext) m_context;
+ return c.getGlobalContext();
+ }
+
+ /**
+ * Get the parent service context.
+ * @return the parent service context.
+ */
+ public ServiceContext getParentServiceContext() {
+ IPojoContext c = (IPojoContext) m_context;
+ return c.getServiceContext();
+ }
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java
index 1431eca..de1c44a 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/IPojoContext.java
@@ -309,5 +309,13 @@
public BundleContext getGlobalContext() {
return m_bundleContext;
}
+
+ /**
+ * Get the service context, i.e. the composite context.
+ * @return the service context.
+ */
+ public ServiceContext getServiceContext() {
+ return m_serviceContext;
+ }
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
index 16f3600..b1e3c80 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/InstanceManager.java
@@ -851,6 +851,14 @@
public BundleContext getContext() {
return m_context;
}
+
+ public BundleContext getGlobalContext() {
+ return ((IPojoContext) m_context).getGlobalContext();
+ }
+
+ public ServiceContext getLocalServiceContext() {
+ return ((IPojoContext) m_context).getServiceContext();
+ }
/**
* Check the state of all handlers.
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java
new file mode 100644
index 0000000..2cca0d6
--- /dev/null
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/PolicyServiceContext.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo;
+
+import java.util.Dictionary;
+
+import org.apache.felix.ipojo.composite.ServiceReferenceImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The policy service context is a service context aiming to solve service requirement.
+ * It's parameterized by a resolving policy. Three policies are managed :
+ * - Local : services are only solve un the local service registry
+ * - Global : services are resolved only in the global (i.e. OSGi) service registry
+ * - Local and Global : services are resolved inside the local registry and the global registry
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PolicyServiceContext implements ServiceContext {
+
+ /**
+ * Resolving policy, look only in the composite.
+ */
+ public static final int LOCAL = 0;
+
+ /**
+ * Resolving policy, look inside the composite and in the global scope.
+ * This policy is the default one for implementation dependency.
+ */
+ public static final int LOCAL_AND_GLOBAL = 1;
+
+ /**
+ * Resolving policy, look inside the global only.
+ */
+ public static final int GLOBAL = 2;
+
+ /**
+ * Global service registry.
+ */
+ private BundleContext m_global;
+
+ /**
+ * Local (Composite) Service Registry.
+ */
+ private ServiceContext m_local;
+
+ /**
+ * Resolving policy.
+ */
+ private int m_policy = LOCAL_AND_GLOBAL;
+
+
+ /**
+ * Create a new PolicyServiceContext.
+ * @param global : global bundle context
+ * @param local : parent (local) service context
+ * @param policy : resolution policy
+ */
+ public PolicyServiceContext(BundleContext global, ServiceContext local, int policy) {
+ m_global = global;
+ m_local = local;
+ m_policy = policy;
+ }
+
+ /**
+ * Add a service listener according to the policy.
+ * @param listener : the listener to add
+ * @param filter : LDAP filter
+ * @throws InvalidSyntaxException occurs when the filter is malformed.
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener, java.lang.String)
+ */
+ public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.addServiceListener(listener, filter);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.addServiceListener(listener, filter);
+ }
+
+ }
+
+ /**
+ * Add a service listener according to the policy.
+ * @param listener : the listener to add
+ * @see org.apache.felix.ipojo.ServiceContext#addServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void addServiceListener(ServiceListener listener) {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.addServiceListener(listener);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.addServiceListener(listener);
+ }
+ }
+
+ /**
+ * Get all service references. These reference are found inside the local registry, global registry or both according to the policy.
+ * @param clazz : required service specification.
+ * @param filter : LDAP filter
+ * @return the array of service reference, null if no service available
+ * @throws InvalidSyntaxException occurs when the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getAllServiceReferences(java.lang.String, java.lang.String)
+ */
+ public ServiceReference[] getAllServiceReferences(String clazz,
+ String filter) throws InvalidSyntaxException {
+ switch (m_policy) {
+ case LOCAL:
+ return m_local.getAllServiceReferences(clazz, filter);
+ case GLOBAL:
+ return m_global.getAllServiceReferences(clazz, filter);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference[] refLocal = m_local.getAllServiceReferences(clazz, filter);
+ ServiceReference[] refGlobal = m_global.getAllServiceReferences(clazz, filter);
+ if (refLocal != null && refGlobal != null) {
+ ServiceReference[] refs = new ServiceReference[refLocal.length + refGlobal.length];
+ int j = 0;
+ for (int i = 0; i < refLocal.length; i++) {
+ refs[j] = refLocal[i];
+ j++;
+ }
+ for (int i = 0; i < refGlobal.length; i++) {
+ refs[j] = refGlobal[i];
+ j++;
+ }
+ return refs;
+ } else if (refLocal != null && refGlobal == null) {
+ return refLocal;
+ } else {
+ return refGlobal;
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get the service object for the given reference.
+ * @param ref : the service reference
+ * @return the service object
+ * @see org.apache.felix.ipojo.ServiceContext#getService(org.osgi.framework.ServiceReference)
+ */
+ public Object getService(ServiceReference ref) {
+ switch(m_policy) {
+ case LOCAL:
+ // The reference comes from the local scope
+ return m_local.getService(ref);
+ case GLOBAL:
+ // The reference comes from the global registry
+ return m_global.getService(ref);
+ case LOCAL_AND_GLOBAL:
+ if (ref instanceof org.apache.felix.ipojo.composite.ServiceReferenceImpl) {
+ // The reference comes from a composite, i.e. necessary the local composite
+ return m_local.getService(ref);
+ } else {
+ return m_global.getService(ref);
+ }
+ default :
+ return null;
+ }
+ }
+
+ /**
+ * Get a service reference for the required service specification.
+ * @param clazz : the required service specification
+ * @return a service reference or null if not consistent service available
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference getServiceReference(String clazz) {
+ switch (m_policy) {
+ case LOCAL:
+ return m_local.getServiceReference(clazz);
+ case GLOBAL:
+ return m_global.getServiceReference(clazz);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference refLocal = m_local.getServiceReference(clazz);
+ if (refLocal != null) {
+ return refLocal;
+ } else {
+ return m_global.getServiceReference(clazz);
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get a service reference for the required service specification.
+ * @param clazz : the required service specification
+ * @param filter : LDAP filter
+ * @return a service reference array or null if not consistent service available
+ * @throws InvalidSyntaxException occurs when the LDAP filter is malformed
+ * @see org.apache.felix.ipojo.ServiceContext#getServiceReference(java.lang.String)
+ */
+ public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ switch (m_policy) {
+ case LOCAL:
+ return m_local.getServiceReferences(clazz, filter);
+ case GLOBAL:
+ return m_global.getServiceReferences(clazz, filter);
+ case LOCAL_AND_GLOBAL:
+ ServiceReference[] refLocal = m_local.getServiceReferences(clazz, filter);
+ ServiceReference[] refGlobal = m_global.getServiceReferences(clazz, filter);
+ if (refLocal != null && refGlobal != null) {
+ ServiceReference[] refs = new ServiceReference[refLocal.length + refGlobal.length];
+ int j = 0;
+ for (int i = 0; i < refLocal.length; i++) {
+ refs[j] = refLocal[i];
+ j++;
+ }
+ for (int i = 0; i < refGlobal.length; i++) {
+ refs[j] = refGlobal[i];
+ j++;
+ }
+ return refs;
+ } else if (refLocal != null && refGlobal == null) {
+ return refLocal;
+ } else {
+ return refGlobal;
+ }
+ default:
+ return null;
+ }
+
+ }
+
+ /**
+ * This method is not supported.
+ * @param clazzes : specifications
+ * @param service : service object
+ * @param properties : service properties
+ * @return : the service registration object
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+ throw new UnsupportedOperationException("PolicyServiceContext can only be used for service dependency and not service providing");
+ }
+
+ /**
+ * This method is not supported.
+ * @param clazz : specification
+ * @param service : service object
+ * @param properties : service properties
+ * @return : the service registration object
+ * @see org.apache.felix.ipojo.ServiceContext#registerService(java.lang.String, java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+ throw new UnsupportedOperationException("PolicyServiceContext can only be used for service dependency and not service providing");
+ }
+
+ /**
+ * Remove a service listener.
+ * @param listener : the service listener to remove
+ * @see org.apache.felix.ipojo.ServiceContext#removeServiceListener(org.osgi.framework.ServiceListener)
+ */
+ public void removeServiceListener(ServiceListener listener) {
+ if (m_policy == LOCAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_local.removeServiceListener(listener);
+ }
+ if (m_policy == GLOBAL || m_policy == LOCAL_AND_GLOBAL) {
+ m_global.removeServiceListener(listener);
+ }
+ }
+
+ /**
+ * Unget the service reference.
+ * @param reference : the service reference to unget.
+ * @return true if the unget is successful.
+ * @see org.apache.felix.ipojo.ServiceContext#ungetService(org.osgi.framework.ServiceReference)
+ */
+ public boolean ungetService(ServiceReference reference) {
+ if (reference instanceof ServiceReferenceImpl) {
+ return m_local.ungetService(reference);
+ } else {
+ return m_global.ungetService(reference);
+ }
+ }
+
+}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ImportExportHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ImportExportHandler.java
index 2c837a3..5d9100a 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ImportExportHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ImportExportHandler.java
@@ -24,6 +24,7 @@
import org.apache.felix.ipojo.CompositeHandler;
import org.apache.felix.ipojo.CompositeManager;
+import org.apache.felix.ipojo.PolicyServiceContext;
import org.apache.felix.ipojo.ServiceContext;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.metadata.Element;
@@ -116,7 +117,24 @@
filter = "(&" + filter + imp[i].getAttribute("filter") + ")";
}
}
- ServiceImporter si = new ServiceImporter(specification, filter, aggregate, optional, m_context, m_scope, this);
+
+ String id = null;
+ if (imp[i].containsAttribute("id")) {
+ id = imp[i].getAttribute("id");
+ }
+
+ int scopePolicy = -1;
+ if (imp[i].containsAttribute("scope")) {
+ if (imp[i].getAttribute("scope").equalsIgnoreCase("global")) {
+ scopePolicy = PolicyServiceContext.GLOBAL;
+ } else if (imp[i].getAttribute("scope").equalsIgnoreCase("composite")) {
+ scopePolicy = PolicyServiceContext.LOCAL;
+ } else if (imp[i].getAttribute("scope").equalsIgnoreCase("composite+global")) {
+ scopePolicy = PolicyServiceContext.LOCAL_AND_GLOBAL;
+ }
+ }
+
+ ServiceImporter si = new ServiceImporter(specification, filter, aggregate, optional, m_context, m_scope, scopePolicy, id, this);
m_importers.add(si);
}
}
@@ -287,4 +305,8 @@
public HandlerDescription getDescription() {
return new ImportExportDescription(this.getClass().getName(), isValid(), m_importers, m_exporters);
}
+
+ public List getRequirements() {
+ return m_importers;
+ }
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ServiceImporter.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ServiceImporter.java
index c50a394..db73e9c 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ServiceImporter.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/importer/ServiceImporter.java
@@ -23,6 +23,7 @@
import java.util.List;
import java.util.Properties;
+import org.apache.felix.ipojo.PolicyServiceContext;
import org.apache.felix.ipojo.ServiceContext;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -46,9 +47,9 @@
private ServiceContext m_destination;
/**
- * Origin Context.
+ * Context where service need to be found.
*/
- private BundleContext m_origine;
+ private ServiceContext m_origin;
/**
* Imported Specification.
@@ -79,6 +80,11 @@
* Is the importer valid?
*/
private boolean m_isValid;
+
+ /**
+ * Resolving policy.
+ */
+ private int m_policy;
/**
* Reference on the handler.
@@ -106,6 +112,16 @@
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_isServiceLevelRequirement;
+
+ /**
* Constructor.
*
* @param specification : targeted specification
@@ -114,12 +130,13 @@
* @param optional : is the import optional ?
* @param from : parent context
* @param to : internal context
+ * @param policy : resolving policy
+ * @param id : requirement id (may be null)
* @param in : handler
*/
- public ServiceImporter(String specification, String filter, boolean multiple, boolean optional, BundleContext from, ServiceContext to,
+ public ServiceImporter(String specification, String filter, boolean multiple, boolean optional, BundleContext from, ServiceContext to, int policy, String id,
ImportExportHandler in) {
this.m_destination = to;
- this.m_origine = from;
try {
this.m_filter = from.createFilter(filter);
} catch (InvalidSyntaxException e) {
@@ -130,6 +147,18 @@
this.m_specification = specification;
this.m_optional = optional;
this.m_handler = in;
+
+ if (m_id == null) {
+ m_id = m_specification;
+ } else {
+ m_id = id;
+ }
+
+ if (policy == -1) {
+ m_policy = PolicyServiceContext.LOCAL_AND_GLOBAL;
+ } else {
+ m_policy = policy;
+ }
}
/**
@@ -137,7 +166,8 @@
*/
public void start() {
try {
- ServiceReference[] refs = m_origine.getServiceReferences(m_specification, null);
+ m_origin = new PolicyServiceContext(m_handler.getManager().getGlobalContext(), m_handler.getManager().getParentServiceContext(), m_policy);
+ ServiceReference[] refs = m_origin.getServiceReferences(m_specification, null);
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
if (m_filter.match(refs[i])) {
@@ -156,19 +186,19 @@
if (m_aggregate) {
for (int i = 0; i < m_records.size(); i++) {
Record rec = (Record) m_records.get(i);
- rec.m_svcObject = m_origine.getService(rec.m_ref);
+ rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
} else {
Record rec = (Record) m_records.get(0);
- rec.m_svcObject = m_origine.getService(rec.m_ref);
+ rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
}
// Register service listener
try {
- m_origine.addServiceListener(this, "(" + Constants.OBJECTCLASS + "=" + m_specification + ")");
+ m_origin.addServiceListener(this, "(" + Constants.OBJECTCLASS + "=" + m_specification + ")");
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
@@ -196,14 +226,14 @@
*/
public void stop() {
- m_origine.removeServiceListener(this);
+ m_origin.removeServiceListener(this);
for (int i = 0; i < m_records.size(); i++) {
Record rec = (Record) m_records.get(i);
rec.m_svcObject = null;
if (rec.m_reg != null) {
rec.m_reg.unregister();
- m_origine.ungetService(rec.m_ref);
+ m_origin.ungetService(rec.m_ref);
rec.m_ref = null;
}
}
@@ -286,7 +316,7 @@
m_records.add(rec);
// Publishing ?
if (m_records.size() == 1 || m_aggregate) { // If the service is the first one, or if it is a multiple imports
- rec.m_svcObject = m_origine.getService(rec.m_ref);
+ rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
// Compute the new state
@@ -310,7 +340,7 @@
rec.m_svcObject = null;
rec.m_reg.unregister();
rec.m_reg = null;
- m_origine.ungetService(rec.m_ref);
+ m_origin.ungetService(rec.m_ref);
}
}
m_records.removeAll(l);
@@ -321,7 +351,7 @@
if (!m_aggregate) { // Import the next one
Record rec = (Record) m_records.get(0);
if (rec.m_svcObject == null) { // It is the first service which disappears - create the next one
- rec.m_svcObject = m_origine.getService(rec.m_ref);
+ rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
}
@@ -353,5 +383,31 @@
public String getFilter() {
return m_filterStr;
}
+
+ /**
+ * Set that this dependency is a service level dependency.
+ * This forces the scoping policy to be STRICT.
+ * @param b
+ */
+ public void setServiceLevelDependency() {
+ m_isServiceLevelRequirement = true;
+ m_policy = PolicyServiceContext.LOCAL;
+ }
+
+ public String getId() {
+ return m_id;
+ }
+
+ public boolean isServiceLevelRequirement() {
+ return m_isServiceLevelRequirement;
+ }
+
+ public boolean isAggregate() {
+ return m_aggregate;
+ }
+
+ public boolean isOptional() {
+ return m_optional;
+ }
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
index 25e6875..2a81edc 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/CompositionMetadata.java
@@ -88,7 +88,7 @@
// Get implemented service specification
String spec = description.getAttribute("specification");
- m_specification = new SpecificationMetadata(spec, m_context, false, false, m_handler);
+ m_specification = new SpecificationMetadata(spec, m_context, false, false, m_handler);
Element[] mappings = description.getElements("delegation");
for (int i = 0; i < mappings.length; i++) {
@@ -270,6 +270,7 @@
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"));
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
index 3b24bd2..2a2844f 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/composite/service/provides/ProvidedServiceHandler.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.ipojo.composite.service.provides;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
@@ -25,8 +26,13 @@
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.CompositeHandler;
import org.apache.felix.ipojo.CompositeManager;
+import org.apache.felix.ipojo.PolicyServiceContext;
import org.apache.felix.ipojo.architecture.HandlerDescription;
+import org.apache.felix.ipojo.composite.service.importer.ImportExportHandler;
+import org.apache.felix.ipojo.composite.service.importer.ServiceImporter;
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.Logger;
import org.osgi.framework.BundleContext;
@@ -84,22 +90,27 @@
if (provides.length == 0) {
return;
}
+
+ for (int i = 0; i < provides.length; i++) {
+ ProvidedService ps = new ProvidedService(this, provides[i], "" + i);
+ m_managedServices.add(ps);
+ // Check requirements against the service specification
+ if (!checkServiceSpecification(ps)) {
+ return;
+ }
+ im.getComponentDescription().addProvidedServiceSpecification(ps.getSpecification());
+ }
// Compute imports and instances
computeAvailableServices(metadata);
computeAvailableTypes(metadata);
- for (int i = 0; i < provides.length; i++) {
- ProvidedService ps = new ProvidedService(this, provides[i], "" + i);
- m_managedServices.add(ps);
- im.getComponentDescription().addProvidedServiceSpecification(ps.getSpecification());
- }
-
+
im.register(this);
}
/**
- * Start metod.
+ * Start method.
* Start all managed provided service.
* @see org.apache.felix.ipojo.CompositeHandler#start()
*/
@@ -128,7 +139,7 @@
/**
* Stop method.
- * Stop all managedprovided service.
+ * Stop all managed provided service.
* @see org.apache.felix.ipojo.CompositeHandler#stop()
*/
public void stop() {
@@ -221,6 +232,131 @@
m_services.add(sm);
}
}
+
+ /**
+ * Check composite requirement against service specification requirement is available.
+ * @param ps : the provided service to check
+ * @return true if the composite is a correct implementation of the service
+ */
+ private boolean checkServiceSpecification(ProvidedService ps) {
+ try {
+ Class spec = m_manager.getFactory().loadClass(ps.getSpecification());
+ Field specField = spec.getField("specification");
+ Object o = specField.get(null);
+ if (!(o instanceof String)) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getInstanceName() + "] The specification field of the service specification " + ps.getSpecification() + " need to be a String");
+ return false;
+ } else {
+ Element specification = ManifestMetadataParser.parse((String) o);
+ Element[] reqs = specification.getElements("requires");
+ for (int j = 0; j < reqs.length; j++) {
+ ServiceImporter imp = getAttachedRequirement(reqs[j]);
+ if (imp != null) {
+ // Fix service-level dependency flag
+ imp.setServiceLevelDependency();
+ }
+ if (!isRequirementCorrect(imp, reqs[j])) {
+ return false;
+ }
+ }
+ }
+ } catch (NoSuchFieldException e) {
+ return true; // No specification field
+ } catch (ClassNotFoundException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getInstanceName() + "] The service specification " + ps.getSpecification() + " cannot be load");
+ return false;
+ } catch (IllegalArgumentException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getInstanceName() + "] The field 'specification' of the service specification " + ps.getSpecification() + " is not accessible : " + e.getMessage());
+ return false;
+ } catch (IllegalAccessException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getInstanceName() + "] The field 'specification' of the service specification " + ps.getSpecification() + " is not accessible : " + e.getMessage());
+ return false;
+ } catch (ParseException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getInstanceName() + "] The field 'specification' of the service specification " + ps.getSpecification() + " does not contain a valid String : " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 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) {
+ ImportExportHandler ih = (ImportExportHandler) m_manager.getCompositeHandler(ImportExportHandler.class.getName());
+ if (ih == null) {
+ return null;
+ }
+
+ if (element.containsAttribute("id")) {
+ // Look for dependency Id
+ String id = element.getAttribute("id");
+ for (int i = 0; i < ih.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) ih.getRequirements().get(i);
+ if (imp.getId().equals(id)) {
+ 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 < ih.getRequirements().size(); i++) {
+ ServiceImporter imp = (ServiceImporter) ih.getRequirements().get(i);
+ if (imp.getSpecification().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
+ * @return true if the dependency is correct, false otherwise
+ */
+ private boolean isRequirementCorrect(ServiceImporter imp, Element elem) {
+ boolean opt = false;
+ if (elem.containsAttribute("optional") && elem.getAttribute("optional").equalsIgnoreCase("true")) {
+ opt = false;
+ }
+
+ boolean agg = false;
+ if (elem.containsAttribute("aggregate") && elem.getAttribute("aggregate").equalsIgnoreCase("true")) {
+ agg = false;
+ }
+
+ if (imp == null && !opt) {
+ // Add the missing requirement
+ ImportExportHandler ih = (ImportExportHandler) m_manager.getCompositeHandler(ImportExportHandler.class.getName());
+ String spec = elem.getAttribute("specification");
+ String filter = null;
+ if (elem.containsAttribute("filter")) {
+ filter = elem.getAttribute("filter");
+ }
+ ServiceImporter si = new ServiceImporter(spec, filter, agg, opt, m_manager.getContext(), m_manager.getServiceContext(), PolicyServiceContext.LOCAL, null, ih);
+ ih.getRequirements().add(si);
+ }
+
+ if (imp.isAggregate() && !agg) {
+ getManager().getFactory().getLogger().log(Logger.ERROR, "[" + getManager().getInstanceName() + "] The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ return false;
+ }
+
+ if (elem.containsAttribute("filter")) {
+ String filter = elem.getAttribute("filter");
+ String filter2 = imp.getFilter();
+ if (filter2 == null || !filter2.equalsIgnoreCase(filter)) {
+ getManager().getFactory().getLogger().log(Logger.ERROR, "[" + getManager().getInstanceName() + "] The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ return false;
+ }
+ }
+
+ return true;
+ }
public HandlerDescription getDescription() {
return new ProvidedServiceHandlerDescription(true, m_managedServices);
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index 0f2882b..26e23b1 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -20,10 +20,14 @@
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.felix.ipojo.IPojoContext;
import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.PolicyServiceContext;
+import org.apache.felix.ipojo.ServiceContext;
+import org.apache.felix.ipojo.composite.CompositeServiceContext;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
@@ -67,6 +71,11 @@
* Service Specification required by the dependency.
*/
private String m_specification;
+
+ /**
+ * Dependency ID (declared ID, if not declare use the specification).
+ */
+ private String m_id;
/**
* Is the dependency a multiple dependency ?
@@ -82,6 +91,16 @@
* LDAP Filter of the Dependency (String form).
*/
private String m_strFilter;
+
+ /**
+ * Is the dependency a service level dependency.
+ */
+ private boolean m_isServiceLevelRequirement = false;
+
+ /**
+ * Resolution policy.
+ */
+ private int m_policy = PolicyServiceContext.LOCAL_AND_GLOBAL;
/**
* Array of Service Objects. When cardinality = 1 : just the first element
@@ -91,13 +110,19 @@
private Object[] m_services = new Object[0];
/**
- * Array of service references. m_ref : Array
+ * Array of service references.
+ * m_ref : Array
*/
- private ServiceReference[] m_ref = new ServiceReference[0];
+ private List m_references = new ArrayList();
+
+ /**
+ * Array of service reference containing used service references.
+ */
+ private List m_usedReferences = new ArrayList();
/**
- * State of the dependency. 0 : stopped, 1 : valid, 2 : invalid. m_state :
- * int
+ * State of the dependency. 0 : stopped, 1 : valid, 2 : invalid.
+ * m_state : int
*/
private int m_state;
@@ -108,8 +133,8 @@
private boolean m_change;
/**
- * Class of the dependency. Usefull to create in the case of multiple
- * dependency
+ * Class of the dependency.
+ * Useful to create in the case of multiple dependency
*/
private Class m_clazz;
@@ -117,9 +142,14 @@
* LDAP Filter of the dependency.
*/
private Filter m_filter;
+
+ /**
+ * Service Context in which resolving the dependency.
+ */
+ private ServiceContext m_serviceContext;
/**
- * Dependency contructor. After the creation the dependency is not started.
+ * Dependency constructor. After the creation the dependency is not started.
*
* @param dh : the dependency handler managing this dependency
* @param field : field of the dependency
@@ -127,14 +157,29 @@
* @param filter : LDAP filter of the dependency
* @param isOptional : is the dependency an optional dependency ?
* @param isAggregate : is the dependency an aggregate dependency
+ * @param id : id of the dependency, may be null
+ * @param policy : resolution policy
*/
- public Dependency(DependencyHandler dh, String field, String spec, String filter, boolean isOptional, boolean isAggregate) {
+ public Dependency(DependencyHandler dh, String field, String spec, String filter, boolean isOptional, boolean isAggregate, String id, int policy) {
m_handler = dh;
m_field = field;
m_specification = spec;
m_isOptional = isOptional;
m_strFilter = filter;
m_isAggregate = isAggregate;
+ if (m_id == null) {
+ m_id = m_specification;
+ } else {
+ m_id = id;
+ }
+ if (policy != -1) {
+ m_policy = policy;
+ }
+ // Fix the policy according to the level
+ if ((m_policy == PolicyServiceContext.LOCAL_AND_GLOBAL || m_policy == PolicyServiceContext.LOCAL) && ! ((((IPojoContext) m_handler.getInstanceManager().getContext()).getServiceContext()) instanceof CompositeServiceContext)) {
+ // We are not in a composite : BOTH | STRICT => GLOBAL
+ m_policy = PolicyServiceContext.GLOBAL;
+ }
}
public String getField() {
@@ -199,32 +244,29 @@
}
/**
- * Build the map [service object, service reference] of used services.
+ * Build the List [service reference] of used services.
* @return the used service.
*/
- public Map getUsedServices() {
- Map hm = new HashMap();
+ public List getUsedServices() {
+ List list = new ArrayList();
if (m_isAggregate) {
- for (int i = 0; i < m_ref.length; i++) {
- if (i < m_services.length) {
- hm.put(((Object) m_services[i]).toString(), m_ref[i]);
- }
- }
+ list.addAll(m_usedReferences);
+ return list;
} else {
- if (m_ref.length != 0 && m_services.length != 0) {
- hm.put((m_services[0]).toString(), m_ref[0]);
- }
+ if (m_usedReferences.size() != 0 && m_services.length != 0) {
+ list.add(m_usedReferences.get(0));
+ }
}
- return hm;
+ return list;
}
/**
- * A dependency is satisfied if it is optional of ref.length != 0.
+ * A dependency is satisfied if it is optional or there is useful references.
*
- * @return true is the dependency is satified
+ * @return true is the dependency is satisfied
*/
protected boolean isSatisfied() {
- return m_isOptional || m_ref.length != 0;
+ return m_isOptional || ! m_references.isEmpty();
}
/**
@@ -235,13 +277,7 @@
* the dependency.
*/
protected Object get() {
- // m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO,
- // "[" + m_handler.getInstanceManager().getClassName() + "] Call get for
- // a dependency on : " + m_metadata.getServiceSpecification()
- // + " Multiple : " + m_metadata.isMultiple() + " Optional : " +
- // m_metadata.isOptional());
try {
-
// 1 : Test if there is any change in the reference list :
if (!m_change) {
if (!m_isAggregate) {
@@ -257,11 +293,9 @@
// m_services array
m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO,
"[" + m_handler.getInstanceManager().getClassName() + "] Create a service array of " + m_clazz.getName());
- m_services = (Object[]) Array.newInstance(m_clazz, m_ref.length);
+
- for (int i = 0; i < m_ref.length; i++) {
- m_services[i] = m_handler.getInstanceManager().getContext().getService(m_ref[i]);
- }
+ buildServiceObjectArray();
m_change = false;
// m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO,
@@ -325,17 +359,36 @@
}
/**
- * Method calld when a service event is throwed.
+ * Create the service object array according to the resolving policy.
+ */
+ private void buildServiceObjectArray() {
+ if (m_isAggregate) {
+ m_services = (Object[]) Array.newInstance(m_clazz, m_references.size());
+ for (int i = 0; i < m_references.size(); i++) {
+ m_services[i] = getService((ServiceReference) m_references.get(i));
+ }
+ } else {
+ if (m_references.size() == 0) {
+ m_services = new Object[0];
+ } else {
+ m_services = new Object[] { getService((ServiceReference) m_references.get(0)) };
+ }
+ }
+ }
+
+ /**
+ * Method called when a service event occurs.
*
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
- * @param event : the received service event
+ * @param event :
+ * the received service event
*/
public void serviceChanged(ServiceEvent event) {
synchronized (this) {
// If a service goes way.
if (event.getType() == ServiceEvent.UNREGISTERING) {
- if (containsSR(event.getServiceReference())) {
+ if (m_references.contains(event.getServiceReference())) {
departureManagement(event.getServiceReference());
}
return;
@@ -351,11 +404,11 @@
// If a service is modified
if (event.getType() == ServiceEvent.MODIFIED) {
if (m_filter.match(event.getServiceReference())) {
- if (!containsSR(event.getServiceReference())) {
+ if (!m_references.contains(event.getServiceReference())) {
arrivalManagement(event.getServiceReference());
}
} else {
- if (containsSR(event.getServiceReference())) {
+ if (m_references.contains(event.getServiceReference())) {
departureManagement(event.getServiceReference());
}
}
@@ -371,10 +424,10 @@
* @param ref : the arriving service reference
*/
private void arrivalManagement(ServiceReference ref) {
- addReference(ref);
+ m_references.add(ref);
if (isSatisfied()) {
m_state = RESOLVED;
- if (m_isAggregate || m_ref.length == 1) {
+ if (m_isAggregate || ! m_references.isEmpty()) {
m_change = true;
callBindMethod(ref);
}
@@ -389,35 +442,35 @@
*/
private void departureManagement(ServiceReference ref) {
// Call unbind method
- if (!m_isAggregate && ref == m_ref[0]) {
+ boolean hasChanged = false;
+ if (m_usedReferences.contains(ref)) {
callUnbindMethod(ref);
- }
- if (m_isAggregate) {
- callUnbindMethod(ref);
+ // Unget the service reference
+ ungetService(ref);
+ hasChanged = true;
}
- // Unget the service reference
- m_handler.getInstanceManager().getContext().ungetService(ref);
- int index = removeReference(ref);
+ // Remove from the list (remove on both to be sure.
+ m_references.remove(ref);
// Is the state valid or invalid
- if (m_ref.length == 0 && !m_isOptional) {
+ if (m_references.isEmpty() && !m_isOptional) {
m_state = UNRESOLVED;
- }
- if (m_ref.length == 0 && m_isOptional) {
+ } else {
m_state = RESOLVED;
}
+
// Is there any change ?
- if (!m_isAggregate && index == 0) {
- m_change = true;
- if (m_ref.length != 0) {
- callBindMethod(m_ref[0]);
+ if (!m_isAggregate) {
+ if (hasChanged) {
+ m_change = true;
+ if (!m_references.isEmpty()) {
+ callBindMethod((ServiceReference) m_references.get(0));
+ }
+ } else {
+ m_change = false;
}
- }
- if (!m_isAggregate && index != 0) {
- m_change = false;
- }
- if (m_isAggregate) {
+ } else {
m_change = true;
}
@@ -435,7 +488,7 @@
for (int i = 0; i < m_callbacks.length; i++) {
if (m_callbacks[i].getMethodType() == DependencyCallback.UNBIND) {
try {
- m_callbacks[i].call(ref, m_handler.getInstanceManager().getContext().getService(ref));
+ m_callbacks[i].call(ref, getService(ref));
} catch (NoSuchMethodException e) {
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR, "The method " + m_callbacks[i].getMethodName() + " does not exist in the class "
@@ -451,7 +504,7 @@
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR,
"The method " + m_callbacks[i].getMethodName() + " in the class " + m_handler.getInstanceManager().getClassName()
- + "thorws an exception : " + e.getMessage());
+ + "throws an exception : " + e.getMessage());
return;
}
}
@@ -460,24 +513,43 @@
}
/**
+ * Get a service object for the given reference according to the resolving policy.
+ * @param ref : service reference
+ * @return the service object
+ */
+ private Object getService(ServiceReference ref) {
+ if (!m_usedReferences.contains(ref)) {
+ m_usedReferences.add(ref);
+ }
+ return m_serviceContext.getService(ref);
+ }
+
+ /**
+ * Unget the given service reference according to the resolving policy.
+ * @param ref : service reference to unget
+ */
+ private void ungetService(ServiceReference ref) {
+ m_usedReferences.remove(ref);
+ m_serviceContext.ungetService(ref);
+ }
+
+ /**
* Call the bind method.
*
* @param instance : instance on which calling the bind method.
*/
protected void callBindMethod(Object instance) {
- // Check optional case : nullable object case : do not call bind on
- // nullable object
- if (m_isOptional && m_ref.length == 0) {
+ // Check optional case : nullable object case : do not call bind on nullable object
+ if (m_isOptional && m_references.isEmpty()) {
return;
}
if (m_isAggregate) {
- for (int i = 0; i < m_ref.length; i++) {
+ for (int i = 0; i < m_references.size(); i++) {
for (int j = 0; j < m_callbacks.length; j++) {
if (m_callbacks[j].getMethodType() == DependencyCallback.BIND) {
try {
- m_callbacks[j].callOnInstance(instance, m_ref[i], m_handler.getInstanceManager()
- .getContext().getService(m_ref[i]));
+ m_callbacks[j].callOnInstance(instance, (ServiceReference) m_references.get(i), getService((ServiceReference) m_references.get(i)));
} catch (NoSuchMethodException e) {
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR, "The method " + m_callbacks[j].getMethodName() + " does not exist in the class "
@@ -503,7 +575,7 @@
for (int j = 0; j < m_callbacks.length; j++) {
if (m_callbacks[j].getMethodType() == DependencyCallback.BIND) {
try {
- m_callbacks[j].callOnInstance(instance, m_ref[0], m_handler.getInstanceManager().getContext().getService(m_ref[0]));
+ m_callbacks[j].callOnInstance(instance, (ServiceReference) m_references.get(0), getService((ServiceReference) m_references.get(0)));
} catch (NoSuchMethodException e) {
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR, "The method " + m_callbacks[j].getMethodName() + " does not exist in the class "
@@ -538,7 +610,7 @@
for (int i = 0; i < m_callbacks.length; i++) {
if (m_callbacks[i].getMethodType() == DependencyCallback.BIND) {
try {
- m_callbacks[i].call(ref, m_handler.getInstanceManager().getContext().getService(ref));
+ m_callbacks[i].call(ref, getService(ref));
} catch (NoSuchMethodException e) {
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR, "The method " + m_callbacks[i].getMethodName() + " does not exist in the class "
@@ -554,7 +626,7 @@
m_handler.getInstanceManager().getFactory().getLogger().log(
Logger.ERROR,
"The method " + m_callbacks[i].getMethodName() + " in the class " + m_handler.getInstanceManager().getClassName()
- + "thorws an exception : " + e.getMessage());
+ + "throws an exception : " + e.getMessage());
return;
}
}
@@ -566,6 +638,9 @@
* Start the dependency.
*/
public void start() {
+
+ m_serviceContext = new PolicyServiceContext(m_handler.getInstanceManager().getGlobalContext(), m_handler.getInstanceManager().getLocalServiceContext(), m_policy);
+
// Construct the filter with the objectclass + filter
String classnamefilter = "(objectClass=" + m_specification + ")";
String filter = "";
@@ -588,44 +663,61 @@
try {
// Look if the service is already present :
- ServiceReference[] sr = m_handler.getInstanceManager().getContext().getServiceReferences(m_specification, filter);
- if (sr != null) {
- for (int i = 0; i < sr.length; i++) {
- addReference(sr[i]);
- }
+ if (lookForServiceReferences(m_specification, filter)) {
m_state = RESOLVED;
}
// Register a listener :
- m_handler.getInstanceManager().getContext().addServiceListener(this);
- m_filter = m_handler.getInstanceManager().getContext().createFilter(filter); // Store
- // the
- // filter
- m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO,
- "[" + m_handler.getInstanceManager().getClassName() + "] Create a filter from : " + filter);
+ m_serviceContext.addServiceListener(this);
+
+ m_filter = m_handler.getInstanceManager().getContext().createFilter(filter); // Store the filter
+ m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO, "[" + m_handler.getInstanceManager().getClassName() + "] Create a filter from : " + filter);
m_change = true;
} catch (InvalidSyntaxException e1) {
m_handler.getInstanceManager().getFactory().getLogger().log(Logger.ERROR,
- "[" + m_handler.getInstanceManager().getClassName() + "] A filter is malformed : " + filter);
- e1.printStackTrace();
+ "[" + m_handler.getInstanceManager().getClassName() + "] A filter is malformed : " + filter + " - " + e1.getMessage());
}
}
/**
+ * Look for available service.
+ * @param specification : required specification.
+ * @param filter : LDAP Filter
+ * @return true if at least one service is found.
+ */
+ private boolean lookForServiceReferences(String specification, String filter) {
+ boolean success = false; // Are the query fulfilled ?
+ try {
+ ServiceReference[] refs = m_serviceContext.getServiceReferences(specification, filter);
+ if (refs != null) {
+ success = true;
+ for (int i = 0; i < refs.length; i++) {
+ m_references.add(refs[i]);
+ }
+ }
+ } catch (InvalidSyntaxException e) {
+ m_handler.getInstanceManager().getFactory().getLogger().log(Logger.ERROR,
+ "The requirement on " + m_specification + " does not have a vlid filter : " + e.getMessage());
+ }
+ return success;
+ }
+
+ /**
* Stop the dependency.
*/
public void stop() {
- m_handler.getInstanceManager().getContext().removeServiceListener(this);
+ m_serviceContext.removeServiceListener(this);
m_handler.getInstanceManager().getFactory().getLogger().log(Logger.INFO,
"[" + m_handler.getInstanceManager().getInstanceName() + "] Stop a dependency on : " + m_specification + " with " + m_strFilter + " (" + m_handler.getInstanceManager() + ")");
m_state = UNRESOLVED;
// Unget all services references
- for (int i = 0; i < m_ref.length; i++) {
- m_handler.getInstanceManager().getContext().ungetService(m_ref[i]);
+ for (int i = 0; i < m_usedReferences.size(); i++) {
+ ungetService((ServiceReference) m_usedReferences.get(i));
}
- m_ref = new ServiceReference[0];
+ m_references.clear();
+ m_usedReferences.clear();
m_clazz = null;
m_services = new Object[0];
}
@@ -637,96 +729,43 @@
*/
public int getState() {
if (m_isOptional) {
- return 1;
+ return RESOLVED;
} else {
return m_state;
}
}
/**
- * Return the list of service reference.
+ * Return the list of used service reference.
*
* @return the service reference list.
*/
- public ServiceReference[] getServiceReferences() {
- return m_ref;
- }
-
- /**
- * Add a service reference in the current list.
- *
- * @param r : the new service reference to add
- */
- private void addReference(ServiceReference r) {
- for (int i = 0; (m_ref != null) && (i < m_ref.length); i++) {
- if (m_ref[i] == r) {
- return;
- }
- }
-
- if (m_ref != null) {
- ServiceReference[] newSR = new ServiceReference[m_ref.length + 1];
- System.arraycopy(m_ref, 0, newSR, 0, m_ref.length);
- newSR[m_ref.length] = r;
- m_ref = newSR;
- } else {
- m_ref = new ServiceReference[] { r };
- }
- }
-
- /**
- * Find if a service registration il already registred.
- *
- * @param sr : the service registration to find.
- * @return true if the service registration is already in the array
- */
- private boolean containsSR(ServiceReference sr) {
- for (int i = 0; i < m_ref.length; i++) {
- if (m_ref[i] == sr) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Remove a service reference in the current list.
- *
- * @param r : the new service reference to remove
- * @return the index of the founded element, or -1 if the element is not
- * found
- */
- private int removeReference(ServiceReference r) {
- if (m_ref == null) {
- m_ref = new ServiceReference[0];
- }
-
- int idx = -1;
- for (int i = 0; i < m_ref.length; i++) {
- if (m_ref[i] == r) {
- idx = i;
- break;
- }
- }
-
- if (idx >= 0) {
- // If this is the module, then point to empty list.
- if ((m_ref.length - 1) == 0) {
- m_ref = new ServiceReference[0];
- } else { // Otherwise, we need to do some array copying.
- ServiceReference[] newSR = new ServiceReference[m_ref.length - 1];
- System.arraycopy(m_ref, 0, newSR, 0, idx);
- if (idx < newSR.length) {
- System.arraycopy(m_ref, idx + 1, newSR, idx, newSR.length - idx);
- }
- m_ref = newSR;
- }
- }
- return idx;
+ public List getServiceReferences() {
+ List refs = new ArrayList();
+ refs.addAll(m_references);
+ return refs;
}
protected DependencyCallback[] getCallbacks() {
return m_callbacks;
}
+ /**
+ * Set that this dependency is a service level dependency.
+ * This forces the scoping policy to be STRICT.
+ * @param b
+ */
+ public void setServiceLevelDependency() {
+ m_isServiceLevelRequirement = true;
+ m_policy = PolicyServiceContext.LOCAL;
+ }
+
+ public String getId() {
+ return m_id;
+ }
+
+ public boolean isServiceLevelRequirement() {
+ return m_isServiceLevelRequirement;
+ }
+
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java
index eb31f9a..280ff7c 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyDescription.java
@@ -18,8 +18,8 @@
*/
package org.apache.felix.ipojo.handlers.dependency;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
import org.osgi.framework.ServiceReference;
@@ -55,20 +55,20 @@
private String m_filter;
/**
- * Map [Instance reference, service reference] of the used service.
+ * List[service reference] of the used service.
*/
- private Map m_usedServices = new HashMap();
+ private List m_usedServices = new ArrayList();
/**
* The list of service reference.
*/
- private ServiceReference[] m_serviceReferences;
+ private List m_serviceReferences;
/**
* Constructor.
- * @param itf : the needed itf
+ * @param itf : the needed interface
* @param multiple : is the dependency a multiple dependency ?
- * @param optional : is the depdendency optional ?
+ * @param optional : is the dependency optional ?
* @param filter : the filter
* @param state : the state
*/
@@ -79,7 +79,7 @@
m_optional = optional;
m_filter = filter;
m_state = state;
- m_serviceReferences = new ServiceReference[0];
+ m_serviceReferences = new ArrayList();
}
public boolean isMultiple() { return m_multiple; }
@@ -93,34 +93,34 @@
public int getState() { return m_state; }
/**
- * Get the service refrence array.
+ * Get the service reference list.
* @return the array of service reference (only if the cardinality could be n).
*/
- public ServiceReference[] getServiceReferences() { return m_serviceReferences; }
+ public List getServiceReferences() { return m_serviceReferences; }
/**
* Get the service reference if only 1 used.
* @return the ServiceReference (only if the cardinality could be 1).
*/
- public ServiceReference getServiceReference() { return m_serviceReferences[0]; }
+ public ServiceReference getServiceReference() { return (ServiceReference) m_serviceReferences.get(0); }
/**
* Set the service reference array.
- * @param sr : the array of service reference
+ * @param sr : the list of service reference
*/
- public void setServiceReferences(ServiceReference[] sr) { m_serviceReferences = sr; }
+ public void setServiceReferences(List sr) { m_serviceReferences = sr; }
/**
- * Get the used service map.
- * @return the map [object reference, service reference] containing the used services
+ * Get the used service list.
+ * @return the list [service reference] containing the used services
*/
- public Map getUsedServices() { return m_usedServices; }
+ public List getUsedServices() { return m_usedServices; }
/**
* Set the usedServices.
- * @param hm : the new usedService
+ * @param hm : the list of used service reference.
*/
- public void setUsedServices(Map hm) {
+ public void setUsedServices(List hm) {
m_usedServices = hm;
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
index 7e510ed..e5c1d28 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandler.java
@@ -25,6 +25,7 @@
import org.apache.felix.ipojo.Handler;
import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.PolicyServiceContext;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.handlers.dependency.nullable.NullableObjectWriter;
import org.apache.felix.ipojo.metadata.Element;
@@ -269,8 +270,24 @@
if (deps[i].containsAttribute("aggregate") && deps[i].getAttribute("aggregate").equals("true")) {
aggregate = true;
}
+
+ String id = null;
+ if (deps[i].containsAttribute("id")) {
+ id = deps[i].getAttribute("id");
+ }
+
+ int scopePolicy = -1;
+ if (deps[i].containsAttribute("scope")) {
+ if (deps[i].getAttribute("scope").equalsIgnoreCase("global")) {
+ scopePolicy = PolicyServiceContext.GLOBAL;
+ } else if (deps[i].getAttribute("scope").equalsIgnoreCase("composite")) {
+ scopePolicy = PolicyServiceContext.LOCAL;
+ } else if (deps[i].getAttribute("scope").equalsIgnoreCase("composite+global")) {
+ scopePolicy = PolicyServiceContext.LOCAL_AND_GLOBAL;
+ }
+ }
- Dependency dep = new Dependency(this, field, serviceSpecification, filter, optional, aggregate);
+ Dependency dep = new Dependency(this, field, serviceSpecification, filter, optional, aggregate, id, scopePolicy);
// Look for dependency callback :
for (int j = 0; j < (deps[i].getElements("Callback", "")).length; j++) {
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
index 6d01ff8..6c1bd85 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
@@ -18,11 +18,13 @@
*/
package org.apache.felix.ipojo.handlers.dependency;
-import java.util.Iterator;
+import java.util.List;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
/**
* Dependency Handler Description.
@@ -88,12 +90,30 @@
Element dep = new Element("Requires", "");
dep.addAttribute(new Attribute("Specification", m_dependencies[i].getInterface()));
dep.addAttribute(new Attribute("Filter", m_dependencies[i].getFilter()));
+
+ if (m_dependencies[i].isOptional()) {
+ dep.addAttribute(new Attribute("Optional", "true"));
+ } else {
+ dep.addAttribute(new Attribute("Optional", "false"));
+ }
+
+ if (m_dependencies[i].isMultiple()) {
+ dep.addAttribute(new Attribute("Aggregate", "true"));
+ } else {
+ dep.addAttribute(new Attribute("Aggregate", "false"));
+ }
+
dep.addAttribute(new Attribute("State", state));
Element usages = new Element("Usages", "");
- Iterator it = m_dependencies[i].getUsedServices().keySet().iterator();
- while (it.hasNext()) {
+ List list = m_dependencies[i].getUsedServices();
+ for (int j = 0; j < list.size(); j++) {
Element use = new Element("Use", "");
- use.addAttribute(new Attribute("object", it.next().toString()));
+ ServiceReference ref = (ServiceReference) list.get(i);
+ use.addAttribute(new Attribute("service.id", (String) ref.getProperty(Constants.SERVICE_ID)));
+ String pid = (String) ref.getProperty(Constants.SERVICE_PID);
+ if (pid != null) {
+ use.addAttribute(new Attribute("service.pid", pid));
+ }
usages.addElement(use);
}
deps.addElement(dep);
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
index 172811c..cd00739 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
@@ -100,8 +100,6 @@
addProperty(new Property(this, "factory.pid", handler.getInstanceManager().getFactory().getName()));
}
- // TODO check if we need to erase previous props or add to the previous
- // props.
/**
* Add properties to the provided service.
*
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
index da2dda9..2b15082 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.ipojo.handlers.providedservice;
+import java.lang.reflect.Field;
import java.util.Dictionary;
import java.util.Properties;
@@ -26,9 +27,13 @@
import org.apache.felix.ipojo.architecture.ComponentDescription;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.architecture.PropertyDescription;
+import org.apache.felix.ipojo.handlers.dependency.Dependency;
+import org.apache.felix.ipojo.handlers.dependency.DependencyHandler;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.ManifestMetadataParser;
import org.apache.felix.ipojo.parser.ManipulationMetadata;
+import org.apache.felix.ipojo.parser.ParseException;
import org.apache.felix.ipojo.parser.ParseUtils;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.Constants;
@@ -226,42 +231,129 @@
* metadata are consistent.
*
* @param ps : the provided service to check.
- * @param manipulation : componenet-type manipulation metadata.
+ * @param manipulation : component-type manipulation metadata.
* @return true if the provided service is correct
*/
private boolean checkProvidedService(ProvidedService ps, ManipulationMetadata manipulation) {
-
for (int i = 0; i < ps.getServiceSpecification().length; i++) {
+ // Check the implementation of the specification
if (! manipulation.isInterfaceImplemented(ps.getServiceSpecification()[i])) {
m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The service specification " + ps.getServiceSpecification()[i]
+ " is not implemented by the component class");
return false;
}
+
+ // Check service level dependencies
+ try {
+ Class spec = m_manager.getFactory().loadClass(ps.getServiceSpecification()[i]);
+ Field specField = spec.getField("specification");
+ Object o = specField.get(null);
+ if (!(o instanceof String)) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The specification field of the service specification " + ps.getServiceSpecification()[i] + " need to be a String");
+ return false;
+ } else {
+ Element specification = ManifestMetadataParser.parse((String) o);
+ Element[] deps = specification.getElements("requires");
+ for (int j = 0; j < deps.length; j++) {
+ Dependency d = getAttachedDependency(deps[j]);
+ if (d != null) {
+ // Fix service-level dependency flag
+ d.setServiceLevelDependency();
+ }
+ if (!isDependencyCorrect(d, deps[j])) {
+ return false;
+ }
+ }
+ }
+ } catch (NoSuchFieldException e) {
+ return true; // No specification field
+ } catch (ClassNotFoundException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The service specification " + ps.getServiceSpecification()[i] + " cannot be load");
+ return false;
+ } catch (IllegalArgumentException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The field 'specification' of the service specification " + ps.getServiceSpecification()[i] + " is not accessible : " + e.getMessage());
+ return false;
+ } catch (IllegalAccessException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The field 'specification' of the service specification " + ps.getServiceSpecification()[i] + " is not accessible : " + e.getMessage());
+ return false;
+ } catch (ParseException e) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The field 'specification' of the service specification " + ps.getServiceSpecification()[i] + " does not contain a valid String : " + e.getMessage());
+ return false;
+ }
}
-// // Fix internal property type
-// for (int i = 0; i < ps.getProperties().length; i++) {
-// Property prop = ps.getProperties()[i];
-// String field = prop.getField();
-//
-// if (field == null) {
-// return true; // Static property -> Nothing to check
-// } else {
-// String type = null;
-// for (int j = 0; j < manipulation.getElements("Field").length; j++) {
-// if (field.equals(manipulation.getElements("Field")[j].getAttribute("name"))) {
-// type = manipulation.getElements("Field")[j].getAttribute("type");
-// break;
-// }
-// }
-// if (type == null) {
-// m_manager.getFactory().getLogger().log(Logger.ERROR,
-// "[" + m_manager.getClassName() + "] A declared property was not found in the class : " + prop.getField());
-// return false;
-// }
-// prop.setType(type); // Set the type
-// }
-// }
+ return true;
+ }
+
+ /**
+ * Look for the implementation (i.e. component) dependency for the given service-level requirement metadata.
+ * @param element : the service-level requirement metadata
+ * @return the Dependency object, null if not found or if the DependencyHandler is not plugged to the instance
+ */
+ private Dependency getAttachedDependency(Element element) {
+ DependencyHandler dh = (DependencyHandler) m_manager.getHandler(DependencyHandler.class.getName());
+ if (dh == null) {
+ return null;
+ }
+
+ if (element.containsAttribute("id")) {
+ // Look for dependency Id
+ String id = element.getAttribute("id");
+ for (int i = 0; i < dh.getDependencies().length; i++) {
+ if (dh.getDependencies()[i].getId().equals(id)) {
+ return dh.getDependencies()[i];
+ }
+ }
+ }
+
+ // If not found or no id, look for a dependency with the same specification
+ String requirement = element.getAttribute("specification");
+ for (int i = 0; i < dh.getDependencies().length; i++) {
+ if (dh.getDependencies()[i].getSpecification().equals(requirement)) {
+ return dh.getDependencies()[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Check the correctness of the implementation dependency against the service level dependency.
+ * @param dep : dependency to check
+ * @param elem : service-level dependency metadata
+ * @return true if the dependency is correct, false otherwise
+ */
+ private boolean isDependencyCorrect(Dependency dep, Element elem) {
+ boolean opt = false;
+ if (elem.containsAttribute("optional") && elem.getAttribute("optional").equalsIgnoreCase("true")) {
+ opt = false;
+ }
+
+ boolean agg = false;
+ if (elem.containsAttribute("aggregate") && elem.getAttribute("aggregate").equalsIgnoreCase("true")) {
+ agg = false;
+ }
+
+ if (dep == null && !opt) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The requirement " + elem.getAttribute("specification") + " is not present in the implementation and is declared as a mandatory service-level requirement");
+ return false;
+ }
+
+
+ if (dep.isAggregate() && !agg) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The requirement " + elem.getAttribute("specification") + " is aggregate in the implementation and is declared as a simple service-level requirement");
+ return false;
+ }
+
+ if (elem.containsAttribute("filter")) {
+ String filter = elem.getAttribute("filter");
+ String filter2 = dep.getFilter();
+ if (filter2 == null || !filter2.equalsIgnoreCase(filter)) {
+ m_manager.getFactory().getLogger().log(Logger.ERROR, "[" + m_manager.getClassName() + "] The specification requirement " + elem.getAttribute("specification") + " as not the same filter as declared in the service-level requirement");
+ return false;
+ }
+ }
+
return true;
}
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java
index a2a8eec..2fd6641 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandlerDescription.java
@@ -82,7 +82,7 @@
public Element getHandlerInfo() {
Element services = super.getHandlerInfo();
for (int i = 0; i < m_providedServices.length; i++) {
- Element service = new Element("service", "");
+ Element service = new Element("provides", "");
String state = "unregistered";
if (m_providedServices[i].getState() == ProvidedService.REGISTERED) {
state = "registered";
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
index f1ec6e7..546f359 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
@@ -21,7 +21,7 @@
import java.util.StringTokenizer;
/**
- * Parse Utils Methods.
+ * Parse Utility Methods.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
@@ -69,5 +69,4 @@
}
return result;
}
-
}