diff options
author | rfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68> | 2009-08-18 23:04:29 +0000 |
---|---|---|
committer | rfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68> | 2009-08-18 23:04:29 +0000 |
commit | f449ada1fb543ed96ebd7f22afa6d1c0a8126bb1 (patch) | |
tree | 70a4dc05f120f35e81cefd69f503cb7b1751ae68 /java/sca/modules | |
parent | 59cec57eec422d8157c2cbc6ba158aef4d2390bf (diff) |
Update the osgi remote service admin interfaces/classes
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@805623 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/sca/modules')
14 files changed, 1626 insertions, 773 deletions
diff --git a/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF b/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF index cef4fb1ff0..9f5c57c6e9 100644 --- a/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF +++ b/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF @@ -12,6 +12,9 @@ Import-Package: javax.xml.namespace, javax.xml.stream,
org.apache.tuscany.sca.assembly;version="2.0.0",
org.apache.tuscany.sca.assembly.builder;version="2.0.0",
+ org.apache.tuscany.sca.common.java.collection;version="2.0.0",
+ org.apache.tuscany.sca.common.java.io;version="2.0.0",
+ org.apache.tuscany.sca.common.xml.stax;version="2.0.0",
org.apache.tuscany.sca.contribution;version="2.0.0",
org.apache.tuscany.sca.contribution.processor;version="2.0.0",
org.apache.tuscany.sca.contribution.resolver;version="2.0.0",
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java index 0fbcbc8395..6d4a60de6f 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java @@ -27,17 +27,19 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.Map.Entry; import java.util.logging.Level; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamReader; import org.apache.tuscany.sca.assembly.AssemblyFactory; +import org.apache.tuscany.sca.common.java.io.IOHelper; +import org.apache.tuscany.sca.common.xml.stax.StAXHelper; import org.apache.tuscany.sca.contribution.processor.ExtensibleStAXArtifactProcessor; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint; @@ -48,15 +50,16 @@ import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions; import org.apache.tuscany.sca.monitor.Monitor; import org.apache.tuscany.sca.monitor.MonitorFactory; import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants; import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointDescriptionImpl; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleListener { - private XMLInputFactory xmlInputFactory; - private XMLOutputFactory xmlOutputFactory; + private StAXHelper staxHelper; private AssemblyFactory assemblyFactory; private StAXArtifactProcessor processor; @@ -71,8 +74,7 @@ public class LocalDiscoveryService extends AbstractDiscoveryService implements B FactoryExtensionPoint factories = registry.getExtensionPoint(FactoryExtensionPoint.class); this.assemblyFactory = factories.getFactory(AssemblyFactory.class); - this.xmlInputFactory = factories.getFactory(XMLInputFactory.class); - this.xmlOutputFactory = factories.getFactory(XMLOutputFactory.class); + this.staxHelper = StAXHelper.getInstance(registry); StAXArtifactProcessorExtensionPoint processors = registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class); UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class); @@ -81,18 +83,32 @@ public class LocalDiscoveryService extends AbstractDiscoveryService implements B if (monitorFactory != null) { monitor = monitorFactory.createMonitor(); } - processor = new ExtensibleStAXArtifactProcessor(processors, xmlInputFactory, xmlOutputFactory, monitor); + processor = + new ExtensibleStAXArtifactProcessor(processors, staxHelper.getInputFactory(), + staxHelper.getOutputFactory(), monitor); processExistingBundles(); } public void bundleChanged(BundleEvent event) { - switch (event.getType()) { - case STARTED: - discover(event.getBundle()); - break; - case STOPPING: - removeServicesDeclaredInBundle(event.getBundle()); - break; + try { + switch (event.getType()) { + case STARTED: + discover(event.getBundle()); + break; + case STOPPING: + removeServicesDeclaredInBundle(event.getBundle()); + break; + } + } 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); + } } } @@ -137,8 +153,16 @@ public class LocalDiscoveryService extends AbstractDiscoveryService implements B } private EndpointDescription createEndpointDescription(ServiceDescription sd) { - // Endpoint endpoint = assemblyFactory.createEndpoint(); - EndpointDescription sed = new EndpointDescriptionImpl(sd.getInterfaces(), sd.getProperties()); + 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.ENDPOINT_REMOTE_SERVICE_ID)) { + props.put(RemoteConstants.ENDPOINT_REMOTE_SERVICE_ID, UUID.randomUUID().toString()); + } + if (!props.containsKey(RemoteConstants.ENDPOINT_URI)) { + props.put(RemoteConstants.ENDPOINT_URI, UUID.randomUUID().toString()); + } + + EndpointDescription sed = new EndpointDescriptionImpl(props); return sed; } @@ -167,9 +191,9 @@ public class LocalDiscoveryService extends AbstractDiscoveryService implements B } private ServiceDescriptions loadServiceDescriptions(URL url) throws Exception { - InputStream is = url.openStream(); + InputStream is = IOHelper.openStream(url); try { - XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(is); + XMLStreamReader reader = staxHelper.createXMLStreamReader(is); reader.nextTag(); Object model = processor.read(reader); if (model instanceof ServiceDescriptions) { diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java index 482aa62a23..3dde0fb95d 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java @@ -1,146 +1,296 @@ /* + * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved. * - * 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. + * 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.service.remoteadmin; -import java.net.URI; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +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.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 + * 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. + * 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. * * @Immutable + * @version $Revision$ */ -public interface EndpointDescription { +public class EndpointDescription implements Serializable { + private static final long serialVersionUID = 1L; + private static Version nullVersion = new Version("0"); + + final Map/* <String,Object> */properties = new Hashtable/* <String,Object> */(); + final List /* String */interfaces; + final String remoteServiceId; + final String uri; + /** - * 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 distributon - * provider can create a connection to this endpoint. This value represents - * the RemoteConstants.SERVICE_IMPORTED_CONFIGS - * - * @return Returns The configuration type used for the associated endpoint - * and optionally synonyms. + * Create an Endpoint Description based on a Map. + * + * @param properties + * @throws IllegalArgumentException + * When the properties are not proper for an Endpoint + * Description */ - public List<String> getConfigurationTypes(); + + public EndpointDescription(Map/* <String,Object> */properties) throws IllegalArgumentException { + this.properties.putAll(properties); + + interfaces = verifyInterfacesProperty(); + remoteServiceId = verifyStringProperty(RemoteConstants.ENDPOINT_REMOTE_SERVICE_ID); + uri = verifyStringProperty(RemoteConstants.ENDPOINT_URI); + } /** - * 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 RemoteConstants.SERVICE_INTENTS + * Create an Endpoint Description based on a reference. * - * @return A list of expanded intents that are provided by this endpoint. + * @param ref A service reference that is exportable + * @throws IllegalArgumentException */ - public List<String> getIntents(); + public EndpointDescription(ServiceReference ref) throws IllegalArgumentException { + String[] keys = ref.getPropertyKeys(); + for (int i = 0; i > keys.length; i++) + properties.put(keys[i], ref.getProperty(keys[i])); + + interfaces = verifyInterfacesProperty(); + remoteServiceId = verifyStringProperty(RemoteConstants.ENDPOINT_REMOTE_SERVICE_ID); + uri = verifyStringProperty(RemoteConstants.ENDPOINT_URI); + } /** - * 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 objectClass - * property. + * Verify and obtain the interface list from the properties. + * @return A list with the interface names. + * @throws IllegalArgumentException when + */ + protected 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 the a required String property. + * @param propName The name of the + * @return The value of the property. + * @throws IllegalArgumentException when the property is not set or doesn't + * have the correct data type. + */ + protected 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; + } + + /** + * 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#ENDPOINT_URI} property. * - * @return The list of Java interface names accessible by this endpoint + * @return The URI of the endpoint, never null. */ - public List<String> getInterfaces(); + public String getURI() { + return uri; + } /** - * Answer the version of the given interface. The version is encoded by - * prefixing the given interface name with endpoint.version., and then using - * this as a property key. The value must then be the Version object. For - * example: endpoint.version.com.acme.Foo + * Answer the list of interfaces implemented by the exported service. * - * @param name The name of the interface for which a version is requested - * @return Returns The version of the given interface or null if the - * interface has no version in this Endpoint Description + * 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 Version getInterfaceVersion(String name); + public List/* <String> */getInterfaces() { + return interfaces; + } /** - * Returns An immutable map referring to the properties of this Endpoint - * Description. + * 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: * - * @return Returns all endpoint properties. + * <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 Map<String, Object> getProperties(); + public Version getInterfaceVersion(String name) { + String v = (String)properties.get("endpoint.version." + name); + if (v == null) { + return nullVersion; + } else { + return new Version(v); + } + } /** * Returns the universally unique id for the service exported through this - * endpoint. Each service in the OSGi service registry has a universally - * unique id. The UUID can be used to detect that two Endpoint Descriptions - * really refer to the same registered service instance in some remote - * framework. This UUID can be used to filter out duplicate ways of - * communicating with the same service. The service UUID is constructed from - * two properties. It is first the org.osgi.framework.uuid System property - * set by the framework or through configuration. This property must - * uniquely represents the UUID of a framework instance. This UUID must not - * contain any dots ('.' .). This is suffixed with a dot and then the - * service.id service property of the service. - * <p> - * For example: 72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72.121 - * <p> + * endpoint. + * + * Each service in the OSGi service registry has a universally unique id. + * The UUID can be used to detect that two Endpoint Descriptions really + * refer to the same registered service instance in some remote framework. + * This UUID can be used to filter out duplicate ways of communicating with + * the same service. + * + * The service UUID is constructed from two properties. It is first the + * <code>org.osgi.framework.uuid</code> System property set by the + * framework or through configuration. This property must uniquely + * represents the UUID of a framework instance. This UUID must not contain + * any dots ('.' \u002E). This is suffixed with a dot and then the + * <code>service.id</code> service property of the service. + * + * For example: + * + * <pre> + * 72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72.121 + * </pre> + * * If this Endpoint Description does not map to a remote OSGi service, for * example some web service, then the Endpoint Description must not have a * service UUID. If two endpoints have the same URI, then they must refer to * the same OSGi service. * - * @return Returns Unique id of a service or null if this Endpoint + * Starting . is not an OSGi service. + * + * @return Unique id of a service or <code>null</code> if this Endpoint * Description does not relate to an OSGi service + * */ - public String getRemoteServiceID(); + public String getRemoteServiceID() { + return remoteServiceId; + } /** - * 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 RemoteConstants.ENDPOINT_URI property. + * Returns the configuration types. * - * @return Returns The URI of the endpoint, never null. + * 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 URI getURI(); + 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; + + } + + /** + * Returns all endpoint properties. + * + * @return An immutable map referring to the properties of this Endpoint + * Description. + */ + public Map/* <String, Object> */getProperties() { + // TODO + return Collections.unmodifiableMap(properties); + } /** * Two endpoints are equal if their URIs are equal, the hash code is * therefore derived from the URI. */ - public int hashCode(); + public int hashCode() { + // TODO + return getURI().hashCode(); + } /** * Two endpoints are equal if their URIs are equal. - * - * @param other - * @return */ - public boolean equals(Object other); - + public boolean equals(Object other) { + if (other instanceof EndpointDescription) { + return getURI().equals(((EndpointDescription)other).getURI()); + } + return false; + } } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java index 033897ba7b..0802fea6e4 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java @@ -1,67 +1,67 @@ -/* - * - * 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.remoteadmin; /** - * A whiteboard service that represents a listener for endpoints. An Endpoint - * Listener represents a participant in the distributed model that is interested - * in Endpoint Descriptions. This whiteboard service can be used in many - * different scenarios. However, the primary use case is to allow a remote - * controller 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 controller bundle register a Endpoint Listener - * service. The controller informs the network bundle about End Points that it - * creates. The network bundles then uses a protocol like for example 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. - * <p> - * Endpoint Listener services can express their scope with the service property - * 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. - * <p> - * (org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821- 9ebb433a5b72)<br> + * A whiteboard service that represents a listener for endpoints. + * + * An Endpoint Listener represents a participant in the distributed model that + * is interested in Endpoint Descriptions. + * + * This whiteboard service can be used in many different scenarios. However, the + * primary use case is to allow a remote controller 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 controller bundle register a Endpoint + * Listener service. The controller informs the network bundle about End Points + * that it creates. The network bundles then uses a protocol like for example + * 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 controller that is only interested in remote Endpoint * Descriptions can use a filter like: - * <p> - * (!(org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72)) <br> + * + * <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 controller 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 best - * effort 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. + * can be found in the Framework properties. + * + * The Endpoint Listener's scope maps very well to the service hooks. A + * controller 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 */ @@ -69,33 +69,41 @@ 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 String+. + * given filter. The type of this property must be <code>String+</code>. */ - public static final String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope"; + String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope"; /** - * Register an endpoint with this listener. If the endpoint matches one of - * the filters registered with the ENDPOINT_LISTENER_SCOPE service property - * then this filter should be given as the matchedFilter parameter. When - * this service is first registered or it is modified, it should receive all - * known endpoints matching the filter. + * 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 matchedFilter The filter from the - * ENDPOINT_LISTENER_SCOPE that matched the endpoint, must not be - * null. + * @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>. */ - public void addEndpoint(EndpointDescription endpoint, String matchedFilter); + void addEndpoint(EndpointDescription endpoint, String matchedFilter); /** - * Remove the registration of an endpoint. If an endpoint that was - * registered with the addEndpoint method is no longer available then this - * method should be called. This will remove the endpoint from the listener. + * Remove the registration of an endpoint. + * + * If an endpoint that was registered with the {@link #addEndpoint} + * 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 endpoint + * The Endpoint Description that is no longer valid. */ - public void removeEndpoint(EndpointDescription endpoint); + void removeEndpoint(EndpointDescription endpoint); } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java index 0658fa5ffc..69b753fa03 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java @@ -1,172 +1,918 @@ +package org.apache.tuscany.sca.osgi.service.remoteadmin; + +// TODO Hacked from ServiePermission + /* - * - * 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 + * Copyright (c) OSGi Alliance (2000, 2009). All Rights Reserved. * - * 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. + * 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.service.remoteadmin; +import java.io.*; +import java.security.*; +import java.util.*; -import java.security.BasicPermission; -import java.security.Permission; -import java.security.PermissionCollection; +import org.osgi.framework.*; /** - * A bundle’s authority to register or get a service. + * A bundle's authority to register or get a service. * <ul> - * <li>The register action allows a bundle to register a service on the - * specified names. - * <li>The get action allows a bundle to detect a service and get it. - * EndpointPermission to get the specific service. + * <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 { - private static final long serialVersionUID = 577543263888050488L; - /** - * The action string get. - */ - public static final String EXPORT = "export"; - /** - * The action string register. - */ - public static final String IMPORT = "import"; - - private String actions; - - /** - *Create a new EndpointPermission. - * - * @param name The service class name The name of the service is specified - * as a fully qualified class name. Wildcards may be used. - * <p> - * name ::= <class name> | <class name ending in ".*"> | * - * <p> - * Examples: - * <ul> - * <li>org.osgi.service.http.HttpService - * <li>org.osgi.service.http.* - * <li>* - * </ul> - * For the get action, the name can also be a filter expression. - * The filter gives access to the service properties as well as - * the following attributes: - * <p> - * <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>location - The location of the bundle publishing the - * service. - * <li>id - The bundle ID of the bundle publishing the service. - * <li>name - The symbolic name of the bundle publishing the - * service. - * </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: get and register. The get - * permission allows the owner of this permission to obtain a - * service with this name. The register permission allows the - * bundle to register a service under that name. - * @param actions actions get,register (canonical order) - * @throws IllegalArgumentException – If the specified name is a filter - * expression and either the specified action is not get or the - * filter has an invalid syntax. + 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"; + + 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 ::= <class name> | <class name ending in ".*"> | * + * </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) { - super(name); - this.actions = 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 EndpointPermission object to be used by code that - * must perform checkPermission for the get action. EndpointPermission - * objects created with this constructor cannot be added to a - * EndpointPermission permission collection. + * 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 get. - * @throws IllegalArgumentException – If the specified action is not get or - * reference is null. + * @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(null); - this.actions = 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"); + } } /** - * Determines the equality of two EndpointPermission objects. Checks that - * specified object has the same class name and action as this - * EndpointPermission. obj The object to test for equality. + * Create a permission name from a EndpointDescription TODO Needs work * - * @return true if obj is a EndpointPermission, and has the same class name - * and actions as this EndpointPermission object; false otherwise. + * @param endpoint + * EndpointDescription to use to create permission name. + * @return permission name. */ - public boolean equals(Object obj) { - return super.equals(obj); + 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: get, register. + * returns present actions in the following order: <code>get</code>, + * <code>register</code>. * * @return The canonical string representation of the actions. */ public String getActions() { - return actions; + 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. Returns Hash code value for - * this object. + * Returns the hash code value for this object. * - * @return + * @return Hash code value for this object. */ public int hashCode() { - return super.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(); } /** - *Determines if a EndpointPermission object "implies" the specified - * permission. + * 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>. * - * @param p The target permission to check. - * @return true if the specified permission is implied by this object; false - * otherwise. newPermissionCollection() + * @return a dictionary of properties for this permission. */ - public boolean implies(Permission p) { - return super.implies(p); + 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; } /** - * Returns a new PermissionCollection object for storing EndpointPermission - * objects. + * Adds a permission to this permission collection. * - * @return A new PermissionCollection object suitable for storing - * EndpointPermission objects. + * @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 PermissionCollection newPermissionCollection() { - return super.newPermissionCollection(); + 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/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java index 49c1d14550..a8c825d4ef 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java @@ -1,71 +1,63 @@ -/* - * - * 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.remoteadmin; -import org.osgi.framework.ServiceReference; +import org.osgi.framework.*; /** - * 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 - * RemoteAdmin.exportService(ServiceReference) method. When this Export - * Registration has been unregistered, the methods must all return null. + * 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 RemoteAdmin#exportService(ServiceReference)} + * method. + * + * When this Export Registration has been unregistered, the methods must all + * return <code>null</code>. * * @ThreadSafe */ public interface ExportRegistration { /** - * Delete the local endpoint and disconnect any remote distribution - * providers. After this method returns, all the methods must return null. - * This method has no effect when the endpoint is already destroyed or being - * destroyed. + * 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()} */ - public void close(); + ServiceReference getExportedService() throws IllegalStateException; /** - *Return the Endpoint Description that is created for this registration. + * 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()} */ - public EndpointDescription getEndpointDescription(); + EndpointDescription getEndpointDescription(); /** - * 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 Throwable 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 null. The error must be set before this - * Import Registration is returned. Asynchronously occurring errors must be - * reported to the log. + * Delete the local endpoint and disconnect any remote distribution + * providers. After this method returns, all the methods must return + * <code>null</code>. * - * @return The exception that occurred during the creation of the - * registration or null if no exception occurred. + * This method has no effect when the endpoint is already destroyed or being + * destroyed. */ - public Throwable getException(); + void close(); /** - * Return the service being exported. + * 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. * - * @return The service being exported, must be null when this registration - * is unregistered. + * future .... + * + * @return The exception that occurred during the creation of the + * registration or <code>null</code> if no exception occurred. */ - public ServiceReference getExportedService(); + Throwable getException(); } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java index fdf70f849f..2fcf973d3c 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java @@ -1,70 +1,58 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package org.apache.tuscany.sca.osgi.service.remoteadmin; -import org.osgi.framework.ServiceReference; +import org.osgi.framework.*; /** * 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 RemoteAdmin.importService method. + * endpoint. + * + * The Import Registration can be used to delete the proxy associated with an + * endpoint. It is created with the{@link RemoteAdmin#importService} + * method. * * @ThreadSafe */ public interface ImportRegistration { - /** - * 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. + * 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()} */ - public void close(); + ServiceReference getImportedService(); /** - * 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 Throwable 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 null. The error must be set before this - * Import Registration is returned. Asynchronously occurring errors must be - * reported to the log. + * Answer the associated remote Endpoint Description. * - * @return The exception that occurred during the creation of the - * registration or null if no exception occurred. + * @return A Endpoint Description for the remote endpoint. + * @throws IllegalStateException Thrown when this object was not properly initialized, see {@link #getException()} */ - public Throwable getException(); + EndpointDescription getImportedEndpointDescription(); /** - * Answer the associated remote Endpoint Description. + * 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. * - * @return A Endpoint Description for the remote endpoint. + * This method has no effect when the service is already unregistered or in the process off. */ - public EndpointDescription getImportedEndpointDescription(); + void close(); /** - * Answer the associated Service Reference for the proxy to the endpoint. + * Exception for any error during the import process. * - * @return A Service Reference to the proxy for the endpoint. + * 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. + * + * @return The exception that occurred during the creation of the + * registration or <code>null</code> if no exception occurred. */ - public ServiceReference getImportedService(); + Throwable getException(); } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java index 02ad9c81fb..5045abdea9 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java @@ -1,148 +1,116 @@ -/* - * - * 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.remoteadmin; -import java.util.EventObject; - import org.osgi.framework.Bundle; /** + * * Provides the event information for a Remote Admin event. * * @Immutable */ -public class RemoteAdminEvent extends EventObject { +public class RemoteAdminEvent { /** - * A fatal exporting error occurred. The Export Registration has been - * closed. + * 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 final static int EXPORT_ERROR = 0x1; + 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. + * 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 final static int EXPORT_REGISTRATION = 0x2; + 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. + * Remove an export registration. The Remote Services Admin will call this + * method when it removes the export of a service. + * */ - public final static int EXPORT_UNREGISTRATION = 0x4; + public static final int EXPORT_UNREGISTRATION = 3; /** - * A problematic situation occurred, the export is still active. + * Remove an import registration. The Remote Services Admin will call this + * method when it removes the import of a service. + * */ - public final static int EXPORT_WARNING = 0x8; + public static final int IMPORT_UNREGISTRATION = 4; /** * A fatal importing error occurred. The Import Registration has been * closed. */ - public final static int IMPORT_ERROR = 0x10; + public static final int IMPORT_ERROR = 5; /** - * 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. + * A fatal exporting error occurred. The Export Registration has been + * closed. */ - public final static int IMPORT_REGISTRATION = 0x20; + public static final int EXPORT_ERROR = 6; /** - * Remove an import registration. The Remote Services Admin will call - * this method when it removes the import of a service. + * A problematic situation occurred, the export is still active. */ - public final static int IMPORT_UNREGISTRATION = 0x40; - + public static final int EXPORT_WARNING = 7; /** * A problematic situation occurred, the import is still active. */ - public final static int IMPORT_WARNING = 0x80; + public static final int IMPORT_WARNING = 8; - private static final long serialVersionUID = -6562225073284539118L; - private Throwable exception; - private ExportRegistration exportRegistration; private ImportRegistration importRegistration; + private ExportRegistration exportRegistration; + private Throwable exception; private int type; + private Bundle source; - public RemoteAdminEvent(Bundle source, int type, ExportRegistration registration, Throwable exception) { - super(source); - this.type = type; - this.exportRegistration = registration; - this.exception = exception; - } - - public RemoteAdminEvent(Bundle source, int type, ImportRegistration registration, Throwable exception) { - super(source); + RemoteAdminEvent(int type, + Bundle source, + ImportRegistration importRegistration, + ExportRegistration exportRegistration, + Throwable exception) { this.type = type; - this.importRegistration = registration; + this.source = source; + this.importRegistration = importRegistration; + this.exportRegistration = exportRegistration; this.exception = exception; } /** - * Returns the exception - * - * @return + * @return the importRegistration */ - public Throwable getException() { - return exception; + public ImportRegistration getImportRegistration() { + return importRegistration; } /** - * Returns the exportRegistration - * - * @return + * @return the exportRegistration */ public ExportRegistration getExportRegistration() { return exportRegistration; } /** - * Returns the importRegistration - * - * @return + * @return the exception */ - public ImportRegistration getImportRegistration() { - return importRegistration; + public Throwable getException() { + return exception; } /** - * Returns the source - * - * @return + * @return the type */ - public Bundle getSource() { - return (Bundle)source; + public int getType() { + return type; } /** - * Returns the type - * - * @return + * @return the source */ - public int getType() { - return type; + public Bundle getSource() { + return source; } - } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java index 21ffee971f..c81ec7ff13 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java @@ -1,23 +1,3 @@ -/* - * - * 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.remoteadmin; /** @@ -26,11 +6,10 @@ package org.apache.tuscany.sca.osgi.service.remoteadmin; * * @ThreadSafe */ + public interface RemoteAdminListener { /** - * Notify of a remote admin event - * - * @param event The remote admin event + * @param event */ - public void remoteAdminEvent(RemoteAdminEvent event); + void remoteAdminEvent(RemoteAdminEvent event); } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java index f55bb030d7..379bb8c368 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java @@ -1,130 +1,119 @@ -/* - * - * 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.remoteadmin; /** * Provide the definition of the constants used in the Remote Services API. + * */ public class RemoteConstants { - /** - * 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. - */ - public static final String ENDPOINT_URI = "endpoint.uri"; + private RemoteConstants() { + } /** - * The key for a framework property that defines the UUID of the framework. - * The property must be set by the framework or through configuration before - * the VM is started or some bundle. The value must be a Universally Unique - * Id, it must not contain any dots ('.' .). - */ - public static final String FRAMEWORK_UUID = "org.osgi.framework.uuid"; - - /** - * 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 - * SERVICE_EXPORTED_CONFIGS or 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 String+ - * - * @see SERVICE_EXPORTED_CONFIGS - * @see SERVICE_IMPORTED_CONFIGS + * 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 static final String REMOTE_CONFIGS_SUPPORTED = "remote.configs.supported"; + 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 - * String+. - * - * @see SERVICE_INTENTS - * @see SERVICE_EXPORTED_INTENTS - * @see SERVICE_EXPORTED_INTENTS_EXTRA + * 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 static final String REMOTE_INTENTS_SUPPORTED = "remote.intents.supported"; + public final static String REMOTE_INTENTS_SUPPORTED = "remote.intents.supported"; /** - * A list of configuration types that should be used to export the service. - * Configuration types can be synonymous or alternatives. 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 - * String+. + * 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 static final String SERVICE_EXPORTED_CONFIGS = "service.exported.configs"; + 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 String+. + * + * The value of this property is of type <code>String+</code>. */ - public static final String SERVICE_EXPORTED_INTENTS = "service.exported.intents"; + public final static String SERVICE_EXPORTED_INTENTS = "service.exported.intents"; /** * Extra intents configured in addition to the the intents specified in - * SERVICE_EXPORTED_INTENTS. These intents are merged with the service. - * exported.intents and therefore have the same semantics. They are extra, - * so that the 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 - * String+. + * {@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 static final String SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra"; + public final static String SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra"; /** - * 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 ('*' *) 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+. + * 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 static final String SERVICE_EXPORTED_INTERFACES = "service.exported.interfaces"; + public final static String SERVICE_EXPORTED_CONFIGS = "service.exported.configs"; /** - * Must be set by a distribution provider to true 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. + * 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 static final String SERVICE_IMPORTED = "service.imported"; + public final static String SERVICE_IMPORTED = "service.imported"; /** * The configuration type used to import this services, as described in - * SERVICE_EXPORTED_CONFIGS. Any associated properties for this + * {@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 String+. + * must be synonymous. + * + * The value of this property is of type <code>String+</code>. */ public final String SERVICE_IMPORTED_CONFIGS = "service.imported.configs"; @@ -136,8 +125,32 @@ public class RemoteConstants { * 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 - * String+. + * 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 ENDPOINT_URI = "endpoint.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 ENDPOINT_REMOTE_SERVICE_ID = "endpoint.remote.service.id"; + + /** + * The key for a framework property that defines the UUID of the framework. + * + * The property must be set by the framework or through configuration before + * the VM is started or some bundle. The value must be a Universally Unique + * Id, it must not contain any dots ('.' \u002E). */ - public static final String SERVICE_INTENTS = "service.intents"; + public final static String FRAMEWORK_UUID = "org.osgi.framework.uuid"; } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteServiceAdmin.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteServiceAdmin.java index 5057089e7d..a341222a17 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteServiceAdmin.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteServiceAdmin.java @@ -1,54 +1,39 @@ -/* - * - * 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.remoteadmin; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; -import org.osgi.framework.ServiceReference; +import org.osgi.framework.*; /** - * 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 controller to control the export and import of services. The API - * allows a remote controller to export a service, to import a service, and find - * out about the current imports and exports. + * 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 controller to control the export and import of services. + * + * The API allows a remote controller 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 an endpoint. The Remote Service Admin must create an * endpoint 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. + * return null if the service could not be exported. ### do we need + * exceptions? * - * @param ref The Service Reference to export - * @return Export Registration that combines the Endpoint Description and - * the Service Reference or <code>null</code if the service could - * not be exported + * @param ref + * The Service Reference to export + * @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 */ - public List<ExportRegistration> exportService(ServiceReference ref); + List/*<ExportRegistration>*/exportService(ServiceReference ref) throws IllegalArgumentException, + UnsupportedOperationException; /** * Export a service to a given endpoint. The Remote Service Admin must @@ -56,46 +41,51 @@ public interface RemoteServiceAdmin { * 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. + * Service Admin. ### do we need exceptions? * - * @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, - * @see exportService(ServiceReference). The properties are the same as - * given for an exported service. They are overlaid over any properties - * the service defines - * @return Export Registration that combines the Endpoint Description and - * the Service Reference or <code>null</code if the service could - * not be exported + * @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, see + * {@link #exportService(ServiceReference)}. The properties are + * the same as given for an exported service. They are overlaid + * over any properties the service defines. + * @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 */ - public List<ExportRegistration> exportService(ServiceReference ref, Map<String, Object> properties); + List/*<ExportRegistration>*/exportService(ServiceReference ref, Map/*<String,Object>*/properties) + throws IllegalArgumentException, UnsupportedOperationException; /** - * Answer the currently active Export Registrations. + * 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. ### do we need exceptions? * - * @return Returns A collection of Export Registrations that are currently - * active. + * @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 */ - public Collection<ExportRegistration> getExportedServices(); + ImportRegistration importService(EndpointDescription endpoint); /** - * Answer the currently active Import Registrations. + * Answer the currently active Export Registrations. * - * @return Returns A collection of Import Registrations that are currently - * active. + * @return A collection of Export Registrations that are currently active. */ - public Collection<ImportRegistration> getImportedEndpoints(); + Collection/*<? extends ExportRegistration>*/getExportedServices(); /** - * 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. + * Answer the currently active Import Registrations. * - * @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 + * @return A collection of Import Registrations that are currently active. */ - public ImportRegistration importService(EndpointDescription endpoint); + Collection/*<? extends ImportRegistration>*/getImportedEndpoints(); + } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java index 6557b185ee..d294bcfadf 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java @@ -19,14 +19,10 @@ package org.apache.tuscany.sca.osgi.service.remoteadmin.impl; -import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_EXPORTED_INTERFACES; - -import java.net.URI; import java.util.ArrayList; 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.UUID; @@ -38,37 +34,50 @@ import org.apache.tuscany.sca.interfacedef.java.JavaInterface; import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription; import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants; import org.apache.tuscany.sca.policy.Intent; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; /** * Implementation of {@link EndpointDescription} */ -public class EndpointDescriptionImpl implements EndpointDescription { - private Endpoint endpoint; - +public class EndpointDescriptionImpl extends EndpointDescription { private static final Logger logger = Logger.getLogger(EndpointDescriptionImpl.class.getName()); + private Endpoint endpoint; - private Collection<String> interfaces; - private Map<String, Object> properties; + /** + * @param properties + * @throws IllegalArgumentException + */ + public EndpointDescriptionImpl(Map properties) throws IllegalArgumentException { + super(properties); + this.endpoint = (Endpoint)getProperties().get(Endpoint.class.getName()); + } - public EndpointDescriptionImpl(Collection<String> interfaceNames) { - this(interfaceNames, Collections.<String, Object> singletonMap(SERVICE_EXPORTED_INTERFACES, interfaceNames)); + /** + * @param ref + * @throws IllegalArgumentException + */ + public EndpointDescriptionImpl(ServiceReference ref) throws IllegalArgumentException { + super(ref); + this.endpoint = (Endpoint)getProperties().get(Endpoint.class.getName()); } - public EndpointDescriptionImpl(Collection<String> interfaceNames, Map<String, Object> remoteProperties) { - this.interfaces = new HashSet<String>(interfaceNames); - this.properties = - remoteProperties == null ? new HashMap<String, Object>() : new HashMap<String, Object>(remoteProperties); - this.properties.put(RemoteConstants.SERVICE_EXPORTED_INTERFACES, interfaceNames); - this.endpoint = (Endpoint) properties.get(Endpoint.class.getName()); + public EndpointDescriptionImpl(Collection<String> interfaces, String remoteServiceId, String uri) { + super(getProperties(interfaces, remoteServiceId, uri)); + this.endpoint = (Endpoint)getProperties().get(Endpoint.class.getName()); } - public EndpointDescriptionImpl(String interfaceName) { - this(Collections.singleton(interfaceName)); + private static Map<String, Object> getProperties(Collection<String> interfaces, String remoteServiceId, String uri) { + Map<String, Object> props = new HashMap<String, Object>(); + props.put(Constants.OBJECTCLASS, interfaces.toArray(new String[interfaces.size()])); + props.put(RemoteConstants.ENDPOINT_REMOTE_SERVICE_ID, remoteServiceId); + props.put(RemoteConstants.ENDPOINT_URI, uri); + return props; } public EndpointDescriptionImpl(Endpoint endpoint) { - this(getInterfaces(endpoint), getProperties(endpoint)); + this(getProperties(endpoint)); this.endpoint = endpoint; } @@ -97,31 +106,20 @@ public class EndpointDescriptionImpl implements EndpointDescription { return Version.emptyVersion; } - /** - * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getInterfaces() - */ - public List<String> getInterfaces() { - return new ArrayList<String>(interfaces); - } - private static List<String> getInterfaces(Endpoint endpoint) { Interface intf = endpoint.getInterfaceContract().getInterface(); JavaInterface javaInterface = (JavaInterface)intf; return Collections.singletonList(javaInterface.getName()); } - /** - * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getProperties() - */ - public Map<String, Object> getProperties() { - return Collections.unmodifiableMap(properties); // endpoint.getService().getExtensions(); - } - private static Map<String, Object> getProperties(Endpoint endpoint) { Map<String, Object> props = new HashMap<String, Object>(); props.put(RemoteConstants.ENDPOINT_URI, endpoint.getURI()); + props.put(RemoteConstants.ENDPOINT_REMOTE_SERVICE_ID, UUID.randomUUID().toString()); props.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, new String[] {"sca"}); props.put(Endpoint.class.getName(), endpoint); + List<String> interfaces = getInterfaces(endpoint); + props.put(Constants.OBJECTCLASS, interfaces.toArray(new String[interfaces.size()])); return props; } @@ -135,11 +133,11 @@ public class EndpointDescriptionImpl implements EndpointDescription { /** * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getURI() */ - public URI getURI() { + public String getURI() { if (endpoint != null) { - return URI.create(endpoint.getURI()); + return endpoint.getURI(); } else { - return URI.create("urn:" + UUID.randomUUID()); + return super.getURI(); } } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java index 53c9d6fb4a..1c57c0ec1a 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java @@ -31,9 +31,7 @@ import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.I import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS; import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_IMPORTED; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; @@ -42,18 +40,21 @@ 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.service.remoteadmin.EndpointDescription; import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener; import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration; import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration; -import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteServiceAdmin; import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent; import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener; +import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteServiceAdmin; 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; @@ -79,12 +80,12 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, private ServiceTracker remotableServices; // Service listeners keyed by the filter - private MappedCollections<String, ListenerInfo> serviceListeners = new MappedCollections<String, ListenerInfo>(); + private CollectionMap<String, ListenerInfo> serviceListeners = new CollectionMap<String, ListenerInfo>(); - private MappedCollections<ServiceReference, ExportRegistration> exportedServices = - new MappedCollections<ServiceReference, ExportRegistration>(); - private MappedCollections<EndpointDescription, ImportRegistration> importedServices = - new MappedCollections<EndpointDescription, ImportRegistration>(); + private CollectionMap<ServiceReference, ExportRegistration> exportedServices = + new CollectionMap<ServiceReference, ExportRegistration>(); + private CollectionMap<EndpointDescription, ImportRegistration> importedServices = + new CollectionMap<EndpointDescription, ImportRegistration>(); private Filter remotableServiceFilter; @@ -186,7 +187,7 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra; List<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference); if (exportRegistrations != null && !exportRegistrations.isEmpty()) { - exportedServices.putValue(reference, exportRegistrations); + exportedServices.putValues(reference, exportRegistrations); } } } @@ -196,28 +197,40 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, * @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection) */ public void added(Collection listeners) { - 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); + 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; } - infos.add(l); - changed = true; } - } - if (changed) { - updateEndpointListenerScope(); + 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); + } } } @@ -229,7 +242,7 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, endpointListener.setProperties(props); } - private MappedCollections<Class<?>, ListenerInfo> findServiceListeners(EndpointDescription endpointDescription, + private CollectionMap<Class<?>, ListenerInfo> findServiceListeners(EndpointDescription endpointDescription, String matchedFilter) { // First find all the listeners that have the matching filter Collection<ListenerInfo> listeners = serviceListeners.get(matchedFilter); @@ -239,8 +252,8 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, // Try to partition the listeners by the interface classes List<String> interfaceNames = endpointDescription.getInterfaces(); - MappedCollections<Class<?>, ListenerInfo> interfaceToListeners = - new MappedCollections<Class<?>, ListenerInfo>(); + CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = + new CollectionMap<Class<?>, ListenerInfo>(); for (String i : interfaceNames) { for (ListenerInfo listener : listeners) { try { @@ -258,21 +271,33 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, * @see org.osgi.framework.hooks.service.ListenerHook#removed(java.util.Collection) */ public void removed(Collection listeners) { - 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; + 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(); + 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); + } } } @@ -316,7 +341,7 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, return; } - MappedCollections<Class<?>, ListenerInfo> interfaceToListeners = findServiceListeners(endpoint, matchedFilter); + 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(); @@ -326,8 +351,8 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties()); props.put(Bundle.class.getName(), bundle); - EndpointDescription description = - new EndpointDescriptionImpl(Collections.singletonList(interfaceClass.getName()), props); + props.put(Constants.OBJECTCLASS, new String[] {interfaceClass.getName()}); + EndpointDescription description = new EndpointDescriptionImpl(props); if (admins != null) { for (Object ra : admins) { @@ -369,35 +394,4 @@ public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, serviceListeners.clear(); } - private static class MappedCollections<K, V> extends HashMap<K, Collection<V>> { - private static final long serialVersionUID = -8926174610229029369L; - - public boolean putValue(K key, V value) { - Collection<V> collection = get(key); - if (collection == null) { - collection = new ArrayList<V>(); - put(key, collection); - } - return collection.add(value); - } - - public boolean putValue(K key, Collection<? extends V> value) { - Collection<V> collection = get(key); - if (collection == null) { - collection = new ArrayList<V>(); - put(key, collection); - } - return collection.addAll(value); - } - - public boolean removeValue(K key, V value) { - Collection<V> collection = get(key); - if (collection == null) { - return false; - } - return collection.remove(value); - } - - } - } diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteServiceAdminImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteServiceAdminImpl.java index 325911d916..1f2c03a5c6 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteServiceAdminImpl.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteServiceAdminImpl.java @@ -27,8 +27,8 @@ import java.util.Map; import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription; import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration; import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration; -import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteServiceAdmin; import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener; +import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteServiceAdmin; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -105,7 +105,7 @@ public class RemoteServiceAdminImpl implements RemoteServiceAdmin { * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteServiceAdmin#exportService(org.osgi.framework.ServiceReference, * java.util.Map) */ - public List<ExportRegistration> exportService(ServiceReference ref, Map<String, Object> properties) { + public List<ExportRegistration> exportService(ServiceReference ref, Map properties) { List<ExportRegistration> exportRegistrations = exporter.exportService(ref); if (exportRegistrations != null) { exportedServices.addAll(exportRegistrations); |