You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ui...@apache.org on 2012/02/24 11:47:51 UTC
svn commit: r1293173 [5/8] - in
/felix/sandbox/uiterlix/dependencymanager/core: ./ .externalToolBuilders/
.settings/ src/ src/main/ src/main/java/ src/main/java/org/
src/main/java/org/apache/ src/main/java/org/apache/felix/
src/main/java/org/apache/fel...
Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.impl.Logger;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class DependencyBase implements Dependency, DependencyActivation {
+ private boolean m_isRequired;
+ private boolean m_isInstanceBound;
+ protected final Logger m_logger;
+ protected volatile boolean m_isStarted;
+
+ public DependencyBase(Logger logger) {
+ m_logger = logger;
+ }
+
+ public DependencyBase(DependencyBase prototype) {
+ m_logger = prototype.m_logger;
+ m_isRequired = prototype.isRequired();
+ m_isInstanceBound = prototype.m_isInstanceBound;
+ }
+
+ public synchronized boolean isRequired() {
+ return m_isRequired;
+ }
+
+ protected synchronized void setIsRequired(boolean isRequired) {
+ m_isRequired = isRequired;
+ }
+
+ public final boolean isInstanceBound() {
+ return m_isInstanceBound;
+ }
+
+ public final void setIsInstanceBound(boolean isInstanceBound) {
+ m_isInstanceBound = isInstanceBound;
+ }
+
+ public int getState() {
+ if (m_isStarted) {
+ return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+ }
+ else {
+ return isRequired() ? ComponentDependencyDeclaration.STATE_REQUIRED : ComponentDependencyDeclaration.STATE_OPTIONAL;
+ }
+ }
+
+}
Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,531 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.InvocationUtil;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceDependencyImpl extends DependencyBase implements ResourceDependency, ResourceHandler, DependencyActivation, ComponentDependencyDeclaration {
+ private volatile BundleContext m_context;
+ private volatile ServiceRegistration m_registration;
+ private Object m_callbackInstance;
+ private String m_callbackAdded;
+ private String m_callbackChanged;
+ private String m_callbackRemoved;
+ private boolean m_autoConfig;
+ private String m_autoConfigInstance;
+ protected List m_services = new ArrayList();
+ private String m_resourceFilter;
+ private URL m_trackedResource;
+ private List m_resources = new ArrayList();
+ private List m_resourceProperties = new ArrayList();
+ private URL m_resourceInstance;
+ private Dictionary m_resourcePropertiesInstance;
+ private boolean m_propagate;
+ private Object m_propagateCallbackInstance;
+ private String m_propagateCallbackMethod;
+
+ public ResourceDependencyImpl(BundleContext context, Logger logger) {
+ super(logger);
+ m_context = context;
+ m_autoConfig = true;
+ }
+
+ public ResourceDependencyImpl(ResourceDependencyImpl prototype) {
+ super(prototype);
+ m_context = prototype.m_context;
+ m_autoConfig = prototype.m_autoConfig;
+ m_callbackInstance = prototype.m_callbackInstance;
+ m_callbackAdded = prototype.m_callbackAdded;
+ m_callbackChanged = prototype.m_callbackChanged;
+ m_callbackRemoved = prototype.m_callbackRemoved;
+ m_autoConfigInstance = prototype.m_autoConfigInstance;
+ m_resourceFilter = prototype.m_resourceFilter;
+ m_trackedResource = prototype.m_trackedResource;
+ m_propagate = prototype.m_propagate;
+ }
+
+ public Dependency createCopy() {
+ return new ResourceDependencyImpl(this);
+ }
+
+ public synchronized boolean isAvailable() {
+ return m_resources.size() > 0;
+ }
+
+ public void start(DependencyService service) {
+ boolean needsStarting = false;
+ synchronized (this) {
+ m_services.add(service);
+ if (!m_isStarted) {
+ m_isStarted = true;
+ needsStarting = true;
+ }
+ }
+ if (needsStarting) {
+ Dictionary props = null;
+ if (m_trackedResource != null) {
+ props = new Properties();
+ props.put(ResourceHandler.URL, m_trackedResource);
+ }
+ else {
+ if (m_resourceFilter != null) {
+ props = new Properties();
+ props.put(ResourceHandler.FILTER, m_resourceFilter);
+ }
+ }
+ m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
+ }
+ }
+
+ public void stop(DependencyService service) {
+ boolean needsStopping = false;
+ synchronized (this) {
+ if (m_services.size() == 1 && m_services.contains(service)) {
+ m_isStarted = false;
+ needsStopping = true;
+ m_services.remove(service);
+ }
+ }
+ if (needsStopping) {
+ m_registration.unregister();
+ m_registration = null;
+ }
+ }
+
+ public void added(URL resource) {
+ handleResourceAdded(resource, null);
+ }
+
+ public void added(URL resource, Dictionary resourceProperties) {
+ handleResourceAdded(resource, resourceProperties);
+ }
+
+ private void handleResourceAdded(URL resource, Dictionary resourceProperties) {
+ if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+ long counter;
+ Object[] services;
+ synchronized (this) {
+ m_resources.add(resource);
+ m_resourceProperties.add(resourceProperties);
+ counter = m_resources.size();
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (counter == 1) {
+ ds.dependencyAvailable(this);
+ if (!isRequired()) {
+ invokeAdded(ds, resource, resourceProperties);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeAdded(ds, resource, resourceProperties);
+ }
+ }
+ }
+ }
+
+ public void changed(URL resource) {
+ handleResourceChanged(resource, null);
+ }
+
+ public void changed(URL resource, Dictionary resourceProperties) {
+ handleResourceChanged(resource, resourceProperties);
+ }
+
+ private void handleResourceChanged(URL resource, Dictionary resourceProperties) {
+ if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+ Object[] services;
+ synchronized (this) {
+ // change the resource properties for the resource
+ m_resourceProperties.set(m_resources.indexOf(resource), resourceProperties);
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ invokeChanged(ds, resource, resourceProperties);
+ }
+ }
+ }
+
+ public void removed(URL resource) {
+ handleResourceRemoved(resource, null);
+ }
+
+ public void removed(URL resource, Dictionary resourceProperties) {
+ handleResourceRemoved(resource, resourceProperties);
+ }
+
+ public void handleResourceRemoved(URL resource, Dictionary resourceProperties) {
+ if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+ long counter;
+ Object[] services;
+ synchronized (this) {
+ m_resourceProperties.remove(m_resources.indexOf(resource));
+ m_resources.remove(resource);
+ counter = m_resources.size();
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (counter == 0) {
+ ds.dependencyUnavailable(this);
+ if (!isRequired()) {
+ invokeRemoved(ds, resource, resourceProperties);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeRemoved(ds, resource, resourceProperties);
+ }
+ }
+ }
+ }
+
+ public void invokeAdded(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+ invoke(ds, serviceInstance, resourceProperties, m_callbackAdded);
+ }
+
+ public void invokeChanged(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+ invoke(ds, serviceInstance, resourceProperties, m_callbackChanged);
+ }
+
+ public void invokeRemoved(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+ invoke(ds, serviceInstance, resourceProperties, m_callbackRemoved);
+ }
+
+ private void invoke(DependencyService ds, URL serviceInstance, Dictionary resourceProperties, String name) {
+ if (name != null) {
+ ds.invokeCallbackMethod(getCallbackInstances(ds), name,
+ new Class[][] {
+ { Component.class, URL.class, Dictionary.class },
+ { Component.class, URL.class },
+ { Component.class },
+ { URL.class, Dictionary.class },
+ { URL.class },
+ { Object.class },
+ {}},
+ new Object[][] {
+ { ds.getServiceInterface(), serviceInstance, resourceProperties },
+ { ds.getServiceInterface(), serviceInstance },
+ { ds.getServiceInterface() },
+ { serviceInstance, resourceProperties },
+ { serviceInstance },
+ { serviceInstance },
+ {}}
+ );
+ }
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. When you specify callbacks, the auto configuration
+ * feature is automatically turned off, because we're assuming you don't need it in this
+ * case.
+ *
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setCallbacks(String added, String removed) {
+ return setCallbacks(null, added, null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. When you specify callbacks, the auto
+ * configuration feature is automatically turned off, because we're assuming you don't
+ * need it in this case.
+ *
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setCallbacks(String added, String changed, String removed) {
+ return setCallbacks(null, added, changed, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setCallbacks(Object instance, String added, String removed) {
+ return setCallbacks(instance, added, null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+ ensureNotActive();
+ // if at least one valid callback is specified, we turn off auto configuration
+ if (added != null || removed != null || changed != null) {
+ setAutoConfig(false);
+ }
+ m_callbackInstance = instance;
+ m_callbackAdded = added;
+ m_callbackChanged = changed;
+ m_callbackRemoved = removed;
+ return this;
+ }
+
+ private void ensureNotActive() {
+ if (m_registration != null) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in any attributes in the service implementation that
+ * are of the same type as this dependency. Default is on.
+ *
+ * @param autoConfig the value of auto config
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setAutoConfig(boolean autoConfig) {
+ ensureNotActive();
+ m_autoConfig = autoConfig;
+ return this;
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in the attribute in the service implementation that
+ * has the same type and instance name.
+ *
+ * @param instanceName the name of attribute to auto config
+ * @return this service dependency
+ */
+ public synchronized ResourceDependency setAutoConfig(String instanceName) {
+ ensureNotActive();
+ m_autoConfig = (instanceName != null);
+ m_autoConfigInstance = instanceName;
+ return this;
+ }
+
+ private synchronized Object[] getCallbackInstances(DependencyService ds) {
+ if (m_callbackInstance == null) {
+ return ds.getCompositionInstances();
+ }
+ else {
+ return new Object[] { m_callbackInstance };
+ }
+ }
+
+ public ResourceDependency setResource(URL resource) {
+ m_trackedResource = resource;
+ return this;
+ }
+
+ public synchronized ResourceDependency setRequired(boolean required) {
+ ensureNotActive();
+ setIsRequired(required);
+ return this;
+ }
+
+ public ResourceDependency setFilter(String resourceFilter) {
+ ensureNotActive();
+ m_resourceFilter = resourceFilter;
+ return this;
+ }
+
+ public ResourceDependency setFilter(String resourceFilter, String resourcePropertiesFilter) {
+ ensureNotActive();
+ m_resourceFilter = resourceFilter;
+ return this;
+ }
+
+ public void setResourcePropertiesConfigurationMember() {
+
+ }
+
+ public synchronized boolean isAutoConfig() {
+ return m_autoConfig;
+ }
+
+ public URL getResource() {
+ return lookupResource();
+ }
+
+ private URL lookupResource() {
+ try {
+ return (URL) m_resources.get(0);
+ }
+ catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ private Dictionary lookupResourceProperties() {
+ try {
+ return (Dictionary) m_resourceProperties.get(0);
+ }
+ catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ public Object getAutoConfigInstance() {
+ return lookupResource();
+ }
+
+ public String getAutoConfigName() {
+ return m_autoConfigInstance;
+ }
+
+ public Class getAutoConfigType() {
+ return URL.class;
+ }
+
+ public void invokeAdded(DependencyService service) {
+ // we remember these for future reference, needed for required callbacks
+ m_resourceInstance = lookupResource();
+ m_resourcePropertiesInstance = lookupResourceProperties();
+ invokeAdded(service, m_resourceInstance, m_resourcePropertiesInstance);
+ }
+
+ public void invokeRemoved(DependencyService service) {
+ invokeRemoved(service, m_resourceInstance, m_resourcePropertiesInstance);
+ m_resourceInstance = null;
+ m_resourcePropertiesInstance = null;
+ }
+
+ public ResourceDependency setPropagate(boolean propagate) {
+ ensureNotActive();
+ m_propagate = propagate;
+ return this;
+ }
+
+ public ResourceDependency setPropagate(Object instance, String method) {
+ setPropagate(instance != null && method != null);
+ m_propagateCallbackInstance = instance;
+ m_propagateCallbackMethod = method;
+ return this;
+ }
+
+ public Dictionary getProperties() {
+ URL resource = lookupResource();
+ Dictionary resourceProperties = lookupResourceProperties();
+ if (resource != null) {
+ if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+ try {
+ return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ URL.class }}, new Object[][] {{ resource }});
+ }
+ catch (InvocationTargetException e) {
+ m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+ }
+ catch (Exception e) {
+ m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
+ }
+ throw new IllegalStateException("Could not invoke callback");
+ }
+ else {
+ Properties props = new Properties();
+ props.setProperty(ResourceHandler.HOST, resource.getHost());
+ props.setProperty(ResourceHandler.PATH, resource.getPath());
+ props.setProperty(ResourceHandler.PROTOCOL, resource.getProtocol());
+ props.setProperty(ResourceHandler.PORT, Integer.toString(resource.getPort()));
+ // add the custom resource properties
+ if (resourceProperties != null) {
+ Enumeration properyKeysEnum = resourceProperties.keys();
+ while (properyKeysEnum.hasMoreElements()) {
+ String key = (String) properyKeysEnum.nextElement();
+ if (!key.equals(ResourceHandler.HOST) &&
+ !key.equals(ResourceHandler.PATH) &&
+ !key.equals(ResourceHandler.PROTOCOL) &&
+ !key.equals(ResourceHandler.PORT)) {
+ props.setProperty(key, resourceProperties.get(key).toString());
+ } else {
+ m_logger.log(LogService.LOG_WARNING, "Custom resource property is overlapping with the default resource property for key: " + key);
+ }
+ }
+ }
+ return props;
+ }
+ }
+ else {
+ throw new IllegalStateException("cannot find resource");
+ }
+ }
+
+ public boolean isPropagated() {
+ return m_propagate;
+ }
+
+ public ResourceDependency setInstanceBound(boolean isInstanceBound) {
+ setIsInstanceBound(isInstanceBound);
+ return this;
+ }
+
+ public String getName() {
+ StringBuilder sb = new StringBuilder();
+ if (m_resourceFilter != null) {
+ sb.append(m_resourceFilter);
+ }
+ if (m_trackedResource != null) {
+ sb.append(m_trackedResource.toString());
+ }
+ return sb.toString();
+ }
+
+ public String getType() {
+ return "resource";
+ }
+}
Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,1136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.InvocationUtil;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.impl.DefaultNullObject;
+import org.apache.felix.dm.impl.Logger;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * Service dependency that can track an OSGi service.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
+ protected List m_services = new ArrayList();
+ protected volatile ServiceTracker m_tracker;
+ protected BundleContext m_context;
+ protected volatile Class m_trackedServiceName;
+ private Object m_nullObject;
+ private volatile String m_trackedServiceFilter;
+ private volatile String m_trackedServiceFilterUnmodified;
+ private volatile ServiceReference m_trackedServiceReference;
+ private Object m_callbackInstance;
+ private String m_callbackAdded;
+ private String m_callbackChanged;
+ private String m_callbackRemoved;
+ private String m_callbackSwapped;
+ private boolean m_autoConfig;
+ protected ServiceReference m_reference;
+ protected Object m_serviceInstance;
+ private String m_autoConfigInstance;
+ private boolean m_autoConfigInvoked;
+ private Object m_defaultImplementation;
+ private Object m_defaultImplementationInstance;
+ private boolean m_isAvailable;
+ private boolean m_propagate;
+ private Object m_propagateCallbackInstance;
+ private String m_propagateCallbackMethod;
+ private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
+ private Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
+
+ private static final Comparator COMPARATOR = new Comparator() {
+ public int getRank(ServiceReference ref) {
+ Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
+ if (ranking != null && (ranking instanceof Integer)) {
+ return ((Integer) ranking).intValue();
+ }
+ return 0;
+ }
+
+ public int compare(Object a, Object b) {
+ ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
+ int ranka = getRank(ra);
+ int rankb = getRank(rb);
+ if (ranka < rankb) {
+ return -1;
+ }
+ else if (ranka > rankb) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ private static final class Tuple /* <ServiceReference, Object> */ {
+ private final ServiceReference m_serviceReference;
+ private final Object m_service;
+
+ public Tuple(ServiceReference first, Object last) {
+ m_serviceReference = first;
+ m_service = last;
+ }
+
+ public ServiceReference getServiceReference() {
+ return m_serviceReference;
+ }
+
+ public Object getService() {
+ return m_service;
+ }
+
+ public boolean equals(Object obj) {
+ return ((Tuple) obj).getServiceReference().equals(getServiceReference());
+ }
+
+ public int hashCode() {
+ return m_serviceReference.hashCode();
+ }
+ }
+
+ /**
+ * Entry to wrap service properties behind a Map.
+ */
+ private static final class ServicePropertiesMapEntry implements Map.Entry {
+ private final String m_key;
+ private Object m_value;
+
+ public ServicePropertiesMapEntry(String key, Object value) {
+ m_key = key;
+ m_value = value;
+ }
+
+ public Object getKey() {
+ return m_key;
+ }
+
+ public Object getValue() {
+ return m_value;
+ }
+
+ public String toString() {
+ return m_key + "=" + m_value;
+ }
+
+ public Object setValue(Object value) {
+ Object oldValue = m_value;
+ m_value = value;
+ return oldValue;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry e = (Map.Entry) o;
+ return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
+ }
+
+ public int hashCode() {
+ return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
+ }
+
+ private static final boolean eq(Object o1, Object o2) {
+ return (o1 == null ? o2 == null : o1.equals(o2));
+ }
+ }
+
+ /**
+ * Wraps service properties behind a Map.
+ */
+ private final static class ServicePropertiesMap extends AbstractMap {
+ private final ServiceReference m_ref;
+
+ public ServicePropertiesMap(ServiceReference ref) {
+ m_ref = ref;
+ }
+
+ public Object get(Object key) {
+ return m_ref.getProperty(key.toString());
+ }
+
+ public int size() {
+ return m_ref.getPropertyKeys().length;
+ }
+
+ public Set entrySet() {
+ Set set = new HashSet();
+ String[] keys = m_ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
+ }
+ return set;
+ }
+ }
+
+ /**
+ * Creates a new service dependency.
+ *
+ * @param context the bundle context
+ * @param logger the logger
+ */
+ public ServiceDependencyImpl(BundleContext context, Logger logger) {
+ super(logger);
+ m_context = context;
+ m_autoConfig = true;
+ }
+
+ /** Copying constructor that clones an existing instance. */
+ public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
+ super(prototype);
+ synchronized (prototype) {
+ m_context = prototype.m_context;
+ m_autoConfig = prototype.m_autoConfig;
+ m_trackedServiceName = prototype.m_trackedServiceName;
+ m_nullObject = prototype.m_nullObject;
+ m_trackedServiceFilter = prototype.m_trackedServiceFilter;
+ m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
+ m_trackedServiceReference = prototype.m_trackedServiceReference;
+ m_callbackInstance = prototype.m_callbackInstance;
+ m_callbackAdded = prototype.m_callbackAdded;
+ m_callbackChanged = prototype.m_callbackChanged;
+ m_callbackRemoved = prototype.m_callbackRemoved;
+ m_autoConfigInstance = prototype.m_autoConfigInstance;
+ m_defaultImplementation = prototype.m_defaultImplementation;
+ }
+ }
+
+ public Dependency createCopy() {
+ return new ServiceDependencyImpl(this);
+ }
+
+ public synchronized boolean isAutoConfig() {
+ return m_autoConfig;
+ }
+
+ public synchronized boolean isAvailable() {
+ return m_isAvailable;
+ }
+
+ public synchronized Object getService() {
+ Object service = null;
+ if (m_isStarted) {
+ service = m_tracker.getService();
+ }
+ if (service == null && isAutoConfig()) {
+ service = getDefaultImplementation();
+ if (service == null) {
+ service = getNullObject();
+ }
+ }
+ return service;
+ }
+
+ public Object lookupService() {
+ Object service = null;
+ if (m_isStarted) {
+ service = getService();
+ }
+ else {
+ ServiceReference[] refs = null;
+ ServiceReference ref = null;
+ if (m_trackedServiceName != null) {
+ if (m_trackedServiceFilter != null) {
+ try {
+ refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+ if (refs != null) {
+ Arrays.sort(refs, COMPARATOR);
+ ref = refs[0];
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("Invalid filter definition for dependency.");
+ }
+ }
+ else if (m_trackedServiceReference != null) {
+ ref = m_trackedServiceReference;
+ }
+ else {
+ ref = m_context.getServiceReference(m_trackedServiceName.getName());
+ }
+ if (ref != null) {
+ service = m_context.getService(ref);
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+ }
+ }
+ if (service == null && isAutoConfig()) {
+ service = getDefaultImplementation();
+ if (service == null) {
+ service = getNullObject();
+ }
+ }
+ return service;
+ }
+
+ // TODO lots of duplication in lookupService()
+ public ServiceReference lookupServiceReference() {
+ ServiceReference service = null;
+ if (m_isStarted) {
+ service = m_tracker.getServiceReference();
+ }
+ else {
+ ServiceReference[] refs = null;
+ ServiceReference ref = null;
+ if (m_trackedServiceName != null) {
+ if (m_trackedServiceFilter != null) {
+ try {
+ refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+ if (refs != null) {
+ Arrays.sort(refs, COMPARATOR);
+ ref = refs[0];
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("Invalid filter definition for dependency.");
+ }
+ }
+ else if (m_trackedServiceReference != null) {
+ ref = m_trackedServiceReference;
+ }
+ else {
+ ref = m_context.getServiceReference(m_trackedServiceName.getName());
+ }
+ if (ref != null) {
+ service = ref;
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+ }
+ }
+ return service;
+ }
+
+ private Object getNullObject() {
+ if (m_nullObject == null) {
+ Class trackedServiceName;
+ synchronized (this) {
+ trackedServiceName = m_trackedServiceName;
+ }
+ try {
+ m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject());
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+ }
+ }
+ return m_nullObject;
+ }
+
+ private Object getDefaultImplementation() {
+ if (m_defaultImplementation != null) {
+ if (m_defaultImplementation instanceof Class) {
+ try {
+ m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+ }
+ catch (Exception e) {
+ m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
+ }
+ }
+ else {
+ m_defaultImplementationInstance = m_defaultImplementation;
+ }
+ }
+ return m_defaultImplementationInstance;
+ }
+
+ public synchronized Class getInterface() {
+ return m_trackedServiceName;
+ }
+
+ public void start(DependencyService service) {
+ boolean needsStarting = false;
+ synchronized (this) {
+ m_services.add(service);
+ if (!m_isStarted) {
+ if (m_trackedServiceName != null) {
+ if (m_trackedServiceFilter != null) {
+ try {
+ m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
+ }
+ catch (InvalidSyntaxException e) {
+ throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
+ }
+ }
+ else if (m_trackedServiceReference != null) {
+ m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
+ }
+ else {
+ m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+ }
+ }
+ else {
+ throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
+ }
+ m_isStarted = true;
+ needsStarting = true;
+ }
+ }
+ if (needsStarting) {
+ m_tracker.open();
+ }
+ }
+
+ public void stop(DependencyService service) {
+ boolean needsStopping = false;
+ synchronized (this) {
+ if (m_services.size() == 1 && m_services.contains(service)) {
+ m_isStarted = false;
+ needsStopping = true;
+ }
+ }
+ if (needsStopping) {
+ m_tracker.close();
+ m_tracker = null;
+ }
+ //moved this down
+ synchronized (this) {
+ m_services.remove(service);
+ }
+ }
+
+ public Object addingService(ServiceReference ref) {
+ Object service = m_context.getService(ref);
+ // first check to make sure the service is actually an instance of our service
+ if (!m_trackedServiceName.isInstance(service)) {
+ return null;
+ }
+ return service;
+ }
+
+ public void addedService(ServiceReference ref, Object service) {
+ boolean makeAvailable = makeAvailable();
+
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeAvailable) {
+ if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
+ invokeAdded(ds, ref, service);
+ }
+ // The dependency callback will be defered until all required dependency are available.
+ ds.dependencyAvailable(this);
+ if (!isRequired()) {
+ // For optional dependency, we always invoke callback, because at this point, we know
+ // that the service has been started, and the service start method has been called.
+ // (See the ServiceImpl.bindService method, which will activate optional dependencies using
+ // startTrackingOptional() method).
+ invokeAdded(ds, ref, service);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ // At this point, either the dependency is optional (meaning that the service has been started,
+ // because if not, then our dependency would not be active); or the dependency is required,
+ // meaning that either the service is not yet started, or already started.
+ // In all cases, we have to inject the required dependency.
+
+ // we only try to invoke the method here if we are really already instantiated
+ if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
+ invokeAdded(ds, ref, service);
+ }
+ }
+ }
+ }
+
+ public void modifiedService(ServiceReference ref, Object service) {
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ ds.dependencyChanged(this);
+ if (ds.isRegistered()) {
+ invokeChanged(ds, ref, service);
+ }
+ }
+ }
+
+ public void removedService(ServiceReference ref, Object service) {
+ boolean makeUnavailable = makeUnavailable();
+
+ Object[] services;
+ synchronized (this) {
+ services = m_services.toArray();
+ }
+
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeUnavailable) {
+ ds.dependencyUnavailable(this);
+ if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
+ invokeRemoved(ds, ref, service);
+ }
+ }
+ else {
+ ds.dependencyChanged(this);
+ invokeRemoved(ds, ref, service);
+ }
+ }
+ // unget what we got in addingService (see ServiceTracker 701.4.1)
+ m_context.ungetService(ref);
+
+ }
+
+ public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+ boolean added = false;
+ synchronized (m_sr) {
+ Set set = (Set) m_sr.get(dependencyService);
+ if (set == null) {
+ set = new HashSet();
+ m_sr.put(dependencyService, set);
+ }
+ added = set.add(new Tuple(reference, service));
+ }
+ if (added) {
+ // when a changed callback is specified we might not call the added callback just yet
+ if (m_callbackSwapped != null) {
+ handleAspectAwareAdded(dependencyService, reference, service);
+ }
+ else {
+ invoke(dependencyService, reference, service, m_callbackAdded);
+ }
+ }
+ }
+
+ private void handleAspectAwareAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+ if (componentIsDependencyManagerFactory(dependencyService)) {
+ // component is either aspect or adapter factory instance, these must be ignored.
+ return;
+ }
+ boolean invokeAdded = false;
+ Integer ranking = ServiceUtil.getRankingAsInteger(reference);
+ Tuple highestRankedService = null;
+ synchronized (m_componentByRank) {
+ Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+ Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+ if (componentMap == null) {
+ // create new componentMap
+ componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
+ m_componentByRank.put(dependencyService, componentMap);
+ }
+ Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+ if (rankings == null) {
+ // new component added
+ rankings = new HashMap(); /* <Integer, Tuple> */
+ componentMap.put(originalServiceId, rankings);
+ rankings.put(ranking, new Tuple(reference, service));
+ invokeAdded = true;
+ }
+
+ if (!invokeAdded) {
+ highestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
+ }
+ }
+ if (invokeAdded) {
+ invoke(dependencyService, reference, service, m_callbackAdded);
+ } else {
+ invokeSwappedCallback(dependencyService, highestRankedService.getServiceReference(), highestRankedService.getService(), reference, service);
+ }
+ }
+
+ private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
+ Object component = dependencyService.getService();
+ if (component != null) {
+ String className = component.getClass().getName();
+ return className.startsWith("org.apache.felix.dm")
+ && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
+ && !className.startsWith("org.apache.felix.dm.test");
+ }
+ return false;
+ }
+
+ private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference, Object newService, Integer newRanking) {
+ // does a component with a higher ranking exists
+ synchronized (m_componentByRank) {
+ Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+ Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+ Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
+ rankings.remove(highestEntry.getKey());
+ rankings.put(newRanking, new Tuple(newReference, newService));
+ return (Tuple) highestEntry.getValue();
+ }
+ }
+
+ private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
+ Entry highestEntry = null; /* <Integer, Tuple> */
+ Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+ Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+ if (rankings != null) {
+ for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) { /* <Integer, Tuple> */
+ Entry mapEntry = (Entry) entryIterator.next();
+ if (highestEntry == null) {
+ highestEntry = mapEntry;
+ } else {
+ if (((Integer)mapEntry.getKey()).intValue() > ((Integer)highestEntry.getKey()).intValue()) {
+ highestEntry = mapEntry;
+ }
+ }
+ }
+ }
+ return highestEntry;
+ }
+
+
+
+ private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
+ // get the collection of rankings
+ Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+
+ Map rankings = null; /* <Integer, Tuple> */
+ if (componentMap != null) {
+ rankings = (Map) componentMap.get(serviceId);
+ }
+ // if there is only one element left in the collection of rankings
+ // and this last element has the same ranking as the supplied service (in other words, it is the same)
+ // then this is the last service
+ // NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
+ // because an aspect on top of the original service is being removed (but the original service is still
+ // there). That in turn triggers:
+ // 1) a call to added(original-service)
+ // 2) that causes a swap
+ // 3) a call to removed(aspect-service) <-- that's what we're talking about
+ return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry)rankings.entrySet().iterator().next()).getKey()
+ .equals(ServiceUtil.getRankingAsInteger(reference)));
+ }
+
+
+ public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
+ invoke(dependencyService, reference, service, m_callbackChanged);
+ }
+
+ public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+ boolean removed = false;
+ synchronized (m_sr) {
+ Set set = (Set) m_sr.get(dependencyService);
+ removed = (set != null && set.remove(new Tuple(reference, service)));
+ }
+ if (removed) {
+ if (m_callbackSwapped != null) {
+ handleAspectAwareRemoved(dependencyService, reference, service);
+ }
+ else {
+ invoke(dependencyService, reference, service, m_callbackRemoved);
+ }
+ }
+ }
+
+ private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+ if (componentIsDependencyManagerFactory(dependencyService)) {
+ // component is either aspect or adapter factory instance, these must be ignored.
+ return;
+ }
+ Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
+ synchronized (m_componentByRank) {
+ if (isLastService(dependencyService, reference, service, serviceId)) {
+ invoke(dependencyService, reference, service, m_callbackRemoved);
+ }
+ Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+ Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+ if (componentMap != null) {
+ Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+ for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) {
+ Entry mapEntry = (Entry) entryIterator.next();
+ if (((Tuple)mapEntry.getValue()).getServiceReference().equals(reference)) {
+ // remove the reference
+ rankings.remove(mapEntry.getKey());
+ }
+ }
+ if (rankings.size() == 0) {
+ componentMap.remove(originalServiceId);
+ }
+ if (componentMap.size() == 0) {
+ m_componentByRank.remove(dependencyService);
+ }
+ }
+ }
+ }
+
+ public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
+ if (name != null) {
+ dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
+ new Class[][] {
+ {Component.class, ServiceReference.class, m_trackedServiceName}, {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class}, {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class}, {Component.class, Map.class, m_trackedServiceName},
+ {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
+ },
+ new Object[][] {
+ {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
+ {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
+ }
+ );
+ }
+ }
+
+ private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous, ServiceReference currentServiceReference,
+ Object current) {
+ component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][] { { m_trackedServiceName, m_trackedServiceName },
+ { Object.class, Object.class }, { ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
+ { ServiceReference.class, Object.class, ServiceReference.class, Object.class } }, new Object[][] { { previous, current },
+ { previous, current }, { previousReference, previous, currentServiceReference, current },
+ { previousReference, previous, currentServiceReference, current } });
+ }
+
+ protected synchronized boolean makeAvailable() {
+ if (!isAvailable()) {
+ m_isAvailable = true;
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized boolean makeUnavailable() {
+ if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
+ m_isAvailable = false;
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+ if (m_callbackInstance == null) {
+ return dependencyService.getCompositionInstances();
+ }
+ else {
+ return new Object[] { m_callbackInstance };
+ }
+ }
+
+ // ----- CREATION
+
+ /**
+ * Sets the name of the service that should be tracked.
+ *
+ * @param serviceName the name of the service
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName) {
+ setService(serviceName, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the name of the service that should be tracked. You can either specify
+ * only the name, only the filter, or the name and a filter.
+ * <p>
+ * If you specify name and filter, the filter is used
+ * to track the service and should only return services of the type that was specified
+ * in the name. To make sure of this, the filter is actually extended internally to
+ * filter on the correct name.
+ * <p>
+ * If you specify only the filter, the name is assumed to be a service of type
+ * <code>Object</code> which means that, when auto configuration is on, instances
+ * of that service will be injected in any field of type <code>Object</code>.
+ *
+ * @param serviceName the name of the service
+ * @param serviceFilter the filter condition
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
+ setService(serviceName, null, serviceFilter);
+ return this;
+ }
+
+ /**
+ * Sets the name of the service that should be tracked. The name is assumed to be
+ * a service of type <code>Object</code> which means that, when auto configuration
+ * is on, instances of that service will be injected in any field of type
+ * <code>Object</code>.
+ *
+ * @param serviceFilter the filter condition
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(String serviceFilter) {
+ if (serviceFilter == null) {
+ throw new IllegalArgumentException("Service filter cannot be null.");
+ }
+ setService(null, null, serviceFilter);
+ return this;
+ }
+
+ /**
+ * Sets the name of the service that should be tracked. By specifying the service
+ * reference of the service you want to track, you can directly track a single
+ * service. The name you use must match the type of service referred to by the
+ * service reference and it is up to you to make sure that is the case.
+ *
+ * @param serviceName the name of the service
+ * @param serviceReference the service reference to track
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
+ setService(serviceName, serviceReference, null);
+ return this;
+ }
+
+ /** Internal method to set the name, service reference and/or filter. */
+ private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
+ ensureNotActive();
+ if (serviceName == null) {
+ m_trackedServiceName = Object.class;
+ }
+ else {
+ m_trackedServiceName = serviceName;
+ }
+ if (serviceFilter != null) {
+ m_trackedServiceFilterUnmodified = serviceFilter;
+ if (serviceName == null) {
+ m_trackedServiceFilter = serviceFilter;
+ }
+ else {
+ m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
+ }
+ }
+ else {
+ m_trackedServiceFilterUnmodified = null;
+ m_trackedServiceFilter = null;
+ }
+ if (serviceReference != null) {
+ m_trackedServiceReference = serviceReference;
+ if (serviceFilter != null) {
+ throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
+ }
+ }
+ else {
+ m_trackedServiceReference = null;
+ }
+ }
+
+ /**
+ * Sets the default implementation for this service dependency. You can use this to supply
+ * your own implementation that will be used instead of a Null Object when the dependency is
+ * not available. This is also convenient if the service dependency is not an interface
+ * (which would cause the Null Object creation to fail) but a class.
+ *
+ * @param implementation the instance to use or the class to instantiate if you want to lazily
+ * instantiate this implementation
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
+ ensureNotActive();
+ m_defaultImplementation = implementation;
+ return this;
+ }
+
+ /**
+ * Sets the required flag which determines if this service is required or not.
+ *
+ * @param required the required flag
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setRequired(boolean required) {
+ ensureNotActive();
+ setIsRequired(required);
+ return this;
+ }
+
+ public ServiceDependency setInstanceBound(boolean isInstanceBound) {
+ setIsInstanceBound(isInstanceBound);
+ return this;
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in any attributes in the service implementation that
+ * are of the same type as this dependency. Default is on.
+ *
+ * @param autoConfig the value of auto config
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
+ ensureNotActive();
+ m_autoConfig = autoConfig;
+ m_autoConfigInvoked = true;
+ return this;
+ }
+
+ /**
+ * Sets auto configuration for this service. Auto configuration allows the
+ * dependency to fill in the attribute in the service implementation that
+ * has the same type and instance name.
+ *
+ * @param instanceName the name of attribute to auto config
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setAutoConfig(String instanceName) {
+ ensureNotActive();
+ m_autoConfig = (instanceName != null);
+ m_autoConfigInstance = instanceName;
+ m_autoConfigInvoked = true;
+ return this;
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. When you specify callbacks, the auto configuration
+ * feature is automatically turned off, because we're assuming you don't need it in this
+ * case.
+ *
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(String added, String removed) {
+ return setCallbacks((Object) null, added, null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. When you specify callbacks, the auto
+ * configuration feature is automatically turned off, because we're assuming you don't
+ * need it in this case.
+ *
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
+ return setCallbacks((Object) null, added, changed, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. When you specify callbacks, the auto
+ * configuration feature is automatically turned off, because we're assuming you don't
+ * need it in this case.
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @param swapped the method to call when the service was swapped due to addition or
+ * removal of an aspect
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
+ return setCallbacks((Object) null, added, changed, removed, swapped);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
+ return setCallbacks(instance, added, (String) null, removed);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. They are called on the instance you provide. When you
+ * specify callbacks, the auto configuration feature is automatically turned off, because
+ * we're assuming you don't need it in this case.
+ *
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+ return setCallbacks(instance, added, changed, removed, null);
+ }
+
+ /**
+ * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+ * dependency is added, changed or removed. When you specify callbacks, the auto
+ * configuration feature is automatically turned off, because we're assuming you don't
+ * need it in this case.
+ * @param instance the instance to call the callbacks on
+ * @param added the method to call when a service was added
+ * @param changed the method to call when a service was changed
+ * @param removed the method to call when a service was removed
+ * @param swapped the method to call when the service was swapped due to addition or
+ * removal of an aspect
+ * @return this service dependency
+ */
+ public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
+ ensureNotActive();
+ // if at least one valid callback is specified, we turn off auto configuration, unless
+ // someone already explicitly invoked autoConfig
+ if ((added != null || removed != null || changed != null || swapped != null) && ! m_autoConfigInvoked) {
+ setAutoConfig(false);
+ }
+ m_callbackInstance = instance;
+ m_callbackAdded = added;
+ m_callbackChanged = changed;
+ m_callbackRemoved = removed;
+ m_callbackSwapped = swapped;
+ return this;
+ }
+
+ private void ensureNotActive() {
+ if (m_tracker != null) {
+ throw new IllegalStateException("Cannot modify state while active.");
+ }
+ }
+
+ public synchronized String toString() {
+ return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+ }
+
+ public String getAutoConfigName() {
+ return m_autoConfigInstance;
+ }
+
+ public Object getAutoConfigInstance() {
+ return lookupService();
+ }
+
+ public Class getAutoConfigType() {
+ return getInterface();
+ }
+
+ public String getName() {
+ StringBuilder sb = new StringBuilder();
+ if (m_trackedServiceName != null) {
+ sb.append(m_trackedServiceName.getName());
+ if (m_trackedServiceFilterUnmodified != null) {
+ sb.append(' ');
+ sb.append(m_trackedServiceFilterUnmodified);
+ }
+ }
+ if (m_trackedServiceReference != null) {
+ sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
+ }
+ return sb.toString();
+ }
+
+ public String getType() {
+ return "service";
+ }
+
+ public void invokeAdded(DependencyService service) {
+ ServiceReference[] refs = m_tracker.getServiceReferences();
+ if (refs != null) {
+ for (int i = 0; i < refs.length; i++) {
+ ServiceReference sr = refs[i];
+ Object svc = m_context.getService(sr);
+ invokeAdded(service, sr, svc);
+ }
+ }
+ }
+
+ public void invokeRemoved(DependencyService service) {
+ Set references = null;
+ synchronized (m_sr) {
+ references = (Set) m_sr.get(service);
+ }
+ Tuple[] refs = (Tuple[]) (references != null ? references.toArray(new Tuple[references.size()]) : new Tuple[0]);
+
+ for (int i = 0; i < refs.length; i++) {
+ ServiceReference sr = refs[i].getServiceReference();
+ Object svc = refs[i].getService();
+ invokeRemoved(service, sr, svc);
+ }
+ if (references != null) {
+ references.clear();
+ }
+ }
+
+ public Dictionary getProperties() {
+ ServiceReference reference = lookupServiceReference();
+ Object service = lookupService();
+ if (reference != null) {
+ if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+ try {
+ return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
+ }
+ catch (InvocationTargetException e) {
+ m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+ }
+ catch (Exception e) {
+ m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
+ }
+ throw new IllegalStateException("Could not invoke callback");
+ }
+ else {
+ Properties props = new Properties();
+ String[] keys = reference.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
+ props.put(keys[i], reference.getProperty(keys[i]));
+ }
+ }
+ return props;
+ }
+ }
+ else {
+ throw new IllegalStateException("cannot find service reference");
+ }
+ }
+
+ public boolean isPropagated() {
+ return m_propagate;
+ }
+
+ public ServiceDependency setPropagate(boolean propagate) {
+ ensureNotActive();
+ m_propagate = propagate;
+ return this;
+ }
+
+ public ServiceDependency setPropagate(Object instance, String method) {
+ setPropagate(instance != null && method != null);
+ m_propagateCallbackInstance = instance;
+ m_propagateCallbackMethod = method;
+ return this;
+ }
+
+}
Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
+ * required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.TemporalServiceDependency;
+import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Temporal Service dependency implementation, used to hide temporary service dependency "outage".
+ * Only works with a required dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements TemporalServiceDependency, InvocationHandler {
+ // Max millis to wait for service availability.
+ private long m_timeout = 30000;
+
+ // Dependency service currently used (with highest rank or highest service id).
+ private volatile Object m_cachedService;
+
+ // Framework bundle (we use it to detect if the framework is stopping)
+ private final Bundle m_frameworkBundle;
+
+ /**
+ * Creates a new Temporal Service Dependency.
+ *
+ * @param context The bundle context of the bundle which is instantiating this dependency object
+ * @param logger the logger our Internal logger for logging events.
+ * @see DependencyActivatorBase#createTemporalServiceDependency()
+ */
+ public TemporalServiceDependencyImpl(BundleContext context, Logger logger) {
+ super(context, logger);
+ super.setRequired(true);
+ m_frameworkBundle = context.getBundle(0);
+ }
+
+ /**
+ * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
+ * and an invocation on a missing service will fail immediately.
+ *
+ * @param timeout the dependency timeout value greater or equals to 0
+ * @throws IllegalArgumentException if the timeout is negative
+ * @return this temporal dependency
+ */
+ public synchronized TemporalServiceDependency setTimeout(long timeout) {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Invalid timeout value: " + timeout);
+ }
+ m_timeout = timeout;
+ return this;
+ }
+
+ /**
+ * Sets the required flag which determines if this service is required or not. This method
+ * just override the superclass method in order to check if the required flag is true
+ * (optional dependency is not supported by this class).
+ *
+ * @param required the required flag, which must be set to true
+ * @return this service dependency
+ * @throws IllegalArgumentException if the "required" parameter is not true.
+ */
+ public ServiceDependency setRequired(boolean required) {
+ if (! required) {
+ throw new IllegalArgumentException("A Temporal Service dependency can't be optional");
+ }
+ super.setRequired(required);
+ return this;
+ }
+
+ /**
+ * The ServiceTracker calls us here in order to inform about a service arrival.
+ */
+ public synchronized void addedService(ServiceReference ref, Object service) {
+ // Update our service cache, using the tracker. We do this because the
+ // just added service might not be the service with the highest rank ...
+ m_cachedService = m_tracker.getService();
+ boolean makeAvailable = makeAvailable();
+ if (makeAvailable) {
+ m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
+ }
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeAvailable) {
+ ds.dependencyAvailable(this);
+ }
+ }
+ if (!makeAvailable) {
+ notifyAll();
+ }
+ }
+
+ /**
+ * The ServiceTracker calls us here when a tracked service properties are modified.
+ */
+ public void modifiedService(ServiceReference ref, Object service) {
+ // We don't care.
+ }
+
+ /**
+ * The ServiceTracker calls us here when a tracked service is lost.
+ */
+ public synchronized void removedService(ServiceReference ref, Object service) {
+ // If we detect that the fwk is stopping, we behave as our superclass. That is:
+ // the lost dependency has to trigger our service deactivation, since the fwk is stopping
+ // and the lost dependency won't come up anymore.
+ if (m_frameworkBundle.getState() == Bundle.STOPPING) {
+ // Important: Notice that calling "super.removedService() might invoke our service "stop"
+ // callback, which in turn might invoke the just removed service dependency. In this case,
+ // our "invoke" method won't use the tracker to get the service dependency (because at this point,
+ // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke"
+ // method will use the "m_cachedService" instead ...
+ super.removedService(ref, service);
+ } else {
+ // Unget what we got in addingService (see ServiceTracker 701.4.1)
+ m_context.ungetService(ref);
+ // Now, ask the service tracker if there is another available service (with a lower rank).
+ // If no more service dependencies are available, the tracker will then return null;
+ // and our invoke method will block the service method invocation, until another service
+ // becomes available.
+ m_cachedService = m_tracker.getService();
+ }
+ }
+
+ /**
+ * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy.
+ */
+ public synchronized Object getService() {
+ return m_serviceInstance;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Object service = m_cachedService;
+ if (service == null) {
+ synchronized (this) {
+ long start = System.currentTimeMillis();
+ long waitTime = m_timeout;
+ while (service == null) {
+ if (waitTime <= 0) {
+ throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+ }
+ try {
+ wait(waitTime);
+ }
+ catch (InterruptedException e) {
+ throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+ }
+ waitTime = m_timeout - (System.currentTimeMillis() - start);
+ service = m_cachedService;
+ }
+
+ }
+ }
+ try {
+ return method.invoke(service, args);
+ }
+ catch (IllegalAccessException iae) {
+ method.setAccessible(true);
+ return method.invoke(service, args);
+ }
+ }
+}