
aQute.bnd.component.ComponentDef Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
The newest version!
package aQute.bnd.component;
import static aQute.bnd.component.DSAnnotationReader.V1_0;
import static aQute.bnd.component.DSAnnotationReader.V1_1;
import static aQute.bnd.component.DSAnnotationReader.V1_2;
import static aQute.bnd.component.DSAnnotationReader.V1_3;
import static aQute.bnd.component.DSAnnotationReader.V1_4;
import static aQute.bnd.component.DSAnnotationReader.V1_5;
import static java.util.stream.Collectors.joining;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Stream;
import aQute.bnd.component.annotations.ConfigurationPolicy;
import aQute.bnd.component.annotations.ServiceScope;
import aQute.bnd.component.error.DeclarativeServicesAnnotationError;
import aQute.bnd.component.error.DeclarativeServicesAnnotationError.ErrorType;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Descriptors.TypeRef;
import aQute.bnd.version.Version;
import aQute.bnd.xmlattribute.ExtensionDef;
import aQute.bnd.xmlattribute.Namespaces;
import aQute.bnd.xmlattribute.XMLAttributeFinder;
import aQute.lib.tag.Tag;
/**
* This class just holds the information for the component, implementation, and
* service/provide elements. The {@link #prepare(Analyzer)} method will check if
* things are ok and the {@link #getTag()} method returns a tag if the prepare
* method returns without any errors. The class uses {@link ReferenceDef} to
* hold the references.
*/
class ComponentDef extends ExtensionDef {
final static String NAMESPACE_STEM = "http://www.osgi.org/xmlns/scr";
/**
* We use a SortedMap and a key set which controls the
* ordering of the generated properties.
*/
final SortedMap propertyDefs;
final static String PROPERTYDEF_CONSTRUCTORFORMAT = "1--%04d";
final static String PROPERTYDEF_FIELDFORMAT = "2-field-%s";
final static String PROPERTYDEF_ACTIVATEFORMAT = "3-activate-%04d";
final static String PROPERTYDEF_MODIFIEDFORMAT = "4-modified-%04d";
final static String PROPERTYDEF_DEACTIVATEFORMAT = "5-deactivate-%04d";
final static String PROPERTYDEF_ANNOTATIONFORMAT = "6-annotation-%04d";
final static String PROPERTYDEF_COMPONENT = "7-component";
/**
* This is an alias to the PropertyDef object in {@link #propertyDefs} under
* the {@link #PROPERTYDEF_COMPONENT} key.
*/
final PropertyDef property;
final PropertiesDef properties;
final PropertyDef factoryProperty;
final PropertiesDef factoryProperties;
final Map references = new LinkedHashMap<>();
Version version;
String versionReason = "base";
String name;
String factory;
Boolean immediate;
ServiceScope scope;
ConfigurationPolicy configurationPolicy;
TypeRef implementation;
TypeRef service[];
String activate;
List activation_fields = new ArrayList<>();
String deactivate;
String modified;
Boolean enabled;
String xmlns;
String[] configurationPid;
Integer init;
private final Analyzer analyzer;
public ComponentDef(Analyzer analyzer, XMLAttributeFinder finder, Version minVersion) {
super(finder);
this.analyzer = analyzer;
version = minVersion;
propertyDefs = new TreeMap<>();
property = new PropertyDef(analyzer);
propertyDefs.put(PROPERTYDEF_COMPONENT, property);
factoryProperty = new PropertyDef(analyzer);
properties = new PropertiesDef(analyzer);
factoryProperties = new PropertiesDef(analyzer);
}
String effectiveName() {
if (name != null)
return name;
if (implementation != null)
return implementation.getFQN();
return "";
}
/**
* Called to prepare. If will look for any errors or inconsistencies in the
* setup.
*
* @param analyzer the analyzer to report errors and create references
* @throws Exception
*/
void prepare(Analyzer analyzer) throws Exception {
prepareVersion(analyzer);
if (implementation == null) {
analyzer.error("No Implementation defined for component %s", name);
return;
}
analyzer.nonClassReferTo(implementation);
if (name == null)
name = implementation.getFQN();
if (service != null && service.length > 0) {
for (TypeRef interfaceName : service)
analyzer.nonClassReferTo(interfaceName);
} else if (scope != null && scope != ServiceScope.BUNDLE)
analyzer.warning("The servicefactory:=true directive is set but no service is provided, ignoring it");
if (factory == null && (!factoryProperty.isEmpty() || !factoryProperties.isEmpty())) {
analyzer.error("The factoryProperty and/or factoryProperies elements are used on a non-factory component");
}
properties.stream()
.filter(p -> !analyzer.getJar()
.exists(p))
.forEach(p -> {
String msg = "[%s] The properties entry \"%s\" is not found in the jar";
if (p.indexOf('=') >= 0) {
msg += " - did you mean to use the \"property\" attribute?";
}
final DeclarativeServicesAnnotationError details = new DeclarativeServicesAnnotationError(name, null,
null, ErrorType.MISSING_PROPERTIES_FILE);
analyzer.warning(msg, details.location(), p)
.details(details);
});
factoryProperties.stream()
.filter(p -> !analyzer.getJar()
.exists(p))
.forEach(p -> {
String msg = "[%s] The factoryProperties entry \"%s\" is not found in the jar";
if (p.indexOf('=') >= 0) {
msg += " - did you mean to use the \"factoryProperty\" attribute?";
}
final DeclarativeServicesAnnotationError factoryDetails = new DeclarativeServicesAnnotationError(name,
null, null, ErrorType.MISSING_FACTORYPROPERTIES_FILE);
analyzer.warning(msg, factoryDetails.location(), p)
.details(factoryDetails);
});
}
private void prepareVersion(Analyzer analyzer) throws Exception {
for (ReferenceDef ref : references.values()) {
ref.prepare(analyzer);
updateVersion(ref.version, ref.name + ":" + ref.reasonForVersion);
}
if (configurationPolicy != null)
updateVersion(V1_1, "configurationPolicy");
if (configurationPid != null)
updateVersion(V1_2, "configurationPid");
if (modified != null)
updateVersion(V1_1, "modified");
if (!factoryProperty.isEmpty()) {
updateVersion(V1_4, "factoryProperty");
}
if (!factoryProperties.isEmpty()) {
updateVersion(V1_4, "factoryProperties");
}
if (propertyDefs.values()
.stream()
.anyMatch(p -> p.containsKey("osgi.ds.satisfying.condition.target"))) {
updateVersion(V1_5, "use of osgi.ds.satisfying.condition.target property");
}
}
void sortReferences() {
Map temp = new TreeMap<>(references);
references.clear();
references.putAll(temp);
}
/**
* Returns a tag describing the component element.
*
* @return a component element
*/
Tag getTag() {
String xmlns = this.xmlns;
if (xmlns == null && !version.equals(V1_0))
xmlns = NAMESPACE_STEM + "/v" + version;
Tag component = new Tag(xmlns == null ? "component" : "scr:component");
Namespaces namespaces = null;
if (xmlns != null) {
namespaces = new Namespaces();
namespaces.registerNamespace("scr", xmlns);
addNamespaces(namespaces, xmlns);
for (ReferenceDef ref : references.values()) {
ref.addNamespaces(namespaces, xmlns);
}
namespaces.addNamespaces(component);
}
component.addAttribute("name", name)
.addAttribute("configuration-policy", configurationPolicy)
.addAttribute("enabled", enabled)
.addAttribute("immediate", immediate)
.addAttribute("factory", factory);
if (!version.equals(V1_0)) {
component.addAttribute("activate", activate)
.addAttribute("deactivate", deactivate);
}
component.addAttribute("modified", modified);
if (configurationPid != null) {
component.addAttribute("configuration-pid", Stream.of(configurationPid)
.map(this::map$)
.collect(joining(" ")));
}
if (!activation_fields.isEmpty()) {
component.addAttribute("activation-fields", activation_fields.stream()
.collect(joining(" ")));
}
component.addAttribute("init", init);
addAttributes(component, namespaces);
PropertyDef mergedProperty = new PropertyDef(analyzer).addAll(propertyDefs.values());
mergedProperty.propertyTags("property")
.forEachOrdered(component::addContent);
properties.propertiesTags("properties")
.forEachOrdered(component::addContent);
factoryProperty.propertyTags("factory-property")
.forEachOrdered(component::addContent);
factoryProperties.propertiesTags("factory-properties")
.forEachOrdered(component::addContent);
if (service != null && service.length != 0) {
Tag s = new Tag(component, "service");
if (scope != null) {// TODO check for DEFAULT???
if (V1_3.compareTo(version) > 0) {
if (scope == ServiceScope.PROTOTYPE) {
throw new IllegalStateException("verification failed, pre 1.3 component with scope PROTOTYPE");
}
s.addAttribute("servicefactory", scope == ServiceScope.BUNDLE);
} else {
s.addAttribute("scope", scope);
}
}
for (TypeRef ss : service) {
Tag provide = new Tag(s, "provide");
provide.addAttribute("interface", ss.getFQN());
}
}
for (ReferenceDef ref : references.values()) {
Tag refTag = ref.getTag(namespaces);
component.addContent(refTag);
}
Tag impl = new Tag(component, "implementation");
impl.addAttribute("class", implementation.getFQN());
return component;
}
private String map$(String v) {
return "$".equals(v) ? name : v;
}
void updateVersion(Version version, String reason) {
if (this.version.compareTo(version) >= 0)
return;
this.versionReason = this.version + ":" + version + ":" + reason;
this.version = version;
}
static > T max(T a, T b) {
int n = a.compareTo(b);
if (n >= 0)
return a;
return b;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy