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

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