
aQute.bnd.make.component.ServiceComponent 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.make.component;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
import aQute.bnd.component.DSAnnotations;
import aQute.bnd.component.HeaderReader;
import aQute.bnd.component.TagResource;
import aQute.bnd.header.Attrs;
import aQute.bnd.header.Parameters;
import aQute.bnd.make.metatype.MetaTypeReader;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Clazz;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Descriptors.TypeRef;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Resource;
import aQute.bnd.osgi.Verifier;
import aQute.bnd.service.AnalyzerPlugin;
import aQute.lib.strings.Strings;
import aQute.lib.tag.Tag;
/**
* This class is an analyzer plugin. It looks at the properties and tries to
* find out if the Service-Component header contains the bnd shortut syntax. If
* not, the header is copied to the output, if it does, an XML file is created
* and added to the JAR and the header is modified appropriately.
*/
public class ServiceComponent implements AnalyzerPlugin {
@Override
public boolean analyzeJar(Analyzer analyzer) throws Exception {
ComponentMaker m = new ComponentMaker(analyzer);
Set l = m.doServiceComponent()
.keySet();
List names = DSAnnotations.removeOverlapInServiceComponentHeader(l);
analyzer.setProperty(Constants.SERVICE_COMPONENT, Strings.join(names));
analyzer.getInfo(m, Constants.SERVICE_COMPONENT + ": ");
m.close();
return false;
}
private static class ComponentMaker extends Processor {
Analyzer analyzer;
ComponentMaker(Analyzer analyzer) {
super(analyzer);
this.analyzer = analyzer;
}
/**
* Iterate over the Service Component entries. There are two cases:
*
* - An XML file reference
* - A FQN/wildcard with a set of attributes
*
* An XML reference is immediately expanded, an FQN/wildcard is more
* complicated and is delegated to
* {@link #componentEntry(Map, String, Map)}.
*
* @throws Exception
*/
Map> doServiceComponent() throws Exception {
Map> serviceComponents = newMap();
String header = getProperty(SERVICE_COMPONENT);
Parameters sc = parseHeader(header);
for (Entry entry : sc.entrySet()) {
String name = entry.getKey();
Map info = entry.getValue();
try {
if (name.indexOf('/') >= 0 || name.endsWith(".xml")) {
// Normal service component, we do not process it
serviceComponents.put(name, Collections.emptyMap());
} else {
componentEntry(serviceComponents, name, info);
}
} catch (Exception e) {
error("Invalid " + Constants.SERVICE_COMPONENT + " header: %s %s, throws %s", name, info, e);
throw e;
}
}
return serviceComponents;
}
/**
* Parse an entry in the Service-Component header. This header supports
* the following types:
*
* - An FQN + attributes describing a component
* - A wildcard expression for finding annotated components.
*
* The problem is the distinction between an FQN and a wildcard because
* an FQN can also be used as a wildcard. If the info specifies
* {@link Constants#NOANNOTATIONS} then wildcards are an error and the
* component must be fully described by the info. Otherwise the
* FQN/wildcard is expanded into a list of classes with annotations. If
* this list is empty, the FQN case is interpreted as a complete
* component definition. For the wildcard case, it is checked if any
* matching classes for the wildcard have been compiled for a class file
* format that does not support annotations, this can be a problem with
* JSR14 who silently ignores annotations. An error is reported in such
* a case.
*
* @param serviceComponents
* @param name
* @param info
* @throws Exception
* @throws IOException
*/
private void componentEntry(Map> serviceComponents, String name,
Map info) throws Exception, IOException {
boolean fqn = Verifier.isFQN(name);
if (fqn)
createComponentResource(serviceComponents, name, info);
else {
reportInvalidUseOfServiceComponentHeader(name);
}
}
/*
* Report the use of old bnd annotations
* @param name the name used in the Service-Component headers
*/
private void reportInvalidUseOfServiceComponentHeader(String name) throws Exception, IOException {
SetLocation warning = warning(Constants.SERVICE_COMPONENT
+ " is normally generated by bnd. If you want to point at specific component XML files (wildcards are allowed, see findEntries) make sure it ends in '.xml'. Found %s",
name);
FileLine where = getHeader(Pattern.compile(Constants.SERVICE_COMPONENT, Pattern.LITERAL),
Pattern.compile(name, Pattern.LITERAL));
if (where != null) {
where.set(warning);
}
warning.reference("https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html#d0e36857");
//
// We now have a selection on the classes in the analyzer.
// we should generate an error if any of those classes has
// and old fashioned bnd annotation.
//
Collection classes = analyzer.getClasses(null, "NAMED", name, "ANNOTATED",
"aQute.bnd.annotation.component.Component");
for (Clazz clazz : classes) {
//
// Generate an error on each class that uses the annotations
SetLocation loc = error(Constants.SERVICE_COMPONENT
+ " refers to %s that is annotated with the deprecated bnd Component annotation. Support for this was removed in 4.0.0 because they were taken over by OSGi. Please update to the comparable OSGi annotations",
clazz.getFQN());
TypeRef cname = clazz.getClassName();
String source = analyzer.getSourceFileFor(cname);
if (source != null) {
File f = getFile(source);
if (f.isFile()) {
Pattern pattern = Pattern.compile("^.*@.*Component.*$", Pattern.CASE_INSENSITIVE);
where = findHeader(f, pattern);
if (where != null) {
where.set(loc);
} else {
loc.file(source);
loc.line(1);
}
}
}
}
}
private void createComponentResource(Map> components, String name,
Map info) throws Exception {
// We can override the name in the parameters
if (info.containsKey(COMPONENT_NAME))
name = info.get(COMPONENT_NAME);
// Assume the impl==name, but allow override
String impl = name;
if (info.containsKey(COMPONENT_IMPLEMENTATION))
impl = info.get(COMPONENT_IMPLEMENTATION);
TypeRef implRef = analyzer.getTypeRefFromFQN(impl);
// Check if such a class exists
analyzer.nonClassReferTo(implRef);
boolean designate = designate(name, info.get(COMPONENT_DESIGNATE), false)
|| designate(name, info.get(COMPONENT_DESIGNATEFACTORY), true);
// If we had a designate, we want a default configuration policy of
// require.
if (designate && info.get(COMPONENT_CONFIGURATION_POLICY) == null)
info.put(COMPONENT_CONFIGURATION_POLICY, "require");
// We have a definition, so make an XML resources
Resource resource = createComponentResource(name, impl, info);
String pathSegment = analyzer.validResourcePath(name, "Invalid component name");
analyzer.getJar()
.putResource("OSGI-INF/" + pathSegment + ".xml", resource);
components.put("OSGI-INF/" + pathSegment + ".xml", Collections.emptyMap());
}
/**
* Create a Metatype and Designate record out of the given
* configurations.
*
* @param name
* @param config
* @throws Exception
*/
private boolean designate(String name, String config, boolean factory) throws Exception {
if (config == null)
return false;
for (String c : Processor.split(config)) {
TypeRef ref = analyzer.getTypeRefFromFQN(c);
Clazz clazz = analyzer.findClass(ref);
if (clazz != null) {
analyzer.nonClassReferTo(ref);
MetaTypeReader r = new MetaTypeReader(clazz, analyzer);
r.setDesignate(name, factory);
String rname = "OSGI-INF/metatype/" + name + ".xml";
analyzer.getJar()
.putResource(rname, r);
} else {
analyzer.error("Cannot find designated configuration class %s for component %s", c, name);
}
}
return true;
}
/**
* Create the resource for a DS component.
*/
Resource createComponentResource(String name, String impl, Map info) throws Exception {
HeaderReader hr = new HeaderReader(analyzer);
Tag tag = hr.createComponentTag(name, impl, info);
hr.close();
return new TagResource(tag);
}
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy