summaryrefslogtreecommitdiffstats
path: root/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultExtensionPointRegistry.java193
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultFactoryExtensionPoint.java162
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultModuleActivatorExtensionPoint.java154
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultUtilityExtensionPoint.java219
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ExtensionPointRegistry.java66
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/FactoryExtensionPoint.java55
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/LifeCycleListener.java37
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivator.java72
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivatorExtensionPoint.java53
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/UtilityExtensionPoint.java74
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ClassLoaderContext.java253
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscoverer.java194
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclaration.java85
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java375
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscoverer.java54
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscovery.java306
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceHelper.java226
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/ClassLoaderDelegate.java89
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/InvalidSyntaxException.java86
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/LDAPFilter.java1373
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.FactoryExtensionPoint19
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint19
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.UtilityExtensionPoint19
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscovererTestCase.java87
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultExtensionPointRegistryTestCase.java58
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultUtilityExtensionPointTestCase.java118
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceDiscoveryTestCase.java101
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceHelperTestCase.java79
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/DummyImpl.java31
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/Test2Impl.java103
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestImpl.java42
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestInterface.java30
-rw-r--r--sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/resources/META-INF/services/org.apache.tuscany.sca.extensibility.test.TestInterface19
33 files changed, 4851 insertions, 0 deletions
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultExtensionPointRegistry.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultExtensionPointRegistry.java
new file mode 100644
index 0000000000..e1f9ac6ddd
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultExtensionPointRegistry.java
@@ -0,0 +1,193 @@
+/*
+ * 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.tuscany.sca.core;
+
+import static org.apache.tuscany.sca.extensibility.ServiceHelper.newInstance;
+
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tuscany.sca.extensibility.ServiceDeclaration;
+import org.apache.tuscany.sca.extensibility.ServiceDiscovery;
+import org.apache.tuscany.sca.extensibility.ServiceHelper;
+
+/**
+ * Default implementation of a registry to hold all the Tuscany core extension
+ * points. As the point of contact for all extension artifacts this registry
+ * allows loaded extensions to find all other parts of the system and register
+ * themselves appropriately.
+ *
+ * @tuscany.spi.extension.asclient
+ *
+ * @version $Rev$ $Date$
+ */
+public class DefaultExtensionPointRegistry implements ExtensionPointRegistry {
+ protected Map<Class<?>, Object> extensionPoints = new HashMap<Class<?>, Object>();
+ private ServiceDiscovery discovery;
+ /**
+ * Constructs a new registry.
+ */
+ public DefaultExtensionPointRegistry() {
+ this.discovery = ServiceDiscovery.getInstance();
+ }
+
+ public DefaultExtensionPointRegistry(ServiceDiscovery discovery) {
+ this.discovery = discovery;
+ }
+
+ /**
+ * Add an extension point to the registry. This default implementation
+ * stores extensions against the interfaces that they implement.
+ *
+ * @param extensionPoint The instance of the extension point
+ *
+ * @throws IllegalArgumentException if extensionPoint is null
+ */
+ public synchronized void addExtensionPoint(Object extensionPoint) {
+ addExtensionPoint(extensionPoint, null);
+ }
+
+ public synchronized void addExtensionPoint(Object extensionPoint, ServiceDeclaration declaration) {
+ if (extensionPoint == null) {
+ throw new IllegalArgumentException("Cannot register null as an ExtensionPoint");
+ }
+ ServiceHelper.start(extensionPoint);
+
+ Set<Class<?>> interfaces = getAllInterfaces(extensionPoint.getClass());
+ for (Class<?> i : interfaces) {
+ registerExtensionPoint(i, extensionPoint, declaration);
+ }
+ }
+
+ protected void registerExtensionPoint(Class<?> i, Object extensionPoint, ServiceDeclaration declaration) {
+ extensionPoints.put(i, extensionPoint);
+ }
+
+ /**
+ * Get the extension point by the interface that it implements
+ *
+ * @param extensionPointType The lookup key (extension point interface)
+ * @return The instance of the extension point
+ *
+ * @throws IllegalArgumentException if extensionPointType is null
+ */
+ public synchronized <T> T getExtensionPoint(Class<T> extensionPointType) {
+ if (extensionPointType == null) {
+ throw new IllegalArgumentException("Cannot lookup ExtensionPoint of type null");
+ }
+
+ Object extensionPoint = findExtensionPoint(extensionPointType);
+ if (extensionPoint == null) {
+
+ // Dynamically load an extension point class declared under META-INF/services
+ try {
+ ServiceDeclaration extensionPointDeclaration =
+ getServiceDiscovery().getServiceDeclaration(extensionPointType);
+ if (extensionPointDeclaration != null) {
+ extensionPoint = newInstance(this, extensionPointDeclaration);
+ // Cache the loaded extension point
+ addExtensionPoint(extensionPoint, extensionPointDeclaration);
+ }
+ } catch (Throwable e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ return extensionPointType.cast(extensionPoint);
+ }
+
+ protected <T> Object findExtensionPoint(Class<T> extensionPointType) {
+ return extensionPoints.get(extensionPointType);
+ }
+
+ /**
+ * Remove an extension point based on the interface that it implements
+ *
+ * @param extensionPoint The extension point to remove
+ *
+ * @throws IllegalArgumentException if extensionPoint is null
+ */
+ public synchronized void removeExtensionPoint(Object extensionPoint) {
+ if (extensionPoint == null) {
+ throw new IllegalArgumentException("Cannot remove null as an ExtensionPoint");
+ }
+
+ ServiceHelper.stop(extensionPoint);
+
+ Set<Class<?>> interfaces = getAllInterfaces(extensionPoint.getClass());
+ for (Class<?> i : interfaces) {
+ unregisterExtensionPoint(i);
+ }
+ }
+
+ protected void unregisterExtensionPoint(Class<?> i) {
+ extensionPoints.remove(i);
+ }
+
+ /**
+ * Returns the set of interfaces implemented by the given class and its
+ * ancestors or a blank set if none
+ */
+ private static Set<Class<?>> getAllInterfaces(Class<?> clazz) {
+ Set<Class<?>> implemented = new HashSet<Class<?>>();
+ getAllInterfaces(clazz, implemented);
+ implemented.remove(LifeCycleListener.class);
+ return implemented;
+ }
+
+ private static void getAllInterfaces(Class<?> clazz, Set<Class<?>> implemented) {
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for (Class<?> interfaze : interfaces) {
+ if (Modifier.isPublic(interfaze.getModifiers())) {
+ implemented.add(interfaze);
+ }
+ }
+ Class<?> superClass = clazz.getSuperclass();
+ // Object has no superclass so check for null
+ if (superClass != null && !superClass.equals(Object.class)) {
+ getAllInterfaces(superClass, implemented);
+ }
+ }
+
+ public synchronized void start() {
+ // Do nothing
+ }
+
+ public synchronized void stop() {
+ // Get a unique map as an extension point may exist in the map by different keys
+ Map<LifeCycleListener, LifeCycleListener> map = new IdentityHashMap<LifeCycleListener, LifeCycleListener>();
+ for (Object extp : extensionPoints.values()) {
+ if (extp instanceof LifeCycleListener) {
+ LifeCycleListener listener = (LifeCycleListener)extp;
+ map.put(listener, listener);
+ }
+ }
+ ServiceHelper.stop(map.values());
+ extensionPoints.clear();
+ }
+
+ public ServiceDiscovery getServiceDiscovery() {
+ return discovery;
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultFactoryExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultFactoryExtensionPoint.java
new file mode 100644
index 0000000000..1c49ea4ee4
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultFactoryExtensionPoint.java
@@ -0,0 +1,162 @@
+/*
+ * 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.tuscany.sca.core;
+
+import static org.apache.tuscany.sca.extensibility.ServiceHelper.newInstance;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tuscany.sca.extensibility.ServiceDeclaration;
+
+
+
+/**
+ * Default implementation of a model factory extension point.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DefaultFactoryExtensionPoint implements FactoryExtensionPoint {
+ private ExtensionPointRegistry registry;
+ private Map<Class<?>, Object> factories = new ConcurrentHashMap<Class<?>, Object>();
+
+ /**
+ * Constructs a new DefaultModelFactoryExtensionPoint.
+ */
+ public DefaultFactoryExtensionPoint(ExtensionPointRegistry extensionPointRegistry) {
+ this.registry = extensionPointRegistry;
+ }
+
+ /**
+ * Add a model factory extension.
+ *
+ * @param factory The factory to add
+ */
+ public void addFactory(Object factory) {
+ Class<?>[] interfaces = factory.getClass().getInterfaces();
+ if (interfaces.length == 0) {
+ Class<?> sc = factory.getClass().getSuperclass();
+ if (sc != Object.class) {
+ factories.put(sc, factory);
+ }
+ } else {
+ for (int i = 0; i<interfaces.length; i++) {
+ factories.put(interfaces[i], factory);
+ }
+ }
+ }
+
+ /**
+ * Remove a model factory.
+ *
+ * @param factory The factory to remove
+ */
+ public void removeFactory(Object factory) {
+ Class<?>[] interfaces = factory.getClass().getInterfaces();
+ if (interfaces.length == 0) {
+ Class<?> sc = factory.getClass().getSuperclass();
+ if (sc != Object.class) {
+ factories.remove(sc);
+ }
+ } else {
+ for (int i = 0; i<interfaces.length; i++) {
+ factories.remove(interfaces[i]);
+ }
+ }
+ }
+
+ private ClassLoader setContextClassLoader(final ClassLoader classLoader) {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ }
+ return tccl;
+ }
+ });
+ }
+
+ /**
+ * Get a factory implementing the given interface.
+ * @param factoryInterface The lookup key (factory interface)
+ * @return The factory
+ */
+ public <T> T getFactory(Class<T> factoryInterface) {
+ Object factory = factories.get(factoryInterface);
+ if (factory == null) {
+
+ // Dynamically load a factory class declared under META-INF/services
+ try {
+ ServiceDeclaration factoryDeclaration =
+ registry.getServiceDiscovery().getServiceDeclaration(factoryInterface);
+ if (factoryDeclaration != null) {
+ try {
+ // Constructor taking the extension point registry
+ factory = newInstance(registry, factoryDeclaration);
+ } catch (NoSuchMethodException e) {
+ factory = newInstance(factoryDeclaration.loadClass(), FactoryExtensionPoint.class, this);
+ }
+
+ // Cache the loaded factory
+ factories.put(factoryInterface, factory);
+
+ return factoryInterface.cast(factory);
+
+ } else {
+
+ // If the input interface is an abstract class
+ if (!factoryInterface.isInterface() && Modifier.isAbstract(factoryInterface.getModifiers())) {
+ Method newInstanceMethod = factoryInterface.getDeclaredMethod("newInstance");
+ ClassLoader tccl = setContextClassLoader(factoryInterface.getClassLoader());
+ try {
+
+ // Create a new instance
+ factory = newInstanceMethod.invoke(null);
+
+ // Cache the factory
+ factories.put(factoryInterface, factory);
+
+ return factoryInterface.cast(factory);
+ } catch (Exception e) {
+ // Sorry no factory found
+ return null;
+ } finally {
+ setContextClassLoader(tccl);
+ }
+ } else {
+
+ // Sorry no factory found
+ return null;
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ } else {
+ return factoryInterface.cast(factory);
+ }
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultModuleActivatorExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultModuleActivatorExtensionPoint.java
new file mode 100644
index 0000000000..99792d0215
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultModuleActivatorExtensionPoint.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tuscany.sca.core;
+
+import static org.apache.tuscany.sca.extensibility.ServiceHelper.newInstance;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.extensibility.ServiceDeclaration;
+
+/**
+ * Default implementation of an extension point to hold Tuscany module activators.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DefaultModuleActivatorExtensionPoint implements ModuleActivatorExtensionPoint {
+ private final static Logger logger = Logger.getLogger(DefaultModuleActivatorExtensionPoint.class.getName());
+ private List<ModuleActivator> activators = new ArrayList<ModuleActivator>();
+ private boolean loadedActivators;
+ private boolean started;
+ private ExtensionPointRegistry registry;
+
+ /**
+ * Constructs a new extension point.
+ */
+ public DefaultModuleActivatorExtensionPoint(ExtensionPointRegistry registry) {
+ this.registry = registry;
+ }
+
+ public void addModuleActivator(ModuleActivator activator) {
+ activators.add(activator);
+ }
+
+ public List<ModuleActivator> getModuleActivators() {
+ loadModuleActivators();
+ return activators;
+ }
+
+ public void removeModuleActivator(ModuleActivator activator) {
+ if (activators.remove(activator)) {
+ activator.stop();
+ }
+ }
+
+ /**
+ * Dynamically load module activators declared under META-INF/services
+ */
+ private synchronized void loadModuleActivators() {
+ if (loadedActivators)
+ return;
+
+ // Get the activator service declarations
+ Collection<ServiceDeclaration> activatorDeclarations;
+ try {
+ // Load the module activators by ranking
+ activatorDeclarations = registry.getServiceDiscovery().getServiceDeclarations(ModuleActivator.class.getName(), true);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+
+ // Load and instantiate module activators
+ for (ServiceDeclaration activatorDeclaration : activatorDeclarations) {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("Loading " + activatorDeclaration.getClassName());
+ }
+ ModuleActivator activator = null;
+ try {
+ Class<ModuleActivator> activatorClass = (Class<ModuleActivator>)activatorDeclaration.loadClass();
+ try {
+ activator = newInstance(activatorClass, ExtensionPointRegistry.class, registry);
+ } catch (NoSuchMethodException e) {
+ try {
+ activator =
+ newInstance(activatorClass,
+ new Class<?>[] {ExtensionPointRegistry.class, Map.class},
+ registry,
+ activatorDeclaration.getAttributes());
+
+ } catch (NoSuchMethodException e1) {
+ activator = newInstance(activatorClass);
+
+ }
+ }
+ } catch (Throwable e) {
+ String optional = activatorDeclaration.getAttributes().get("optional");
+ if ("true".equalsIgnoreCase(optional)) {
+ // If the optional flag is true, just log the error
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ continue;
+ } else {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ addModuleActivator(activator);
+ }
+
+ loadedActivators = true;
+ }
+
+ public void start() {
+ if (started) {
+ return;
+ }
+ getModuleActivators();
+ for (ModuleActivator activator : activators) {
+ try {
+ activator.start();
+ } catch (Throwable e) {
+ // Ignore the failing module for now
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
+ started = true;
+ }
+
+ public void stop() {
+ if (!started) {
+ return;
+ }
+ for (int i = activators.size() - 1; i >= 0; i--) {
+ try {
+ activators.get(i).stop();
+ } catch (Throwable e) {
+ // Ignore the failing module for now
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
+ started = false;
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultUtilityExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultUtilityExtensionPoint.java
new file mode 100644
index 0000000000..40e4635d77
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/DefaultUtilityExtensionPoint.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tuscany.sca.core;
+
+import static org.apache.tuscany.sca.extensibility.ServiceHelper.newInstance;
+
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tuscany.sca.extensibility.ServiceDeclaration;
+
+/**
+ * Default implementation of an extension point to hold Tuscany utility utilities.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DefaultUtilityExtensionPoint implements UtilityExtensionPoint {
+ private Map<Object, Object> utilities = new ConcurrentHashMap<Object, Object>();
+
+ private ExtensionPointRegistry registry;
+ /**
+ * Constructs a new extension point.
+ */
+ public DefaultUtilityExtensionPoint(ExtensionPointRegistry extensionPoints) {
+ this.registry = extensionPoints;
+ }
+
+ /**
+ * Add a utility to the extension point. This default implementation
+ * stores utilities against the interfaces that they implement.
+ *
+ * @param utility The instance of the utility
+ *
+ * @throws IllegalArgumentException if utility is null
+ */
+ public void addUtility(Object utility) {
+ addUtility(null, utility);
+ }
+
+ public void addUtility(Object key, Object utility) {
+ if (utility == null) {
+ throw new IllegalArgumentException("Cannot register null as a Service");
+ }
+
+ if (utility instanceof LifeCycleListener) {
+ ((LifeCycleListener)utility).start();
+ }
+
+ if (key == null) {
+ Class<?> cls = utility.getClass();
+ Set<Class<?>> interfaces = getAllInterfaces(cls);
+ for (Class<?> i : interfaces) {
+ utilities.put(i, utility);
+ }
+ if (interfaces.isEmpty() || isConcreteClass(cls)) {
+ utilities.put(cls, utility);
+ }
+ } else {
+ utilities.put(key, utility);
+ }
+ }
+
+ /**
+ * Get the utility by the interface that it implements
+ *
+ * @param utilityType The lookup key (utility interface)
+ * @return The instance of the utility
+ *
+ * @throws IllegalArgumentException if utilityType is null
+ */
+ public <T> T getUtility(Class<T> utilityType) {
+ return getUtility(utilityType, null);
+ }
+
+ /**
+ * Remove a utility based on the interface that it implements
+ *
+ * @param utility The utility to remove
+ *
+ * @throws IllegalArgumentException if utility is null
+ */
+ public void removeUtility(Object utility) {
+ if (utility == null) {
+ throw new IllegalArgumentException("Cannot remove null as a Service");
+ }
+
+ if(utility instanceof LifeCycleListener) {
+ ((LifeCycleListener) utility).stop();
+ }
+
+ for (Iterator<Map.Entry<Object, Object>> i = utilities.entrySet().iterator(); i.hasNext();) {
+ Map.Entry<Object, Object> entry = i.next();
+ if (entry.getValue() == utility) {
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Returns the set of interfaces implemented by the given class and its
+ * ancestors or a blank set if none
+ */
+ private static Set<Class<?>> getAllInterfaces(Class<?> clazz) {
+ Set<Class<?>> implemented = new HashSet<Class<?>>();
+ getAllInterfaces(clazz, implemented);
+ implemented.remove(LifeCycleListener.class);
+ return implemented;
+ }
+
+ private static void getAllInterfaces(Class<?> clazz, Set<Class<?>> implemented) {
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for (Class<?> interfaze : interfaces) {
+ if (Modifier.isPublic(interfaze.getModifiers())) {
+ implemented.add(interfaze);
+ }
+ }
+ Class<?> superClass = clazz.getSuperclass();
+ // Object has no superclass so check for null
+ if (superClass != null && !superClass.equals(Object.class)) {
+ getAllInterfaces(superClass, implemented);
+ }
+ }
+
+ public <T> T getUtility(Class<T> utilityType, Object key) {
+ if (utilityType == null) {
+ throw new IllegalArgumentException("Cannot lookup Service of type null");
+ }
+
+ if (key == null) {
+ key = utilityType;
+ }
+
+ Object utility = utilities.get(key);
+
+ if (utility == null) {
+
+ // Dynamically load a utility class declared under META-INF/services/"utilityType"
+ try {
+ ServiceDeclaration utilityDeclaration =
+ registry.getServiceDiscovery().getServiceDeclaration(utilityType.getName());
+ Class<?> utilityClass = null;
+ if (utilityDeclaration != null) {
+ utilityClass = utilityDeclaration.loadClass();
+ } else if (isConcreteClass(utilityType)) {
+ utilityClass = utilityType;
+ key = utilityType;
+ }
+ if (utilityClass != null) {
+ // Construct the utility
+ if (utilityDeclaration != null) {
+ utility = newInstance(registry, utilityDeclaration);
+ } else {
+ try {
+ utility = newInstance(utilityClass, ExtensionPointRegistry.class, registry);
+ } catch (NoSuchMethodException e) {
+ utility = newInstance(utilityClass);
+ }
+ }
+ // Cache the loaded utility
+ if (key == utilityType) {
+ addUtility(utility);
+ } else {
+ addUtility(key, utility);
+ }
+ }
+ } catch (Throwable e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ return utilityType.cast(utility);
+ }
+
+ private boolean isConcreteClass(Class<?> utilityType) {
+ int modifiers = utilityType.getModifiers();
+ return !utilityType.isInterface() && Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers);
+ }
+
+ public void start() {
+ // NOOP
+ }
+
+ public synchronized void stop() {
+ // Get a unique map as an extension point may exist in the map by different keys
+ Map<LifeCycleListener, LifeCycleListener> map = new IdentityHashMap<LifeCycleListener, LifeCycleListener>();
+ for (Object util : utilities.values()) {
+ if (util instanceof LifeCycleListener) {
+ LifeCycleListener listener = (LifeCycleListener)util;
+ map.put(listener, listener);
+ }
+ }
+ for (LifeCycleListener listener : map.values()) {
+ listener.stop();
+ }
+ utilities.clear();
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ExtensionPointRegistry.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ExtensionPointRegistry.java
new file mode 100644
index 0000000000..8f9ab7ed3e
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ExtensionPointRegistry.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.core;
+
+import org.apache.tuscany.sca.extensibility.ServiceDiscovery;
+
+
+/**
+ * The registry for the Tuscany core extension points. As the point of contact
+ * for all extension artifacts this registry allows loaded extensions to find
+ * all other parts of the system and register themselves appropriately.
+ *
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.asclient
+ */
+public interface ExtensionPointRegistry extends LifeCycleListener {
+
+ /**
+ * Add an extension point to the registry
+ * @param extensionPoint The instance of the extension point
+ *
+ * @throws IllegalArgumentException if extensionPoint is null
+ */
+ void addExtensionPoint(Object extensionPoint);
+
+ /**
+ * Get the extension point by the interface
+ * @param extensionPointType The lookup key (extension point interface)
+ * @return The instance of the extension point
+ *
+ * @throws IllegalArgumentException if extensionPointType is null
+ */
+ <T> T getExtensionPoint(Class<T> extensionPointType);
+
+ /**
+ * Remove an extension point
+ * @param extensionPoint The extension point to remove
+ *
+ * @throws IllegalArgumentException if extensionPoint is null
+ */
+ void removeExtensionPoint(Object extensionPoint);
+
+ /**
+ * Get an instance of the ServiceDiscovery
+ * @return an instance of the ServiceDiscovery associated with the environment
+ */
+ ServiceDiscovery getServiceDiscovery();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/FactoryExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/FactoryExtensionPoint.java
new file mode 100644
index 0000000000..c3d2fd282a
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/FactoryExtensionPoint.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.core;
+
+/**
+ * An extension point for model factories. Model factories are provided to
+ * abstract the classes that represent artifacts in the assembly model away
+ * from their creation mechanism. When the runtime needs to extend the model
+ * as it reads in contributed artifacts it looks up the factory for the
+ * artifact required in this registry
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.asclient
+ */
+public interface FactoryExtensionPoint {
+
+ /**
+ * Add a model factory extension.
+ *
+ * @param factory The factory to add
+ */
+ void addFactory(Object factory);
+
+ /**
+ * Remove a model factory extension.
+ *
+ * @param factory The factory to remove
+ */
+ void removeFactory(Object factory);
+
+ /**
+ * Get a factory implementing the given interface.
+ * @param factoryInterface the lookup key (factory interface)
+ * @return The factory
+ */
+ <T> T getFactory(Class<T> factoryInterface);
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/LifeCycleListener.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/LifeCycleListener.java
new file mode 100644
index 0000000000..ca92cb8129
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/LifeCycleListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tuscany.sca.core;
+
+/**
+ * A listener that responds to the start/stop event of the ExtensionPointRegistry. Tuscany extension
+ * points or extensions can implement this interface to receive callbacks when the registry is started
+ * or stopped
+ * @tuscany.spi.extension.inheritfrom
+ */
+public interface LifeCycleListener {
+ /**
+ * The method will be invoked when the extension point registry is started
+ */
+ void start();
+ /**
+ * The method will be invoked when the extension point registry is stopped
+ */
+ void stop();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivator.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivator.java
new file mode 100644
index 0000000000..ddc9a6981c
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.core;
+
+
+/**
+ * ModuleActivator represents a module that plugs into the Tuscany system. Each
+ * module should provide an implementation of this interface and register the
+ * ModuleActivator implementation class by defining a file named
+ *
+ * "META-INF/services/org.apache.tuscany.core.ModuleActivator"
+ *
+ * The content of the file is the class name of the ModuleActivator implementation.
+ * The implementation class can have different flavors of constructors. The following
+ * order will be searched:
+ * <ul>
+ * <li>(ExtensionRegistry.class)
+ * <li>(ExtensionRegistry.class, Map.class)
+ * <li>()
+ * </ul>
+ *
+ *
+ *
+ *
+ * The same instance
+ * will be used to invoke all the methods during different phases of the module
+ * activation. Note that the start and stop methods defined by this interface
+ * take a reference to the Tuscany SCA runtime ExtensionPointRegistry. This
+ * gives the ModuleActivator the opportunity to add extension points to the
+ * registry as it is requested to start up and remove them when it is requested
+ * to shut down.
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.inheritfrom
+ */
+public interface ModuleActivator extends LifeCycleListener {
+
+ /**
+ * This method is invoked when the module is started by the Tuscany runtime.
+ * It can be used by this module to register extensions against extension
+ * points.
+ *
+ * @param registry The extension point registry
+ */
+ void start();
+
+ /**
+ * This method is invoked when the module is stopped by the Tuscany runtime.
+ * It can be used by this module to unregister extensions against the
+ * extension points.
+ *
+ * @param registry The extension point registry
+ */
+ void stop();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivatorExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivatorExtensionPoint.java
new file mode 100644
index 0000000000..7070d33f2c
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/ModuleActivatorExtensionPoint.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.core;
+
+import java.util.List;
+
+
+/**
+ * The extension point for the Tuscany module activator extensions.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ModuleActivatorExtensionPoint extends LifeCycleListener {
+
+ /**
+ * Add a module activator extension to the extension point
+ * @param activator The instance of the module activator
+ *
+ * @throws IllegalArgumentException if activator is null
+ */
+ void addModuleActivator(ModuleActivator activator);
+
+ /**
+ * Returns the module activator extensions.
+ * @return The module activator extensions
+ */
+ List<ModuleActivator> getModuleActivators();
+
+ /**
+ * Remove a module activator
+ * @param activator The module activator to remove
+ *
+ * @throws IllegalArgumentException if activator is null
+ */
+ void removeModuleActivator(ModuleActivator activator);
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/UtilityExtensionPoint.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/UtilityExtensionPoint.java
new file mode 100644
index 0000000000..7acc7fc409
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/core/UtilityExtensionPoint.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.core;
+
+
+/**
+ * The extension point for the Tuscany core utility extensions.
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.asclient
+ */
+public interface UtilityExtensionPoint extends LifeCycleListener {
+
+ /**
+ * Add a utility to the extension point
+ * @param utility The instance of the utility
+ *
+ * @throws IllegalArgumentException if utility is null
+ */
+ void addUtility(Object utility);
+
+ /**
+ * Add a utility to the extension point for a given key
+ * @param utility The instance of the utility
+ *
+ * @throws IllegalArgumentException if utility is null
+ */
+ void addUtility(Object key, Object utility);
+
+ /**
+ * Get the utility by the interface
+ * @param utilityType The lookup key (utility interface)
+ * @return The instance of the utility
+ *
+ * @throws IllegalArgumentException if utilityType is null
+ */
+ <T> T getUtility(Class<T> utilityType);
+
+ /**
+ * Get an instance of the utility by the interface and key
+ * @param utilityType The lookup key (utility interface)
+ * @param key A key associated with the utility, if it is null,
+ * then the utilityType is used as the key
+ * @return The instance of the utility
+ *
+ * @throws IllegalArgumentException if utilityType is null
+ */
+ <T> T getUtility(Class<T> utilityType, Object key);
+
+ /**
+ * Remove a utility
+ * @param utility The utility to remove
+ *
+ * @throws IllegalArgumentException if utility is null
+ */
+ void removeUtility(Object utility);
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ClassLoaderContext.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ClassLoaderContext.java
new file mode 100644
index 0000000000..598c897402
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ClassLoaderContext.java
@@ -0,0 +1,253 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.tuscany.sca.extensibility.impl.ClassLoaderDelegate;
+
+/**
+ * A utility that controls context class loaders
+ * @tuscany.spi.extension.asclient
+ */
+public class ClassLoaderContext {
+ private ClassLoader classLoader;
+
+ /**
+ * Create a context with the parent classloader and a list of service types that can be discovered
+ * by the {@link ServiceDiscovery}
+ * @param parent
+ * @param discovery
+ * @param serviceTypes
+ */
+ public ClassLoaderContext(ClassLoader parent, ServiceDiscovery discovery, Class<?>... serviceTypes) {
+ this(parent, getClassLoaders(discovery, serviceTypes));
+ }
+
+ private ClassLoaderContext(ClassLoader parent, List<ClassLoader> delegates) {
+ List<ClassLoader> loaders = new ArrayList<ClassLoader>(delegates);
+ loaders.remove(parent);
+ if (delegates.isEmpty()) {
+ classLoader = parent;
+ } else {
+ classLoader = new ClassLoaderDelegate(parent, loaders);
+ }
+ }
+
+ /**
+ * Create a context that is visible to the parent classloader as well as the list of classloaders
+ * @param parent
+ * @param delegates
+ */
+ public ClassLoaderContext(ClassLoader parent, ClassLoader... delegates) {
+ this(parent, Arrays.asList(delegates));
+ }
+
+ /**
+ * Create a context with the parent classloader and a list of service types that can be discovered
+ * by the {@link ServiceDiscovery}
+ * @param parent
+ * @param discovery
+ * @param serviceTypes
+ */
+ public ClassLoaderContext(ClassLoader parent, ServiceDiscovery discovery, String... serviceTypes) {
+ this(parent, getClassLoaders(discovery, serviceTypes));
+ }
+
+ public <T> T doPrivileged(PrivilegedAction<T> action) {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ if (tccl != classLoader) {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ }
+ try {
+ return action.run();
+ } finally {
+ if (tccl != classLoader) {
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+ }
+ }
+
+ public <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws PrivilegedActionException {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ if (tccl != classLoader) {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ }
+ try {
+ return action.run();
+ } catch (Exception e) {
+ throw new PrivilegedActionException(e);
+ } finally {
+ if (tccl != classLoader) {
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+ }
+ }
+
+ /**
+ * Set the thread context classloader (TCCL) to a classloader that delegates to a collection
+ * of classloaders
+ * @param parent The parent classloader
+ * @param delegates A list of classloaders to try
+ * @return The existing TCCL
+ */
+ public static ClassLoader setContextClassLoader(ClassLoader parent, ClassLoader... delegates) {
+ ClassLoaderContext context = new ClassLoaderContext(parent, delegates);
+ return context.setContextClassLoader();
+ }
+
+ /**
+ * Set the context classloader so that it can access the list of service providers
+ * @param parent The parent classloader
+ * @param serviceNames A list of service provider names
+ * @return The old TCCL if a new one is set, otherwise null
+ */
+ public static ClassLoader setContextClassLoader(ClassLoader parent,
+ ServiceDiscovery discovery,
+ String... serviceNames) {
+ ClassLoaderContext context = new ClassLoaderContext(parent, discovery, serviceNames);
+ return context.setContextClassLoader();
+ }
+
+ /**
+ * Set the context classloader so that it can access the list of service providers
+ * @param parent The parent classloader
+ * @param serviceNames A list of service provider names
+ * @return The old TCCL if a new one is set, otherwise null
+ */
+ public static ClassLoader setContextClassLoader(ClassLoader parent,
+ ServiceDiscovery discovery,
+ Class<?>... serviceTypes) {
+ ClassLoaderContext context = new ClassLoaderContext(parent, discovery, serviceTypes);
+ return context.setContextClassLoader();
+ }
+
+ public ClassLoader setContextClassLoader() {
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ if (tccl != classLoader) {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ return tccl;
+ } else {
+ return null;
+ }
+ }
+
+ private static ClassLoader getClassLoader(ServiceDiscovery discovery, String serviceProvider) {
+ try {
+ ServiceDeclaration sd = discovery.getServiceDeclaration(serviceProvider);
+ if (sd != null) {
+ try {
+ if (sd.loadClass() != null) {
+ return sd.loadClass().getClassLoader();
+ } else {
+ return new ClassLoaderImpl(sd);
+ }
+ } catch (ClassNotFoundException e) {
+ return new ClassLoaderImpl(sd);
+ }
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ return null;
+ }
+
+ private static List<ClassLoader> getClassLoaders(ServiceDiscovery discovery, String... serviceNames) {
+ List<ClassLoader> loaders = new ArrayList<ClassLoader>();
+ for (String sp : serviceNames) {
+ ClassLoader loader = getClassLoader(discovery, sp);
+ if (loader != null) {
+ if (!loaders.contains(loader)) {
+ loaders.add(loader);
+ }
+ }
+ }
+ ClassLoader tccl = discovery.getContextClassLoader();
+ if (!loaders.contains(tccl)) {
+ loaders.add(tccl);
+ }
+ return loaders;
+ }
+
+ private static ClassLoader getClassLoader(ServiceDiscovery discovery, Class<?> serviceType) {
+ try {
+ ServiceDeclaration sd = discovery.getServiceDeclaration(serviceType);
+ if (sd != null) {
+ return sd.loadClass().getClassLoader();
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ return null;
+ }
+
+ private static List<ClassLoader> getClassLoaders(ServiceDiscovery discovery, Class<?>... serviceTypes) {
+ List<ClassLoader> loaders = new ArrayList<ClassLoader>();
+ for (Class<?> serviceType : serviceTypes) {
+ ClassLoader classLoader = getClassLoader(discovery, serviceType);
+ if (classLoader != null && !loaders.contains(classLoader)) {
+ loaders.add(classLoader);
+ }
+ }
+ ClassLoader tccl = discovery.getContextClassLoader();
+ if (!loaders.contains(tccl)) {
+ loaders.add(tccl);
+ }
+ return loaders;
+ }
+
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ private static class ClassLoaderImpl extends ClassLoader {
+ private final ServiceDeclaration sd;
+
+ public ClassLoaderImpl(ServiceDeclaration sd) {
+ super();
+ this.sd = sd;
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return sd.loadClass(name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return sd.getResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ return sd.getResources(name);
+ }
+
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscoverer.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscoverer.java
new file mode 100644
index 0000000000..d40c37e4be
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscoverer.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A ServiceDiscoverer that find META-INF/services/... using the Context ClassLoader.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ContextClassLoaderServiceDiscoverer implements ServiceDiscoverer {
+ private static final Logger logger = Logger.getLogger(ContextClassLoaderServiceDiscoverer.class.getName());
+
+ public class ServiceDeclarationImpl implements ServiceDeclaration {
+ private URL url;
+ private String className;
+ private Class<?> javaClass;
+ private Map<String, String> attributes;
+
+ public ServiceDeclarationImpl(URL url, String className, Map<String, String> attributes) {
+ super();
+ this.url = url;
+ this.className = className;
+ this.attributes = attributes;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public URL getLocation() {
+ return url;
+ }
+
+ public Class<?> loadClass() throws ClassNotFoundException {
+ if (className == null) {
+ return null;
+ }
+ if (javaClass == null) {
+ javaClass = loadClass(className);
+ }
+ return javaClass;
+ }
+
+ public Class<?> loadClass(String className) throws ClassNotFoundException {
+ return Class.forName(className, false, classLoaderReference.get());
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Location: ").append(url);
+ sb.append(" ClassLoader: ").append(classLoaderReference.get());
+ sb.append(" Attributes: ").append(attributes);
+ return sb.toString();
+ }
+
+ public URL getResource(final String name) {
+ return AccessController.doPrivileged(new PrivilegedAction<URL>() {
+ public URL run() {
+ return classLoaderReference.get().getResource(name);
+ }
+ });
+ }
+
+ public boolean isAssignableTo(Class<?> serviceType) {
+ try {
+ loadClass();
+ } catch (ClassNotFoundException e) {
+ // Ignore
+ }
+ return (javaClass != null && serviceType.isAssignableFrom(javaClass));
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return Collections.enumeration(ContextClassLoaderServiceDiscoverer.this.getResources(name));
+ }
+ }
+
+ private WeakReference<ClassLoader> classLoaderReference;
+
+ public ContextClassLoaderServiceDiscoverer() {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ this.classLoaderReference = new WeakReference<ClassLoader>(classLoader);
+ }
+
+ public ContextClassLoaderServiceDiscoverer(ClassLoader classLoader) {
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ }
+ this.classLoaderReference = new WeakReference<ClassLoader>(classLoader);
+ }
+
+ public ClassLoader getContextClassLoader() {
+ //return classLoaderReference.get();
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ private Collection<URL> getResources(final String name) throws IOException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Collection<URL>>() {
+ public Collection<URL> run() throws IOException {
+ List<URL> urls = Collections.list(classLoaderReference.get().getResources(name));
+ // Eliminate the duplicate URLs (which can be found from child/parent classloaders)
+ return new HashSet<URL>(urls);
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (IOException)e.getException();
+ }
+ }
+
+ public ServiceDeclaration getServiceDeclaration(String name) throws IOException {
+ Collection<ServiceDeclaration> declarations = getServiceDeclarations(name);
+ if (declarations.isEmpty()) {
+ return null;
+ } else {
+ return declarations.iterator().next();
+ }
+ }
+
+ public Collection<ServiceDeclaration> getServiceDeclarations(String serviceName) {
+ Collection<ServiceDeclaration> descriptors = new HashSet<ServiceDeclaration>();
+
+ // http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/xpath/XPathFactory.html
+ boolean isPropertyFile = "javax.xml.xpath.XPathFactory".equals(serviceName);
+
+ String name = serviceName;
+ if (serviceName.startsWith("/")) {
+ // If the service name starts with /, treat it as the entry name
+ name = serviceName.substring(1);
+ } else {
+ // Use JDK SPI pattern
+ name = "META-INF/services/" + serviceName;
+ }
+
+ boolean debug = logger.isLoggable(Level.FINE);
+ try {
+ for (final URL url : getResources(name)) {
+ if (debug) {
+ logger.fine("Reading service provider file: " + url.toExternalForm());
+ }
+
+ for (Map<String, String> attributes : ServiceDeclarationParser.load(url, isPropertyFile)) {
+ String className = attributes.get("class");
+ ServiceDeclarationImpl descriptor = new ServiceDeclarationImpl(url, className, attributes);
+ descriptors.add(descriptor);
+ }
+ }
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ return descriptors;
+
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclaration.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclaration.java
new file mode 100644
index 0000000000..24b2c17a0f
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclaration.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+ * Service declaration using J2SE Jar service provider spec Classes specified
+ * inside this declaration are loaded using the ClassLoader used to read the
+ * configuration file corresponding to this declaration.
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.asclient
+ */
+public interface ServiceDeclaration {
+ /**
+ * Load a java class in the same context as the service definition
+ * @param className The class name
+ * @return The loaded class
+ * @throws ClassNotFoundException
+ */
+ Class<?> loadClass(String className) throws ClassNotFoundException;
+
+ /**
+ * Get the java class for the service impl
+ * @return The java class
+ */
+ Class<?> loadClass() throws ClassNotFoundException;
+
+ /**
+ * Get all attributes (name=value pairs) defined for the given entry
+ * @return All attributes keyed by name
+ */
+ Map<String, String> getAttributes();
+
+ /**
+ * Check if the service implementation class is a type of the service
+ * @param serviceType The java class of the service SPI
+ * @return true if the implementation class is a type of the service
+ */
+ boolean isAssignableTo(Class<?> serviceType);
+
+ URL getLocation();
+
+ /**
+ * Return the class name for the service provider
+ * @return
+ */
+ String getClassName();
+
+ URL getResource(String name);
+ Enumeration<URL> getResources(String name) throws IOException;
+
+ /**
+ * The service descriptor might be hashed
+ * @param obj Another object
+ * @return
+ */
+ boolean equals(Object obj);
+ /**
+ * The service descriptor might be hashed
+ * @return
+ */
+ int hashCode();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java
new file mode 100644
index 0000000000..f546e44dfb
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java
@@ -0,0 +1,375 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.namespace.QName;
+
+/**
+ * Parser for the service descriptors. The syntax of the service declaration is similar with the OSGi
+ * headers with the following exceptions:
+ * <ul>
+ * <li>Tuscany uses , and ; as the separator for attibutes
+ * <li>Tuscany
+ */
+public class ServiceDeclarationParser {
+
+ // private static final String PATH_SEPARATOR = ","; // OSGi style
+ private static final String PATH_SEPARATOR = "|";
+
+ // private static final String SEGMENT_SEPARATOR = ";"; // OSGi style
+ private static final String SEGMENT_SEPARATOR = ";,";
+
+ private static final String ATTRIBUTE_SEPARATOR = "=";
+ private static final String DIRECTIVE_SEPARATOR = ":=";
+
+ private static final char QUOTE_CHAR = '"';
+ private static final String QUOTE = "\"";
+
+ // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2,
+ // path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
+ public static List<Descriptor> parse(String header) {
+
+ if (header != null) {
+ if (header.length() == 0) {
+ throw new IllegalArgumentException("A header cannot be an empty string.");
+ }
+
+ String[] clauseStrings = parseDelimitedString(header, PATH_SEPARATOR);
+
+ List<Descriptor> completeList = new ArrayList<Descriptor>();
+ for (int i = 0; (clauseStrings != null) && (i < clauseStrings.length); i++) {
+ completeList.add(parseClause(clauseStrings[i]));
+ }
+
+ return completeList;
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Parse the declaration into a map of name/value pairs. The class name is added under "class"
+ * and directives are added using @<directiveName> as the key.
+ * @param declaration
+ * @return A map of attributes
+ */
+ public static Map<String, String> parseDeclaration(String declaration) {
+ List<Descriptor> descriptors = parse(declaration);
+ Descriptor descriptor = descriptors.get(0);
+ Map<String, String> map = new HashMap<String, String>();
+ map.putAll(descriptor.getAttributes());
+ map.put("class", descriptor.getValue());
+ for (Map.Entry<String, String> e : descriptor.getDirectives().entrySet()) {
+ // For directives, add @ as the prefix for the key
+ map.put("@" + e.getKey(), e.getValue());
+ }
+ return map;
+ }
+
+ // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
+ private static Descriptor parseClause(String clauseString) throws IllegalArgumentException {
+ // Break string into semi-colon delimited pieces.
+ String[] pieces = parseDelimitedString(clauseString, SEGMENT_SEPARATOR);
+
+ // Count the number of different paths; paths
+ // will not have an '=' in their string. This assumes
+ // that paths come first, before directives and
+ // attributes.
+ int pathCount = 0;
+ for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) {
+ if (pieces[pieceIdx].indexOf('=') >= 0) {
+ break;
+ }
+ pathCount++;
+ }
+
+ // Create an array of paths.
+ String[] paths = new String[pathCount];
+ System.arraycopy(pieces, 0, paths, 0, pathCount);
+
+ // Parse the directives/attributes.
+ Map<String, String> dirsMap = new HashMap<String, String>();
+ Map<String, String> attrsMap = new HashMap<String, String>();
+ int idx = -1;
+ String sep = null;
+ for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) {
+ // Check if it is a directive.
+ if ((idx = pieces[pieceIdx].indexOf(DIRECTIVE_SEPARATOR)) >= 0) {
+ sep = DIRECTIVE_SEPARATOR;
+ }
+ // Check if it is an attribute.
+ else if ((idx = pieces[pieceIdx].indexOf(ATTRIBUTE_SEPARATOR)) >= 0) {
+ sep = ATTRIBUTE_SEPARATOR;
+ }
+ // It is an error.
+ else {
+ throw new IllegalArgumentException("Not a directive/attribute: " + clauseString);
+ }
+
+ String key = pieces[pieceIdx].substring(0, idx).trim();
+ String value = pieces[pieceIdx].substring(idx + sep.length()).trim();
+
+ // Remove quotes, if value is quoted.
+ if (value.startsWith(QUOTE) && value.endsWith(QUOTE)) {
+ value = value.substring(1, value.length() - 1);
+ }
+
+ // Save the directive/attribute in the appropriate array.
+ if (sep.equals(DIRECTIVE_SEPARATOR)) {
+ // Check for duplicates.
+ if (dirsMap.get(key) != null) {
+ throw new IllegalArgumentException("Duplicate directive: " + key);
+ }
+ dirsMap.put(key, value);
+ } else {
+ // Check for duplicates.
+ if (attrsMap.get(key) != null) {
+ throw new IllegalArgumentException("Duplicate attribute: " + key);
+ }
+ attrsMap.put(key, value);
+ }
+ }
+
+ StringBuffer path = new StringBuffer();
+ for (int i = 0; i < paths.length; i++) {
+ path.append(paths[i]);
+ if (i != paths.length - 1) {
+ path.append(';');
+ }
+ }
+
+ Descriptor descriptor = new Descriptor();
+ descriptor.text = clauseString;
+ descriptor.value = path.toString();
+ descriptor.valueComponents = paths;
+ descriptor.attributes = attrsMap;
+ descriptor.directives = dirsMap;
+
+ return descriptor;
+ }
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This
+ * parser obeys quotes, so the delimiter character will be ignored if it is
+ * inside of a quote. This method assumes that the quote character is not
+ * included in the set of delimiter characters.
+ * @param value the delimited string to parse.
+ * @param delim the characters delimiting the tokens.
+ * @return an array of string tokens or null if there were no tokens.
+ **/
+ private static String[] parseDelimitedString(String value, String delim) {
+ if (value == null) {
+ value = "";
+ }
+
+ List<String> list = new ArrayList<String>();
+
+ int CHAR = 1;
+ int DELIMITER = 2;
+ int STARTQUOTE = 4;
+ int ENDQUOTE = 8;
+
+ StringBuffer sb = new StringBuffer();
+
+ int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+
+ boolean isDelimiter = (delim.indexOf(c) >= 0);
+ boolean isQuote = (c == QUOTE_CHAR);
+
+ if (isDelimiter && ((expecting & DELIMITER) > 0)) {
+ list.add(sb.toString().trim());
+ sb.delete(0, sb.length());
+ expecting = (CHAR | DELIMITER | STARTQUOTE);
+ } else if (isQuote && ((expecting & STARTQUOTE) > 0)) {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ } else if (isQuote && ((expecting & ENDQUOTE) > 0)) {
+ sb.append(c);
+ expecting = (CHAR | STARTQUOTE | DELIMITER);
+ } else if ((expecting & CHAR) > 0) {
+ sb.append(c);
+ } else {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+ }
+
+ if (sb.length() > 0) {
+ list.add(sb.toString().trim());
+ }
+
+ return (String[])list.toArray(new String[list.size()]);
+ }
+
+ public static class Descriptor {
+ private String text;
+ private String value;
+ private String[] valueComponents;
+ private Map<String, String> attributes;
+ private Map<String, String> directives;
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String[] getValueComponents() {
+ return valueComponents;
+ }
+
+ public void setValueComponents(String[] valueComponents) {
+ this.valueComponents = valueComponents;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public Map<String, String> getDirectives() {
+ return directives;
+ }
+
+ public String toString() {
+ return text;
+ }
+
+ }
+
+ /**
+ * Returns a QName object from a QName expressed as {ns}name
+ * or ns#name.
+ *
+ * @param qname
+ * @return
+ */
+ public static QName getQName(String qname) {
+ if (qname == null) {
+ return null;
+ }
+ qname = qname.trim();
+ if (qname.startsWith("{")) {
+ int h = qname.indexOf('}');
+ if (h != -1) {
+ return new QName(qname.substring(1, h), qname.substring(h + 1));
+ }
+ } else {
+ int h = qname.indexOf('#');
+ if (h != -1) {
+ return new QName(qname.substring(0, h), qname.substring(h + 1));
+ }
+ }
+ return new QName(qname);
+ }
+
+ public static Collection<Map<String, String>> load(final URL url, boolean isPropertyFile) throws IOException {
+ Collection<Map<String, String>> descriptors = new ArrayList<Map<String, String>>();
+
+ // Allow privileged access to open URL stream. Add FilePermission to added to security
+ // policy file.
+ InputStream is;
+ try {
+ is = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
+ public InputStream run() throws IOException {
+ URLConnection connection = url.openConnection();
+ // TUSCANY-2539
+ // Don't cache connections by default to stop Tuscany locking contribution jar files
+ // done here as this is one of the first places we open a stream and the only way to
+ // set the default is to set it on an instance of URLConnection
+ connection.setDefaultUseCaches(false);
+ connection.setUseCaches(false);
+ return url.openStream();
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (IOException)e.getException();
+ }
+ if (isPropertyFile) {
+ // Load as a property file
+ Properties props = new Properties();
+ props.load(is);
+ is.close();
+ for (Map.Entry<Object, Object> e : props.entrySet()) {
+ Map<String, String> attributes = new HashMap<String, String>();
+ String key = (String)e.getKey();
+ String value = (String)e.getValue();
+ // Unfortunately, the xalan file only has the classname
+ if (value == null || "".equals(value)) {
+ value = key;
+ key = "";
+ }
+ if (!"".equals(key)) {
+ attributes.put(key, value);
+ attributes.put("uri", key);
+ }
+ attributes.putAll(parseDeclaration(value));
+ descriptors.add(attributes);
+ }
+ return descriptors;
+ }
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(is));
+ while (true) {
+ String line = reader.readLine();
+ if (line == null)
+ break;
+ line = line.trim();
+ if (!line.startsWith("#") && !"".equals(line)) {
+ String reg = line.trim();
+
+ Map<String, String> attributes = parseDeclaration(reg);
+ descriptors.add(attributes);
+ }
+ }
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ return descriptors;
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscoverer.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscoverer.java
new file mode 100644
index 0000000000..aebd6cd3de
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscoverer.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * A SPI that allows different implementations of discovering service declarations
+ */
+public interface ServiceDiscoverer {
+
+ /**
+ * Get all service declarations for this interface
+ *
+ * @param name
+ * @return set of service declarations
+ * @throws IOException
+ */
+ public Collection<ServiceDeclaration> getServiceDeclarations(String name) throws IOException;
+
+ /**
+ * Get first service declaration class for the given interface
+ *
+ * @param name
+ * @return service implementation class
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ public ServiceDeclaration getServiceDeclaration(String name) throws IOException;
+
+ /**
+ * Get a classloader that can be used for thread context loader
+ * @return A classloader that can provide access to public classes and resources
+ */
+ public ClassLoader getContextClassLoader();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscovery.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscovery.java
new file mode 100644
index 0000000000..973664f382
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDiscovery.java
@@ -0,0 +1,306 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.extensibility.impl.LDAPFilter;
+
+/**
+ * Service discovery for Tuscany based on J2SE Jar service provider spec.
+ * Services are described using configuration files in META-INF/services.
+ * Service description specifies a class name followed by optional properties.
+ *
+ * TODO: this is broken as it uses a static INSTANCE but non-static serviceAttributes
+ * and discoverer so the same INSTANCE gets used across NodeFactories and picks up
+ * old values
+ *
+ * @version $Rev$ $Date$
+ * @tuscany.spi.extension.asclient
+ */
+public final class ServiceDiscovery implements ServiceDiscoverer {
+ private final static Logger logger = Logger.getLogger(ServiceDiscovery.class.getName());
+ private final static ServiceDiscovery INSTANCE = new ServiceDiscovery();
+
+ private final Map<String, Map<String, String>> serviceAttributes = new HashMap<String, Map<String, String>>();
+ private ServiceDiscoverer discoverer;
+
+ private ServiceDiscovery() {
+ super();
+ }
+
+ private ServiceDiscovery(ServiceDiscoverer discoverer) {
+ super();
+ this.discoverer = discoverer;
+ }
+
+ /**
+ * Get an instance of Service discovery, one instance is created per
+ * ClassLoader that this class is loaded from
+ *
+ * @return
+ */
+ public static ServiceDiscovery getInstance() {
+ return INSTANCE;
+ }
+
+ public static ServiceDiscovery getInstance(ServiceDiscoverer discoverer) {
+ return new ServiceDiscovery(discoverer);
+ }
+
+ public ServiceDiscoverer getServiceDiscoverer() {
+ if (discoverer != null) {
+ return discoverer;
+ }
+ try {
+ // FIXME: This is a hack to trigger the activation of the extensibility-equinox bundle in OSGi
+ Class.forName("org.apache.tuscany.sca.extensibility.equinox.EquinoxServiceDiscoverer");
+ if (discoverer != null) {
+ return discoverer;
+ }
+ } catch (Throwable e) {
+ }
+ discoverer = new ContextClassLoaderServiceDiscoverer(getClass().getClassLoader());
+ return discoverer;
+ }
+
+ public void setServiceDiscoverer(ServiceDiscoverer sd) {
+ if (discoverer != null && sd != null) {
+ logger.warning("ServiceDiscoverer is reset to " + sd);
+ }
+ discoverer = sd;
+ }
+
+ public Collection<ServiceDeclaration> getServiceDeclarations(String name) throws IOException {
+ return getServiceDeclarations(name, false);
+ }
+
+ public Collection<ServiceDeclaration> getServiceDeclarations(String name, boolean byRanking) throws IOException {
+ Collection<ServiceDeclaration> declarations = getServiceDiscoverer().getServiceDeclarations(name);
+ // Check if any of the service declarations has attributes that are overrided
+ if (!serviceAttributes.isEmpty()) {
+ for (ServiceDeclaration declaration : declarations) {
+ Map<String, String> attrs = getAttributes(name);
+ if (attrs != null) {
+ declaration.getAttributes().putAll(attrs);
+ }
+ }
+ }
+ if (!byRanking) {
+ return declarations;
+ }
+ if (!declarations.isEmpty()) {
+ List<ServiceDeclaration> declarationList = new ArrayList<ServiceDeclaration>(declarations);
+ /*
+ for (ServiceDeclaration sd1 : declarations) {
+ for (Iterator<ServiceDeclaration> i = declarationList.iterator(); i.hasNext();) {
+ ServiceDeclaration sd2 = i.next();
+ if (sd1 != sd2 && sd1.getAttributes().equals(sd2.getAttributes())) {
+ logger
+ .warning("Duplicate service declarations: " + sd1.getLocation() + "," + sd2.getLocation());
+ i.remove();
+ }
+ }
+ }
+ */
+ Collections.sort(declarationList, ServiceComparator.DESCENDING_ORDER);
+ return declarationList;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Get the service declaration. If there are more than one services, the one with highest ranking will
+ * be returned.
+ */
+ public ServiceDeclaration getServiceDeclaration(final String name) throws IOException {
+ Collection<ServiceDeclaration> declarations = getServiceDeclarations(name, true);
+ if (!declarations.isEmpty()) {
+ // List<ServiceDeclaration> declarationList = new ArrayList<ServiceDeclaration>(declarations);
+ // Collections.sort(declarationList, ServiceComparator.DESCENDING_ORDER);
+ return declarations.iterator().next();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get service declarations that are filtered by the service type. In an OSGi runtime, there
+ * might be different versions of the services
+ * @param serviceType
+ * @return
+ * @throws IOException
+ */
+ public Collection<ServiceDeclaration> getServiceDeclarations(Class<?> serviceType, boolean byRanking)
+ throws IOException {
+ Collection<ServiceDeclaration> sds = getServiceDeclarations(serviceType.getName(), byRanking);
+ for (Iterator<ServiceDeclaration> i = sds.iterator(); i.hasNext();) {
+ ServiceDeclaration sd = i.next();
+ if (!sd.isAssignableTo(serviceType)) {
+ logger.log(Level.WARNING, "Service provider {0} is not a type of {1}", new Object[] {sd,serviceType.getName()});
+ i.remove();
+ }
+ }
+ return sds;
+ }
+
+ /**
+ * Discover all service providers that are compatible with the service type
+ * @param serviceType
+ * @return
+ * @throws IOException
+ */
+ public Collection<ServiceDeclaration> getServiceDeclarations(Class<?> serviceType) throws IOException {
+ return getServiceDeclarations(serviceType, false);
+ }
+
+ /**
+ * Discover all service providers that are compatible with the service type and match the filter
+ * @param serviceType
+ * @param filter
+ * @return
+ * @throws IOException
+ */
+ public Collection<ServiceDeclaration> getServiceDeclarations(Class<?> serviceType, String filter) throws IOException {
+ Collection<ServiceDeclaration> sds = getServiceDeclarations(serviceType, false);
+ Collection<ServiceDeclaration> filtered = new ArrayList<ServiceDeclaration>();
+ LDAPFilter filterImpl = LDAPFilter.newInstance(filter);
+ for(ServiceDeclaration sd: sds) {
+ if(filterImpl.match(sd.getAttributes())) {
+ filtered.add(sd);
+ }
+ }
+ return filtered;
+ }
+
+ /**
+ * @param serviceName
+ * @param filter
+ * @return
+ * @throws IOException
+ */
+ public Collection<ServiceDeclaration> getServiceDeclarations(String serviceName, String filter) throws IOException {
+ Collection<ServiceDeclaration> sds = getServiceDeclarations(serviceName, false);
+ Collection<ServiceDeclaration> filtered = new ArrayList<ServiceDeclaration>();
+ LDAPFilter filterImpl = LDAPFilter.newInstance(filter);
+ for(ServiceDeclaration sd: sds) {
+ if(filterImpl.match(sd.getAttributes())) {
+ filtered.add(sd);
+ }
+ }
+ return filtered;
+ }
+
+ public ServiceDeclaration getServiceDeclaration(Class<?> serviceType) throws IOException {
+ Collection<ServiceDeclaration> sds = getServiceDeclarations(serviceType, true);
+ if (sds.isEmpty()) {
+ return null;
+ } else {
+ return sds.iterator().next();
+ }
+ }
+
+ /**
+ * Compare service declarations by ranking
+ */
+ private static class ServiceComparator implements Comparator<ServiceDeclaration> {
+ private final static Comparator<ServiceDeclaration> DESCENDING_ORDER = new ServiceComparator();
+
+ public int compare(ServiceDeclaration o1, ServiceDeclaration o2) {
+ int rank1 = 0;
+ String r1 = o1.getAttributes().get("ranking");
+ if (r1 != null) {
+ rank1 = Integer.parseInt(r1);
+ }
+ int rank2 = 0;
+ String r2 = o2.getAttributes().get("ranking");
+ if (r2 != null) {
+ rank2 = Integer.parseInt(r2);
+ }
+ return rank2 - rank1; // descending
+ }
+ }
+
+ public ClassLoader getContextClassLoader() {
+ return discoverer.getContextClassLoader();
+ }
+
+ /**
+ * Set the attributes for a given service type
+ * @param serviceType
+ * @param attributes
+ */
+ public void setAttribute(String serviceType, Map<String, String> attributes) {
+ serviceAttributes.put(serviceType, attributes);
+ }
+
+ /**
+ * Set an attribute to the given value for a service type
+ * @param serviceType The service type
+ * @param attribute The attribute name
+ * @param value The attribute value
+ */
+ public void setAttribute(String serviceType, String attribute, String value) {
+ Map<String, String> attributes = serviceAttributes.get(serviceType);
+ if (attributes == null) {
+ attributes = new HashMap<String, String>();
+ serviceAttributes.put(serviceType, attributes);
+ }
+ attributes.put(attribute, value);
+ }
+
+ /**
+ * Return a map of attributes for a given service type
+ * @param serviceType
+ * @return
+ */
+ public Map<String, String> getAttributes(String serviceType) {
+ return serviceAttributes.get(serviceType);
+ }
+
+ /**
+ * Remove the duplicate service declarations. The duplication happens when we have the same jar from more than one entries on the classpath
+ * @param declarations
+ * @return
+ */
+ public static Collection<ServiceDeclaration> removeDuplicateDeclarations(Collection<ServiceDeclaration> declarations) {
+ // Use LinkedHashMap to maintain the insertion order
+ Map<String, ServiceDeclaration> map = new LinkedHashMap<String, ServiceDeclaration>();
+ for (ServiceDeclaration sd : declarations) {
+ ServiceDeclaration existed = map.put(sd.getClassName(), sd);
+ if (existed != null) {
+ logger.warning("Duplicate service declaration is ignored: " + existed + " <-> " + sd);
+ }
+ }
+ return map.values();
+ }
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceHelper.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceHelper.java
new file mode 100644
index 0000000000..7f38aa6d06
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceHelper.java
@@ -0,0 +1,226 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+
+/**
+ * A helper for handling service lifecycle and instantiations
+ * @tuscany.spi.extension.asclient
+ */
+public class ServiceHelper {
+ private ServiceHelper() {
+ }
+
+ /**
+ * Start the service instance
+ * @param instance
+ */
+ public static boolean start(Object instance) {
+ if (instance instanceof LifeCycleListener) {
+ ((LifeCycleListener)instance).start();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop the service instance
+ * @param instance
+ */
+ public static boolean stop(Object instance) {
+ if (instance instanceof LifeCycleListener) {
+ ((LifeCycleListener)instance).stop();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop a collection of service instances
+ * @param instances
+ */
+ public static void stop(Collection<? extends Object> instances) {
+ if (instances == null) {
+ return;
+ }
+ for (Object instance : instances) {
+ if (instance instanceof LifeCycleListener) {
+ ((LifeCycleListener)instance).stop();
+ }
+ }
+ }
+
+ /**
+ * Create a service instance with one parameter
+ * @param cls The service type
+ * @param parameterType The parameter type
+ * @param parameter The parameter value
+ * @return The newly created service instance
+ * @throws Exception
+ */
+ public static <T> T newInstance(Class<T> cls, Class<?> parameterType, Object parameter) throws Exception {
+ Constructor<T> constructor = cls.getConstructor(parameterType);
+ return constructor.newInstance(parameter);
+ }
+
+ /**
+ * Create a service instance with an array of parameters
+ * @param cls The service type
+ * @param parameterTypes An array of parameter types
+ * @param parameters An array of parameter values
+ * @return The newly created service instance
+ * @throws Exception
+ */
+ public static <T> T newInstance(Class<T> cls, Class<?> parameterTypes[], Object... parameters) throws Exception {
+ Constructor<T> constructor = cls.getConstructor(parameterTypes);
+ return constructor.newInstance(parameters);
+ }
+
+ /**
+ * Create a service instance with the default no-arg constructor
+ * @param cls The service type
+ * @return The newly created service instance
+ * @throws Exception
+ */
+ public static <T> T newInstance(Class<T> cls) throws Exception {
+ Constructor<T> constructor = cls.getConstructor();
+ return constructor.newInstance();
+ }
+
+ private final static Class<?>[] ARG_TYPES = new Class<?>[] {ExtensionPointRegistry.class, Map.class};
+
+ /**
+ * Create a service instance from the service declaration
+ * @param <T>
+ * @param registry The extension point registry
+ * @param sd The service declaration
+ * @return The newly created service instance
+ * @throws Exception
+ */
+ public static <T> T newInstance(ExtensionPointRegistry registry, ServiceDeclaration sd) throws Exception {
+ Class<T> cls = (Class<T>)sd.loadClass();
+ T instance = null;
+ try {
+ // Try constructor(ExtensionPointRegistry.class)
+ instance = newInstance(cls, ExtensionPointRegistry.class, registry);
+ } catch (NoSuchMethodException e) {
+ try {
+ // Try Try constructor(ExtensionPointRegistry.class, Map.class)
+ instance = newInstance(cls, ARG_TYPES, registry, sd.getAttributes());
+ } catch (NoSuchMethodException e1) {
+ // Try constructor()
+ instance = newInstance(cls);
+ }
+ }
+ return instance;
+ }
+
+ public static <T> T newLazyInstance(ExtensionPointRegistry registry, ServiceDeclaration sd, Class<T> serviceType) {
+ return serviceType.cast(Proxy.newProxyInstance(serviceType.getClassLoader(),
+ new Class<?>[] {serviceType, LifeCycleListener.class},
+ new InvocationHandlerImpl(registry, serviceType, sd)));
+ }
+
+ private static class InvocationHandlerImpl implements InvocationHandler {
+ private ExtensionPointRegistry registry;
+ private Class<?> type;
+ private ServiceDeclaration sd;
+ private Object instance;
+
+ private final static Method STOP_METHOD = getMethod(LifeCycleListener.class, "stop");
+ private final static Method START_METHOD = getMethod(LifeCycleListener.class, "start");
+ private final static Method EQUALS_METHOD = getMethod(Object.class, "equals");
+ private final static Method HASHCODE_METHOD = getMethod(Object.class, "hashCode");
+ private final static Method TOSTRING_METHOD = getMethod(Object.class, "toString");
+
+ private static Method getMethod(Class<?> type, String name) {
+ Method[] methods = type.getMethods();
+ for (Method method : methods) {
+ if (name.equals(method.getName())) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ public InvocationHandlerImpl(ExtensionPointRegistry registry, Class<?> type, ServiceDeclaration sd) {
+ super();
+ this.registry = registry;
+ this.sd = sd;
+ this.type = type;
+ }
+
+ private Object getAttribute(Method method) throws Exception {
+ if (method.getParameterTypes().length != 0) {
+ return null;
+ }
+ String name = method.getName();
+ if (name.equals("getModelType") && method.getReturnType() == Class.class) {
+ return sd.loadClass(sd.getAttributes().get("model"));
+ } else if (name.equals("getArtifactType")) {
+ return ServiceDeclarationParser.getQName(sd.getAttributes().get("qname"));
+ }
+ return null;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ synchronized (this) {
+ // Check if the method is to get the qname/model attribute
+ Object value = getAttribute(method);
+ if (value != null) {
+ return value;
+ }
+ if (instance == null && method.getDeclaringClass() == type) {
+ // Only initialize the instance when a method on the service type is invoked
+ instance = newInstance(registry, sd);
+ start(instance);
+ }
+ if (method.equals(EQUALS_METHOD)) {
+ return proxy == args[0];
+ } else if (method.equals(HASHCODE_METHOD)) {
+ return System.identityHashCode(proxy);
+ } else if (method.equals(TOSTRING_METHOD)) {
+ return "Proxy: " + sd.toString();
+ }
+ if (instance == null) {
+ return null;
+ }
+ }
+ if (method.equals(STOP_METHOD)) {
+ stop(instance);
+ return null;
+ } else if (method.equals(START_METHOD)) {
+ // Skip the start()
+ return null;
+ } else {
+ return method.invoke(instance, args);
+ }
+ }
+ }
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/ClassLoaderDelegate.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/ClassLoaderDelegate.java
new file mode 100644
index 0000000000..d052d6b10f
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/ClassLoaderDelegate.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tuscany.sca.extensibility.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A classloader that can delegate to a list of other classloaders
+ */
+public class ClassLoaderDelegate extends ClassLoader {
+ private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
+
+ /**
+ * @param parent The parent classloaders
+ * @param loaders A list of classloaders to be used to load classes or resources
+ */
+ public ClassLoaderDelegate(ClassLoader parent, Collection<ClassLoader> loaders) {
+ super(parent);
+ if (loaders != null) {
+ for (ClassLoader cl : loaders) {
+ if (cl != null && cl != parent && !classLoaders.contains(cl)) {
+ this.classLoaders.add(cl);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String className) throws ClassNotFoundException {
+ for (ClassLoader delegate : classLoaders) {
+ try {
+ return delegate.loadClass(className);
+ } catch (ClassNotFoundException e) {
+ continue;
+ }
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ @Override
+ protected URL findResource(String resName) {
+ for (ClassLoader delegate : classLoaders) {
+ URL url = delegate.getResource(resName);
+ if (url != null) {
+ return url;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String resName) throws IOException {
+ Set<URL> urlSet = new HashSet<URL>();
+ for (ClassLoader delegate : classLoaders) {
+ Enumeration<URL> urls = delegate.getResources(resName);
+ if (urls != null) {
+ while (urls.hasMoreElements()) {
+ urlSet.add(urls.nextElement());
+ }
+ }
+ }
+ return Collections.enumeration(urlSet);
+ }
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/InvalidSyntaxException.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/InvalidSyntaxException.java
new file mode 100644
index 0000000000..77f6f3e8db
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/InvalidSyntaxException.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.extensibility.impl;
+
+/**
+ * A Framework exception used to indicate that a filter string has an invalid
+ * syntax.
+ *
+ * <p>
+ * An <code>InvalidSyntaxException</code> object indicates that a filter
+ * string parameter has an invalid syntax and cannot be parsed.
+ *
+ * <p>
+ * This exception conforms to the general purpose exception chaining mechanism.
+ *
+ */
+
+public class InvalidSyntaxException extends RuntimeException {
+ static final long serialVersionUID = -4295194420816491875L;
+ /**
+ * The invalid filter string.
+ */
+ private final String filter;
+
+ /**
+ * Creates an exception of type <code>InvalidSyntaxException</code>.
+ *
+ * <p>
+ * This method creates an <code>InvalidSyntaxException</code> object with
+ * the specified message and the filter string which generated the
+ * exception.
+ *
+ * @param msg The message.
+ * @param filter The invalid filter string.
+ */
+ public InvalidSyntaxException(String msg, String filter) {
+ super(msg);
+ this.filter = filter;
+ }
+
+ /**
+ * Creates an exception of type <code>InvalidSyntaxException</code>.
+ *
+ * <p>
+ * This method creates an <code>InvalidSyntaxException</code> object with
+ * the specified message and the filter string which generated the
+ * exception.
+ *
+ * @param msg The message.
+ * @param filter The invalid filter string.
+ * @param cause The cause of this exception.
+ * @since 1.3
+ */
+ public InvalidSyntaxException(String msg, String filter, Throwable cause) {
+ super(msg, cause);
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the filter string that generated the
+ * <code>InvalidSyntaxException</code> object.
+ *
+ * @return The invalid filter string.
+ * @see BundleContext#getServiceReferences
+ * @see BundleContext#addServiceListener(ServiceListener,String)
+ */
+ public String getFilter() {
+ return filter;
+ }
+} \ No newline at end of file
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/LDAPFilter.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/LDAPFilter.java
new file mode 100644
index 0000000000..8d3630baa4
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/impl/LDAPFilter.java
@@ -0,0 +1,1373 @@
+/*
+ * Copyright (c) OSGi Alliance (2005, 2009). All Rights Reserved.
+ *
+ * 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.tuscany.sca.extensibility.impl;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.tuscany.sca.extensibility.ServiceDeclaration;
+
+
+/**
+ * This code is derived from <a href="http://svn.apache.org/repos/asf/felix/releases/org.osgi.core-1.4.0/src/main/java/org/osgi/framework/FrameworkUtil.java">FrameworkUtil</a>
+ * <p>
+ * RFC 1960-based Filter. Filter objects can be created by calling the
+ * constructor with the desired filter string. A Filter object can be called
+ * numerous times to determine if the match argument matches the filter
+ * string that was used to create the Filter object.
+ *
+ * <p>
+ * The syntax of a filter string is the string representation of LDAP search
+ * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
+ * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
+ * be noted that RFC 2254: <i>A String Representation of LDAP Search
+ * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
+ * RFC 1960 but only adds extensible matching and is not applicable for this
+ * API.
+ *
+ * <p>
+ * The string representation of an LDAP search filter is defined by the
+ * following grammar. It uses a prefix format.
+ *
+ * <pre>
+ * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
+ * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
+ * &lt;and&gt; ::= '&amp;' &lt;filterlist&gt;
+ * &lt;or&gt; ::= '|' &lt;filterlist&gt;
+ * &lt;not&gt; ::= '!' &lt;filter&gt;
+ * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
+ * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
+ * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
+ * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
+ * &lt;equal&gt; ::= '='
+ * &lt;approx&gt; ::= '&tilde;='
+ * &lt;greater&gt; ::= '&gt;='
+ * &lt;less&gt; ::= '&lt;='
+ * &lt;present&gt; ::= &lt;attr&gt; '=*'
+ * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
+ * &lt;initial&gt; ::= NULL | &lt;value&gt;
+ * &lt;any&gt; ::= '*' &lt;starval&gt;
+ * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
+ * &lt;final&gt; ::= NULL | &lt;value&gt;
+ * </pre>
+ *
+ * <code>&lt;attr&gt;</code> is a string representing an attribute, or key,
+ * in the properties objects of the registered services. Attribute names are
+ * not case sensitive; that is cn and CN both refer to the same attribute.
+ * <code>&lt;value&gt;</code> is a string representing the value, or part of
+ * one, of a key in the properties objects of the registered services. If a
+ * <code>&lt;value&gt;</code> must contain one of the characters '
+ * <code>*</code>' or '<code>(</code>' or '<code>)</code>', these characters
+ * should be escaped by preceding them with the backslash '<code>\</code>'
+ * character. Note that although both the <code>&lt;substring&gt;</code> and
+ * <code>&lt;present&gt;</code> productions can produce the <code>'attr=*'</code>
+ * construct, this construct is used only to denote a presence filter.
+ *
+ * <p>
+ * Examples of LDAP filters are:
+ *
+ * <pre>
+ * &quot;(cn=Babs Jensen)&quot;
+ * &quot;(!(cn=Tim Howes))&quot;
+ * &quot;(&amp;(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
+ * &quot;(o=univ*of*mich*)&quot;
+ * </pre>
+ *
+ * <p>
+ * The approximate match (<code>~=</code>) is implementation specific but
+ * should at least ignore case and white space differences. Optional are
+ * codes like soundex or other smart "closeness" comparisons.
+ *
+ * <p>
+ * Comparison of values is not straightforward. Strings are compared
+ * differently than numbers and it is possible for a key to have multiple
+ * values. Note that that keys in the match argument must always be strings.
+ * The comparison is defined by the object type of the key's value. The
+ * following rules apply for comparison:
+ *
+ * <blockquote>
+ * <TABLE BORDER=0>
+ * <TR>
+ * <TD><b>Property Value Type </b></TD>
+ * <TD><b>Comparison Type</b></TD>
+ * </TR>
+ * <TR>
+ * <TD>String</TD>
+ * <TD>String comparison</TD>
+ * </TR>
+ * <TR valign=top>
+ * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
+ * <TD>numerical comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Character</TD>
+ * <TD>character comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Boolean</TD>
+ * <TD>equality comparisons only</TD>
+ * </TR>
+ * <TR>
+ * <TD>[] (array)</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * <TR>
+ * <TD>Collection</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * </TABLE>
+ * Note: arrays of primitives are also supported. </blockquote>
+ *
+ * A filter matches a key that has multiple values if it matches at least
+ * one of those values. For example,
+ *
+ * <pre>
+ * Dictionary d = new Hashtable();
+ * d.put(&quot;cn&quot;, new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
+ * </pre>
+ *
+ * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
+ *
+ * <p>
+ * A filter component that references a key having an unrecognizable data
+ * type will evaluate to <code>false</code> .
+ */
+public class LDAPFilter {
+ /* filter operators */
+ private static final int EQUAL = 1;
+ private static final int APPROX = 2;
+ private static final int GREATER = 3;
+ private static final int LESS = 4;
+ private static final int PRESENT = 5;
+ private static final int SUBSTRING = 6;
+ private static final int AND = 7;
+ private static final int OR = 8;
+ private static final int NOT = 9;
+
+ /** filter operation */
+ private final int op;
+ /** filter attribute or null if operation AND, OR or NOT */
+ private final String attr;
+ /** filter operands */
+ private final Object value;
+
+ /* normalized filter string for Filter object */
+ private transient volatile String filterString;
+
+ /**
+ * Constructs a {@link LDAPFilter} object. This filter object may be
+ * used to match a {@link ServiceReference} or a Dictionary.
+ *
+ * <p>
+ * If the filter cannot be parsed, an {@link InvalidSyntaxException}
+ * will be thrown with a human readable message where the filter became
+ * unparsable.
+ *
+ * @param filterString the filter string.
+ * @exception InvalidSyntaxException If the filter parameter contains an
+ * invalid filter string that cannot be parsed.
+ */
+ public static LDAPFilter newInstance(String filterString) throws InvalidSyntaxException {
+ return new Parser(filterString).parse();
+ }
+
+ LDAPFilter(int operation, String attr, Object value) {
+ this.op = operation;
+ this.attr = attr;
+ this.value = value;
+ }
+
+ /**
+ * Filter using a <code>Dictionary</code>. This <code>Filter</code> is
+ * executed using the specified <code>Dictionary</code>'s keys and
+ * values. The keys are case insensitively matched with this
+ * <code>Filter</code>.
+ *
+ * @param dictionary The <code>Dictionary</code> whose keys are used in
+ * the match.
+ * @return <code>true</code> if the <code>Dictionary</code>'s keys and
+ * values match this filter; <code>false</code> otherwise.
+ * @throws IllegalArgumentException If <code>dictionary</code> contains
+ * case variants of the same key name.
+ */
+ public boolean match(Dictionary dictionary) {
+ return match0(new CaseInsensitiveDictionary(dictionary));
+ }
+
+ public boolean match(Map map) {
+ Properties props = new Properties();
+ props.putAll(map);
+ return match0(new CaseInsensitiveDictionary(props));
+ }
+
+ public static boolean matches(ServiceDeclaration declaration, String filter) {
+ if (filter == null) {
+ return true;
+ }
+ LDAPFilter filterImpl = newInstance(filter);
+ return filterImpl.match(declaration.getAttributes());
+ }
+
+ /**
+ * Filter with case sensitivity using a <code>Dictionary</code>. This
+ * <code>Filter</code> is executed using the specified
+ * <code>Dictionary</code>'s keys and values. The keys are case
+ * sensitively matched with this <code>Filter</code>.
+ *
+ * @param dictionary The <code>Dictionary</code> whose keys are used in
+ * the match.
+ * @return <code>true</code> if the <code>Dictionary</code>'s keys and
+ * values match this filter; <code>false</code> otherwise.
+ * @since 1.3
+ */
+ public boolean matchCase(Dictionary dictionary) {
+ return match0(dictionary);
+ }
+
+ /**
+ * Returns this <code>Filter</code>'s filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This <code>Filter</code>'s filter string.
+ */
+ public String toString() {
+ String result = filterString;
+ if (result == null) {
+ filterString = result = normalize();
+ }
+ return result;
+ }
+
+ /**
+ * Returns this <code>Filter</code>'s normalized filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This <code>Filter</code>'s filter string.
+ */
+ private String normalize() {
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+
+ switch (op) {
+ case AND: {
+ sb.append('&');
+
+ LDAPFilter[] filters = (LDAPFilter[])value;
+ for (int i = 0, size = filters.length; i < size; i++) {
+ sb.append(filters[i].normalize());
+ }
+
+ break;
+ }
+
+ case OR: {
+ sb.append('|');
+
+ LDAPFilter[] filters = (LDAPFilter[])value;
+ for (int i = 0, size = filters.length; i < size; i++) {
+ sb.append(filters[i].normalize());
+ }
+
+ break;
+ }
+
+ case NOT: {
+ sb.append('!');
+ LDAPFilter filter = (LDAPFilter)value;
+ sb.append(filter.normalize());
+
+ break;
+ }
+
+ case SUBSTRING: {
+ sb.append(attr);
+ sb.append('=');
+
+ String[] substrings = (String[])value;
+
+ for (int i = 0, size = substrings.length; i < size; i++) {
+ String substr = substrings[i];
+
+ if (substr == null) /* * */{
+ sb.append('*');
+ } else /* xxx */{
+ sb.append(encodeValue(substr));
+ }
+ }
+
+ break;
+ }
+ case EQUAL: {
+ sb.append(attr);
+ sb.append('=');
+ sb.append(encodeValue((String)value));
+
+ break;
+ }
+ case GREATER: {
+ sb.append(attr);
+ sb.append(">=");
+ sb.append(encodeValue((String)value));
+
+ break;
+ }
+ case LESS: {
+ sb.append(attr);
+ sb.append("<=");
+ sb.append(encodeValue((String)value));
+
+ break;
+ }
+ case APPROX: {
+ sb.append(attr);
+ sb.append("~=");
+ sb.append(encodeValue(approxString((String)value)));
+
+ break;
+ }
+
+ case PRESENT: {
+ sb.append(attr);
+ sb.append("=*");
+
+ break;
+ }
+ }
+
+ sb.append(')');
+
+ return sb.toString();
+ }
+
+ /**
+ * Compares this <code>Filter</code> to another <code>Filter</code>.
+ *
+ * <p>
+ * This implementation returns the result of calling
+ * <code>this.toString().equals(obj.toString()</code>.
+ *
+ * @param obj The object to compare against this <code>Filter</code>.
+ * @return If the other object is a <code>Filter</code> object, then
+ * returns the result of calling
+ * <code>this.toString().equals(obj.toString()</code>;
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof LDAPFilter)) {
+ return false;
+ }
+
+ return this.toString().equals(obj.toString());
+ }
+
+ /**
+ * Returns the hashCode for this <code>Filter</code>.
+ *
+ * <p>
+ * This implementation returns the result of calling
+ * <code>this.toString().hashCode()</code>.
+ *
+ * @return The hashCode of this <code>Filter</code>.
+ */
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ /**
+ * Internal match routine. Dictionary parameter must support
+ * case-insensitive get.
+ *
+ * @param properties A dictionary whose keys are used in the match.
+ * @return If the Dictionary's keys match the filter, return
+ * <code>true</code>. Otherwise, return <code>false</code>.
+ */
+ private boolean match0(Dictionary properties) {
+ switch (op) {
+ case AND: {
+ LDAPFilter[] filters = (LDAPFilter[])value;
+ for (int i = 0, size = filters.length; i < size; i++) {
+ if (!filters[i].match0(properties)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ case OR: {
+ LDAPFilter[] filters = (LDAPFilter[])value;
+ for (int i = 0, size = filters.length; i < size; i++) {
+ if (filters[i].match0(properties)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ case NOT: {
+ LDAPFilter filter = (LDAPFilter)value;
+
+ return !filter.match0(properties);
+ }
+
+ case SUBSTRING:
+ case EQUAL:
+ case GREATER:
+ case LESS:
+ case APPROX: {
+ Object prop = (properties == null) ? null : properties.get(attr);
+
+ return compare(op, prop, value);
+ }
+
+ case PRESENT: {
+ Object prop = (properties == null) ? null : properties.get(attr);
+
+ return prop != null;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Encode the value string such that '(', '*', ')' and '\' are escaped.
+ *
+ * @param value unencoded value string.
+ * @return encoded value string.
+ */
+ private static String encodeValue(String value) {
+ boolean encoded = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++) {
+ char c = output[i];
+
+ switch (c) {
+ case '(':
+ case '*':
+ case ')':
+ case '\\': {
+ output[cursor] = '\\';
+ cursor++;
+ encoded = true;
+
+ break;
+ }
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return encoded ? new String(output, 0, cursor) : value;
+ }
+
+ private boolean compare(int operation, Object value1, Object value2) {
+ if (value1 == null) {
+ return false;
+ }
+ if (value1 instanceof String) {
+ return compare_String(operation, (String)value1, value2);
+ }
+
+ Class clazz = value1.getClass();
+ if (clazz.isArray()) {
+ Class type = clazz.getComponentType();
+ if (type.isPrimitive()) {
+ return compare_PrimitiveArray(operation, type, value1, value2);
+ }
+ return compare_ObjectArray(operation, (Object[])value1, value2);
+ }
+ if (value1 instanceof Collection) {
+ return compare_Collection(operation, (Collection)value1, value2);
+ }
+ if (value1 instanceof Integer) {
+ return compare_Integer(operation, ((Integer)value1).intValue(), value2);
+ }
+ if (value1 instanceof Long) {
+ return compare_Long(operation, ((Long)value1).longValue(), value2);
+ }
+ if (value1 instanceof Byte) {
+ return compare_Byte(operation, ((Byte)value1).byteValue(), value2);
+ }
+ if (value1 instanceof Short) {
+ return compare_Short(operation, ((Short)value1).shortValue(), value2);
+ }
+ if (value1 instanceof Character) {
+ return compare_Character(operation, ((Character)value1).charValue(), value2);
+ }
+ if (value1 instanceof Float) {
+ return compare_Float(operation, ((Float)value1).floatValue(), value2);
+ }
+ if (value1 instanceof Double) {
+ return compare_Double(operation, ((Double)value1).doubleValue(), value2);
+ }
+ if (value1 instanceof Boolean) {
+ return compare_Boolean(operation, ((Boolean)value1).booleanValue(), value2);
+ }
+ if (value1 instanceof Comparable) {
+ return compare_Comparable(operation, (Comparable)value1, value2);
+ }
+ return compare_Unknown(operation, value1, value2); // RFC 59
+ }
+
+ private boolean compare_Collection(int operation, Collection collection, Object value2) {
+ for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
+ if (compare(operation, iterator.next(), value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_ObjectArray(int operation, Object[] array, Object value2) {
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_PrimitiveArray(int operation, Class type, Object primarray, Object value2) {
+ if (Integer.TYPE.isAssignableFrom(type)) {
+ int[] array = (int[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Integer(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Long.TYPE.isAssignableFrom(type)) {
+ long[] array = (long[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Long(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Byte.TYPE.isAssignableFrom(type)) {
+ byte[] array = (byte[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Byte(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Short.TYPE.isAssignableFrom(type)) {
+ short[] array = (short[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Short(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Character.TYPE.isAssignableFrom(type)) {
+ char[] array = (char[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Character(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Float.TYPE.isAssignableFrom(type)) {
+ float[] array = (float[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Float(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Double.TYPE.isAssignableFrom(type)) {
+ double[] array = (double[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Double(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Boolean.TYPE.isAssignableFrom(type)) {
+ boolean[] array = (boolean[])primarray;
+ for (int i = 0, size = array.length; i < size; i++) {
+ if (compare_Boolean(operation, array[i], value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
+ private boolean compare_String(int operation, String string, Object value2) {
+ switch (operation) {
+ case SUBSTRING: {
+ String[] substrings = (String[])value2;
+ int pos = 0;
+ for (int i = 0, size = substrings.length; i < size; i++) {
+ String substr = substrings[i];
+
+ if (i + 1 < size) /* if this is not that last substr */{
+ if (substr == null) /* * */{
+ String substr2 = substrings[i + 1];
+
+ if (substr2 == null) /* ** */
+ continue; /* ignore first star */
+ /* xxx */
+ int index = string.indexOf(substr2, pos);
+ if (index == -1) {
+ return false;
+ }
+
+ pos = index + substr2.length();
+ if (i + 2 < size) // if there are more
+ // substrings, increment
+ // over the string we just
+ // matched; otherwise need
+ // to do the last substr
+ // check
+ i++;
+ } else /* xxx */{
+ int len = substr.length();
+ if (string.regionMatches(pos, substr, 0, len)) {
+ pos += len;
+ } else {
+ return false;
+ }
+ }
+ } else /* last substr */{
+ if (substr == null) /* * */{
+ return true;
+ }
+ /* xxx */
+ return string.endsWith(substr);
+ }
+ }
+
+ return true;
+ }
+ case EQUAL: {
+ return string.equals(value2);
+ }
+ case APPROX: {
+ string = approxString(string);
+ String string2 = approxString((String)value2);
+
+ return string.equalsIgnoreCase(string2);
+ }
+ case GREATER: {
+ return string.compareTo((String)value2) >= 0;
+ }
+ case LESS: {
+ return string.compareTo((String)value2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Integer(int operation, int intval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ int intval2 = Integer.parseInt(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return intval == intval2;
+ }
+ case GREATER: {
+ return intval >= intval2;
+ }
+ case LESS: {
+ return intval <= intval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Long(int operation, long longval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ long longval2 = Long.parseLong(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return longval == longval2;
+ }
+ case GREATER: {
+ return longval >= longval2;
+ }
+ case LESS: {
+ return longval <= longval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Byte(int operation, byte byteval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ byte byteval2 = Byte.parseByte(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return byteval == byteval2;
+ }
+ case GREATER: {
+ return byteval >= byteval2;
+ }
+ case LESS: {
+ return byteval <= byteval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Short(int operation, short shortval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ short shortval2 = Short.parseShort(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return shortval == shortval2;
+ }
+ case GREATER: {
+ return shortval >= shortval2;
+ }
+ case LESS: {
+ return shortval <= shortval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Character(int operation, char charval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ char charval2 = (((String)value2).trim()).charAt(0);
+ switch (operation) {
+ case EQUAL: {
+ return charval == charval2;
+ }
+ case APPROX: {
+ return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2))
+ || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
+ }
+ case GREATER: {
+ return charval >= charval2;
+ }
+ case LESS: {
+ return charval <= charval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Boolean(int operation, boolean boolval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ boolean boolval2 = Boolean.valueOf(((String)value2).trim()).booleanValue();
+ switch (operation) {
+ case APPROX:
+ case EQUAL:
+ case GREATER:
+ case LESS: {
+ return boolval == boolval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Float(int operation, float floatval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ float floatval2 = Float.parseFloat(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return Float.compare(floatval, floatval2) == 0;
+ }
+ case GREATER: {
+ return Float.compare(floatval, floatval2) >= 0;
+ }
+ case LESS: {
+ return Float.compare(floatval, floatval2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Double(int operation, double doubleval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ double doubleval2 = Double.parseDouble(((String)value2).trim());
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return Double.compare(doubleval, doubleval2) == 0;
+ }
+ case GREATER: {
+ return Double.compare(doubleval, doubleval2) >= 0;
+ }
+ case LESS: {
+ return Double.compare(doubleval, doubleval2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private static final Class[] constructorType = new Class[] {String.class};
+
+ private boolean compare_Comparable(int operation, Comparable value1, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ Constructor constructor;
+ try {
+ constructor = value1.getClass().getConstructor(constructorType);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ try {
+ if (!constructor.isAccessible())
+ AccessController.doPrivileged(new SetAccessibleAction(constructor));
+ value2 = constructor.newInstance(new Object[] {((String)value2).trim()});
+ } catch (IllegalAccessException e) {
+ return false;
+ } catch (InvocationTargetException e) {
+ return false;
+ } catch (InstantiationException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX:
+ case EQUAL: {
+ return value1.compareTo(value2) == 0;
+ }
+ case GREATER: {
+ return value1.compareTo(value2) >= 0;
+ }
+ case LESS: {
+ return value1.compareTo(value2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Unknown(int operation, Object value1, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ Constructor constructor;
+ try {
+ constructor = value1.getClass().getConstructor(constructorType);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ try {
+ if (!constructor.isAccessible())
+ AccessController.doPrivileged(new SetAccessibleAction(constructor));
+ value2 = constructor.newInstance(new Object[] {((String)value2).trim()});
+ } catch (IllegalAccessException e) {
+ return false;
+ } catch (InvocationTargetException e) {
+ return false;
+ } catch (InstantiationException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX:
+ case EQUAL:
+ case GREATER:
+ case LESS: {
+ return value1.equals(value2);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Map a string for an APPROX (~=) comparison.
+ *
+ * This implementation removes white spaces. This is the minimum
+ * implementation allowed by the OSGi spec.
+ *
+ * @param input Input string.
+ * @return String ready for APPROX comparison.
+ */
+ private static String approxString(String input) {
+ boolean changed = false;
+ char[] output = input.toCharArray();
+ int cursor = 0;
+ for (int i = 0, length = output.length; i < length; i++) {
+ char c = output[i];
+
+ if (Character.isWhitespace(c)) {
+ changed = true;
+ continue;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return changed ? new String(output, 0, cursor) : input;
+ }
+
+ /**
+ * Parser class for OSGi filter strings. This class parses the complete
+ * filter string and builds a tree of Filter objects rooted at the
+ * parent.
+ */
+ private static class Parser {
+ private final String filterstring;
+ private final char[] filterChars;
+ private int pos;
+
+ Parser(String filterstring) {
+ this.filterstring = filterstring;
+ filterChars = filterstring.toCharArray();
+ pos = 0;
+ }
+
+ LDAPFilter parse() throws InvalidSyntaxException {
+ LDAPFilter filter;
+ try {
+ filter = parse_filter();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidSyntaxException("Filter ended abruptly", filterstring);
+ }
+
+ if (pos != filterChars.length) {
+ throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos),
+ filterstring);
+ }
+ return filter;
+ }
+
+ private LDAPFilter parse_filter() throws InvalidSyntaxException {
+ LDAPFilter filter;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+ }
+
+ pos++;
+
+ filter = parse_filtercomp();
+
+ skipWhiteSpace();
+
+ if (filterChars[pos] != ')') {
+ throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring);
+ }
+
+ pos++;
+
+ skipWhiteSpace();
+
+ return filter;
+ }
+
+ private LDAPFilter parse_filtercomp() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ char c = filterChars[pos];
+
+ switch (c) {
+ case '&': {
+ pos++;
+ return parse_and();
+ }
+ case '|': {
+ pos++;
+ return parse_or();
+ }
+ case '!': {
+ pos++;
+ return parse_not();
+ }
+ }
+ return parse_item();
+ }
+
+ private LDAPFilter parse_and() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+ }
+
+ List operands = new ArrayList(10);
+
+ while (filterChars[pos] == '(') {
+ LDAPFilter child = parse_filter();
+ operands.add(child);
+ }
+
+ return new LDAPFilter(LDAPFilter.AND, null, operands.toArray(new LDAPFilter[operands.size()]));
+ }
+
+ private LDAPFilter parse_or() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+ }
+
+ List operands = new ArrayList(10);
+
+ while (filterChars[pos] == '(') {
+ LDAPFilter child = parse_filter();
+ operands.add(child);
+ }
+
+ return new LDAPFilter(LDAPFilter.OR, null, operands.toArray(new LDAPFilter[operands.size()]));
+ }
+
+ private LDAPFilter parse_not() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+ }
+
+ LDAPFilter child = parse_filter();
+
+ return new LDAPFilter(LDAPFilter.NOT, null, child);
+ }
+
+ private LDAPFilter parse_item() throws InvalidSyntaxException {
+ String attr = parse_attr();
+
+ skipWhiteSpace();
+
+ switch (filterChars[pos]) {
+ case '~': {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new LDAPFilter(LDAPFilter.APPROX, attr, parse_value());
+ }
+ break;
+ }
+ case '>': {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new LDAPFilter(LDAPFilter.GREATER, attr, parse_value());
+ }
+ break;
+ }
+ case '<': {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new LDAPFilter(LDAPFilter.LESS, attr, parse_value());
+ }
+ break;
+ }
+ case '=': {
+ if (filterChars[pos + 1] == '*') {
+ int oldpos = pos;
+ pos += 2;
+ skipWhiteSpace();
+ if (filterChars[pos] == ')') {
+ return new LDAPFilter(LDAPFilter.PRESENT, attr, null);
+ }
+ pos = oldpos;
+ }
+
+ pos++;
+ Object string = parse_substring();
+
+ if (string instanceof String) {
+ return new LDAPFilter(LDAPFilter.EQUAL, attr, string);
+ }
+ return new LDAPFilter(LDAPFilter.SUBSTRING, attr, string);
+ }
+ }
+
+ throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring);
+ }
+
+ private String parse_attr() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ int begin = pos;
+ int end = pos;
+
+ char c = filterChars[pos];
+
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+ pos++;
+
+ if (!Character.isWhitespace(c)) {
+ end = pos;
+ }
+
+ c = filterChars[pos];
+ }
+
+ int length = end - begin;
+
+ if (length == 0) {
+ throw new InvalidSyntaxException("Missing attr: " + filterstring.substring(pos), filterstring);
+ }
+
+ return new String(filterChars, begin, length);
+ }
+
+ private String parse_value() throws InvalidSyntaxException {
+ StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')': {
+ break parseloop;
+ }
+
+ case '(': {
+ throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ case '\\': {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default: {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ if (sb.length() == 0) {
+ throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
+ }
+
+ return sb.toString();
+ }
+
+ private Object parse_substring() throws InvalidSyntaxException {
+ StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+ List operands = new ArrayList(10);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')': {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ break parseloop;
+ }
+
+ case '(': {
+ throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ case '*': {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ sb.setLength(0);
+
+ operands.add(null);
+ pos++;
+
+ break;
+ }
+
+ case '\\': {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default: {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ int size = operands.size();
+
+ if (size == 0) {
+ throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
+ }
+
+ if (size == 1) {
+ Object single = operands.get(0);
+
+ if (single != null) {
+ return single;
+ }
+ }
+
+ return operands.toArray(new String[size]);
+ }
+
+ private void skipWhiteSpace() {
+ for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
+ pos++;
+ }
+ }
+ }
+
+ /**
+ * This Dictionary is used for case-insensitive key lookup during filter
+ * evaluation. This Dictionary implementation only supports the get
+ * operation using a String key as no other operations are used by the
+ * Filter implementation.
+ */
+ static class CaseInsensitiveDictionary extends Dictionary {
+ private final Dictionary dictionary;
+ private final String[] keys;
+
+ /**
+ * Create a case insensitive dictionary from the specified dictionary.
+ *
+ * @param dictionary
+ * @throws IllegalArgumentException If <code>dictionary</code> contains
+ * case variants of the same key name.
+ */
+ CaseInsensitiveDictionary(Dictionary dictionary) {
+ if (dictionary == null) {
+ this.dictionary = null;
+ this.keys = new String[0];
+ return;
+ }
+ this.dictionary = dictionary;
+ List keyList = new ArrayList(dictionary.size());
+ for (Enumeration e = dictionary.keys(); e.hasMoreElements();) {
+ Object k = e.nextElement();
+ if (k instanceof String) {
+ String key = (String)k;
+ for (Iterator i = keyList.iterator(); i.hasNext();) {
+ if (key.equalsIgnoreCase((String)i.next())) {
+ throw new IllegalArgumentException();
+ }
+ }
+ keyList.add(key);
+ }
+ }
+ this.keys = (String[])keyList.toArray(new String[keyList.size()]);
+ }
+
+ public Object get(Object o) {
+ String k = (String)o;
+ for (int i = 0, length = keys.length; i < length; i++) {
+ String key = keys[i];
+ if (key.equalsIgnoreCase(k)) {
+ return dictionary.get(key);
+ }
+ }
+ return null;
+ }
+
+ public boolean isEmpty() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Enumeration keys() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Enumeration elements() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object put(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class SetAccessibleAction implements PrivilegedAction {
+ private final AccessibleObject accessible;
+
+ SetAccessibleAction(AccessibleObject accessible) {
+ this.accessible = accessible;
+ }
+
+ public Object run() {
+ accessible.setAccessible(true);
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.FactoryExtensionPoint b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.FactoryExtensionPoint
new file mode 100644
index 0000000000..be22b6fb13
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.FactoryExtensionPoint
@@ -0,0 +1,19 @@
+# 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.
+
+org.apache.tuscany.sca.core.DefaultFactoryExtensionPoint
+
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint
new file mode 100644
index 0000000000..4e5a05d522
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint
@@ -0,0 +1,19 @@
+# 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.
+
+org.apache.tuscany.sca.core.DefaultModuleActivatorExtensionPoint
+
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.UtilityExtensionPoint b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.UtilityExtensionPoint
new file mode 100644
index 0000000000..52d7cb59b2
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/main/resources/META-INF/services/org.apache.tuscany.sca.core.UtilityExtensionPoint
@@ -0,0 +1,19 @@
+# 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.
+
+org.apache.tuscany.sca.core.DefaultUtilityExtensionPoint
+
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscovererTestCase.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscovererTestCase.java
new file mode 100644
index 0000000000..6865ba0d9d
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ContextClassLoaderServiceDiscovererTestCase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test Case for ClasspathServiceDiscover
+ */
+public class ContextClassLoaderServiceDiscovererTestCase {
+ private static ContextClassLoaderServiceDiscoverer discover;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ discover = new ContextClassLoaderServiceDiscoverer();
+ }
+
+ @Test
+ public void testDiscovery() {
+ Collection<ServiceDeclaration> discriptors =
+ discover.getServiceDeclarations("org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint");
+ Assert.assertEquals(1, discriptors.size());
+ discriptors =
+ discover.getServiceDeclarations("notthere");
+ Assert.assertEquals(0, discriptors.size());
+ }
+
+ @Test
+ public void testDiscoverResources() throws IOException {
+ Collection<ServiceDeclaration> descriptors = discover.getServiceDeclarations("/META-INF/services/org.apache.tuscany.sca.extensibility.test.TestInterface");
+ Assert.assertEquals(3, descriptors.size());
+ }
+
+ @Test
+ public void testDiscoveryFirst() throws IOException {
+ ServiceDeclaration descriptor =
+ discover.getServiceDeclaration("org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint");
+ Assert.assertNotNull(descriptor);
+ descriptor = discover.getServiceDeclaration("notthere");
+ Assert.assertNull(descriptor);
+ }
+
+ @Test
+ public void testXPathFactory() {
+ Collection<ServiceDeclaration> discriptors = discover.getServiceDeclarations("javax.xml.xpath.XPathFactory");
+ if (!discriptors.isEmpty()) {
+ ServiceDeclaration d = discriptors.iterator().next();
+ Assert.assertNotNull(d.getClassName());
+ Assert.assertTrue(d.getAttributes().containsKey("class"));
+ }
+ }
+
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultExtensionPointRegistryTestCase.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultExtensionPointRegistryTestCase.java
new file mode 100644
index 0000000000..9758cba3b4
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultExtensionPointRegistryTestCase.java
@@ -0,0 +1,58 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultExtensionPointRegistryTestCase {
+ private ExtensionPointRegistry registry;
+
+ @Before
+ public void setUp() throws Exception {
+ registry = new DefaultExtensionPointRegistry();
+ }
+
+ @Test
+ public void testRegistry() {
+ MyExtensionPoint service = new MyExtensionPointImpl();
+ registry.addExtensionPoint(service);
+ assertSame(service, registry.getExtensionPoint(MyExtensionPoint.class));
+ registry.removeExtensionPoint(service);
+ assertNull(registry.getExtensionPoint(MyExtensionPoint.class));
+ }
+
+ public static interface MyExtensionPoint {
+ void doSomething();
+ }
+
+ private static class MyExtensionPointImpl implements MyExtensionPoint {
+
+ public void doSomething() {
+ }
+
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultUtilityExtensionPointTestCase.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultUtilityExtensionPointTestCase.java
new file mode 100644
index 0000000000..0a5f4d6442
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/DefaultUtilityExtensionPointTestCase.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.extensibility;
+
+
+import java.io.Serializable;
+
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.DefaultUtilityExtensionPoint;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class DefaultUtilityExtensionPointTestCase {
+ private static UtilityExtensionPoint ep;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ ep = new DefaultUtilityExtensionPoint(new DefaultExtensionPointRegistry());
+ ep.start();
+ }
+
+ @Test
+ public void testGet() {
+ MyUtilityImpl my = new MyUtilityImpl();
+ ep.addUtility(my);
+ Assert.assertTrue(my.started);
+ Utility1 u1 = ep.getUtility(Utility1.class);
+ Assert.assertSame(my, u1);
+ Utility2 u2 = ep.getUtility(Utility2.class);
+ Assert.assertSame(my, u2);
+ ep.removeUtility(my);
+ Assert.assertFalse(my.started);
+ u1 = ep.getUtility(Utility1.class);
+ Assert.assertNull(u1);
+
+ ep.addUtility("1", my);
+ u1= ep.getUtility(Utility1.class);
+ Assert.assertNull(u1);
+ u1= ep.getUtility(Utility1.class, "1");
+ Assert.assertNotNull(u1);
+ ep.removeUtility(my);
+ u1= ep.getUtility(Utility1.class, "1");
+ Assert.assertNull(u1);
+
+ u1 = ep.getUtility(MyUtilityImpl.class);
+ Assert.assertNotNull(u1);
+ u2 = ep.getUtility(Utility2.class);
+ Assert.assertSame(u1, u2);
+ ep.removeUtility(u1);
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ ep.stop();
+ }
+
+ public static interface Utility1 extends Serializable {
+ void op1();
+ }
+
+ public static interface Utility2 extends Serializable {
+ void op2();
+ }
+
+ public static class MyUtilityImpl implements Utility1, Utility2, LifeCycleListener {
+ public boolean started;
+
+ public void start() {
+ System.out.println("start");
+ started = true;
+ }
+
+ public void stop() {
+ System.out.println("stop");
+ started = false;
+ }
+
+ public void op1() {
+ System.out.println("op1");
+ }
+
+ public void op2() {
+ System.out.println("op2");
+ }
+
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceDiscoveryTestCase.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceDiscoveryTestCase.java
new file mode 100644
index 0000000000..e13a47cc1b
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceDiscoveryTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tuscany.sca.extensibility;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.tuscany.sca.extensibility.test.TestInterface;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ServiceDiscoveryTestCase {
+ private static final String NAME = "org.apache.tuscany.sca.extensibility.test.TestInterface";
+
+ @Test
+ public void testRanking() throws Exception {
+ ServiceDeclaration sd = ServiceDiscovery.getInstance().getServiceDeclaration(NAME);
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.Test2Impl", sd.getClassName());
+
+ Collection<ServiceDeclaration> sds = ServiceDiscovery.getInstance().getServiceDeclarations(NAME);
+ Assert.assertEquals(3, sds.size());
+
+ sds = ServiceDiscovery.getInstance().getServiceDeclarations(NAME, true);
+ Assert.assertEquals(3, sds.size());
+ Iterator<ServiceDeclaration> it = sds.iterator();
+ ServiceDeclaration sd1 = it.next();
+ ServiceDeclaration sd2 = it.next();
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.Test2Impl", sd1.getClassName());
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.TestImpl", sd2.getClassName());
+ }
+
+ @Test
+ public void testServiceType() throws Exception {
+ ServiceDeclaration sd = ServiceDiscovery.getInstance().getServiceDeclaration(TestInterface.class);
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.Test2Impl", sd.getClassName());
+
+ Collection<ServiceDeclaration> sds = ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class);
+ Assert.assertEquals(2, sds.size());
+
+ sds = ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class, true);
+ Assert.assertEquals(2, sds.size());
+ Iterator<ServiceDeclaration> it = sds.iterator();
+ ServiceDeclaration sd1 = it.next();
+ ServiceDeclaration sd2 = it.next();
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.Test2Impl", sd1.getClassName());
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.TestImpl", sd2.getClassName());
+ }
+
+ @Test
+ /**
+ * Test if the external attributes override the one in the META-INF/services/<SPI>
+ */
+ public void testAttributes() throws Exception {
+ ServiceDiscovery serviceDiscovery = ServiceDiscovery.getInstance(new ContextClassLoaderServiceDiscoverer());
+ serviceDiscovery.setAttribute(TestInterface.class.getName(), "attr", "value");
+ serviceDiscovery.setAttribute(TestInterface.class.getName(), "attr1", "value1");
+ for (ServiceDeclaration sd : serviceDiscovery.getServiceDeclarations(TestInterface.class)) {
+ Assert.assertEquals("value1", sd.getAttributes().get("attr1"));
+ Assert.assertEquals("value", sd.getAttributes().get("attr"));
+ }
+ }
+
+ @Test
+ public void testFilter() throws Exception {
+ Collection<ServiceDeclaration> sds =
+ ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class, "(attr=abc)");
+
+ Assert.assertEquals(1, sds.size());
+
+ Iterator<ServiceDeclaration> it = sds.iterator();
+ ServiceDeclaration sd1 = it.next();
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.Test2Impl", sd1.getClassName());
+
+ sds = ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class, "(attr=1*)");
+ Assert.assertEquals(1, sds.size());
+ it = sds.iterator();
+ sd1 = it.next();
+ Assert.assertEquals("org.apache.tuscany.sca.extensibility.test.TestImpl", sd1.getClassName());
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceHelperTestCase.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceHelperTestCase.java
new file mode 100644
index 0000000000..7920fdf4f4
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/ServiceHelperTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.extensibility;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.xml.namespace.QName;
+
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.extensibility.test.Test2Impl;
+import org.apache.tuscany.sca.extensibility.test.TestImpl;
+import org.apache.tuscany.sca.extensibility.test.TestInterface;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServiceHelperTestCase {
+ @Test
+ public void testNewInstance() throws Exception {
+ Test2Impl implA = ServiceHelper.newInstance(Test2Impl.class);
+ Assert.assertNull(implA.getRegistry());
+
+ TestImpl impl1 = ServiceHelper.newInstance(TestImpl.class);
+ Assert.assertNotNull(impl1);
+ }
+
+ @Test
+ public void testNewInstance2() throws Exception {
+ Collection<ServiceDeclaration> sds =
+ ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class, true);
+ ExtensionPointRegistry registry = new DefaultExtensionPointRegistry();
+ Iterator<ServiceDeclaration> iterator = sds.iterator();
+ Test2Impl implA = ServiceHelper.newInstance(registry, iterator.next());
+ Assert.assertSame(registry, implA.getRegistry());
+
+ TestImpl impl1 = ServiceHelper.newInstance(registry, iterator.next());
+ Assert.assertNotNull(impl1);
+ }
+
+ @Test
+ public void testNewLazyInstance() throws Exception {
+ Collection<ServiceDeclaration> sds =
+ ServiceDiscovery.getInstance().getServiceDeclarations(TestInterface.class, true);
+ ExtensionPointRegistry registry = new DefaultExtensionPointRegistry();
+ Iterator<ServiceDeclaration> iterator = sds.iterator();
+ TestInterface ti = ServiceHelper.newLazyInstance(registry, iterator.next(), TestInterface.class);
+ Assert.assertTrue(Proxy.isProxyClass(ti.getClass()));
+ Assert.assertTrue(ti instanceof LifeCycleListener);
+ Assert.assertTrue(ti.toString().startsWith("Proxy"));
+ Assert.assertEquals(System.identityHashCode(ti), ti.hashCode());
+ ServiceHelper.start(ti);
+ ServiceHelper.stop(ti);
+ QName name = ti.getArtifactType();
+ Assert.assertEquals(new QName("http://sample", "Test2"), name);
+ String str = ti.test("ABC");
+ Assert.assertEquals("Test 2: ABC", str);
+ ServiceHelper.stop(ti);
+ }
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/DummyImpl.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/DummyImpl.java
new file mode 100644
index 0000000000..f1ed50c5f2
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/DummyImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tuscany.sca.extensibility.test;
+
+/**
+ *
+ */
+public class DummyImpl {
+
+ public void test(String str) {
+ System.out.println("Test: " + str);
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/Test2Impl.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/Test2Impl.java
new file mode 100644
index 0000000000..6a1ba4d7ba
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/Test2Impl.java
@@ -0,0 +1,103 @@
+/*
+ * 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.tuscany.sca.extensibility.test;
+
+import javax.xml.namespace.QName;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+
+/**
+ *
+ */
+public class Test2Impl implements TestInterface, LifeCycleListener {
+ private ExtensionPointRegistry registry;
+ public int state = 0;
+
+ public Test2Impl(ExtensionPointRegistry registry) {
+ this.registry = registry;
+ }
+
+ public Test2Impl() {
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.extensibility.test.TestInterface#test(java.lang.String)
+ */
+ public String test(String str) {
+ System.out.println("Test 2: " + str);
+ return "Test 2: " + str;
+ }
+
+ public ExtensionPointRegistry getRegistry() {
+ return registry;
+ }
+
+ public void start() {
+ state = 1;
+ }
+
+ public void stop() {
+ state = -1;
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((registry == null) ? 0 : registry.hashCode());
+ result = prime * result + state;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Test2Impl other = (Test2Impl)obj;
+ if (registry == null) {
+ if (other.registry != null)
+ return false;
+ } else if (!registry.equals(other.registry))
+ return false;
+ if (state != other.state)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Test2Impl [state=" + state + "]";
+ }
+
+ public QName getArtifactType() {
+ // TODO Auto-generated method stub
+ return new QName("http://sample", "Test2");
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestImpl.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestImpl.java
new file mode 100644
index 0000000000..4def8a5227
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestImpl.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.extensibility.test;
+
+import javax.xml.namespace.QName;
+
+/**
+ *
+ */
+public class TestImpl implements TestInterface {
+
+ /* (non-Javadoc)
+ * @see org.apache.tuscany.sca.extensibility.test.TestInterface#test(java.lang.String)
+ */
+ public String test(String str) {
+ System.out.println("Test: " + str);
+ return "Test: " + str;
+ }
+
+ public QName getArtifactType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestInterface.java b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestInterface.java
new file mode 100644
index 0000000000..2f0c34df7d
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/java/org/apache/tuscany/sca/extensibility/test/TestInterface.java
@@ -0,0 +1,30 @@
+/*
+ * 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.tuscany.sca.extensibility.test;
+
+import javax.xml.namespace.QName;
+
+/**
+ *
+ */
+public interface TestInterface {
+ String test(String str);
+ QName getArtifactType();
+}
diff --git a/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/resources/META-INF/services/org.apache.tuscany.sca.extensibility.test.TestInterface b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/resources/META-INF/services/org.apache.tuscany.sca.extensibility.test.TestInterface
new file mode 100644
index 0000000000..12991071e5
--- /dev/null
+++ b/sca-java-2.x/branches/2.0-Beta2/modules/extensibility/src/test/resources/META-INF/services/org.apache.tuscany.sca.extensibility.test.TestInterface
@@ -0,0 +1,19 @@
+# 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.
+org.apache.tuscany.sca.extensibility.test.TestImpl;ranking=10;attr=123
+org.apache.tuscany.sca.extensibility.test.Test2Impl;ranking=20,attr=abc,qname=http://sample#Test2,model=org.apache.tuscany.sca.extensibility.test.TestInterface
+org.apache.tuscany.sca.extensibility.test.DummyImpl \ No newline at end of file