Package aQute.bnd.make.component

Source Code of aQute.bnd.make.component.ServiceComponent

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, "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 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
     */
    private boolean designate(String name, String config, boolean factory) {
      if (config == null)
        return false;

      for (String c : Processor.split(config)) {
        TypeRef ref = analyzer.getTypeRefFromFQN(c);
        Clazz clazz = analyzer.getClassspace().get(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 {
      Tag tag = new HeaderReader(analyzer).createComponentTag(name, impl, info);
      return new TagResource(tag);
    }
  }

}
TOP

Related Classes of aQute.bnd.make.component.ServiceComponent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
cript','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');