blob: 03b582ae47b95398d2b9b987b847410125882905 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1997, 2010 by ProSyst Software GmbH
* http://www.prosyst.com
* 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:
* ProSyst Software GmbH - initial API and implementation
* Andrew Teirney - bug.id = 278732
* Simon Kaegi - bug.id = 296750
*******************************************************************************/
package org.eclipse.equinox.internal.ds;
import java.util.*;
import org.apache.felix.scr.Component;
import org.eclipse.equinox.internal.ds.model.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentException;
import org.osgi.service.log.LogService;
/**
* Resolver.java
*
* @author Valentin Valchev
* @author Stoyan Boshev
* @author Pavlin Dobrev
*/
public final class Resolver implements WorkPerformer {
// these strings are used only for debugging purpose
static final String[] WORK_TITLES = {"BUILD ", "DYNAMICBIND ", "DISPOSE "}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
/**
* Service Component instances need to be built.
*/
public static final int BUILD = 1;
/**
* Service Component instances need to be rebound
*/
public static final int DYNAMICBIND = 2;
/**
* Service Component instances need to be disposed
*/
public static final int DISPOSE = 3;
/* Holds the enabled SCPs*/
protected Vector scpEnabled;
private InstanceProcess instanceProcess;
private Object syncLock = new Object();
private Hashtable serviceReferenceTable = new Hashtable();
public SCRManager mgr;
// TODO: Add a hashtable connecting servicereference to a list of References
// which they are bound to
// This way the search of references to unbind becomes faster when there are
// plenty of components.
// Keep in mind that build process is asynchronous.
static {
/** preload some DS bundle classes to avoid classloader deadlocks */
Reference.class.getName();
SCRUtil.class.getName();
}
/**
* Resolver constructor
*
*/
Resolver(SCRManager mgr) {
scpEnabled = new Vector();
// satisfiedSCPs = new Vector();
instanceProcess = new InstanceProcess(this);
this.mgr = mgr;
}
void synchronizeServiceReferences() {
synchronized (syncLock) {
try {
ServiceReference[] references = Activator.bc.getAllServiceReferences(null, null);
serviceReferenceTable.clear();
if (references != null) {
for (int i = 0; i < references.length; i++) {
serviceReferenceTable.put(references[i], Boolean.TRUE);
}
}
} catch (InvalidSyntaxException e) {
Activator.log(Activator.bc, LogService.LOG_WARNING, "Resolver(): " + NLS.bind(Messages.INVALID_TARGET_FILTER, ""), e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
public Object getSyncLock() {
return syncLock;
}
// This method should be called when the event processing thread is blocked
// in a user code
void queueBlocked() {
syncLock = new Object();
instanceProcess = new InstanceProcess(this);
}
// -- begin *enable* component routines
/**
* enableComponents - called by the dispatchWorker
*
* @param serviceComponents -
* a list of all component descriptions for a single bundle to be
* enabled Receive ArrayList of enabled CD's from ComponentCache
* For each CD add to list of enabled create list of CD:CD+P
* create list of CD+P:ref ( where ref is a Reference Object)
* resolve CD+P
*/
void enableComponents(Vector serviceComponents) {
long start = 0l;
if (Activator.DEBUG) {
Activator.log.debug("Resolver.enableComponents(): " + (serviceComponents != null ? serviceComponents.toString() : "null"), null); //$NON-NLS-1$ //$NON-NLS-2$
}
if (Activator.PERF) {
start = System.currentTimeMillis();
}
synchronized (syncLock) {
Configuration[] configs = null;
if (serviceComponents != null) {
for (int i = 0; i < serviceComponents.size(); i++) {
ServiceComponent current = (ServiceComponent) serviceComponents.elementAt(i);
// don't enable components which are not marked enabled
// this is done here, not in the activator just because it
// saves a little memory
if (!current.enabled) {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.enableComponents(): ignoring not enabled component " + current.name, null); //$NON-NLS-1$
}
continue;
}
if (current.componentProps != null && current.componentProps.size() > 0) {
//component is already enabled and processed. Skipping it.
continue;
}
current.setState(Component.STATE_UNSATISFIED);
if (current.getConfigurationPolicy() == ServiceComponent.CONF_POLICY_IGNORE) {
//skip looking for configurations
map(current, (Dictionary) null);
continue;
}
// check for a Configuration properties for this component
try {
String filter = "(|(" + Constants.SERVICE_PID + '=' + current.getConfigurationPID() + ")(" + ConfigurationAdmin.SERVICE_FACTORYPID + '=' + current.getConfigurationPID() + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
configs = Activator.listConfigurations(filter);
} catch (Exception e) {
Activator.log(null, LogService.LOG_ERROR, NLS.bind(Messages.CANT_LIST_CONFIGURATIONS, current.name), e);
}
// if no Configuration
if (configs == null || configs.length == 0) {
if (current.getConfigurationPolicy() != ServiceComponent.CONF_POLICY_REQUIRE) {
// create ServiceComponent + Prop
map(current, (Dictionary) null);
} else {
String customReason = Activator.configAdmin != null ? "" : Messages.CONFIG_ADMIN_SERVICE_NOT_AVAILABLE; //$NON-NLS-1$
if (Activator.DEBUG) {
Activator.log.debug(NLS.bind(Messages.COMPONENT_REQURES_CONFIGURATION_ACTIVATION, current.name) + customReason, null);
}
}
} else {
// if ManagedServiceFactory
Configuration config = configs[0];
if (config.getFactoryPid() != null && config.getFactoryPid().equals(current.getConfigurationPID())) {
// if ComponentFactory is specified
if (current.factory != null) {
Activator.log(current.bc, LogService.LOG_ERROR, NLS.bind(Messages.REGISTERED_AS_COMPONENT_AND_MANAGED_SERVICE_FACORY, current.name), null);
continue; // skip current component
}
if (Activator.DEBUG) {
Activator.log.debug("[SCR - Resolver] Resolver.enableComponents(): " + current.name + " as *managed service factory*", null); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
configs = Activator.listConfigurations("(service.factoryPid=" + config.getFactoryPid() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
Activator.log(null, LogService.LOG_ERROR, NLS.bind(Messages.CANT_LIST_CONFIGURATIONS, current.name), e);
}
// for each MSF set of properties(P), map(CD, new
// CD+P(CD,P))
if (configs != null) {
for (int index = 0; index < configs.length; index++) {
map(current, configs[index]);
}
}
} else {
if (Activator.DEBUG) {
Activator.log.debug("[SCR - Resolver] Resolver.enableComponents(): " + current.name + " as *service*", null); //$NON-NLS-1$ //$NON-NLS-2$
} // if Service, not ManagedServiceFactory
map(current, config);
}
} // end has configuration
} // end process all components!
}
}
buildNewlySatisfied(true);
if (Activator.PERF) {
start = System.currentTimeMillis() - start;
Activator.log.info("[DS perf] " + (serviceComponents != null ? Integer.toString(serviceComponents.size()) : "") + " Components enabled for " + Long.toString(start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}
protected ServiceComponentProp map(ServiceComponent component, Configuration config) {
Dictionary configProps = null;
if (config != null) {
try {
configProps = config.getProperties();
} catch (IllegalStateException ise) {
// the configuration may have beed deleted already
}
}
ServiceComponentProp scp = map(component, configProps);
if (config != null) {
// set the service PID & Factory Pid
String pid = config.getPid();
String fpid = config.getFactoryPid();
if (pid != null)
scp.properties.put(Constants.SERVICE_PID, pid);
if (fpid != null)
scp.properties.put(ConfigurationAdmin.SERVICE_FACTORYPID, fpid);
}
return scp;
}
/**
* Create the SCP and add to the maps
*
* @param component the component for which SCP will be created
* @param configProperties CM configuration properties
*/
public ServiceComponentProp map(ServiceComponent component, Dictionary configProperties) {
ServiceComponentProp scp = null;
try {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.map(): Creating SCP for component " + component.name, null); //$NON-NLS-1$
}
scp = new ServiceComponentProp(component, configProperties, mgr);
// Get all the required service reference descriptions for this
Vector referenceDescriptions = component.references;
// for each Reference Description, create a reference object
if (referenceDescriptions != null && !referenceDescriptions.isEmpty()) {
Vector references = new Vector(referenceDescriptions.size());
for (int i = 0; i < referenceDescriptions.size(); i++) {
// create new Reference Object and add to CD+P:ref map
ComponentReference cRef = (ComponentReference) referenceDescriptions.elementAt(i);
Reference ref = new Reference(cRef, scp, scp.getProperties());
references.addElement(ref);
}
scp.references = references;
}
component.addServiceComponentProp(scp);
scpEnabled.addElement(scp);
} catch (Throwable t) {
Activator.log(component.bc, LogService.LOG_ERROR, NLS.bind(Messages.ERROR_CREATING_SCP, component), t);
}
return scp;
}
/**
* Get the Eligible Components
*
* loop through CD+P list of enabled get references check if eligible if
* true add to eligible list send to Instance Process
*
*/
void getEligible(ServiceEvent event) {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.getEligible(): processing service event " + event.toString(), null); //$NON-NLS-1$
String eventType = ""; //$NON-NLS-1$
if (event.getType() == ServiceEvent.UNREGISTERING) {
eventType = "UNREGISTERING"; //$NON-NLS-1$
} else if (event.getType() == ServiceEvent.REGISTERED) {
eventType = "REGISTERED"; //$NON-NLS-1$
} else if (event.getType() == ServiceEvent.MODIFIED) {
eventType = "MODIFIED"; //$NON-NLS-1$
}
Activator.log.debug("Service event type: " + eventType, null); //$NON-NLS-1$
}
Object target = null;
Vector resolvedComponents = null;
switch (event.getType()) {
case ServiceEvent.REGISTERED :
synchronized (syncLock) {
serviceReferenceTable.put(event.getServiceReference(), Boolean.TRUE);
if (scpEnabled.isEmpty())
return; // check for any enabled configurations
//check for any static references with policy option "greedy" that need to be bound with this service reference
target = selectStaticBind(scpEnabled, event.getServiceReference());
}
if (target != null) {
//dispose instances of components with static reference that needs to be bound with the service reference
instanceProcess.disposeInstances((Vector) target, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
synchronized (syncLock) {
resolvedComponents = getComponentsToBuild();
target = selectDynamicBind(scpEnabled, event.getServiceReference());
}
//do synchronous bind
if (target != null) {
Vector unboundRefs = instanceProcess.dynamicBind((Vector) target);
if (unboundRefs != null) {
// put delayed dynamic binds on the queue
// (this is used to handle class circularity errors)
mgr.enqueueWork(this, Resolver.DYNAMICBIND, unboundRefs, false);
}
}
if (!resolvedComponents.isEmpty()) {
instanceProcess.buildComponents(resolvedComponents, false);
}
break;
case ServiceEvent.UNREGISTERING :
Vector componentsToDispose;
synchronized (syncLock) {
//check for components with static reference to this service
componentsToDispose = selectStaticUnBind(scpEnabled, event.getServiceReference(), false);
}
//dispose instances from staticUnbind
if (componentsToDispose != null) {
instanceProcess.disposeInstances(componentsToDispose, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
Vector newlyUnsatisfiedSCPs;
synchronized (syncLock) {
serviceReferenceTable.remove(event.getServiceReference());
if (scpEnabled.isEmpty())
return; // check for any enabled configurations
newlyUnsatisfiedSCPs = selectNewlyUnsatisfied(event.getServiceReference());
}
if (!newlyUnsatisfiedSCPs.isEmpty()) {
// synchronously dispose newly unsatisfied components
instanceProcess.disposeInstances(newlyUnsatisfiedSCPs, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
synchronized (syncLock) {
// Pass in the set of currently resolved components, check each one -
// do we need to unbind
target = selectDynamicUnBind(scpEnabled, event.getServiceReference(), false);
if (componentsToDispose != null || !newlyUnsatisfiedSCPs.isEmpty()) {
// some components with static references were disposed. Try to build them again
// get list of newly satisfied SCPs and build them
resolvedComponents = getComponentsToBuild();
}
}
instanceProcess.dynamicUnBind((Hashtable) target); // do synchronous unbind
if (resolvedComponents != null && !resolvedComponents.isEmpty()) {
instanceProcess.buildComponents(resolvedComponents, false);
}
return;
case ServiceEvent.MODIFIED :
synchronized (syncLock) {
if (scpEnabled.isEmpty())
return; // check for any enabled configurations
// check for newly unsatisfied components and synchronously
// dispose them
newlyUnsatisfiedSCPs = selectNewlyUnsatisfied(event.getServiceReference());
}
if (!newlyUnsatisfiedSCPs.isEmpty()) {
instanceProcess.disposeInstances(newlyUnsatisfiedSCPs, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
synchronized (syncLock) {
//check for components with static reference to this service
componentsToDispose = selectStaticUnBind(scpEnabled, event.getServiceReference(), true);
}
if (componentsToDispose != null) {
instanceProcess.disposeInstances(componentsToDispose, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
synchronized (syncLock) {
//check for any static references with policy option "greedy" that need to be bound with this service reference
componentsToDispose = selectStaticBind(scpEnabled, event.getServiceReference());
}
if (componentsToDispose != null) {
//dispose instances of components with static reference that needs to be bound with the modified service reference
instanceProcess.disposeInstances(componentsToDispose, ComponentConstants.DEACTIVATION_REASON_REFERENCE);
}
Hashtable referencesToUpdate = null;
synchronized (syncLock) {
// dynamic unbind
// check each satisfied scp - do we need to unbind
target = selectDynamicUnBind(scpEnabled, event.getServiceReference(), true);
//check references that need to be updated
referencesToUpdate = selectReferencesToUpdate(scpEnabled, event.getServiceReference());
}
if (target != null) {
instanceProcess.dynamicUnBind((Hashtable) target);
}
if (referencesToUpdate != null) {
instanceProcess.referencePropertiesUpdated(referencesToUpdate);
}
synchronized (syncLock) {
// dynamic bind
target = selectDynamicBind(scpEnabled, event.getServiceReference());
// get list of newly satisfied SCPs and build them
resolvedComponents = getComponentsToBuild();
}
if (target != null) {
Vector unboundRefs = instanceProcess.dynamicBind((Vector) target);
if (unboundRefs != null) {
// put delayed dynamic binds on the queue
// (this is used to handle class circularity errors)
mgr.enqueueWork(this, Resolver.DYNAMICBIND, unboundRefs, false);
}
}
if (!resolvedComponents.isEmpty()) {
instanceProcess.buildComponents(resolvedComponents, false);
}
}
}
public void buildNewlySatisfied(boolean checkForDependencyCycles) {
Vector resolvedComponents;
synchronized (syncLock) {
if (checkForDependencyCycles) {
findDependencyCycles();
}
resolvedComponents = getComponentsToBuild();
}
if (!resolvedComponents.isEmpty()) {
instanceProcess.buildComponents(resolvedComponents, false);
}
}
private Vector getComponentsToBuild() {
Vector resolvedComponents = resolveEligible();
// select the satisfied components only
ServiceComponentProp scp;
for (int i = resolvedComponents.size() - 1; i >= 0; i--) {
scp = (ServiceComponentProp) resolvedComponents.elementAt(i);
if (scp.getState() != Component.STATE_UNSATISFIED) {
resolvedComponents.removeElementAt(i);
}
}
return resolvedComponents;
}
/**
* Notifies the resolver that a component has been disposed.
* It should accordingly update its data structures if needed
*
**/
public void componentDisposed(ServiceComponentProp scp) {
//
}
private Vector resolveEligible() {
try {
Vector enabledSCPs = (Vector) scpEnabled.clone();
for (int k = enabledSCPs.size() - 1; k >= 0; k--) {
ServiceComponentProp scp = (ServiceComponentProp) enabledSCPs.elementAt(k);
try {
Vector refs = scp.references;
for (int i = 0; refs != null && i < refs.size(); i++) {
// Loop though all the references (dependencies)for a given
// scp. If a dependency is not met, remove it's associated scp and
// re-run the algorithm
Reference reference = (Reference) refs.elementAt(i);
if (reference != null) {
boolean resolved = !reference.isRequiredFor(scp.serviceComponent) || reference.hasProviders(this.serviceReferenceTable);
if (!resolved) {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.resolveEligible(): reference '" + reference.reference.name + "' of component '" + scp.name + "' is not resolved", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
enabledSCPs.removeElementAt(k);
break;
}
}
}
} catch (IllegalStateException ise) {
//the bundle of the scp is probably already uninstalled
scpEnabled.removeElement(scp);
enabledSCPs.removeElementAt(k);
continue;
}
// check if the bundle providing the service has permission to
// register the provided interface(s)
if (scp.serviceComponent.provides != null && System.getSecurityManager() != null) {
String[] provides = scp.serviceComponent.provides;
boolean hasPermission = true;
int i = 0;
for (; i < provides.length; i++) {
// make sure bundle has permission to register the service
try {
if (!scp.bc.getBundle().hasPermission(new ServicePermission(provides[i], ServicePermission.REGISTER))) {
hasPermission = false;
break;
}
} catch (IllegalStateException ise) {
// the bundle of the service component is uninstalled
// System.out.println("IllegalStateException occured
// while processing component "+scp);
// ise.printStackTrace();
hasPermission = false;
break;
} catch (Throwable t) {
// System.out.println("Exception occured processing
// component "+scp);
// t.printStackTrace();
hasPermission = false;
break;
}
}
if (!hasPermission) {
Activator.log(null, LogService.LOG_WARNING, NLS.bind(Messages.COMPONENT_LACKS_APPROPRIATE_PERMISSIONS, scp.name, provides[i]), null);
removeEnabledSCP(scp);
enabledSCPs.removeElementAt(k);
continue;
}
}
if (!scp.isBuilt() && !(scp.getState() == Component.STATE_DEACTIVATING)) {
scp.setState(Component.STATE_UNSATISFIED);
}
}
if (Activator.DEBUG) {
Activator.log.debug("Resolver.resolveEligible(): resolved components = " + enabledSCPs.toString(), null); //$NON-NLS-1$
}
return enabledSCPs;
} catch (Throwable e) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, e);
return new Vector();
}
}
private Vector selectNewlyUnsatisfied(ServiceReference serviceRef) {
try {
Vector result = (Vector) scpEnabled.clone();
for (int k = result.size() - 1; k >= 0; k--) {
try {
ServiceComponentProp scp = (ServiceComponentProp) result.elementAt(k);
Vector refs = scp.references;
boolean toDispose = false;
for (int i = 0; refs != null && i < refs.size(); i++) {
// Loop though all the references (dependencies)for a given
// scp. If a dependency is not met, remove it's associated
// scp and re-run the algorithm
Reference reference = (Reference) refs.elementAt(i);
if (reference != null) {
if (serviceRef != null && reference.reference.bind != null && scp.getState() == Component.STATE_ACTIVE && !(reference.dynamicUnbindReference(serviceRef) || reference.staticUnbindReference(serviceRef))) {
//make quick test - the service reference is not bound to the current component reference
continue;
}
if (serviceRef != null && !isPossibleMatch(reference, serviceRef)) {
// the service reference is not a possible match. Skipping further checks
continue;
}
boolean resolved = !reference.isRequiredFor(scp.serviceComponent) || reference.hasProviders(this.serviceReferenceTable);
if (!resolved && scp.isBuilt()) {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectNewlyUnsatisfied(): reference '" + reference.reference.name + "' of component '" + scp.name + "' is not resolved", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
toDispose = true;
break;
}
}
}
if (!toDispose) {
result.removeElementAt(k);
}
} catch (IllegalStateException ise) {
//the bundle of the scp is probably already uninstalled
scpEnabled.removeElement(result.elementAt(k));
result.removeElementAt(k);
continue;
}
}
return result;
} catch (Throwable e) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, e);
return new Vector(1);
}
}
private boolean isPossibleMatch(Reference reference, ServiceReference serviceRef) {
String[] serviceNames = (String[]) serviceRef.getProperty(Constants.OBJECTCLASS);
boolean hasName = false;
for (int i = 0; i < serviceNames.length; i++) {
if (serviceNames[i].equals(reference.interfaceName)) {
hasName = true;
break;
}
}
if (!hasName) {
return false;
}
// check target filter
try {
Filter filter = FrameworkUtil.createFilter(reference.target);
if (!filter.match(serviceRef)) {
return false;
}
} catch (InvalidSyntaxException e) {
return false;
}
return true;
}
// -- begin *disable* component routines
/**
* Disable list of ComponentDescriptions
*
* get all CD+P's from CD:CD+P Map get instances from CD+P:list of instance
* (1:n) map
*
* Strip out of Map all CD+P's Continue to pull string check each Ref
* dependency and continue to pull out CD+P's if they become not eligible
* Then call Resolver to re-resolve
*
* @param componentDescriptions
*/
void disableComponents(Vector componentDescriptions, int deactivateReason) {
long start = 0l;
if (Activator.PERF) {
start = System.currentTimeMillis();
}
ServiceComponentProp scp;
ServiceComponent component;
Vector removeList = null;
// Received list of CDs to disable
if (componentDescriptions != null) {
for (int i = 0; i < componentDescriptions.size(); i++) {
// get the first CD
component = (ServiceComponent) componentDescriptions.elementAt(i);
component.enabled = false;
component.setState(Component.STATE_DISABLED);
if (Activator.DEBUG) {
Activator.log.debug("Resolver.disableComponents() " + component.name, null); //$NON-NLS-1$
}
// then get the list of SCPs for this CD
Vector scpList = component.componentProps;
if (scpList != null) {
for (int iter = 0; iter < scpList.size(); iter++) {
scp = (ServiceComponentProp) scpList.elementAt(iter);
if (removeList == null) {
removeList = new Vector();
}
removeList.addElement(scp);
}
}
if (removeList != null) {
disposeComponentConfigs(removeList, deactivateReason);
removeList.removeAllElements();
}
if (component.componentProps != null) {
for (int j = 0; j < component.componentProps.size(); j++) {
scp = (ServiceComponentProp) component.componentProps.elementAt(j);
scp.setState(Component.STATE_DISPOSED);
}
component.componentProps.removeAllElements();
}
}
}
if (Activator.PERF) {
start = System.currentTimeMillis() - start;
Activator.log.info("[DS perf] " + Integer.toString(componentDescriptions.size()) + " Components disabled for " + Long.toString(start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
}
public void disposeComponentConfigs(Vector scps, int deactivateReason) {
// unregister, deactivate, and unbind
synchronized (syncLock) {
removeAll(scpEnabled, scps);
}
instanceProcess.disposeInstances(scps, deactivateReason);
}
// -- end *service listener*
public void performWork(int workAction, Object workObject) {
try {
if (Activator.DEBUG) {
String work = WORK_TITLES[workAction - 1];
Activator.log.debug("Resolver.performWork(): " + work + workObject, null); //$NON-NLS-1$
}
switch (workAction) {
case BUILD :
if (workObject != null) {
instanceProcess.buildComponents((Vector) workObject, false);
}
break;
case DYNAMICBIND :
if (workObject != null) {
Vector toBind = (Vector) workObject;
synchronized (syncLock) {
// remove unsatisfied configs
for (int i = toBind.size() - 1; i >= 0; i--) {
Reference ref = (Reference) toBind.elementAt(i);
if (ref.scp.isUnsatisfied()) {
//System.out.println("--BIND: removing "+ref.scp);
toBind.removeElementAt(i);
}
}
if (toBind.isEmpty())
return;
}
instanceProcess.dynamicBind(toBind);
}
break;
case DISPOSE :
if (workObject != null) {
instanceProcess.disposeInstances((Vector) workObject, ComponentConstants.DEACTIVATION_REASON_UNSPECIFIED);
}
break;
}
} catch (Throwable e) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, e);
}
}
private Vector selectDynamicBind(Vector scps, ServiceReference serviceReference) {
try {
Vector toBind = null;
for (int i = 0, size = scps.size(); i < size; i++) {
ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
if (scp.isUnsatisfied()) {
//do not check disposed components
continue;
}
// if it is not already eligible it will bind with the static
// scps
Vector references = scp.references;
// it is absolutely legal component if it doesn't contains
// references!
if (references != null) {
for (int j = 0; j < references.size(); j++) {
Reference reference = (Reference) references.elementAt(j);
if (reference.bindNewReference(serviceReference, true)) {
if (toBind == null) {
toBind = new Vector(2);
}
toBind.addElement(reference);
}
}
}
}
if (toBind != null && Activator.DEBUG) {
Activator.log.debug("Resolver.selectDynamicBind(): selected = " + toBind.toString(), null); //$NON-NLS-1$
}
return toBind;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, t);
return null;
}
}
//Returns the components with static reference that needs to be bound with the service reference.
//This can happen if the static reference has policy option "greedy"
private Vector selectStaticBind(Vector scps, ServiceReference serviceReference) {
try {
Vector toBind = null;
for (int i = 0, size = scps.size(); i < size; i++) {
ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
if (scp.isUnsatisfied()) {
//do not check disposed components
continue;
}
Vector references = scp.references;
if (references != null) {
for (int j = 0; j < references.size(); j++) {
Reference reference = (Reference) references.elementAt(j);
if (reference.bindNewReference(serviceReference, false)) {
if (toBind == null) {
toBind = new Vector(2);
}
toBind.addElement(reference);
}
}
}
}
Vector result = null;
Reference ref = null;
if (toBind != null) {
result = new Vector();
for (int i = 0; i < toBind.size(); i++) {
ref = (Reference) toBind.elementAt(i);
if (!result.contains(ref.scp)) {
result.addElement(ref.scp);
}
}
}
if (result != null && Activator.DEBUG) {
Activator.log.debug("Resolver.selectStaticBind(): selected = " + result.toString(), null); //$NON-NLS-1$
}
return result;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, t);
return null;
}
}
private Vector selectStaticUnBind(Vector scpsToCheck, ServiceReference serviceReference, boolean checkSatisfied) {
try {
Vector toUnbind = null;
for (int i = 0, size = scpsToCheck.size(); i < size; i++) {
ServiceComponentProp scp = (ServiceComponentProp) scpsToCheck.elementAt(i);
if (scp.isUnsatisfied()) {
//the scp is already deactivated
continue;
}
// if it is not already eligible it will bind with the static scps
Vector references = scp.references;
// it is absolutely legal component if it doesn't contains references!
if (references != null) {
for (int j = 0; j < references.size(); j++) {
Reference reference = (Reference) references.elementAt(j);
if (reference.staticUnbindReference(serviceReference)) {
if (checkSatisfied && reference.isInSatisfiedList(serviceReference)) {
//the service reference do still satisfy the reference and shall not be unbound
continue;
}
if (toUnbind == null) {
toUnbind = new Vector(2);
}
toUnbind.addElement(scp);
}
}
}
}
if (toUnbind != null)
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectStaticUnBind(): selected = " + toUnbind.toString(), null); //$NON-NLS-1$
}
return toUnbind;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, t);
return null;
}
}
/**
* selectDynamicUnBind Determine which resolved component description with
* properties need to unbind from this unregistering service Return map of
* reference description and component description with properties, for
* each.
*
* @param scps
* @param serviceReference
* @return this is fairly complex to explain ;(
*/
private Hashtable selectDynamicUnBind(Vector scps, ServiceReference serviceReference, boolean checkSatisfied) {
try {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectDynamicUnBind(): entered", null); //$NON-NLS-1$
}
Hashtable unbindTable = null; // ReferenceDescription:subTable
for (int i = 0; i < scps.size(); i++) {
Hashtable unbindSubTable = null; // scp:sr
ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
if (scp.isUnsatisfied()) {
//do not check deactivated components
continue;
}
Vector references = scp.references;
// some components may not contain references and it is
// absolutely valid
if (references != null) {
for (int j = 0; j < references.size(); j++) {
Reference reference = (Reference) references.elementAt(j);
// Does the scp require this service, use the Reference
// object to check
if (reference.dynamicUnbindReference(serviceReference)) {
if (checkSatisfied && reference.isInSatisfiedList(serviceReference)) {
//the service reference do still satisfy the reference and shall not be unbound
continue;
}
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectDynamicUnBind(): unbinding " + scp.toString(), null); //$NON-NLS-1$
}
if (unbindSubTable == null) {
unbindSubTable = new Hashtable(11);
}
unbindSubTable.put(scp, serviceReference);
if (unbindTable == null) {
unbindTable = new Hashtable(11);
}
unbindTable.put(reference, unbindSubTable);
} else {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectDynamicUnBind(): not unbinding " + scp + " service ref=" + serviceReference, null); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
}
}
if (unbindTable != null && Activator.DEBUG) {
Activator.log.debug("Resolver.selectDynamicUnBind(): unbindTable is " + unbindTable.toString(), null); //$NON-NLS-1$
}
return unbindTable;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, t);
return null;
}
}
/**
* Determine which component references needs to be updated by their specified updated method due to the current service references properties change
*
* @param scps
* @param serviceReference
* @return Map of <Reference>:<Map of <ServiceComponentProp>:<ServiceReference>>
*
*/
private Hashtable selectReferencesToUpdate(Vector scps, ServiceReference serviceReference) {
try {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectReferencesToUpdate(): entered", null); //$NON-NLS-1$
}
Hashtable referencesTable = null;
for (int i = 0; i < scps.size(); i++) {
Hashtable updateSubTable = null;
ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
if (scp.isUnsatisfied() || !scp.serviceComponent.isNamespaceAtLeast12()) {
//do not check deactivated components or components which are not DS 1.2 compliant
continue;
}
Vector references = scp.references;
if (references != null) {
for (int j = 0; j < references.size(); j++) {
Reference reference = (Reference) references.elementAt(j);
if (reference.reference.updated == null) {
//the reference does not have updated method specified
continue;
}
if (reference.isStatic() ? reference.staticUnbindReference(serviceReference) : reference.dynamicUnbindReference(serviceReference)) {
if (Activator.DEBUG) {
Activator.log.debug("Resolver.selectReferencesToUpdate(): selected for update reference " + reference.reference.name + " of component " + scp.toString(), null); //$NON-NLS-1$ //$NON-NLS-2$
}
if (updateSubTable == null) {
updateSubTable = new Hashtable(11);
}
updateSubTable.put(scp, serviceReference);
if (referencesTable == null) {
referencesTable = new Hashtable(11);
}
referencesTable.put(reference, updateSubTable);
}
}
}
}
if (referencesTable != null && Activator.DEBUG) {
Activator.log.debug("Resolver.selectReferencesToUpdate(): referencesTable is " + referencesTable.toString(), null); //$NON-NLS-1$
}
return referencesTable;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.UNEXPECTED_EXCEPTION, t);
return null;
}
}
// used by the ComponentFactoryImpl to build new Component configurations
public ServiceComponentProp mapNewFactoryComponent(ServiceComponent component, Dictionary configProperties) {
synchronized (syncLock) {
// create a new scp (adds to resolver enabledSCPs list)
ServiceComponentProp newSCP = map(component, configProperties);
newSCP.setComponentFactory(false); // avoid registration of new
// ComponentFactory
// we added a SCP, so check for circularity and mark cycles
findDependencyCycles();
// get list of newly satisfied SCPs and check whether the new SCP is
// satisfied
Vector eligibleSCPs = resolveEligible();
if (!eligibleSCPs.contains(newSCP)) {
removeEnabledSCP(newSCP);
throw new ComponentException(NLS.bind(Messages.CANT_RESOLVE_COMPONENT_INSTANCE, newSCP, configProperties));
}
return newSCP;
}
}
/**
* Check through the enabled list for cycles. Cycles can only exist if every
* service is provided by a Service Component (not legacy OSGi). If the
* cycle has no optional dependencies, log an error and disable a Component
* Configuration in the cycle. If cycle can be "broken" by an optional
* dependency, make a note (stored in the
* {@link ServiceComponentProp#delayActivateSCPNames} Vector).
*
* @throws CircularityException
* if cycle exists with no optional dependencies
*/
private void findDependencyCycles() {
Vector emptyVector = new Vector();
try {
// find the SCPs that resolve using other SCPs and record their
// dependencies
Hashtable dependencies = new Hashtable();
for (int i = scpEnabled.size() - 1; i >= 0; i--) {
ServiceComponentProp enabledSCP = (ServiceComponentProp) scpEnabled.elementAt(i);
if (enabledSCP.references != null) {
Vector dependencyVector = new Vector(1);
for (int j = 0; j < enabledSCP.references.size(); j++) {
Reference reference = (Reference) enabledSCP.references.elementAt(j);
// see if it resolves to one of the other enabled SCPs
ServiceComponentProp[] providerSCPs = reference.selectProviders(scpEnabled);
if (providerSCPs != null) {
for (int k = 0; k < providerSCPs.length; k++) {
dependencyVector.addElement(new ReferenceSCPWrapper(reference, providerSCPs[k]));
}
}
} // end for
if (!dependencyVector.isEmpty()) {
// SCP resolves using some other SCPs, could be a cycle
dependencies.put(enabledSCP, dependencyVector);
} else {
dependencies.put(enabledSCP, emptyVector);
}
}
} // end for
// traverse dependency tree and look for cycles
Hashtable visited = new Hashtable(11);
Enumeration keys = dependencies.keys();
while (keys.hasMoreElements()) {
ServiceComponentProp scp = (ServiceComponentProp) keys.nextElement();
if (!visited.containsKey(scp)) {
Vector currentStack = new Vector(2);
checkDependencies(scp, visited, dependencies, currentStack);
}
}
} catch (CircularityException e) {
Activator.log(e.getCausingComponent().serviceComponent.bc, LogService.LOG_ERROR, NLS.bind(Messages.CIRCULARITY_EXCEPTION_FOUND, e.getCausingComponent().serviceComponent), e);
// disable offending SCP
removeEnabledSCP(e.getCausingComponent());
// try again
findDependencyCycles();
}
}
/**
* Recursively do a depth-first traversal of a dependency tree, looking for
* cycles.
* <p>
* If a cycle is found, calls
* {@link Resolver#processDependencyCycle(ReferenceSCPWrapper, Vector)}.
* </p>
*
* @param scp
* current node in dependency tree
* @param visited
* holdes the visited nodes
* @param dependencies
* Dependency tree - a Hashtable of ({@link ServiceComponentProp}):(Vector
* of {@link ReferenceSCPWrapper}s)
* @param currentStack
* Vector of {@link ReferenceSCPWrapper}s - the history of our
* traversal so far (the path back to the root of the tree)
* @throws CircularityException
* if an cycle with no optional dependencies is found.
*/
private void checkDependencies(ServiceComponentProp scp, Hashtable visited, Hashtable dependencies, Vector currentStack) throws CircularityException {
// the component has already been visited and it's dependencies checked
// for cycles
if (visited.containsKey(scp)) {
return;
}
Vector refSCPs = (Vector) dependencies.get(scp);
if (refSCPs != null) {
for (int i = 0; i < refSCPs.size(); i++) {
ReferenceSCPWrapper refSCP = (ReferenceSCPWrapper) refSCPs.elementAt(i);
if (currentStack.contains(refSCP)) {
// may throw circularity exception
processDependencyCycle(refSCP, currentStack);
continue;
}
currentStack.addElement(refSCP);
checkDependencies(refSCP.producer, visited, dependencies, currentStack);
currentStack.removeElement(refSCP);
}
}
visited.put(scp, ""); //$NON-NLS-1$
}
/**
* A cycle was detected. SCP is referenced by the last element in
* currentStack. Throws CircularityException if the cycle does not contain
* an optional dependency, else choses a point at which to "break" the cycle
* (the break point must be immediately after an optional dependency) and
* adds a "cycle note".
*
* @see ServiceComponentProp#delayActivateSCPNames
*/
private void processDependencyCycle(ReferenceSCPWrapper refSCP, Vector currentStack) throws CircularityException {
// find an optional dependency
ReferenceSCPWrapper optionalRefSCP = null;
for (int i = currentStack.indexOf(refSCP); i < currentStack.size(); i++) {
ReferenceSCPWrapper cycleRefSCP = (ReferenceSCPWrapper) currentStack.elementAt(i);
if (!cycleRefSCP.ref.isRequired()) {
optionalRefSCP = cycleRefSCP;
break;
}
}
if (optionalRefSCP == null) {
throw new CircularityException(refSCP.ref.scp);
}
// check whether the optional reference is static - this is not allowed
// because of the way components with static refereces are built
if (optionalRefSCP.ref.policy == ComponentReference.POLICY_STATIC) {
Activator.log(optionalRefSCP.ref.scp.bc, LogService.LOG_ERROR, NLS.bind(Messages.STATIC_OPTIONAL_REFERENCE_TO_BE_REMOVED, optionalRefSCP.ref.reference), null);
optionalRefSCP.ref.scp.references.removeElement(optionalRefSCP.ref);
}
// the dependent component will be processed with delay whenever
// necessary
optionalRefSCP.ref.scp.setDelayActivateSCPName(optionalRefSCP.producer.serviceComponent.name);
}
// used to remove all elements of vector which occur in another vector
private void removeAll(Vector src, Vector elementsToRemove) {
for (int i = src.size() - 1; i >= 0; i--) {
if (elementsToRemove.contains(src.elementAt(i))) {
src.removeElementAt(i);
}
}
}
private void removeEnabledSCP(ServiceComponentProp scp) {
scpEnabled.removeElement(scp);
scp.serviceComponent.componentProps.remove(scp);
scp.setState(Component.STATE_DISPOSED);
}
/**
* Reorder the specified SCP and place it at the end of the enabledSCPs list
* @param scp the SCP to reorder
*/
protected void reorderSCP(ServiceComponentProp scp) {
synchronized (syncLock) {
if (scpEnabled.removeElement(scp)) {
scpEnabled.addElement(scp);
}
}
}
public void removeFromSatisfiedList(ServiceComponentProp scp) {
Vector tmp = new Vector();
tmp.addElement(scp);
mgr.enqueueWork(this, Resolver.DISPOSE, tmp, false);
}
/**
* Used to traverse the dependency tree in order to find cycles.
*
*/
private static class ReferenceSCPWrapper {
public Reference ref;
public ServiceComponentProp producer;
protected ReferenceSCPWrapper(Reference ref, ServiceComponentProp producer) {
this.ref = ref;
this.producer = producer;
}
public String toString() {
return "Reference : " + ref + " ::: SCP : " + producer; //$NON-NLS-1$ //$NON-NLS-2$
}
}
public Component getComponent(long componentId) {
synchronized (scpEnabled) {
for (int i = 0; i < scpEnabled.size(); i++) {
ServiceComponentProp scp = (ServiceComponentProp) scpEnabled.elementAt(i);
if (scp.getId() == componentId) {
return scp;
}
}
}
return null;
}
}