Package org.infinispan.config.parsing

Source Code of org.infinispan.config.parsing.AutomatedXmlConfigurationParserImpl

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.config.parsing;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.infinispan.config.AbstractConfigurationBean;
import org.infinispan.config.Configuration;
import org.infinispan.config.ConfigurationAttribute;
import org.infinispan.config.ConfigurationElement;
import org.infinispan.config.ConfigurationElements;
import org.infinispan.config.ConfigurationException;
import org.infinispan.config.ConfigurationProperties;
import org.infinispan.config.ConfigurationProperty;
import org.infinispan.config.DuplicateCacheNameException;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.util.ClassFinder;
import org.infinispan.util.FileLookup;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* XML configuration parser that uses reflection API and annotations to read Infinispan configuration files.
*
* @author Vladimir Blagojevic
* @since 4.0
*/
public class AutomatedXmlConfigurationParserImpl extends XmlParserBase implements XmlConfigurationParser {  
   private static  List<Class<?>> CONFIG_BEANS =null;
  
   static {
      String path = ClassFinder.PATH;
      try {        
         CONFIG_BEANS = ClassFinder.isAssignableFrom(ClassFinder.infinispanClasses(),AbstractConfigurationBean.class);
      } catch (Exception e) {
         throw new ConfigurationException(
                  "Exception while searching for Infinispan configuration beans, path is " + path,
                  e);
      }
      if (CONFIG_BEANS == null || CONFIG_BEANS.isEmpty())
         throw new ConfigurationException("Could not find Infinispan configuration beans, path is "
                  + path);
   }

   // this parser will need to be initialized.
   boolean initialized = false;

   // the root element, representing the <infinispan /> tag
   Element rootElement;

   GlobalConfiguration gc;
   Map<String, Configuration> namedCaches;

   /**
    * Constructs a new parser
    */
   public AutomatedXmlConfigurationParserImpl() {}

   /**
    * Constructs a parser and initializes it with the file name passed in, by calling {@link #initialize(String)}.
    *
    * @param fileName file name to initialize the parser with
    * @throws IOException if there is a problem reading or locating the file.
    */
   public AutomatedXmlConfigurationParserImpl(String fileName) throws IOException {
      initialize(fileName);
   }

   /**
    * Constructs a parser and initializes it with the input stream passed in, by calling {@link
    * #initialize(InputStream)}.
    *
    * @param inputStream input stream to initialize the parser with
    * @throws IOException if there is a problem reading the stream
    */
   public AutomatedXmlConfigurationParserImpl(InputStream inputStream) throws IOException {
      initialize(inputStream);
   }

   public void initialize(String fileName) throws IOException {
      if (fileName == null) throw new NullPointerException("File name cannot be null!");
      FileLookup fileLookup = new FileLookup();
      InputStream is = fileLookup.lookupFile(fileName);
      if (is == null)
         throw new FileNotFoundException("File " + fileName + " could not be found, either on the classpath or on the file system!");
      initialize(is);
   }

   public void initialize(InputStream inputStream) throws IOException {
      if (inputStream == null) throw new NullPointerException("Input stream cannot be null!");
      initialized = true;
      rootElement = new RootElementBuilder().readRoot(inputStream);
   }

   public Configuration parseDefaultConfiguration() throws ConfigurationException {
      assertInitialized();
      if (gc == null) {
         Element defaultElement = getSingleElementInCoreNS("default", rootElement);
         // there may not be a <default /> element!
         if (defaultElement == null) {
            return new Configuration();
         } else {
            defaultElement.normalize();           
            AbstractConfigurationBean bean = findAndInstantiateBean(defaultElement);
            visitElement(defaultElement, bean);
            return (Configuration) bean;
         }
      } else {
         return gc.getDefaultConfiguration();
      }
   }

   public Map<String, Configuration> parseNamedConfigurations() throws ConfigurationException {
      assertInitialized();
      // there may not be any namedCache elements!
      if (namedCaches == null) {
         Set<Element> elements = getAllElementsInCoreNS("namedCache", rootElement);
         if (elements.isEmpty()) return Collections.emptyMap();
         namedCaches = new HashMap<String, Configuration>(elements.size(), 1.0f);
         for (Element e : elements) {
            String configurationName = getAttributeValue(e, "name");
            if (namedCaches.containsKey(configurationName)) {
               namedCaches = null;
               throw new DuplicateCacheNameException("Named cache " + configurationName + " is declared more than once!");
            }
            try {
               AbstractConfigurationBean bean = findAndInstantiateBean(e);
               visitElement(e, bean);              
               namedCaches.put(configurationName,(Configuration) bean);
            } catch (ConfigurationException ce) {
               throw new ConfigurationException("Problems configuring named cache '" + configurationName + "'", ce);
            }
         }
      }

      return namedCaches;
   }

   public GlobalConfiguration parseGlobalConfiguration() {
      assertInitialized();
      if (gc == null) {
         Configuration defaultConfiguration = parseDefaultConfiguration();
         Element globalElement = getSingleElementInCoreNS("global", rootElement);        
         AbstractConfigurationBean bean = findAndInstantiateBean(globalElement);
         visitElement(globalElement, bean);
         gc = (GlobalConfiguration) bean;
         gc.setDefaultConfiguration(defaultConfiguration);
      }
      return gc;
   }
  
   AbstractConfigurationBean findAndInstantiateBean(List<Class<?>> b, Element e) throws ConfigurationException {
      String name = e.getTagName();
      String parentName = ((Element) e.getParentNode()).getTagName();
      if (parentName.equals("namedCache"))
         parentName = "default";
      for (Class<?> clazz : b) {
         ConfigurationElements elements = clazz.getAnnotation(ConfigurationElements.class);
         try {
            if (elements != null) {
               for (ConfigurationElement ce : elements.elements()) {
                  if (ce.name().equals(name) && ce.parent().equals(parentName)) {
                     return (AbstractConfigurationBean) clazz.newInstance();
                  }
               }
            } else {
               ConfigurationElement ce = clazz.getAnnotation(ConfigurationElement.class);
               if (ce != null && (ce.name().equals(name) && ce.parent().equals(parentName))) {
                  return (AbstractConfigurationBean) clazz.newInstance();
               }
            }
         } catch (Exception e1) {
            throw new ConfigurationException("Could not instantiate class " + clazz, e1);
         }
      }
      return null;
   }
  
   AbstractConfigurationBean findAndInstantiateBean(Element e) throws ConfigurationException {
      return findAndInstantiateBean(CONFIG_BEANS,e);
   }
  
   private ConfigurationElement findConfigurationElement(Element e, Class<?> bean) {
      ConfigurationElement result = null;
      ConfigurationElement ces[] = null;
      ConfigurationElements configurationElements = bean.getAnnotation(ConfigurationElements.class);
      ConfigurationElement configurationElement = bean.getAnnotation(ConfigurationElement.class);
      String parentName = ((Element)e.getParentNode()).getTagName();
      if(parentName.equals("namedCache"))
         parentName = "default";

      if (configurationElement != null) {
         ces = new ConfigurationElement[] { configurationElement };
      }
      if (configurationElements != null) {
         ces = configurationElements.elements();
      }
      if (ces != null) {
         for (ConfigurationElement el : ces) {
            if (el.name().equals(e.getNodeName()) && el.parent().equals(parentName)) {
               result = el;
               break;
            }
         }
      }
      return result;
   }
  
   private Class<? extends ConfigurationElementReader> customReader(Element e, Class<?> bean) {
      Class<? extends ConfigurationElementReader> clazz = null;
      ConfigurationElement ce = findConfigurationElement(e, bean);
      if (ce == null) {
         for (Class<?> beanClass : CONFIG_BEANS) {
            ce = findConfigurationElement(e, beanClass);
            if (ce != null)
               break;
         }
      }
      if (ce != null && !ce.customReader().equals(ConfigurationElementReader.class)) {
         clazz = ce.customReader();
      }
      return clazz;
   }
  
   void visitElement(Element e, AbstractConfigurationBean bean) throws ConfigurationException {    
      Class<? extends ConfigurationElementReader> readerClass = customReader(e, bean.getClass());     
      //has custom reader? if so, use it
      if (readerClass != null) {        
         ConfigurationElementReader reader = null;
         try {           
            reader = readerClass.newInstance();
            reader.setParser(this);
            reader.process(e, bean);
         } catch (Exception e1) {
            throw new ConfigurationException("Exception while using custom reader " + readerClass
                     + " for element " + e.getNodeName(), e1);
         }
      } else {
         //normal processing
         visitElementDefault(e, bean);
      }
   }

   void visitElementDefault(Element e, AbstractConfigurationBean bean) {
      for (Method m : bean.getClass().getMethods()) {
         boolean setter = m.getName().startsWith("set") && m.getParameterTypes().length == 1;
         if (setter) {
            reflectAndInvokeAttribute(bean, m, e);
            reflectAndInvokeProperties(bean, m, e);             
         }
      }
      NodeList nodeList = e.getChildNodes();
      for (int numChildren = nodeList.getLength(), i = 0; i < numChildren; i++) {
         Node child = nodeList.item(i);
         if (child instanceof Element) {              
            // recursive step
            visitElement((Element) child, bean);
         }
      }
   }
  
   void reflectAndInvokeAttribute(AbstractConfigurationBean bean, Method m, Element node) {    
      Class<?> parameterType = m.getParameterTypes()[0];
      // is there a ConfigurationAttribute matching the current node iterated?
      ConfigurationAttribute a = m.getAnnotation(ConfigurationAttribute.class);
      boolean matchedAttributeToSetter = a != null && a.containingElement().equals(node.getNodeName());
      boolean isConfigBean = AbstractConfigurationBean.class.isAssignableFrom(parameterType);
      if (matchedAttributeToSetter) {
         String attValue = getAttributeValue(node, a.name());
         Object methodAttributeValue = null;
         if (attValue != null && attValue.length() > 0) {
            PropertyEditor editor = PropertyEditorManager.findEditor(parameterType);
            if (editor == null) {
               throw new ConfigurationException("Could not find property editor, type="
                        + parameterType + ",method=" + m + ",attribute=" + a.name());
            }
            editor.setAsText(attValue);
            methodAttributeValue = editor.getValue();
         } else if (a.defaultValue().length() > 0) {
            methodAttributeValue = a.defaultValue();
         }
         if (methodAttributeValue != null) {
            try {
               m.invoke(bean, methodAttributeValue);
            } catch (Exception ae) {
               throw new ConfigurationException("Illegal attribute value " + attValue + ",type="
                        + parameterType + ",method=" + m + ",attribute=" + a.name(), ae);
            }
         }
      } else if (isConfigBean) {
         AbstractConfigurationBean childBean = findAndInstantiateBean(node);
         boolean foundMatchingChild = childBean != null
                  && !bean.getClass().equals(childBean.getClass())
                  && parameterType.isInstance(childBean);
         if (foundMatchingChild) { 
            //recurse into child
            visitElement(node,childBean);
            try {
               //and finally invoke setter on father bean
               m.invoke(bean, childBean);
            } catch (Exception ae) {
               throw new ConfigurationException("Illegal bean value " + childBean + ",type="
                        + parameterType + ", method=" + m, ae);
            }
         }
      }
   }
  
   boolean reflectAndInvokeProperties(AbstractConfigurationBean bean, Method m, Element node){
      Class<?> parameterType = m.getParameterTypes()[0];
     
      //how about ConfigurationProperties or ConfigurationProperty matching the current node iterated?
      ConfigurationProperty[] cprops = null;
      ConfigurationProperties cp = m.getAnnotation(ConfigurationProperties.class);              
      if (cp != null) {
         cprops = cp.elements();
      } else {
         ConfigurationProperty p = null;
         p = m.getAnnotation(ConfigurationProperty.class);
         if (p != null) {
            cprops = new ConfigurationProperty[] { p };
         }
      }
      boolean matchedPropertyToSetter  = cprops != null && cprops.length >0;
      if(matchedPropertyToSetter){
         String parentElement = cprops[0].parentElement();        
         if(parentElement.equals(node.getParentNode().getNodeName())){          
            if(node.getNodeName().equals("property")){
               Properties props = XmlConfigHelper.extractProperties((Element) node.getParentNode());
               //special case where setter argument is Properties object
               if (Properties.class.isAssignableFrom(parameterType)) {
                  try {
                     m.invoke(bean, props);
                  } catch (Exception ae) {
                     throw new ConfigurationException("Illegal props " + props + ",type="
                              + parameterType + ", method=" + m, ae);
                  }
               } else {
                  //regular case where setter argument are primitives and String
                  //follow bean conventions
                  XmlConfigHelper.setValues(bean, props, false, true);
               }
               //we assume that all other siblings of <property> element are also <property> elements
               // there no need to iterate them as we have extracted them all using the  method above
               // therefore skip them by returning
               return true;
            }
         }        
      }
      return false;
   }
  
   private void assertInitialized() {
      if (!initialized)
         throw new ConfigurationException("Parser not initialized.  Please invoke initialize() first, or use a constructor that initializes the parser.");
   }
}
TOP

Related Classes of org.infinispan.config.parsing.AutomatedXmlConfigurationParserImpl

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.