Package org.springframework.osgi.config

Source Code of org.springframework.osgi.config.AbstractReferenceDefinitionParser$ReferenceAttributesCallback

/*
* Copyright 2006-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.osgi.config;

import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanReferenceFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Conventions;
import org.springframework.core.enums.StaticLabeledEnumResolver;
import org.springframework.osgi.config.ParserUtils.AttributeCallback;
import org.springframework.osgi.service.importer.support.Cardinality;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Base class for parsing reference declarations. Contains common functionality
* such as adding listeners (and their custom methods), interfaces, cardinality
* and so on.
*
* <p/>
*
* <strong>Note:</strong> This parser also handles the cyclic injection between
* an importer and its listeners by breaking the chain by creating an adapter
* instead of the listener. The adapter will then do dependency lookup for the
* listener.
*
* @author Costin Leau
*
*/
abstract class AbstractReferenceDefinitionParser extends AbstractBeanDefinitionParser {

  /**
   * Attribute callback dealing with 'cardinality' attribute.
   *
   * @author Costin Leau
   */
  class ReferenceAttributesCallback implements AttributeCallback {

    /** global cardinality setting */
    public boolean isCardinalitySpecified = false;


    public boolean process(Element parent, Attr attribute, BeanDefinitionBuilder builder) {
      String name = attribute.getLocalName();
      String value = attribute.getValue();

      // make sure the attribute is
      if (CARDINALITY.equals(name)) {
        isCardinalitySpecified = true;
        builder.addPropertyValue(CARDINALITY_PROP, determineCardinality(value));
        return false;
      }

      else if (SERVICE_BEAN_NAME.equals(name)) {
        builder.addPropertyValue(SERVICE_BEAN_NAME_PROP, value);
        return false;
      }

      else if (INTERFACE.equals(name)) {
        builder.addPropertyValue(INTERFACES_PROP, value);
        return false;
      }

      else if (CONTEXT_CLASSLOADER.equals(name)) {
        // convert constant to upper case to let Spring do the
        // conversion
        String val = value.toUpperCase(Locale.ENGLISH).replace('-', '_');
        builder.addPropertyValue(CCL_PROP, val);
        return false;
      }

      return true;
    }
  };


  // Class properties
  private static final String LISTENERS_PROP = "listeners";

  private static final String CARDINALITY_PROP = "cardinality";

  private static final String SERVICE_BEAN_NAME_PROP = "serviceBeanName";

  private static final String INTERFACES_PROP = "interfaces";

  private static final String CCL_PROP = "contextClassLoader";

  private static final String TARGET_BEAN_NAME_PROP = "targetBeanName";

  private static final String TARGET_PROP = "target";

  // XML attributes/elements
  private static final String LISTENER = "listener";

  private static final String REF = "ref";

  private static final String INTERFACE = "interface";

  private static final String INTERFACES = "interfaces";

  private static final String CARDINALITY = "cardinality";

  private static final String ZERO = "0";

  private static final String SERVICE_BEAN_NAME = "bean-name";

  private static final String CONTEXT_CLASSLOADER = "context-class-loader";

  // document defaults
  protected OsgiDefaultsDefinition defaults = null;


  /**
   * Get OSGi defaults (in case they haven't been resolved).
   *
   * @param document
   * @return
   */
  private OsgiDefaultsDefinition resolveDefaults(Document document) {
    if (defaults == null) {
      defaults = ParserUtils.initOsgiDefaults(document);
    }
    return defaults;
  }

  protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();

    Class beanClass = getBeanClass(element);
    Assert.notNull(beanClass);

    if (beanClass != null) {
      builder.getRawBeanDefinition().setBeanClass(beanClass);
    }

    builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
    if (parserContext.isNested()) {
      // Inner bean definition must receive same scope as containing bean.
      builder.setScope(parserContext.getContainingBeanDefinition().getScope());
    }
    if (parserContext.isDefaultLazyInit()) {
      // Default-lazy-init applies to custom bean definitions as well.
      builder.setLazyInit(true);
    }
    doParse(element, parserContext, builder);

    // check whether the bean is mandatory (and if it is, make it top-level
    // bean)

    AbstractBeanDefinition def = builder.getBeanDefinition();

    if (parserContext.isNested()) {
      StringBuffer id = new StringBuffer();
      String value = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
      if (StringUtils.hasText(value)) {
        id.append(value);
        id.append(BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR);
      }

      id.append(parserContext.getReaderContext().generateBeanName(def));
      BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id.toString());
      BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
      return createBeanReferenceDefinition(id.toString());
    }

    return def;
  }

  private AbstractBeanDefinition createBeanReferenceDefinition(String beanName) {
    GenericBeanDefinition def = new GenericBeanDefinition();
    def.setBeanClass(BeanReferenceFactoryBean.class);
    MutablePropertyValues mpv = new MutablePropertyValues();
    mpv.addPropertyValue(TARGET_BEAN_NAME_PROP, beanName);
    def.setPropertyValues(mpv);
    return def;
  }

  protected void doParse(Element element, ParserContext context, BeanDefinitionBuilder builder) {
    if (defaults == null)
      resolveDefaults(element.getOwnerDocument());

    ReferenceAttributesCallback callback = new ReferenceAttributesCallback();

    parseAttributes(element, builder, new AttributeCallback[] { callback });

    if (!callback.isCardinalitySpecified) {
      applyDefaultCardinality(builder, defaults);
    }

    parseNestedElements(element, context, builder);

    handleNestedDefinition(element, context, builder);
  }

  /**
   * If the reference is a nested bean, make it a top-level bean if it's a
   * mandatory dependency. This is done so that the beans can be discovered at
   * startup and the appCtx can start waiting.
   *
   * @param element
   * @param context
   * @param builder
   */
  protected void handleNestedDefinition(Element element, ParserContext context, BeanDefinitionBuilder builder) {

  }

  /**
   * Allow subclasses to add their own callbacks.
   *
   * @param element
   * @param builder
   * @param callbacks
   */
  protected void parseAttributes(Element element, BeanDefinitionBuilder builder, AttributeCallback[] callbacks) {
    ParserUtils.parseCustomAttributes(element, builder, callbacks);
  }

  /**
   * Subclasses should override this method to provide the proper mandatory
   * cardinality option/string.
   *
   * @return mandatory cardinality as a string.
   */
  protected abstract String mandatoryCardinality();

  /**
   * Subclasses should overide this method to provide the proper optional
   * cardinality option/string.
   *
   * @return optional cardinality as a string
   */
  protected abstract String optionalCardinality();

  /**
   * Indicate the bean definition class for this element.
   *
   * @param element
   * @return
   */
  protected abstract Class getBeanClass(Element element);

  /**
   * Utility method declared for reusability. It maintains the
   * optional/mandatory option of the cardinality option and returns a
   * specialized (singular/multiple) cardinality string.
   *
   * @param value cardinality string
   * @return the specialized (singular/multiple) cardinality.
   */
  protected Object determineCardinality(String value) {
    return processCardinalityString((value.startsWith(ZERO) ? optionalCardinality() : mandatoryCardinality()));
  }

  /**
   * Since cardinality contains numbers and the constants name cannot start
   * with a number we have to do conversion of the name or of the string. the
   * latter is easier and quicker.
   *
   * @param value
   * @return
   */
  private Cardinality processCardinalityString(String value) {
    return (Cardinality) StaticLabeledEnumResolver.instance().getLabeledEnumByLabel(Cardinality.class,
      value.toUpperCase(Locale.ENGLISH));
  }

  /**
   * Apply default cardinality.
   *
   * @param builder
   * @param defaults
   */
  protected void applyDefaultCardinality(BeanDefinitionBuilder builder, OsgiDefaultsDefinition defaults) {
    builder.addPropertyValue(CARDINALITY_PROP, determineCardinality(defaults.getCardinality()));
  }

  /**
   * Parse nested elements. In case of a reference definition, this means
   * using the listeners.
   *
   *
   * @param element
   * @param context
   * @param builder
   */
  protected void parseNestedElements(Element element, ParserContext context, BeanDefinitionBuilder builder) {
    parseInterfaces(element, context, builder);
    parseListeners(element, context, builder);
  }

  /**
   * Parse interfaces.
   *
   * @param element
   * @param context
   * @param builder
   */
  protected void parseInterfaces(Element parent, ParserContext parserContext, BeanDefinitionBuilder builder) {

    Element element = DomUtils.getChildElementByTagName(parent, INTERFACES);
    if (element != null) {
      // check shortcut on the parent
      if (parent.hasAttribute(INTERFACE)) {
        parserContext.getReaderContext().error(
          "either 'interface' attribute or <intefaces> sub-element has be specified", parent);
      }
      Set interfaces = parserContext.getDelegate().parseSetElement(element, builder.getBeanDefinition());
      builder.addPropertyValue(INTERFACES_PROP, interfaces);
    }
  }

  /**
   * Parse listeners.
   *
   * @param element
   * @param context
   * @param builder
   */
  protected void parseListeners(Element element, ParserContext context, BeanDefinitionBuilder builder) {
    List listeners = DomUtils.getChildElementsByTagName(element, LISTENER);

    ManagedList listenersRef = new ManagedList();
    // loop on listeners
    for (Iterator iter = listeners.iterator(); iter.hasNext();) {
      Element listnr = (Element) iter.next();

      // wrapper target object
      Object target = null;

      // target bean name (in case of a reference)
      String targetName = null;

      // filter elements
      NodeList nl = listnr.getChildNodes();

      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element beanDef = (Element) node;

          // check inline ref
          if (listnr.hasAttribute(REF))
            context.getReaderContext().error(
              "nested bean declaration is not allowed if 'ref' attribute has been specified", beanDef);

          target = context.getDelegate().parsePropertySubElement(beanDef, builder.getBeanDefinition());

          // if this is a bean reference (nested <ref>), extract the name
          if (target instanceof RuntimeBeanReference) {
            targetName = ((RuntimeBeanReference) target).getBeanName();
          }
        }
      }

      // extract bind/unbind attributes from <osgi:listener>
      // Element
      MutablePropertyValues vals = new MutablePropertyValues();

      NamedNodeMap attrs = listnr.getAttributes();
      for (int x = 0; x < attrs.getLength(); x++) {
        Attr attribute = (Attr) attrs.item(x);
        String name = attribute.getLocalName();

        // extract ref value
        if (REF.equals(name))
          targetName = attribute.getValue();
        else
          vals.addPropertyValue(Conventions.attributeNameToPropertyName(name), attribute.getValue());
      }

      // create serviceListener adapter
      RootBeanDefinition wrapperDef = new RootBeanDefinition(OsgiServiceLifecycleListenerAdapter.class);

      // set the target name (if we have one)
      if (targetName != null)
        vals.addPropertyValue(TARGET_BEAN_NAME_PROP, targetName);
      // else set the actual target
      else
        vals.addPropertyValue(TARGET_PROP, target);

      wrapperDef.setPropertyValues(vals);
      // add listener to list
      listenersRef.add(wrapperDef);
    }

    builder.addPropertyValue(LISTENERS_PROP, listenersRef);
  }
}
TOP

Related Classes of org.springframework.osgi.config.AbstractReferenceDefinitionParser$ReferenceAttributesCallback

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.