summaryrefslogtreecommitdiffstats
path: root/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org')
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java129
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java143
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java142
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java385
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointListener.java127
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointPermission.java953
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ExportRegistration.java76
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ImportRegistration.java75
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteConstants.java170
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdmin.java125
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminEvent.java164
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminListener.java31
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java97
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java82
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java474
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java89
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java92
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java204
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java125
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java95
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java289
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java410
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java280
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java66
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java57
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java124
-rw-r--r--tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java205
27 files changed, 5209 insertions, 0 deletions
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java
new file mode 100644
index 0000000000..36feb6526a
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.node.osgi.impl;
+
+import static org.apache.tuscany.sca.node.osgi.impl.NodeManager.isSCABundle;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.RemoteServiceAdminImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.TopologyManagerImpl;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.DiscoveryActivator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * Bundle activator to receive the BundleContext
+ */
+public class NodeActivator implements BundleActivator, SynchronousBundleListener {
+ private final static Logger logger = Logger.getLogger(NodeActivator.class.getName());
+ private static BundleContext bundleContext;
+ private boolean inited;
+ private NodeManager manager;
+
+ private DiscoveryActivator discoveryActivator = new DiscoveryActivator();
+ private RemoteServiceAdminImpl remoteAdmin;
+ private TopologyManagerImpl controller;
+
+ private void init() {
+ synchronized (this) {
+ if (inited) {
+ return;
+ }
+ manager = new NodeManager(bundleContext);
+ manager.start();
+ bundleContext.addBundleListener(manager);
+ inited = true;
+ }
+ }
+
+ public void start(BundleContext context) throws Exception {
+ try {
+ bundleContext = context;
+
+ // FIXME: We should try to avoid aggressive initialization
+ init();
+
+ remoteAdmin = new RemoteServiceAdminImpl(context);
+ remoteAdmin.start();
+
+ discoveryActivator.start(context);
+
+ controller = new TopologyManagerImpl(context);
+ controller.start();
+
+ boolean found = false;
+ for (Bundle b : context.getBundles()) {
+ if (isSCABundle(b)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ init();
+ } else {
+ context.addBundleListener(this);
+ }
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ if (inited) {
+ context.removeBundleListener(this);
+ controller.stop();
+ controller = null;
+
+ discoveryActivator.stop(context);
+ discoveryActivator = null;
+
+ remoteAdmin.stop();
+ remoteAdmin = null;
+
+ manager.stop();
+ bundleContext.removeBundleListener(manager);
+ manager = null;
+ bundleContext = null;
+ inited = false;
+ }
+ }
+
+ public static BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.STARTING) {
+ if (isSCABundle(event.getBundle())) {
+ bundleContext.removeBundleListener(this);
+ init();
+ }
+ }
+
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java
new file mode 100644
index 0000000000..6914e09b8e
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java
@@ -0,0 +1,143 @@
+/*
+ * 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.node.osgi.impl;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * Managing the mapping between OSGi bundles and SCA implementation.osgi
+ */
+public class NodeManager implements SynchronousBundleListener, ServiceListener {
+ private static final Logger logger = Logger.getLogger(NodeManager.class.getName());
+ private BundleContext bundleContext;
+ OSGiNodeFactoryImpl factory;
+
+ public NodeManager(BundleContext bundleContext) {
+ super();
+ this.bundleContext = bundleContext;
+ this.factory = new OSGiNodeFactoryImpl(this.bundleContext);
+ }
+
+ public void start() {
+ for (Bundle b : bundleContext.getBundles()) {
+ if ((b.getState() & Bundle.ACTIVE) != 0) {
+ // Process the active bundles
+ bundleStarted(b);
+ }
+ }
+ }
+
+ public void stop() {
+ if (factory != null) {
+ factory.destroy();
+ }
+ }
+
+ public static boolean isSCABundle(Bundle bundle) {
+ Dictionary<?, ?> headers = bundle.getHeaders();
+ // OSGi RFC 119 SCA
+ if (headers.get("SCA-Composite") != null) {
+ return true;
+ }
+
+ URL bundleComposite = bundle.getResource("OSGI-INF/sca/bundle.composite");
+ if (bundleComposite != null) {
+ return true;
+ }
+
+ /* FIXME: What if there is a META-INF/sca-contribution.xml? There are two cases:
+ * 1. The file contains deployable elements
+ * 2. The file doesn't contain deployable elements
+ */
+
+ /*
+ * FIXME: Do we want to use all of the .composite files under OSGI-INF/sca?
+ */
+
+ /*
+ Enumeration<?> entries = bundle.findEntries("OSGI-INF/sca", "bundle.composite", false);
+ if (entries != null && entries.hasMoreElements()) {
+ return true;
+ }
+
+ // OSGi Declarative Services
+ if (headers.get("Service-Component") != null) {
+ return true;
+ }
+
+ // OSGI RFC 124: BluePrint Service
+ if (headers.get("Bundle-Blueprint") != null) {
+ return true;
+ }
+
+ entries = bundle.findEntries("OSGI-INF/blueprint", "*.xml", false);
+ if (entries != null && entries.hasMoreElements()) {
+ return true;
+ }
+ */
+ return false;
+ }
+
+ private void bundleStarted(Bundle bundle) {
+ if (!isSCABundle(bundle)) {
+ return;
+ }
+ try {
+ NodeConfiguration configuration = factory.getConfiguration(bundle, null);
+ Node node = factory.createNode(configuration);
+ node.start();
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
+
+ private void bundleStopping(Bundle bundle) {
+ Node node = factory.getNodes().get(bundle);
+ if (node == null) {
+ return;
+ }
+ node.stop();
+ }
+
+ public void serviceChanged(ServiceEvent event) {
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ int type = event.getType();
+ if (type == BundleEvent.STOPPING) {
+ bundleStopping(event.getBundle());
+ } else if (type == BundleEvent.STARTED) {
+ bundleStarted(event.getBundle());
+ }
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java
new file mode 100644
index 0000000000..09e32ccc56
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiNodeFactoryImpl.java
@@ -0,0 +1,142 @@
+/*
+* 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.node.osgi.impl;
+
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.logging.Level;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.extensibility.equinox.OSGiExtensionPointRegistry;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The OSGi based NodeFactory
+ *
+ * @version $Rev$ $Date$
+ */
+public class OSGiNodeFactoryImpl extends NodeFactoryImpl {
+ private ServiceRegistration registration;
+ private BundleContext bundleContext;
+
+ public OSGiNodeFactoryImpl() {
+ super();
+ Bundle bundle = FrameworkUtil.getBundle(OSGiNodeFactoryImpl.class);
+ if (bundle != null) {
+ this.bundleContext = bundle.getBundleContext();
+ autoDestroy = false;
+ setNodeFactory(this);
+ } else {
+ throw new IllegalStateException(OSGiNodeFactoryImpl.class + " is not loaded by OSGi");
+ }
+ }
+ /**
+ * Constructs a new Node controller
+ */
+ public OSGiNodeFactoryImpl(BundleContext bundleContext) {
+ super();
+ this.bundleContext = bundleContext;
+ autoDestroy = false;
+ setNodeFactory(this);
+ }
+
+ protected NodeConfiguration getConfiguration(Bundle bundle, String compositeContent) {
+ init();
+
+ // Create a node configuration
+ FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ NodeConfigurationFactory configurationFactory = modelFactories.getFactory(NodeConfigurationFactory.class);
+ NodeConfiguration configuration = configurationFactory.createNodeConfiguration();
+
+ URL location = bundle.getEntry("/");
+ String uri = bundle.getSymbolicName();
+ configuration.setURI(uri).addContribution(uri, location);
+
+ if (compositeContent != null) {
+ configuration.addDeploymentComposite(uri, new StringReader(compositeContent));
+ } else {
+ String compositeURI = (String)bundle.getHeaders().get("SCA-Composite");
+ if (compositeURI == null) {
+ compositeURI = "OSGI-INF/sca/bundle.composite";
+ }
+ if (compositeURI != null) {
+ configuration.addDeploymentComposite(uri, compositeURI);
+ }
+ }
+ // Set the bundle
+ configuration.getExtensions().add(bundle);
+ return configuration;
+ }
+
+ public synchronized void init() {
+ if (!inited) {
+ // Register the ExtensionPointRegistry as an OSGi service
+ Dictionary<Object, Object> props = new Hashtable<Object, Object>();
+ registry = createExtensionPointRegistry();
+ registry.start();
+ registration =
+ bundleContext.registerService(ExtensionPointRegistry.class.getName(), registry, props);
+
+ // Call super.init after the extension point registry is registered
+ super.init();
+ }
+ }
+
+ public synchronized void destroy() {
+ if (inited) {
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ super.destroy();
+ }
+
+ }
+
+ @Override
+ protected Object getNodeKey(NodeConfiguration configuration) {
+ // Use the bundle as the key
+ return configuration.getExtensions().get(0);
+ }
+
+ @Override
+ protected ExtensionPointRegistry createExtensionPointRegistry() {
+ return new OSGiExtensionPointRegistry(bundleContext);
+ }
+
+ @Override
+ protected boolean isSchemaValidationEnabled() {
+ return logger.isLoggable(Level.FINE);
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
new file mode 100644
index 0000000000..23a91b89ef
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+
+/**
+ * A description of an endpoint that provides sufficient information for a
+ * compatible distribution provider to create a connection to this endpoint
+ *
+ * An Endpoint Description is easy to transfer between different systems. This
+ * allows it to be used as a communications device to convey available endpoint
+ * information to nodes in a network.
+ *
+ * An Endpoint Description reflects the perspective of an importer. That is, the
+ * property keys have been chosen to match filters that are created by client
+ * bundles that need a service.
+ *
+ * TODO Automatically calculate versions of interface packages?
+ *
+ * TODO Constructor that takes a class?
+ *
+ * TODO Skipping of service.exported.* properties?
+ *
+ * TODO qualified intents?
+ *
+ *
+ *
+ * @Immutable
+ * @version $Revision$
+ */
+
+public class EndpointDescription {
+ private final Map /* <String,Object> */properties = new HashMap/*
+ * <String ,
+ * Object >
+ */();
+ private final List /* String */interfaces;
+ private final long remoteServiceId;
+ private final String remoteFrameworkUUID;
+ private final String remoteUri;
+
+ /**
+ * Create an Endpoint Description based on a Map.
+ *
+ * @param properties
+ * @throws IllegalArgumentException When the properties are not proper for
+ * an Endpoint Description
+ */
+
+ public EndpointDescription(Map/* <String,Object> */properties) throws IllegalArgumentException {
+ this.properties.putAll(properties);
+
+ interfaces = verifyInterfacesProperty();
+ remoteServiceId = verifyLongProperty(RemoteConstants.SERVICE_REMOTE_ID);
+ remoteFrameworkUUID = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
+ remoteUri = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_URI);
+ }
+
+ /**
+ * Create an Endpoint Description based on a reference and optionally a map
+ * of additional properties.
+ *
+ * @param ref A service reference that can be exported
+ * @param properties Additional properties to add. Can be <code>null</code>.
+ * @throws IllegalArgumentException
+ */
+ public EndpointDescription(ServiceReference ref, Map /* <String,Object> */properties)
+ throws IllegalArgumentException {
+ if (properties != null) {
+ this.properties.putAll(properties);
+ }
+
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i > keys.length; i++)
+ properties.put(keys[i], ref.getProperty(keys[i]));
+
+ interfaces = verifyInterfacesProperty();
+ remoteServiceId = verifyLongProperty(RemoteConstants.SERVICE_REMOTE_ID);
+ remoteFrameworkUUID = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
+ remoteUri = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_URI);
+ }
+
+ /**
+ * Create an Endpoint Description based on the URI, the remote service ID
+ * and the interface names, and optionally service properties.
+ *
+ * @param uri The URI of the remote service.
+ * @param interfaceNames The names of the interfaces of the service to
+ * consider.
+ * @param remoteServiceId the remote service ID.
+ * @param properties Optionally service properties.
+ */
+ public EndpointDescription(String uri, String[] interfaceNames, int remoteServiceId, Map properties) {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI must not be null");
+ }
+ if (interfaceNames == null) {
+ throw new IllegalArgumentException("Interfaces must not be null");
+ }
+ this.remoteUri = uri;
+ this.interfaces = Arrays.asList(interfaceNames);
+ this.remoteServiceId = remoteServiceId;
+ this.remoteFrameworkUUID = (String)properties.get(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
+ if (properties != null) {
+ this.properties.putAll(properties);
+ }
+ if (interfaceNames != null) {
+ this.properties.put(Constants.OBJECTCLASS, interfaceNames);
+ }
+ this.properties.put(RemoteConstants.SERVICE_REMOTE_URI, uri);
+ if (remoteServiceId <= 0) {
+ this.properties.put(RemoteConstants.SERVICE_REMOTE_ID, new Integer(remoteServiceId));
+ }
+ }
+
+ /**
+ * Verify and obtain the interface list from the properties.
+ *
+ * @return A list with the interface names.
+ * @throws IllegalArgumentException when
+ */
+ private List /* <String> */verifyInterfacesProperty() {
+ List l = null;
+
+ Object objectClass = properties.get(Constants.OBJECTCLASS);
+ if (objectClass == null)
+ l = Collections.EMPTY_LIST;
+ else if (!(objectClass instanceof String[]))
+ throw new IllegalArgumentException("objectClass must be a String[]");
+ else {
+ l = Collections.unmodifiableList(Arrays.asList((String[])objectClass));
+ for (Iterator i = l.iterator(); i.hasNext();) {
+ String interf = (String)i.next();
+ try {
+ getInterfaceVersion(interf);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Improper version for interface " + interf + " caused by " + e);
+ }
+ }
+ }
+ return l;
+ }
+
+ /**
+ * Verify and obtain a required String property.
+ *
+ * @param propName The name of the property
+ * @return The value of the property.
+ * @throws IllegalArgumentException when the property is not set or doesn't
+ * have the correct data type.
+ */
+ private String verifyStringProperty(String propName) {
+ Object r = properties.get(propName);
+ if (r == null) {
+ throw new IllegalArgumentException("Required property not set: " + propName);
+ }
+ if (!(r instanceof String)) {
+ throw new IllegalArgumentException("Required property is not a string: " + propName);
+ }
+ return (String)r;
+ }
+
+ /**
+ * Verify and obtain a required long property.
+ *
+ * @param propName The name of the property
+ * @return The value of the property.
+ * @throws IllegalArgumentException when the property is not set or doesn't
+ * have the correct data type.
+ */
+ private long verifyLongProperty(String propName) {
+ Object r = properties.get(propName);
+ long result;
+ if (r == null) {
+ throw new IllegalArgumentException("Required property not set: " + propName);
+ }
+ if (!(r instanceof String)) {
+ throw new IllegalArgumentException("Required property is not a string: " + propName);
+ }
+ try {
+ result = Long.parseLong((String)r);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Required property cannot be parsed as a long: " + propName);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the endpoint's URI.
+ *
+ * The URI is an opaque id for an endpoint in URI form. No two different
+ * endpoints must have the same URI, two Endpoint Descriptions with the same
+ * URI must represent the same endpoint.
+ *
+ * The value of the URI is stored in the
+ * {@link RemoteConstants#SERVICE_REMOTE_URI} property.
+ *
+ * @return The URI of the endpoint, never <code>null</code>.
+ */
+ public String getRemoteURI() {
+ return remoteUri;
+ }
+
+ /**
+ * Answer the list of interfaces implemented by the exported service.
+ *
+ * If this Endpoint Description does not map to a service, then this List
+ * must be empty.
+ *
+ * The value of the interfaces is derived from the <code>objectClass</code>
+ * property.
+ *
+ * @return The read only list of Java interface names accessible by this
+ * endpoint.
+ */
+ public List/* <String> */getInterfaces() {
+ return interfaces;
+ }
+
+ /**
+ * Answer the version of the given interface.
+ *
+ * The version is encoded by prefixing the given interface name with
+ * <code>endpoint.version.</code>, and then using this as a property key.
+ * The value must then be the <code>Version</code> object. For example:
+ *
+ * <pre>
+ * endpoint.version.com.acme.Foo
+ * </pre>
+ *
+ * @param name The name of the interface for which a version is requested
+ * @return The version of the given interface or <code>null</code> if the
+ * interface has no version in this Endpoint Description
+ */
+ public Version getInterfaceVersion(String name) {
+ String v = (String)properties.get("endpoint.version." + name);
+ if (v == null) {
+ return Version.emptyVersion;
+ } else {
+ return new Version(v);
+ }
+ }
+
+ /**
+ * Returns the service id for the service exported through this
+ * endpoint.
+ *
+ * This is the service id under which the framework has registered the service. This
+ * field together with the Framework UUID is a globally unique id for a service.
+ *
+ * @return Service id of a service or 0 if this Endpoint
+ * Description does not relate to an OSGi service
+ *
+ */
+ public long getRemoteServiceID() {
+ return remoteServiceId;
+ }
+
+ /**
+ * Returns the configuration types.
+ *
+ * A distribution provider exports a service with an endpoint. This endpoint
+ * uses some kind of communications protocol with a set of configuration
+ * parameters. There are many different types but each endpoint is
+ * configured by only one configuration type. However, a distribution
+ * provider can be aware of different configuration types and provide
+ * synonyms to increase the change a receiving distribution provider can
+ * create a connection to this endpoint.
+ *
+ * This value represents the
+ * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS}
+ *
+ * @return The configuration type used for the associated endpoint and
+ * optionally synonyms.
+ */
+ public List/* <String> */getConfigurationTypes() {
+ // TODO
+ return null;
+ }
+
+ /**
+ * Return the list of intents implemented by this endpoint.
+ *
+ * The intents are based on the service.intents on an imported service,
+ * except for any intents that are additionally provided by the importing
+ * distribution provider. All qualified intents must have been expanded.
+ *
+ * The property the intents come from is
+ * {@link RemoteConstants#SERVICE_INTENTS}
+ *
+ * @return A list of expanded intents that are provided by this endpoint.
+ */
+ public List/* <String> */getIntents() {
+ // TODO
+ return null;
+
+ }
+
+ /**
+ * Return the framework UUID, if present.
+ *
+ * The property the intents come from is
+ * {@link RemoteConstants#SERVICE_REMOTE_FRAMEWORK_UUID}
+ *
+ * @return Remote Framework UUID, or null if this endpoint is not associated with an OSGi service
+ */
+ public String getRemoteFrameworkUUID() {
+ return remoteFrameworkUUID;
+ }
+
+ /**
+ * Returns all endpoint properties.
+ *
+ * @return An unmodifiable map referring to the properties of this Endpoint
+ * Description.
+ */
+ public Map/* <String, Object> */getProperties() {
+ // TODO
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Answers if this Endpoint Description refers to the same service instance
+ * as the given Endpoint Description.
+ *
+ * Two Endpoint Descriptions point to the same service if they have the same
+ * URI or their framework UUIDs and remote service ids are equal.
+ *
+ * @param other The Endpoint Description to look at
+ * @return True if this endpoint description points to the same service as
+ * the other
+ */
+ public boolean isSameService(EndpointDescription other) {
+ if (remoteUri.equals(other.remoteUri))
+ return true;
+
+ if (remoteFrameworkUUID == null)
+ return false;
+
+ return remoteServiceId == other.remoteServiceId && remoteFrameworkUUID.equals(other.remoteFrameworkUUID);
+ }
+
+ /**
+ * Two endpoints are equal if their URIs are equal, the hash code is
+ * therefore derived from the URI.
+ */
+ public int hashCode() {
+ // TODO
+ return getRemoteURI().hashCode();
+ }
+
+ /**
+ * Two endpoints are equal if their URIs are equal.
+ */
+ public boolean equals(Object other) {
+ if (other instanceof EndpointDescription) {
+ return getRemoteURI().equals(((EndpointDescription)other).getRemoteURI());
+ }
+ return false;
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointListener.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointListener.java
new file mode 100644
index 0000000000..316cac081b
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointListener.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+/**
+ * A white board service that represents a listener for endpoints.
+ *
+ * An Endpoint Listener represents a participant in the distributed model that
+ * is interested in Endpoint Descriptions.
+ *
+ * This white board service can be used in many different scenarios. However, the
+ * primary use case is to allow a remote manager to be informed of End Point
+ * Descriptions available in the network and inform the network about available
+ * End Point Descriptions.
+ *
+ * Both the network bundle and the manager bundle register an Endpoint
+ * Listener service. The manager informs the network bundle about End Points
+ * that it creates. The network bundles then uses a protocol like
+ * SLP to announce these local end-points to the network.
+ *
+ * If the network bundle discovers a new Endpoint through its discovery
+ * protocol, then it sends an End Point Description to all the End Point
+ * Listener services that are registered (except its own) that have specified an
+ * interest in that endpoint.
+ *
+ * Endpoint Listener services can express their <i>scope</i> with the service
+ * property {@link #ENDPOINT_LISTENER_SCOPE}. This service property is a list
+ * of filters. An Endpoint Description should only be given to a Endpoint
+ * Listener when there is at least one filter that matches the Endpoint
+ * Description properties. given to it.
+ *
+ * This filter model is quite flexible. For example, a discovery bundle is only
+ * interested in locally originating Endpoint Descriptions. The following filter
+ * ensure that it only sees local endpoints.
+ *
+ * <pre>
+ * (org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72)
+ * </pre>
+ *
+ * In the same vein, a manager that is only interested in remote Endpoint
+ * Descriptions can use a filter like:
+ *
+ * <pre>
+ * (!(org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72))
+ * </pre>
+ *
+ * Where in both cases, the given UUID is the UUID of the local framework that
+ * can be found in the Framework properties.
+ *
+ * The Endpoint Listener's scope maps very well to the service hooks. A
+ * manager can just register all filters found from the Listener Hook as its
+ * scope. This will automatically provide it with all known endpoints that match
+ * the given scope, without having to inspect the filter string.
+ *
+ * In general, when an Endpoint Description is discovered, it should be
+ * dispatched to all registered Endpoint Listener services. If a new Endpoint
+ * Listener is registered, it should be informed about all currently known
+ * Endpoints that match its scope. If a getter of the Endpoint Listener service
+ * is unregistered, then all its registered Endpoint Description objects must be
+ * removed.
+ *
+ * The Endpoint Listener models a <i>best effort</i> approach. Participating
+ * bundles should do their utmost to keep the listeners up to date, but
+ * implementers should realize that many endpoints come through unreliable
+ * discovery processes.
+ *
+ *
+ * @ThreadSafe
+ */
+public interface EndpointListener {
+ /**
+ * Specifies the interest of this listener with filters. This listener is
+ * only interested in Endpoint Descriptions where its properties match the
+ * given filter. The type of this property must be <code>String+</code>.
+ */
+ String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope";
+
+ /**
+ * Register an endpoint with this listener.
+ *
+ * If the endpoint matches one of the filters registered with the
+ * {@link #ENDPOINT_LISTENER_SCOPE} service property then this filter should
+ * be given as the <code>matchedFilter</code> parameter.
+ *
+ * When this service is first registered or it is modified, it should
+ * receive all known endpoints matching the filter.
+ *
+ * @param endpoint
+ * The Endpoint Description to be published
+ * @param matchedFilter
+ * The filter from the {@link #ENDPOINT_LISTENER_SCOPE} that
+ * matched the endpoint, must not be <code>null</code>.
+ */
+ void endpointAdded(EndpointDescription endpoint, String matchedFilter);
+
+ /**
+ * Remove the registration of an endpoint.
+ *
+ * If an endpoint that was registered with the {@link #endpointAdded(EndpointDescription, String)}
+ * method is no longer available then this method should be called. This
+ * will remove the endpoint from the listener.
+ *
+ * It is not necessary to remove endpoints when the service is unregistered
+ * or modified in such a way that not all endpoints match the interest
+ * filter anymore.
+ *
+ * @param endpoint
+ * The Endpoint Description that is no longer valid.
+ * @param matchedFilter
+ * The filter from the {@link #ENDPOINT_LISTENER_SCOPE} that
+ * matched the endpoint, must not be <code>null</code>.
+ */
+ void endpointRemoved(EndpointDescription endpoint, String matchedFilter);
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointPermission.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointPermission.java
new file mode 100644
index 0000000000..74f913583e
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointPermission.java
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+// TODO Hacked from ServiePermission
+
+/*
+ * Copyright (c) OSGi Alliance (2000, 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.
+ */
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * A bundle's authority to register or get a service.
+ * <ul>
+ * <li>The <code>register</code> action allows a bundle to register a service
+ * on the specified names.
+ * <li>The <code>get</code> action allows a bundle to detect a service and
+ * get it.
+ * </ul>
+ * Permission to get a service is required in order to detect events regarding
+ * the service. Untrusted bundles should not be able to detect the presence of
+ * certain services unless they have the appropriate
+ * <code>EndpointPermission</code> to get the specific service.
+ *
+ * @ThreadSafe
+ * @version $Revision$
+ */
+
+public final class EndpointPermission extends BasicPermission {
+ static final long serialVersionUID = -7662148639076511574L;
+ /**
+ * The action string <code>get</code>.
+ */
+ public final static String EXPORT = "export";
+ /**
+ * The action string <code>register</code>.
+ */
+ public final static String IMPORT = "import";
+
+ public final static String LISTENING = "listening";
+
+ public final static String READ = "listening";
+
+ private final static int ACTION_EXPORT = 0x00000001;
+ private final static int ACTION_IMPORT = 0x00000002;
+ private final static int ACTION_ALL = ACTION_EXPORT | ACTION_IMPORT;
+ final static int ACTION_NONE = 0;
+
+ /**
+ * The actions mask.
+ */
+ transient int action_mask;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private volatile String actions = null;
+
+ /**
+ * The service used by this EndpointPermission. Must be null if not
+ * constructed with a service.
+ */
+ transient final EndpointDescription endpoint;
+
+ /**
+ * The object classes for this EndpointPermission. Must be null if not
+ * constructed with a service.
+ */
+ transient final String[] objectClass;
+
+ /**
+ * If this EndpointPermission was constructed with a filter, this holds a
+ * Filter matching object used to evaluate the filter in implies.
+ */
+ transient Filter filter;
+
+ /**
+ * This dictionary holds the properties of the permission, used to match a
+ * filter in implies. This is not initialized until necessary, and then
+ * cached in this object.
+ */
+ private transient volatile Dictionary properties;
+
+ /**
+ * True if constructed with a name and the name is "*" or ends with ".*".
+ */
+ private transient boolean wildcard;
+
+ /**
+ * If constructed with a name and the name ends with ".*", this contains the
+ * name without the final "*".
+ */
+ private transient String prefix;
+
+ /**
+ * Create a new EndpointPermission.
+ *
+ * <p>
+ * The name of the service is specified as a fully qualified class name.
+ * Wildcards may be used.
+ *
+ * <pre>
+ * name ::= &lt;class name&gt; | &lt;class name ending in &quot;.*&quot;&gt; | *
+ * </pre>
+ *
+ * Examples:
+ *
+ * <pre>
+ * org.osgi.service.http.HttpService
+ * org.osgi.service.http.*
+ * *
+ * </pre>
+ *
+ * For the <code>get</code> action, the name can also be a filter
+ * expression. The filter gives access to the service properties as well as
+ * the following attributes:
+ * <ul>
+ * <li>signer - A Distinguished Name chain used to sign the bundle
+ * publishing the service. Wildcards in a DN are not matched according to
+ * the filter string rules, but according to the rules defined for a DN
+ * chain.</li>
+ * <li>location - The location of the bundle publishing the service.</li>
+ * <li>id - The bundle ID of the bundle publishing the service.</li>
+ * <li>name - The symbolic name of the bundle publishing the service.</li>
+ * </ul>
+ * Since the above attribute names may conflict with service property names
+ * used by a service, you can prefix an attribute name with '@' in the
+ * filter expression to match against the service property and not one of
+ * the above attributes. Filter attribute names are processed in a case
+ * sensitive manner unless the attribute references a service property.
+ * Service properties names are case insensitive.
+ *
+ * <p>
+ * There are two possible actions: <code>get</code> and
+ * <code>register</code>. The <code>get</code> permission allows the
+ * owner of this permission to obtain a service with this name. The
+ * <code>register</code> permission allows the bundle to register a
+ * service under that name.
+ *
+ * @param name
+ * The service class name
+ * @param actions
+ * <code>get</code>,<code>register</code> (canonical order)
+ * @throws IllegalArgumentException
+ * If the specified name is a filter expression and either the
+ * specified action is not <code>get</code> or the filter has
+ * an invalid syntax.
+ */
+ public EndpointPermission(String name, String actions) {
+ this(name, parseActions(actions));
+ if ((filter != null) && ((action_mask & ACTION_ALL) != ACTION_EXPORT)) {
+ throw new IllegalArgumentException("invalid action string for filter expression");
+ }
+ }
+
+ /**
+ * Creates a new requested <code>EndpointPermission</code> object to be
+ * used by code that must perform <code>checkPermission</code> for the
+ * <code>get</code> action. <code>EndpointPermission</code> objects
+ * created with this constructor cannot be added to a
+ * <code>EndpointPermission</code> permission collection.
+ *
+ * @param endpoint
+ * The requested service.
+ * @param actions
+ * The action <code>get</code>.
+ * @throws IllegalArgumentException
+ * If the specified action is not <code>get</code> or
+ * reference is <code>null</code>.
+ * @since 1.5
+ */
+ public EndpointPermission(EndpointDescription endpoint, String actions) {
+ super(createName(endpoint));
+ setTransients(null, parseActions(actions));
+ this.endpoint = endpoint;
+ this.objectClass = (String[])endpoint.getProperties().get(Constants.OBJECTCLASS);
+ if ((action_mask & ACTION_ALL) != ACTION_EXPORT) {
+ throw new IllegalArgumentException("invalid action string");
+ }
+ }
+
+ /**
+ * Create a permission name from a EndpointDescription TODO Needs work
+ *
+ * @param endpoint
+ * EndpointDescription to use to create permission name.
+ * @return permission name.
+ */
+ private static String createName(EndpointDescription endpoint) {
+ if (endpoint == null) {
+ throw new IllegalArgumentException("reference must not be null");
+ }
+ StringBuffer sb = new StringBuffer("(service.id=");
+ // TODO sb.append(endpoint.getProperty(Constants.SERVICE_ID));
+ sb.append(")");
+ return sb.toString();
+ }
+
+ /**
+ * Package private constructor used by EndpointPermissionCollection.
+ *
+ * @param name
+ * class name
+ * @param mask
+ * action mask
+ */
+ EndpointPermission(String name, int mask) {
+ super(name);
+ setTransients(parseFilter(name), mask);
+ this.endpoint = null;
+ this.objectClass = null;
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param mask
+ * action mask
+ */
+ private void setTransients(Filter f, int mask) {
+ if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+ throw new IllegalArgumentException("invalid action string");
+ }
+ action_mask = mask;
+ filter = f;
+ if (f == null) {
+ String name = getName();
+ int l = name.length();
+ /* if "*" or endsWith ".*" */
+ wildcard = ((name.charAt(l - 1) == '*') && ((l == 1) || (name.charAt(l - 2) == '.')));
+ if (wildcard && (l > 1)) {
+ prefix = name.substring(0, l - 1);
+ }
+ }
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions
+ * Action string.
+ * @return action mask.
+ */
+ private static int parseActions(String actions) {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null) {
+ return mask;
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return mask;
+
+ while (i != -1) {
+ char c;
+
+ // skip whitespace
+ while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 2 && (a[i - 2] == 'g' || a[i - 2] == 'G')
+ && (a[i - 1] == 'e' || a[i - 1] == 'E')
+ && (a[i] == 't' || a[i] == 'T')) {
+ matchlen = 3;
+ mask |= ACTION_EXPORT;
+
+ } else if (i >= 7 && (a[i - 7] == 'r' || a[i - 7] == 'R')
+ && (a[i - 6] == 'e' || a[i - 6] == 'E')
+ && (a[i - 5] == 'g' || a[i - 5] == 'G')
+ && (a[i - 4] == 'i' || a[i - 4] == 'I')
+ && (a[i - 3] == 's' || a[i - 3] == 'S')
+ && (a[i - 2] == 't' || a[i - 2] == 'T')
+ && (a[i - 1] == 'e' || a[i - 1] == 'E')
+ && (a[i] == 'r' || a[i] == 'R')) {
+ matchlen = 8;
+ mask |= ACTION_IMPORT;
+
+ } else {
+ // parse error
+ throw new IllegalArgumentException("invalid permission: " + actions);
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarfregister". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma) {
+ switch (a[i - matchlen]) {
+ case ',':
+ seencomma = true;
+ /* FALLTHROUGH */
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\f':
+ case '\t':
+ break;
+ default:
+ throw new IllegalArgumentException("invalid permission: " + actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma) {
+ throw new IllegalArgumentException("invalid permission: " + actions);
+ }
+
+ return mask;
+ }
+
+ /**
+ * Parse filter string into a Filter object.
+ *
+ * @param filterString
+ * The filter string to parse.
+ * @return a Filter for this bundle. If the specified filterString is not a
+ * filter expression, then <code>null</code> is returned.
+ * @throws IllegalArgumentException
+ * If the filter syntax is invalid.
+ */
+ private static Filter parseFilter(String filterString) {
+ filterString = filterString.trim();
+ if (filterString.charAt(0) != '(') {
+ return null;
+ }
+
+ try {
+ return FrameworkUtil.createFilter(filterString);
+ } catch (InvalidSyntaxException e) {
+ IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+
+ /**
+ * Determines if a <code>EndpointPermission</code> object "implies" the
+ * specified permission.
+ *
+ * @param p
+ * The target permission to check.
+ * @return <code>true</code> if the specified permission is implied by
+ * this object; <code>false</code> otherwise.
+ */
+ public boolean implies(Permission p) {
+ if (!(p instanceof EndpointPermission)) {
+ return false;
+ }
+ EndpointPermission requested = (EndpointPermission)p;
+ if (endpoint != null) {
+ return false;
+ }
+ // if requested permission has a filter, then it is an invalid argument
+ if (requested.filter != null) {
+ return false;
+ }
+ return implies0(requested, ACTION_NONE);
+ }
+
+ /**
+ * Internal implies method. Used by the implies and the permission
+ * collection implies methods.
+ *
+ * @param requested
+ * The requested EndpointPermission which has already be
+ * validated as a proper argument. The requested
+ * EndpointPermission must not have a filter expression.
+ * @param effective
+ * The effective actions with which to start.
+ * @return <code>true</code> if the specified permission is implied by
+ * this object; <code>false</code> otherwise.
+ */
+ boolean implies0(EndpointPermission requested, int effective) {
+ /* check actions first - much faster */
+ effective |= action_mask;
+ final int desired = requested.action_mask;
+ if ((effective & desired) != desired) {
+ return false;
+ }
+ /* we have name of "*" */
+ if (wildcard && (prefix == null)) {
+ return true;
+ }
+ /* if we have a filter */
+ Filter f = filter;
+ if (f != null) {
+ return f.matchCase(requested.getProperties());
+ }
+ /* if requested permission not created with EndpointDescription */
+ String[] requestedNames = requested.objectClass;
+ if (requestedNames == null) {
+ return super.implies(requested);
+ }
+ /* requested permission created with EndpointDescription */
+ if (wildcard) {
+ int pl = prefix.length();
+ for (int i = 0, l = requestedNames.length; i < l; i++) {
+ String requestedName = requestedNames[i];
+ if ((requestedName.length() > pl) && requestedName.startsWith(prefix)) {
+ return true;
+ }
+ }
+ } else {
+ String name = getName();
+ for (int i = 0, l = requestedNames.length; i < l; i++) {
+ if (requestedNames[i].equals(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the canonical string representation of the actions. Always
+ * returns present actions in the following order: <code>get</code>,
+ * <code>register</code>.
+ *
+ * @return The canonical string representation of the actions.
+ */
+ public String getActions() {
+ String result = actions;
+ if (result == null) {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ int mask = action_mask;
+ if ((mask & ACTION_EXPORT) == ACTION_EXPORT) {
+ sb.append(EXPORT);
+ comma = true;
+ }
+
+ if ((mask & ACTION_IMPORT) == ACTION_IMPORT) {
+ if (comma)
+ sb.append(',');
+ sb.append(IMPORT);
+ }
+
+ actions = result = sb.toString();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new <code>PermissionCollection</code> object for storing
+ * <code>EndpointPermission<code> objects.
+ *
+ * @return A new <code>PermissionCollection</code> object suitable for storing
+ * <code>EndpointPermission</code> objects.
+ */
+ public PermissionCollection newPermissionCollection() {
+ return new EndpointPermissionCollection();
+ }
+
+ /**
+ * Determines the equality of two EndpointPermission objects.
+ *
+ * Checks that specified object has the same class name and action as this
+ * <code>EndpointPermission</code>.
+ *
+ * @param obj
+ * The object to test for equality.
+ * @return true if obj is a <code>EndpointPermission</code>, and has the
+ * same class name and actions as this
+ * <code>EndpointPermission</code> object; <code>false</code>
+ * otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof EndpointPermission)) {
+ return false;
+ }
+
+ EndpointPermission sp = (EndpointPermission)obj;
+
+ return (action_mask == sp.action_mask) && getName().equals(sp.getName())
+ && ((endpoint == sp.endpoint) || ((endpoint != null) && (sp.endpoint != null) && endpoint
+ .equals(sp.endpoint)));
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return Hash code value for this object.
+ */
+ public int hashCode() {
+ int h = 31 * 17 + getName().hashCode();
+ h = 31 * h + getActions().hashCode();
+ if (endpoint != null) {
+ h = 31 * h + endpoint.hashCode();
+ }
+ return h;
+ }
+
+ /**
+ * WriteObject is called to save the state of this permission to a stream.
+ * The actions are serialized, and the superclass takes care of the name.
+ */
+ private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+ if (endpoint != null) {
+ throw new NotSerializableException("cannot serialize");
+ }
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null)
+ getActions();
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of this permission from a
+ * stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+ // Read in the action, then initialize the rest
+ s.defaultReadObject();
+ setTransients(parseFilter(getName()), parseActions(actions));
+ }
+
+ /**
+ * Called by <code><@link EndpointPermission#implies(Permission)></code>.
+ *
+ * @return a dictionary of properties for this permission.
+ */
+ private Dictionary/*<String,Object>*/getProperties() {
+ Dictionary/*<String, Object>*/result = properties;
+ if (result != null) {
+ return result;
+ }
+ if (endpoint == null) {
+ result = new Hashtable/*<String, Object>*/(1);
+ if (filter == null) {
+ result.put(Constants.OBJECTCLASS, new String[] {getName()});
+ }
+ return properties = result;
+ }
+ final Map props = new HashMap(4);
+ // TODO needs work
+ /*
+ final Bundle bundle = endpoint.getBundle();
+ if (bundle != null) {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ props.put("id", new Long(bundle.getBundleId()));
+ props.put("location", bundle.getLocation());
+ String name = bundle.getSymbolicName();
+ if (name != null) {
+ props.put("name", name);
+ }
+ SignerProperty signer = new SignerProperty(bundle);
+ if (signer.isBundleSigned()) {
+ props.put("signer", signer);
+ }
+ return null;
+ }
+ });
+ }
+ */
+ return properties = new Properties(props, endpoint);
+ }
+
+ private static class Properties extends Dictionary {
+ private final Map properties;
+ private final EndpointDescription service;
+
+ Properties(Map properties, EndpointDescription service) {
+ this.properties = properties;
+ this.service = service;
+ }
+
+ public Object get(Object k) {
+ if (!(k instanceof String)) {
+ return null;
+ }
+ String key = (String)k;
+ if (key.charAt(0) == '@') {
+ return service.getProperties().get(key.substring(1));
+ }
+ Object value = properties.get(key);
+ if (value != null) { // fall back to service properties
+ return value;
+ }
+ return service.getProperties().get(key);
+ }
+
+ public int size() {
+ return properties.size() + service.getProperties().size();
+ }
+
+ public boolean isEmpty() {
+ // we can return false because this must never be empty
+ return false;
+ }
+
+ public Enumeration keys() {
+ Collection pk = properties.keySet();
+ String spk[] =
+ (String[])service.getProperties().keySet().toArray(new String[service.getProperties().size()]);
+ List all = new ArrayList(pk.size() + spk.length);
+ all.addAll(pk);
+ add: for (int i = 0, length = spk.length; i < length; i++) {
+ String key = spk[i];
+ for (Iterator iter = pk.iterator(); iter.hasNext();) {
+ if (key.equalsIgnoreCase((String)iter.next())) {
+ continue add;
+ }
+ }
+ all.add(key);
+ }
+ return Collections.enumeration(all);
+ }
+
+ public Enumeration elements() {
+ Collection pk = properties.keySet();
+ String spk[] =
+ (String[])service.getProperties().keySet().toArray(new String[service.getProperties().size()]);
+ List all = new ArrayList(pk.size() + spk.length);
+ all.addAll(properties.values());
+ add: for (int i = 0, length = spk.length; i < length; i++) {
+ String key = spk[i];
+ for (Iterator iter = pk.iterator(); iter.hasNext();) {
+ if (key.equalsIgnoreCase((String)iter.next())) {
+ continue add;
+ }
+ }
+ all.add(service.getProperties().get(key));
+ }
+ return Collections.enumeration(all);
+ }
+
+ public Object put(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
+
+/**
+ * Stores a set of EndpointPermission permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class EndpointPermissionCollection extends PermissionCollection {
+ static final long serialVersionUID = 662615640374640621L;
+ /**
+ * Table of permissions.
+ *
+ * @GuardedBy this
+ */
+ private transient Map permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private boolean all_allowed;
+
+ /**
+ * Table of permissions with filter expressions.
+ *
+ * @serial
+ * @GuardedBy this
+ */
+ private Map filterPermissions;
+
+ /**
+ * Creates an empty EndpointPermissions object.
+ */
+ public EndpointPermissionCollection() {
+ permissions = new HashMap();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds a permission to this permission collection.
+ *
+ * @param permission
+ * The Permission object to add.
+ * @throws IllegalArgumentException
+ * If the specified permission is not a EndpointPermission
+ * object.
+ * @throws SecurityException
+ * If this <code>EndpointPermissionCollection</code> object
+ * has been marked read-only.
+ */
+ public void add(final Permission permission) {
+ if (!(permission instanceof EndpointPermission)) {
+ throw new IllegalArgumentException("invalid permission: " + permission);
+ }
+ if (isReadOnly()) {
+ throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+ }
+
+ final EndpointPermission sp = (EndpointPermission)permission;
+ if (sp.endpoint != null) {
+ throw new IllegalArgumentException("cannot add to collection: " + sp);
+ }
+
+ final String name = sp.getName();
+ final Filter f = sp.filter;
+ synchronized (this) {
+ /* select the bucket for the permission */
+ Map pc;
+ if (f != null) {
+ pc = filterPermissions;
+ if (pc == null) {
+ filterPermissions = pc = new HashMap();
+ }
+ } else {
+ pc = permissions;
+ }
+ final EndpointPermission existing = (EndpointPermission)pc.get(name);
+
+ if (existing != null) {
+ final int oldMask = existing.action_mask;
+ final int newMask = sp.action_mask;
+ if (oldMask != newMask) {
+ pc.put(name, new EndpointPermission(name, oldMask | newMask));
+ }
+ } else {
+ pc.put(name, sp);
+ }
+
+ if (!all_allowed) {
+ if (name.equals("*")) {
+ all_allowed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if a set of permissions implies the permissions expressed in
+ * <code>permission</code>.
+ *
+ * @param permission
+ * The Permission object to compare.
+ * @return <code>true</code> if <code>permission</code> is a proper
+ * subset of a permission in the set; <code>false</code>
+ * otherwise.
+ */
+ public boolean implies(final Permission permission) {
+ if (!(permission instanceof EndpointPermission)) {
+ return false;
+ }
+ final EndpointPermission requested = (EndpointPermission)permission;
+ /* if requested permission has a filter, then it is an invalid argument */
+ if (requested.filter != null) {
+ return false;
+ }
+
+ int effective = EndpointPermission.ACTION_NONE;
+ Collection perms;
+ synchronized (this) {
+ final int desired = requested.action_mask;
+ /* short circuit if the "*" Permission was added */
+ if (all_allowed) {
+ EndpointPermission sp = (EndpointPermission)permissions.get("*");
+ if (sp != null) {
+ effective |= sp.action_mask;
+ if ((effective & desired) == desired) {
+ return true;
+ }
+ }
+ }
+
+ String[] requestedNames = requested.objectClass;
+ /* if requested permission not created with EndpointDescription */
+ if (requestedNames == null) {
+ effective |= effective(requested.getName(), desired, effective);
+ if ((effective & desired) == desired) {
+ return true;
+ }
+ }
+ /* requested permission created with EndpointDescription */
+ else {
+ for (int i = 0, l = requestedNames.length; i < l; i++) {
+ if ((effective(requestedNames[i], desired, effective) & desired) == desired) {
+ return true;
+ }
+ }
+ }
+ Map pc = filterPermissions;
+ if (pc == null) {
+ return false;
+ }
+ perms = pc.values();
+ }
+
+ /* iterate one by one over filteredPermissions */
+ for (Iterator iter = perms.iterator(); iter.hasNext();) {
+ if (((EndpointPermission)iter.next()).implies0(requested, effective)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Consult permissions map to compute the effective permission for the
+ * requested permission name.
+ *
+ * @param requestedName
+ * The requested service name.
+ * @param desired
+ * The desired actions.
+ * @param effective
+ * The effective actions.
+ * @return The new effective actions.
+ */
+ private int effective(String requestedName, final int desired, int effective) {
+ final Map pc = permissions;
+ EndpointPermission sp = (EndpointPermission)pc.get(requestedName);
+ // strategy:
+ // Check for full match first. Then work our way up the
+ // name looking for matches on a.b.*
+ if (sp != null) {
+ // we have a direct hit!
+ effective |= sp.action_mask;
+ if ((effective & desired) == desired) {
+ return effective;
+ }
+ }
+ // work our way up the tree...
+ int last;
+ int offset = requestedName.length() - 1;
+ while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+ requestedName = requestedName.substring(0, last + 1) + "*";
+ sp = (EndpointPermission)pc.get(requestedName);
+ if (sp != null) {
+ effective |= sp.action_mask;
+ if ((effective & desired) == desired) {
+ return effective;
+ }
+ }
+ offset = last - 1;
+ }
+ /*
+ * we don't have to check for "*" as it was already checked before we
+ * were called.
+ */
+ return effective;
+ }
+
+ /**
+ * Returns an enumeration of all the <code>EndpointPermission</code>
+ * objects in the container.
+ *
+ * @return Enumeration of all the EndpointPermission objects.
+ */
+ public synchronized Enumeration elements() {
+ List all = new ArrayList(permissions.values());
+ Map pc = filterPermissions;
+ if (pc != null) {
+ all.addAll(pc.values());
+ }
+ return Collections.enumeration(all);
+ }
+
+ /* serialization logic */
+ private static final ObjectStreamField[] serialPersistentFields =
+ {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE),
+ new ObjectStreamField("filterPermissions", HashMap.class)};
+
+ private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+ Hashtable hashtable = new Hashtable(permissions);
+ ObjectOutputStream.PutField pfields = out.putFields();
+ pfields.put("permissions", hashtable);
+ pfields.put("all_allowed", all_allowed);
+ pfields.put("filterPermissions", filterPermissions);
+ out.writeFields();
+ }
+
+ private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField gfields = in.readFields();
+ Hashtable hashtable = (Hashtable)gfields.get("permissions", null);
+ permissions = new HashMap(hashtable);
+ all_allowed = gfields.get("all_allowed", false);
+ filterPermissions = (HashMap)gfields.get("filterPermissions", null);
+ }
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ExportRegistration.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ExportRegistration.java
new file mode 100644
index 0000000000..0e747293cb
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ExportRegistration.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Export Registration associates a service to a local endpoint.
+ *
+ * The Export Registration can be used to delete the endpoint associated with an
+ * this registration. It is created with the
+ * {@link RemoteServiceAdmin#exportService(ServiceReference,java.util.Map)} method.
+ *
+ * When this Export Registration has been unregistered, the methods must all
+ * return <code>null</code>.
+ *
+ * @ThreadSafe
+ */
+public interface ExportRegistration {
+ /**
+ * Return the service being exported.
+ *
+ * @return The service being exported, must be <code>null</code> when this
+ * registration is unregistered.
+ * @throws IllegalStateException Thrown when this object was not properly initialized, see {@link #getException()}
+ */
+ ServiceReference getExportedService() throws IllegalStateException;
+
+ /**
+ * Return the Endpoint Description that is created for this registration.
+ *
+ * @return the local Endpoint Description
+ * @throws IllegalStateException Thrown when this object was not properly initialized, see {@link #getException()}
+ */
+ EndpointDescription getEndpointDescription();
+
+ /**
+ * Delete the local endpoint and disconnect any remote distribution
+ * providers. After this method returns, all the methods must return
+ * <code>null</code>.
+ *
+ * This method has no effect when the endpoint is already destroyed or being
+ * destroyed.
+ */
+ void close();
+
+ /**
+ * Exception for any error during the import process.
+ *
+ * If the Remote Admin for some reasons is unable to create a registration,
+ * then it must return a <code>Throwable</code> from this method. In this
+ * case, all other methods must return on this interface must throw an
+ * Illegal State Exception. If no error occurred, this method must return
+ * <code>null</code>.
+ *
+ * The error must be set before this Import Registration is returned.
+ * Asynchronously occurring errors must be reported to the log.
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or <code>null</code> if no exception occurred.
+ */
+ Throwable getException();
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ImportRegistration.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ImportRegistration.java
new file mode 100644
index 0000000000..0346902383
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/ImportRegistration.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Import Registration associates an active proxy service to a remote
+ * endpoint.
+ *
+ * The Import Registration can be used to delete the proxy associated with an
+ * endpoint. It is created with the{@link RemoteServiceAdmin#importService}
+ * method.
+ *
+ * @ThreadSafe
+ */
+public interface ImportRegistration {
+ /**
+ * Answer the associated Service Reference for the proxy to the endpoint.
+ *
+ * @return A Service Reference to the proxy for the endpoint.
+ * @throws IllegalStateException Thrown when this object was not properly initialized, see {@link #getException()}
+ */
+ ServiceReference getImportedService();
+
+ /**
+ * Answer the associated remote Endpoint Description.
+ *
+ * @return A Endpoint Description for the remote endpoint.
+ * @throws IllegalStateException Thrown when this object was not properly initialized, see {@link #getException()}
+ */
+ EndpointDescription getImportedEndpointDescription();
+
+ /**
+ * Unregister this Import Registration. This must close the connection
+ * to the end endpoint unregister the proxy. After this method returns,
+ * all other methods must return null.
+ *
+ * This method has no effect when the service is already unregistered or in the process off.
+ */
+ void close();
+
+ /**
+ * Exception for any error during the import process.
+ *
+ * If the Remote Admin for some reasons is unable to create a registration,
+ * then it must return a <code>Throwable</code> from this method. In this
+ * case, all other methods must return on this interface must thrown an
+ * Illegal State Exception. If no error occurred, this method must return
+ * <code>null</code>.
+ *
+ * The error must be set before this Import Registration is returned.
+ * Asynchronously occurring errors must be reported to the log.
+ *
+ * TODO can we assume the proxy service is registered when getException==null?
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or <code>null</code> if no exception occurred.
+ */
+ Throwable getException();
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteConstants.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteConstants.java
new file mode 100644
index 0000000000..afc3355c38
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteConstants.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+/**
+ * Provide the definition of the constants used in the Remote Services API.
+ *
+ * TODO Should some of the properties in chapter 13 go the framework constants?
+ */
+public class RemoteConstants {
+ private RemoteConstants() {
+ }
+
+ /**
+ * The configuration types supported by this Distribution Provider.
+ *
+ * Services that are suitable for distribution list the configuration types
+ * that describe the configuration information for that service in the
+ * {@link #SERVICE_EXPORTED_CONFIGS} or {@link #SERVICE_IMPORTED_CONFIGS}
+ * property.
+ *
+ * A distribution provider must register a service that has this property
+ * and enumerate all configuration types that it supports.
+ *
+ * The type of this property <code>String+</code>
+ *
+ * @see #SERVICE_EXPORTED_CONFIGS
+ * @see #SERVICE_IMPORTED_CONFIGS
+ */
+ public final static String REMOTE_CONFIGS_SUPPORTED = "remote.configs.supported";
+
+ /**
+ * Service property that lists the intents supported by the distribution
+ * provider.
+ *
+ * Each distribution provider must register a service that has this property
+ * and enumerate all the supported intents, having any qualified intents
+ * expanded.
+ *
+ * The value of this property is of type <code>String+</code>.
+ *
+ * @see #SERVICE_INTENTS
+ * @see #SERVICE_EXPORTED_INTENTS
+ * @see #SERVICE_EXPORTED_INTENTS_EXTRA
+ */
+ public final static String REMOTE_INTENTS_SUPPORTED = "remote.intents.supported";
+
+ /**
+ * Defines the interfaces under which this service can be exported.
+ *
+ * This list must be a subset of the types listed in the objectClass service
+ * property. The single value of an asterisk ('*' \u002A) indicates all
+ * interfaces in the registration's objectClass property (not classes). It
+ * is highly recommended to only export interfaces and not concrete classes
+ * due to the complexity of creating proxies for some type of classes.
+ *
+ * The value of this property is of type String+.
+ */
+ public final static String SERVICE_EXPORTED_INTERFACES = "service.exported.interfaces";
+
+ /**
+ * A list of intents that the distribution provider must implement to
+ * distribute the service. Intents listed in this property are reserved for
+ * intents that are critical for the code to function correctly, for
+ * example, ordering of messages. These intents should not be configurable.
+ *
+ * The value of this property is of type <code>String+</code>.
+ */
+ public final static String SERVICE_EXPORTED_INTENTS = "service.exported.intents";
+
+ /**
+ * Extra intents configured in addition to the the intents specified in
+ * {@link #SERVICE_EXPORTED_INTENTS}.
+ *
+ * These intents are merged with the service.exported.intents and therefore
+ * have the same semantics. They are extra, so that the
+ * {@link #SERVICE_EXPORTED_INTENTS} can be set by the bundle developer and
+ * this property is then set by the administrator/deployer. Bundles should
+ * make this property configurable, for example through the Configuration
+ * Admin service.
+ *
+ * The value of this property is of type <code>String+</code>.
+ */
+ public final static String SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra";
+
+ /**
+ * A list of configuration types that should be used to export the service.
+ *
+ * Configuration types can be <em>synonymous</em> or <em>alternatives</em>.
+ * In principle, a distribution provider should create an endpoint for each
+ * recognized configuration type, the deployer is responsible that synonyms
+ * do not clash.
+ *
+ * Each configuration type has an associated specification that describes
+ * how the configuration data for the exported service is represented in an
+ * OSGi framework.
+ *
+ * The value of this property is of type <code>String+</code>.
+ */
+ public final static String SERVICE_EXPORTED_CONFIGS = "service.exported.configs";
+
+ /**
+ * Must be set by a distribution provider to <code>true</code> when it
+ * registers the end-point proxy as an imported service. Can be used by a
+ * bundle to prevent it from getting an imported service.
+ *
+ * The value of this property is not defined, setting it is sufficient.
+ */
+ public final static String SERVICE_IMPORTED = "service.imported";
+
+ /**
+ * The configuration type used to import this services, as described in
+ * {@link #SERVICE_EXPORTED_CONFIGS}. Any associated properties for this
+ * configuration types must be properly mapped to the importing system. For
+ * example, a URL in these properties must point to a valid resource when
+ * used in the importing framework. Configuration types in this property
+ * must be synonymous.
+ *
+ * The value of this property is of type <code>String+</code>.
+ */
+ public final String SERVICE_IMPORTED_CONFIGS = "service.imported.configs";
+
+ /**
+ * A list of intents that this service implements. This property has dual
+ * purpose. A bundle can use this service property to notify the
+ * distribution provider that these intents are already implemented by the
+ * exported service object. For an imported service, a distribution provider
+ * must use this property to convey the combined intents of the exporting
+ * service and the intents that the distribution providers add. To export a
+ * service, a distribution provider must recognize all these intents and
+ * expand any qualified intents.
+ *
+ * The value of this property is of type <code>String+</code>.
+ */
+ public final static String SERVICE_INTENTS = "service.intents";
+
+ /**
+ * The property key for the endpoint URI. This is a unique id for an
+ * endpoint following the URI syntax. As far as this specification is
+ * concerned, this unique id is opaque.
+ */
+ final public static String SERVICE_REMOTE_URI = "service.remote.uri";
+
+ /**
+ * The property key for the endpoint service id. This is a unique id for a
+ * service based on the framework id '.' service id or another model. As far as this specification is
+ * concerned, this unique id is opaque.
+ */
+ final public static String SERVICE_REMOTE_ID = "service.remote.id";
+
+ /**
+ * The property key for the endpoint service id. This is a unique id for a
+ * service based on the framework id '.' service id or another model. As far as this specification is
+ * concerned, this unique id is opaque.
+ */
+ final public static String SERVICE_REMOTE_FRAMEWORK_UUID = "service.remote.framework.id";
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdmin.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdmin.java
new file mode 100644
index 0000000000..dade67c096
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdmin.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A Remote Service Admin manages the import and export of services.
+ *
+ * A Distribution Provider can expose a control interface. This interface allows
+ * the a remote manager to control the export and import of services.
+ *
+ * The API allows a remote manager to export a service, to import a service, and
+ * find out about the current imports and exports.
+ *
+ *
+ *
+ * @ThreadSafe
+ */
+public interface RemoteServiceAdmin {
+
+ /**
+ * Export a service to a given endpoint. The Remote Service Admin must
+ * create an endpoint from the given description that can be used by other
+ * Distrbution Providers to connect to this Remote Service Admin and use the
+ * exported service. This method can return null if the service could not be
+ * exported because the endpoint could not be implemented by this Remote
+ * Service Admin.
+ *
+ * The properties on a Service Reference are case insensitive while the
+ * properties on a <code>properties</code> are case sensitive. A value in
+ * the <code>properties</code> must therefore override any case variant in
+ * the properties of the Service Reference.
+ *
+ * If an endpoint can not be created because no
+ * {@link EndpointPermission#EXPORT} can be obtained to export this service,
+ * then this endpoint must be ignored and no Export Registration must be
+ * included in the returned list.
+ *
+ * @param ref The Service Reference to export
+ * @param properties The properties to create a local endpoint that can be
+ * implemented by this Remote Service Admin. If this is null, the
+ * endpoint will be determined by the properties on the service. The
+ * properties are the same as given for an exported service. They are
+ * overlaid over any properties the service defines (case
+ * insensitive). This parameter can be <code>null</code>, this
+ * should be treated as an empty map.
+ * @return An Export Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code> if the service could
+ * not be exported
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ *
+ * TODO discuss case difference in properties
+ *
+ * TODO More exceptions?
+ * TODO Can you export ANY service by providing the proper properties?
+ */
+ List/* <ExportRegistration> */exportService(ServiceReference ref, Map/* <String,Object> */properties)
+ throws IllegalArgumentException, UnsupportedOperationException;
+
+ /**
+ * Import a service from an endpoint. The Remote Service Admin must use the
+ * given endpoint to create a proxy. This method can return null if the
+ * service could not be imported.
+ *
+ * TODO if the import reg. is valid (getException==null), can we then assume the
+ * service is registered?
+ *
+ * If an endpoint can not be imported because no
+ * {@link EndpointPermission#IMPORT} can be obtained, then this endpoint
+ * must be ignored and no Import Registration must included in the returned
+ * list.
+ *
+ * @param endpoint The Endpoint Description to be used for import
+ * @return An Import Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code> if the endpoint
+ * could not be imported
+ */
+ ImportRegistration importService(EndpointDescription endpoint);
+
+ /**
+ * Answer the currently active Export Registrations.
+ *
+ * @return A collection of Export Registrations that are currently active.
+ * @throws SecurityException When the caller no
+ * {@link EndpointPermission#READ} could be obtained
+ *
+ * TODO I guess we must ensure these registrations cannot be closed? Only the owners should be able to close them,
+ * TODO should we make sure that the list contains the registration objects that the caller created?
+ */
+ Collection/* <ExportRegistration> */getExportedServices();
+
+ /**
+ * Answer the currently active Import Registrations.
+ *
+ * @throws SecurityException When the caller no EndpointPermission LIST
+ * could be obtained
+ * @return A collection of Import Registrations that are currently active.
+ * @throws SecurityException When the caller no
+ * {@link EndpointPermission#READ} could be obtained
+ *
+ * TODO I guess we must ensure these registrations cannot be closed? Only the owners should be able to close them,
+ * TODO should we make sure that the list contains the registration objects that the caller created?
+ */
+ Collection/* <ImportRegistration> */getImportedEndpoints();
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminEvent.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminEvent.java
new file mode 100644
index 0000000000..d0eda01f6e
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminEvent.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ *
+ * Provides the event information for a Remote Admin event.
+ *
+ * @Immutable
+ */
+public class RemoteServiceAdminEvent {
+ /**
+ * Add an import registration. The Remote Services Admin will call this
+ * method when it imports a service. When this service is registered, the
+ * Remote Service Admin must notify the listener of all existing Import
+ * Registrations.
+ *
+ */
+ public static final int IMPORT_REGISTRATION = 1;
+
+ /**
+ * Add an export registration. The Remote Services Admin will call this
+ * method when it exports a service. When this service is registered, the
+ * Remote Service Admin must notify the listener of all existing Export
+ * Registrations.
+ */
+ public static final int EXPORT_REGISTRATION = 2;
+
+ /**
+ * Remove an export registration. The Remote Services Admin will call this
+ * method when it removes the export of a service.
+ *
+ */
+ public static final int EXPORT_UNREGISTRATION = 3;
+
+ /**
+ * Remove an import registration. The Remote Services Admin will call this
+ * method when it removes the import of a service.
+ *
+ */
+ public static final int IMPORT_UNREGISTRATION = 4;
+
+ /**
+ * A fatal importing error occurred. The Import Registration has been
+ * closed.
+ */
+ public static final int IMPORT_ERROR = 5;
+
+ /**
+ * A fatal exporting error occurred. The Export Registration has been
+ * closed.
+ */
+ public static final int EXPORT_ERROR = 6;
+
+ /**
+ * A problematic situation occurred, the export is still active.
+ */
+ public static final int EXPORT_WARNING = 7;
+ /**
+ * A problematic situation occurred, the import is still active.
+ */
+ public static final int IMPORT_WARNING = 8;
+
+ private final ImportRegistration importRegistration;
+ private final ExportRegistration exportRegistration;
+ private final Throwable exception;
+ private final int type;
+ private final Bundle source;
+
+ /**
+ * Private constructor.
+ *
+ * @param type The event type
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param importRegistration The importRegistration, can be <code>null</code>.
+ * @param exportRegistration The exportRegistration, can be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>
+ */
+ RemoteServiceAdminEvent(int type,
+ Bundle source,
+ ImportRegistration importRegistration,
+ ExportRegistration exportRegistration,
+ Throwable exception) {
+ this.type = type;
+ this.source = source;
+ this.importRegistration = importRegistration;
+ this.exportRegistration = exportRegistration;
+ this.exception = exception;
+ }
+
+ /**
+ * Create a Remote Service Admin Event for an export issue.
+ *
+ * @param type The event type
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param exportRegistration The exportRegistration, can not be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>
+ */
+ public RemoteServiceAdminEvent(int type, Bundle source, ExportRegistration exportRegistration, Throwable exception) {
+ this(type, source, null, exportRegistration, exception);
+ }
+
+ /**
+ * Create a Remote Service Admin Event for an import issue.
+ *
+ * @param type The event type
+ * @param source The source bundle, must not be <code>null</code>.
+ * @param importRegistration The importRegistration, can not be <code>null</code>.
+ * @param exception Any exceptions encountered, can be <code>null</code>
+ */
+ public RemoteServiceAdminEvent(int type, Bundle source, ImportRegistration importRegistration, Throwable exception) {
+ this(type, source, importRegistration, null, exception);
+ }
+
+ /**
+ * @return the importRegistration or <code>null</code>
+ */
+ public ImportRegistration getImportRegistration() {
+ return importRegistration;
+ }
+
+ /**
+ * @return the exportRegistration or <code>null</code>
+ */
+ public ExportRegistration getExportRegistration() {
+ return exportRegistration;
+ }
+
+ /**
+ * @return the exception or <code>null</code>
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * @return the type
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * @return the source
+ */
+ public Bundle getSource() {
+ return source;
+ }
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminListener.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminListener.java
new file mode 100644
index 0000000000..40f68a4ba8
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/RemoteServiceAdminListener.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 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.osgi.remoteserviceadmin;
+
+/**
+ * A Remote Service Admin Listener is notified asynchronously of any export or
+ * import registrations and unregistrations.
+ *
+ * @ThreadSafe
+ */
+
+public interface RemoteServiceAdminListener {
+ /**
+ * Receive a Remote Service Admin event.
+ * @param event
+ */
+ void remoteAdminEvent(RemoteServiceAdminEvent event);
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java
new file mode 100644
index 0000000000..7c9cf8ef6e
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java
@@ -0,0 +1,97 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class AbstractOSGiServiceHandler implements LifeCycleListener {
+ protected ExtensionPointRegistry registry;
+ protected BundleContext context;
+ protected NodeFactoryImpl nodeFactory;
+ protected EndpointIntrospector introspector;
+ protected ServiceTracker discoveryTracker;
+ protected String domainRegistry;
+ protected String domainURI;
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ protected AbstractOSGiServiceHandler(BundleContext context) {
+ this.context = context;
+ this.domainRegistry = context.getProperty("org.osgi.sca.domain.registry");
+ this.domainURI = context.getProperty("org.osgi.sca.domain.uri");
+ }
+
+ protected ExtensionPointRegistry getExtensionPointRegistry() {
+ if (registry == null) {
+ ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null);
+ tracker.open();
+ // tracker.waitForService(1000);
+ registry = (ExtensionPointRegistry)tracker.getService();
+ tracker.close();
+ }
+ return registry;
+ }
+
+ protected synchronized void init() {
+ if (nodeFactory == null) {
+ this.nodeFactory = (NodeFactoryImpl)NodeFactory.newInstance();
+ this.nodeFactory.init();
+ this.discoveryTracker = LocalDiscoveryService.getTracker(context);
+ discoveryTracker.open();
+ this.introspector = new EndpointIntrospector(context, getExtensionPointRegistry(), discoveryTracker);
+ }
+ }
+
+ public void start() {
+ init();
+ }
+
+ public void stop() {
+ if (nodeFactory != null) {
+ discoveryTracker.close();
+ discoveryTracker = null;
+ introspector = null;
+ nodeFactory = null;
+ registry = null;
+ context = null;
+ }
+ }
+
+ public void setDomainRegistry(String domainRegistry) {
+ this.domainRegistry = domainRegistry;
+ }
+
+ public void setDomainURI(String domainURI) {
+ this.domainURI = domainURI;
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java
new file mode 100644
index 0000000000..d50d1ef9f9
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java
@@ -0,0 +1,82 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.interfacedef.Interface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Implementation of {@link EndpointDescription}
+ */
+public class EndpointHelper {
+ private EndpointHelper() {
+ }
+
+ public static EndpointDescription createEndpointDescription(BundleContext bundleContext, Endpoint endpoint) {
+ return new EndpointDescription(getProperties(bundleContext, endpoint));
+ }
+
+ private static List<String> getInterfaces(Endpoint endpoint) {
+ Interface intf = endpoint.getInterfaceContract().getInterface();
+ JavaInterface javaInterface = (JavaInterface)intf;
+ return Collections.singletonList(javaInterface.getName());
+ }
+
+ private static Map<String, Object> getProperties(BundleContext bundleContext, Endpoint endpoint) {
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ if (!endpoint.isRemote()) {
+ String uuid = OSGiHelper.getFrameworkUUID(bundleContext);
+ props.put(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID, uuid);
+ }
+
+ for (Object ext : endpoint.getService().getExtensions()) {
+ if (ext instanceof OSGiProperty) {
+ OSGiProperty prop = (OSGiProperty)ext;
+ props.put(prop.getName(), prop.getStringValue());
+ }
+ }
+
+ props.put(RemoteConstants.SERVICE_REMOTE_ID, props.get(Constants.SERVICE_ID));
+ props.put(RemoteConstants.SERVICE_REMOTE_URI, endpoint.getURI());
+ // FIXME: [rfeng] How to pass in the remote service id from the endpoint XML
+ props.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, new String[] {"org.osgi.sca"});
+ props.put(Endpoint.class.getName(), endpoint);
+ List<String> interfaces = getInterfaces(endpoint);
+ props.put(Constants.OBJECTCLASS, interfaces.toArray(new String[interfaces.size()]));
+ return props;
+ }
+
+ public static Endpoint getEndpoint(EndpointDescription endpointDescription) {
+ return (Endpoint)endpointDescription.getProperties().get(Endpoint.class.getName());
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java
new file mode 100644
index 0000000000..a649dac357
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java
@@ -0,0 +1,474 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.assembly.Base.SCA11_TUSCANY_NS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SCA_BINDINGS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS_EXTRA;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getStringArray;
+import static org.osgi.framework.Constants.OBJECTCLASS;
+import static org.osgi.framework.Constants.SERVICE_ID;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.tuscany.sca.assembly.AssemblyFactory;
+import org.apache.tuscany.sca.assembly.Base;
+import org.apache.tuscany.sca.assembly.Binding;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Composite;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Reference;
+import org.apache.tuscany.sca.assembly.Service;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.ContributionFactory;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ContributionResolveException;
+import org.apache.tuscany.sca.contribution.resolver.ExtensibleModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolverExtensionPoint;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
+import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService;
+import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService.ExtenderConfiguration;
+import org.apache.tuscany.sca.policy.Intent;
+import org.apache.tuscany.sca.policy.PolicyFactory;
+import org.oasisopen.sca.ServiceRuntimeException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Introspect an OSGi Service to create an SCA composite that contains a single component with
+ * implementation.osgi
+ */
+public class EndpointIntrospector {
+ // private BundleContext context;
+ private AssemblyFactory assemblyFactory;
+ private ContributionFactory contributionFactory;
+ private OSGiImplementationFactory implementationFactory;
+ private PolicyFactory policyFactory;
+ // private ExtensionPointRegistry registry;
+ private FactoryExtensionPoint factories;
+ private ModelResolverExtensionPoint modelResolvers;
+ // private StAXArtifactProcessor<Composite> compositeProcessor;
+ private JavaInterfaceFactory javaInterfaceFactory;
+ // private Deployer deployer;
+ private ServiceTracker discoveryTracker;
+
+ /**
+ * @param intentName
+ * @return
+ */
+ private static QName getQName(String intentName) {
+ QName qname;
+ if (intentName.startsWith("{")) {
+ int i = intentName.indexOf('}');
+ if (i != -1) {
+ qname = new QName(intentName.substring(1, i), intentName.substring(i + 1));
+ } else {
+ throw new IllegalArgumentException("Invalid intent: " + intentName);
+ }
+ } else {
+ // Default to SCA namespace
+ qname = new QName(Base.SCA11_NS, intentName);
+ }
+ return qname;
+ }
+
+ /**
+ * @param context TODO
+ * @param registry
+ */
+ public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry, ServiceTracker discoveryTracker) {
+ super();
+ // this.context = context;
+ this.discoveryTracker = discoveryTracker;
+ // this.registry = registry;
+ this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ this.modelResolvers = registry.getExtensionPoint(ModelResolverExtensionPoint.class);
+// this.compositeProcessor =
+// registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class).getProcessor(Composite.class);
+ this.assemblyFactory = factories.getFactory(AssemblyFactory.class);
+ this.contributionFactory = factories.getFactory(ContributionFactory.class);
+ this.policyFactory = factories.getFactory(PolicyFactory.class);
+ this.implementationFactory = factories.getFactory(OSGiImplementationFactory.class);
+ this.javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class);
+ // this.deployer = registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(Deployer.class);
+ }
+
+ private Intent getIntent(String intent) {
+ QName name = getQName(intent);
+ Intent i = policyFactory.createIntent();
+ i.setName(name);
+ return i;
+ }
+
+ private List<Intent> getIntents(String[] intents) {
+ if (intents == null || intents.length == 0) {
+ return Collections.emptyList();
+ }
+ List<Intent> intentList = new ArrayList<Intent>();
+ for (String i : intents) {
+ Intent intent = getIntent(i);
+ if (intent != null) {
+ intentList.add(intent);
+ }
+ }
+ return intentList;
+ }
+
+ /**
+ * Any property in the map overrides the service reference properties, regardless of
+ * case. That is, if the map contains a key then it will override any case variant
+ * of this key in the Service Reference.<p>
+ * If the map contains the objectClass or service. id property key in any case
+ * variant, then these properties must not override the Service Reference’s value. This
+ * implies that the map can provide the service.exported. interfaces, property allowing
+ * the Topology Manager to export any registered service, also services not specifically
+ * marked to be exported.
+ * @param reference
+ * @param props
+ * @return
+ */
+ private Map<String, Object> getProperties(ServiceReference reference, Map<String, Object> props) {
+ String[] names = reference.getPropertyKeys();
+ Map<String, Object> properties = new HashMap<String, Object>();
+ if (names != null) {
+ for (String name : names) {
+ properties.put(name, reference.getProperty(name));
+ }
+ }
+ if (props != null) {
+ // Create a map of names (key = lowcase name, value = name)
+ Map<String, String> nameMap = new HashMap<String, String>();
+ if (names != null) {
+ for (String name : names) {
+ nameMap.put(name.toLowerCase(), name);
+ }
+ }
+ for (Map.Entry<String, Object> p : props.entrySet()) {
+ if (Constants.OBJECTCLASS.equalsIgnoreCase(p.getKey())) {
+ throw new IllegalArgumentException(Constants.OBJECTCLASS + " property cannot be overridden.");
+ } else if (Constants.SERVICE_ID.equalsIgnoreCase(p.getKey())) {
+ throw new IllegalArgumentException(Constants.SERVICE_ID + " property cannot be overridden.");
+ }
+ String key = nameMap.get(p.getKey().toLowerCase());
+ if (key != null) {
+ properties.put(key, p.getValue());
+ } else {
+ properties.put(p.getKey(), p.getValue());
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Parse the Stringp[] to support values that are separated by comma
+ * @param interfaces
+ * @return
+ */
+ private String[] parse(String[] interfaces) {
+ if (interfaces == null) {
+ return null;
+ }
+ List<String> names = new ArrayList<String>();
+ for (String i : interfaces) {
+ String[] parts = i.split(",");
+ for (String p : parts) {
+ names.add(p.trim());
+ }
+ }
+ return names.toArray(new String[names.size()]);
+ }
+
+ /**
+ * Introspect a local OSGi Service represented by the ServiceReference to create
+ * an SCA service with the required intents and bindings
+ * @param reference The service reference for a local OSGi service
+ * @param props Addiontal properties
+ * @return An SCA contribution with a deployable composite for the SCA service
+ * @throws Exception
+ */
+ public Contribution introspect(ServiceReference reference, Map<String, Object> props) throws Exception {
+ Bundle bundle = reference.getBundle();
+ Map<String, Object> properties = getProperties(reference, props);
+ Long sid = (Long)reference.getProperty(SERVICE_ID);
+
+ String[] requiredIntents = getStringArray(properties.get(SERVICE_EXPORTED_INTENTS));
+ List<Intent> intents = getIntents(requiredIntents);
+ String[] requiredIntentsExtra = getStringArray(properties.get(SERVICE_EXPORTED_INTENTS_EXTRA));
+ List<Intent> extraIntents = getIntents(requiredIntentsExtra);
+ Set<Intent> allIntents = new HashSet<Intent>(intents);
+ allIntents.addAll(extraIntents);
+
+ String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
+ Collection<Binding> bindings = loadBindings(bindingNames);
+
+ String[] remoteInterfaces = getStringArray(reference.getProperty(SERVICE_EXPORTED_INTERFACES));
+ if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) {
+ remoteInterfaces = getStringArray(reference.getProperty(OBJECTCLASS));
+ } else {
+ remoteInterfaces = parse(remoteInterfaces);
+ String[] objectClasses = getStringArray(reference.getProperty(OBJECTCLASS));
+ Set<String> objectClassSet = new HashSet<String>(Arrays.asList(objectClasses));
+ if (!objectClassSet.containsAll(Arrays.asList(remoteInterfaces))) {
+ throw new IllegalArgumentException(
+ "The exported interfaces are not a subset of the types" + " listed in the objectClass service property from the Service Reference");
+ }
+ }
+
+ Contribution contribution = generateContribution(bundle, sid, remoteInterfaces, bindings, allIntents);
+ return contribution;
+ }
+
+ /**
+ * Generate a contribution that contains the composite for the exported service
+ * @param bundle The OSGi bundle
+ * @param sid The service id
+ * @param remoteInterfaces
+ * @param bindings
+ * @param allIntents
+ * @return
+ * @throws ClassNotFoundException
+ * @throws InvalidInterfaceException
+ */
+ private Contribution generateContribution(Bundle bundle,
+ Long sid,
+ String[] remoteInterfaces,
+ Collection<Binding> bindings,
+ Set<Intent> allIntents) throws ClassNotFoundException,
+ InvalidInterfaceException {
+ String id = "osgi.service." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ OSGiProperty serviceID = implementationFactory.createOSGiProperty();
+ serviceID.setName(SERVICE_ID);
+ // The service.id is Long
+ serviceID.setValue(String.valueOf(sid));
+
+ for (String intf : remoteInterfaces) {
+ Service service = assemblyFactory.createService();
+ JavaInterfaceContract interfaceContract = createJavaInterfaceContract(bundle, intf);
+ String name = intf.substring(intf.lastIndexOf('.') + 1);
+ service.setName(name);
+ service.setInterfaceContract(interfaceContract);
+
+ service.getExtensions().add(serviceID);
+
+ implementation.getServices().add(service);
+
+ ComponentService componentService = assemblyFactory.createComponentService();
+ componentService.setName(service.getName());
+ component.getServices().add(componentService);
+ componentService.setService(service);
+ }
+
+ for (ComponentService componentService : component.getServices()) {
+ componentService.getRequiredIntents().addAll(allIntents);
+ componentService.getBindings().addAll(bindings);
+ }
+
+ // FIXME: Should we scan the owning bundle to create the SCA contribution?
+ Contribution contribution = createContribution(bundle, id, composite);
+ return contribution;
+ }
+
+ private Contribution createContribution(Bundle bundle, String id, Composite composite) {
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setClassLoader(OSGiHelper.createBundleClassLoader(bundle));
+ contribution.setURI("urn:" + id);
+ contribution.setLocation(bundle.getEntry("/").toString());
+ contribution.getDeployables().add(composite);
+ ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
+ contribution.setModelResolver(modelResolver);
+ // compositeProcessor.resolve(composite, modelResolver, new ProcessorContext(registry));
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+ /**
+ * @param bundle
+ * @param endpoint
+ * @return
+ * @throws Exception
+ */
+ public Contribution introspect(Bundle bundle, EndpointDescription endpoint) throws Exception {
+ Collection<Binding> bindings = Collections.emptyList();
+ Collection<String> interfaces = Collections.emptyList();
+ Collection<Intent> intents = Collections.emptyList();
+ Endpoint ep = (Endpoint)endpoint.getProperties().get(Endpoint.class.getName());
+ if (ep != null) {
+ bindings = Collections.singletonList(ep.getBinding());
+ interfaces = Collections.singletonList(((JavaInterface)ep.getInterfaceContract().getInterface()).getName());
+ intents = ep.getRequiredIntents();
+ } else {
+ Map<String, Object> properties = endpoint.getProperties();
+ interfaces = endpoint.getInterfaces();
+ String[] requiredIntents = getStringArray(properties.get(SERVICE_EXPORTED_INTENTS));
+ intents = getIntents(requiredIntents);
+
+ String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
+ bindings = loadBindings(bindingNames);
+ }
+
+ Contribution contribution = generateContribution(bundle, interfaces, bindings, intents);
+ return contribution;
+ }
+
+ private Contribution generateContribution(Bundle bundle,
+ Collection<String> remoteInterfaces,
+ Collection<Binding> bindings,
+ Collection<Intent> intents) throws ClassNotFoundException,
+ InvalidInterfaceException, ContributionResolveException {
+ String id = "osgi.reference." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(Base.SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+ // component.setAutowire(Boolean.TRUE);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ int count = 0;
+ for (String intf : remoteInterfaces) {
+ Reference reference = assemblyFactory.createReference();
+ JavaInterfaceContract interfaceContract = createJavaInterfaceContract(bundle, intf);
+
+ reference.setName("ref" + (count++));
+ reference.setInterfaceContract(interfaceContract);
+
+ implementation.getReferences().add(reference);
+
+ ComponentReference componentReference = assemblyFactory.createComponentReference();
+ componentReference.setName(reference.getName());
+ component.getReferences().add(componentReference);
+ componentReference.setReference(reference);
+ componentReference.setWiredByImpl(true);
+ }
+
+ for (ComponentReference componentReference : component.getReferences()) {
+ componentReference.getRequiredIntents().addAll(intents);
+ componentReference.getBindings().addAll(bindings);
+ }
+
+ Contribution contribution = createContribution(bundle, id, composite);
+ return contribution;
+ }
+
+ private JavaInterfaceContract createJavaInterfaceContract(Bundle bundle, String intf)
+ throws ClassNotFoundException, InvalidInterfaceException {
+ JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
+ Class<?> interfaceClass = bundle.loadClass(intf);
+ JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface(interfaceClass);
+ interfaceContract.setInterface(javaInterface);
+ if (javaInterface.getCallbackClass() != null) {
+ interfaceContract.setCallbackInterface(javaInterfaceFactory.createJavaInterface(javaInterface
+ .getCallbackClass()));
+ }
+ return interfaceContract;
+ }
+
+ private Collection<Binding> loadBindings(String[] qnames) throws IOException, ContributionReadException,
+ XMLStreamException {
+ if (qnames == null || qnames.length == 0) {
+ return Collections.emptyList();
+ }
+ QName[] bindingNames = new QName[qnames.length];
+ int index = 0;
+ for (String name : qnames) {
+ bindingNames[index++] = getQName(name);
+ }
+
+ LocalDiscoveryService discoveryService = (LocalDiscoveryService)discoveryTracker.getService();
+
+ Map<QName, Binding> bindingMap = new HashMap<QName, Binding>();
+ if (discoveryService != null) {
+ for (ExtenderConfiguration config : discoveryService.getConfigurations()) {
+ for (SCAConfig sc : config.getSCAConfigs()) {
+ for (QName bindingName : bindingNames) {
+ if (sc.getTargetNamespace().equals(bindingName.getNamespaceURI())) {
+ for (Binding binding : sc.getBindings()) {
+ if (bindingName.getLocalPart().equals(binding.getName())) {
+ bindingMap.put(bindingName, binding);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (QName bindingName : bindingNames) {
+ if (!bindingMap.containsKey(bindingName)) {
+ throw new ServiceRuntimeException("Binding cannot be resolved: " + bindingName);
+ }
+ }
+ return bindingMap.values();
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java
new file mode 100644
index 0000000000..58d72f389f
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.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.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Implementation of {@link ExportRegistration}
+ */
+public class ExportRegistrationImpl implements ExportRegistration {
+ private Node node;
+ private ServiceReference exportedService;
+ private EndpointDescription endpointDescription;
+ private Throwable exception;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ExportRegistrationImpl(Node node,
+ ServiceReference exportedService,
+ EndpointDescription endpointDescription,
+ Throwable exception) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ this.exception = exception;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ExportRegistrationImpl(Node node, ServiceReference exportedService, EndpointDescription endpointDescription) {
+ this(node, exportedService, endpointDescription, null);
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration#close()
+ */
+ public void close() {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ exception = null;
+ endpointDescription = null;
+ exportedService = null;
+ }
+
+ public ServiceReference getExportedService() {
+ return exportedService;
+ }
+
+ public EndpointDescription getEndpointDescription() {
+ return endpointDescription;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java
new file mode 100644
index 0000000000..d085bd282d
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ */
+public class ImportRegistrationImpl implements ImportRegistration {
+ private Node node;
+ private ServiceReference exportedService;
+ private EndpointDescription endpointDescription;
+ private Throwable exception;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ImportRegistrationImpl(Node node,
+ ServiceReference exportedService,
+ EndpointDescription endpointDescription,
+ Throwable exception) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ this.exception = exception;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ImportRegistrationImpl(Node node, ServiceReference exportedService, EndpointDescription endpointDescription) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration#close()
+ */
+ public void close() {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ exception = null;
+ endpointDescription = null;
+ exportedService = null;
+ }
+
+ public ServiceReference getImportedService() {
+ return exportedService;
+ }
+
+ public EndpointDescription getImportedEndpointDescription() {
+ return endpointDescription;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java
new file mode 100644
index 0000000000..a4b51d9d0c
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java
@@ -0,0 +1,204 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ */
+public class OSGiHelper {
+ public final static String FRAMEWORK_UUID = "org.osgi.framework.uuid";
+
+ private OSGiHelper() {
+ }
+
+ /**
+ * In OSGi, the value of String+ can be a single String, String[] or Collection<String>
+ * @param value
+ * @return
+ */
+ public static String[] getStringArray(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return new String[] {(String)value};
+ } else if (value instanceof Collection) {
+ Collection<String> collection = (Collection)value;
+ return collection.toArray(new String[collection.size()]);
+ }
+ return (String[])value;
+
+ }
+
+ public static Collection<String> getStringCollection(Object value) {
+ String[] values = getStringArray(value);
+ if (values == null) {
+ return null;
+ } else {
+ return Arrays.asList(values);
+ }
+ }
+
+ public static String[] getStringArray(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringArray(propertyValue);
+ }
+
+ public static Collection<String> getStringCollection(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringCollection(propertyValue);
+ }
+
+ public static Filter createFilter(BundleContext context, String filterValue) {
+ if (filterValue == null) {
+ return null;
+ }
+ try {
+ return context.createFilter(filterValue);
+ } catch (InvalidSyntaxException ex) {
+ throw new IllegalArgumentException("Invalid Filter: " + filterValue, ex);
+ }
+ }
+
+ /**
+ * Get a collection of resources that are configured by the given header
+ * @param bundle The bundle
+ * @param header
+ * @param defaultValue
+ * @return
+ */
+ public static Collection<URL> getConfiguration(Bundle bundle, String header, String defaultValue) {
+ String value = (String)bundle.getHeaders().get(header);
+ if (value == null) {
+ return Collections.emptyList();
+ }
+ String paths[] = value.trim().split("( |\t|\n|\r|\f|,)+");
+ if (paths.length == 0) {
+ if (defaultValue != null) {
+ paths = new String[] {defaultValue};
+ } else {
+ paths = new String[0];
+ }
+ }
+ Collection<URL> files = new HashSet<URL>();
+ for (String path : paths) {
+ if (path.endsWith("/")) {
+ path = path + "*.xml";
+ }
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ int lastIndex = path.lastIndexOf('/');
+ String root = path.substring(0, lastIndex);
+ if ("".equals(root)) {
+ root = "/";
+ }
+ String pattern = path.substring(lastIndex + 1);
+ Enumeration<URL> entries = bundle.findEntries(root, pattern, false);
+ if (entries != null) {
+ while (entries.hasMoreElements()) {
+ files.add(entries.nextElement());
+ }
+ }
+ }
+ return files;
+ }
+
+ public static Collection<OSGiProperty> getOSGiProperties(ExtensionPointRegistry registry, ServiceReference reference) {
+ FactoryExtensionPoint factoryExtensionPoint = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ OSGiImplementationFactory implementationFactory= factoryExtensionPoint.getFactory(OSGiImplementationFactory.class);
+ return implementationFactory.createOSGiProperties(reference);
+ }
+
+ public static OSGiProperty createOSGiProperty(ExtensionPointRegistry registry, String name, Object value) {
+ FactoryExtensionPoint factoryExtensionPoint = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ OSGiImplementationFactory implementationFactory= factoryExtensionPoint.getFactory(OSGiImplementationFactory.class);
+ return implementationFactory.createOSGiProperty(name, value);
+ }
+
+
+ public synchronized static String getFrameworkUUID(BundleContext bundleContext) {
+ String uuid = null;
+ if (bundleContext != null) {
+ uuid = bundleContext.getProperty(FRAMEWORK_UUID);
+ } else {
+ uuid = System.getProperty(FRAMEWORK_UUID);
+ }
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ }
+ System.setProperty(FRAMEWORK_UUID, uuid);
+ return uuid;
+ }
+
+ public static ClassLoader createBundleClassLoader(Bundle bundle) {
+ return new BundleClassLoader(bundle);
+ }
+
+ private static class BundleClassLoader extends ClassLoader {
+ private Bundle bundle;
+ public BundleClassLoader(Bundle bundle) {
+ super(null);
+ this.bundle = bundle;
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return bundle.loadClass(name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return bundle.getResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ Enumeration<URL> urls = bundle.getResources(name);
+ if (urls == null) {
+ List<URL> list = Collections.emptyList();
+ return Collections.enumeration(list);
+ } else {
+ return urls;
+ }
+ }
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java
new file mode 100644
index 0000000000..479ae54e42
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_REMOTE_ID;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointHelper.createEndpointDescription;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.createOSGiProperty;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getFrameworkUUID;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getOSGiProperties;
+import static org.osgi.framework.Constants.SERVICE_ID;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.impl.NodeImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class OSGiServiceExporter extends AbstractOSGiServiceHandler implements ServiceTrackerCustomizer {
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ public OSGiServiceExporter(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ init();
+ }
+
+ public Object addingService(ServiceReference reference) {
+ return exportService(reference, null);
+ }
+
+ public List<ExportRegistration> exportService(ServiceReference reference, Map<String, Object> properties) {
+ // FIXME: [rfeng] We need to check if a corresponding endpoint has been exported
+ try {
+ Contribution contribution = introspector.introspect(reference, properties);
+ if (contribution != null) {
+
+ NodeConfiguration configuration = nodeFactory.createNodeConfiguration();
+ if (domainRegistry != null) {
+ configuration.setDomainRegistryURI(domainRegistry);
+ }
+ if (domainURI != null) {
+ configuration.setDomainURI(domainURI);
+ }
+ configuration.setURI(contribution.getURI());
+ configuration.getExtensions().add(reference.getBundle());
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentService service = component.getServices().get(0);
+ service.getExtensions().addAll(getOSGiProperties(registry, reference));
+ service.getExtensions().add(createOSGiProperty(registry,
+ SERVICE_REMOTE_FRAMEWORK_UUID,
+ getFrameworkUUID(reference.getBundle()
+ .getBundleContext())));
+ service.getExtensions().add(createOSGiProperty(registry, SERVICE_REMOTE_ID, reference
+ .getProperty(SERVICE_ID)));
+
+ // FIXME: Configure the domain and node URI
+ NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
+ node.start();
+ List<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>();
+ for (Endpoint endpoint : service.getEndpoints()) {
+ EndpointDescription endpointDescription = createEndpointDescription(context, endpoint);
+ ExportRegistration exportRegistration =
+ new ExportRegistrationImpl(node, reference, endpointDescription);
+ exportedServices.add(exportRegistration);
+ }
+ return exportedServices;
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ removedService(reference, service);
+ exportService(reference, null);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ List<ExportRegistration> exportedServices = (List<ExportRegistration>)service;
+ for(ExportRegistration exportRegistration: exportedServices) {
+ exportRegistration.close();
+ }
+ }
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java
new file mode 100644
index 0000000000..c869ab8c14
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import java.util.Collections;
+
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.node.configuration.NodeConfiguration;
+import org.apache.tuscany.sca.node.impl.NodeImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class OSGiServiceImporter extends AbstractOSGiServiceHandler {
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ public OSGiServiceImporter(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ // Defer init() to importService()
+ }
+
+ public ImportRegistration importService(Bundle bundle, EndpointDescription endpointDescription) {
+ init();
+ try {
+ Contribution contribution = introspector.introspect(bundle, endpointDescription);
+ if (contribution != null) {
+
+ NodeConfiguration configuration = nodeFactory.createNodeConfiguration();
+ if (domainRegistry != null) {
+ configuration.setDomainRegistryURI(domainRegistry);
+ }
+ if (domainURI != null) {
+ configuration.setDomainURI(domainURI);
+ }
+ configuration.setURI(contribution.getURI());
+ configuration.getExtensions().add(bundle);
+ // FIXME: Configure the domain and node URI
+ NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
+ node.start();
+
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentReference componentReference = component.getReferences().get(0);
+ ServiceReference serviceReference =
+ context.getServiceReference("(sca.reference=" + component.getURI()
+ + "#reference("
+ + componentReference.getName()
+ + ")");
+ return new ImportRegistrationImpl(node, serviceReference, endpointDescription);
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public void unimportService(ImportRegistration importRegistration) {
+ Node node = (Node)importRegistration.getImportedService().getProperty("sca.node");
+ node.stop();
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java
new file mode 100644
index 0000000000..9da7b5f014
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java
@@ -0,0 +1,289 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * SCA Implementation of {@link RemoteServiceAdmin}
+ */
+public class RemoteServiceAdminImpl implements RemoteServiceAdmin, ManagedService {
+ private BundleContext context;
+ private ServiceRegistration registration;
+ private ServiceRegistration managedService;
+ private ServiceTracker listeners;
+
+ private OSGiServiceExporter exporter;
+ private OSGiServiceImporter importer;
+
+ private Collection<ImportRegistration> importedEndpoints = new ArrayList<ImportRegistration>();
+ private Collection<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>();
+
+ public RemoteServiceAdminImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ this.exporter = new OSGiServiceExporter(context);
+ this.importer = new OSGiServiceImporter(context);
+ exporter.start();
+ importer.start();
+ registration = context.registerService(RemoteServiceAdmin.class.getName(), this, null);
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_PID, RemoteServiceAdminImpl.class.getName());
+ managedService = context.registerService(ManagedService.class.getName(), this, props);
+ listeners = new ServiceTracker(this.context, RemoteServiceAdminListener.class.getName(), null);
+ listeners.open();
+ }
+
+ public void stop() {
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ if (managedService != null) {
+ try {
+ managedService.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ managedService = null;
+ }
+ if (listeners != null) {
+ listeners.close();
+ listeners = null;
+ }
+ for (ExportRegistration exportRegistration : exportedServices) {
+ exportRegistration.close();
+ }
+ exportedServices.clear();
+ for (ImportRegistration importRegistration : importedEndpoints) {
+ importRegistration.close();
+ }
+ importedEndpoints.clear();
+ if (importer != null) {
+ importer.stop();
+ importer = null;
+ }
+ if (exporter != null) {
+ exporter.stop();
+ exporter = null;
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#exportService(org.osgi.framework.ServiceReference,
+ * java.util.Map)
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref, Map properties) {
+ List<ExportRegistration> exportRegistrations = exporter.exportService(ref, properties);
+ if (exportRegistrations != null) {
+ exportedServices.addAll(exportRegistrations);
+ fireExportEvents(ref.getBundle(), exportRegistrations);
+ }
+ return exportRegistrations;
+ }
+
+ private void fireExportEvents(Bundle source, List<ExportRegistration> exportRegistrations) {
+ for (ExportRegistration registration : exportRegistrations) {
+ RemoteServiceAdminEvent rsaEvent =
+ new RemoteServiceAdminEvent(RemoteServiceAdminEvent.EXPORT_REGISTRATION, source, registration,
+ registration.getException());
+ EventAdmin eventAdmin = getEventAdmin();
+ if (eventAdmin != null) {
+ eventAdmin.postEvent(wrap(rsaEvent));
+ }
+ for (Object listener : listeners.getServices()) {
+ RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener;
+ rsaListener.remoteAdminEvent(rsaEvent);
+ }
+ }
+ }
+
+ private EventAdmin getEventAdmin() {
+ ServiceReference reference = context.getServiceReference(EventAdmin.class.getName());
+ if (reference == null) {
+ return null;
+ } else {
+ return (EventAdmin)context.getService(reference);
+ }
+ }
+
+ private Event wrap(RemoteServiceAdminEvent rsaEvent) {
+ int type = rsaEvent.getType();
+ String eventType = null;
+ switch (type) {
+ case RemoteServiceAdminEvent.EXPORT_ERROR:
+ eventType = "EXPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
+ eventType = "EXPORT_REGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
+ eventType = "EXPORT_UNREGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.EXPORT_WARNING:
+ eventType = "EXPORT_WARNING";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_ERROR:
+ eventType = "IMPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
+ eventType = "IMPORT_REGISTRATION";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
+ eventType = "EXPORT_ERROR";
+ break;
+ case RemoteServiceAdminEvent.IMPORT_WARNING:
+ eventType = "IMPORT_UNREGISTRATION";
+ break;
+ }
+ String topic = "org/osgi/service/remoteserviceadmin/" + eventType;
+ Map<String, Object> props = new HashMap<String, Object>();
+ /*
+ * <ul>
+ <li>bundle – (Bundle) The Remote Service Admin bundle
+ <li>bundle-id – (Long) The id of the Blueprint bundle.
+ <li>bundle-symbolicname – (String) The Bundle Symbolic Name of the
+ Remote Service Admin bundle.
+ <li>bundle-version - (Version) The version of the Blueprint bundle.
+ <li>cause – The exception, if present.
+ <li>import.registration – An imported endpoint, if present
+ <li>export.registration – An exported endpoint, if present
+ <li>service.remote.id – Remote service UUID, if present
+ <li>service.remote.uuid – Remote service UUID, if present
+ <li>service.remote.uri – (String) The URI of the endpoint, if present
+ <li>objectClass – (String[]) The interface names, if present
+ <li>service.imported.configs – (String+) The configuration types of the
+ imported services, if present
+ <li>timestamp – (Long) The time when the event occurred
+ <li>event – (RemoteServiceAdminEvent) The RemoteServiceAdminEvent
+ object that caused this event.
+ </ul>
+ */
+ Bundle rsaBundle = context.getBundle();
+ props.put("bundle", rsaBundle);
+ props.put("bundle-id", rsaBundle.getBundleId());
+ props.put("bundle-symbolicname", rsaBundle.getSymbolicName());
+ props.put("bundle-version", rsaBundle.getHeaders().get(Constants.BUNDLE_VERSION));
+ props.put("cause", rsaEvent.getException());
+ props.put("import.registration", rsaEvent.getImportRegistration());
+ props.put("export.registration", rsaEvent.getExportRegistration());
+ EndpointDescription ep = null;
+ if (rsaEvent.getImportRegistration() != null) {
+ ep = rsaEvent.getImportRegistration().getImportedEndpointDescription();
+ } else {
+ ep = rsaEvent.getExportRegistration().getEndpointDescription();
+ }
+ props.put("service.remote.id", ep.getRemoteServiceID());
+ props.put("service.remote.uuid", ep.getRemoteFrameworkUUID());
+ props.put("service.remote.uri", ep.getRemoteURI());
+ props.put("objectClass", ep.getInterfaces());
+ props.put("service.imported.configs", ep.getConfigurationTypes());
+ props.put("timestamp", new Long(System.currentTimeMillis()));
+ props.put("event", rsaEvent);
+ return new Event(topic, props);
+ }
+
+ private void fireImportEvents(Bundle source, ImportRegistration registration) {
+ RemoteServiceAdminEvent rsaEvent =
+ new RemoteServiceAdminEvent(RemoteServiceAdminEvent.IMPORT_REGISTRATION, source, registration, registration
+ .getException());
+ EventAdmin eventAdmin = getEventAdmin();
+ if (eventAdmin != null) {
+ eventAdmin.postEvent(wrap(rsaEvent));
+ }
+ for (Object listener : listeners.getServices()) {
+ RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener;
+ rsaListener.remoteAdminEvent(rsaEvent);
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#getExportedServices()
+ */
+ public Collection<ExportRegistration> getExportedServices() {
+ return exportedServices;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#getImportedEndpoints()
+ */
+ public Collection<ImportRegistration> getImportedEndpoints() {
+ return importedEndpoints;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#importService(org.apache.tuscany.sca.dosgi.discovery.EndpointDescription)
+ */
+ public ImportRegistration importService(EndpointDescription endpoint) {
+ Bundle bundle = (Bundle)endpoint.getProperties().get(Bundle.class.getName());
+ ImportRegistration importReg = importer.importService(bundle, endpoint);
+ if (importReg != null) {
+ fireImportEvents(bundle, importReg);
+ importedEndpoints.add(importReg);
+ }
+ return importReg;
+ }
+
+ public synchronized void updated(Dictionary props) throws ConfigurationException {
+ if (props == null) {
+ // It can be null in Apache Felix
+ return;
+ }
+ String domainRegistry = (String)props.get("org.osgi.sca.domain.registry");
+ String domainURI = (String)props.get("org.osgi.sca.domain.uri");
+ if (domainRegistry != null) {
+ exporter.setDomainRegistry(domainRegistry);
+ importer.setDomainRegistry(domainRegistry);
+ }
+ if (domainURI != null) {
+ exporter.setDomainURI(domainURI);
+ importer.setDomainURI(domainURI);
+ }
+ }
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java
new file mode 100644
index 0000000000..1967fa6e4a
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java
@@ -0,0 +1,410 @@
+/*
+ * 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.osgi.remoteserviceadmin.impl;
+
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS;
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.common.java.collection.CollectionMap;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Implementation of Remote Controller
+ */
+public class TopologyManagerImpl implements ListenerHook, RemoteServiceAdminListener, EndpointListener,
+ ServiceTrackerCustomizer, LifeCycleListener /*, EventHook */{
+ private final static Logger logger = Logger.getLogger(TopologyManagerImpl.class.getName());
+ public final static String ENDPOINT_LOCAL = "service.local";
+
+ private BundleContext context;
+ private ServiceTracker remoteAdmins;
+
+ private ServiceRegistration registration;
+ private ServiceRegistration endpointListener;
+
+ private ServiceTracker remotableServices;
+
+ // Service listeners keyed by the filter
+ private CollectionMap<String, ListenerInfo> serviceListeners = new CollectionMap<String, ListenerInfo>();
+
+ private CollectionMap<ServiceReference, ExportRegistration> exportedServices =
+ new CollectionMap<ServiceReference, ExportRegistration>();
+ private CollectionMap<EndpointDescription, ImportRegistration> importedServices =
+ new CollectionMap<EndpointDescription, ImportRegistration>();
+
+ private Filter remotableServiceFilter;
+
+ public TopologyManagerImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ String filter =
+ "(& (!(" + SERVICE_IMPORTED
+ + "=*)) ("
+ + SERVICE_EXPORTED_INTERFACES
+ + "=*) ("
+ + SERVICE_EXPORTED_CONFIGS
+ + "=org.osgi.sca) )";
+ try {
+ remotableServiceFilter = context.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+
+ endpointListener = context.registerService(EndpointListener.class.getName(), this, null);
+ remoteAdmins = new ServiceTracker(this.context, RemoteServiceAdmin.class.getName(), null);
+ remoteAdmins.open();
+
+ // DO NOT register EventHook.class.getName() as it cannot report existing services
+ String interfaceNames[] =
+ new String[] {ListenerHook.class.getName(), RemoteServiceAdminListener.class.getName()};
+ // The registration will trigger the added() method before registration is assigned
+ registration = context.registerService(interfaceNames, this, null);
+
+ remotableServices = new ServiceTracker(context, remotableServiceFilter, this);
+ remotableServices.open(true);
+
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.EventHook#event(org.osgi.framework.ServiceEvent,
+ * java.util.Collection)
+ */
+ /*
+ public void event(ServiceEvent event, Collection contexts) {
+ ServiceReference reference = event.getServiceReference();
+ if (!remotableServiceFilter.match(reference)) {
+ // Only export remotable services that are for SCA
+ return;
+ }
+ switch (event.getType()) {
+ case ServiceEvent.REGISTERED:
+ exportService(reference);
+ break;
+ case ServiceEvent.UNREGISTERING:
+ unexportService(reference);
+ break;
+ case ServiceEvent.MODIFIED:
+ case ServiceEvent.MODIFIED_ENDMATCH:
+ // First check if the property changes will impact
+ // Call remote admin to update the service
+ unexportService(reference);
+ exportService(reference);
+ break;
+ }
+ }
+ */
+
+ public Object addingService(ServiceReference reference) {
+ exportService(reference);
+ return reference.getBundle().getBundleContext().getService(reference);
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ exportService(reference);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ }
+
+ private void unexportService(ServiceReference reference) {
+ // Call remote admin to unexport the service
+ Collection<ExportRegistration> exportRegistrations = exportedServices.get(reference);
+ if (exportRegistrations != null) {
+ for (Iterator<ExportRegistration> i = exportRegistrations.iterator(); i.hasNext();) {
+ ExportRegistration exported = i.next();
+ exported.close();
+ i.remove();
+ }
+ }
+ }
+
+ private void exportService(ServiceReference reference) {
+ // Call remote admin to export the service
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ // Ignore
+ logger.warning("No RemoteAdmin services are available.");
+ } else {
+ for (Object ra : admins) {
+ RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
+ List<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference, null);
+ if (exportRegistrations != null && !exportRegistrations.isEmpty()) {
+ exportedServices.putValues(reference, exportRegistrations);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection)
+ */
+ public void added(Collection listeners) {
+ synchronized (serviceListeners) {
+ try {
+ Collection<ListenerInfo> listenerInfos = (Collection<ListenerInfo>)listeners;
+ boolean changed = false;
+ for (ListenerInfo l : listenerInfos) {
+ if (!l.isRemoved() && l.getBundleContext() != context) {
+ String key = l.getFilter();
+ if (key == null) {
+ // key = "";
+ // FIXME: It should always match, let's ignore it for now
+ logger.warning("Service listner without a filter is skipped: " + l);
+ continue;
+ }
+ Collection<ListenerInfo> infos = serviceListeners.get(key);
+ if (infos == null) {
+ infos = new HashSet<ListenerInfo>();
+ serviceListeners.put(key, infos);
+ }
+ infos.add(l);
+ changed = true;
+ }
+ }
+ if (changed) {
+ updateEndpointListenerScope();
+ }
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ if (e instanceof Error) {
+ throw (Error)e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private void updateEndpointListenerScope() {
+ Set<String> filterSet = serviceListeners.keySet();
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(ENDPOINT_LISTENER_SCOPE, filterSet.toArray(new String[filterSet.size()]));
+ endpointListener.setProperties(props);
+ }
+
+ private CollectionMap<Class<?>, ListenerInfo> findServiceListeners(EndpointDescription endpointDescription,
+ String matchedFilter) {
+ synchronized (serviceListeners) {
+
+ // First find all the listeners that have the matching filter
+ Collection<ListenerInfo> listeners = serviceListeners.get(matchedFilter);
+ if (listeners == null) {
+ return null;
+ }
+
+ // Try to partition the listeners by the interface classes
+ List<String> interfaceNames = endpointDescription.getInterfaces();
+ CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = new CollectionMap<Class<?>, ListenerInfo>();
+ for (String i : interfaceNames) {
+ for (Iterator<ListenerInfo> it = listeners.iterator(); it.hasNext();) {
+ try {
+ ListenerInfo listener = it.next();
+ if (listener.isRemoved()) {
+ it.remove();
+ continue;
+ }
+ try {
+ Class<?> interfaceClass = listener.getBundleContext().getBundle().loadClass(i);
+ interfaceToListeners.putValue(interfaceClass, listener);
+ } catch (IllegalStateException e) {
+ logger.log(Level.WARNING, e.getMessage(), e);
+ // Ignore the exception
+ }
+ } catch (ClassNotFoundException e) {
+ // Ignore the listener as it cannot load the interface class
+ }
+ }
+ }
+ return interfaceToListeners;
+ }
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#removed(java.util.Collection)
+ */
+ public void removed(Collection listeners) {
+ synchronized (serviceListeners) {
+ try {
+ Collection<ListenerInfo> listenerInfos = (Collection<ListenerInfo>)listeners;
+ boolean changed = false;
+ for (ListenerInfo l : listenerInfos) {
+ if (registration != null && l.getBundleContext() != context) {
+ String key = l.getFilter();
+ if (key == null) {
+ continue;
+ }
+ if (serviceListeners.removeValue(key, l)) {
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ updateEndpointListenerScope();
+ }
+ } catch (Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ if (e instanceof Error) {
+ throw (Error)e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException)e;
+ } else {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener#remoteAdminEvent(org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent)
+ */
+ public void remoteAdminEvent(RemoteServiceAdminEvent event) {
+ switch (event.getType()) {
+ case RemoteServiceAdminEvent.EXPORT_ERROR:
+ case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
+ case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
+ case RemoteServiceAdminEvent.EXPORT_WARNING:
+ break;
+ case RemoteServiceAdminEvent.IMPORT_ERROR:
+ case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
+ case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
+ case RemoteServiceAdminEvent.IMPORT_WARNING:
+ break;
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener#addEndpoint(org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription,
+ * java.lang.String)
+ */
+ public void endpointAdded(EndpointDescription endpoint, String matchedFilter) {
+ importService(endpoint, matchedFilter);
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener#removeEndpoint(org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription)
+ */
+ public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) {
+ unimportService(endpoint);
+ }
+
+ private void importService(EndpointDescription endpoint, String matchedFilter) {
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ logger.warning("No RemoteAdmin services are available.");
+ return;
+ }
+
+ CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = findServiceListeners(endpoint, matchedFilter);
+ for (Map.Entry<Class<?>, Collection<ListenerInfo>> e : interfaceToListeners.entrySet()) {
+ Class<?> interfaceClass = e.getKey();
+ Collection<ListenerInfo> listeners = e.getValue();
+ // Get a listener
+ ListenerInfo listener = listeners.iterator().next();
+ Bundle bundle = listener.getBundleContext().getBundle();
+
+ Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
+ props.put(Bundle.class.getName(), bundle);
+ props.put(Constants.OBJECTCLASS, new String[] {interfaceClass.getName()});
+ EndpointDescription description = new EndpointDescription(props);
+
+ if (admins != null) {
+ for (Object ra : admins) {
+ RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
+ ImportRegistration importRegistration = remoteAdmin.importService(description);
+ if (importRegistration != null) {
+ importedServices.putValue(endpoint, importRegistration);
+ }
+ }
+ }
+ }
+ }
+
+ private void unimportService(EndpointDescription endpoint) {
+ // Call remote admin to unimport the service
+ Collection<ImportRegistration> importRegistrations = importedServices.get(endpoint);
+ if (importRegistrations != null) {
+ for (Iterator<ImportRegistration> i = importRegistrations.iterator(); i.hasNext();) {
+ ImportRegistration imported = i.next();
+ imported.close();
+ i.remove();
+ }
+ }
+ }
+
+ public void stop() {
+ remotableServices.close();
+
+ if (registration != null) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ registration = null;
+ }
+ if (remoteAdmins != null) {
+ remoteAdmins.close();
+ remoteAdmins = null;
+ }
+ synchronized (serviceListeners) {
+ serviceListeners.clear();
+ }
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
new file mode 100644
index 0000000000..5b0b311d7a
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
@@ -0,0 +1,280 @@
+/*
+ * 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.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener.ENDPOINT_LISTENER_SCOPE;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ *
+ */
+public abstract class AbstractDiscoveryService implements Discovery, LifeCycleListener {
+ protected final static int ADDED = 0x1;
+ protected final static int REMOVED = 0x2;
+ protected final static int MODIFIED = 0x4;
+
+ protected final static Logger logger = Logger.getLogger(AbstractDiscoveryService.class.getName());
+
+ protected BundleContext context;
+ protected ExtensionPointRegistry registry;
+
+ private Map<String, List<EndpointListener>> filtersToListeners = new HashMap<String, List<EndpointListener>>();
+ // this is effectively a set which allows for multiple service descriptions with the
+ // same interface name but different properties and takes care of itself with respect to concurrency
+ protected Map<EndpointDescription, Bundle> servicesInfo = new ConcurrentHashMap<EndpointDescription, Bundle>();
+ private Map<EndpointListener, Collection<String>> listenersToFilters =
+ new HashMap<EndpointListener, Collection<String>>();
+ private ServiceTracker trackerTracker;
+
+ public AbstractDiscoveryService(BundleContext context) {
+ super();
+ this.context = context;
+ }
+
+ public void start() {
+ // track the registration of EndpointListener
+ trackerTracker = new ServiceTracker(this.context, EndpointListener.class.getName(), null) {
+ public Object addingService(ServiceReference reference) {
+ Object result = super.addingService(reference);
+ cacheTracker(reference, result);
+ return result;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ super.modifiedService(reference, service);
+ updateTracker(reference, service);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ super.removedService(reference, service);
+ clearTracker(service);
+ }
+ };
+
+ trackerTracker.open();
+ }
+
+ public void stop() {
+ trackerTracker.close();
+ }
+
+ protected ExtensionPointRegistry getExtensionPointRegistry() {
+ NodeFactoryImpl factory = (NodeFactoryImpl)NodeFactory.newInstance();
+ factory.init();
+ ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null);
+ tracker.open();
+ // tracker.waitForService(1000);
+ registry = (ExtensionPointRegistry)tracker.getService();
+ tracker.close();
+ return registry;
+ }
+
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary headers = context.getBundle().getHeaders();
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(PRODUCT_NAME, "Apache Tuscany SCA");
+ props.put(PRODUCT_VERSION, headers.get(Constants.BUNDLE_VERSION));
+ props.put(VENDOR_NAME, headers.get(Constants.BUNDLE_VENDOR));
+ // props.put(SUPPORTED_PROTOCOLS, new String[] {"local", "org.osgi.sca"});
+ return props;
+ }
+
+ private synchronized void cacheTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ Collection<String> filters =
+ addTracker(reference, listener, ENDPOINT_LISTENER_SCOPE, filtersToListeners, listenersToFilters);
+
+ triggerCallbacks(null, filters, listener, true);
+ }
+ }
+
+ private synchronized void clearTracker(Object service) {
+ if (service instanceof EndpointListener) {
+ removeTracker((EndpointListener)service, filtersToListeners, listenersToFilters);
+ }
+ }
+
+ private synchronized void updateTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("updating listener: " + listener);
+ }
+ Collection<String> oldFilters = removeTracker(listener, filtersToListeners, listenersToFilters);
+
+ Collection<String> newFilters =
+ addTracker(reference, listener, ENDPOINT_LISTENER_SCOPE, filtersToListeners, listenersToFilters);
+
+ triggerCallbacks(oldFilters, newFilters, listener, true);
+ }
+ }
+
+ private void triggerCallbacks(Collection<String> oldInterest,
+ Collection<String> newInterest,
+ EndpointListener listener,
+ boolean isFilter) {
+ // compute delta between old & new interfaces/filters and
+ // trigger callbacks for any entries in servicesInfo that
+ // match any *additional* interface/filters
+ Collection<String> deltaInterest = new ArrayList<String>();
+ if (newInterest != null && !newInterest.isEmpty()) {
+ if (oldInterest == null || oldInterest.isEmpty()) {
+ deltaInterest.addAll(newInterest);
+ } else {
+ Iterator<String> i = newInterest.iterator();
+ while (i.hasNext()) {
+ String next = (String)i.next();
+ if (!oldInterest.contains(next)) {
+ deltaInterest.add(next);
+ }
+ }
+ }
+ }
+
+ if (logger.isLoggable(Level.FINE)) {
+ if (servicesInfo.size() > 0) {
+ logger.fine("search for matches to trigger callbacks with delta: " + deltaInterest);
+ } else {
+ logger.fine("nothing to search for matches to trigger callbacks with delta: " + deltaInterest);
+ }
+ }
+ Iterator<String> i = deltaInterest.iterator();
+ while (i.hasNext()) {
+ String next = i.next();
+ for (EndpointDescription sd : servicesInfo.keySet()) {
+ triggerCallbacks(listener, next, sd, ADDED);
+ }
+ }
+ }
+
+ private void triggerCallbacks(EndpointListener listener, String matchedFilter, EndpointDescription sd, int type) {
+ switch (type) {
+ case ADDED:
+ listener.endpointAdded(sd, matchedFilter);
+ break;
+ case REMOVED:
+ listener.endpointRemoved(sd, matchedFilter);
+ break;
+ case MODIFIED:
+ listener.endpointRemoved(sd, matchedFilter);
+ listener.endpointAdded(sd, matchedFilter);
+ break;
+ }
+ }
+
+ private boolean filterMatches(String filterValue, EndpointDescription sd) {
+ Filter filter = OSGiHelper.createFilter(context, filterValue);
+ Hashtable<String, Object> props = new Hashtable<String, Object>(sd.getProperties());
+ // Add two faked properties to make the filter match
+ props.put(Constants.OBJECTCLASS, sd.getInterfaces());
+ props.put(RemoteConstants.SERVICE_IMPORTED, "true");
+ return filter != null ? filter.match(props) : false;
+ }
+
+ static Collection<String> removeTracker(EndpointListener listener,
+ Map<String, List<EndpointListener>> forwardMap,
+ Map<EndpointListener, Collection<String>> reverseMap) {
+ Collection<String> collection = reverseMap.get(listener);
+ if (collection != null && !collection.isEmpty()) {
+ reverseMap.remove(listener);
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).remove(listener);
+ } else {
+ // if the element wasn't on the forwardmap, its a new element and
+ // shouldn't be returned as part of the collection of old ones
+ i.remove();
+ }
+ }
+ }
+ return collection;
+ }
+
+ @SuppressWarnings("unchecked")
+ static Collection<String> addTracker(ServiceReference reference,
+ EndpointListener listener,
+ String property,
+ Map<String, List<EndpointListener>> forwardMap,
+ Map<EndpointListener, Collection<String>> reverseMap) {
+ Collection<String> collection = OSGiHelper.getStringCollection(reference, property);
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("adding listener: " + listener
+ + " collection: "
+ + collection
+ + " registered against prop: "
+ + property);
+ }
+ if (collection != null && !collection.isEmpty()) {
+ reverseMap.put(listener, new ArrayList<String>(collection));
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).add(listener);
+ } else {
+ List<EndpointListener> trackerList = new ArrayList<EndpointListener>();
+ trackerList.add(listener);
+ forwardMap.put(element, trackerList);
+ }
+ }
+ }
+ return collection;
+ }
+
+ protected void endpointChanged(EndpointDescription sd, int type) {
+ for (Map.Entry<EndpointListener, Collection<String>> entry : listenersToFilters.entrySet()) {
+ for (String filter : entry.getValue()) {
+ if (filterMatches(filter, sd)) {
+ triggerCallbacks(entry.getKey(), filter, sd, type);
+ }
+ }
+ }
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java
new file mode 100644
index 0000000000..3ad1e53fee
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.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.osgi.service.discovery.impl;
+
+/**
+ * Every Discovery Provider registers a service implementing this interface.
+ * This service is registered with extra properties identified at the beginning
+ * of this interface to denote the name of the product providing Discovery
+ * functionality, its version, vendor, used protocols etc..
+ * <p>
+ * Discovery allows to publish services exposed for remote access as well as to
+ * search for remote services.
+ * <p>
+ * Discovery service implementations usually rely on some discovery protocols or
+ * other information distribution means.
+ *
+ * @ThreadSafe
+ */
+public interface Discovery {
+
+ /**
+ * ServiceRegistration property for the name of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String PRODUCT_NAME = "osgi.remote.discovery.product";
+
+ /**
+ * ServiceRegistration property for the version of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String PRODUCT_VERSION = "osgi.remote.discovery.product.version";
+
+ /**
+ * ServiceRegistration property for the Discovery product vendor name.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ String VENDOR_NAME = "osgi.remote.discovery.vendor";
+
+ /**
+ * ServiceRegistration property that lists the discovery protocols used by
+ * this Discovery service.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>.
+ */
+ String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols";
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java
new file mode 100644
index 0000000000..32bd10b77b
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class DiscoveryActivator implements BundleActivator {
+ private List<AbstractDiscoveryService> discoveryServices = new ArrayList<AbstractDiscoveryService>();
+ private List<ServiceRegistration> discoveryServiceRegistrations = new ArrayList<ServiceRegistration>();
+
+ public void start(BundleContext context) {
+ discoveryServices.add(new LocalDiscoveryService(context));
+
+ discoveryServices.add(new DomainDiscoveryService(context));
+
+ for (AbstractDiscoveryService service : discoveryServices) {
+ service.start();
+ ServiceRegistration registration =
+ context.registerService(Discovery.class.getName(), service, service.getProperties());
+ discoveryServiceRegistrations.add(registration);
+ }
+ }
+
+ public void stop(BundleContext context) {
+ for (ServiceRegistration registration : discoveryServiceRegistrations) {
+ try {
+ registration.unregister();
+ } catch (IllegalStateException e) {
+ // The service has been unregistered, ignore it
+ }
+ }
+ for (AbstractDiscoveryService service : discoveryServices) {
+ service.stop();
+ }
+ }
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java
new file mode 100644
index 0000000000..bf70c98634
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java
@@ -0,0 +1,124 @@
+/*
+ * 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.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointHelper.createEndpointDescription;
+
+import java.util.Dictionary;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Implementation;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.runtime.DomainRegistryFactory;
+import org.apache.tuscany.sca.runtime.EndpointListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Discovery service based on the distributed SCA domain
+ */
+public class DomainDiscoveryService extends AbstractDiscoveryService implements EndpointListener {
+ private DomainRegistryFactory domainRegistryFactory;
+
+ public DomainDiscoveryService(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ super.start();
+ getExtensionPointRegistry();
+ this.domainRegistryFactory =
+ registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(DomainRegistryFactory.class);
+ domainRegistryFactory.addListener(this);
+ }
+
+ public void endpointAdded(Endpoint endpoint) {
+ Implementation impl = endpoint.getComponent().getImplementation();
+ if (!(impl instanceof OSGiImplementation)) {
+ return;
+ }
+
+ BundleContext bundleContext = null;
+ // Remote endpoint doesn't have a bundle
+ if (!endpoint.isRemote()) {
+ OSGiImplementation osgiImpl = (OSGiImplementation)impl;
+ Bundle bundle = osgiImpl.getBundle();
+ bundleContext = bundle != null ? bundle.getBundleContext() : null;
+ }
+
+ /*
+ if (!endpoint.isRemote()) {
+ Interface intf = endpoint.getService().getInterfaceContract().getInterface();
+ JavaInterface javaInterface = (JavaInterface)intf;
+ // String filter = getOSGiFilter(provider.getOSGiProperties(service));
+ // FIXME: What is the filter?
+ String filter = "(!(sca.reference=*))";
+ // "(sca.service=" + component.getURI() + "#service-name\\(" + service.getName() + "\\))";
+ ServiceReference ref = null;
+ try {
+ ref = bundleContext.getServiceReferences(javaInterface.getName(), filter)[0];
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+ if (ref != null) {
+
+ }
+ } else
+ */
+ {
+ // Notify the endpoint listeners
+ EndpointDescription description = createEndpointDescription(bundleContext, endpoint);
+ endpointChanged(description, ADDED);
+ }
+ }
+
+ public void endpointRemoved(Endpoint endpoint) {
+ /*
+ if (!endpoint.isRemote()) {
+ // export services
+ } else
+ */
+ {
+ EndpointDescription description = createEndpointDescription(context, endpoint);
+ endpointChanged(description, REMOVED);
+ }
+ }
+
+ public void endpointUpdated(Endpoint oldEndpoint, Endpoint newEndpoint) {
+ // FIXME: This is a quick and dirty way for the update
+ endpointRemoved(oldEndpoint);
+ endpointAdded(newEndpoint);
+ }
+
+ public void stop() {
+ domainRegistryFactory.removeListener(this);
+ super.stop();
+ }
+
+ @Override
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary<String, Object> props = super.getProperties();
+ props.put(SUPPORTED_PROTOCOLS, new String[] {"org.osgi.sca"});
+ return props;
+ }
+
+}
diff --git a/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
new file mode 100644
index 0000000000..11dcd56272
--- /dev/null
+++ b/tags/java/sca/2.0-M4-RC3/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
@@ -0,0 +1,205 @@
+/**
+ * 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.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getConfiguration;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.deployment.Deployer;
+import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescription;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants;
+import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper;
+import org.oasisopen.sca.ServiceRuntimeException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleTrackerCustomizer {
+ private Deployer deployer;
+ private BundleTracker bundleTracker;
+ private Collection<ExtenderConfiguration> extenders = new ArrayList<ExtenderConfiguration>();
+
+ public LocalDiscoveryService(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ super.start();
+ getExtensionPointRegistry();
+
+ UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class);
+ this.deployer = utilities.getUtility(Deployer.class);
+ bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STARTING, this);
+ bundleTracker.open();
+ }
+
+ public static ServiceTracker getTracker(BundleContext context) {
+ Filter filter = null;
+ try {
+ filter =
+ context.createFilter("(& (" + Discovery.SUPPORTED_PROTOCOLS
+ + "=local) ("
+ + Constants.OBJECTCLASS
+ + "="
+ + Discovery.class.getName()
+ + "))");
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return new ServiceTracker(context, filter, null);
+ }
+
+ private EndpointDescription createEndpointDescription(ServiceDescription sd) {
+ Map<String, Object> props = new HashMap<String, Object>(sd.getProperties());
+ props.put(Constants.OBJECTCLASS, sd.getInterfaces().toArray(new String[sd.getInterfaces().size()]));
+ if (!props.containsKey(RemoteConstants.SERVICE_REMOTE_ID)) {
+ props.put(RemoteConstants.SERVICE_REMOTE_ID, String.valueOf(System.currentTimeMillis()));
+ }
+ if (!props.containsKey(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID)) {
+ props.put(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID, OSGiHelper.getFrameworkUUID(context));
+ }
+ if (!props.containsKey(RemoteConstants.SERVICE_REMOTE_URI)) {
+ props.put(RemoteConstants.SERVICE_REMOTE_URI, UUID.randomUUID().toString());
+ }
+
+ EndpointDescription sed = new EndpointDescription(props);
+ return sed;
+ }
+
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Map.Entry<EndpointDescription, Bundle>> i = servicesInfo.entrySet().iterator(); i.hasNext();) {
+ Entry<EndpointDescription, Bundle> entry = i.next();
+ if (entry.getValue().equals(bundle)) {
+ serviceDescriptionRemoved(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
+ private void serviceDescriptionAdded(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, ADDED);
+ }
+
+ private void serviceDescriptionRemoved(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, REMOVED);
+ }
+
+ public void stop() {
+ if (bundleTracker != null) {
+ bundleTracker.close();
+ }
+ super.stop();
+ }
+
+ public Object addingBundle(Bundle bundle, BundleEvent event) {
+ Collection<URL> scaConfigs = getConfiguration(bundle, "SCA-Configuration", "OSGI-INF/sca-config/*.xml");
+ Collection<URL> descriptions = getConfiguration(bundle, "Remote-Service", null);
+ if (scaConfigs.isEmpty() && descriptions.isEmpty()) {
+ return null;
+ }
+ ExtenderConfiguration extender = new ExtenderConfiguration();
+ for (URL url : scaConfigs) {
+ try {
+ SCAConfig scaConfig = deployer.loadXMLDocument(url, deployer.createMonitor());
+ extender.scaConfigs.add(scaConfig);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ throw new ServiceRuntimeException(e);
+ }
+ }
+ for (URL url : descriptions) {
+ try {
+ ServiceDescriptions sds = deployer.loadXMLDocument(url, deployer.createMonitor());
+ extender.remoteServiceDescriptions.add(sds);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ // throw new ServiceRuntimeException(e);
+ }
+ }
+
+ // Notify
+ for (ServiceDescriptions sds : extender.getRemoteServiceDescriptions()) {
+ for (ServiceDescription sd : sds) {
+ EndpointDescription sed = createEndpointDescription(sd);
+ servicesInfo.put(sed, bundle);
+ serviceDescriptionAdded(sed);
+ }
+ }
+
+ this.extenders.add(extender);
+ return extender;
+ }
+
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+ // STARTING --> ACTIVE
+ }
+
+ public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+ if (object instanceof ExtenderConfiguration) {
+ extenders.remove((ExtenderConfiguration)object);
+ removeServicesDeclaredInBundle(bundle);
+ }
+ }
+
+ public Collection<ExtenderConfiguration> getConfigurations() {
+ return extenders;
+ }
+
+ public static class ExtenderConfiguration {
+ private Collection<SCAConfig> scaConfigs = new ArrayList<SCAConfig>();
+ private Collection<ServiceDescriptions> remoteServiceDescriptions = new ArrayList<ServiceDescriptions>();
+
+ public Collection<ServiceDescriptions> getRemoteServiceDescriptions() {
+ return remoteServiceDescriptions;
+ }
+
+ public Collection<SCAConfig> getSCAConfigs() {
+ return scaConfigs;
+ }
+
+ }
+
+ @Override
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary<String, Object> props = super.getProperties();
+ props.put(SUPPORTED_PROTOCOLS, new String[] {"local"});
+ return props;
+ }
+
+}