summaryrefslogtreecommitdiffstats
path: root/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java')
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java337
1 files changed, 212 insertions, 125 deletions
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
index 23a91b89ef..9fda882738 100644
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/EndpointDescription.java
@@ -15,14 +15,21 @@
*/
package org.apache.tuscany.sca.osgi.remoteserviceadmin;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
+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;
@@ -36,28 +43,20 @@ import org.osgi.framework.Version;
*
* An Endpoint Description reflects the perspective of an importer. That is, the
* property keys have been chosen to match filters that are created by client
- * bundles that need a service.
- *
- * TODO Automatically calculate versions of interface packages?
- *
- * TODO Constructor that takes a class?
- *
- * TODO Skipping of service.exported.* properties?
- *
- * TODO qualified intents?
- *
+ * bundles that need a service. Therefore the map must not contain any
+ * service.exported.* property and must contain the service.imported.* ones.
*
+ * The service.intents property contains the intents provided by the service
+ * itself combined with the intents added by the exporting distribution
+ * provider. Qualified intents appear expanded on this property.
*
* @Immutable
* @version $Revision$
*/
public class EndpointDescription {
- private final Map /* <String,Object> */properties = new HashMap/*
- * <String ,
- * Object >
- */();
- private final List /* String */interfaces;
+ private final Map<String, Object> properties;
+ private final List<String> interfaces;
private final long remoteServiceId;
private final String remoteFrameworkUUID;
private final String remoteUri;
@@ -65,38 +64,58 @@ public class EndpointDescription {
/**
* Create an Endpoint Description based on a Map.
*
- * @param properties
+ * @param properties The map to create the Endpoint Description from.
* @throws IllegalArgumentException When the properties are not proper for
* an Endpoint Description
*/
- public EndpointDescription(Map/* <String,Object> */properties) throws IllegalArgumentException {
- this.properties.putAll(properties);
-
- interfaces = verifyInterfacesProperty();
- remoteServiceId = verifyLongProperty(RemoteConstants.SERVICE_REMOTE_ID);
- remoteFrameworkUUID = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
- remoteUri = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_URI);
+ public EndpointDescription(Map<String, Object> properties) {
+ this(properties, null);
}
/**
* Create an Endpoint Description based on a reference and optionally a map
- * of additional properties.
+ * of additional properties. The properties on the original service take
+ * precedence over the ones in the map.
*
- * @param ref A service reference that can be exported
+ * @param reference A service reference that can be exported
* @param properties Additional properties to add. Can be <code>null</code>.
- * @throws IllegalArgumentException
+ * @throws IllegalArgumentException When the properties are not proper for
+ * an Endpoint Description
*/
- public EndpointDescription(ServiceReference ref, Map /* <String,Object> */properties)
- throws IllegalArgumentException {
- if (properties != null) {
- this.properties.putAll(properties);
+ public EndpointDescription(ServiceReference reference, Map<String, Object> properties) {
+ this(properties, reference);
+ if (reference == null) {
+ throw new NullPointerException("reference must not be null");
+ }
+ }
+
+ private EndpointDescription(Map<String, Object> map, ServiceReference reference) {
+ Map<String, Object> props = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
+
+ if (map != null) {
+ try {
+ props.putAll(map);
+ } catch (ClassCastException e) {
+ IllegalArgumentException iae = new IllegalArgumentException("non-String key in properties");
+ iae.initCause(e);
+ throw iae;
+ }
+ if (props.size() < map.size()) {
+ throw new IllegalArgumentException("duplicate keys with different cases in properties");
+ }
}
- String[] keys = ref.getPropertyKeys();
- for (int i = 0; i > keys.length; i++)
- properties.put(keys[i], ref.getProperty(keys[i]));
+ if (reference != null) {
+ for (String key : reference.getPropertyKeys()) {
+ if (!props.containsKey(key)) {
+ props.put(key, reference.getProperty(key));
+ }
+ }
+ }
+ properties = Collections.unmodifiableMap(props);
+ /* properties must be initialized before calling the following methods */
interfaces = verifyInterfacesProperty();
remoteServiceId = verifyLongProperty(RemoteConstants.SERVICE_REMOTE_ID);
remoteFrameworkUUID = verifyStringProperty(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
@@ -104,64 +123,32 @@ public class EndpointDescription {
}
/**
- * Create an Endpoint Description based on the URI, the remote service ID
- * and the interface names, and optionally service properties.
- *
- * @param uri The URI of the remote service.
- * @param interfaceNames The names of the interfaces of the service to
- * consider.
- * @param remoteServiceId the remote service ID.
- * @param properties Optionally service properties.
- */
- public EndpointDescription(String uri, String[] interfaceNames, int remoteServiceId, Map properties) {
- if (uri == null) {
- throw new IllegalArgumentException("URI must not be null");
- }
- if (interfaceNames == null) {
- throw new IllegalArgumentException("Interfaces must not be null");
- }
- this.remoteUri = uri;
- this.interfaces = Arrays.asList(interfaceNames);
- this.remoteServiceId = remoteServiceId;
- this.remoteFrameworkUUID = (String)properties.get(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID);
- if (properties != null) {
- this.properties.putAll(properties);
- }
- if (interfaceNames != null) {
- this.properties.put(Constants.OBJECTCLASS, interfaceNames);
- }
- this.properties.put(RemoteConstants.SERVICE_REMOTE_URI, uri);
- if (remoteServiceId <= 0) {
- this.properties.put(RemoteConstants.SERVICE_REMOTE_ID, new Integer(remoteServiceId));
- }
- }
-
- /**
* Verify and obtain the interface list from the properties.
*
* @return A list with the interface names.
- * @throws IllegalArgumentException when
+ * @throws IllegalArgumentException When the properties do not contain the
+ * right values for and interface list.
+ *
*/
- private List /* <String> */verifyInterfacesProperty() {
- List l = null;
-
- Object objectClass = properties.get(Constants.OBJECTCLASS);
- if (objectClass == null)
- l = Collections.EMPTY_LIST;
- else if (!(objectClass instanceof String[]))
+ private List<String> verifyInterfacesProperty() {
+ Object o = properties.get(Constants.OBJECTCLASS);
+ if (o == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (!(o 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);
- }
+ }
+ String[] objectClass = (String[])o;
+ for (String interf : objectClass) {
+ try {
+ getInterfaceVersion(interf);
+ } catch (IllegalArgumentException e) {
+ IllegalArgumentException iae = new IllegalArgumentException("Improper version for interface " + interf);
+ iae.initCause(e);
+ throw iae;
}
}
- return l;
+ return Collections.unmodifiableList(Arrays.asList(objectClass));
}
/**
@@ -178,7 +165,7 @@ public class EndpointDescription {
throw new IllegalArgumentException("Required property not set: " + propName);
}
if (!(r instanceof String)) {
- throw new IllegalArgumentException("Required property is not a string: " + propName);
+ throw new IllegalArgumentException("Required property is not a String: " + propName);
}
return (String)r;
}
@@ -193,7 +180,6 @@ public class EndpointDescription {
*/
private long verifyLongProperty(String propName) {
Object r = properties.get(propName);
- long result;
if (r == null) {
throw new IllegalArgumentException("Required property not set: " + propName);
}
@@ -201,11 +187,13 @@ public class EndpointDescription {
throw new IllegalArgumentException("Required property is not a string: " + propName);
}
try {
- result = Long.parseLong((String)r);
+ return Long.parseLong((String)r);
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Required property cannot be parsed as a long: " + propName);
+ IllegalArgumentException iae =
+ new IllegalArgumentException("Required property cannot be parsed as a long: " + propName);
+ iae.initCause(e);
+ throw iae;
}
- return result;
}
/**
@@ -225,7 +213,7 @@ public class EndpointDescription {
}
/**
- * Answer the list of interfaces implemented by the exported service.
+ * Provide 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.
@@ -236,43 +224,42 @@ public class EndpointDescription {
* @return The read only list of Java interface names accessible by this
* endpoint.
*/
- public List/* <String> */getInterfaces() {
+ public List<String> getInterfaces() {
return interfaces;
}
/**
- * Answer the version of the given interface.
+ * Provide 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:
+ * For example:
*
* <pre>
* endpoint.version.com.acme.Foo
* </pre>
*
+ * The value of this property is in String format and will be converted to a
+ * <code>Version</code> object by this method.
+ *
* @param name The name of the interface for which a version is requested
* @return The version of the given interface or <code>null</code> if the
* interface has no version in this Endpoint Description
*/
public Version getInterfaceVersion(String name) {
- String v = (String)properties.get("endpoint.version." + name);
- if (v == null) {
- return Version.emptyVersion;
- } else {
- return new Version(v);
- }
+ String version = (String)properties.get("endpoint.version." + name);
+ return Version.parseVersion(version);
}
/**
- * Returns the service id for the service exported through this
- * endpoint.
+ * 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.
+ * This is the service id under which the framework has registered the
+ * service. This field together with the Framework UUID is a globally unique
+ * id for a service.
*
- * @return Service id of a service or 0 if this Endpoint
- * Description does not relate to an OSGi service
+ * @return Service id of a service or 0 if this Endpoint Description does
+ * not relate to an OSGi service
*
*/
public long getRemoteServiceID() {
@@ -293,12 +280,11 @@ public class EndpointDescription {
* This value represents the
* {@link RemoteConstants#SERVICE_IMPORTED_CONFIGS}
*
- * @return The configuration type used for the associated endpoint and
- * optionally synonyms.
+ * @return An unmodifiable list of the configuration types used for the
+ * associated endpoint and optionally synonyms.
*/
- public List/* <String> */getConfigurationTypes() {
- // TODO
- return null;
+ public List<String> getConfigurationTypes() {
+ return getStringPlusProperty(RemoteConstants.SERVICE_IMPORTED_CONFIGS);
}
/**
@@ -311,21 +297,66 @@ public class EndpointDescription {
* The property the intents come from is
* {@link RemoteConstants#SERVICE_INTENTS}
*
- * @return A list of expanded intents that are provided by this endpoint.
+ * @return An unmodifiable list of expanded intents that are provided by
+ * this endpoint.
+ */
+ public List<String> getIntents() {
+ return getStringPlusProperty(RemoteConstants.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
+ * @throws Illegal
*/
- public List/* <String> */getIntents() {
- // TODO
- return null;
+ 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 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 != null) && (v instanceof String)) {
+ result.add((String)v);
+ }
+ }
+ return result;
+ }
+
+ return Collections.EMPTY_LIST;
}
/**
- * Return the framework UUID, if present.
+ * Return the framework UUID for the remote service, if present.
*
- * The property the intents come from is
+ * The property the framework UUID comes from is
* {@link RemoteConstants#SERVICE_REMOTE_FRAMEWORK_UUID}
*
- * @return Remote Framework UUID, or null if this endpoint is not associated with an OSGi service
+ * @return Remote Framework UUID, or null if this endpoint is not associated
+ * with an OSGi service
*/
public String getRemoteFrameworkUUID() {
return remoteFrameworkUUID;
@@ -337,9 +368,8 @@ public class EndpointDescription {
* @return An unmodifiable map referring to the properties of this Endpoint
* Description.
*/
- public Map/* <String, Object> */getProperties() {
- // TODO
- return Collections.unmodifiableMap(properties);
+ public Map<String, Object> getProperties() {
+ return properties;
}
/**
@@ -366,20 +396,77 @@ public class EndpointDescription {
/**
* Two endpoints are equal if their URIs are equal, the hash code is
* therefore derived from the URI.
+ *
+ * @return The hashcode of this endpoint.
*/
public int hashCode() {
- // TODO
return getRemoteURI().hashCode();
}
/**
* Two endpoints are equal if their URIs are equal.
+ *
+ * @return
*/
public boolean equals(Object other) {
- if (other instanceof EndpointDescription) {
- return getRemoteURI().equals(((EndpointDescription)other).getRemoteURI());
+ if (this == other) {
+ return true;
}
- return false;
+ if (!(other instanceof EndpointDescription)) {
+ return false;
+ }
+ return getRemoteURI().equals(((EndpointDescription)other).getRemoteURI());
+ }
+
+ /**
+ * TODO
+ *
+ * @param filter
+ * @return
+ * @throws InvalidSyntaxException
+ */
+ public boolean match(String filter) throws InvalidSyntaxException {
+ Filter f = FrameworkUtil.createFilter(filter);
+ Dictionary<String, Object> d = new UnmodifiableDictionary<String, Object>(properties);
+ return f.matchCase(d);
}
+ /**
+ * Unmodifiable wrapper for Dictionary.
+ */
+ private static class UnmodifiableDictionary<K, V> extends Dictionary<K, V> {
+ private final Map<? extends K, ? extends V> wrapped;
+
+ UnmodifiableDictionary(Map<? extends K, ? extends V> wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ public Enumeration<V> elements() {
+ return (Enumeration<V>)Collections.enumeration(wrapped.values());
+ }
+
+ public V get(Object key) {
+ return wrapped.get(key);
+ }
+
+ public boolean isEmpty() {
+ return wrapped.isEmpty();
+ }
+
+ public Enumeration<K> keys() {
+ return (Enumeration<K>)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();
+ }
+ }
}