All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.bnd.make.component.ServiceComponent Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package aQute.bnd.make.component;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

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.Clazz.QUERY;
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.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 {

	public boolean analyzeJar(Analyzer analyzer) throws Exception {

		ComponentMaker m = new ComponentMaker(analyzer);

		Map> l = m.doServiceComponent();

		analyzer.setProperty(Constants.SERVICE_COMPONENT, Processor.printClauses(l));

		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:
		 * 
    *
  1. An XML file reference
  2. *
  3. A FQN/wildcard with a set of attributes
  4. *
* 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, EMPTY); } else { componentEntry(serviceComponents, name, info); } } catch (Exception e) { e.printStackTrace(); 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: *
    *
  1. An FQN + attributes describing a component
  2. *
  3. A wildcard expression for finding annotated components.
  4. *
* 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 annotations = !Processor.isTrue(info.get(NOANNOTATIONS)); boolean fqn = Verifier.isFQN(name); if (annotations) { // Annotations possible! Collection annotatedComponents = analyzer.getClasses("", QUERY.ANNOTATED.toString(), "aQute.bnd.annotation.component.Component", // QUERY.NAMED.toString(), name // ); if (fqn) { if (annotatedComponents.isEmpty()) { // No annotations, fully specified in header createComponentResource(serviceComponents, name, info); } else { // We had a FQN so expect only one for (Clazz c : annotatedComponents) { annotated(serviceComponents, c, info); } } } else { // We did not have an FQN, so expect the use of wildcards if (annotatedComponents.isEmpty()) checkAnnotationsFeasible(name); else for (Clazz c : annotatedComponents) { annotated(serviceComponents, c, info); } } } else { // No annotations if (fqn) createComponentResource(serviceComponents, name, info); else error("Set to %s but entry %s is not an FQN ", NOANNOTATIONS, name); } } /** * Check if annotations are actually feasible looking at the class * format. If the class format does not provide annotations then it is * no use specifying annotated components. * * @param name * @throws Exception */ private Collection checkAnnotationsFeasible(String name) throws Exception { Collection not = analyzer.getClasses("", QUERY.NAMED.toString(), name // ); if (not.isEmpty()) { if ("*".equals(name)) return not; error("Specified %s but could not find any class matching this pattern", name); } for (Clazz c : not) { if (c.getFormat().hasAnnotations()) return not; } warning("Wildcards are used (%s) requiring annotations to decide what is a component. Wildcard maps to classes that are compiled with java.target < 1.5. Annotations were introduced in Java 1.5", name); return not; } void annotated(Map> components, Clazz c, Map info) throws Exception { analyzer.warning( "%s annotation used in class %s. Bnd DS annotations are deprecated as of Bnd 3.2 and support will be removed in Bnd 4.0. Please change to use OSGi DS annotations.", "aQute.bnd.annotation.component.Component", c); // Get the component definition // from the annotations Map map = ComponentAnnotationReader.getDefinition(c, this); // Pick the name, the annotation can override // the name. String localname = map.get(COMPONENT_NAME); if (localname == null) localname = c.getFQN(); // Override the component info without manifest // entries. We merge the properties though. String merged = Processor.merge(info.remove(COMPONENT_PROPERTIES), map.remove(COMPONENT_PROPERTIES)); if (merged != null && merged.length() > 0) map.put(COMPONENT_PROPERTIES, merged); map.putAll(info); createComponentResource(components, localname, map); } 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.referTo(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", EMPTY); } /** * 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.referTo(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. * * @param list * @param name * @param info * @throws UnsupportedEncodingException */ 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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy