summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Liebig2008-02-20 08:34:41 (EST)
committerStefan Liebig2008-02-20 08:34:41 (EST)
commita68ea99396b08933669bd4470b44d76552ed9ef8 (patch)
tree26a891b7698b6435767e147f1fedea24947ce94d
parent63f867b6d5773eab3a0827a9e6c399ed792b48b1 (diff)
downloadorg.eclipse.riena-a68ea99396b08933669bd4470b44d76552ed9ef8.zip
org.eclipse.riena-a68ea99396b08933669bd4470b44d76552ed9ef8.tar.gz
org.eclipse.riena-a68ea99396b08933669bd4470b44d76552ed9ef8.tar.bz2
Splitted implementation for ranked and filtered injection into separate classes based on a common abstraction
-rw-r--r--org.eclipse.riena.core/src/org/eclipse/riena/core/service/FilterInjector.java92
-rw-r--r--org.eclipse.riena.core/src/org/eclipse/riena/core/service/Injector.java280
-rw-r--r--org.eclipse.riena.core/src/org/eclipse/riena/core/service/RankingInjector.java117
-rw-r--r--org.eclipse.riena.core/src/org/eclipse/riena/core/service/ServiceId.java25
4 files changed, 371 insertions, 143 deletions
diff --git a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/FilterInjector.java b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/FilterInjector.java
new file mode 100644
index 0000000..aab7dbf
--- /dev/null
+++ b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/FilterInjector.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 compeople AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * compeople AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.riena.core.service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The specialized filter injector implementation.
+ */
+public class FilterInjector extends Injector {
+
+ private List<ServiceReference> trackedServiceRefs = null;
+
+ /**
+ * @param serviceId
+ * @param target
+ */
+ FilterInjector(ServiceId serviceId, Object target) {
+ super(serviceId, target);
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doStart()
+ */
+ @Override
+ protected void doStart() {
+ trackedServiceRefs = new ArrayList<ServiceReference>(1);
+ ServiceReference[] serviceRefs = getServiceReferences();
+
+ // register service listener early because its very likely that no
+ // service is registered between getServiceReferences and
+ // registerServiceListener()
+ registerServiceListener();
+ if (serviceRefs != null)
+ for (ServiceReference serviceRef : serviceRefs)
+ doBind(serviceRef);
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doStop()
+ */
+ @Override
+ protected void doStop() {
+ // copy list to array so that I iterate through array and still
+ // remove entries from List concurrently
+ ServiceReference[] serviceRefs;
+ synchronized (trackedServiceRefs) {
+ serviceRefs = trackedServiceRefs.toArray(new ServiceReference[trackedServiceRefs.size()]);
+ }
+ for (ServiceReference serviceRef : serviceRefs)
+ doUnbind(serviceRef);
+ trackedServiceRefs = null;
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doBind(org.osgi.framework.ServiceReference)
+ */
+ @Override
+ protected void doBind(ServiceReference serviceRef) {
+ synchronized (trackedServiceRefs) {
+ if (trackedServiceRefs.contains(serviceRef))
+ return;
+ invokeBindMethod(serviceRef);
+ trackedServiceRefs.add(serviceRef);
+ }
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doUnbind(org.osgi.framework.ServiceReference)
+ */
+ @Override
+ protected void doUnbind(ServiceReference serviceRef) {
+ synchronized (trackedServiceRefs) {
+ if (!trackedServiceRefs.contains(serviceRef))
+ return;
+ invokeUnbindMethod(serviceRef);
+ trackedServiceRefs.remove(serviceRef);
+ }
+ }
+
+}
diff --git a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/Injector.java b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/Injector.java
index 4f1fe06..7c915cb 100644
--- a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/Injector.java
+++ b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/Injector.java
@@ -15,7 +15,6 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import org.eclipse.core.runtime.Assert;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
@@ -24,9 +23,11 @@ import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
/**
- * The is the service injector. See {@link ServiceId} for explanation and usage.
+ * The is the abstract base class for the specialized service injectors. See
+ * {@link ServiceId} for explanation and usage. It provides common functionality
+ * for the ranking injector and the filtering injector.
*/
-public class Injector {
+public abstract class Injector {
/**
* Default ´bind´ method name.
@@ -39,16 +40,16 @@ public class Injector {
public static final String DEFAULT_UNBIND_METHOD_NAME = "unbind";
private ServiceId serviceId;
+ private BundleContext context = null;
+ private String filter;
private Object target;
private String bindMethodName = null;
+ private List<Method> bindMethodProspects = null;
private String unbindMethodName = null;
+ private List<Method> unbindMethodProspects = null;
private boolean started = false;
- private BundleContext context = null;
- private List<ServiceReference> trackedServiceRefs = null;
private ServiceListener serviceListener;
- private RuntimeException lastError;
-
/**
* Constructor for the <code>injectInto()</code> of <code>ServiceId</code>.
*
@@ -58,6 +59,11 @@ public class Injector {
Injector(ServiceId serviceId, Object target) {
this.serviceId = serviceId;
this.target = target;
+ StringBuilder bob = new StringBuilder().append("(").append(Constants.OBJECTCLASS).append("=").append(
+ serviceId.getServiceId()).append(")");
+ if (serviceId.getFilter() != null)
+ bob.append(serviceId.getFilter());
+ this.filter = bob.toString();
}
/**
@@ -70,47 +76,43 @@ public class Injector {
* @param context
* @return this injector
*/
- public Injector start(BundleContext context) {
+ public Injector andStart(BundleContext context) {
if (started)
throw new IllegalStateException("Injector already started!");
started = true;
- lastError = null;
this.context = context;
if (bindMethodName == null)
bindMethodName = DEFAULT_BIND_METHOD_NAME;
- assertMethod("Bind method", bindMethodName);
+ bindMethodProspects = collectMethods("Bind method", bindMethodName);
if (unbindMethodName == null)
unbindMethodName = DEFAULT_UNBIND_METHOD_NAME;
- assertMethod("Unbind method", unbindMethodName);
- serviceListener = new InjectorServiceListener();
- trackedServiceRefs = new ArrayList<ServiceReference>(1);
- start();
- if (lastError != null)
- throw lastError;
+ unbindMethodProspects = collectMethods("Unbind method", unbindMethodName);
+
+ doStart();
+ // for sure
+ registerServiceListener();
+
+ started = true;
return this;
}
+ protected abstract void doStart();
+
/**
* Stops tracking the specified service
*/
public void stop() {
if (!started)
return;
- context.removeServiceListener(serviceListener);
-
- // copy list to array so that I iterate through array and still
- // remove entries from List concurrently
- ServiceReference[] serviceRefs;
- synchronized (trackedServiceRefs) {
- serviceRefs = trackedServiceRefs.toArray(new ServiceReference[trackedServiceRefs.size()]);
- }
- for (ServiceReference serviceRef : serviceRefs)
- unbind(serviceRef);
- serviceListener = null;
- trackedServiceRefs = null;
+ unregisterServiceListener();
+ doStop();
+ bindMethodProspects = null;
+ unbindMethodProspects = null;
started = false;
}
+ protected abstract void doStop();
+
/**
* Specify the bind method. If not specified
* {@link #DEFAULT_BIND_METHOD_NAME} will be used.
@@ -143,128 +145,133 @@ public class Injector {
return this;
}
- public Throwable getLastError() {
- return lastError;
+ /**
+ * Registers the listener for service events. Can be called by subclasses
+ * within their {@link #doStart()} method for be timely closer to some other
+ * actions. However, if this is not done it will be called by this base
+ * class.
+ */
+ protected void registerServiceListener() {
+ if (serviceListener == null)
+ serviceListener = new InjectorServiceListener();
+ try {
+ context.addServiceListener(serviceListener, filter);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("The specified filter has syntax errors.", e);
+ }
+ }
+
+ /**
+ * Unregisters the listener for service events.
+ */
+ private void unregisterServiceListener() {
+ if (serviceListener == null)
+ return;
+ context.removeServiceListener(serviceListener);
+ serviceListener = null;
}
- private void assertMethod(String message, String methodName) {
+ private List<Method> collectMethods(String message, String methodName) {
+ List<Method> prospects = new ArrayList<Method>();
Method[] methods = target.getClass().getMethods();
for (Method method : methods)
- if (method.getName().equals(methodName))
- return;
+ if (method.getName().equals(methodName) && method.getParameterTypes().length == 1)
+ prospects.add(method);
+
+ if (prospects.size() != 0)
+ return prospects;
throw new IllegalArgumentException(message + " '" + methodName + "' does not exist in target class '"
+ target.getClass().getName());
}
- /**
- * Starts to track the specified OSGi Service
- */
- private void start() {
- try {
- ServiceReference[] serviceRefs = null;
- // try to find the service initially
- if (serviceId.usesRanking()) {
- ServiceReference serviceRef = context.getServiceReference(serviceId.getServiceId());
- if (serviceRef != null)
- serviceRefs = new ServiceReference[] { serviceRef };
- } else
- serviceRefs = context.getServiceReferences(serviceId.getServiceId(), serviceId.getFilter());
-
- // add an service listener for register or unregister
- // register the service listener before we go through the reference
- // list since its very more likely that no service is registered
- // between getServiceReferences and addServiceListener
- context.addServiceListener(serviceListener, serviceId.getFilter());
- // then go through the list of references
- if (serviceRefs != null)
- for (ServiceReference serviceRef : serviceRefs)
- bind(serviceRef);
- } catch (InvalidSyntaxException e) {
- e.printStackTrace();
- }
- started = true;
- }
-
protected void handleEvent(ServiceEvent event) {
switch (event.getType()) {
case ServiceEvent.REGISTERED:
- bind(event.getServiceReference());
+ doBind(event.getServiceReference());
break;
case ServiceEvent.UNREGISTERING:
- unbind(event.getServiceReference());
+ doUnbind(event.getServiceReference());
break;
}
}
- private void bind(ServiceReference serviceRef) {
- synchronized (trackedServiceRefs) {
- if (trackedServiceRefs.contains(serviceRef))
- return;
-
- Object service = context.getService(serviceRef);
- if (service != null) {
- Method method = findMatchingMethod(target.getClass(), bindMethodName, service.getClass());
- invoke(method, service);
- }
- trackedServiceRefs.add(serviceRef);
+ protected abstract void doBind(ServiceReference serviceRef);
+
+ protected abstract void doUnbind(ServiceReference serviceRef);
+
+ /**
+ * Get all service references for the service.
+ *
+ * @return array of service references
+ */
+ protected ServiceReference[] getServiceReferences() {
+ try {
+ return context.getServiceReferences(serviceId.getServiceId(), filter);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("The specified filter has syntax errors.", e);
}
}
- private void unbind(ServiceReference serviceRef) {
- synchronized (trackedServiceRefs) {
- if (!trackedServiceRefs.contains(serviceRef))
- return;
-
- Object service = context.getService(serviceRef);
- if (service != null) {
- Method method = findMatchingMethod(target.getClass(), unbindMethodName, service.getClass());
- invoke(method, service);
- }
- trackedServiceRefs.remove(serviceRef);
- context.ungetService(serviceRef);
- }
+ /**
+ * Find the matching bind method for the specified service reference.
+ *
+ * @param service
+ * @return the bind method
+ */
+ protected void invokeBindMethod(ServiceReference serviceRef) {
+ if (serviceRef == null)
+ return;
+ // increments service use count
+ Object service = context.getService(serviceRef);
+ if (service == null)
+ return;
+ invokeMethod(bindMethodProspects, service);
}
/**
- * @param method
+ * Find the matching unbind method for the specified service reference.
+ *
* @param service
+ * @return the unbind method
*/
- private void invoke(Method method, Object service) {
- if (method == null) {
- lastError = new IllegalArgumentException("Bind/Unbind method '" + method
- + "' does not exist in target class '" + target.getClass().getName());
- // TODO logging
+ protected void invokeUnbindMethod(ServiceReference serviceRef) {
+ if (serviceRef == null)
return;
- }
- try {
- method.invoke(target, service);
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
+ // need to get the service object, increments the use count
+ Object service = context.getService(serviceRef);
+ if (service == null)
+ return;
+ invokeMethod(unbindMethodProspects, service);
+ // decrement the use count from prior getService()
+ context.ungetService(serviceRef);
+ // decrement the use count from from prior bind
+ context.ungetService(serviceRef);
+ }
+
+ private void invokeMethod(List<Method> methods, Object service /*
+ * ServiceReference
+ * serviceRef
+ */) {
+ // if (serviceRef == null)
+ // return;
+ // Object service = context.getService(serviceRef);
+ // if (service == null)
+ // return;
+ Method method = findMatchingMethod(methods, service);
+ if (method == null)
+ return;
+ invoke(method, service);
}
- private static Method findMatchingMethod(Class<?> targetType, String methodName, Class<?> parameterType) {
- assert targetType != null;
- assert methodName != null;
- assert parameterType != null;
+ private static Method findMatchingMethod(List<Method> methods, Object service) {
+ assert methods != null;
+ assert service != null;
+ Class<?> parameterType = service.getClass();
List<Method> targetedMethods = new ArrayList<Method>(1);
- Method[] methods = targetType.getMethods();
for (Method method : methods) {
- if (!method.getName().equals(methodName))
- continue;
-
Class<?>[] parameterTypes = method.getParameterTypes();
- if (parameterTypes.length != 1)
- continue;
-
if (parameterTypes[0].isAssignableFrom(parameterType))
targetedMethods.add(method);
}
@@ -287,19 +294,38 @@ public class Injector {
return targetedMethods.get(0);
}
+ /**
+ * @param method
+ * @param service
+ */
+ private void invoke(Method method, Object service) {
+ if (method == null) {
+ // TODO logging
+ throw new IllegalArgumentException("Bind/Unbind method '" + method + "' does not exist in target class '"
+ + target.getClass().getName() + "'.");
+ }
+ try {
+ method.invoke(target, service);
+ } catch (SecurityException e) {
+ // TODO How to handle this and the following
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * The service listener for this injector.
+ */
class InjectorServiceListener implements ServiceListener {
public void serviceChanged(ServiceEvent event) {
int eventType = event.getType();
- if (eventType != ServiceEvent.REGISTERED && eventType != ServiceEvent.UNREGISTERING)
- return;
- Object value = event.getServiceReference().getProperty(Constants.OBJECTCLASS);
- Assert.isTrue(value instanceof String[], "Contract vioaltion: property ´objectClass´ is not a String[]");
- String[] types = (String[]) value;
- for (String type : types)
- if (type.equals(serviceId.getServiceId())) {
- handleEvent(event);
- return;
- }
+ if (eventType == ServiceEvent.REGISTERED || eventType == ServiceEvent.UNREGISTERING)
+ handleEvent(event);
}
}
diff --git a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/RankingInjector.java b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/RankingInjector.java
new file mode 100644
index 0000000..e50d77e
--- /dev/null
+++ b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/RankingInjector.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 compeople AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * compeople AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.riena.core.service;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The specialized ranking implementation.
+ */
+public class RankingInjector extends Injector {
+
+ private ServiceReference trackedServiceRef = null;
+
+ /**
+ * @param serviceId
+ * @param target
+ */
+ RankingInjector(ServiceId serviceId, Object target) {
+ super(serviceId, target);
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doStart()
+ */
+ @Override
+ protected void doStart() {
+ ServiceReference serviceRef = getCurrentHighest();
+ registerServiceListener();
+ doBind(serviceRef);
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#doStop()
+ */
+ @Override
+ protected void doStop() {
+ invokeUnbindMethod(trackedServiceRef);
+ trackedServiceRef = null;
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#bind(org.osgi.framework.ServiceReference)
+ */
+ @Override
+ protected void doBind(ServiceReference serviceRef) {
+ if (serviceRef == null)
+ return;
+ if (trackedServiceRef != null && serviceRef.compareTo(trackedServiceRef) > 0)
+ return;
+
+ invokeUnbindMethod(trackedServiceRef);
+ invokeBindMethod(serviceRef);
+ trackedServiceRef = serviceRef;
+ }
+
+ /*
+ * @see org.eclipse.riena.core.service.Injector#unbind(org.osgi.framework.ServiceReference)
+ */
+ @Override
+ protected void doUnbind(ServiceReference serviceRef) {
+ if (serviceRef == null)
+ return;
+ if (serviceRef.compareTo(trackedServiceRef) != 0)
+ return;
+ invokeUnbindMethod(serviceRef);
+ ServiceReference highest = getCurrentHighest();
+ if (highest == null) {
+ trackedServiceRef = null;
+ return;
+ }
+ invokeBindMethod(highest);
+ trackedServiceRef = highest;
+ }
+
+ /**
+ * @return
+ */
+ private ServiceReference getCurrentHighest() {
+ ServiceReference[] serviceRefs = getServiceReferences();
+ return highestServiceRef(serviceRefs);
+ }
+
+ /**
+ * @param serviceRefs
+ * @return
+ */
+ private static ServiceReference highestServiceRef(ServiceReference[] serviceRefs) {
+ if (serviceRefs == null)
+ return null;
+ if (serviceRefs.length == 1)
+ return serviceRefs[0];
+ Arrays.sort(serviceRefs, new ObjectRankingComparator());
+ return serviceRefs[0];
+ }
+
+ private static final class ObjectRankingComparator implements Comparator<ServiceReference> {
+
+ /*
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(ServiceReference sr1, ServiceReference sr2) {
+ return sr1.compareTo(sr2);
+ }
+ }
+
+}
diff --git a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/ServiceId.java b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/ServiceId.java
index ff60161..e7e1f51 100644
--- a/org.eclipse.riena.core/src/org/eclipse/riena/core/service/ServiceId.java
+++ b/org.eclipse.riena.core/src/org/eclipse/riena/core/service/ServiceId.java
@@ -20,22 +20,22 @@ package org.eclipse.riena.core.service;
* unregistered.
* <p>
* The service injector tracks the specified OSGi Service after calls
- * {@link #start()} and stop tracks after calls {@link #stop()}.
+ * {@link #doStart()} and stop tracks after calls {@link #stop()}.
* <p>
* The ServiceId and Injector are implemented as a ´fluent interface´ allowing
* constructs like:
* <ol>
- * <li>new ServiceId("id1").injectInto(target).start(context)</li>
+ * <li>new ServiceId("id1").injectInto(target).andStart(context)</li>
* <li>new
- * ServiceId("id2").useFilter(filter).injectInto(target).bind("register").unbind("unregister").start(context)</li>
+ * ServiceId("id2").useFilter(filter).injectInto(target).bind("register").unbind("unregister").andStart(context)</li>
* <li>new
- * ServiceId("id3").useRanking().injectInto(target).bind("register").unbind("unregister").start(context)</li>
+ * ServiceId("id3").useRanking().injectInto(target).bind("register").unbind("unregister").andStart(context)</li>
* <li>..</li>
* </ol>
* <p>
- * This fluent interface make a few presumptions (defaults) that makes writing
- * service injectors short and expressive , e.g. number one the list, means use
- * no service ranking, no filter, and the bind method name is "bind" and the
+ * This fluent interface makes a few assumptions (defaults) that makes writing
+ * service injectors short and expressive , e.g. item one the list, means use no
+ * service ranking, no filter, and the bind method name is "bind" and the
* un-bind method name is "unbind".
* <p>
* A service or services may be injected into the target by either specifying
@@ -45,7 +45,7 @@ package org.eclipse.riena.core.service;
* The bind and un-bind method that get called by the injector can be
* polymorphic, i.e. the target can have multiple bind/un-bind methods with the
* same name but with different signatures. The injector takes responsibility
- * for choosing the appropriate methods.
+ * for choosing the appropriate bind/unbind methods.
*/
public class ServiceId {
@@ -102,7 +102,7 @@ public class ServiceId {
if (target == null)
throw new IllegalArgumentException("target may not be null.");
- return new Injector(this, target);
+ return ranking ? new RankingInjector(this, target) : new FilterInjector(this, target);
}
private void assertState() {
@@ -120,13 +120,6 @@ public class ServiceId {
}
/**
- * @return the useRanking
- */
- boolean usesRanking() {
- return ranking;
- }
-
- /**
* @return the filter
*/
String getFilter() {