Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.config.providers » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002-2006 by OpenSymphony
    3    * All rights reserved.
    4    */
    5   package com.opensymphony.xwork2.config.providers;
    6   
    7   import java.io.IOException;
    8   import java.io.InputStream;
    9   import java.io.File;
   10   import java.lang.reflect.Modifier;
   11   import java.net.URL;
   12   import java.net.URLClassLoader;
   13   import java.net.URISyntaxException;
   14   import java.util.ArrayList;
   15   import java.util.Collections;
   16   import java.util.HashMap;
   17   import java.util.HashSet;
   18   import java.util.Iterator;
   19   import java.util.LinkedHashMap;
   20   import java.util.List;
   21   import java.util.Map;
   22   import java.util.Set;
   23   import java.util.Vector;
   24   
   25   import org.w3c.dom.Document;
   26   import org.w3c.dom.Element;
   27   import org.w3c.dom.Node;
   28   import org.w3c.dom.NodeList;
   29   import org.xml.sax.InputSource;
   30   
   31   import com.opensymphony.xwork2.Action;
   32   import com.opensymphony.xwork2.ActionSupport;
   33   import com.opensymphony.xwork2.ObjectFactory;
   34   import com.opensymphony.xwork2.XWorkException;
   35   import com.opensymphony.xwork2.config.Configuration;
   36   import com.opensymphony.xwork2.config.ConfigurationException;
   37   import com.opensymphony.xwork2.config.ConfigurationProvider;
   38   import com.opensymphony.xwork2.config.ConfigurationUtil;
   39   import com.opensymphony.xwork2.config.entities.ActionConfig;
   40   import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
   41   import com.opensymphony.xwork2.config.entities.InterceptorConfig;
   42   import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
   43   import com.opensymphony.xwork2.config.entities.PackageConfig;
   44   import com.opensymphony.xwork2.config.entities.ResultConfig;
   45   import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
   46   import com.opensymphony.xwork2.config.impl.LocatableFactory;
   47   import com.opensymphony.xwork2.inject.Container;
   48   import com.opensymphony.xwork2.inject.ContainerBuilder;
   49   import com.opensymphony.xwork2.inject.Inject;
   50   import com.opensymphony.xwork2.inject.Scope;
   51   import com.opensymphony.xwork2.util.ClassLoaderUtil;
   52   import com.opensymphony.xwork2.util.ClassPathFinder;
   53   import com.opensymphony.xwork2.util.DomHelper;
   54   import com.opensymphony.xwork2.util.FileManager;
   55   import com.opensymphony.xwork2.util.TextUtils;
   56   import com.opensymphony.xwork2.util.location.LocatableProperties;
   57   import com.opensymphony.xwork2.util.location.Location;
   58   import com.opensymphony.xwork2.util.location.LocationUtils;
   59   import com.opensymphony.xwork2.util.logging.Logger;
   60   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   61   
   62   
   63   /**
   64    * Looks in the classpath for an XML file, "xwork.xml" by default,
   65    * and uses it for the XWork configuration.
   66    *
   67    * @author tmjee
   68    * @author Rainer Hermanns
   69    * @author Neo
   70    * @version $Revision: 1756 $
   71    */
   72   public class XmlConfigurationProvider implements ConfigurationProvider {
   73   
   74       private static final Logger LOG = LoggerFactory.getLogger(XmlConfigurationProvider.class);
   75   
   76       private List<Document> documents;
   77       private Set<String> includedFileNames;
   78       private String configFileName;
   79       private ObjectFactory objectFactory;
   80   
   81       private Set<String> loadedFileUrls = new HashSet<String>();
   82       private boolean errorIfMissing;
   83       private Map<String, String> dtdMappings;
   84       private Configuration configuration;
   85       private boolean throwExceptionOnDuplicateBeans = true;
   86       
   87       public XmlConfigurationProvider() {
   88           this("xwork.xml", true);
   89       }
   90   
   91       public XmlConfigurationProvider(String filename) {
   92           this(filename, true);
   93       }
   94   
   95       public XmlConfigurationProvider(String filename, boolean errorIfMissing) {
   96           this.configFileName = filename;
   97           this.errorIfMissing = errorIfMissing;
   98   
   99           Map<String, String> mappings = new HashMap<String, String>();
  100           mappings.put("-//OpenSymphony Group//XWork 2.0//EN", "xwork-2.0.dtd");
  101           mappings.put("-//OpenSymphony Group//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
  102           mappings.put("-//OpenSymphony Group//XWork 1.1//EN", "xwork-1.1.dtd");
  103           mappings.put("-//OpenSymphony Group//XWork 1.0//EN", "xwork-1.0.dtd");
  104           setDtdMappings(mappings);
  105       }
  106       
  107       public void setThrowExceptionOnDuplicateBeans(boolean val) {
  108           this.throwExceptionOnDuplicateBeans = val;
  109       }
  110   
  111       public void setDtdMappings(Map<String, String> mappings) {
  112           this.dtdMappings = Collections.unmodifiableMap(mappings);
  113       }
  114   
  115       @Inject
  116       public void setObjectFactory(ObjectFactory objectFactory) {
  117           this.objectFactory = objectFactory;
  118       }
  119   
  120       /**
  121        * Returns an unmodifiable map of DTD mappings
  122        */
  123       public Map<String, String> getDtdMappings() {
  124           return dtdMappings;
  125       }
  126   
  127       public void init(Configuration configuration) {
  128           this.configuration = configuration;
  129           this.includedFileNames = configuration.getLoadedFileNames();
  130           loadDocuments(configFileName);
  131       }
  132   
  133       public void destroy() {
  134       }
  135   
  136       public boolean equals(Object o) {
  137           if (this == o) {
  138               return true;
  139           }
  140   
  141           if (!(o instanceof XmlConfigurationProvider)) {
  142               return false;
  143           }
  144   
  145           final XmlConfigurationProvider xmlConfigurationProvider = (XmlConfigurationProvider) o;
  146   
  147           if ((configFileName != null) ? (!configFileName.equals(xmlConfigurationProvider.configFileName)) : (xmlConfigurationProvider.configFileName != null)) {
  148               return false;
  149           }
  150   
  151           return true;
  152       }
  153   
  154       public int hashCode() {
  155           return ((configFileName != null) ? configFileName.hashCode() : 0);
  156       }
  157   
  158       private void loadDocuments(String configFileName) {
  159           try {
  160               loadedFileUrls.clear();
  161               documents = loadConfigurationFiles(configFileName, null);
  162           } catch (ConfigurationException e) {
  163               throw e;
  164           } catch (Exception e) {
  165               throw new ConfigurationException("Error loading configuration file " + configFileName, e);
  166           }
  167       }
  168   
  169       public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
  170           LOG.info("Parsing configuration file [" + configFileName + "]");
  171           Map<String, Node> loadedBeans = new HashMap<String, Node>();
  172           for (Document doc : documents) {
  173               Element rootElement = doc.getDocumentElement();
  174               NodeList children = rootElement.getChildNodes();
  175               int childSize = children.getLength();
  176   
  177               for (int i = 0; i < childSize; i++) {
  178                   Node childNode = children.item(i);
  179   
  180                   if (childNode instanceof Element) {
  181                       Element child = (Element) childNode;
  182   
  183                       final String nodeName = child.getNodeName();
  184   
  185                       if (nodeName.equals("bean")) {
  186                           String type = child.getAttribute("type");
  187                           String name = child.getAttribute("name");
  188                           String impl = child.getAttribute("class");
  189                           String onlyStatic = child.getAttribute("static");
  190                           String scopeStr = child.getAttribute("scope");
  191                           boolean optional = "true".equals(child.getAttribute("optional"));
  192                           Scope scope = Scope.SINGLETON;
  193                           if ("default".equals(scopeStr)) {
  194                               scope = Scope.DEFAULT;
  195                           } else if ("request".equals(scopeStr)) {
  196                               scope = Scope.REQUEST;
  197                           } else if ("session".equals(scopeStr)) {
  198                               scope = Scope.SESSION;
  199                           } else if ("singleton".equals(scopeStr)) {
  200                               scope = Scope.SINGLETON;
  201                           } else if ("thread".equals(scopeStr)) {
  202                               scope = Scope.THREAD;
  203                           }
  204   
  205                           if (!TextUtils.stringSet(name)) {
  206                               name = Container.DEFAULT_NAME;
  207                           }
  208   
  209                           try {
  210                               Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
  211                               Class ctype = cimpl;
  212                               if (TextUtils.stringSet(type)) {
  213                                   ctype = ClassLoaderUtil.loadClass(type, getClass());
  214                               }
  215                               if ("true".equals(onlyStatic)) {
  216                                   // Force loading of class to detect no class def found exceptions
  217                                   cimpl.getDeclaredClasses();
  218                                   containerBuilder.injectStatics(cimpl);
  219                               } else {
  220                                   if (containerBuilder.contains(ctype, name)) {
  221                                       Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
  222                                       if (throwExceptionOnDuplicateBeans) {
  223                                           throw new ConfigurationException("Bean type " + ctype + " with the name " +
  224                                                   name + " has already been loaded by " + loc, child);
  225                                       } 
  226                                   }
  227   
  228                                   // Force loading of class to detect no class def found exceptions
  229                                   cimpl.getDeclaredConstructors();
  230   
  231                                   if (LOG.isDebugEnabled()) {
  232                                       LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
  233                                   }
  234                                   containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
  235                               }
  236                               loadedBeans.put(ctype.getName() + name, child);
  237                           } catch (Throwable ex) {
  238                               if (!optional) {
  239                                   throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
  240                               } else {
  241                                   LOG.debug("Unable to load optional class: " + ex);
  242                               }
  243                           }
  244                       } else if (nodeName.equals("constant")) {
  245                           String name = child.getAttribute("name");
  246                           String value = child.getAttribute("value");
  247                           props.setProperty(name, value, childNode);
  248                       }
  249                   }
  250               }
  251           }
  252       }
  253   
  254       public void loadPackages() throws ConfigurationException {
  255           List<Element> reloads = new ArrayList<Element>();
  256           for (Document doc : documents) {
  257               Element rootElement = doc.getDocumentElement();
  258               NodeList children = rootElement.getChildNodes();
  259               int childSize = children.getLength();
  260   
  261               for (int i = 0; i < childSize; i++) {
  262                   Node childNode = children.item(i);
  263   
  264                   if (childNode instanceof Element) {
  265                       Element child = (Element) childNode;
  266   
  267                       final String nodeName = child.getNodeName();
  268   
  269                       if (nodeName.equals("package")) {
  270                           PackageConfig cfg = addPackage(child);
  271                           if (cfg.isNeedsRefresh()) {
  272                               reloads.add(child);
  273                           }
  274                       }
  275                   }
  276               }
  277               loadExtraConfiguration(doc);
  278           }
  279   
  280           if (reloads.size() > 0) {
  281               reloadRequiredPackages(reloads);
  282           }
  283   
  284           for (Document doc : documents) {
  285               loadExtraConfiguration(doc);
  286           }
  287   
  288           documents.clear();
  289           configuration = null;
  290       }
  291   
  292       private void reloadRequiredPackages(List<Element> reloads) {
  293           if (reloads.size() > 0) {
  294               List<Element> result = new ArrayList<Element>();
  295               for (Element pkg : reloads) {
  296                   PackageConfig cfg = addPackage(pkg);
  297                   if (cfg.isNeedsRefresh()) {
  298                       result.add(pkg);
  299                   }
  300               }
  301               if ((result.size() > 0) && (result.size() != reloads.size())) {
  302                   reloadRequiredPackages(result);
  303                   return;
  304               }
  305   
  306               // Print out error messages for all misconfigured inheritence packages
  307               if (result.size() > 0) {
  308                   for (Element rp : result) {
  309                       String parent = rp.getAttribute("extends");
  310                       if (parent != null) {
  311                           List parents = ConfigurationUtil.buildParentsFromString(configuration, parent);
  312                           if (parents != null && parents.size() <= 0) {
  313                               LOG.error("Unable to find parent packages " + parent);
  314                           }
  315                       }
  316                   }
  317               }
  318           }
  319       }
  320   
  321       /**
  322        * Tells whether the ConfigurationProvider should reload its configuration. This method should only be called
  323        * if ConfigurationManager.isReloadingConfigs() is true.
  324        *
  325        * @return true if the file has been changed since the last time we read it
  326        */
  327       public boolean needsReload() {
  328   
  329           for (String url : loadedFileUrls) {
  330               if (FileManager.fileNeedsReloading(url)) {
  331                   return true;
  332               }
  333           }
  334           return false;
  335       }
  336   
  337       protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException {
  338           String name = actionElement.getAttribute("name");
  339           String className = actionElement.getAttribute("class");
  340           String methodName = actionElement.getAttribute("method");
  341           Location location = DomHelper.getLocationObject(actionElement);
  342   
  343           if (location == null) {
  344               LOG.warn("location null for " + className);
  345           }
  346           //methodName should be null if it's not set
  347           methodName = (methodName.trim().length() > 0) ? methodName.trim() : null;
  348   
  349           // if there isnt a class name specified for an <action/> then try to
  350           // use the default-class-ref from the <package/>
  351           if (!TextUtils.stringSet(className)) {
  352               // if there is a package default-class-ref use that, otherwise use action support
  353              /* if (TextUtils.stringSet(packageContext.getDefaultClassRef())) {
  354                   className = packageContext.getDefaultClassRef();
  355               } else {
  356                   className = ActionSupport.class.getName();
  357               }*/
  358           
  359           } else {
  360           	if (!verifyAction(className, name, location)) {
  361                   return;
  362               }
  363           }
  364   
  365           
  366   
  367           Map results;
  368           try {
  369               results = buildResults(actionElement, packageContext);
  370           } catch (ConfigurationException e) {
  371               throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement);
  372           }
  373   
  374           List interceptorList = buildInterceptorList(actionElement, packageContext);
  375   
  376           List exceptionMappings = buildExceptionMappings(actionElement, packageContext);
  377   
  378           ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className)
  379                   .methodName(methodName)
  380                   .addResultConfigs(results)
  381                   .addInterceptors(interceptorList)
  382                   .addExceptionMappings(exceptionMappings)
  383                   .addParams(XmlHelper.getParams(actionElement))
  384                   .location(location)
  385                   .build();
  386           packageContext.addActionConfig(name, actionConfig);
  387   
  388           if (LOG.isDebugEnabled()) {
  389               LOG.debug("Loaded " + (TextUtils.stringSet(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig);
  390           }
  391       }
  392   
  393       protected boolean verifyAction(String className, String name, Location loc) {
  394           if (className.indexOf('{') > -1) {
  395               if (LOG.isDebugEnabled()) {
  396                   LOG.debug("Action class [" + className + "] contains a wildcard " +
  397                           "replacement value, so it can't be verified");
  398               }
  399               return true;
  400           }
  401           try {
  402               Class clazz = objectFactory.getClassInstance(className);
  403               if (objectFactory.isNoArgConstructorRequired()) {
  404                   if (!Modifier.isPublic(clazz.getModifiers())) {
  405                       throw new ConfigurationException("Action class [" + className + "] is not public", loc);
  406                   }
  407                   clazz.getConstructor(new Class[]{});
  408               }
  409           } catch (ClassNotFoundException e) {
  410               throw new ConfigurationException("Action class [" + className + "] not found", loc);
  411           } catch (NoSuchMethodException e) {
  412               throw new ConfigurationException("Action class [" + className + "] does not have a public no-arg constructor", e, loc);
  413   
  414               // Probably not a big deal, like request or session-scoped Spring 2 beans that need a real request
  415           } catch (RuntimeException ex) {
  416               LOG.info("Unable to verify action class [" + className + "] exists at initialization");
  417               if (LOG.isDebugEnabled()) {
  418                   LOG.debug("Action verification cause", ex);
  419               }
  420   
  421               // Default to failing fast
  422           } catch (Exception ex) {
  423               throw new ConfigurationException(ex, loc);
  424           }
  425           return true;
  426       }
  427   
  428       /**
  429        * Create a PackageConfig from an XML element representing it.
  430        */
  431       protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
  432           PackageConfig.Builder newPackage = buildPackageContext(packageElement);
  433   
  434           if (newPackage.isNeedsRefresh()) {
  435               return newPackage.build();
  436           }
  437   
  438           if (LOG.isDebugEnabled()) {
  439               LOG.debug("Loaded " + newPackage);
  440           }
  441   
  442           // add result types (and default result) to this package
  443           addResultTypes(newPackage, packageElement);
  444   
  445           // load the interceptors and interceptor stacks for this package
  446           loadInterceptors(newPackage, packageElement);
  447   
  448           // load the default interceptor reference for this package
  449           loadDefaultInterceptorRef(newPackage, packageElement);
  450   
  451           // load the default class ref for this package
  452           loadDefaultClassRef(newPackage, packageElement);
  453   
  454           // load the global result list for this package
  455           loadGlobalResults(newPackage, packageElement);
  456   
  457           // load the global exception handler list for this package
  458           loadGobalExceptionMappings(newPackage, packageElement);
  459   
  460           // get actions
  461           NodeList actionList = packageElement.getElementsByTagName("action");
  462   
  463           for (int i = 0; i < actionList.getLength(); i++) {
  464               Element actionElement = (Element) actionList.item(i);
  465               addAction(actionElement, newPackage);
  466           }
  467   
  468           // load the default action reference for this package
  469           loadDefaultActionRef(newPackage, packageElement);
  470   
  471           PackageConfig cfg = newPackage.build();
  472           configuration.addPackageConfig(cfg.getName(), cfg);
  473           return cfg;
  474       }
  475   
  476       protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
  477           NodeList resultTypeList = element.getElementsByTagName("result-type");
  478   
  479           for (int i = 0; i < resultTypeList.getLength(); i++) {
  480               Element resultTypeElement = (Element) resultTypeList.item(i);
  481               String name = resultTypeElement.getAttribute("name");
  482               String className = resultTypeElement.getAttribute("class");
  483               String def = resultTypeElement.getAttribute("default");
  484   
  485               Location loc = DomHelper.getLocationObject(resultTypeElement);
  486   
  487               Class clazz = verifyResultType(className, loc);
  488               if (clazz != null) {
  489                   String paramName = null;
  490                   try {
  491                       paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
  492                   }
  493                   catch (Throwable t) {
  494                       // if we get here, the result type doesn't have a default param defined.
  495                   }
  496                   ResultTypeConfig.Builder resultType = new ResultTypeConfig.Builder(name, className).defaultResultParam(paramName)
  497                           .location(DomHelper.getLocationObject(resultTypeElement));
  498   
  499                   Map params = XmlHelper.getParams(resultTypeElement);
  500   
  501                   if (!params.isEmpty()) {
  502                       resultType.addParams(params);
  503                   }
  504                   packageContext.addResultTypeConfig(resultType.build());
  505   
  506                   // set the default result type
  507                   if ("true".equals(def)) {
  508                       packageContext.defaultResultType(name);
  509                   }
  510               }
  511           }
  512       }
  513   
  514       protected Class verifyResultType(String className, Location loc) {
  515           try {
  516               return objectFactory.getClassInstance(className);
  517           } catch (ClassNotFoundException e) {
  518               LOG.warn("Result class [" + className + "] doesn't exist (ClassNotFoundException) at " +
  519                       loc.toString() + ", ignoring", e);
  520           } catch (NoClassDefFoundError e) {
  521               LOG.warn("Result class [" + className + "] doesn't exist (NoClassDefFoundError) at " +
  522                       loc.toString() + ", ignoring", e);
  523           }
  524   
  525           return null;
  526       }
  527   
  528       protected List buildInterceptorList(Element element, PackageConfig.Builder context) throws ConfigurationException {
  529           List interceptorList = new ArrayList();
  530           NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");
  531   
  532           for (int i = 0; i < interceptorRefList.getLength(); i++) {
  533               Element interceptorRefElement = (Element) interceptorRefList.item(i);
  534   
  535               if (interceptorRefElement.getParentNode().equals(element) || interceptorRefElement.getParentNode().getNodeName().equals(element.getNodeName())) {
  536                   List interceptors = lookupInterceptorReference(context, interceptorRefElement);
  537                   interceptorList.addAll(interceptors);
  538               }
  539           }
  540   
  541           return interceptorList;
  542       }
  543   
  544       /**
  545        * This method builds a package context by looking for the parents of this new package.
  546        * <p/>
  547        * If no parents are found, it will return a root package.
  548        */
  549       protected PackageConfig.Builder buildPackageContext(Element packageElement) {
  550           String parent = packageElement.getAttribute("extends");
  551           String abstractVal = packageElement.getAttribute("abstract");
  552           boolean isAbstract = Boolean.valueOf(abstractVal).booleanValue();
  553           String name = TextUtils.noNull(packageElement.getAttribute("name"));
  554           String namespace = TextUtils.noNull(packageElement.getAttribute("namespace"));
  555   
  556   
  557           if (TextUtils.stringSet(packageElement.getAttribute("externalReferenceResolver"))) {
  558               throw new ConfigurationException("The 'externalReferenceResolver' attribute has been removed.  Please use " +
  559                       "a custom ObjectFactory or Interceptor.", packageElement);
  560           }
  561   
  562           PackageConfig.Builder cfg = new PackageConfig.Builder(name)
  563                   .namespace(namespace)
  564                   .isAbstract(isAbstract)
  565                   .location(DomHelper.getLocationObject(packageElement));
  566   
  567   
  568           if (TextUtils.stringSet(TextUtils.noNull(parent))) { // has parents, let's look it up
  569   
  570               List parents = ConfigurationUtil.buildParentsFromString(configuration, parent);
  571   
  572               if (parents.size() <= 0) {
  573                   cfg.needsRefresh(true);
  574               } else {
  575                   cfg.addParents(parents);
  576               }
  577           }
  578   
  579           return cfg;
  580       }
  581   
  582       /**
  583        * Build a map of ResultConfig objects from below a given XML element.
  584        */
  585       protected Map buildResults(Element element, PackageConfig.Builder packageContext) {
  586           NodeList resultEls = element.getElementsByTagName("result");
  587   
  588           Map results = new LinkedHashMap();
  589   
  590           for (int i = 0; i < resultEls.getLength(); i++) {
  591               Element resultElement = (Element) resultEls.item(i);
  592   
  593               if (resultElement.getParentNode().equals(element) || resultElement.getParentNode().getNodeName().equals(element.getNodeName())) {
  594                   String resultName = resultElement.getAttribute("name");
  595                   String resultType = resultElement.getAttribute("type");
  596   
  597                   // if you don't specify a name on <result/>, it defaults to "success"
  598                   if (!TextUtils.stringSet(resultName)) {
  599                       resultName = Action.SUCCESS;
  600                   }
  601   
  602                   // there is no result type, so let's inherit from the parent package
  603                   if (!TextUtils.stringSet(resultType)) {
  604                       resultType = packageContext.getFullDefaultResultType();
  605   
  606                       // now check if there is a result type now
  607                       if (!TextUtils.stringSet(resultType)) {
  608                           // uh-oh, we have a problem
  609                           throw new ConfigurationException("No result type specified for result named '"
  610                                   + resultName + "', perhaps the parent package does not specify the result type?", resultElement);
  611                       }
  612                   }
  613   
  614   
  615                   ResultTypeConfig config = packageContext.getResultType(resultType);
  616   
  617                   if (config == null) {
  618                       throw new ConfigurationException("There is no result type defined for type '" + resultType + "' mapped with name '" + resultName + "'", resultElement);
  619                   }
  620   
  621                   String resultClass = config.getClazz();
  622   
  623                   // invalid result type specified in result definition
  624                   if (resultClass == null) {
  625                       throw new ConfigurationException("Result type '" + resultType + "' is invalid");
  626                   }
  627   
  628                   Map<String, String> resultParams = XmlHelper.getParams(resultElement);
  629   
  630                   if (resultParams.size() == 0) // maybe we just have a body - therefore a default parameter
  631                   {
  632                       // if <result ...>something</result> then we add a parameter of 'something' as this is the most used result param
  633                       if (resultElement.getChildNodes().getLength() >= 1) {
  634                           resultParams = new LinkedHashMap();
  635   
  636                           String paramName = config.getDefaultResultParam();
  637                           if (paramName != null) {
  638                               StringBuffer paramValue = new StringBuffer();
  639                               for (int j = 0; j < resultElement.getChildNodes().getLength(); j++) {
  640                                   if (resultElement.getChildNodes().item(j).getNodeType() == Node.TEXT_NODE) {
  641                                       String val = resultElement.getChildNodes().item(j).getNodeValue();
  642                                       if (val != null) {
  643                                           paramValue.append(val);
  644                                       }
  645                                   }
  646                               }
  647                               String val = paramValue.toString().trim();
  648                               if (val.length() > 0) {
  649                                   resultParams.put(paramName, val);
  650                               }
  651                           } else {
  652                               LOG.warn("no default parameter defined for result of type " + config.getName());
  653                           }
  654                       }
  655                   }
  656   
  657                   // create new param map, so that the result param can override the config param
  658                   Map params = new LinkedHashMap();
  659                   Map configParams = config.getParams();
  660                   if (configParams != null) {
  661                       params.putAll(configParams);
  662                   }
  663                   params.putAll(resultParams);
  664   
  665                   ResultConfig resultConfig = new ResultConfig.Builder(resultName, resultClass)
  666                       .addParams(params)
  667                       .location(DomHelper.getLocationObject(element))
  668                       .build();
  669                   results.put(resultConfig.getName(), resultConfig);
  670               }
  671           }
  672   
  673           return results;
  674       }
  675   
  676       /**
  677        * Build a map of ResultConfig objects from below a given XML element.
  678        */
  679       protected List buildExceptionMappings(Element element, PackageConfig.Builder packageContext) {
  680           NodeList exceptionMappingEls = element.getElementsByTagName("exception-mapping");
  681   
  682           List exceptionMappings = new ArrayList();
  683   
  684           for (int i = 0; i < exceptionMappingEls.getLength(); i++) {
  685               Element ehElement = (Element) exceptionMappingEls.item(i);
  686   
  687               if (ehElement.getParentNode().equals(element) || ehElement.getParentNode().getNodeName().equals(element.getNodeName())) {
  688                   String emName = ehElement.getAttribute("name");
  689                   String exceptionClassName = ehElement.getAttribute("exception");
  690                   String exceptionResult = ehElement.getAttribute("result");
  691   
  692                   Map params = XmlHelper.getParams(ehElement);
  693   
  694                   if (!TextUtils.stringSet(emName)) {
  695                       emName = exceptionResult;
  696                   }
  697   
  698                   ExceptionMappingConfig ehConfig = new ExceptionMappingConfig.Builder(emName, exceptionClassName, exceptionResult)
  699                       .addParams(params)
  700                       .location(DomHelper.getLocationObject(ehElement))
  701                       .build();
  702                   exceptionMappings.add(ehConfig);
  703               }
  704           }
  705   
  706           return exceptionMappings;
  707       }
  708   
  709   
  710       protected void loadDefaultInterceptorRef(PackageConfig.Builder packageContext, Element element) {
  711           NodeList resultTypeList = element.getElementsByTagName("default-interceptor-ref");
  712   
  713           if (resultTypeList.getLength() > 0) {
  714               Element defaultRefElement = (Element) resultTypeList.item(0);
  715               packageContext.defaultInterceptorRef(defaultRefElement.getAttribute("name"));
  716           }
  717       }
  718   
  719       protected void loadDefaultActionRef(PackageConfig.Builder packageContext, Element element) {
  720           NodeList resultTypeList = element.getElementsByTagName("default-action-ref");
  721   
  722           if (resultTypeList.getLength() > 0) {
  723               Element defaultRefElement = (Element) resultTypeList.item(0);
  724               packageContext.defaultActionRef(defaultRefElement.getAttribute("name"));
  725           }
  726       }
  727   
  728       /**
  729        * Load all of the global results for this package from the XML element.
  730        */
  731       protected void loadGlobalResults(PackageConfig.Builder packageContext, Element packageElement) {
  732           NodeList globalResultList = packageElement.getElementsByTagName("global-results");
  733   
  734           if (globalResultList.getLength() > 0) {
  735               Element globalResultElement = (Element) globalResultList.item(0);
  736               Map results = buildResults(globalResultElement, packageContext);
  737               packageContext.addGlobalResultConfigs(results);
  738           }
  739       }
  740   
  741       protected void loadDefaultClassRef(PackageConfig.Builder packageContext, Element element) {
  742           NodeList defaultClassRefList = element.getElementsByTagName("default-class-ref");
  743           if (defaultClassRefList.getLength() > 0) {
  744               Element defaultClassRefElement = (Element) defaultClassRefList.item(0);
  745               packageContext.defaultClassRef(defaultClassRefElement.getAttribute("class"));
  746           }
  747       }
  748   
  749       /**
  750        * Load all of the global results for this package from the XML element.
  751        */
  752       protected void loadGobalExceptionMappings(PackageConfig.Builder packageContext, Element packageElement) {
  753           NodeList globalExceptionMappingList = packageElement.getElementsByTagName("global-exception-mappings");
  754   
  755           if (globalExceptionMappingList.getLength() > 0) {
  756               Element globalExceptionMappingElement = (Element) globalExceptionMappingList.item(0);
  757               List exceptionMappings = buildExceptionMappings(globalExceptionMappingElement, packageContext);
  758               packageContext.addGlobalExceptionMappingConfigs(exceptionMappings);
  759           }
  760       }
  761   
  762       //    protected void loadIncludes(Element rootElement, DocumentBuilder db) throws Exception {
  763       //        NodeList includeList = rootElement.getElementsByTagName("include");
  764       //
  765       //        for (int i = 0; i < includeList.getLength(); i++) {
  766       //            Element includeElement = (Element) includeList.item(i);
  767       //            String fileName = includeElement.getAttribute("file");
  768       //            includedFileNames.add(fileName);
  769       //            loadConfigurationFile(fileName, db);
  770       //        }
  771       //    }
  772       protected InterceptorStackConfig loadInterceptorStack(Element element, PackageConfig.Builder context) throws ConfigurationException {
  773           String name = element.getAttribute("name");
  774   
  775           InterceptorStackConfig.Builder config = new InterceptorStackConfig.Builder(name)
  776                   .location(DomHelper.getLocationObject(element));
  777           NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");
  778   
  779           for (int j = 0; j < interceptorRefList.getLength(); j++) {
  780               Element interceptorRefElement = (Element) interceptorRefList.item(j);
  781               List interceptors = lookupInterceptorReference(context, interceptorRefElement);
  782               config.addInterceptors(interceptors);
  783           }
  784   
  785           return config.build();
  786       }
  787   
  788       protected void loadInterceptorStacks(Element element, PackageConfig.Builder context) throws ConfigurationException {
  789           NodeList interceptorStackList = element.getElementsByTagName("interceptor-stack");
  790   
  791           for (int i = 0; i < interceptorStackList.getLength(); i++) {
  792               Element interceptorStackElement = (Element) interceptorStackList.item(i);
  793   
  794               InterceptorStackConfig config = loadInterceptorStack(interceptorStackElement, context);
  795   
  796               context.addInterceptorStackConfig(config);
  797           }
  798       }
  799   
  800       protected void loadInterceptors(PackageConfig.Builder context, Element element) throws ConfigurationException {
  801           NodeList interceptorList = element.getElementsByTagName("interceptor");
  802   
  803           for (int i = 0; i < interceptorList.getLength(); i++) {
  804               Element interceptorElement = (Element) interceptorList.item(i);
  805               String name = interceptorElement.getAttribute("name");
  806               String className = interceptorElement.getAttribute("class");
  807   
  808               Map params = XmlHelper.getParams(interceptorElement);
  809               InterceptorConfig config = new InterceptorConfig.Builder(name, className)
  810                   .addParams(params)
  811                   .location(DomHelper.getLocationObject(interceptorElement))
  812                   .build();
  813   
  814               context.addInterceptorConfig(config);
  815           }
  816   
  817           loadInterceptorStacks(element, context);
  818       }
  819   
  820       //    protected void loadPackages(Element rootElement) throws ConfigurationException {
  821       //        NodeList packageList = rootElement.getElementsByTagName("package");
  822       //
  823       //        for (int i = 0; i < packageList.getLength(); i++) {
  824       //            Element packageElement = (Element) packageList.item(i);
  825       //            addPackage(packageElement);
  826       //        }
  827       //    }
  828       private List loadConfigurationFiles(String fileName, Element includeElement) {
  829           List<Document> docs = new ArrayList<Document>();
  830           if (!includedFileNames.contains(fileName)) {
  831               if (LOG.isDebugEnabled()) {
  832                   LOG.debug("Loading action configurations from: " + fileName);
  833               }
  834   
  835               includedFileNames.add(fileName);
  836   
  837               Iterator<URL> urls = null;
  838               Document doc = null;
  839               InputStream is = null;
  840   
  841               IOException ioException = null;
  842               try {
  843                   urls = getConfigurationUrls(fileName);
  844               } catch (IOException ex) {
  845                   ioException = ex;
  846               }
  847   
  848               if (urls == null || !urls.hasNext()) {
  849                   if (errorIfMissing) {
  850                       throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
  851                   } else {
  852                       if (LOG.isDebugEnabled()) {
  853                           LOG.debug("Unable to locate configuration files of the name "
  854                                   + fileName + ", skipping");
  855                       }
  856                       return docs;
  857                   }
  858               }
  859   
  860               URL url = null;
  861               while (urls.hasNext()) {
  862                   try {
  863                       url = urls.next();
  864                       is = FileManager.loadFile(url);
  865   
  866                       InputSource in = new InputSource(is);
  867   
  868                       in.setSystemId(url.toString());
  869   
  870                       doc = DomHelper.parse(in, dtdMappings);
  871                   } catch (XWorkException e) {
  872                       if (includeElement != null) {
  873                           throw new ConfigurationException("Unable to load "+url, e, includeElement);
  874                       } else {
  875                           throw new ConfigurationException("Unable to load "+url, e);
  876                       }
  877                   } catch (Exception e) {
  878                       final String s = "Caught exception while loading file " + fileName;
  879                       throw new ConfigurationException(s, e, includeElement);
  880                   } finally {
  881                       if (is != null) {
  882                           try {
  883                               is.close();
  884                           } catch (IOException e) {
  885                               LOG.error("Unable to close input stream", e);
  886                           }
  887                       }
  888                   }
  889   
  890                   Element rootElement = doc.getDocumentElement();
  891                   NodeList children = rootElement.getChildNodes();
  892                   int childSize = children.getLength();
  893   
  894                   for (int i = 0; i < childSize; i++) {
  895                       Node childNode = children.item(i);
  896   
  897                       if (childNode instanceof Element) {
  898                           Element child = (Element) childNode;
  899   
  900                           final String nodeName = child.getNodeName();
  901   
  902                           if (nodeName.equals("include")) {
  903                               String includeFileName = child.getAttribute("file");
  904                               if(includeFileName.indexOf('*') != -1 ) {
  905                                   // handleWildCardIncludes(includeFileName, docs, child);
  906                               	ClassPathFinder wildcardFinder = new ClassPathFinder();
  907                               	wildcardFinder.setPattern(includeFileName);
  908                               	Vector<String> wildcardMatches = wildcardFinder.findMatches();
  909                               	for (String match : wildcardMatches) {
  910                               		docs.addAll(loadConfigurationFiles(match, child));
  911                               	}
  912                               }
  913                               else {
  914                               	
  915                                   docs.addAll(loadConfigurationFiles(includeFileName, child));    
  916                               }    
  917                       }
  918                   }
  919                   }
  920                   docs.add(doc);
  921                   loadedFileUrls.add(url.toString());
  922               }
  923   
  924               if (LOG.isDebugEnabled()) {
  925                   LOG.debug("Loaded action configuration from: " + fileName);
  926               }
  927           }
  928           return docs;
  929       }
  930   
  931       private void handleWildCardIncludes(String includeFileName, List<Document> docs, Element child) {
  932           // check for star*, pedantic
  933           if( includeFileName.indexOf('*') == -1 ) {
  934               throw new XWorkException("handleWildCardIncludes called with name not containing wildcard");
  935           }
  936           LOG.info("encountered wildcard include, checking for - " + includeFileName);
  937           URL [] curDirUrls ;
  938   
  939              // curDirUrls = ClassLoaderUtil.getResources("/", this.getClass(), true);
  940              ClassLoader cl = XmlConfigurationProvider.class.getClassLoader();
  941              URLClassLoader ucl ;
  942              if (cl instanceof URLClassLoader) {
  943                  ucl = (URLClassLoader) cl;
  944              }
  945              else {
  946                  throw new XWorkException("cannot create an URLClassLoader from current classloader");
  947              }
  948              curDirUrls = ucl.getURLs();
  949   
  950           String fileNamePrefix = null;
  951           if (! includeFileName.startsWith("*")) {
  952               fileNamePrefix = includeFileName.substring(0, includeFileName.indexOf('*'));    
  953           }
  954           
  955           String fileNameSuffix = null;
  956           if( ! includeFileName.endsWith("*")) {
  957               fileNameSuffix = includeFileName.substring(includeFileName.lastIndexOf('*') +1 );
  958           }
  959           
  960           String relativeDir = null;
  961           
  962           if(includeFileName.indexOf("/") != -1) {
  963               if(LOG.isDebugEnabled() ) {
  964                   LOG.debug("includeFileName contains a /");
  965               }
  966               if(includeFileName.lastIndexOf('/') > includeFileName.indexOf('*')) {
  967                   throw new XWorkException("wildcard includes does not support wildcard directories");
  968               }
  969               fileNamePrefix = includeFileName.substring(includeFileName.lastIndexOf('/') +1, includeFileName.indexOf('*'));
  970               relativeDir = includeFileName.substring(0,includeFileName.lastIndexOf('/'));
  971               if(LOG.isDebugEnabled() ) {
  972                   LOG.debug("relativeDir = " + relativeDir + ", fileNameMask = " + fileNamePrefix);
  973               }
  974           }
  975           
  976           for(URL baseSearchURL : curDirUrls ) {
  977               if (! baseSearchURL.getProtocol().equals("file")) {
  978                   continue;
  979               }
  980               
  981               File searchDir ;
  982               
  983               if (relativeDir != null ) {
  984                   if (relativeDir.startsWith("/")) {
  985                       relativeDir = relativeDir.substring(relativeDir.indexOf('/') + 1);
  986                   }
  987                   File parent ;
  988                   try {
  989                       parent = new File(baseSearchURL.toURI());
  990                   } catch (URISyntaxException e) {
  991                       throw new XWorkException("bad URI for searchDir - " + baseSearchURL.toString());
  992                   }
  993                   if( ! parent.isDirectory()) {
  994                       continue;
  995                   }
  996                   searchDir = new File(parent, relativeDir);
  997               }
  998               else {
  999                   try {
 1000                       searchDir = new File(baseSearchURL.toURI());
 1001                   } catch (URISyntaxException e) {
 1002                       throw new XWorkException("bad URI for searchDir - " + baseSearchURL.toString());
 1003                   }
 1004               }
 1005               if(LOG.isDebugEnabled() ) {
 1006                   LOG.debug("using - " + searchDir.toURI().toString() + ", as searchDir");
 1007               }
 1008               if( searchDir != null && searchDir.isDirectory() ) {
 1009                   if(LOG.isDebugEnabled() ) {
 1010                       LOG.debug("getting searchDir file list");
 1011                   }
 1012                   String [] filesInDir = searchDir.list();
 1013                   if (filesInDir == null ) {
 1014                       throw new XWorkException("unable to find any files in include directory");
 1015                   }
 1016                   for (String fileInDir: filesInDir) {
 1017                       if(LOG.isDebugEnabled() ) {
 1018                           LOG.debug("checking - " + fileInDir);
 1019                       }
 1020                       boolean fileMatches = false ;
 1021                       if (fileNameSuffix != null || fileNamePrefix != null) {
 1022                           fileMatches = ( fileNameSuffix != null && fileNamePrefix != null &&
 1023                                           fileNameSuffix.length() + fileNamePrefix.length() < fileInDir.length() && 
 1024                                           fileInDir.startsWith(fileNamePrefix) && fileInDir.endsWith(fileNameSuffix ) ) ;
 1025                           fileMatches = fileMatches || 
 1026                                       ( fileNamePrefix == null &&
 1027                                         fileInDir.endsWith(fileNameSuffix) );
 1028                           fileMatches = fileMatches || 
 1029                                       ( fileNameSuffix == null &&
 1030                                         fileInDir.startsWith(fileNamePrefix) );
 1031                       }
 1032                       
 1033                       if( fileMatches ) {
 1034                           if (relativeDir != null ) {
 1035                               if (!relativeDir.endsWith("/")) {
 1036                                   relativeDir = relativeDir.concat("/");
 1037                               }
 1038                               if(LOG.isDebugEnabled() ) {
 1039                                   LOG.debug("calling load on - " + relativeDir + fileInDir);
 1040                               }
 1041                               docs.addAll(loadConfigurationFiles(relativeDir + fileInDir, child));
 1042                           }
 1043                           else {
 1044                               if(LOG.isDebugEnabled() ) {
 1045                                   LOG.debug("calling load on - " + fileInDir);
 1046                               }
 1047                               docs.addAll(loadConfigurationFiles(fileInDir, child));
 1048                           }
 1049                       }
 1050                   }
 1051               }
 1052           }
 1053       }
 1054       
 1055       protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
 1056           return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
 1057       }
 1058   
 1059       /**
 1060        * Allows subclasses to load extra information from the document
 1061        *
 1062        * @param doc The configuration document
 1063        */
 1064       protected void loadExtraConfiguration(Document doc) {
 1065           // no op
 1066       }
 1067   
 1068       /**
 1069        * Looks up the Interceptor Class from the interceptor-ref name and creates an instance, which is added to the
 1070        * provided List, or, if this is a ref to a stack, it adds the Interceptor instances from the List to this stack.
 1071        *
 1072        * @param interceptorRefElement Element to pull interceptor ref data from
 1073        * @param context               The PackageConfig to lookup the interceptor from
 1074        * @return A list of Interceptor objects
 1075        */
 1076       private List lookupInterceptorReference(PackageConfig.Builder context, Element interceptorRefElement) throws ConfigurationException {
 1077           String refName = interceptorRefElement.getAttribute("name");
 1078           Map refParams = XmlHelper.getParams(interceptorRefElement);
 1079   
 1080           Location loc = LocationUtils.getLocation(interceptorRefElement);
 1081           return InterceptorBuilder.constructInterceptorReference(context, refName, refParams, loc, objectFactory);
 1082       }
 1083   
 1084   }

Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.config.providers » [javadoc | source]