Package com.carrotgarden.osgi.anno.scr.make

Source Code of com.carrotgarden.osgi.anno.scr.make.Builder

package com.carrotgarden.osgi.anno.scr.make;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Property;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.carrotgarden.osgi.anno.scr.bean.AggregatorBean;
import com.carrotgarden.osgi.anno.scr.bean.ComponentBean;
import com.carrotgarden.osgi.anno.scr.bean.PropertyBean;
import com.carrotgarden.osgi.anno.scr.bean.PropertyFileBean;
import com.carrotgarden.osgi.anno.scr.bean.ProvideBean;
import com.carrotgarden.osgi.anno.scr.bean.ReferenceBean;
import com.carrotgarden.osgi.anno.scr.bean.ServiceBean;
import com.carrotgarden.osgi.anno.scr.conv.PropertyType;
import com.carrotgarden.osgi.anno.scr.util.Util;

public class Builder {

  private static final Logger log = LoggerFactory.getLogger(Builder.class);

  /**
   * names of interfaces that should be excluded from
   *
   * <provide interface="..."/>
   */
  private final Set<String> unwantedServiceSet;

  public Builder(final Set<String> unwantedServiceSet) {
    this.unwantedServiceSet = unwantedServiceSet;
  }

  public AggregatorBean makeAggregator(final List<Class<?>> klazList) {

    final AggregatorBean aggregator = new AggregatorBean();

    for (final Class<?> klaz : klazList) {
      aggregator.componentList.add(makeComponent(klaz));
    }

    return aggregator;

  }

  /**
   * super class annotations are overridden by derived classes in the type
   * hierarchy
   */
  private ComponentBean makeComponent(final Class<?> klaz) {

    final ComponentBean bean = new ComponentBean();

    final List<Class<?>> typeList = Util.getInheritanceList(klaz);

    for (final Class<?> type : typeList) {

      applyServiceInferred(bean, type);

      applyPropertyEmbedded(bean, type);

      applyReference(bean, type);

      applyLifecycle(bean, type);

      // keep last
      applyComponent(bean, type);

    }

    finalizeProvidedServices(bean);

    return bean;

  }

  /**
   * collect life cycle annotations
   */
  private void applyLifecycle(final ComponentBean bean, final Class<?> type) {

    final Method[] methodArray = type.getDeclaredMethods();

    int countActivate = 0;
    int countDeactivate = 0;
    int countModified = 0;

    for (final Method method : methodArray) {

      final String methodName = method.getName();

      if (method.getAnnotation(Activate.class) != null) {
        bean.activate = methodName;
        countActivate++;
      }

      if (method.getAnnotation(Deactivate.class) != null) {
        bean.deactivate = methodName;
        countDeactivate++;
      }

      if (method.getAnnotation(Modified.class) != null) {
        bean.modified = methodName;
        countModified++;
      }

    }

    if (countActivate > 1) {
      throw new IllegalArgumentException(
          "duplicate @Activate in class : " + type);
    }

    if (countDeactivate > 1) {
      throw new IllegalArgumentException(
          "duplicate @Deactivate in class : " + type);
    }

    if (countModified > 1) {
      throw new IllegalArgumentException(
          "duplicate @Modified in class : " + type);
    }

  }

  /**
   * cleanup and discard unwanted service interfaces
   */
  private void finalizeProvidedServices(final ComponentBean bean) {

    if (bean.service.provideSet.isEmpty()) {
      bean.service = null;
      return;
    }

    if (unwantedServiceSet.isEmpty()) {
      return;
    }

    final Set<ProvideBean> provideSet = new TreeSet<ProvideBean>();

    for (final ProvideBean provide : bean.service.provideSet) {
      if (unwantedServiceSet.contains(provide.type)) {
        continue; // ignore
      } else {
        provideSet.add(provide);
      }
    }

    if (provideSet.isEmpty()) {
      bean.service = null;
    } else {
      bean.service.provideSet = provideSet;
    }

  }

  private void applyComponent(final ComponentBean component,
      final Class<?> type) {

    if (Util.hasComponentAnno(type)) {

      final Component anno = type.getAnnotation(Component.class);

      //

      final String name = anno.name();
      final String nameDefault = type.getName();
      component.name = Util.isValidText(name) ? name : nameDefault;

      component.enabled = anno.enabled();

      final String factory = anno.factory();
      component.factory = Util.isValidText(factory) ? factory : null;

      component.immediate = anno.immediate();

      component.policy = anno.configurationPolicy();

      //

      component.implementation.klaz = type.getName();

      component.service.servicefactory = anno.servicefactory();

      //

      applyPropertyKeyValue(component, anno, type);

      applyPropertyFileEntry(component, anno, type);

      //

      applyServiceDeclared(component, anno, type);

    }

  }

  private void applyPropertyKeyValue(final ComponentBean bean,
      final Component anno, final Class<?> klaz) {

    for (final String entry : anno.property()) {

      if (!Util.isValidText(entry)) {
        throw new IllegalArgumentException(
            "property must not be empty : " + klaz + " / " + anno);
      }

      if (!entry.contains("=")) {
        throw new IllegalArgumentException(
            "property must contain '=' : " + klaz + " / " + anno);
      }

      final int indexEquals = entry.indexOf("=");

      final String nameType = entry.substring(0, indexEquals);

      if (nameType.length() == 0) {
        throw new IllegalArgumentException(
            "property name must not be empty : " + klaz + " / "
                + anno);
      }

      final String name;
      final String type;

      if (nameType.contains(":")) {

        final int indexColon = nameType.indexOf(":");

        name = nameType.substring(0, indexColon);
        type = PropertyType.from(nameType.substring(indexColon + 1)).value;

        if (name.length() == 0) {
          throw new IllegalArgumentException(
              "property name must not be empty : " + klaz + " / "
                  + anno);
        }

      } else {
        name = nameType;
        type = PropertyType.STRING.value;
      }

      final String value = entry.substring(indexEquals + 1);

      final PropertyBean propBean = new PropertyBean();

      propBean.name = name;
      propBean.type = type;
      propBean.value = value;

      /** no duplicates */
      bean.propertySet.remove(propBean);
      bean.propertySet.add(propBean);

    }

  }

  private void applyPropertyFileEntry(final ComponentBean bean,
      final Component anno, final Class<?> type) {

    for (final String entry : anno.properties()) {

      if (!Util.isValidText(entry)) {
        throw new IllegalArgumentException(
            "property file entry must not be empty : " + type
                + " / " + anno);
      }

      final PropertyFileBean propBean = new PropertyFileBean();

      propBean.entry = entry;

      /** no duplicates */
      bean.propertyFileSet.remove(propBean);
      bean.propertyFileSet.add(propBean);

    }

  }

  /**
   * collect static final annotated property fields
   */
  private void applyPropertyEmbedded(final ComponentBean component,
      final Class<?> type) {

    if (!Util.hasPropertyAnno(type)) {
      return;
    }

    final Field[] fieldArray = type.getDeclaredFields();

    for (final Field field : fieldArray) {

      final Property anno = field.getAnnotation(Property.class);

      if (anno == null) {
        continue;
      }

      field.setAccessible(true);

      final String fieldName = field.getName();

      final int modifiers = field.getModifiers();

      if (!Modifier.isStatic(modifiers)) {
        throw new IllegalArgumentException(
            "property field must be static : " + fieldName);
      }

      if (!Modifier.isFinal(modifiers)) {
        throw new IllegalArgumentException(
            "property field must be final : " + fieldName);
      }

      if (!String.class.equals(field.getType())) {
        throw new IllegalArgumentException(
            "property field must be java.lang.String : "
                + fieldName + " / " + field.getType());
      }

      //

      final String name = Util.isValidText(anno.name()) ? anno.name()
          : fieldName;

      final String value;
      try {
        value = (String) field.get(null);
      } catch (final Exception e) {
        throw new IllegalArgumentException(
            "property annotated value is invalid : " + fieldName, e);
      }

      final PropertyBean bean = new PropertyBean();

      bean.name = name;
      bean.type = PropertyType.STRING.value;
      bean.value = value;

      /** override if any */
      component.propertySet.remove(bean);
      component.propertySet.add(bean);

    }

  }

  /**
   * override collected service interfaces with explicit "service=..." if
   * present
   * */
  private void applyServiceDeclared(final ComponentBean component,
      final Component anno, final Class<?> type) {

    final Class<?>[] serviceArray = anno.service();

    if (serviceArray == null || serviceArray.length == 0) {
      return;
    }

    final Set<ProvideBean> provideSet = component.service.provideSet;

    /** discard previously collected interfaces */
    provideSet.clear();

    for (final Class<?> service : serviceArray) {

      if (!service.isAssignableFrom(type)) {
        throw new IllegalArgumentException(
            "annotated service must also be implemented : "
                + service + " / " + type);
      }

      final ProvideBean bean = new ProvideBean();

      bean.type = service.getName();

      provideSet.add(bean);

    }

  }

  /**
   * collect all service interfaces
   */
  private void applyServiceInferred(final ComponentBean component,
      final Class<?> type) {

    final Class<?>[] ifaceArray = type.getInterfaces();

    if (ifaceArray.length == 0) {
      return;
    }

    final ServiceBean service = component.service;

    for (final Class<?> iface : ifaceArray) {

      final ProvideBean bean = new ProvideBean();
      bean.type = iface.getName();

      /** no duplicates */
      if (service.provideSet.contains(bean)) {
        continue;
      }

      service.provideSet.add(bean);

    }

  }

  /**
   * collect bind methods
   */
  private void applyReference(final ComponentBean component,
      final Class<?> type) {

    final Method[] methodArray = type.getDeclaredMethods();

    for (final Method method : methodArray) {

      final Reference anno = method.getAnnotation(Reference.class);

      if (anno == null) {
        continue;
      }

      final Class<?>[] paramArray = method.getParameterTypes();

      if (!Util.isValidBindParam(paramArray)) {
        throw new IllegalArgumentException("invalid reference : "
            + method);
      }

      final ReferenceBean bean = makeReference(type, method, anno);

      final Set<ReferenceBean> referenceSet = component.referenceSet;

      if (referenceSet.contains(bean)) {
        throw new IllegalArgumentException("duplicate reference : "
            + method);
      }

      referenceSet.add(bean);

    }

  }

  private ReferenceBean makeReference(final Class<?> type,
      final Method bindMethod, final Reference anno) {

    final ReferenceBean reference = new ReferenceBean();

    final String bindName = bindMethod.getName();

    final Class<?> bindType = Util.bindType(bindMethod);

    //

    reference.type = bindType.getName();

    final String target = anno.target();
    reference.target = Util.isValidText(target) ? target : null;

    /**
     * name must be unique in a component;
     *
     * use (interface type)/(target filter) combination
     */
    final String name = anno.name();
    final String nameType = bindType.getName();
    final String nameTarget = Util.isValidText(target) ? target : "*";
    final String nameDefault = nameType + "/" + nameTarget;
    reference.name = Util.isValidText(name) ? name : nameDefault;

    reference.cardinality = anno.cardinality();

    reference.policy = anno.policy();

    reference.bind = bindName;

    reference.unbind = Util.getUnbindName(bindName);

    //

    Util.assertBindUnbindMatch(type, bindType, reference.bind,
        reference.unbind);

    return reference;

  }

}
TOP

Related Classes of com.carrotgarden.osgi.anno.scr.make.Builder

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.