diff options
Diffstat (limited to 'osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi')
12 files changed, 2390 insertions, 0 deletions
diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointDescription.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointDescription.java new file mode 100644 index 000000000..76fea7711 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointDescription.java @@ -0,0 +1,711 @@ +/* + * Copyright (c) OSGi Alliance (2008, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_PACKAGE_VERSION_; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS; +import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_INTENTS; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +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 because + * it is property based where the property keys are strings and the values are + * simple types. 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 <i>importer</i>. That + * is, the property keys have been chosen to match filters that are created by + * client bundles that need a service. Therefore the map must not contain any + * {@code service.exported.*} property and must contain the corresponding + * {@code service.imported.*} ones. + * + * The {@code service.intents} property must contain the intents provided + * by the service itself combined with the intents added by the exporting + * distribution provider. Qualified intents appear fully expanded on this + * property. + * + * @Immutable + * @version $Id: 5eee827d56c59f61ee7c7b2e07601e20e97813b5 $ + */ + +public class EndpointDescription { + private final Map<String, Object> properties; + private final List<String> interfaces; + private final long serviceId; + private final String frameworkUUID; + private final String id; + + /** + * Create an Endpoint Description from a Map. + * + * <p> + * The {@link RemoteConstants#ENDPOINT_ID endpoint.id}, + * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS service.imported.configs} + * and {@code objectClass} properties must be set. + * + * @param properties The map from which to create the Endpoint Description. + * The keys in the map must be type {@code String} and, since + * the keys are case insensitive, there must be no duplicates with + * case variation. + * @throws IllegalArgumentException When the properties are not proper for + * an Endpoint Description. + */ + + public EndpointDescription(Map<String, ? > properties) { + Map<String, Object> props = new TreeMap<String, Object>( + String.CASE_INSENSITIVE_ORDER); + try { + props.putAll(properties); + } + catch (ClassCastException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "non-String key in properties"); + iae.initCause(e); + throw iae; + } + if (props.size() < properties.size()) { + throw new IllegalArgumentException( + "duplicate keys with different cases in properties: " + + new ArrayList<String>(props.keySet()) + .removeAll(properties.keySet())); + } + + conditionProperties(props); + this.properties = Collections.unmodifiableMap(props); + /* properties must be initialized before calling the following methods */ + interfaces = verifyObjectClassProperty(); + serviceId = verifyLongProperty(ENDPOINT_SERVICE_ID); + frameworkUUID = verifyStringProperty(ENDPOINT_FRAMEWORK_UUID); + id = verifyStringProperty(ENDPOINT_ID).trim(); + if (id == null) { + throw new IllegalArgumentException(ENDPOINT_ID + + " property must be set"); + } + if (getConfigurationTypes().isEmpty()) { + throw new IllegalArgumentException(SERVICE_IMPORTED_CONFIGS + + " property must be set and non-empty"); + } + } + + /** + * Create an Endpoint Description based on a Service Reference and a Map of + * properties. The properties in the map take precedence over the properties + * in the Service Reference. + * + * <p> + * This method will automatically set the + * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID endpoint.framework.uuid} + * and {@link RemoteConstants#ENDPOINT_SERVICE_ID endpoint.service.id} + * properties based on the specified Service Reference as well as the + * {@link RemoteConstants#SERVICE_IMPORTED service.imported} property if + * they are not specified as properties. + * <p> + * The {@link RemoteConstants#ENDPOINT_ID endpoint.id}, + * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS service.imported.configs} + * and {@code objectClass} properties must be set. + * + * @param reference A service reference that can be exported. + * @param properties Map of properties. This argument can be + * {@code null}. The keys in the map must be type + * {@code String} and, since the keys are case insensitive, + * there must be no duplicates with case variation. + * @throws IllegalArgumentException When the properties are not proper for + * an Endpoint Description + */ + public EndpointDescription(final ServiceReference reference, + final Map<String, ? > properties) { + Map<String, Object> props = new TreeMap<String, Object>( + String.CASE_INSENSITIVE_ORDER); + + if (properties != null) { + try { + props.putAll(properties); + } + catch (ClassCastException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "non-String key in properties"); + iae.initCause(e); + throw iae; + } + if (props.size() < properties.size()) { + throw new IllegalArgumentException( + "duplicate keys with different cases in properties: " + + new ArrayList<String>(props.keySet()) + .removeAll(properties.keySet())); + } + } + + for (String key : reference.getPropertyKeys()) { + if (!props.containsKey(key)) { + props.put(key, reference.getProperty(key)); + } + } + + if (!props.containsKey(ENDPOINT_SERVICE_ID)) { + props.put(ENDPOINT_SERVICE_ID, reference.getProperty(Constants.SERVICE_ID)); + } + if (!props.containsKey(ENDPOINT_FRAMEWORK_UUID)) { + String uuid = null; + try { + uuid = AccessController + .doPrivileged(new PrivilegedAction<String>() { + public String run() { + return reference.getBundle().getBundleContext() + .getProperty("org.osgi.framework.uuid"); + } + }); + } + catch (SecurityException e) { + // if we don't have permission, we can't get the property + } + if (uuid != null) { + props.put(ENDPOINT_FRAMEWORK_UUID, uuid); + } + } + conditionProperties(props); + this.properties = Collections.unmodifiableMap(props); + /* properties must be initialized before calling the following methods */ + interfaces = verifyObjectClassProperty(); + serviceId = verifyLongProperty(ENDPOINT_SERVICE_ID); + frameworkUUID = verifyStringProperty(ENDPOINT_FRAMEWORK_UUID); + id = verifyStringProperty(ENDPOINT_ID).trim(); + if (id == null) { + throw new IllegalArgumentException(ENDPOINT_ID + + " property must be set"); + } + if (getConfigurationTypes().isEmpty()) { + throw new IllegalArgumentException(SERVICE_IMPORTED_CONFIGS + + " property must be set and non-empty"); + } + } + + private static final String SERVICE_EXPORTED_ = "service.exported."; + private static final int SERVICE_EXPORTED_length = SERVICE_EXPORTED_ + .length(); + + /** + * Condition the properties. + * + * @param props Property map to condition. + */ + private void conditionProperties(Map<String, Object> props) { + // ensure service.imported is set + if (!props.containsKey(SERVICE_IMPORTED)) { + props.put(SERVICE_IMPORTED, Boolean.toString(true)); + } + + // remove service.exported.* properties + for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) { + String key = iter.next(); + if (SERVICE_EXPORTED_.regionMatches(true, 0, key, 0, + SERVICE_EXPORTED_length)) { + iter.remove(); + } + } + } + + /** + * Verify and obtain the interface list from the properties. + * + * @return A list with the interface names. + * @throws IllegalArgumentException If the objectClass property is not set + * or is empty or if the package version property values are + * malformed. + */ + private List<String> verifyObjectClassProperty() { + Object o = properties.get(Constants.OBJECTCLASS); + if (!(o instanceof String[])) { + throw new IllegalArgumentException( + "objectClass value must be of type String[]"); + } + String[] objectClass = (String[]) o; + if (objectClass.length < 1) { + throw new IllegalArgumentException("objectClass is empty"); + } + for (String interf : objectClass) { + int index = interf.lastIndexOf('.'); + if (index == -1) { + continue; + } + String packageName = interf.substring(0, index); + try { + /* Make sure any package version properties are well formed */ + getPackageVersion(packageName); + } + catch (IllegalArgumentException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "Improper version for package " + packageName); + iae.initCause(e); + throw iae; + } + } + return Collections.unmodifiableList(Arrays.asList(objectClass)); + } + + /** + * Verify and obtain a required String property. + * + * @param propName The name of the property + * @return The value of the property or {@code null} if the property is + * not set. + * @throws IllegalArgumentException when the property doesn't have the + * correct data type. + */ + private String verifyStringProperty(String propName) { + Object r = properties.get(propName); + try { + return (String) r; + } + catch (ClassCastException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "property value is not a String: " + propName); + iae.initCause(e); + throw iae; + } + } + + /** + * Verify and obtain a required long property. + * + * @param propName The name of the property + * @return The value of the property or 0 if the property is not set. + * @throws IllegalArgumentException when the property doesn't have the + * correct data type. + */ + private long verifyLongProperty(String propName) { + Object r = properties.get(propName); + if (r == null) { + return 0l; + } + try { + return ((Long) r).longValue(); + } + catch (ClassCastException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "property value is not a Long: " + propName); + iae.initCause(e); + throw iae; + } + } + + /** + * Returns the endpoint's id. + * + * The id is an opaque id for an endpoint. No two different endpoints must + * have the same id. Two Endpoint Descriptions with the same id must + * represent the same endpoint. + * + * The value of the id is stored in the {@link RemoteConstants#ENDPOINT_ID} + * property. + * + * @return The id of the endpoint, never {@code null}. The returned + * value has leading and trailing whitespace removed. + */ + public String getId() { + return id; + } + + /** + * Provide the list of interfaces implemented by the exported service. + * + * The value of the interfaces is derived from the {@code objectClass} + * property. + * + * @return An unmodifiable list of Java interface names implemented by this + * endpoint. + */ + public List<String> getInterfaces() { + return interfaces; + } + + /** + * Provide the version of the given package name. + * + * The version is encoded by prefixing the given package name with + * {@link RemoteConstants#ENDPOINT_PACKAGE_VERSION_ + * endpoint.package.version.}, and then using this as an endpoint property + * key. For example: + * + * <pre> + * endpoint.package.version.com.acme + * </pre> + * + * The value of this property is in String format and will be converted to a + * {@code Version} object by this method. + * + * @param packageName The name of the package for which a version is + * requested. + * @return The version of the specified package or + * {@code Version.emptyVersion} if the package has no version + * in this Endpoint Description. + * @throws IllegalArgumentException If the version property value is not + * String. + */ + public Version getPackageVersion(String packageName) { + String key = ENDPOINT_PACKAGE_VERSION_ + packageName; + Object value = properties.get(key); + String version; + try { + version = (String) value; + } + catch (ClassCastException e) { + IllegalArgumentException iae = new IllegalArgumentException(key + + " property value is not a String"); + iae.initCause(e); + throw iae; + } + return Version.parseVersion(version); + } + + /** + * Returns the service id for the service exported through this endpoint. + * + * This is the service id under which the framework has registered the + * service. This field together with the Framework UUID is a globally unique + * id for a service. + * + * The value of the remote service id is stored in the + * {@link RemoteConstants#ENDPOINT_SERVICE_ID} endpoint property. + * + * @return Service id of a service or 0 if this Endpoint Description does + * not relate to an OSGi service. + * + */ + public long getServiceId() { + return serviceId; + } + + /** + * Returns the configuration types. + * + * A distribution provider exports a service with an endpoint. This endpoint + * uses some kind of communications protocol with a set of configuration + * parameters. There are many different types but each endpoint is + * configured by only one configuration type. However, a distribution + * provider can be aware of different configuration types and provide + * synonyms to increase the change a receiving distribution provider can + * create a connection to this endpoint. + * + * This value of the configuration types is stored in the + * {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS} service property. + * + * @return An unmodifiable list of the configuration types used for the + * associated endpoint and optionally synonyms. + */ + public List<String> getConfigurationTypes() { + return getStringPlusProperty(SERVICE_IMPORTED_CONFIGS); + } + + /** + * 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. + * + * This value of the intents is stored in the + * {@link RemoteConstants#SERVICE_INTENTS} service property. + * + * @return An unmodifiable list of expanded intents that are provided by + * this endpoint. + */ + public List<String> getIntents() { + return getStringPlusProperty(SERVICE_INTENTS); + } + + /** + * Reads a 'String+' property from the properties map, which may be of type + * String, String[] or Collection<String> and returns it as an + * unmodifiable List. + * + * @param key The property + * @return An unmodifiable list + */ + private List<String> getStringPlusProperty(String key) { + Object value = properties.get(key); + if (value == null) { + return Collections.EMPTY_LIST; + } + + if (value instanceof String) { + return Collections.singletonList((String) value); + } + + if (value instanceof String[]) { + String[] values = (String[]) value; + List<String> result = new ArrayList<String>(values.length); + for (String v : values) { + if (v != null) { + result.add(v); + } + } + return Collections.unmodifiableList(result); + } + + if (value instanceof Collection< ? >) { + Collection< ? > values = (Collection< ? >) value; + List<String> result = new ArrayList<String>(values.size()); + for (Iterator< ? > iter = values.iterator(); iter.hasNext();) { + Object v = iter.next(); + if (v instanceof String) { + result.add((String) v); + } + } + return Collections.unmodifiableList(result); + } + + return Collections.EMPTY_LIST; + } + + /** + * Return the framework UUID for the remote service, if present. + * + * The value of the remote framework uuid is stored in the + * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID} endpoint property. + * + * @return Remote Framework UUID, or {@code null} if this endpoint is + * not associated with an OSGi framework having a framework uuid. + */ + public String getFrameworkUUID() { + return frameworkUUID; + } + + /** + * Returns all endpoint properties. + * + * @return An unmodifiable map referring to the properties of this Endpoint + * Description. + */ + public Map<String, Object> getProperties() { + return properties; + } + + /** + * Answers if this Endpoint Description refers to the same service instance + * as the given Endpoint Description. + * + * Two Endpoint Descriptions point to the same service if they have the same + * id or their framework UUIDs and remote service ids are equal. + * + * @param other The Endpoint Description to look at + * @return True if this endpoint description points to the same service as + * the other + */ + public boolean isSameService(EndpointDescription other) { + if (this.equals(other)) { + return true; + } + + if (this.getFrameworkUUID() == null) { + return false; + } + + return (this.getServiceId() == other.getServiceId()) + && this.getFrameworkUUID().equals( + other.getFrameworkUUID()); + } + + /** + * Returns a hash code value for the object. + * + * @return An integer which is a hash code value for this object. + */ + public int hashCode() { + return getId().hashCode(); + } + + /** + * Compares this {@code EndpointDescription} object to another object. + * + * <p> + * An Endpoint Description is considered to be <b>equal to</b> another + * Endpoint Description if their ids are equal. + * + * @param other The {@code EndpointDescription} object to be compared. + * @return {@code true} if {@code object} is a + * {@code EndpointDescription} and is equal to this object; + * {@code false} otherwise. + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof EndpointDescription)) { + return false; + } + return getId().equals( + ((EndpointDescription) other).getId()); + } + + /** + * Tests the properties of this {@code EndpointDescription} against + * the given filter using a case insensitive match. + * + * @param filter The filter to test. + * @return {@code true} If the properties of this + * {@code EndpointDescription} match the filter, + * {@code false} otherwise. + * @throws IllegalArgumentException If {@code filter} contains an + * invalid filter string that cannot be parsed. + */ + public boolean matches(String filter) { + Filter f; + try { + f = FrameworkUtil.createFilter(filter); + } + catch (InvalidSyntaxException e) { + IllegalArgumentException iae = new IllegalArgumentException(e + .getMessage()); + iae.initCause(e); + throw iae; + } + Dictionary<String, Object> d = new UnmodifiableDictionary<String, Object>( + properties); + /* + * we can use matchCase here since properties already supports case + * insensitive key lookup. + */ + return f.matchCase(d); + } + + /** + * Returns the string representation of this EndpointDescription. + * + * @return String form of this EndpointDescription. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append('{'); + Iterator<Map.Entry<String, Object>> iter = properties.entrySet() + .iterator(); + boolean comma = false; + while (iter.hasNext()) { + Map.Entry<String, Object> entry = iter.next(); + if (comma) { + sb.append(", "); + } + else { + comma = true; + } + sb.append(entry.getKey()); + sb.append('='); + Object value = entry.getValue(); + if (value != null) { + Class< ? > valueType = value.getClass(); + if (Object[].class.isAssignableFrom(valueType)) { + append(sb, (Object[]) value); + continue; + } + } + sb.append(value); + } + sb.append('}'); + return sb.toString(); + } + + /** + * Append the specified Object array to the specified StringBuffer. + * + * @param sb Receiving StringBuffer. + * @param value Object array to append to the specified StringBuffer. + */ + private static void append(StringBuffer sb, Object[] value) { + sb.append('['); + boolean comma = false; + final int length = value.length; + for (int i = 0; i < length; i++) { + if (comma) { + sb.append(", "); + } + else { + comma = true; + } + sb.append(String.valueOf(value[i])); + } + sb.append(']'); + } + + /** + * Unmodifiable Dictionary wrapper for a Map. This class is also used by + * EndpointPermission. + */ + static final class UnmodifiableDictionary<K, V> extends Dictionary<K, V> { + private final Map<K, V> wrapped; + + UnmodifiableDictionary(Map<K, V> wrapped) { + this.wrapped = wrapped; + } + + public Enumeration<V> elements() { + return Collections.enumeration(wrapped.values()); + } + + public V get(Object key) { + return wrapped.get(key); + } + + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + public Enumeration<K> keys() { + return Collections.enumeration(wrapped.keySet()); + } + + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + public int size() { + return wrapped.size(); + } + + public String toString() { + return wrapped.toString(); + } + } +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointListener.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointListener.java new file mode 100644 index 000000000..35dc904b8 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointListener.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +/** + * A white board service that represents a listener for endpoints. + * + * An Endpoint Listener represents a participant in the distributed model that + * is interested in Endpoint Descriptions. + * + * This white board service can be used in many different scenarios. However, + * the primary use case is to allow a remote manager to be informed of Endpoint + * Descriptions available in the network and inform the network about available + * Endpoint Descriptions. + * + * Both the network bundle and the manager bundle register an Endpoint Listener + * service. The manager informs the network bundle about Endpoints that it + * creates. The network bundles then uses a protocol like SLP to announce these + * local end-points to the network. + * + * If the network bundle discovers a new Endpoint through its discovery + * protocol, then it sends an Endpoint Description to all the Endpoint 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. + * + * This filter model is quite flexible. For example, a discovery bundle is only + * interested in locally originating Endpoint Descriptions. The following filter + * ensure that it only sees local endpoints. + * + * <pre> + * (org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72) + * </pre> + * + * In the same vein, a manager that is only interested in remote Endpoint + * Descriptions can use a filter like: + * + * <pre> + * (!(org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72)) + * </pre> + * + * Where in both cases, the given UUID is the UUID of the local framework that + * can be found in the Framework properties. + * + * The Endpoint Listener's scope maps very well to the service hooks. A manager + * can just register all filters found from the Listener Hook as its scope. This + * will automatically provide it with all known endpoints that match the given + * scope, without having to inspect the filter string. + * + * In general, when an Endpoint Description is discovered, it should be + * dispatched to all registered Endpoint Listener services. If a new Endpoint + * Listener is registered, it should be informed about all currently known + * Endpoints that match its scope. If a getter of the Endpoint Listener service + * is unregistered, then all its registered Endpoint Description objects must be + * removed. + * + * The Endpoint Listener models a <i>best effort</i> approach. Participating + * bundles should do their utmost to keep the listeners up to date, but + * implementers should realize that many endpoints come through unreliable + * discovery processes. + * + * @ThreadSafe + * @version $Id: 8feaa377aecc7db74348c2c1b26ce7cd9fe64fea $ + */ +public interface EndpointListener { + /** + * Specifies the interest of this listener with filters. This listener is + * only interested in Endpoint Descriptions where its properties match the + * given filter. The type of this property must be {@code String+}. + */ + String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope"; + + /** + * Register an endpoint with this listener. + * + * If the endpoint matches one of the filters registered with the + * {@link #ENDPOINT_LISTENER_SCOPE} service property then this filter should + * be given as the {@code matchedFilter} parameter. + * + * When this service is first registered or it is modified, it should + * receive all known endpoints matching the filter. + * + * @param endpoint The Endpoint Description to be published + * @param matchedFilter The filter from the {@link #ENDPOINT_LISTENER_SCOPE} + * that matched the endpoint, must not be {@code null}. + */ + void endpointAdded(EndpointDescription endpoint, String matchedFilter); + + /** + * Remove the registration of an endpoint. + * + * If an endpoint that was registered with the + * {@link #endpointAdded(EndpointDescription, String)} method is no longer + * available then this method should be called. This will remove the + * endpoint from the listener. + * + * It is not necessary to remove endpoints when the service is unregistered + * or modified in such a way that not all endpoints match the interest + * filter anymore. + * + * @param endpoint The Endpoint Description that is no longer valid. + * @param matchedFilter The filter from the {@link #ENDPOINT_LISTENER_SCOPE} + * that matched the endpoint, must not be {@code null}. + */ + void endpointRemoved(EndpointDescription endpoint, String matchedFilter); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointPermission.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointPermission.java new file mode 100644 index 000000000..5ddaf67ce --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/EndpointPermission.java @@ -0,0 +1,692 @@ +/* + * Copyright (c) OSGi Alliance (2000, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import static org.osgi.service.remoteserviceadmin.RemoteConstants.*; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; + +/** + * A bundle's authority to export, import or read an Endpoint. + * <ul> + * <li>The {@code export} action allows a bundle to export a service as an + * Endpoint.</li> + * <li>The {@code import} action allows a bundle to import a service from + * an Endpoint.</li> + * <li>The {@code read} action allows a bundle to read references to an + * Endpoint.</li> + * </ul> + * Permission to read an Endpoint is required in order to detect events + * regarding an Endpoint. Untrusted bundles should not be able to detect the + * presence of certain Endpoints unless they have the appropriate + * {@code EndpointPermission} to read the specific service. + * + * @ThreadSafe + * @version $Id: a7565f494a3d68772eaca2707221f7c8eca88e20 $ + */ + +public final class EndpointPermission extends Permission { + static final long serialVersionUID = -7662148639076511574L; + /** + * The action string {@code read}. + */ + public final static String READ = "read"; + /** + * The action string {@code import}. The {@code import} action + * implies the {@code read} action. + */ + public final static String IMPORT = "import"; + /** + * The action string {@code export}. The {@code export} action + * implies the {@code read} action. + */ + public final static String EXPORT = "export"; + + private final static int ACTION_READ = 0x00000001; + private final static int ACTION_IMPORT = 0x00000002; + private final static int ACTION_EXPORT = 0x00000004; + private final static int ACTION_ALL = ACTION_EXPORT + | ACTION_IMPORT + | ACTION_READ; + 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 endpoint used by this EndpointPermission. Must be {@code null} + * if not constructed with a endpoint. + */ + transient final EndpointDescription endpoint; + + /** + * This dictionary holds the properties of the permission, used to match a + * filter in implies. + */ + private transient final Dictionary<String, Object> properties; + + /** + * If this EndpointPermission was not constructed with an + * EndpointDescription, this holds a Filter matching object used to evaluate + * the filter in implies or {@code null} for wildcard. + */ + transient Filter filter; + + /** + * Create a new EndpointPermission with the specified filter. + * + * <p> + * The filter will be evaluated against the endpoint properties of a + * requested EndpointPermission. + * + * <p> + * There are three possible actions: {@code read}, {@code import} + * and {@code export}. The {@code read} action allows the owner of + * this permission to see the presence of distributed services. The + * {@code import} action allows the owner of this permission to import + * an endpoint. The {@code export} action allows the owner of this + * permission to export a service. + * + * @param filterString The filter string or "*" to match all + * endpoints. + * @param actions The actions {@code read}, {@code import}, or + * {@code export}. + * @throws IllegalArgumentException If the filter has an invalid syntax or + * the actions are not valid. + */ + public EndpointPermission(String filterString, String actions) { + this(filterString, parseActions(actions)); + } + + /** + * Creates a new requested {@code EndpointPermission} object to be used + * by code that must perform {@code checkPermission}. + * {@code EndpointPermission} objects created with this constructor + * cannot be added to an {@code EndpointPermission} permission + * collection. + * + * @param endpoint The requested endpoint. + * @param localFrameworkUUID The UUID of the local framework. This is used + * to support matching the + * {@link RemoteConstants#ENDPOINT_FRAMEWORK_UUID + * endpoint.framework.uuid} endpoint property to the + * {@code <<LOCAL>>} value in the filter expression. + * @param actions The actions {@code read}, {@code import}, or + * {@code export}. + * @throws IllegalArgumentException If the endpoint is {@code null} or + * the actions are not valid. + */ + public EndpointPermission(EndpointDescription endpoint, + String localFrameworkUUID, String actions) { + super(createName(endpoint)); + setTransients(null, parseActions(actions)); + Map<String, Object> props; + if ((localFrameworkUUID != null) + && localFrameworkUUID.equals(endpoint.getFrameworkUUID())) { + props = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER); + props.putAll(endpoint.getProperties()); + props.put(ENDPOINT_FRAMEWORK_UUID, new String[] { + endpoint.getFrameworkUUID(), "<<LOCAL>>"}); + } + else { + props = endpoint.getProperties(); + } + this.endpoint = endpoint; + this.properties = new EndpointDescription.UnmodifiableDictionary<String, Object>( + props); + } + + /** + * Create a permission name from a EndpointDescription. + * + * @param endpoint EndpointDescription to use to create permission name. + * @return permission name. + */ + private static String createName(EndpointDescription endpoint) { + if (endpoint == null) { + throw new IllegalArgumentException("invalid endpoint: null"); + } + StringBuffer sb = new StringBuffer("(" + ENDPOINT_ID + "="); + sb.append(endpoint.getId()); + 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.properties = 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; + } + + /** + * 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 >= 5 && (a[i - 5] == 'i' || a[i - 5] == 'I') + && (a[i - 4] == 'm' || a[i - 4] == 'M') + && (a[i - 3] == 'p' || a[i - 3] == 'P') + && (a[i - 2] == 'o' || a[i - 2] == 'O') + && (a[i - 1] == 'r' || a[i - 1] == 'R') + && (a[i] == 't' || a[i] == 'T')) { + matchlen = 6; + mask |= ACTION_IMPORT | ACTION_READ; + + } + else + if (i >= 5 && (a[i - 5] == 'e' || a[i - 5] == 'E') + && (a[i - 4] == 'x' || a[i - 4] == 'X') + && (a[i - 3] == 'p' || a[i - 3] == 'P') + && (a[i - 2] == 'o' || a[i - 2] == 'O') + && (a[i - 1] == 'r' || a[i - 1] == 'R') + && (a[i] == 't' || a[i] == 'T')) { + matchlen = 6; + mask |= ACTION_EXPORT | ACTION_READ; + + } + else { + if (i >= 3 && (a[i - 3] == 'r' || a[i - 3] == 'R') + && (a[i - 2] == 'e' || a[i - 2] == 'E') + && (a[i - 1] == 'a' || a[i - 1] == 'A') + && (a[i] == 'd' || a[i] == 'D')) { + matchlen = 4; + mask |= ACTION_READ; + + } + else { + // parse error + throw new IllegalArgumentException( + "invalid permission: " + actions); + } + } + + // make sure we didn't just match the tail of a word + // like "ackbarfread". 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. + * @throws IllegalArgumentException If the filter syntax is invalid. + */ + private static Filter parseFilter(String filterString) { + if (filterString == null) { + throw new IllegalArgumentException("invalid filter: null"); + } + filterString = filterString.trim(); + if (filterString.equals("*")) { + return null; // wildcard + } + try { + return FrameworkUtil.createFilter(filterString); + } + catch (InvalidSyntaxException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "invalid filter"); + iae.initCause(e); + throw iae; + } + } + + /** + * Determines if a {@code EndpointPermission} object "implies" the + * specified permission. + * + * @param p The target permission to check. + * @return {@code true} if the specified permission is implied by this + * object; {@code false} 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} if the specified permission is implied by this + * object; {@code false} 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; + } + /* if we have no filter */ + Filter f = filter; + if (f == null) { + // it's "*" + return true; + } + return f.matchCase(requested.getProperties()); + } + + /** + * Returns the canonical string representation of the actions. Always + * returns present actions in the following canonical order: + * {@code read}, {@code import}, {@code export}. + * + * @return The canonical string representation of the actions. + */ + public String getActions() { + String result = actions; + if (result == null) { + StringBuffer sb = new StringBuffer(); + boolean comma = false; + + int mask = action_mask; + if ((mask & ACTION_READ) == ACTION_READ) { + sb.append(READ); + comma = true; + } + + if ((mask & ACTION_IMPORT) == ACTION_IMPORT) { + if (comma) + sb.append(','); + sb.append(IMPORT); + } + + if ((mask & ACTION_EXPORT) == ACTION_EXPORT) { + if (comma) + sb.append(','); + sb.append(EXPORT); + } + + actions = result = sb.toString(); + } + + return result; + } + + /** + * Returns a new {@code PermissionCollection} object for storing + * {@code EndpointPermission} objects. + * + * @return A new {@code PermissionCollection} object suitable for storing + * {@code EndpointPermission} objects. + */ + public PermissionCollection newPermissionCollection() { + return new EndpointPermissionCollection(); + } + + /** + * Determines the equality of two EndpointPermission objects. + * + * Checks that specified object has the same name, actions and endpoint as + * this {@code EndpointPermission}. + * + * @param obj The object to test for equality. + * @return true If obj is a {@code EndpointPermission}, and has the + * same name, actions and endpoint as this + * {@code EndpointPermission} object; {@code false} + * otherwise. + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof EndpointPermission)) { + return false; + } + + EndpointPermission ep = (EndpointPermission) obj; + + return (action_mask == ep.action_mask) + && getName().equals(ep.getName()) + && ((endpoint == ep.endpoint) || ((endpoint != null) + && (ep.endpoint != null) && endpoint + .equals(ep.endpoint))); + } + + /** + * Returns the hash code value for this object. + * + * @return Hash code value for this object. + */ + public int hashCode() { + int h = 31 * 17 + getName().hashCode(); + h = 31 * h + getActions().hashCode(); + if (endpoint != null) { + h = 31 * h + endpoint.hashCode(); + } + return h; + } + + /** + * WriteObject is called to save the state of this permission to a stream. + * The actions are serialized, and the superclass takes care of the name. + */ + private synchronized void writeObject(java.io.ObjectOutputStream s) + throws IOException { + if (endpoint != null) { + throw new NotSerializableException("cannot serialize"); + } + // Write out the actions. The superclass takes care of the name + // call getActions to make sure actions field is initialized + if (actions == null) { + getActions(); + } + s.defaultWriteObject(); + } + + /** + * readObject is called to restore the state of this permission from a + * stream. + */ + private synchronized void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + // Read in the action, then initialize the rest + s.defaultReadObject(); + setTransients(parseFilter(getName()), parseActions(actions)); + } + + /** + * Called by {@code <@link EndpointPermission#implies(Permission)>}. + * + * @return a dictionary of properties for this permission. + */ + private Dictionary<String, Object> getProperties() { + return properties; + } +} + +/** + * 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. + * + * @serial + * @GuardedBy this + */ + private Map<String, EndpointPermission> permissions; + + /** + * Boolean saying if "*" is in the collection. + * + * @serial + * @GuardedBy this + */ + private boolean all_allowed; + + /** + * Creates an empty EndpointPermissions object. + */ + public EndpointPermissionCollection() { + permissions = new HashMap<String, EndpointPermission>(); + all_allowed = false; + } + + /** + * Adds a permission to this permission collection. + * + * @param permission The Permission object to add. + * @throws IllegalArgumentException If the specified permission is not a + * EndpointPermission object. + * @throws SecurityException If this + * {@code EndpointPermissionCollection} object has been marked + * read-only. + */ + public void add(final Permission permission) { + if (!(permission instanceof EndpointPermission)) { + throw new IllegalArgumentException("invalid permission: " + + permission); + } + if (isReadOnly()) { + throw new SecurityException("attempt to add a Permission to a " + + "readonly PermissionCollection"); + } + + final EndpointPermission ep = (EndpointPermission) permission; + if (ep.endpoint != null) { + throw new IllegalArgumentException("cannot add to collection: " + + ep); + } + + final String name = ep.getName(); + synchronized (this) { + /* select the bucket for the permission */ + Map<String, EndpointPermission> pc = permissions; + final EndpointPermission existing = pc.get(name); + + if (existing != null) { + final int oldMask = existing.action_mask; + final int newMask = ep.action_mask; + if (oldMask != newMask) { + pc.put(name, + new EndpointPermission(name, oldMask | newMask)); + } + } + else { + pc.put(name, ep); + } + + if (!all_allowed) { + if (name.equals("*")) { + all_allowed = true; + } + } + } + } + + /** + * Determines if a set of permissions implies the permissions expressed in + * {@code permission}. + * + * @param permission The Permission object to compare. + * @return {@code true} if {@code permission} is a proper subset + * of a permission in the set; {@code false} 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<EndpointPermission> perms; + synchronized (this) { + final int desired = requested.action_mask; + /* short circuit if the "*" Permission was added */ + if (all_allowed) { + EndpointPermission ep = permissions.get("*"); + if (ep != null) { + effective |= ep.action_mask; + if ((effective & desired) == desired) { + return true; + } + } + } + perms = permissions.values(); + } + + /* iterate one by one over permissions */ + for (Iterator<EndpointPermission> iter = perms.iterator(); iter + .hasNext();) { + if (iter.next().implies0(requested, effective)) { + return true; + } + } + return false; + } + + /** + * Returns an enumeration of all the {@code EndpointPermission} objects + * in the container. + * + * @return Enumeration of all the EndpointPermission objects. + */ + public synchronized Enumeration<Permission> elements() { + List<Permission> all = new ArrayList<Permission>(permissions.values()); + return Collections.enumeration(all); + } + + /* serialization logic */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("permissions", HashMap.class), + new ObjectStreamField("all_allowed", Boolean.TYPE) }; + + private synchronized void writeObject(ObjectOutputStream out) + throws IOException { + ObjectOutputStream.PutField pfields = out.putFields(); + pfields.put("permissions", permissions); + pfields.put("all_allowed", all_allowed); + out.writeFields(); + } + + private synchronized void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField gfields = in.readFields(); + permissions = (HashMap<String, EndpointPermission>) gfields.get( + "permissions", new HashMap<String, EndpointPermission>()); + all_allowed = gfields.get("all_allowed", false); + } +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportReference.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportReference.java new file mode 100644 index 000000000..18b917b87 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportReference.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import org.osgi.framework.ServiceReference; + +/** + * An Export Reference associates a service with a local endpoint. + * + * The Export Reference can be used to reference an exported service. When the + * service is no longer exported, all methods must return {@code null}. + * + * @ThreadSafe + * @noimplement + * @version $Id: 282839133c0934b5b636098f96994a95674e0f24 $ + */ +public interface ExportReference { + /** + * Return the service being exported. + * + * @return The service being exported. Must be {@code null} when the + * service is no longer exported. + */ + ServiceReference getExportedService(); + + /** + * Return the Endpoint Description for the local endpoint. + * + * @return The Endpoint Description for the local endpoint. Must be + * {@code null} when the service is no longer exported. + */ + EndpointDescription getExportedEndpoint(); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportRegistration.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportRegistration.java new file mode 100644 index 000000000..012e38def --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ExportRegistration.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import java.util.Map; + +import org.osgi.framework.ServiceReference; + +/** + * An Export Registration associates a service to a local endpoint. + * + * The Export Registration can be used to delete the endpoint associated with an + * this registration. It is created with the + * {@link RemoteServiceAdmin#exportService(ServiceReference,Map)} method. + * + * When this Export Registration has been closed, all methods must return + * {@code null}. + * + * @ThreadSafe + * @noimplement + * @version $Id: d215ae704a94dd944d2311c81fa32a4647351713 $ + */ +public interface ExportRegistration { + /** + * Return the Export Reference for the exported service. + * + * @return The Export Reference for this registration. + * @throws IllegalStateException When this registration was not properly + * initialized. See {@link #getException()}. + */ + ExportReference getExportReference(); + + /** + * Delete the local endpoint and disconnect any remote distribution + * providers. After this method returns, all methods must return + * {@code null}. + * + * This method has no effect when this registration has already been closed + * or is being closed. + */ + void close(); + + /** + * Return the exception for any error during the export process. + * + * If the Remote Service Admin for some reasons is unable to properly + * initialize this registration, then it must return an exception from this + * method. If no error occurred, this method must return {@code null}. + * + * The error must be set before this Export Registration is returned. + * Asynchronously occurring errors must be reported to the log. + * + * @return The exception that occurred during the initialization of this + * registration or {@code null} if no exception occurred. + */ + Throwable getException(); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportReference.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportReference.java new file mode 100644 index 000000000..751b5edc9 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportReference.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import org.osgi.framework.ServiceReference; + +/** + * An Import Reference associates an active proxy service to a remote endpoint. + * + * The Import Reference can be used to reference an imported service. When the + * service is no longer imported, all methods must return {@code null}. + * + * @ThreadSafe + * @noimplement + * @version $Id: 943a38c0869cbe8cb9e04af62d4ac8169ad807ce $ + */ +public interface ImportReference { + /** + * Return the Service Reference for the proxy for the endpoint. + * + * @return The Service Reference to the proxy for the endpoint. Must be + * {@code null} when the service is no longer imported. + */ + ServiceReference getImportedService(); + + /** + * Return the Endpoint Description for the remote endpoint. + * + * @return The Endpoint Description for the remote endpoint. Must be + * {@code null} when the service is no longer imported. + */ + EndpointDescription getImportedEndpoint(); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportRegistration.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportRegistration.java new file mode 100644 index 000000000..e387c4a53 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/ImportRegistration.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +/** + * An Import Registration associates an active proxy service to a remote + * endpoint. + * + * The Import Registration can be used to delete the proxy associated with an + * endpoint. It is created with the + * {@link RemoteServiceAdmin#importService(EndpointDescription)} method. + * + * When this Import Registration has been closed, all methods must return + * {@code null}. + * + * @ThreadSafe + * @noimplement + * @version $Id: e8a83aa65f535311cde90a8e7e1667540f887440 $ + */ +public interface ImportRegistration { + /** + * Return the Import Reference for the imported service. + * + * @return The Import Reference for this registration. + * @throws IllegalStateException When this registration was not properly + * initialized. See {@link #getException()}. + */ + ImportReference getImportReference(); + + /** + * Close this Import Registration. This must close the connection to the + * endpoint and unregister the proxy. After this method returns, all other + * methods must return {@code null}. + * + * This method has no effect when this registration has already been closed + * or is being closed. + */ + void close(); + + /** + * Return the exception for any error during the import process. + * + * If the Remote Service Admin for some reasons is unable to properly + * initialize this registration, then it must return an exception from this + * method. If no error occurred, this method must return {@code null}. + * + * 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 initialization of this + * registration or {@code null} if no exception occurred. + */ + Throwable getException(); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteConstants.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteConstants.java new file mode 100644 index 000000000..b768c4f50 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteConstants.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import org.osgi.framework.Constants; + +/** + * Provide the definition of the constants used in the Remote Service Admin + * specification. + * + * @Immutable + * @version $Id: 03766065c89cec9d6b51caf9617d024fd18e4bc4 $ + */ +public class RemoteConstants { + private RemoteConstants() { + // non-instantiable + } + + /** + * Service property identifying the configuration types supported by a + * distribution provider. Registered by the distribution provider on one of + * its services to indicate the supported configuration types. + * + * <p> + * The value of this property must be of type {@code String}, + * {@code String[]}, or {@code Collection} of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String REMOTE_CONFIGS_SUPPORTED = Constants.REMOTE_CONFIGS_SUPPORTED; + + /** + * Service property identifying the intents supported by a distribution + * provider. Registered by the distribution provider on one of its services + * to indicate the vocabulary of implemented intents. + * + * <p> + * The value of this property must be of type {@code String}, + * {@code String[]}, or {@code Collection} of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String REMOTE_INTENTS_SUPPORTED = Constants.REMOTE_INTENTS_SUPPORTED; + + /** + * Service property identifying the configuration types that should be used + * to export the service. Each configuration type represents the + * configuration parameters for an endpoint. A distribution provider should + * create an endpoint for each configuration type that it supports. + * + * <p> + * This property may be supplied in the {@code properties} + * {@code Dictionary} object passed to the + * {@code BundleContext.registerService} method. The value of this property + * must be of type {@code String}, {@code String[]}, or {@code Collection} + * of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_EXPORTED_CONFIGS = Constants.SERVICE_EXPORTED_CONFIGS; + + /** + * Service property identifying the 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. + * + * <p> + * This property may be supplied in the {@code properties} + * {@code Dictionary} object passed to the + * {@code BundleContext.registerService} method. The value of this property + * must be of type {@code String}, {@code String[]}, or {@code Collection} + * of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_EXPORTED_INTENTS = Constants.SERVICE_EXPORTED_INTENTS; + + /** + * Service property identifying the extra intents that the distribution + * provider must implement to distribute the service. This property is + * merged with the {@code service.exported.intents} property before the + * distribution provider interprets the listed intents; it has therefore the + * same semantics but the property should be configurable so the + * administrator can choose the intents based on the topology. Bundles + * should therefore make this property configurable, for example through the + * Configuration Admin service. + * + * <p> + * This property may be supplied in the {@code properties} + * {@code Dictionary} object passed to the + * {@code BundleContext.registerService} method. The value of this property + * must be of type {@code String}, {@code String[]}, or {@code Collection} + * of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_EXPORTED_INTENTS_EXTRA = Constants.SERVICE_EXPORTED_INTENTS_EXTRA; + + /** + * Service property marking the service for export. It defines the + * interfaces under which this service can be exported. This list must be a + * subset of the types under which the service was registered. The single + * value of an asterisk ("*", \u002A) indicates all the + * interface types under which the service was registered excluding the + * non-interface types. It is strongly recommended to only export interface + * types and not concrete classes due to the complexity of creating proxies + * for some type of concrete classes. + * + * <p> + * This property may be supplied in the {@code properties} + * {@code Dictionary} object passed to the + * {@code BundleContext.registerService} method. The value of this property + * must be of type {@code String}, {@code String[]}, or {@code Collection} + * of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_EXPORTED_INTERFACES = Constants.SERVICE_EXPORTED_INTERFACES; + + /** + * Service property identifying the service as imported. This service + * property must be set by a distribution provider to any value when it + * registers the endpoint proxy as an imported service. A bundle can use + * this property to filter out imported services. + * + * <p> + * The value of this property may be of any type. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_IMPORTED = Constants.SERVICE_IMPORTED; + + /** + * Service property identifying the configuration types used to import the + * service. 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. If multiple configuration types are listed in this property, + * then they must be synonyms for exactly the same remote endpoint that is + * used to export this service. + * + * <p> + * The value of this property must be of type {@code String}, + * {@code String[]}, or {@code Collection} of {@code String}. + * + * @see "Remote Services Specification" + * @see #SERVICE_EXPORTED_CONFIGS + */ + public static final String SERVICE_IMPORTED_CONFIGS = Constants.SERVICE_IMPORTED_CONFIGS; + + /** + * Service property identifying the intents that this service implement. + * This property has a dual purpose: + * <ul> + * <li>A bundle can use this service property to notify the distribution + * provider that these intents are already implemented by the exported + * service object.</li> + * <li>A distribution provider must use this property to convey the combined + * intents of: The exporting service, and the intents that the exporting + * distribution provider adds, and the intents that the importing + * distribution provider adds.</li> + * </ul> + * + * To export a service, a distribution provider must expand any qualified + * intents. Both the exporting and importing distribution providers must + * recognize all intents before a service can be distributed. + * + * The value of this property must be of type {@code String}, + * {@code String[]}, or {@code Collection} of {@code String}. + * + * @see "Remote Services Specification" + */ + public static final String SERVICE_INTENTS = Constants.SERVICE_INTENTS; + + /* The above are from Ch. 13 Remote Services specification. */ + + /** + * Endpoint property identifying the id for this endpoint. This service + * property must always be set. + * + * <p> + * The value of this property must be of type {@code String}. + */ + public final static String ENDPOINT_ID = "endpoint.id"; + + /** + * Endpoint property identifying the service id of the exported service. Can + * be absent or 0 if the corresponding endpoint is not for an OSGi service. + * + * <p> + * The value of this property must be of type {@code Long}. + */ + public final static String ENDPOINT_SERVICE_ID = "endpoint.service.id"; + + /** + * Endpoint property identifying the universally unique id of the exporting + * framework. Can be absent if the corresponding endpoint is not for an OSGi + * service. + * + * <p> + * The value of this property must be of type {@code String}. + */ + public final static String ENDPOINT_FRAMEWORK_UUID = "endpoint.framework.uuid"; + + /** + * Prefix for an endpoint property identifying the interface Java package + * version for an interface. For example, the property + * {@code endpoint.package.version.com.acme=1.3} describes the version + * of the package for the {@code com.acme.Foo} interface. This endpoint + * property for an interface package does not have to be set. If not set, + * the value must be assumed to be 0. + * + * <p> + * Since endpoint properties are stored in a case insensitive map, case + * variants of a package name are folded together. + * + * <p> + * The value of properties having this prefix must be of type + * {@code String}. + */ + public final static String ENDPOINT_PACKAGE_VERSION_ = "endpoint.package.version."; +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java new file mode 100644 index 000000000..919469dc0 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdmin.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import java.util.Collection; +import java.util.Map; + +import org.osgi.framework.ServiceReference; + +/** + * A Remote Service Admin manages the import and export of services. + * + * A Distribution Provider can expose a control interface. This interface allows + * a Topology Manager to control the export and import of services. + * + * The API allows a Topology Manager to export a service, to import a service, + * and find out about the current imports and exports. + * + * @ThreadSafe + * @noimplement + * @version $Id: 7b2e01c324cc2d04a06f7d7addfe9ea1af1dc7ad $ + */ +public interface RemoteServiceAdmin { + + /** + * Export a service to a given Endpoint. The Remote Service Admin must + * create an Endpoint from the given description that can be used by other + * Distribution Providers to connect to this Remote Service Admin and use + * the exported service. + * + * The property keys of a Service Reference are case insensitive while the + * property keys of the specified {@code properties} map are case + * sensitive. A property key in the specified {@code properties} map + * must therefore override any case variant property key in the properties + * of the specified Service Reference. + * + * <p> + * If the caller does not have the appropriate + * {@code EndpointPermission[endpoint,EXPORT]} for an Endpoint, and the + * Java Runtime Environment supports permissions, then the + * {@link ExportRegistration#getException() getException} method on the + * corresponding returned {@link ExportRegistration} will return a + * {@code SecurityException}. + * + * @param reference 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 + * {@code null}, the Endpoint will be determined by the + * properties on the service. The properties are the same as given + * for an exported service. They override any properties in the + * specified Service Reference (case insensitive). The properties + * {@code objectClass} and {@code service.id}, in any case + * variant, are ignored. Those properties in the Service Reference + * cannot be overridden. This parameter can be {@code null}, + * this should be treated as an empty map. + * @return A {@code Collection} of {@link ExportRegistration}s for the + * specified Service Reference and properties. Multiple Export + * Registrations may be returned because a single service can be + * exported to multiple Endpoints depending on the available + * configuration type properties. The result is never + * {@code null} but may be empty if this Remove Service Admin + * does not recognize any of the configuration types. + * @throws IllegalArgumentException If any of the properties has a value + * that is not syntactically correct or if the service properties + * and the overlaid properties do not contain a + * {@link RemoteConstants#SERVICE_EXPORTED_INTERFACES} entry. + * @throws UnsupportedOperationException If any of the intents expressed + * through the properties is not supported by the distribution + * provider. + */ + Collection<ExportRegistration> exportService(ServiceReference reference, + Map<String, ? > properties); + + /** + * Import a service from an Endpoint. The Remote Service Admin must use the + * given Endpoint to create a proxy. This method can return + * {@code null} if the service could not be imported. + * + * @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} if the Endpoint could + * not be imported. + * @throws SecurityException If the caller does not have the appropriate + * {@code EndpointPermission[endpoint,IMPORT]} for the + * Endpoint, and the Java Runtime Environment supports permissions. + */ + ImportRegistration importService(EndpointDescription endpoint); + + /** + * Return the currently active Export References. + * + * <p> + * If the caller does not have the appropriate + * {@code EndpointPermission[endpoint,READ]} for an Endpoint, and the + * Java Runtime Environment supports permissions, then returned collection + * will not contain a reference to the exported Endpoint. + * + * @return A {@code Collection} of {@link ExportReference}s that are + * currently active. + */ + Collection<ExportReference> getExportedServices(); + + /** + * Return the currently active Import References. + * + * <p> + * If the caller does not have the appropriate + * {@code EndpointPermission[endpoint,READ]} for an Endpoint, and the + * Java Runtime Environment supports permissions, then returned collection + * will not contain a reference to the imported Endpoint. + * + * @return A {@code Collection} of {@link ImportReference}s that are + * currently active. + */ + Collection<ImportReference> getImportedEndpoints(); + +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java new file mode 100644 index 000000000..22c0b2fe8 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminEvent.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +import org.osgi.framework.Bundle; + +/** + * Provides the event information for a Remote Service Admin event. + * + * @Immutable + * @version $Id: 3b212dcbb7822a3312e20cd967f46bf49b3bb82a $ + */ +public class RemoteServiceAdminEvent { + /** + * Add an import registration. The Remote Service Admin will call this + * method when it imports a service. When this service is registered, the + * Remote Service Admin must notify the listener of all existing Import + * Registrations. + * + */ + public static final int IMPORT_REGISTRATION = 1; + + /** + * Add an export registration. The Remote Service Admin will call this + * method when it exports a service. When this service is registered, the + * Remote Service Admin must notify the listener of all existing Export + * Registrations. + */ + public static final int EXPORT_REGISTRATION = 2; + + /** + * Remove an export registration. The Remote Service Admin will call this + * method when it removes the export of a service. + * + */ + public static final int EXPORT_UNREGISTRATION = 3; + + /** + * Remove an import registration. The Remote Service Admin will call this + * method when it removes the import of a service. + * + */ + public static final int IMPORT_UNREGISTRATION = 4; + + /** + * A fatal importing error occurred. The Import Registration has been + * closed. + */ + public static final int IMPORT_ERROR = 5; + + /** + * A fatal exporting error occurred. The Export Registration has been + * closed. + */ + public static final int EXPORT_ERROR = 6; + + /** + * A problematic situation occurred, the export is still active. + */ + public static final int EXPORT_WARNING = 7; + /** + * A problematic situation occurred, the import is still active. + */ + public static final int IMPORT_WARNING = 8; + + private final ImportReference importReference; + private final ExportReference exportReference; + private final Throwable exception; + private final int type; + private final Bundle source; + + /** + * Private constructor. + * + * @param type The event type + * @param source The source bundle, must not be {@code null}. + * @param importReference The importReference, can be {@code null}. + * @param exportReference The exportReference, can be {@code null}. + * @param exception Any exceptions encountered, can be {@code null} + */ + private RemoteServiceAdminEvent(int type, Bundle source, + ImportReference importReference, ExportReference exportReference, + Throwable exception) { + if (source == null) { + throw new NullPointerException("source must not be null"); + } + this.type = type; + this.source = source; + this.importReference = importReference; + this.exportReference = exportReference; + this.exception = exception; + } + + /** + * Create a Remote Service Admin Event for an export notification. + * + * @param type The event type. + * @param source The source bundle, must not be {@code null}. + * @param exportReference The exportReference, can not be {@code null}. + * @param exception Any exceptions encountered, can be {@code null}. + */ + public RemoteServiceAdminEvent(int type, Bundle source, + ExportReference exportReference, Throwable exception) { + this(type, source, null, exportReference, exception); + } + + /** + * Create a Remote Service Admin Event for an import notification. + * + * @param type The event type. + * @param source The source bundle, must not be {@code null}. + * @param importReference The importReference, can not be {@code null}. + * @param exception Any exceptions encountered, can be {@code null}. + */ + public RemoteServiceAdminEvent(int type, Bundle source, + ImportReference importReference, Throwable exception) { + this(type, source, importReference, null, exception); + } + + /** + * Return the Import Reference for this event. + * + * @return The Import Reference or {@code null}. + */ + public ImportReference getImportReference() { + return importReference; + } + + /** + * Return the Export Reference for this event. + * + * @return The Export Reference or {@code null}. + */ + public ExportReference getExportReference() { + return exportReference; + } + + /** + * Return the exception for this event. + * + * @return The exception or {@code null}. + */ + public Throwable getException() { + return exception; + } + + /** + * Return the type of this event. + * + * @return The type of this event. + */ + public int getType() { + return type; + } + + /** + * Return the bundle source of this event. + * + * @return The bundle source of this event. + */ + public Bundle getSource() { + return source; + } +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java new file mode 100644 index 000000000..7691b727e --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/RemoteServiceAdminListener.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.service.remoteserviceadmin; + +/** + * A {@link RemoteServiceAdminEvent} listener is notified synchronously of any + * export or import registrations and unregistrations. + * + * <p> + * If the Java Runtime Environment supports permissions, then filtering is done. + * {@code RemoteServiceAdminEvent} objects are only delivered to the + * listener if the bundle which defines the listener object's class has the + * appropriate {@code EndpointPermission[endpoint,READ]} for the endpoint + * referenced by the event. + * + * + * @see RemoteServiceAdminEvent + * @ThreadSafe + * @version $Id: 824fb19364804de5ecd00695547134044847600d $ + */ + +public interface RemoteServiceAdminListener { + /** + * Receive notification of any export or import registrations and + * unregistrations as well as errors and warnings. + * + * @param event The {@link RemoteServiceAdminEvent} object. + */ + void remoteAdminEvent(RemoteServiceAdminEvent event); +} diff --git a/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/package-info.java b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/package-info.java new file mode 100644 index 000000000..8687a9a55 --- /dev/null +++ b/osgi/bundles/org.eclipse.osgi.services.remoteserviceadmin/src/org/osgi/service/remoteserviceadmin/package-info.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) OSGi Alliance (2010). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Remote Service Admin Package Version 1.0. + * + * <p> + * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. This package has two types of + * users: the consumers that use the API in this package and the providers that + * implement the API in this package. + * + * <p> + * Example import for consumers using the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.remoteserviceadmin; version="[1.0,2.0)"} + * <p> + * Example import for providers implementing the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.remoteserviceadmin; version="[1.0,1.1)"} + * + * @version $Id: 2df93207eb7823d0b80b99275312519a7cb635e8 $ + */ + +package org.osgi.service.remoteserviceadmin; |