/*
* 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 WARRANTIESOR 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.aries.application.modelling.impl;
import static org.apache.aries.application.modelling.ResourceType.SERVICE;
import static org.apache.aries.application.utils.AppConstants.LOG_ENTRY;
import static org.apache.aries.application.utils.AppConstants.LOG_EXIT;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.aries.application.InvalidAttributeException;
import org.apache.aries.application.modelling.ImportedService;
import org.apache.aries.application.modelling.ModellingConstants;
import org.apache.aries.application.modelling.Provider;
import org.apache.aries.application.modelling.ResourceType;
import org.apache.aries.application.modelling.WrappedReferenceMetadata;
import org.apache.aries.application.modelling.utils.impl.ModellingHelperImpl;
import org.apache.aries.application.utils.FilterUtils;
import org.apache.aries.util.manifest.ManifestHeaderProcessor;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* an Import-Service entry
*/
public class ImportedServiceImpl implements ImportedService
{
private final static String DEPRECATED_FILTER_ATTRIBUTE = "filter";
private final boolean _optional;
private final String _iface;
private final String _componentName;
private final String _blueprintFilter;
private final Filter _attributeFilter;
private final boolean _isMultiple;
private final String _id;
private final Map<String, String> _attributes;
private String _toString;
private String _attribFilterString; // The manner in which we set means it can't be final
private final static Pattern SERVICE_EQUALS_SERVICE = Pattern.compile("\\(" + ResourceType.SERVICE.toString()
+ "=" + ResourceType.SERVICE.toString() + "\\)");
private final Logger logger = LoggerFactory.getLogger(ImportedServiceImpl.class);
/**
* Build an ImportedServiceImpl from its elements
* @param optional
* @param iface
* @param componentName
* @param blueprintFilter
* @param id
* @param isMultiple
* @throws InvalidAttributeException
*/
public ImportedServiceImpl (boolean optional, String iface, String componentName,
String blueprintFilter, String id, boolean isMultiple)
throws InvalidAttributeException
{
_optional = optional;
_iface = iface;
_componentName = componentName;
_blueprintFilter = FilterUtils.removeMandatoryFilterToken(blueprintFilter);
_id = id;
_isMultiple = isMultiple;
_attributes = new HashMap<String, String>();
_attributeFilter = generateAttributeFilter (_attributes);
}
private Filter generateAttributeFilter (Map<String, String> attrsToPopulate) throws InvalidAttributeException {
logger.debug(LOG_ENTRY, "generateAttributeFilter", new Object[]{attrsToPopulate});
Filter result = null;
try {
attrsToPopulate.put(ModellingConstants.OBR_SERVICE, ModellingConstants.OBR_SERVICE);
if (_blueprintFilter != null) {
// We may get blueprint filters of the form (&(a=b)(c=d)). We can't put these in 'whole' because we'll
// end up generating a filter of the form (&(objectClass=foo)(&(a=b)(c=d)) which subsequent calls to
// parseFilter will choke on. So as an interim fix we'll strip off a leading &( and trailing ) if present.
String reducedBlueprintFilter;
if (_blueprintFilter.startsWith("(&")) {
reducedBlueprintFilter = _blueprintFilter.substring(2, _blueprintFilter.length() - 1);
} else {
reducedBlueprintFilter = _blueprintFilter;
}
attrsToPopulate.put(ManifestHeaderProcessor.NESTED_FILTER_ATTRIBUTE, reducedBlueprintFilter);
}
if (_componentName != null) {
attrsToPopulate.put ("osgi.service.blueprint.compname", _componentName);
}
if (_iface != null) {
attrsToPopulate.put (Constants.OBJECTCLASS, _iface);
}
_attribFilterString = ManifestHeaderProcessor.generateFilter(_attributes);
if (! "".equals(_attribFilterString)) {
result = FrameworkUtil.createFilter(FilterUtils.removeMandatoryFilterToken(_attribFilterString));
}
} catch (InvalidSyntaxException isx) {
InvalidAttributeException iax = new InvalidAttributeException(
"A syntax error occurred attempting to parse the blueprint filter string '"
+ _blueprintFilter + "' for element with id " + _id + ": "
+ isx.getLocalizedMessage(), isx);
logger.debug(LOG_EXIT, "generateAttributeFilter", new Object[]{isx});
throw iax;
}
logger.debug(LOG_EXIT, "generateAttributeFilter", new Object[]{result});
return result;
}
/**
* Deprecated constructor for building these from deprecated Export-Service manifest headers. Do not use this
* constructor for any other purpose.
* @param ifaceName
* @param attributes
* @throws InvalidAttributeException
*/
@Deprecated
public ImportedServiceImpl (String ifaceName, Map<String, String> attributes) throws InvalidAttributeException {
_optional = ("optional".equals(attributes.get("availability:")));
_iface = ifaceName;
_isMultiple = false;
_componentName = null;
_id = null;
_attributes = new HashMap<String, String>(attributes);
// The syntax for this deprecated header allows statements of the form,
// ImportService: myService;filter="(a=b")
_blueprintFilter = _attributes.remove(DEPRECATED_FILTER_ATTRIBUTE);
_attributeFilter = generateAttributeFilter (_attributes);
}
public String getFilter() {
logger.debug(LOG_ENTRY, "getFilter");
logger.debug(LOG_EXIT, "getFilter", _blueprintFilter);
return _blueprintFilter;
}
public ResourceType getType() {
logger.debug(LOG_ENTRY, "getType");
logger.debug(LOG_EXIT, "getType", ResourceType.SERVICE);
return ResourceType.SERVICE;
}
public boolean isMultiple() {
logger.debug(LOG_ENTRY, "isMultiple");
logger.debug(LOG_EXIT, "isMultiple", _isMultiple);
return _isMultiple;
}
public boolean isOptional() {
logger.debug(LOG_ENTRY, "isOptional");
logger.debug(LOG_EXIT, "isOptional", _optional);
return _optional;
}
public boolean isSatisfied(Provider capability) {
logger.debug(LOG_ENTRY, "isSatisfied", capability);
if (capability.getType() != SERVICE) {
logger.debug(LOG_EXIT, "isSatisfied", false);
return false;
}
Dictionary<String, Object> dict = new Hashtable<String, Object> (capability.getAttributes());
// If there's a value for ObjectClass, it may be a comma separated list.
String objectClass = (String) dict.get(Constants.OBJECTCLASS);
if (objectClass != null) {
String [] split = objectClass.split (",");
dict.put (Constants.OBJECTCLASS, split);
}
if (_attributeFilter == null) {
logger.debug(LOG_EXIT, "isSatisfied", true);
return true;
}
boolean allPresent = ModellingHelperImpl.areMandatoryAttributesPresent_(_attributes, capability);
boolean result = allPresent && _attributeFilter.match(dict);
logger.debug(LOG_EXIT, "isSatisfied", result);
return result;
}
public String getComponentName() {
logger.debug(LOG_ENTRY, "getComponentName");
logger.debug(LOG_EXIT, "getComponentName", _componentName);
return _componentName;
}
public String getId() {
logger.debug(LOG_ENTRY, "getId");
logger.debug(LOG_EXIT, "getId", _id);
return _id;
}
public String getInterface() {
logger.debug(LOG_ENTRY, "getInterface");
logger.debug(LOG_EXIT, "getInterface", _iface);
return _iface;
}
public boolean isList() {
logger.debug(LOG_ENTRY, "isList");
boolean result = isMultiple();
logger.debug(LOG_EXIT, "isList", result);
return result;
}
public String getAttributeFilter() {
logger.debug(LOG_ENTRY, "getAttributeFilter");
logger.debug(LOG_EXIT, "getAttributeFilter", _attribFilterString);
return _attribFilterString;
}
@Override
public boolean equals (Object o) {
boolean equal = false;
if (o==null) {
equal = false;
} else if (o==this) {
equal = true;
} else if (!(o instanceof WrappedReferenceMetadata)) {
equal = false;
} else {
equal = toString().equals(o.toString());
}
return equal;
}
@Override
public int hashCode() {
int result = toString().hashCode();
return result;
}
@Override
public String toString() {
logger.debug(LOG_ENTRY, "toString");
if (_toString != null) {
logger.debug(LOG_EXIT, "toString", _toString);
return _toString;
}
StringBuffer buf = new StringBuffer("<reference>");
buf.append("<componentName>" + _componentName + "</componentName>");
buf.append("<id>" + _id + "</id>");
buf.append("<interface>" + _iface + "</interface>");
buf.append("<isList>" + _isMultiple + "</isList>");
buf.append("<isOptional>" + _optional + "</isOptional>");
// We don't have a method for writing filters in a canonical form
buf.append("<filter>" + _blueprintFilter + "</filter>");
_toString = buf.toString();
logger.debug(LOG_EXIT, "toString", _toString);
return _toString;
}
/**
* A String suitable for use in DeployedImport-Service
*/
public String toDeploymentString() {
logger.debug(LOG_ENTRY, "toDeploymentString");
String baseFilter = getAttributeFilter();
// We may have one or more (service=service) elements that must be removed.
String reducedFilter = SERVICE_EQUALS_SERVICE.matcher(baseFilter).replaceAll("");
// now trim off mandatory:<*service occurrences
String result = FilterUtils.removeMandatoryFilterToken(reducedFilter);
logger.debug(LOG_EXIT, "toDeploymentString", result);
return result;
}
}