• Intro Headers Instructions Macros Commands
Fork me on GitHub
    • Getting Started
      • Introduction
      • How to install bnd
      • Guided Tour Workspace & Projects
      • bnd / bndlib Features and JAR Wrapping Techniques
      • FAQ - Frequently Asked Questions
    • Concepts and Practices
      • Concepts
      • Background
      • Best practices
    • Build and Development
      • Build
      • Generating JARs
      • Versioning
      • Baselining
      • Service Components
      • Metatype
      • Contracts
      • Bundle Annotations
      • Accessor Properties
      • SPI Annotations
    • Dependency and Launching
      • Resolving Dependencies
      • Launching
      • Startlevels
    • Testing
      • Testing
      • Testing with Launchpad
    • Packaging and Distribution
      • Packaging Applications
      • JPMS Libraries
      • Wrapping Libraries to OSGi Bundles
    • Documentation and Tools
      • Generating Documentation
      • bnd CLI Commands
      • For Developers
      • Templates for Workspaces
      • Tips for Windows users
      • Tools bound to bnd
    • Reference Material
      • Reference
      • Headers
      • Instruction Reference
      • Instruction Index
      • Macro Reference
      • Macro Index
      • Plugins
      • External Plugins
    • Configuration and Troubleshooting
      • Settings
      • Errors
      • Warnings
  • Service-Component ::= RESOURCE ( ',' RESOURCE )

    Prev Next
    Header
    XML documents containing component descriptions must be specified by the Service-Component header in the manifest.

    • Example: Service-Component=com.acme.Foo?;activate='start'

    • Pattern: .*



    Service-Component

    The Service-Component header lists XML files that describe Declarative Services (DS) components in the bundle. These XML documents must be present in the bundle and are used by the OSGi framework to register and manage components at runtime.

    Example:

    Service-Component: OSGI-INF/components.xml
    

    This header is required for bundles that use OSGi Declarative Services.

    /**
     * Analyze the class space for any classes that have an OSGi annotation for DS.
     */
    public class DSAnnotations implements AnalyzerPlugin {
    
    	public boolean analyzeJar(Analyzer analyzer) throws Exception {
    		Parameters header = OSGiHeader.parseHeader(analyzer.getProperty(Constants.DSANNOTATIONS));
    		if (header.size() == 0)
    			return false;
    
    		Instructions instructions = new Instructions(header);
    		Collection<Clazz> list = analyzer.getClassspace().values();
    		String sc = analyzer.getProperty(Constants.SERVICE_COMPONENT);
    		List<String> names = new ArrayList<String>();
    		if (sc != null && sc.trim().length() > 0)
    			names.add(sc);
    
    		for (Clazz c: list) {
    			for (Instruction instruction : instructions.keySet()) {
    
    				if (instruction.matches(c.getFQN())) {
    					if (instruction.isNegated())
    						break;
    					ComponentDef definition = AnnotationReader.getDefinition(c, analyzer);
    					if (definition != null) {
    						definition.sortReferences();
    						definition.prepare(analyzer);
    						String name = "OSGI-INF/" + definition.name + ".xml";
    						names.add(name);
    						analyzer.getJar().putResource(name, new TagResource(definition.getTag()));
    					}
    				}
    			}
    		}
    		sc = Processor.append(names.toArray(new String[names.size()]));
    		analyzer.setProperty(Constants.SERVICE_COMPONENT, sc);
    		return false;
    	}
    
    	@Override
    	public String toString() {
    		return "DSAnnotations";
    	}
    }
    
    			package aQute.bnd.make.component;
    		
    		import java.io.*;
    		import java.util.*;
    		import java.util.Map.Entry;
    		
    		import aQute.bnd.annotation.component.*;
    		import aQute.bnd.component.*;
    		import aQute.bnd.header.*;
    		import aQute.bnd.make.metatype.*;
    		import aQute.bnd.osgi.*;
    		import aQute.bnd.osgi.Clazz.QUERY;
    		import aQute.bnd.osgi.Descriptors.TypeRef;
    		import aQute.bnd.service.*;
    		import aQute.lib.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<String,Map<String,String>> 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:
    				 * <ol>
    				 * <li>An XML file reference</li>
    				 * <li>A FQN/wildcard with a set of attributes</li>
    				 * </ol>
    				 * An XML reference is immediately expanded, an FQN/wildcard is more
    				 * complicated and is delegated to
    				 * {@link #componentEntry(Map, String, Map)}.
    				 * 
    				 * @throws Exception
    				 */
    				Map<String,Map<String,String>> doServiceComponent() throws Exception {
    					Map<String,Map<String,String>> serviceComponents = newMap();
    					String header = getProperty(SERVICE_COMPONENT);
    					Parameters sc = parseHeader(header);
    		
    					for (Entry<String,Attrs> entry : sc.entrySet()) {
    						String name = entry.getKey();
    						Map<String,String> 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:
    				 * <ol>
    				 * <li>An FQN + attributes describing a component</li>
    				 * <li>A wildcard expression for finding annotated components.</li>
    				 * </ol>
    				 * 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<String,Map<String,String>> serviceComponents, String name,
    						Map<String,String> info) throws Exception, IOException {
    		
    					boolean annotations = !Processor.isTrue(info.get(NOANNOTATIONS));
    					boolean fqn = Verifier.isFQN(name);
    		
    					if (annotations) {
    		
    						// Annotations possible!
    		
    						Collection<Clazz> annotatedComponents = analyzer.getClasses("", QUERY.ANNOTATED.toString(),
    								Component.class.getName(), //
    								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
    				 * @return
    				 * @throws Exception
    				 */
    				private Collection<Clazz> checkAnnotationsFeasible(String name) throws Exception {
    					Collection<Clazz> 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<String,Map<String,String>> components, Clazz c, Map<String,String> info) throws Exception {
    					// Get the component definition
    					// from the annotations
    					Map<String,String> 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<String,Map<String,String>> components, String name,
    						Map<String,String> 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);
    					analyzer.getJar().putResource("OSGI-INF/" + name + ".xml", resource);
    		
    					components.put("OSGI-INF/" + name + ".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<String, String> info)
    						throws Exception {
    					HeaderReader hr = new HeaderReader(analyzer);
    					Tag tag = hr.createComponentTag(name, impl, info);
    					hr.close();
    					return new TagResource(tag);
    				}
    			}
    		
    		}
    			
    			
    			
    			
    			
    			
    				private void verifyComponent() {
    	String serviceComponent = main.get(Constants.SERVICE_COMPONENT);
    	if (serviceComponent != null) {
    		Parameters map = parseHeader(serviceComponent);
    		for (String component : map.keySet()) {
    			if (component.indexOf("*") < 0 && !dot.exists(component)) {
    				error(Constants.SERVICE_COMPONENT + " entry can not be located in JAR: " + component);
    			} else {
    				// validate component ...
    			}
    		}
    	}
    }
    

    TODO Needs review - AI Generated content

Prev Next
Search
    • Home