Author: mrdon Date: Sun Nov 5 01:21:42 2006 New Revision: 471384 URL: http://svn.apache.org/viewvc?view=rev&rev=471384 Log: Adding a classloader scanning configuration provider that uses several annotations to discover Actions, packages, and namespaces at runtime WW-1491
Added: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPersonAction.java - copied, changed from r471248, struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson.java struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeopleAction.java - copied, changed from r471248, struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeople.java struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/NewPersonAction.java struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/EditPersonAction-conversion.properties - copied unchanged from r471248, struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/EditPerson-conversion.properties struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/NewPersonAction-validation.xml struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ClasspathConfigurationProvider.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Namespace.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/NullResult.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ParentPackage.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Result.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Results.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/ClasspathConfigurationProviderTest.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomNamespaceAction.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomParentPackageAction.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/OneResultAction.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/TwoResultAction.java Removed: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/CreatePerson-validation.xml struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/CreatePerson.java struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson-conversion.properties struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson.java struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeople.java struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/Person-validation.xml struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/CreatePerson-validation.xml struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/EditPerson-conversion.properties Modified: struts/struts2/trunk/apps/showcase/src/main/resources/struts-person.xml struts/struts2/trunk/apps/showcase/src/main/resources/struts.properties struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp struts/struts2/trunk/apps/showcase/src/main/webapp/person/editPeople.jsp struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java Copied: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPersonAction.java (from r471248, struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson.java) URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPersonAction.java?view=diff&rev=471384&p1=struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson.java&r1=471248&p2=struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPersonAction.java&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPerson.java (original) +++ struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/EditPersonAction.java Sun Nov 5 01:21:42 2006 @@ -17,17 +17,26 @@ */ package org.apache.struts2.showcase.person; -import com.opensymphony.xwork2.ActionSupport; - -import java.util.List; -import java.util.Iterator; import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.struts2.config.Result; +import org.apache.struts2.config.Results; +import org.apache.struts2.dispatcher.ServletRedirectResult; + +import com.opensymphony.xwork2.ActionSupport; /** * <code>EditPerson</code> * */ -public class EditPerson extends ActionSupport { [EMAIL PROTECTED]({ + @Result(value="editPeople.jsp"), + @Result(name="error",value="editPeople.jsp"), + @Result(name="list", value="listPeople.action", type=ServletRedirectResult.class) +}) +public class EditPersonAction extends ActionSupport { private static final long serialVersionUID = 7699491775215130850L; @@ -70,7 +79,7 @@ personManager.getPeople().remove(p); personManager.getPeople().add(p); } - return SUCCESS; + return "list"; } } Copied: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeopleAction.java (from r471248, struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeople.java) URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeopleAction.java?view=diff&rev=471384&p1=struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeople.java&r1=471248&p2=struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeopleAction.java&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeople.java (original) +++ struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/ListPeopleAction.java Sun Nov 5 01:21:42 2006 @@ -17,14 +17,16 @@ */ package org.apache.struts2.showcase.person; -import com.opensymphony.xwork2.ActionSupport; - -import java.util.List; import java.util.ArrayList; +import java.util.List; + +import org.apache.struts2.config.Result; +import org.apache.struts2.views.freemarker.FreemarkerResult; + +import com.opensymphony.xwork2.ActionSupport; -/** - */ -public class ListPeople extends ActionSupport { [EMAIL PROTECTED](value="listPeople.ftl", type=FreemarkerResult.class) +public class ListPeopleAction extends ActionSupport { private static final long serialVersionUID = 3608017189783645371L; Added: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/NewPersonAction.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/NewPersonAction.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/NewPersonAction.java (added) +++ struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/person/NewPersonAction.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,58 @@ +/* + * $Id: CreatePerson.java 420385 2006-07-10 00:57:05Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.showcase.person; + +import org.apache.struts2.config.ParentPackage; +import org.apache.struts2.config.Result; +import org.apache.struts2.config.Results; +import org.apache.struts2.views.freemarker.FreemarkerResult; + +import com.opensymphony.xwork2.ActionSupport; + +/** + */ [EMAIL PROTECTED]("person") [EMAIL PROTECTED]({ + @Result(value="newPerson.ftl", type=FreemarkerResult.class), + @Result(name="input", value="newPerson.ftl", type=FreemarkerResult.class) +}) +public class NewPersonAction extends ActionSupport { + + private static final long serialVersionUID = 200410824352645515L; + + PersonManager personManager; + Person person; + + public void setPersonManager(PersonManager personManager) { + this.personManager = personManager; + } + + public String execute() { + personManager.createPerson(person); + + return SUCCESS; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } +} Added: struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/NewPersonAction-validation.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/NewPersonAction-validation.xml?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/NewPersonAction-validation.xml (added) +++ struts/struts2/trunk/apps/showcase/src/main/resources/org/apache/struts2/showcase/person/NewPersonAction-validation.xml Sun Nov 5 01:21:42 2006 @@ -0,0 +1,8 @@ +<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd"> +<validators> + <field name="person"> + <field-validator type="visitor"> + <message></message> + </field-validator> + </field> +</validators> Modified: struts/struts2/trunk/apps/showcase/src/main/resources/struts-person.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/resources/struts-person.xml?view=diff&rev=471384&r1=471383&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/resources/struts-person.xml (original) +++ struts/struts2/trunk/apps/showcase/src/main/resources/struts-person.xml Sun Nov 5 01:21:42 2006 @@ -5,26 +5,7 @@ <!-- START SNIPPET: xworkSample --> <struts> - <package name="person" extends="struts-default" namespace="/person"> - <action name="listPeople" class="org.apache.struts2.showcase.person.ListPeople"> - <interceptor-ref name="validationWorkflowStack"/> - <result type="freemarker">listPeople.ftl</result> - </action> - - <action name="newPerson!*" class="org.apache.struts2.showcase.person.CreatePerson" method="{1}"> - <result type="redirect">listPeople.action</result> - <result name="input" type="freemarker">newPerson.ftl</result> - </action> - - <action name="editPerson" class="org.apache.struts2.showcase.person.EditPerson"> - <result>editPeople.jsp</result> - </action> - - <action name="doEditPerson" class="org.apache.struts2.showcase.person.EditPerson" method="save"> - <result name="error">editPeople.jsp</result> - <result type="redirect">listPeople.action</result> - </action> - </package> + <package name="person" extends="struts-default" namespace="/person" /> </struts> <!-- END SNIPPET: xworkSample --> Modified: struts/struts2/trunk/apps/showcase/src/main/resources/struts.properties URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/resources/struts.properties?view=diff&rev=471384&r1=471383&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/resources/struts.properties (original) +++ struts/struts2/trunk/apps/showcase/src/main/resources/struts.properties Sun Nov 5 01:21:42 2006 @@ -9,3 +9,5 @@ struts.freemarker.manager.classname=customFreemarkerManager struts.serve.static=true struts.serve.static.browserCache=false + +struts.configuration.files=struts-default.xml,struts-plugin.xml,struts.xml,org.apache.struts2.showcase.person Modified: struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp?view=diff&rev=471384&r1=471383&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp (original) +++ struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp Sun Nov 5 01:21:42 2006 @@ -11,7 +11,8 @@ com.opensymphony.xwork2.ActionInvocation inv = com.opensymphony.xwork2.ActionContext.getContext().getActionInvocation(); org.apache.struts2.dispatcher.mapper.ActionMapping mapping = org.apache.struts2.ServletActionContext.getActionMapping(); if (inv != null) { - sourceUrl += "?config="+inv.getProxy().getConfig().getLocation().getURI()+":"+inv.getProxy().getConfig().getLocation().getLineNumber(); + com.opensymphony.xwork2.util.location.Location loc = inv.getProxy().getConfig().getLocation(); + sourceUrl += "?config="+(loc != null ? loc.getURI()+":"+loc.getLineNumber() : ""); sourceUrl += "&className="+inv.getProxy().getConfig().getClassName(); if (inv.getResult() != null && inv.getResult() instanceof org.apache.struts2.dispatcher.StrutsResultSupport) { Modified: struts/struts2/trunk/apps/showcase/src/main/webapp/person/editPeople.jsp URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/webapp/person/editPeople.jsp?view=diff&rev=471384&r1=471383&r2=471384 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/webapp/person/editPeople.jsp (original) +++ struts/struts2/trunk/apps/showcase/src/main/webapp/person/editPeople.jsp Sun Nov 5 01:21:42 2006 @@ -6,7 +6,7 @@ </head> <body> -<s:form action="doEditPerson" theme="simple" validate="false"> +<s:form action="editPerson" theme="simple" validate="false"> <table> <tr> @@ -29,7 +29,7 @@ </s:iterator> </table> - <s:submit value="Save all persons"/> + <s:submit method="save" value="Save all persons"/> </s:form> <ul> Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ClasspathConfigurationProvider.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ClasspathConfigurationProvider.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ClasspathConfigurationProvider.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ClasspathConfigurationProvider.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,354 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts2.StrutsConstants; + +import com.opensymphony.xwork2.ObjectFactory; +import com.opensymphony.xwork2.VoidResult; +import com.opensymphony.xwork2.config.Configuration; +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.config.ConfigurationProvider; +import com.opensymphony.xwork2.config.entities.ActionConfig; +import com.opensymphony.xwork2.config.entities.PackageConfig; +import com.opensymphony.xwork2.config.entities.ResultConfig; +import com.opensymphony.xwork2.config.entities.ResultTypeConfig; +import com.opensymphony.xwork2.util.ClassLoaderUtil; +import com.opensymphony.xwork2.util.ResolverUtil; +import com.opensymphony.xwork2.util.TextUtils; + +/** + * Loads the configuration by scanning the classpath looking for classes that end in + * 'Action'. + */ +public class ClasspathConfigurationProvider implements ConfigurationProvider { + + private static final String DEFAULT_PAGE_PREFIX = "struts.configuration.classpath.defaultPagePrefix"; + private static final String DEFAULT_PAGE_EXTENSION = "struts.configuration.classpath.defaultPageExtension"; + private static final String DEFAULT_PARENT_PACKAGE = "struts.configuration.classpath.defaultParentPackage"; + private static final String ACTION = "Action"; + private String[] packages; + private String defaultParentPackage = "struts-default"; + private String defaultPageExtension = ".jsp"; + private String defaultPagePrefix = ""; + private PageLocator pageLocator = new ClasspathPageLocator(); + private boolean initialized = false; + + private Map<String,PackageConfig> loadedPackageConfigs = new HashMap<String,PackageConfig>(); + + private static final Log LOG = LogFactory.getLog(ClasspathConfigurationProvider.class); + + private Configuration configuration; + + public ClasspathConfigurationProvider(String[] pkgs) { + this.packages = pkgs; + + if (Settings.isSet(DEFAULT_PARENT_PACKAGE)) { + defaultParentPackage = Settings.get(DEFAULT_PARENT_PACKAGE); + } + + if (Settings.isSet(DEFAULT_PAGE_EXTENSION)) { + defaultPageExtension = Settings.get(DEFAULT_PAGE_EXTENSION); + } + + if (Settings.isSet(DEFAULT_PAGE_PREFIX)) { + defaultPagePrefix = Settings.get(DEFAULT_PAGE_PREFIX); + } + + } + + public static interface PageLocator { + public URL locate(String path); + } + + public static class ClasspathPageLocator implements PageLocator { + public URL locate(String path) { + return ClassLoaderUtil.getResource(path, getClass()); + } + } + + /** + * @param defaultParentPackage the defaultParentPackage to set + */ + public void setDefaultParentPackage(String defaultParentPackage) { + this.defaultParentPackage = defaultParentPackage; + } + + /** + * @param defaultPageExtension the defaultPageExtension to set + */ + public void setDefaultPageExtension(String defaultPageExtension) { + this.defaultPageExtension = defaultPageExtension; + } + + /** + * @param defaultPagePrefix the defaultPagePrefix to set + */ + public void setDefaultPagePrefix(String defaultPagePrefix) { + this.defaultPagePrefix = defaultPagePrefix; + } + + public void setPageLocator(PageLocator locator) { + this.pageLocator = locator; + } + + /** + * @param pkgs + */ + protected void loadPackages(String[] pkgs) { + ResolverUtil<Class> resolver = new ResolverUtil<Class>(); + resolver.findSuffix(ACTION, pkgs); + Set actionClasses = resolver.getClasses(); + for (Object obj : actionClasses) { + Class cls = (Class) obj; + if (!Modifier.isAbstract(cls.getModifiers())) { + processActionClass(cls, pkgs); + } + } + + for (String key : loadedPackageConfigs.keySet()) { + configuration.addPackageConfig(key, loadedPackageConfigs.get(key)); + } + } + + protected void processActionClass(Class cls, String[] pkgs) { + String name = cls.getName(); + String actionPackage = cls.getPackage().getName(); + String actionNamespace = null; + String actionName = null; + for (String pkg : pkgs) { + if (name.startsWith(pkg)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Processing class "+name); + } + name = name.substring(pkg.length() + 1); + + actionNamespace = ""; + actionName = name; + int pos = name.lastIndexOf('.'); + if (pos > -1) { + actionNamespace = "/" + name.substring(0, pos).replace('.','/'); + actionName = name.substring(pos+1); + } + break; + } + } + + PackageConfig pkgConfig = loadPackageConfig(actionNamespace, actionPackage, cls); + + Annotation annotation = cls.getAnnotation(ParentPackage.class); + if (annotation != null) { + String parent = ((ParentPackage)annotation).value(); + PackageConfig parentPkg = configuration.getPackageConfig(parent); + if (parentPkg == null) { + throw new ConfigurationException("Unable to locate parent package "+parent, annotation); + } + pkgConfig.addParent(parentPkg); + + if (!TextUtils.stringSet(pkgConfig.getNamespace()) && TextUtils.stringSet(parentPkg.getNamespace())) { + pkgConfig.setNamespace(parentPkg.getNamespace()); + } + } + + actionName = actionName.substring(0, actionName.length() - ACTION.length()); + + if (actionName.length() > 1) { + int lowerPos = actionName.lastIndexOf('/') + 1; + StringBuilder sb = new StringBuilder(); + sb.append(actionName.substring(0, lowerPos)); + sb.append(Character.toLowerCase(actionName.charAt(lowerPos))); + sb.append(actionName.substring(lowerPos + 1)); + actionName = sb.toString(); + } + + ActionConfig actionConfig = new ActionConfig(); + actionConfig.setClassName(cls.getName()); + actionConfig.setPackageName(actionPackage); + + actionConfig.setResults(new ResultMap<String,ResultConfig>(cls, actionName, pkgConfig)); + + pkgConfig.addActionConfig(actionName, actionConfig); + } + + /** + * @param actionPackage + */ + protected PackageConfig loadPackageConfig(String actionNamespace, String actionPackage, Class actionClass) { + PackageConfig parent = null; + + if (actionClass != null) { + Namespace ns = (Namespace) actionClass.getAnnotation(Namespace.class); + if (ns != null) { + parent = loadPackageConfig(actionNamespace, actionPackage, null); + actionNamespace = ns.value(); + actionPackage = actionClass.getName(); + } + } + + PackageConfig pkgConfig = loadedPackageConfigs.get(actionPackage); + if (pkgConfig == null) { + pkgConfig = new PackageConfig(); + pkgConfig.setName(actionPackage); + + if (parent == null) { + parent = configuration.getPackageConfig(defaultParentPackage); + } + + if (parent == null) { + throw new ConfigurationException("Unable to locate default parent package: " + + defaultParentPackage); + } + pkgConfig.addParent(parent); + + pkgConfig.setNamespace(actionNamespace); + + loadedPackageConfigs.put(actionPackage, pkgConfig); + } + return pkgConfig; + } + + public void destroy() { + + } + + public void init(Configuration configuration) throws ConfigurationException { + this.configuration = configuration; + loadedPackageConfigs.clear(); + loadPackages(packages); + initialized = true; + } + + public boolean needsReload() { + return !initialized; + } + + /** + * Creates result configs from result annotations, and if a result isn't found, + * creates them on the fly. + */ + class ResultMap<K,V> extends HashMap<K,V> { + private Class actionClass; + private String actionName; + private PackageConfig pkgConfig; + + public ResultMap(Class actionClass, String actionName, PackageConfig pkgConfig) { + this.actionClass = actionClass; + this.actionName = actionName; + this.pkgConfig = pkgConfig; + + // check if any annotations are around + while (!actionClass.getName().equals(Object.class.getName())) { + //noinspection unchecked + Results results = (Results) actionClass.getAnnotation(Results.class); + if (results != null) { + // first check here... + for (int i = 0; i < results.value().length; i++) { + Result result = results.value()[i]; + ResultConfig config = createResultConfig(result); + put((K)config.getName(), (V)config); + } + } + + // what about a single Result annotation? + Result result = (Result) actionClass.getAnnotation(Result.class); + if (result != null) { + ResultConfig config = createResultConfig(result); + put((K)config.getName(), (V)config); + } + + actionClass = actionClass.getSuperclass(); + } + + } + + protected ResultConfig createResultConfig(Result result) { + Class cls = result.type(); + if (cls == NullResult.class) { + cls = null; + } + return createResultConfig(result.name(), cls, result.value()); + } + + public V get(Object key) { + + Object result = super.get(key); + if (result != null) { + return (V) result; + } else { + + // TODO: This code never is actually used, do to how the runtime configuration + // is created. + String actionPath = pkgConfig.getNamespace() + "/" + actionName; + + String fileName = actionPath + "-" + key + defaultPageExtension; + if (pageLocator.locate(defaultPagePrefix + fileName) == null) { + fileName = actionPath + defaultPageExtension; + } + + String location = defaultPagePrefix + fileName; + return (V) createResultConfig(key, null, location); + } + } + + /** + * @param key + * @param resultClass + * @param location + * @return + */ + private ResultConfig createResultConfig(Object key, Class resultClass, String location) { + Map configParams = null; + if (resultClass == null) { + String defaultResultType = pkgConfig.getFullDefaultResultType(); + ResultTypeConfig resultType = (ResultTypeConfig) pkgConfig.getAllResultTypeConfigs().get(defaultResultType); + configParams = resultType.getParams(); + String className = resultType.getClazz(); + try { + resultClass = ClassLoaderUtil.loadClass(className, getClass()); + } catch (ClassNotFoundException ex) { + throw new ConfigurationException("Unable to locate result class "+className, actionClass); + } + } + + String defaultParam; + try { + defaultParam = (String) resultClass.getField("DEFAULT_PARAM").get(null); + } catch (Exception e) { + // not sure why this happened, but let's just use a sensible choice + defaultParam = "location"; + } + + HashMap params = new HashMap(); + if (configParams != null) { + params.putAll(configParams); + } + params.put(defaultParam, location); + return new ResultConfig((String) key, resultClass.getName(), params); + } + } +} Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Namespace.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Namespace.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Namespace.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Namespace.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,29 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Allows an action class to specify its namespace + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) +public @interface Namespace { + String value(); +} Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/NullResult.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/NullResult.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/NullResult.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/NullResult.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,33 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.Result; + +/** + * Null result to get around annotation defaults that can't be null + */ +public class NullResult implements Result { + + public void execute(ActionInvocation invocation) throws Exception { + throw new IllegalStateException("Shouldn't be called"); + + } + +} Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ParentPackage.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ParentPackage.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ParentPackage.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/ParentPackage.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,29 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Allows an action class to specify an xwork package to inherit + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) +public @interface ParentPackage { + String value(); +} Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Result.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Result.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Result.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Result.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,33 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import com.opensymphony.xwork2.Action; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Defines an XWork Result + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) +public @interface Result { + String name() default Action.SUCCESS; + Class type() default NullResult.class; + String value(); +} Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Results.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Results.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Results.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Results.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,29 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Defines multiple XWork Results + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) +public @interface Results { + Result[] value(); +} Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java?view=diff&rev=471384&r1=471383&r2=471384 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java Sun Nov 5 01:21:42 2006 @@ -19,6 +19,8 @@ import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,8 +37,11 @@ import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsStatics; +import org.apache.struts2.config.ClasspathConfigurationProvider; import org.apache.struts2.config.Settings; import org.apache.struts2.config.StrutsXmlConfigurationProvider; +import org.apache.struts2.config.ClasspathConfigurationProvider.ClasspathPageLocator; +import org.apache.struts2.config.ClasspathConfigurationProvider.PageLocator; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.dispatcher.multipart.MultiPartRequest; import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper; @@ -175,7 +180,7 @@ * * @param servletContext The servlet context */ - private void init(ServletContext servletContext) { + private void init(final ServletContext servletContext) { boolean reloadi18n = Boolean.valueOf((String) Settings.get(StrutsConstants.STRUTS_I18N_RELOAD)).booleanValue(); LocalizedTextUtil.setReloadBundles(reloadi18n); @@ -258,14 +263,26 @@ configFiles = Settings.get(StrutsConstants.STRUTS_CONFIGURATION_FILES); } if (configFiles != null) { + List<String> packages = new ArrayList<String>(); String[] files = configFiles.split("\\s*[,]\\s*"); for (String file : files) { - if ("xwork.xml".equals(file)) { - configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); - } else { - configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false)); - } + if (file.endsWith(".xml")) { + if ("xwork.xml".equals(file)) { + configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false)); + } else { + configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false)); + } + } else { + packages.add(file); + } } + + // Initialize the classloader scanner with the configured paths + if (packages.size() > 0) { + ClasspathConfigurationProvider provider = new ClasspathConfigurationProvider((String[])packages.toArray(new String[]{})); + provider.setPageLocator(new ServletContextPageLocator(servletContext)); + configurationManager.addConfigurationProvider(provider); + } } synchronized(Dispatcher.class) { @@ -622,6 +639,33 @@ */ public static void setPortletSupportActive(boolean portletSupportActive) { Dispatcher.portletSupportActive = portletSupportActive; + } + + /** + * Resolves pages from the servlet context, failing over to the classpath + */ + private final class ServletContextPageLocator implements PageLocator { + private final ServletContext context; + private ClasspathPageLocator classpathPageLocator = new ClasspathPageLocator(); + + private ServletContextPageLocator(ServletContext context) { + this.context = context; + } + + public URL locate(String path) { + URL url = null; + try { + url = context.getResource(path); + if (url == null) { + url = classpathPageLocator.locate(path); + } + } catch (MalformedURLException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Unable to resolve path "+path+" against the servlet context"); + } + } + return url; + } } /** Simple accessor for a static method */ Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/ClasspathConfigurationProviderTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/ClasspathConfigurationProviderTest.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/ClasspathConfigurationProviderTest.java (added) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/ClasspathConfigurationProviderTest.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,98 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + +import java.util.Map; + +import org.apache.struts2.dispatcher.ServletDispatcherResult; + +import com.opensymphony.xwork2.config.Configuration; +import com.opensymphony.xwork2.config.entities.ActionConfig; +import com.opensymphony.xwork2.config.entities.PackageConfig; +import com.opensymphony.xwork2.config.entities.ResultConfig; +import com.opensymphony.xwork2.config.entities.ResultTypeConfig; +import com.opensymphony.xwork2.config.impl.DefaultConfiguration; +import com.opensymphony.xwork2.config.impl.MockConfiguration; + +import junit.framework.TestCase; + +public class ClasspathConfigurationProviderTest extends TestCase { + + ClasspathConfigurationProvider provider; + Configuration config; + + public void setUp() { + provider = new ClasspathConfigurationProvider(new String[]{"org.apache.struts2.config"}); + config = new DefaultConfiguration(); + PackageConfig strutsDefault = new PackageConfig("struts-default"); + strutsDefault.addResultTypeConfig(new ResultTypeConfig("dispatcher", ServletDispatcherResult.class.getName(), "location")); + strutsDefault.setDefaultResultType("dispatcher"); + config.addPackageConfig("struts-default", strutsDefault); + PackageConfig customPackage = new PackageConfig("custom-package"); + customPackage.setNamespace("/custom"); + config.addPackageConfig("custom-package", customPackage); + provider.init(config); + } + + public void testFoundRootPackages() { + assertEquals(5, config.getPackageConfigs().size()); + PackageConfig pkg = config.getPackageConfig("org.apache.struts2.config"); + assertNotNull(pkg); + Map configs = pkg.getActionConfigs(); + assertNotNull(configs); + assertEquals(1, configs.size()); + assertNotNull(configs.get("customParentPackage")); + } + + public void testParentPackage() { + PackageConfig pkg = config.getPackageConfig("org.apache.struts2.config"); + assertEquals(2, pkg.getParents().size()); + Map configs = pkg.getActionConfigs(); + ActionConfig config = (ActionConfig) configs.get("customParentPackage"); + assertNotNull(config); + assertEquals("/custom", pkg.getNamespace()); + } + + public void testCustomNamespace() { + PackageConfig pkg = config.getPackageConfig("org.apache.struts2.config.CustomNamespaceAction"); + Map configs = pkg.getAllActionConfigs(); + assertEquals(2, configs.size()); + ActionConfig config = (ActionConfig) configs.get("customNamespace"); + assertNotNull(config); + assertEquals("/mynamespace", pkg.getNamespace()); + assertNotNull(configs.get("customParentPackage")); + } + + public void testResultAnnotations() { + PackageConfig pkg = config.getPackageConfig("org.apache.struts2.config.cltest"); + assertEquals("/cltest", pkg.getNamespace()); + ActionConfig acfg = pkg.getActionConfigs().get("twoResult"); + assertNotNull(acfg); + assertEquals(3, acfg.getResults().size()); + } + + public void testDynamicResults() { + PackageConfig pkg = config.getPackageConfig("org.apache.struts2.config.cltest"); + ActionConfig config = pkg.getActionConfigs().get("twoResult"); + ResultConfig result = config.getResults().get("foobar"); + assertNotNull(result); + assertEquals("/cltest/twoResult.jsp", result.getParams().get("location")); + assertEquals(ServletDispatcherResult.class.getName(), result.getClassName()); + } + +} Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomNamespaceAction.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomNamespaceAction.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomNamespaceAction.java (added) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomNamespaceAction.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,23 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + [EMAIL PROTECTED]("/mynamespace") +public class CustomNamespaceAction { + +} Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomParentPackageAction.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomParentPackageAction.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomParentPackageAction.java (added) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/CustomParentPackageAction.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,23 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config; + [EMAIL PROTECTED]("custom-package") +public class CustomParentPackageAction { + +} Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/OneResultAction.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/OneResultAction.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/OneResultAction.java (added) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/OneResultAction.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,25 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config.cltest; + +import org.apache.struts2.config.Result; + [EMAIL PROTECTED]("foo.jsp") +public class OneResultAction { + +} Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/TwoResultAction.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/TwoResultAction.java?view=auto&rev=471384 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/TwoResultAction.java (added) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/config/cltest/TwoResultAction.java Sun Nov 5 01:21:42 2006 @@ -0,0 +1,31 @@ +/* + * $Id: DefaultSettings.java 439747 2006-09-03 09:22:46Z mrdon $ + * + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.config.cltest; + +import org.apache.struts2.config.Result; +import org.apache.struts2.config.Results; +import org.apache.struts2.dispatcher.ServletDispatcherResult; + + [EMAIL PROTECTED]({ + @Result(name="chain", value="bob", type=ServletDispatcherResult.class), + @Result(name="input", value="input.jsp") +}) +public class TwoResultAction extends OneResultAction { + +}