blob: c38adb8075adb644dc82239e770f89d9c1ffa255 [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
* Joerg-Christian Boehme - bug.id = 246757
* Andrew Teirney - bug.id = 278732
*******************************************************************************/
package org.eclipse.equinox.internal.ds;
import java.util.*;
import org.eclipse.equinox.internal.ds.model.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.log.LogService;
/**
* The reference class is used only in the resolver. It is actually like
* "instance of a references". It is used to track available, eligible
* references. The reference objects relates to ServiceComponentProp as many to
* one.
*
* @author Valentin Valchev
* @author Stoyan Boshev
* @author Pavlin Dobrev
*/
/**
* @author stoyan
*
*/
public final class Reference implements org.apache.felix.scr.Reference {
public ComponentReference reference;
ServiceComponentProp scp;
// -- begin cache
String interfaceName;
String target;
int policy;
int cardinalityHigh;
int cardinalityLow;
//holds the matching service references in case the component has no bind method
//in case the cardinality is 1..1, the vector will hold only one matching ServiceReference
Vector boundServiceReferences = new Vector(1);
// -- end cache
/**
* Reference object
*
* @param reference
* the component reference
* @param properties
* the *actual* properties that the component has. These are the
* SCP properties, e.g the default one in the XML file + the one
* that are configured in the CM.
*/
Reference(ComponentReference reference, ServiceComponentProp scp, Dictionary properties) {
this.reference = reference;
this.scp = scp;
this.interfaceName = reference.interfaceName;
this.target = reference.target;
// RFC 80 section 5.3.1.3:
// If [target] is not specified and there is no <reference-name>.target
// component
// property, then the selection filter used to select the desired
// service is
// �(objectClass=�+<interface-name>+�)�.
if (properties != null) {
target = (String) properties.get(reference.name + ComponentConstants.REFERENCE_TARGET_SUFFIX);
}
if (target == null) {
target = "(objectClass=" + interfaceName + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
// If it is not specified, then a policy of �static� is used.
policy = reference.policy;
// Cardinality indicates the number of services, matching this
// reference,
// which will bind to this Service Component. Possible values are:
// 0..1, 0..n, 1..1 (i.e. exactly one), 1..n (i.e. at least one).
// This attribute is optional. If it is not specified, then a
// cardinality
// of �1..1� is used.
switch (reference.cardinality) {
case ComponentReference.CARDINALITY_0_1 :
cardinalityLow = 0;
cardinalityHigh = 1;
break;
case ComponentReference.CARDINALITY_0_N :
cardinalityLow = 0;
cardinalityHigh = 999999999;
break;
case ComponentReference.CARDINALITY_1_1 :
cardinalityLow = 1;
cardinalityHigh = 1;
break;
case ComponentReference.CARDINALITY_1_N :
cardinalityLow = 1;
cardinalityHigh = 999999999;
}
}
public String getTarget() {
return target;
}
public void setTarget(String newTarget) {
target = newTarget;
}
// used in Resolver.resolveEligible()
final boolean hasProviders(Hashtable serviceReferenceTable) {
// check whether the component's bundle has service GET permission
if (System.getSecurityManager() != null && !scp.bc.getBundle().hasPermission(new ServicePermission(interfaceName, ServicePermission.GET))) {
return false;
}
// Get all service references for this target filter
try {
ServiceReference[] serviceReferences = null;
serviceReferences = scp.bc.getServiceReferences(interfaceName, target);
// Only return true if there is a service published that this Reference
// represents and we know about it (if we care about it)
if (serviceReferences != null) {
if (serviceReferenceTable == null)
return true;
for (int i = 0; i < serviceReferences.length; i++) {
if (serviceReferenceTable.containsKey(serviceReferences[i])) {
return true;
}
}
}
} catch (InvalidSyntaxException e) {
Activator.log(reference.component.bc, LogService.LOG_WARNING, "Reference.hasProviders(): " + NLS.bind(Messages.INVALID_TARGET_FILTER, target), e); //$NON-NLS-1$
}
return false;
}
// if the cardinality is "0..1" or "0..n" then this refernce is not required
final boolean isRequiredFor(ServiceComponent cd) {
// // we want to re-resolve if the component is static and already
// eligible
// if (policy == ComponentReference.POLICY_STATIC && cd.eligible) {
// return true;
// }
return cardinalityLow == 1;
}
public boolean isRequired() {
return cardinalityLow == 1;
}
public boolean isUnary() {
return cardinalityHigh == 1;
}
// used in Resolver.selectDynamicBind()
final boolean bindNewReference(ServiceReference referenceToBind, boolean dynamicBind) {
if (dynamicBind) {
if (policy == ComponentReference.POLICY_STATIC) {
return false;
}
} else {
if (policy == ComponentReference.POLICY_DYNAMIC) {
return false;
}
}
String[] serviceNames = (String[]) referenceToBind.getProperty(Constants.OBJECTCLASS);
boolean hasName = false;
for (int i = 0; i < serviceNames.length; i++) {
if (serviceNames[i].equals(interfaceName)) {
hasName = true;
break;
}
}
if (!hasName) {
return false;
}
if (this.reference.bind != null) {
if (this.reference.serviceReferences.size() >= cardinalityHigh) {
return false;
}
} else if (!dynamicBind) {
//custom case: static reference with no bind method - check its bound service references list
if (boundServiceReferences.size() >= cardinalityHigh) {
return false;
}
}
// check target filter
try {
Filter filter = FrameworkUtil.createFilter(target);
if (!filter.match(referenceToBind)) {
return false;
}
} catch (InvalidSyntaxException e) {
return false;
}
return true;
}
/**
* Called to determine if the specified new target filter will still satisfy the current state of the reference
* @param newTargetFilter the new target filter to be checked
* @return true if the reference will still be satisfied after the filter replacement
*/
public boolean doSatisfy(String newTargetFilter) {
ServiceReference[] refs = null;
try {
refs = scp.bc.getServiceReferences(reference.interfaceName, newTargetFilter);
} catch (InvalidSyntaxException e) {
Activator.log(scp.bc, LogService.LOG_WARNING, "[SCR] " + NLS.bind(Messages.INVALID_TARGET_FILTER, newTargetFilter), e); //$NON-NLS-1$
return false;
}
if (refs == null) {
if (cardinalityLow > 0) {
//the reference is mandatory, but there are no matching services with the new target filter
return false;
}
if (policy == ComponentReference.POLICY_STATIC) {
if (this.reference.bind != null) {
if (this.reference.serviceReferences.size() > 0) {
//have bound services which are not matching the new filter
return false;
}
} else {
//custom case: static reference with no bind method - check its bound service references list
if (boundServiceReferences.size() > 0) {
//have bound services which are not matching the new filter
return false;
}
}
}
//the reference is not mandatory and the dynamic bound services can be unbound
return true;
}
if (policy == ComponentReference.POLICY_STATIC) {
if (this.reference.bind != null) {
Enumeration keys = this.reference.serviceReferences.keys();
while (keys.hasMoreElements()) {
Object serviceRef = keys.nextElement();
boolean found = false;
for (int i = 0; i < refs.length; i++) {
if (refs[i] == serviceRef) {
found = true;
break;
}
}
if (!found) {
//the bound service reference is not already in the satisfied references set.
//since this is a static reference a restart of the component is required
return false;
}
}
if (cardinalityHigh > 1 && this.reference.serviceReferences.size() < refs.length) {
//there are more services to bind. this requires restart of the component since the reference is static
return false;
}
} else {
//custom case: static reference with no bind method
for (int i = 0; i < boundServiceReferences.size(); i++) {
Object serviceRef = boundServiceReferences.elementAt(i);
boolean found = false;
for (int j = 0; j < refs.length; j++) {
if (refs[j] == serviceRef) {
found = true;
break;
}
}
if (!found) {
//the bound service reference is not already in the satisfied references set.
//since this is a static reference a restart of the component is required
return false;
}
}
if (cardinalityHigh > 1 && boundServiceReferences.size() < refs.length) {
//there are more services to bind. this requires restart of the component since the reference is static
return false;
}
}
}
return true;
}
// used in Resolver.selectDynamicUnBind();
final boolean dynamicUnbindReference(ServiceReference changedReference) {
// nothing dynamic to do if static
if (policy == ComponentReference.POLICY_STATIC) {
return false;
}
// now check if the Service Reference is found in the list of saved
// ServiceReferences
if (!this.reference.serviceReferences.containsKey(changedReference)) {
return false;
}
return true;
}
final boolean staticUnbindReference(ServiceReference changedReference) {
// does not apply if its policy is dynamic
if (policy == ComponentReference.POLICY_DYNAMIC) {
return false;
}
// now check if the Service Reference is found in the list of saved
// ServiceReferences; this list is saved in case the component has a bind method
if (this.reference.serviceReferences.containsKey(changedReference)) {
return true;
}
if (boundServiceReferences.size() > 0) {
return (cardinalityHigh == 1) ? boundServiceReferences.elementAt(0) == changedReference : boundServiceReferences.contains(changedReference);
}
return false;
}
/**
* Checks if the passed service reference is satisfying this reference according to the target filter
* @param serviceReference the service reference to check
* @return true, if the service reference do satisfy this reference, otherwise returns false
*/
public boolean isInSatisfiedList(ServiceReference serviceReference) {
Filter filter;
try {
filter = FrameworkUtil.createFilter(target);
} catch (InvalidSyntaxException e) {
Activator.log(reference.component.bc, LogService.LOG_WARNING, "Reference.isInSatisfiedList(): " + NLS.bind(Messages.INVALID_TARGET_FILTER, target), e); //$NON-NLS-1$
return false;
}
return filter.match(serviceReference);
}
public void setBoundServiceReferences(ServiceReference[] references) {
if (policy == ComponentReference.POLICY_DYNAMIC) {
//not relevant to dynamic references
return;
}
boundServiceReferences.removeAllElements();
if (cardinalityHigh == 1) {
boundServiceReferences.addElement(references[0]);
} else {
for (int i = 0; i < references.length; i++) {
boundServiceReferences.addElement(references[i]);
}
}
}
/**
* Selects the providers that provide service required by this reference
* @param scps the list of SCPs to be searched for providers
* @return an array of SCPs that provide service required by this reference
*/
public ServiceComponentProp[] selectProviders(Vector scps) {
Filter filter;
try {
filter = FrameworkUtil.createFilter(target);
} catch (InvalidSyntaxException e) {
Activator.log(reference.component.bc, LogService.LOG_WARNING, "Reference.selectProviders(): " + NLS.bind(Messages.INVALID_TARGET_FILTER, target), e); //$NON-NLS-1$
return null;
}
Vector result = new Vector(2);
for (int i = 0; i < scps.size(); i++) {
ServiceComponentProp providerSCP = (ServiceComponentProp) scps.elementAt(i);
if (providerSCP.serviceComponent.serviceInterfaces != null && providerSCP.serviceComponent.serviceInterfaces.contains(interfaceName)) {
if (filter.match(providerSCP.getProperties())) {
result.addElement(providerSCP);
}
}
}
ServiceComponentProp[] res = new ServiceComponentProp[result.size()];
result.copyInto(res);
return res;
}
public boolean isBound() {
if (this.reference.bind != null) {
return (this.reference.serviceReferences.size() >= cardinalityLow);
}
return true;
}
public Vector getBoundServiceReferences() {
return boundServiceReferences;
}
public String getBindMethodName() {
return reference.bind;
}
public String getName() {
return reference.name;
}
public String getServiceName() {
return reference.interfaceName;
}
public ServiceReference[] getServiceReferences() {
Vector result = null;
if (this.reference.bind != null) {
if (!reference.serviceReferences.isEmpty()) {
result = new Vector(2);
Enumeration keys = reference.serviceReferences.keys();
while (keys.hasMoreElements()) {
result.add(keys.nextElement());
}
}
} else {
//no bind method
if (!boundServiceReferences.isEmpty()) {
result = (Vector) boundServiceReferences.clone();
}
}
if (result != null && !result.isEmpty()) {
ServiceReference[] finalResult = new ServiceReference[result.size()];
result.copyInto(finalResult);
return finalResult;
}
return null;
}
public String getUnbindMethodName() {
return reference.unbind;
}
public boolean isMultiple() {
return cardinalityHigh > 1;
}
public boolean isOptional() {
return cardinalityLow == 0;
}
public boolean isSatisfied() {
if (cardinalityLow == 0) {
return true;
}
try {
ServiceReference[] serviceReferences = reference.component.bc.getServiceReferences(interfaceName, target);
if (serviceReferences != null) {
return true;
}
} catch (InvalidSyntaxException e) {
// do nothing
}
return false;
}
public boolean isStatic() {
return policy == ComponentReference.POLICY_STATIC;
}
/* (non-Javadoc)
* @see org.apache.felix.scr.Reference#getUpdatedMethodName()
*/
public String getUpdatedMethodName() {
// DS specification does not specify this method yet
return null;
}
}