WW-2922 move convention plugin out of sandbox

git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@727418 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Musachy Barroso
2008-12-17 15:39:34 +00:00
parent 9215250aad
commit 352fdb5300
85 changed files with 6307 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
Design
Action with no annotations (all the default handlings)
--------------------------
Action name is class name.
Action package is based on the either any Namespace annotations on the class or Java package the
action class is defined in or the Java package name and the root Java package, which is determined
based on the package locators or the action package list. In either case, a root package is
identified and the namespace is the packages below the root up to and including the package that
contains the action class.
Action method is execute in all cases.
Results are built first from the Result annotations at the class level. Next, they are built from
the resources available in the web application and in the classpath based on the result location,
namespace of the action and action name (not anything to do with the code element names). Results
are found for common result codes including, success, error, input, failure, etc. If a specific
result doesn't exist using the result code then just the action namespace and action name are used to
locate results. If there are still no results found, than a redirect is added back to the index
action for the namespace, if one exists.
Action with Action(s) annotations
---------------------------------
If the annotation is located on any method other than execute, than the action configuration built
from the annotation is in addition to the action configuration built using the default. Unless there
is an annotation that contains no value, in which case, the method that annotation is defined for
becomes the default action method. If the annotation is defined on the execute method, regardless of
the value of the annotation, no default configuration is built for the class.
If multiple annotations have no value, an error should occur.
If multiple annotations have the same value, an error should occur.
Action name is taken from the annotation value. If the value is empty, the default action name
is used.
Action package is taken from the annotation value. If the annotation value doesn't start with a slash
character, the default action namespace is used.
Action method is always the method that the annotation is defined on.
Results are built first from the array of Results on the Action annotation. Next, they are built
from the class level Result annotations. Lastly, all default results are created. All Results already
created during this process should not be overridden later in the process.
+91
View File
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.1.3-SNAPSHOT</version>
</parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<packaging>jar</packaging>
<name>Struts 2 Convention Plugin</name>
<scm>
<connection>scm:svn:http://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-convention-plugin</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-convention-plugin</developerConnection>
<url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/trunk/struts2-convention-plugin</url>
</scm>
<build>
<plugins>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,38 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
/**
* <p>
* This interface defines how the action configurations for the current
* web application can be constructed. This must find all actions that
* are not specifically defined in the struts XML files or any plugins.
* Furthermore, it must make every effort to locate all action results
* as well.
* </p>
*/
public interface ActionConfigBuilder {
/**
* Builds all the action configurations and stores them into the XWork configuration instance
* via XWork dependency injetion.
*/
void buildActionConfigs();
}
@@ -0,0 +1,37 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
/**
* <p>
* This interface defines the method that is used to create action
* names based on the name of a class.
* </p>
*/
public interface ActionNameBuilder {
/**
* Given the name of the class, this method should build an action name.
*
* @param className The class name.
* @return The action name and never null.
*/
String build(String className);
}
@@ -0,0 +1,81 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.location.LocatableProperties;
/**
* <p>
* This class is a configuration provider for the XWork configuration
* system. This is really the only way to truly handle loading of the
* packages, actions and results correctly. This doesn't contain any
* logic and instead delegates to the configured instance of the
* {@link ActionConfigBuilder} interface.
* </p>
*/
public class ClasspathConfigurationProvider implements ConfigurationProvider {
private ActionConfigBuilder actionConfigBuilder;
@Inject
public ClasspathConfigurationProvider(ActionConfigBuilder actionConfigBuilder) {
this.actionConfigBuilder = actionConfigBuilder;
}
/**
* Not used.
*/
public void destroy() {
}
/**
* Not used.
*/
public void init(Configuration configuration) {
}
/**
* Does nothing.
*/
public void register(ContainerBuilder containerBuilder, LocatableProperties locatableProperties)
throws ConfigurationException {
}
/**
* Loads the packages using the {@link ActionConfigBuilder}.
*
* @throws ConfigurationException
*/
public void loadPackages() throws ConfigurationException {
actionConfigBuilder.buildActionConfigs();
}
/**
* @return Always false.
*/
public boolean needsReload() {
return false;
}
}
@@ -0,0 +1,418 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.struts2.util.ClassLoaderUtils;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.UnknownHandler;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
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.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* This class is the default unknown handler for all of the Convention
* plugin integration with XWork. This handles instances when a URL doesn't
* have an action associated with it but does a viable result and also the
* instance where an action returns a result code that isn't already
* configured but there is a viable result for the code.
* </p>
*
* <p>
* This class also handles all of the index actions using redirects
* and actions in nested packages. For example, if there is an action
* <strong>/foo/index</strong> and the URL <strong>/foo</strong> is used,
* this will render the index action in the /foo namespace.
* </p>
*/
public class ConventionUnknownHandler implements UnknownHandler {
private static final Logger LOG = LoggerFactory.getLogger(ConventionUnknownHandler.class);
protected Configuration configuration;
protected ObjectFactory objectFactory;
protected ServletContext servletContext;
protected ResultMapBuilder resultMapBuilder;
protected String defaultParentPackageName;
protected PackageConfig parentPackage;
private boolean redirectToSlash;
private ConventionsService conventionsService;
private String nameSeparator;
/**
* Constructs the unknown handler.
*
* @param configuration The XWork configuration.
* @param objectFactory The XWork object factory used to create result instances.
* @param servletContext The servlet context used to help build the action configurations.
* @param resultMapBuilder The result map builder that is used to create results.
* @param conventionsService The conventions service used to get all the conventions and
* configurations.
* @param defaultParentPackageName The default XWork package that the unknown handler will use as
* the parent package for new actions and results.
* @param redirectToSlash A boolean parameter that controls whether or not this will handle
* unknown actions in the same manner as Apache, Tomcat and other web servers. This
* handling will send back a redirect for URLs such as /foo to /foo/ if there doesn't
* exist an action that responds to /foo.
* @param nameSeparator The character used as word separator in the action names. "-" by default
*/
@Inject
public ConventionUnknownHandler(Configuration configuration, ObjectFactory objectFactory,
ServletContext servletContext, ResultMapBuilder resultMapBuilder,
ConventionsService conventionsService,
@Inject("struts.convention.default.parent.package") String defaultParentPackageName,
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
@Inject("struts.convention.action.name.separator") String nameSeparator) {
this.configuration = configuration;
this.objectFactory = objectFactory;
this.servletContext = servletContext;
this.resultMapBuilder = resultMapBuilder;
this.conventionsService = conventionsService;
this.defaultParentPackageName = defaultParentPackageName;
this.nameSeparator = nameSeparator;
this.parentPackage = configuration.getPackageConfig(defaultParentPackageName);
if (parentPackage == null) {
throw new ConfigurationException("Unknown default parent package [" + defaultParentPackageName + "]");
}
this.redirectToSlash = Boolean.parseBoolean(redirectToSlash);
}
public ActionConfig handleUnknownAction(String namespace, String actionName)
throws XWorkException {
// Strip the namespace if it is just a slash
if (namespace == null || "/".equals(namespace)) {
namespace = "";
}
Map<String, ResultTypeConfig> resultsByExtension = conventionsService.getResultTypesByExtension(parentPackage);
String pathPrefix = determinePath(null, namespace);
ActionConfig actionConfig = null;
// Try /idx/action.jsp if actionName is not empty, otherwise it will just be /.jsp
if (!actionName.equals("")) {
Resource resource = findResource(resultsByExtension, pathPrefix, actionName);
if (resource != null) {
actionConfig = buildActionConfig(resource.path, resultsByExtension.get(resource.ext));
}
}
if (actionConfig == null) {
Resource resource = findResource(resultsByExtension, pathPrefix, actionName, "/index");
// If the URL is /foo and there is an action we can redirect to, send the redirect to /foo/.
// However, if that action is not in the same namespace, it is the default, so I'm not going
// to return that.
if (!actionName.equals("") && redirectToSlash) {
ResultTypeConfig redirectResultTypeConfig = parentPackage.getAllResultTypeConfigs().get("redirect");
String redirectNamespace = namespace + "/" + actionName;
if (LOG.isTraceEnabled()) {
LOG.trace("Checking if there is an action named index in the namespace [#0]",
redirectNamespace);
}
actionConfig = configuration.getRuntimeConfiguration().getActionConfig(redirectNamespace, "index");
if (actionConfig != null) {
if (LOG.isTraceEnabled())
LOG.trace("Found action config");
PackageConfig packageConfig = configuration.getPackageConfig(actionConfig.getPackageName());
if (redirectNamespace.equals(packageConfig.getNamespace())) {
if (LOG.isTraceEnabled())
LOG.trace("Action is not a default - redirecting");
return buildActionConfig(redirectNamespace + "/", redirectResultTypeConfig);
}
if (LOG.isTraceEnabled())
LOG.trace("Action was a default - NOT redirecting");
}
if (resource != null) {
return buildActionConfig(redirectNamespace + "/", redirectResultTypeConfig);
}
}
if (resource != null) {
// Otherwise, if the URL is /foo or /foo/ look for index pages in /foo/
actionConfig = buildActionConfig(resource.path, resultsByExtension.get(resource.ext));
}
}
return actionConfig;
}
/**
* Finds a resource using the given path parts and all of the extensions in the map.
*
* @param resultsByExtension Map of extension to result type config objects.
* @param parts The parts of the resource.
* @return The resource path or null.
*/
protected Resource findResource(Map<String, ResultTypeConfig> resultsByExtension, String... parts) {
for (String ext : resultsByExtension.keySet()) {
String path = string(parts) + "." + ext;
if (LOG.isTraceEnabled()) {
LOG.trace("Checking for [#0]", path);
}
try {
if (servletContext.getResource(path) != null) {
return new Resource(path, ext);
}
} catch (MalformedURLException e) {
if (LOG.isErrorEnabled())
LOG.error("Unable to parse path to the web application resource [#0] skipping...", path);
}
}
return null;
}
protected ActionConfig buildActionConfig(String path, ResultTypeConfig resultTypeConfig) {
Map<String, ResultConfig> results = new HashMap<String,ResultConfig>();
HashMap<String, String> params = new HashMap<String, String>();
if (resultTypeConfig.getParams() != null) {
params.putAll(resultTypeConfig.getParams());
}
params.put(resultTypeConfig.getDefaultResultParam(), path);
// PackageConfig pkg = configuration.getPackageConfig(defaultParentPackageName);
// List<InterceptorMapping> interceptors = InterceptorBuilder.constructInterceptorReference(pkg,
// pkg.getFullDefaultInterceptorRef(), Collections.EMPTY_MAP, null, objectFactory);
ResultConfig config = new ResultConfig.Builder(Action.SUCCESS, resultTypeConfig.getClassName()).
addParams(params).build();
results.put(Action.SUCCESS, config);
//addInterceptors(interceptors).
return new ActionConfig.Builder(defaultParentPackageName, "execute", ActionSupport.class.getName()).
addResultConfigs(results).build();
}
private Result scanResultsByExtension(String ns, String actionName, String pathPrefix,
String resultCode, ActionContext actionContext) {
Map<String, ResultTypeConfig> resultsByExtension = conventionsService.getResultTypesByExtension(parentPackage);
Result result = null;
for (String ext : resultsByExtension.keySet()) {
if (LOG.isTraceEnabled()) {
String fqan = ns + "/" + actionName;
LOG.trace("Trying to locate the correct result for the FQ action [#0]"
+ " with an file extension of [#1] in the directory [#2] " + "and a result code of [#3]",
fqan, ext, pathPrefix, resultCode);
}
String path = string(pathPrefix, actionName, nameSeparator, resultCode, "." , ext);
result = findResult(path, resultCode, ext, actionContext, resultsByExtension);
if (result != null) {
break;
}
path = string(pathPrefix, actionName, "." , ext);
result = findResult(path, resultCode, ext, actionContext, resultsByExtension);
if (result != null) {
break;
}
// Issue #6 - Scan for result-code as page name
path = string(pathPrefix, resultCode, "." , ext);
result = findResult(path, resultCode, ext, actionContext, resultsByExtension);
if (result != null) {
break;
}
}
return result;
}
public Result handleUnknownResult(ActionContext actionContext, String actionName,
ActionConfig actionConfig, String resultCode) throws XWorkException {
PackageConfig pkg = configuration.getPackageConfig(actionConfig.getPackageName());
String ns = pkg.getNamespace();
String pathPrefix = determinePath(actionConfig, ns);
Result result = scanResultsByExtension(ns, actionName, pathPrefix, resultCode, actionContext);
if (result == null) {
// Try /idx/action/index.jsp
Map<String, ResultTypeConfig> resultsByExtension = conventionsService.getResultTypesByExtension(pkg);
for (String ext : resultsByExtension.keySet()) {
if (LOG.isTraceEnabled()) {
String fqan = ns + "/" + actionName;
LOG.trace("Checking for [#0/index.#1]", fqan, ext);
}
String path = string(pathPrefix, actionName, "/index", nameSeparator, resultCode, ".", ext);
result = findResult(path, resultCode, ext, actionContext, resultsByExtension);
if (result != null) {
break;
}
path = string(pathPrefix, actionName, "/index." , ext);
result = findResult(path, resultCode, ext, actionContext, resultsByExtension);
if (result != null) {
break;
}
}
}
if (result == null && resultCode != null) {
//try to find an action to chain to. If the source action is "foo" and
//the result is "bar", we will try to find an action called "foo-bar"
//in the same package
String chainedTo = new StringBuilder(actionName).append(nameSeparator).append(resultCode).toString();
ActionConfig chainedToConfig = pkg.getActionConfigs().get(chainedTo);
if (chainedToConfig != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Action [#0] used as chain result for [#1] and result [#2]", chainedTo, actionName, resultCode);
}
ResultTypeConfig chainResultType = pkg.getAllResultTypeConfigs().get("chain");
result = buildResult(chainedTo, resultCode, chainResultType, actionContext);
}
}
return result;
}
protected Result findResult(String path, String resultCode, String ext, ActionContext actionContext,
Map<String, ResultTypeConfig> resultsByExtension) {
try {
boolean traceEnabled = LOG.isTraceEnabled();
if (traceEnabled)
LOG.trace("Checking ServletContext for [#0]", path);
if (servletContext.getResource(path) != null) {
if (traceEnabled)
LOG.trace("Found");
return buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
}
if (traceEnabled)
LOG.trace("Checking ClasLoader for #0", path);
String classLoaderPath = path.startsWith("/") ? path.substring(1, path.length()) : path;
if (ClassLoaderUtils.getResource(classLoaderPath, getClass()) != null) {
if (traceEnabled)
LOG.trace("Found");
return buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
}
} catch (MalformedURLException e) {
if (LOG.isErrorEnabled())
LOG.error("Unable to parse template path: [#0] skipping...", path);
}
return null;
}
protected Result buildResult(String path, String resultCode, ResultTypeConfig config, ActionContext invocationContext) {
String resultClass = config.getClassName();
Map<String,String> params = new LinkedHashMap<String,String>();
if (config.getParams() != null) {
params.putAll(config.getParams());
}
params.put(config.getDefaultResultParam(), path);
ResultConfig resultConfig = new ResultConfig.Builder(resultCode, resultClass).addParams(params).build();
try {
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch (Exception e) {
throw new XWorkException("Unable to build convention result", e, resultConfig);
}
}
protected String string(String... parts) {
StringBuilder sb = new StringBuilder();
for (String part : parts) {
sb.append(part);
}
return sb.toString();
}
/**
* Determines the result path prefix that the request URL is for, minus the action name. This includes
* the base result location and the namespace, with all the slashes handled.
*
* @param actionConfig (Optional) The might be a ConventionActionConfig, from which we can get the
* default base result location of that specific action.
* @param namespace The current URL namespace.
* @return The path prefix and never null.
*/
protected String determinePath(ActionConfig actionConfig, String namespace) {
String finalPrefix = conventionsService.determineResultPath(actionConfig);
if (!finalPrefix.endsWith("/")) {
finalPrefix += "/";
}
if (namespace == null || "/".equals(namespace)) {
namespace = "";
}
if (namespace.length() > 0) {
if (namespace.startsWith("/")) {
namespace = namespace.substring(1);
}
if (!namespace.endsWith("/")) {
namespace += "/";
}
}
return finalPrefix + namespace;
}
/**
* Not used
*/
public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
throw new NoSuchMethodException();
}
public static class Resource {
final String path;
final String ext;
public Resource(String path, String ext) {
this.path = path;
this.ext = ext;
}
}
}
@@ -0,0 +1,67 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.Map;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
/**
* <p>
* This interface defines the conventions that are used by the convention plugin.
* In most cases the methods on this class will provide the best default for any
* values and also handle locating overrides of the default via the annotations
* that are part of the plugin.
* </p>
*/
public interface ConventionsService {
/**
* Locates the result location from annotations on the action class or the package or returns the
* default if no annotations are present.
*
* @param actionClass The action class.
* @return The result location if it is set in the annotations. Otherwise, the default result
* location is returned.
*/
String determineResultPath(Class<?> actionClass);
/**
* Delegates to the other method but first looks up the Action's class using the given class name.
*
* @param actionConfig (Optional) The configuration for the action that the result is being
* built for or null if the default result path is needed.
* @return The result location if it is set in the annotations for the class of the ActionConfig.
* Otherwise, the default result location is returned. If null is passed in, the default
* is returned,
*/
String determineResultPath(ActionConfig actionConfig);
/**
* Returns a mapping between the result type strings and the {@link ResultTypeConfig} instances
* based on the {@link PackageConfig} given.
*
* @param packageConfig The PackageConfig to get the result types for.
* @return The result types or an empty Map of nothing is configured.
*/
Map<String, ResultTypeConfig> getResultTypesByExtension(PackageConfig packageConfig);
}
@@ -0,0 +1,119 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import org.apache.struts2.convention.annotation.AnnotationTools;
import org.apache.struts2.convention.annotation.ResultPath;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
import com.opensymphony.xwork2.inject.Inject;
/**
* <p>
* This class is the implementation of the {@link ConventionsService}
* interface and provides all of the defaults and annotation handling.
* </p>
*/
public class ConventionsServiceImpl implements ConventionsService {
private String resultPath;
/**
* Constructs a new instance.
*
* @param resultPath The result path that is configured in the Struts configuration files using
* the constant name of <strong>struts.convention.result.path</strong>.
*/
@Inject
public ConventionsServiceImpl(@Inject("struts.convention.result.path") String resultPath) {
this.resultPath = resultPath;
}
/**
* {@inheritDoc}
*/
public String determineResultPath(Class<?> actionClass) {
String localResultPath = resultPath;
ResultPath resultPathAnnotation = AnnotationTools.findAnnotation(actionClass, ResultPath.class);
if (resultPathAnnotation != null) {
if (resultPathAnnotation.value().equals("") && resultPathAnnotation.property().equals("")) {
throw new ConfigurationException("The ResultPath annotation must have either" +
" a value or property specified.");
}
String property = resultPathAnnotation.property();
if (property.equals("")) {
localResultPath = resultPathAnnotation.value();
} else {
try {
ResourceBundle strutsBundle = ResourceBundle.getBundle("struts");
localResultPath = strutsBundle.getString(property);
} catch (Exception e) {
throw new ConfigurationException("The action class [" + actionClass + "] defines" +
" a @ResultPath annotation and a property definition however the" +
" struts.properties could not be found in the classpath using ResourceBundle" +
" OR the bundle exists but the property [" + property + "] is not defined" +
" in the file.", e);
}
}
}
return localResultPath;
}
/**
* {@inheritDoc}
*/
public String determineResultPath(ActionConfig actionConfig) {
if (actionConfig == null) {
return resultPath;
}
try {
return determineResultPath(Class.forName(actionConfig.getClassName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException("Invalid action class configuration that references an unknown " +
"class named [" + actionConfig.getClassName() + "]", e);
}
}
/**
* {@inheritDoc}
*/
public Map<String, ResultTypeConfig> getResultTypesByExtension(PackageConfig packageConfig) {
Map<String, ResultTypeConfig> results = packageConfig.getAllResultTypeConfigs();
Map<String, ResultTypeConfig> resultsByExtension = new HashMap<String, ResultTypeConfig>();
resultsByExtension.put("jsp", results.get("dispatcher"));
resultsByExtension.put("vm", results.get("velocity"));
resultsByExtension.put("ftl", results.get("freemarker"));
// Issue 22 - Add html and htm as default result extensions
resultsByExtension.put("html", results.get("dispatcher"));
resultsByExtension.put("htm", results.get("dispatcher"));
return resultsByExtension;
}
}
@@ -0,0 +1,74 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import com.opensymphony.xwork2.inject.Inject;
/**
* <p>
* This class strips the word <b>Action</b> from the end of the class name
* and possibly lowercases the name as well depending on the value of the
* constant <strong>struts.convention.action.name.lowercase</strong>. If the
* constant is set to <strong>true</strong>, this class will lowercase all
* action names.
* </p>
*/
public class DefaultActionNameBuilder implements ActionNameBuilder {
private String actionSuffix = "Action";
private boolean lowerCase;
@Inject
public DefaultActionNameBuilder(@Inject(value="struts.convention.action.name.lowercase") String lowerCase) {
this.lowerCase = Boolean.parseBoolean(lowerCase);
}
/**
* @param actionSuffix (Optional) Classes that end with these value will be mapped as actions
* (defaults to "Action")
*/
@Inject(value = "struts.convention.action.suffix", required = false)
public void setActionSuffix(String actionSuffix) {
if (!StringTools.isTrimmedEmpty(actionSuffix)) {
this.actionSuffix = actionSuffix;
}
}
public String build(String className) {
String actionName = className;
// Truncate Action suffix if found
if (actionName.endsWith(actionSuffix)) {
actionName = actionName.substring(0, actionName.length() - actionSuffix.length());
}
// Force initial letter of action to lowercase, if desired
if ((lowerCase) && (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();
}
return actionName;
}
}
@@ -0,0 +1,109 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.AnnotationTools;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.AnnotationUtils;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* Builds interceptor mappings from annotations.
* </p>
*/
public class DefaultInterceptorMapBuilder implements InterceptorMapBuilder {
private static final Logger LOG = LoggerFactory
.getLogger(DefaultInterceptorMapBuilder.class);
private Configuration configuration;
public List<InterceptorMapping> build(Class<?> actionClass, PackageConfig.Builder builder,
String actionName, Action annotation) {
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(
10);
//from @InterceptorRefs annotation
InterceptorRefs interceptorRefs = AnnotationTools.findAnnotation(actionClass, InterceptorRefs.class);
if (interceptorRefs != null)
interceptorList.addAll(build(interceptorRefs.value(), actionName, builder));
//from @InterceptorRef annotation
InterceptorRef interceptorRef = AnnotationTools.findAnnotation(actionClass, InterceptorRef.class);
if (interceptorRef != null)
interceptorList.addAll(build(new InterceptorRef[] {interceptorRef}, actionName, builder));
//from @Action annotation
if (annotation != null) {
InterceptorRef[] interceptors = annotation.interceptorRefs();
if (interceptors != null) {
interceptorList.addAll(build(interceptors, actionName, builder));
}
}
return interceptorList;
}
protected List<InterceptorMapping> build(InterceptorRef[] interceptors, String actionName, PackageConfig.Builder builder) {
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(
10);
for (InterceptorRef interceptor : interceptors) {
if (LOG.isTraceEnabled())
LOG.trace("Adding interceptor [#0] to [#1]",
interceptor.value(), actionName);
Map<String, String> params = StringTools.createParameterMap(interceptor
.params());
interceptorList.addAll(buildInterceptorList(builder,
interceptor, params));
}
return interceptorList;
}
protected List<InterceptorMapping> buildInterceptorList(
PackageConfig.Builder builder, InterceptorRef ref, Map params) {
return InterceptorBuilder.constructInterceptorReference(builder, ref
.value(), params, builder.build().getLocation(),
(ObjectFactory) configuration.getContainer().getInstance(
ObjectFactory.class));
}
@Inject
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
}
@@ -0,0 +1,462 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.config.ConfigurationException;
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.inject.Inject;
import com.opensymphony.xwork2.util.finder.ResourceFinder;
import com.opensymphony.xwork2.util.finder.Test;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* This class implements the ResultMapBuilder and traverses the web
* application content directory looking for reasonably named JSPs and
* other result types as well as annotations. This naming is in this
* form:
* </p>
*
* <pre>
* /resultPath/namespace/action-&lt;result>.jsp
* </pre>
*
* <p>
* If there are any files in these locations than a result is created
* for each one and the result names is the last portion of the file
* name up to the . (dot).
* </p>
*
* <p>
* When results are found, new ResultConfig instances are created. The
* result config that is created has a number of thing to be aware of:
* </p>
*
* <ul>
* <li>The result config contains the location parameter, which is
* required by most result classes to figure out where to find the result.
* In addition, the config has all the parameters from the default result-type
* configuration.</li>
* </ul>
*
* <p>
* After loading the files in the web application, this class will then
* use any annotations on the action class to override what was found in
* the web application files. These annotations are the {@link Result}
* and {@link Results} annotations. These two annotations allow an action
* to supply different or non-forward based results for specific return
* values of an action method.
* </p>
*
* <p>
* The result path used by this class for locating JSPs and other
* such result files can be set using the Struts2 constant named
* <strong>struts.convention.result.path</strong> or using the
* {@link org.apache.struts2.convention.annotation.ResultPath}
* annotation.
* </p>
*
* <p>
* This class will also locate and configure Results in the classpath,
* including velocity and FreeMarker templates inside the classpath.
* </p>
*
* <p>
* All results that are conigured from resources are given a type corresponding
* to the resources extension. The extensions and types are given in the
* table below:
* </p>
*
* <table>
* <tr><th>Extension</th><th>Type</th></tr>
* <tr><td>.jsp</td><td>dispatcher</td</tr>
* <tr><td>.html</td><td>dispatcher</td</tr>
* <tr><td>.htm</td><td>dispatcher</td</tr>
* <tr><td>.vm</td><td>velocity</td</tr>
* <tr><td>.ftl</td><td>freemarker</td</tr>
* </table>
*/
public class DefaultResultMapBuilder implements ResultMapBuilder {
private static final Logger LOG = LoggerFactory.getLogger(DefaultResultMapBuilder.class);
private final ServletContext servletContext;
private Set<String> relativeResultTypes;
private ConventionsService conventionsService;
private boolean flatResultLayout = true;
/**
* Constructs the SimpleResultMapBuilder using the given result location.
*
* @param servletContext The ServletContext for finding the resources of the web application.
* @param conventionsService The service used to assist in finding configuration and conventions.
* @param relativeResultTypes The list of result types that can have locations that are relative
* and the result location (which is the resultPath plus the namespace) prepended to them.
*/
@Inject
public DefaultResultMapBuilder(ServletContext servletContext, ConventionsService conventionsService,
@Inject("struts.convention.relative.result.types") String relativeResultTypes) {
this.servletContext = servletContext;
this.relativeResultTypes = new HashSet<String>(Arrays.asList(relativeResultTypes.split("\\s*[,]\\s*")));
this.conventionsService = conventionsService;
}
/**
* @param flatResultLayout If 'true' result resources will be expected to be in the form
* ${namespace}/${actionName}-${result}.${extension}, otherwise in the form
* ${namespace}/${actionName}/${result}.${extension}
*/
@Inject("struts.convention.result.flatLayout")
public void setFlatResultLayout(String flatResultLayout) {
this.flatResultLayout = "true".equals(flatResultLayout);
}
/**
* {@inheritDoc}
*/
public Map<String, ResultConfig> build(Class<?> actionClass,
org.apache.struts2.convention.annotation.Action annotation, String actionName,
PackageConfig packageConfig) {
// Get the default result location from the annotation or configuration
String defaultResultPath = conventionsService.determineResultPath(actionClass);
// Add a slash
if (!defaultResultPath.endsWith("/")) {
defaultResultPath = defaultResultPath + "/";
}
// Check for resources with the action name
final String namespace = packageConfig.getNamespace();
if (namespace != null && namespace.startsWith("/")) {
defaultResultPath = defaultResultPath + namespace.substring(1);
} else if (namespace != null) {
defaultResultPath = defaultResultPath + namespace;
}
if (LOG.isTraceEnabled()) {
LOG.trace("Using final calculated namespace [#0]", namespace);
}
// Add that ending slash for concatentation
if (!defaultResultPath.endsWith("/")) {
defaultResultPath += "/";
}
String resultPrefix = defaultResultPath + actionName;
Map<String, ResultConfig> results = new HashMap<String, ResultConfig>();
Map<String, ResultTypeConfig> resultsByExtension = conventionsService.getResultTypesByExtension(packageConfig);
createFromResources(actionClass, results, defaultResultPath, resultPrefix, actionName,
packageConfig, resultsByExtension);
if (annotation != null && annotation.results() != null && annotation.results().length > 0) {
createFromAnnotations(results, defaultResultPath, packageConfig, annotation.results(),
actionClass, resultsByExtension);
}
Results resultsAnn = actionClass.getAnnotation(Results.class);
if (resultsAnn != null) {
createFromAnnotations(results, defaultResultPath, packageConfig, resultsAnn.value(),
actionClass, resultsByExtension);
}
Result resultAnn = actionClass.getAnnotation(Result.class);
if (resultAnn != null) {
createFromAnnotations(results, defaultResultPath, packageConfig, new Result[]{resultAnn},
actionClass, resultsByExtension);
}
return results;
}
/**
* Creates any result types from the resources available in the web application. This scans the
* web application resources using the servlet context.
*
* @param actionClass The action class the results are being built for.
* @param results The results map to put the result configs created into.
* @param resultPath The calculated path to the resources.
* @param resultPrefix The prefix for the result. This is usually <code>/resultPath/actionName</code>.
* @param actionName The action name which is used only for logging in this implementation.
* @param packageConfig The package configuration which is passed along in order to determine
* @param resultsByExtension The map of extensions to result type configuration instances.
*/
protected void createFromResources(Class<?> actionClass, Map<String, ResultConfig> results,
final String resultPath, final String resultPrefix, final String actionName,
PackageConfig packageConfig, Map<String, ResultTypeConfig> resultsByExtension) {
if (LOG.isTraceEnabled()) {
LOG.trace("Searching for results in the Servlet container at [#0]" +
" with result prefix of [#1]", resultPath, resultPrefix);
}
// Build from web application using the ServletContext
@SuppressWarnings("unchecked")
Set<String> paths = servletContext.getResourcePaths(flatResultLayout ? resultPath : resultPrefix);
if (paths != null) {
for (String path : paths) {
if (LOG.isTraceEnabled()) {
LOG.trace("Processing resource path [#0]", path);
}
makeResults(actionClass, path, resultPrefix, results, packageConfig, resultsByExtension);
}
}
// Building from the classpath
String classPathLocation = resultPath.startsWith("/") ?
resultPath.substring(1, resultPath.length()) : resultPath;
if (LOG.isTraceEnabled()) {
LOG.trace("Searching for results in the class path at [#0]"
+ " with a result prefix of [#1] and action name [#2]", classPathLocation, resultPrefix,
actionName);
}
ResourceFinder finder = new ResourceFinder(classPathLocation);
try {
Map<String, URL> matches = finder.getResourcesMap("");
if (matches != null) {
Test<URL> resourceTest = getResourceTest(resultPath, actionName);
for (Map.Entry<String, URL> entry : matches.entrySet()) {
if (resourceTest.test(entry.getValue())) {
if (LOG.isTraceEnabled()) {
LOG.trace("Processing URL [#0]", entry.getKey());
}
String urlStr = entry.getValue().toString();
int index = urlStr.lastIndexOf(resultPrefix);
String path = urlStr.substring(index);
makeResults(actionClass, path, resultPrefix, results, packageConfig, resultsByExtension);
}
}
}
} catch (IOException ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to scan directory [#0] for results", ex, classPathLocation);
}
}
private Test<URL> getResourceTest(final String resultPath, final String actionName) {
return new Test<URL>() {
public boolean test(URL url) {
String urlStr = url.toString();
int index = urlStr.lastIndexOf(resultPath);
String path = urlStr.substring(index + resultPath.length());
return path.startsWith(actionName);
}
};
}
/**
* Makes all the results for the given path.
*
* @param actionClass The action class the results are being built for.
* @param path The path to build the result for.
* @param resultPrefix The is the result prefix which is the result location plus the action name.
* This is used to determine if the path contains a result code or not.
* @param results The Map to place the result(s)
* @param packageConfig The package config the results belong to.
* @param resultsByExtension The map of extensions to result type configuration instances.
*/
protected void makeResults(Class<?> actionClass, String path, String resultPrefix,
Map<String, ResultConfig> results, PackageConfig packageConfig,
Map<String, ResultTypeConfig> resultsByExtension) {
if (path.startsWith(resultPrefix)) {
int indexOfDot = path.indexOf('.', resultPrefix.length());
// This case is when the path doesn't contain a result code
if (indexOfDot == resultPrefix.length() || !flatResultLayout) {
if (LOG.isTraceEnabled()) {
LOG.trace("The result file [#0] has no result code and therefore" +
" will be associated with success, input and error by default. This might" +
" be overridden by another result file or an annotation.", path);
}
if (!results.containsKey(Action.SUCCESS)) {
ResultConfig success = createResultConfig(actionClass,
new ResultInfo(Action.SUCCESS, path, packageConfig, resultsByExtension),
packageConfig, null);
results.put(Action.SUCCESS, success);
}
if (!results.containsKey(Action.INPUT)) {
ResultConfig input = createResultConfig(actionClass,
new ResultInfo(Action.INPUT, path, packageConfig, resultsByExtension),
packageConfig, null);
results.put(Action.INPUT, input);
}
if (!results.containsKey(Action.ERROR)) {
ResultConfig error = createResultConfig(actionClass,
new ResultInfo(Action.ERROR, path, packageConfig, resultsByExtension),
packageConfig, null);
results.put(Action.ERROR, error);
}
// This case is when the path contains a result code
} else if (indexOfDot > resultPrefix.length()) {
if (LOG.isTraceEnabled()) {
LOG.trace("The result file [#0] has a result code and therefore" +
" will be associated with only that result code.", path);
}
String resultCode = path.substring(resultPrefix.length() + 1, indexOfDot);
ResultConfig result = createResultConfig(actionClass,
new ResultInfo(resultCode, path, packageConfig, resultsByExtension),
packageConfig, null);
results.put(resultCode, result);
}
}
}
protected void createFromAnnotations(Map<String, ResultConfig> resultConfigs,
String resultPath, PackageConfig packageConfig, Result[] results,
Class<?> actionClass, Map<String, ResultTypeConfig> resultsByExtension) {
// Check for multiple results on the class
for (Result result : results) {
ResultConfig config = createResultConfig(actionClass,
new ResultInfo(result, packageConfig, resultPath, actionClass, resultsByExtension),
packageConfig, result);
if (config != null) {
resultConfigs.put(config.getName(), config);
}
}
}
/**
* Creates the result configuration for the single result annotation. This will use all the
* information from the annotation and anything that isn't specified will be fetched from the
* PackageConfig defaults (if they exist).
*
* @param actionClass The action class the results are being built for.
* @param info The result info that is used to create the ResultConfig instance.
* @param packageConfig The PackageConfig to use to fetch defaults for result and parameters.
* @param result (Optional) The result annotation to pull additional information from.
* @return The ResultConfig or null if the Result annotation is given and the annotation is
* targeted to some other action than this one.
*/
@SuppressWarnings(value = {"unchecked"})
protected ResultConfig createResultConfig(Class<?> actionClass, ResultInfo info,
PackageConfig packageConfig, Result result) {
// Look up by the type that was determined from the annotation or by the extension in the
// ResultInfo class
ResultTypeConfig resultTypeConfig = packageConfig.getAllResultTypeConfigs().get(info.type);
if (resultTypeConfig == null) {
throw new ConfigurationException("The Result type [" + info.type + "] which is" +
" defined in the Result annotation on the class [" + actionClass + "] or determined" +
" by the file extension or is the default result type for the PackageConfig of the" +
" action, could not be found as a result-type defined for the Struts/XWork package [" +
packageConfig.getName() + "]");
}
// Add the default parameters for the result type config (if any)
HashMap<String, String> params = new HashMap<String, String>();
if (resultTypeConfig.getParams() != null) {
params.putAll(resultTypeConfig.getParams());
}
// Handle the annotation
if (result != null) {
params.putAll(StringTools.createParameterMap(result.params()));
}
// Map the location to the default param for the result or a param named location
if (info.location != null) {
String defaultParamName = resultTypeConfig.getDefaultResultParam();
if (!params.containsKey(defaultParamName)) {
params.put(defaultParamName, info.location);
}
} else if (LOG.isWarnEnabled()){
LOG.warn("Result [#0] for action class [#1] is missing the location", info.name,
actionClass.getSimpleName());
}
return new ResultConfig.Builder(info.name, resultTypeConfig.getClassName()).addParams(params).build();
}
class ResultInfo {
public final String name;
public final String location;
public final String type;
public ResultInfo(String name, String location, PackageConfig packageConfig,
Map<String, ResultTypeConfig> resultsByExtension) {
this.name = name;
this.location = location;
this.type = determineType(location, packageConfig, resultsByExtension);
}
public ResultInfo(Result result, PackageConfig packageConfig, String resultPath,
Class<?> actionClass, Map<String, ResultTypeConfig> resultsByExtension) {
this.name = result.name();
if (!StringTools.isTrimmedEmpty(result.type())) {
this.type = result.type();
} else if (!StringTools.isTrimmedEmpty(result.location())) {
this.type = determineType(result.location(), packageConfig, resultsByExtension);
} else {
throw new ConfigurationException("The action class [" + actionClass + "] contains a " +
"result annotation that has no type parameter and no location parameter. One of " +
"these must be defined.");
}
// See if we can handle relative locations or not
if (!StringTools.isTrimmedEmpty(result.location())) {
if (relativeResultTypes.contains(this.type) && !result.location().startsWith("/")) {
location = resultPath + result.location();
} else {
location = result.location();
}
} else {
this.location = null;
}
}
String determineType(String location, PackageConfig packageConfig,
Map<String, ResultTypeConfig> resultsByExtension) {
int indexOfDot = location.lastIndexOf(".");
if (indexOfDot > 0) {
String extension = location.substring(indexOfDot + 1);
ResultTypeConfig resultTypeConfig = resultsByExtension.get(extension);
if (resultTypeConfig != null) {
return resultTypeConfig.getName();
} else
throw new ConfigurationException("Unable to find a result type for extension [" + extension + "] " +
"in location attribute [" + location + "].");
} else {
return packageConfig.getFullDefaultResultType();
}
}
}
}
@@ -0,0 +1,48 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.List;
import org.apache.struts2.convention.annotation.Action;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.PackageConfig;
/**
* <p>
* This interface defines how interceptors are built from
* annotations.
* </p>
*/
public interface InterceptorMapBuilder {
/**
* Builds the interceptor configurations given the action information.
*
* @param actionClass The class of the action.
* @param annotation The action annotation.
* @param actionName The action name.
* @param builder The package configuration builder.
* @return The mapping of the interceptors. If there were none found
* then this should return an empty List.
*/
List<InterceptorMapping> build(Class<?> actionClass, PackageConfig.Builder builder, String actionName, Action annotation);
}
@@ -0,0 +1,797 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.AnnotationTools;
import org.apache.struts2.convention.annotation.DefaultInterceptorRef;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.ParentPackage;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.finder.ClassFinder;
import com.opensymphony.xwork2.util.finder.Test;
import com.opensymphony.xwork2.util.finder.UrlSet;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* This class implements the ActionConfigBuilder interface.
* </p>
*/
public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
private static final Logger LOG = LoggerFactory.getLogger(PackageBasedActionConfigBuilder.class);
private final Configuration configuration;
private final ActionNameBuilder actionNameBuilder;
private final ResultMapBuilder resultMapBuilder;
private final InterceptorMapBuilder interceptorMapBuilder;
private final ObjectFactory objectFactory;
private final String defaultParentPackage;
private final boolean redirectToSlash;
private String[] actionPackages;
private String[] excludePackages;
private String[] packageLocators;
private String[] excludeJars;
private String packageLocatorsBasePackage;
private boolean disableJarScanning = true;
private boolean disableActionScanning = false;
private boolean disablePackageLocatorsScanning = false;
private String actionSuffix = "Action";
private boolean checkImplementsAction = true;
private boolean mapAllMatches = false;
/**
* Constructs actions based on a list of packages.
*
* @param configuration The XWork configuration that the new package configs and action configs
* are added to.
* @param actionNameBuilder The action name builder used to convert action class names to action
* names.
* @param resultMapBuilder The result map builder used to create ResultConfig mappings for each
* action.
* @param interceptorMapBuilder The interceptor map builder used to create InterceptorConfig mappings for each
* action.
* @param objectFactory The ObjectFactory used to create the actions and such.
* @param redirectToSlash A boolean parameter that controls whether or not this will create an
* action for indexes. If this is set to true, index actions are not created because
* the unknown handler will redirect from /foo to /foo/. The only action that is created
* is to the empty action in the namespace (e.g. the namespace /foo and the action "").
* @param defaultParentPackage The default parent package for all the configuration.
*/
@Inject
public PackageBasedActionConfigBuilder(Configuration configuration, ActionNameBuilder actionNameBuilder,
ResultMapBuilder resultMapBuilder, InterceptorMapBuilder interceptorMapBuilder, ObjectFactory objectFactory,
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
@Inject("struts.convention.default.parent.package") String defaultParentPackage) {
// Validate that the parameters are okay
this.configuration = configuration;
this.actionNameBuilder = actionNameBuilder;
this.resultMapBuilder = resultMapBuilder;
this.interceptorMapBuilder = interceptorMapBuilder;
this.objectFactory = objectFactory;
this.redirectToSlash = Boolean.parseBoolean(redirectToSlash);
if (LOG.isTraceEnabled()) {
LOG.trace("Setting action default parent package to [#0]", defaultParentPackage);
}
this.defaultParentPackage = defaultParentPackage;
}
/**
* @param disableActionScanning Disable scanning for actions
*/
@Inject(value = "struts.convention.action.disableScanning", required = false)
public void setDisableActionScanning(String disableActionScanning) {
this.disableActionScanning = "true".equals(disableActionScanning);
}
/**
* @param exlcudeJars Comma separated list of regular expressions of jars to be exluded.
* Ignored if "struts.convention.action.disableJarScanning" is true
*/
@Inject(value = "struts.convention.action.excludeJars", required = false)
public void setExcludeJars(String excludeJars) {
this.excludeJars = excludeJars.split("\\s*[,]\\s*");;
}
/**
* @param disableJarScanning Disable scanning jar files for actions
*/
@Inject(value = "struts.convention.action.disableJarScanning", required = false)
public void setDisableJarScanning(String disableJarScanning) {
this.disableJarScanning = "true".equals(disableJarScanning);
}
/**
* @param disableActionScanning If set to true, only the named packages will be scanned
*/
@Inject(value = "struts.convention.package.locators.disable", required = false)
public void setDisablePackageLocatorsScanning(String disablePackageLocatorsScanning) {
this.disablePackageLocatorsScanning = "true".equals(disablePackageLocatorsScanning);
}
/**
* @param actionPackages (Optional) An optional list of action packages that this should create
* configuration for.
*/
@Inject(value = "struts.convention.action.packages", required = false)
public void setActionPackages(String actionPackages) {
if (!StringTools.isTrimmedEmpty(actionPackages)) {
this.actionPackages = actionPackages.split("\\s*[,]\\s*");
}
}
/**
* @param actionPackages (Optional) Map classes that implement com.opensymphony.xwork2.Action
* as actions
*/
@Inject(value = "struts.convention.action.checkImplementsAction", required = false)
public void setCheckImplementsAction(String checkImplementsAction) {
this.checkImplementsAction = "true".equals(checkImplementsAction);
}
/**
* @param actionSuffix (Optional) Classes that end with these value will be mapped as actions
* (defaults to "Action")
*/
@Inject(value = "struts.convention.action.suffix", required = false)
public void setActionSuffix(String actionSuffix) {
if (!StringTools.isTrimmedEmpty(actionSuffix)) {
this.actionSuffix = actionSuffix;
}
}
/**
* @param excludePackages (Optional) A list of packages that should be skipped when building
* configuration.
*/
@Inject(value = "struts.convention.exclude.packages", required = false)
public void setExcludePackages(String excludePackages) {
if (!StringTools.isTrimmedEmpty(excludePackages)) {
this.excludePackages = excludePackages.split("\\s*[,]\\s*");
}
}
/**
* @param packageLocators (Optional) A list of names used to find action packages.
*/
@Inject(value = "struts.convention.package.locators", required = false)
public void setPackageLocators(String packageLocators) {
this.packageLocators = packageLocators.split("\\s*[,]\\s*");
}
/**
* @param packageLocatorsBasePackage (Optional) If set, only packages that start with this
* name will be scanned for actions.
*/
@Inject(value = "struts.convention.package.locators.basePackage", required = false)
public void setPackageLocatorsBase(String packageLocatorsBasePackage) {
this.packageLocatorsBasePackage = packageLocatorsBasePackage;
}
/**
* @param mapAllMatches (Optional) Map actions that match the "*${Suffix}" pattern
* even if they don't have a default method. The mapping from
* the url to the action will be delegated the action mapper.
*/
@Inject(value = "struts.convention.action.mapAllMatches", required = false)
public void setMapAllMatches(String mapAllMatches) {
this.mapAllMatches = "true".equals(mapAllMatches);
}
/**
* Builds the action configurations by loading all classes in the packages specified by the
* property <b>struts.convention.action.packages</b> and then figuring out which classes implement Action
* or have Action in their name. Next, if this class is in a Java package that hasn't been
* inspected a new PackageConfig (XWork) is created for that Java package using the Java package
* name. This will contain all the ActionConfigs for all the Action classes that are discovered
* within that Java package. Next, each class is inspected for the {@link ParentPackage}
* annotation which is used to control the parent package for a specific action. Lastly, the
* {@link ResultMapBuilder} is used to create ResultConfig instances of the action.
*/
public void buildActionConfigs() {
if (!disableActionScanning ) {
if (actionPackages == null && packageLocators == null) {
throw new ConfigurationException("At least a list of action packages or action package locators " +
"must be given using one of the properties [struts.convention.action.packages] or " +
"[struts.convention.package.locators]");
}
if (LOG.isTraceEnabled()) {
LOG.trace("Loading action configurations");
if (actionPackages != null) {
LOG.trace("Actions being loaded from action packages " + Arrays.asList(actionPackages));
}
if (packageLocators != null) {
LOG.trace("Actions being loaded using package locators " + Arrays.asList(packageLocators));
}
if (excludePackages != null) {
LOG.trace("Excluding actions from packages " + Arrays.asList(excludePackages));
}
}
Set<Class> classes = findActions();
buildConfiguration(classes);
}
}
@SuppressWarnings("unchecked")
protected Set<Class> findActions() {
Set<Class> classes = new HashSet<Class>();
try {
if (actionPackages != null || (packageLocators != null && !disablePackageLocatorsScanning)) {
ClassFinder finder = new ClassFinder(getClassLoader(), buildUrlSet().getUrls(), true);
// named packages
if (actionPackages != null) {
for (String packageName : actionPackages) {
Test<ClassFinder.ClassInfo> test = getPackageFinderTest(packageName);
classes.addAll(finder.findClasses(test));
}
}
//package locators
if (packageLocators != null && !disablePackageLocatorsScanning) {
for (String packageLocator : packageLocators) {
Test<ClassFinder.ClassInfo> test = getPackageLocatorTest(packageLocator);
classes.addAll(finder.findClasses(test));
}
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to scan named packages", ex);
}
return classes;
}
private UrlSet buildUrlSet() throws IOException {
UrlSet urlSet = new UrlSet(getClassLoader());
urlSet = urlSet.exclude(ClassLoader.getSystemClassLoader().getParent());
urlSet = urlSet.excludeJavaExtDirs();
urlSet = urlSet.excludeJavaEndorsedDirs();
urlSet = urlSet.excludeJavaHome();
urlSet = urlSet.excludePaths(System.getProperty("sun.boot.class.path", ""));
urlSet = urlSet.exclude(".*/JavaVM.framework/.*");
if (disableJarScanning) {
urlSet = urlSet.exclude(".*?jar(!/)?");
} else if (excludeJars != null) {
for (String pattern : excludeJars) {
urlSet = urlSet.exclude(pattern.trim());
}
}
return urlSet;
}
private ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
protected Test<ClassFinder.ClassInfo> getPackageFinderTest(final String packageName) {
// so "my.package" does not match "my.package2.test"
final String strictPackageName = packageName + ".";
return new Test<ClassFinder.ClassInfo>() {
public boolean test(ClassFinder.ClassInfo classInfo) {
String classPackageName = classInfo.getPackageName();
boolean inPackage = classPackageName.equals(packageName) || classPackageName.startsWith(strictPackageName);
boolean nameMatches = classInfo.getName().endsWith(actionSuffix);
try {
return inPackage && (nameMatches || (checkImplementsAction && com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
} catch (ClassNotFoundException ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to load class [#0]", ex, classInfo.getName());
return false;
}
}
};
}
protected Test<ClassFinder.ClassInfo> getPackageLocatorTest(final String packageLocator) {
return new Test<ClassFinder.ClassInfo>() {
public boolean test(ClassFinder.ClassInfo classInfo) {
String packageName = classInfo.getPackageName();
if (packageName.length() > 0 && (packageLocatorsBasePackage == null || packageName.startsWith(packageLocatorsBasePackage))) {
String[] splitted = packageName.split("\\.");
boolean packageMatches = StringTools.contains(splitted, packageLocator, false);
boolean nameMatches = classInfo.getName().endsWith(actionSuffix);
try {
return packageMatches && (nameMatches || (checkImplementsAction && com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
} catch (ClassNotFoundException ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to load class [#0]", ex, classInfo.getName());
return false;
}
} else
return false;
}
};
}
@SuppressWarnings("unchecked")
protected void buildConfiguration(Set<Class> classes) {
Map<String, PackageConfig.Builder> packageConfigs = new HashMap<String, PackageConfig.Builder>();
for (Class<?> actionClass : classes) {
// Skip all interfaces, enums, annotations, and abstract classes
if (actionClass.isAnnotation() || actionClass.isInterface() || actionClass.isEnum() ||
(actionClass.getModifiers() & Modifier.ABSTRACT) != 0) {
continue;
}
// Tell the ObjectFactory about this class
try {
objectFactory.getClassInstance(actionClass.getName());
} catch (ClassNotFoundException e) {
// Impossible
new Throwable().printStackTrace();
System.exit(1);
}
// Determine the action package
String actionPackage = actionClass.getPackage().getName();
if (LOG.isDebugEnabled()) {
LOG.debug("Processing class [#0] in package [#1]", actionClass.getName(), actionPackage);
}
// Determine the default namespace and action name
List<String> namespaces = determineActionNamespace(actionClass);
for (String namespace : namespaces) {
String defaultActionName = determineActionName(actionClass);
String defaultActionMethod = "execute";
PackageConfig.Builder defaultPackageConfig = getPackageConfig(packageConfigs, namespace,
actionPackage, actionClass, null);
// Verify that the annotations have no errors and also determine if the default action
// configuration should still be built or not.
Map<String, List<Action>> map = getActionAnnotations(actionClass);
Set<String> actionNames = new HashSet<String>();
if (!map.containsKey(defaultActionMethod) && ReflectionTools.containsMethod(actionClass, defaultActionMethod)) {
boolean found = false;
for (String method : map.keySet()) {
List<Action> actions = map.get(method);
for (Action action : actions) {
// Check if there are duplicate action names in the annotations.
String actionName = action.value().equals(Action.DEFAULT_VALUE) ? defaultActionName : action.value();
if (actionNames.contains(actionName)) {
throw new ConfigurationException("The action class [" + actionClass +
"] contains two methods with an action name annotation whose value " +
"is the same (they both might be empty as well).");
} else {
actionNames.add(actionName);
}
// Check this annotation is the default action
if (action.value().equals(Action.DEFAULT_VALUE)) {
found = true;
}
}
}
// Build the default
if (!found) {
createActionConfig(defaultPackageConfig, actionClass, defaultActionName, defaultActionMethod, null);
}
}
// Build the actions for the annotations
for (String method : map.keySet()) {
List<Action> actions = map.get(method);
for (Action action : actions) {
PackageConfig.Builder pkgCfg = defaultPackageConfig;
if (action.value().contains("/")) {
pkgCfg = getPackageConfig(packageConfigs, namespace, actionPackage,
actionClass, action);
}
createActionConfig(pkgCfg, actionClass, defaultActionName, method, action);
}
}
// some actions will not have any @Action or a default method, like the rest actions
// where the action mapper is the one that finds the right method at runtime
if (map.isEmpty() && mapAllMatches) {
Action actionAnnotation = actionClass.getAnnotation(Action.class);
createActionConfig(defaultPackageConfig, actionClass, defaultActionName, null, actionAnnotation);
}
}
}
buildIndexActions(packageConfigs);
// Add the new actions to the configuration
Set<String> packageNames = packageConfigs.keySet();
for (String packageName : packageNames) {
configuration.addPackageConfig(packageName, packageConfigs.get(packageName).build());
}
}
/**
* Determines the namespace(s) for the action based on the action class. If there is a {@link Namespace}
* annotation on the class (including parent classes) or on the package that the class is in, than
* it is used. Otherwise, the Java package name that the class is in is used in conjunction with
* either the <b>struts.convention.action.packages</b> or <b>struts.convention.package.locators</b>
* configuration values. These are used to determine which part of the Java package name should
* be converted into the namespace for the XWork PackageConfig.
*
* @param actionClass The action class.
* @return The namespace or an empty string.
*/
protected List<String> determineActionNamespace(Class<?> actionClass) {
List<String> namespaces = new ArrayList<String>();
// Check if there is a class or package level annotation for the namespace
//single namespace
Namespace namespaceAnnotation = AnnotationTools.findAnnotation(actionClass, Namespace.class);
if (namespaceAnnotation != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Using non-default action namespace from Namespace annotation of [#0]", namespaceAnnotation.value());
}
namespaces.add(namespaceAnnotation.value());
}
//multiple annotations
Namespaces namespacesAnnotation = AnnotationTools.findAnnotation(actionClass, Namespaces.class);
if (namespacesAnnotation != null) {
if (LOG.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
for (Namespace namespace : namespacesAnnotation.value())
sb.append(namespace.value()).append(",");
sb.deleteCharAt(sb.length() - 1);
LOG.trace("Using non-default action namespaces from Namespaces annotation of [#0]", sb.toString());
}
for (Namespace namespace : namespacesAnnotation.value())
namespaces.add(namespace.value());
}
//don't use default if there are annotations
if (!namespaces.isEmpty())
return namespaces;
String pkg = actionClass.getPackage().getName();
String pkgPart = null;
if (actionPackages != null) {
for (String actionPackage : actionPackages) {
if (pkg.startsWith(actionPackage)) {
pkgPart = actionClass.getName().substring(actionPackage.length() + 1);
}
}
}
if (pkgPart == null && packageLocators != null) {
for (String packageLocator : packageLocators) {
int index = pkg.lastIndexOf(packageLocator);
// This ensures that the match is at the end, beginning or has a dot on each side of it
if (index >= 0 && (index + packageLocator.length() == pkg.length() || index == 0 ||
(pkg.charAt(index - 1) == '.' && pkg.charAt(index + packageLocator.length()) == '.'))) {
pkgPart = actionClass.getName().substring(index + packageLocator.length() + 1);
}
}
}
if (pkgPart != null) {
final int indexOfDot = pkgPart.lastIndexOf('.');
if (indexOfDot >= 0) {
String convertedNamespace = actionNameBuilder.build(pkgPart.substring(0, indexOfDot));
namespaces.add("/" + convertedNamespace.replace('.', '/'));
return namespaces;
}
}
namespaces.add("");
return namespaces;
}
/**
* Converts the class name into an action name using the ActionNameBuilder.
*
* @param actionClass The action class.
* @return The action name.
*/
protected String determineActionName(Class<?> actionClass) {
String actionName = actionNameBuilder.build(actionClass.getSimpleName());
if (LOG.isTraceEnabled()) {
LOG.trace("Got actionName for class [#0] of [#1]", actionClass.toString(), actionName);
}
return actionName;
}
/**
* Locates all of the {@link Actions} and {@link Action} annotations on methods within the Action
* class and its parent classes.
*
* @param actionClass The action class.
* @return The list of annotations or an empty list if there are none.
*/
protected Map<String, List<Action>> getActionAnnotations(Class<?> actionClass) {
Method[] methods = actionClass.getMethods();
Map<String, List<Action>> map = new HashMap<String, List<Action>>();
for (Method method : methods) {
Actions actionsAnnotation = method.getAnnotation(Actions.class);
if (actionsAnnotation != null) {
Action[] actionArray = actionsAnnotation.value();
boolean valuelessSeen = false;
List<Action> actions = new ArrayList<Action>();
for (Action ann : actionArray) {
if (ann.value().equals(Action.DEFAULT_VALUE) && !valuelessSeen) {
valuelessSeen = true;
} else if (ann.value().equals(Action.DEFAULT_VALUE)) {
throw new ConfigurationException("You may only add a single Action " +
"annotation that has no value parameter.");
}
actions.add(ann);
}
map.put(method.getName(), actions);
} else {
Action ann = method.getAnnotation(Action.class);
if (ann != null) {
map.put(method.getName(), Arrays.asList(ann));
}
}
}
return map;
}
/**
* Creates a single ActionConfig object.
*
* @param pkgCfg The package the action configuration instance will belong to.
* @param actionClass The action class.
* @param actionName The name of the action.
* @param actionMethod The method that the annotation was on (if the annotation is not null) or
* the default method (execute).
* @param annotation The ActionName annotation that might override the action name and possibly
*/
protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> actionClass, String actionName,
String actionMethod, Action annotation) {
if (annotation != null) {
actionName = annotation.value() != null && annotation.value().equals(Action.DEFAULT_VALUE) ?
actionName : annotation.value();
actionName = StringTools.lastToken(actionName, "/");
}
ActionConfig.Builder actionConfig = new ActionConfig.Builder(pkgCfg.getName(),
actionName, actionClass.getName());
actionConfig.methodName(actionMethod);
if (LOG.isDebugEnabled()) {
LOG.debug("Creating action config for class [#0], name [#1] and package name [#2] in namespace [#3]",
actionClass.toString(), actionName, pkgCfg.getName(), pkgCfg.getNamespace());
}
//build interceptors
List<InterceptorMapping> interceptors = interceptorMapBuilder.build(actionClass, pkgCfg, actionName, annotation);
actionConfig.addInterceptors(interceptors);
//build results
Map<String, ResultConfig> results = resultMapBuilder.build(actionClass, annotation, actionName, pkgCfg.build());
actionConfig.addResultConfigs(results);
//add params
if (annotation != null)
actionConfig.addParams(StringTools.createParameterMap(annotation.params()));
//add exception mappings from annotation
if (annotation != null && annotation.exceptionMappings() != null)
actionConfig.addExceptionMappings(buildExceptionMappings(annotation.exceptionMappings(), actionName));
//add exception mapping from class
ExceptionMappings exceptionMappings = actionClass.getAnnotation(ExceptionMappings.class);
if (exceptionMappings != null)
actionConfig.addExceptionMappings(buildExceptionMappings(exceptionMappings.value(), actionName));
//add
pkgCfg.addActionConfig(actionName, actionConfig.build());
//check if an action with the same name exists on that package (from XML config probably)
PackageConfig existingPkg = configuration.getPackageConfig(pkgCfg.getName());
if (existingPkg != null) {
// there is a package already with that name, check action
ActionConfig existingActionConfig = existingPkg.getActionConfigs().get(actionName);
if (existingActionConfig != null && LOG.isWarnEnabled())
LOG.warn("Duplicated action definition in package [#0] with name [#1]. First definition was loaded from [#3]", pkgCfg.getName(), actionName, existingActionConfig.getLocation().toString());
}
}
private List<ExceptionMappingConfig> buildExceptionMappings(ExceptionMapping[] exceptions, String actionName) {
List<ExceptionMappingConfig> exceptionMappings = new ArrayList<ExceptionMappingConfig>();
for (ExceptionMapping exceptionMapping : exceptions) {
if (LOG.isTraceEnabled())
LOG.trace("Mapping exception [#0] to result [#1] for action [#2]", exceptionMapping.exception(),
exceptionMapping.result(), actionName);
ExceptionMappingConfig.Builder builder = new ExceptionMappingConfig.Builder(null, exceptionMapping
.exception(), exceptionMapping.result());
if (exceptionMapping.params() != null)
builder.addParams(StringTools.createParameterMap(exceptionMapping.params()));
exceptionMappings.add(builder.build());
}
return exceptionMappings;
}
private PackageConfig.Builder getPackageConfig(final Map<String, PackageConfig.Builder> packageConfigs,
String actionNamespace, final String actionPackage, final Class<?> actionClass,
Action action) {
if (action != null && !action.value().equals(Action.DEFAULT_VALUE)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Using non-default action namespace from the Action annotation of [#0]", action.value());
}
actionNamespace = StringTools.upToLastToken(action.value(), "/");
}
// Next grab the parent annotation from the class
ParentPackage parent = AnnotationTools.findAnnotation(actionClass, ParentPackage.class);
String parentName = null;
if (parent != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Using non-default parent package from annotation of [#0]", parent.value());
}
parentName = parent.value();
}
// Finally use the default
if (parentName == null) {
parentName = defaultParentPackage;
}
if (parentName == null) {
throw new ConfigurationException("Unable to determine the parent XWork package for the action class [" +
actionClass.getName() + "]");
}
PackageConfig parentPkg = configuration.getPackageConfig(parentName);
if (parentPkg == null) {
throw new ConfigurationException("Unable to locate parent package [" + parentName + "]");
}
// Grab based on package-namespace and if it exists, we need to ensure the existing one has
// the correct parent package. If not, we need to create a new package config
String name = actionPackage + "#" + parentPkg.getName() + "#" + actionNamespace;
PackageConfig.Builder pkgConfig = packageConfigs.get(name);
if (pkgConfig == null) {
pkgConfig = new PackageConfig.Builder(name).namespace(actionNamespace).addParent(parentPkg);
packageConfigs.put(name, pkgConfig);
//check for @DefaultInterceptorRef in the package
DefaultInterceptorRef defaultInterceptorRef = AnnotationTools.findAnnotation(actionClass, DefaultInterceptorRef.class);
if (defaultInterceptorRef != null) {
pkgConfig.defaultInterceptorRef(defaultInterceptorRef.value());
if (LOG.isTraceEnabled())
LOG.trace("Setting [#0] as the default interceptor ref for [#1]", defaultInterceptorRef.value(), pkgConfig.getName());
}
}
if (LOG.isTraceEnabled()) {
LOG.trace("Created package config named [#0] with a namespace [#1]", name, actionNamespace);
}
return pkgConfig;
}
/**
* Determine all the index handling actions and results based on this logic:
*
* 1. Loop over all the namespaces such as /foo and see if it has an action named index
* 2. If an action doesn't exists in the parent namespace of the same name, create an action
* in the parent namespace of the same name as the namespace that points to the index
* action in the namespace. e.g. /foo -> /foo/index
* 3. Create the action in the namespace for empty string if it doesn't exist. e.g. /foo/
* the action is "" and the namespace is /foo
*
* @param packageConfigs Used to store the actions.
*/
protected void buildIndexActions(Map<String, PackageConfig.Builder> packageConfigs) {
Map<String, PackageConfig.Builder> byNamespace = new HashMap<String, PackageConfig.Builder>();
Collection<PackageConfig.Builder> values = packageConfigs.values();
for (PackageConfig.Builder packageConfig : values) {
byNamespace.put(packageConfig.getNamespace(), packageConfig);
}
// Step #1
Set<String> namespaces = byNamespace.keySet();
for (String namespace : namespaces) {
// First see if the namespace has an index action
PackageConfig.Builder pkgConfig = byNamespace.get(namespace);
ActionConfig indexActionConfig = pkgConfig.build().getAllActionConfigs().get("index");
if (indexActionConfig == null) {
continue;
}
// Step #2
if (!redirectToSlash) {
int lastSlash = namespace.lastIndexOf('/');
if (lastSlash >= 0) {
String parentAction = namespace.substring(lastSlash + 1);
String parentNamespace = namespace.substring(0, lastSlash);
PackageConfig.Builder parent = byNamespace.get(parentNamespace);
if (parent == null || parent.build().getAllActionConfigs().get(parentAction) == null) {
if (parent == null) {
parent = new PackageConfig.Builder(parentNamespace).namespace(parentNamespace).
addParents(pkgConfig.build().getParents());
packageConfigs.put(parentNamespace, parent);
}
if (parent.build().getAllActionConfigs().get(parentAction) == null) {
parent.addActionConfig(parentAction, indexActionConfig);
}
} else if (LOG.isTraceEnabled()) {
LOG.trace("The parent namespace [#0] already contains " +
"an action [#1]", parentNamespace, parentAction);
}
}
}
// Step #3
if (pkgConfig.build().getAllActionConfigs().get("") == null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Creating index ActionConfig with an action name of [] for the action " +
"class [#0]", indexActionConfig.getClassName());
}
pkgConfig.addActionConfig("", indexActionConfig);
}
}
}
}
@@ -0,0 +1,65 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* <p>
* This class has some reflection helpers.
* </p>
*/
public class ReflectionTools {
/**
* Determines if the class given contains the method.
*
* @param klass The class to check for the method.
* @param method The method name.
* @param parameterTypes The parameter types of the method.
* @return True if the method exists, false if not.
*/
public static boolean containsMethod(Class<?> klass, String method, Class<?>... parameterTypes) {
try {
klass.getMethod(method, parameterTypes);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Retrieves the annotation from the given method in the given class.
*
* @param klass The class.
* @param methodName The method.
* @param annotationClass The annotation to get.
* @return The annotation or null if it doesn't exist.
*/
public static <T extends Annotation> T getAnnotation(Class<?> klass, String methodName, Class<T> annotationClass) {
try {
Method method = klass.getMethod(methodName);
return method.getAnnotation(annotationClass);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,50 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.Map;
import org.apache.struts2.convention.annotation.Action;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
/**
* <p>
* This interface defines how results are constructed for an Action.
* The action information is supplied and the result is a mapping of
* ResultConfig instances to the result name.
* </p>
*/
public interface ResultMapBuilder {
/**
* Builds the result configurations given the action information.
*
* @param actionClass The class of the action.
* @param annotation The action annotation.
* @param actionName The action name.
* @param packageConfig The package configuration that the action will be added to.
* @return The mapping of the result names to the result configurations. If there were none found
* than this should return an empty Map.
*/
Map<String, ResultConfig> build(Class<?> actionClass, Action annotation, String actionName,
PackageConfig packageConfig);
}
@@ -0,0 +1,94 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* This class converts the class name into a SEO friendly name by recognizing
* camel casing and inserting dashes. This also converts everything to
* lower case if desired. And this will also strip off the word <b>Action</b>
* from the class name.
* </p>
*/
public class SEOActionNameBuilder implements ActionNameBuilder {
private static final Logger LOG = LoggerFactory.getLogger(SEOActionNameBuilder.class);
private String actionSuffix = "Action";
private boolean lowerCase;
private String separator;
@Inject
public SEOActionNameBuilder(@Inject(value="struts.convention.action.name.lowercase") String lowerCase,
@Inject(value="struts.convention.action.name.separator") String separator) {
this.lowerCase = Boolean.parseBoolean(lowerCase);
this.separator = separator;
}
/**
* @param actionSuffix (Optional) Classes that end with these value will be mapped as actions
* (defaults to "Action")
*/
@Inject(value = "struts.convention.action.suffix", required = false)
public void setActionSuffix(String actionSuffix) {
if (!StringTools.isTrimmedEmpty(actionSuffix)) {
this.actionSuffix = actionSuffix;
}
}
public String build(String className) {
String actionName = className;
// Truncate Action suffix if found
if (actionName.endsWith(actionSuffix)) {
actionName = actionName.substring(0, actionName.length() - actionSuffix.length());
}
// Convert to underscores
char[] ca = actionName.toCharArray();
StringBuilder build = new StringBuilder("" + ca[0]);
boolean lower = true;
for (int i = 1; i < ca.length; i++) {
char c = ca[i];
if (Character.isUpperCase(c) && lower) {
build.append(separator);
lower = false;
} else if (!Character.isUpperCase(c)) {
lower = true;
}
build.append(c);
}
actionName = build.toString();
if (lowerCase) {
actionName = actionName.toLowerCase();
}
if (LOG.isTraceEnabled()) {
LOG.trace("Changed action name from [#0] to [#1]", className, actionName);
}
return actionName;
}
}
@@ -0,0 +1,86 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.HashMap;
import java.util.Map;
import com.opensymphony.xwork2.config.ConfigurationException;
/**
* <p>
* This class is a String helper.
* </p>
*/
public class StringTools {
public static boolean isTrimmedEmpty(String s) {
return s == null || s.trim().length() == 0;
}
public static String lastToken(String str, String s) {
int index = str.lastIndexOf(s);
if (index >= 0) {
return str.substring(index + 1);
}
return str;
}
public static boolean contains(String[] strings, String value, boolean ignoreCase) {
if (strings != null) {
for (String string : strings) {
if (string.equals(value) || (ignoreCase && string.equalsIgnoreCase(value)))
return true;
}
}
return false;
}
public static String upToLastToken(String str, String s) {
int index = str.lastIndexOf(s);
if (index >= 0) {
return str.substring(0, index);
}
return "";
}
public static Map<String, String> createParameterMap(String[] parms) {
Map<String, String> map = new HashMap<String, String>();
int subtract = parms.length % 2;
if (subtract != 0) {
throw new ConfigurationException(
"'params' is a string array "
+ "and they must be in a key value pair configuration. It looks like you"
+ " have specified an odd number of parameters and there should only be an even number."
+ " (e.g. params = {\"key\", \"value\"})");
}
for (int i = 0; i < parms.length; i = i + 2) {
String key = parms[i];
String value = parms[i + 1];
map.put(key, value);
}
return map;
}
}
@@ -0,0 +1,95 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation can be used to control the URL that maps to
* a specific method in an Action class. By default, the method
* that is invoked is the execute method of the action and the
* URL is based on the package and class names. This annotation
* allows developers to change the URL or invoke a different
* method. This also allows developers to specify multiple URLs
* that will be handled by a single class or a single method.
* </p>
* <p>
* This can also be used via the {@link Actions} annotation
* to associate multiple URLs with a single method.
* </p>
* <p>
* Here's an example:
* </p>
*
* <pre>
* public class MyAction implements Action {
* {@code @Action("/foo/bar")}
* public String execute() {}
* }
* </pre>
* <!-- END SNIPPET: javadoc -->
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String DEFAULT_VALUE = "DEFAULT_VALUE";
/**
* Allows actions to specify different URLs rather than the default that is based on the package
* and action name. This also allows methods other than execute() to be invoked or multiple URLs
* to map to a single class or a single method to handle multiple URLs.
*
* @return The action URL.
*/
String value() default DEFAULT_VALUE;
/**
* Allows action methods to specifically control the results for specific return values. These
* results are not used for other method/URL invocations on the action. These are only used for
* the URL that this action is associated with.
*
* @return The results for the action.
*/
Result[] results() default {};
/**
* Allows action methods to specify what interceptors must be applied to it.
* @return Interceptors to be applied to the action
*/
InterceptorRef[] interceptorRefs() default {};
/**
* @return The parameters passed to the action. This is a list of strings that form a name/value
* pair chain since creating a Map for annotations is not possible. An example would be:
* <code>{"key", "value", "key2", "value2"}</code>.
*/
String[] params() default {};
/**
* @return Maps return codes to exceptions. The "exceptions" interceptor must be applied to the action.
*/
ExceptionMapping[] exceptionMappings() default {};
}
@@ -0,0 +1,40 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows for multiple {@link Action} annotations
* to be used on a single method.
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Actions {
Action[] value() default {};
}
@@ -0,0 +1,51 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.Annotation;
/**
* <p>
* This class provides helper methods for dealing with annotations.
* </p>
*/
public class AnnotationTools {
/**
* Returns the annotation on the given class or the package of the class. This searchs up the
* class hierarchy and the package hierarchy.
*
* @param klass The class to search for the annotation.
* @param annotationClass The Class of the annotation.
* @return The annotation or null.
*/
public static <T extends Annotation> T findAnnotation(Class<?> klass, Class<T> annotationClass) {
T ann = klass.getAnnotation(annotationClass);
while (ann == null && klass != null) {
ann = klass.getPackage().getAnnotation(annotationClass);
if (ann == null) {
klass = klass.getSuperclass();
}
}
return ann;
}
}
@@ -0,0 +1,39 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Defines the default interceptor for all actions
* in this package
*/
@Target({ElementType.PACKAGE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DefaultInterceptorRef {
/**
* @return The interceptor name.
*/
String value();
}
@@ -0,0 +1,54 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* Adds an exception mapping to an action
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExceptionMapping {
/**
* @return Result name
*/
String result();
/**
* @return Class name of the exception to be thrown
*/
String exception();
/**
* @return The parameters passed to the exception. This is a list of strings that form a name/value
* pair chain since creating a Map for annotations is not possible. An example would be:
* <code>{"key", "value", "key2", "value2"}</code>.
*/
String[] params() default {};
}
@@ -0,0 +1,44 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows a class to define more than one {@link ExceptionMapping}
* annotations. These exception mappings will be on all actions defined in the annotated
* class, they are not global exception mappings.
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExceptionMappings {
/**
* @return Exception mappings
*/
ExceptionMapping[] value();
}
@@ -0,0 +1,52 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows an interceptor to be applied to an action. If
* this annotation is used at the class level, then the interceptor
* will be applied to all actions defined on that class, and will be applied
* before the ones defined at the method level.
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InterceptorRef {
/**
* @return name of the interceptor or interceptor stack
*/
String value();
/**
* @return The parameters passed to the interceptor. This is a list of strings that form a name/value
* pair chain, since creating a Map for annotations is not possible. An example would be:
* <code>{"key", "value", "key2", "value2"}</code>.
*/
String[] params() default {};
}
@@ -0,0 +1,40 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows a class to define more than one {@link InterceptorRef}
* annotations.
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface InterceptorRefs {
InterceptorRef[] value();
}
@@ -0,0 +1,93 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation defines how actions can modify the namespace
* that they are added to. This overrides the behavior of the
* Convention plugin which by default uses the package names for
* namespaces. Since XWork packages are created by the Convention
* plugin via the Java packages that the actions exist in, there
* is some tricky handling of XWork parent packages and namespaces
* of the XWork packages for the Convention plugin discovered
* actions so that two actions in the same package can specify
* different parents and namespaces without collision.
* </p>
*
* <p>
* In order to handle this correctly, the name of the XWork
* package that actions are placed into is built using this
* format:
* </p>
*
* <pre>
* &lt;java-package>#&lt;parent-xwork-package>#&lt;namespace>
* </pre>
*
* <p>
* This mechanism will guarantee that two actions in the same
* Java package can specify different parent packages (using the
* {@link org.apache.struts2.convention.annotation.ParentPackage} annotation)
* and namespaces (using this annotation).
* </p>
*
* <p>
* The value of a Namespace annotation should specify the portion of
* the action URL between the context path and the action name. For
* example:
* </p>
* <pre>
* &#064;Namespace("/careers/job-postings-overview/job-postings")
* </pre>
*
* <p>
* This annotation can also be placed inside the special Java file
* named <strong>package-info.java</strong>, which allows package
* level annotations. If this is used in this manner it changes the
* default namespace for all actions within that Java package. The
* search order for the namespace of a particular class is therefore:
* </p>
*
* <ol>
* <li>If the {@link Action} annotation exists within the action and
* specifies a full URI (i.e. it starts with a / character)</li>
* <li>Any Namespace annotations placed on individual action classes</li>
* <li>Any Namespace annotations placed in the package-info.java file</li>
* <li>The namespace as determined using the Java package name and the
* standard convention based naming.</li>
* </ol>
* <!-- END SNIPPET: javadoc -->
*/
@Target({ElementType.PACKAGE, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Namespace {
/**
* @return The namespace value.
*/
String value();
}
@@ -0,0 +1,39 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows actions to be defined in more than one {@link Namespace}
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Target({ElementType.PACKAGE, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Namespaces {
Namespace[] value();
}
@@ -0,0 +1,78 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows actions to modify the parent package
* that they are using. Since XWork packages are created by
* the Convention plugin via the Java packages that the actions
* exist in, there is some tricky handling of XWork parent packages
* and namespaces of the XWork packages for discovered
* actions so that two actions in the same package can specify
* different parents and namespaces without collision.
* </p>
*
* <p>
* In order to handle this correctly, the name of the XWork
* package that actions are placed into is built using this
* format:
* </p>
*
* <pre>
* &lt;java-package>#&lt;parent-xwork-package>#&lt;namespace>
* </pre>
*
* <p>
* This mechanism will guarantee that two actions in the same
* Java package can specify different parent packages (using this
* annotation) and namespaces (using the {@link Namespace} annotation).
* </p>
*
* <p>
* This annotation can be used directly on Action classes or
* in the <strong>package-info.java</strong> class in order
* to specify the default XWork parent package for all actions
* in the Java package. The search order for XWork parent packages
* is therefore:
* </p>
*
* <ol>
* <li>Any ParentPackage annotations placed on individual action classes</li>
* <li>Any ParentPackage annotations placed in the package-info.java file</li>
* <li>The struts configuration property <strong>struts.convention.default.parent.package</strong></li>
* </ol>
* <!-- END SNIPPET: javadoc -->
*/
@Target({ElementType.TYPE, ElementType.PACKAGE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ParentPackage {
/**
* @return The parent package.
*/
String value();
}
@@ -0,0 +1,95 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation is used to specify non-convention based results for
* the Struts convention handling. This annotation is added to a class
* and can be used to specify the result location for a specific result
* code from an action method. Furthermore, this can also be used to
* handle results only for specific action methods within an action class
* (if there are multiple).
* </p>
*
* <p>
* When this annotation is used on an action class, it generates results
* that are applicable to all of the actions URLs defined in the class.
* These are considered global results for that class. Here is an example
* of a global result:
* </p>
*
* <pre>
* {@code @Result(name="fail", location="failed.jsp")}
* public class MyAction {
* }
* </pre>
*
* <p>
* This annotation can also be used inside an {@link Action} annotation
* on specific methods. This usage will define results for that specific
* action URL. Here is an example of an action URL specific result:
* </p>
*
* <pre>
* {@code @Action(results={@Result(name="success", location="/", type="redirect")})}
* public String execute() {
* }
* </pre>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Result {
/**
* @return The name of the result mapping. This is the value that is returned from the action
* method and is used to associate a location with a return value.
*/
String name();
/**
* @return The location of the result within the web application or anywhere on disk. This location
* can be relative if the type of this result is one of the pre-defined relative result
* types (these default to dispatcher, velocity and freemarker). Or this location can be
* absolute relative to the root of the web application or the classpath (since velocity
* and freemarker templates can be loaded via the classpath).
*/
String location() default "";
/**
* @return The type of the result. This is usually setup in the struts.xml or struts-plugin.xml
* and is a simple name that is mapped to a result Class.
*/
String type() default "";
/**
* @return The parameters passed to the result. This is a list of strings that form a name/value
* pair chain since creating a Map for annotations is not possible. An example would be:
* <code>{"key", "value", "key2", "value2"}</code>.
*/
String[] params() default {};
}
@@ -0,0 +1,75 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows the path to the results be to changed on a class
* by class basis. This will favor the property name setting and then the
* value.
* </p>
*
* <p>
* This is used when locating results for an action. In most cases a result
* is a JSP or some type of template (Velocity for example). In order to
* figure out which results are associated with an action, this class can be
* used to set the base directory that the Convention plugin looks at when
* trying to figure out the correct results. For example, if there is an action:
* </p>
*
* <pre>
* com.example.foo.DoSomething
* </pre>
*
* <p>
* The Convention plugin might find that the namespace is foo and the action
* name is do-something and will need to find the results. Using this annotation
* you can set the base path of the results to something like
* <code>/WEB-INF/jsps</code> so that the Convention plugin will look in the
* web application for files of this pattern:
* </p>
*
* <pre>
* /WEB-INF/jsps/foo/do-something-&lt;resultCode>.ext
* </pre>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.PACKAGE})
public @interface ResultPath {
/**
* @return The result path to use for this action, instead of the default.
*/
String value() default "";
/**
* @return The name of the property from the struts.properties file that contains the result
* path for the action that contains this annotation. This property must be set
* and the struts.properties file must exist in the root of the classpath.
*/
String property() default "";
}
@@ -0,0 +1,40 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* This annotation allows a class to define more than one {@link Result}
* annotations.
* </p>
* <!-- END SNIPPET: javadoc -->
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Results {
Result[] value();
}
@@ -0,0 +1,174 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
@@ -0,0 +1,5 @@
Apache Struts
Copyright 2000-2007 The Apache Software Foundation
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<bean type="com.opensymphony.xwork2.UnknownHandler" class="org.apache.struts2.convention.ConventionUnknownHandler"/>
<bean type="org.apache.struts2.convention.ActionConfigBuilder" class="org.apache.struts2.convention.PackageBasedActionConfigBuilder"/>
<bean type="org.apache.struts2.convention.ActionNameBuilder" class="org.apache.struts2.convention.SEOActionNameBuilder"/>
<bean type="org.apache.struts2.convention.ResultMapBuilder" class="org.apache.struts2.convention.DefaultResultMapBuilder"/>
<bean type="org.apache.struts2.convention.InterceptorMapBuilder" class="org.apache.struts2.convention.DefaultInterceptorMapBuilder"/>
<bean type="org.apache.struts2.convention.ConventionsService" class="org.apache.struts2.convention.ConventionsServiceImpl"/>
<bean type="com.opensymphony.xwork2.config.PackageProvider" class="org.apache.struts2.convention.ClasspathConfigurationProvider"/>
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/>
<constant name="struts.convention.result.flatLayout" value="true"/>
<constant name="struts.convention.action.suffix" value="Action"/>
<constant name="struts.convention.action.disableScanning" value="false"/>
<constant name="struts.convention.action.disableJarScanning" value="true"/>
<constant name="struts.convention.action.mapAllMatches" value="false"/>
<constant name="struts.convention.action.checkImplementsAction" value="true"/>
<constant name="struts.convention.default.parent.package" value="convention-default"/>
<constant name="struts.convention.action.name.lowercase" value="true"/>
<constant name="struts.convention.action.name.separator" value="-"/>
<constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/>
<constant name="struts.convention.package.locators.disable" value="false"/>
<constant name="struts.convention.package.locators.basePackage" value=""/>
<constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/>
<constant name="struts.convention.relative.result.types" value="dispatcher,velocity,freemarker"/>
<constant name="struts.convention.redirect.to.slash" value="true"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true"/>
<package name="convention-default" extends="struts-default">
</package>
</struts>
@@ -0,0 +1,18 @@
struts.convention.action.excludeJars = .*/activemq-(core|ra)-[\\d.]+.jar(!/)?, \
.*/catalina.*?jar(!/)?, \
.*/tomcat.*?jar(!/)?, \
.*/junit-[\\d.]+.jar(!/)?, \
.*/log4j-[\\d.]+.jar(!/)?, \
.*/xwork-[\\d.]+.jar(!/)?, \
.*/ognl-[\\d.]+.jar(!/)?, \
.*/aopalliance-[\\d.]+.jar(!/)?, \
.*/jstl-[\\d.]+.jar(!/)?, \
.*/dwr-[\\d.]+.jar(!/)?, \
.*/freemarker-[\\d.]+.jar(!/)?, \
.*/servlet-api-[\\d.]+.jar(!/)?, \
.*/sitemesh-[\\d.]+.jar(!/)?, \
.*/commons-(beanutils|el|digester|fileupload|codec|chain|logging|cli|pool|lang|collections|dbcp)-[\\d.]+.jar(!/)?, \
.*/spring-(beans|context|core|mock|web|jdbc)-[\\d.]+.jar(!/)?, \
.*/velocity-[\\d.]+.jar(!/)?, \
.*/velocity-(dep|tools)-[\\d.]+.jar(!/)?, \
.*/struts2-(config-browser-plugin|core|dojo-plugin|dwr-plugin|jsf-plugin|sitemesh-plugin|spring-plugin|struts1-plugin|tiles-plugin)-[\\d.]+.jar(!/)?
@@ -0,0 +1,327 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import junit.framework.TestCase;
import static org.apache.struts2.convention.ReflectionTools.*;
import org.apache.struts2.convention.actions.NoAnnotationAction;
import org.apache.struts2.convention.actions.result.ActionLevelResultAction;
import org.apache.struts2.convention.actions.result.ActionLevelResultsAction;
import org.apache.struts2.convention.actions.result.ClassLevelResultAction;
import org.apache.struts2.convention.actions.result.ClassLevelResultsAction;
import org.apache.struts2.convention.actions.resultpath.ClassLevelResultPathAction;
import org.apache.struts2.convention.annotation.Action;
import org.easymock.EasyMock;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
/**
* <p>
* This class tests the simple result map builder.
* </p>
*/
public class DefaultResultMapBuilderTest extends TestCase {
public void testBuild() throws Exception {
ServletContext context = mockServletContext("/WEB-INF/location");
// Test with a slash
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(NoAnnotationAction.class, null, "action", packageConfig);
verify(context, "/WEB-INF/location", results, false);
// Test without a slash
context = mockServletContext("/WEB-INF/location");
packageConfig = createPackageConfigBuilder("namespace");
builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
results = builder.build(NoAnnotationAction.class, null, "action", packageConfig);
verify(context, "/WEB-INF/location", results, false);
}
public void testNull() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(null);
EasyMock.replay(context);
// Test with a slash
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(NoAnnotationAction.class, null, "action", packageConfig);
assertEquals(0, results.size());
EasyMock.verify(context);
}
public void testResultPath() throws Exception {
ServletContext context = mockServletContext("/class-level");
// Test with a result path
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/not-used"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(ClassLevelResultPathAction.class, null, "action", packageConfig);
verify(context, "/class-level", results, false);
}
public void testFromServletContext() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
resources.add("/WEB-INF/location/namespace/no-annotation.ftl");
resources.add("/WEB-INF/location/namespace/no-annotation-success.jsp");
resources.add("/WEB-INF/location/namespace/no-annotation-failure.jsp");
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(resources);
EasyMock.replay(context);
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(NoAnnotationAction.class, null, "no-annotation", packageConfig);
assertEquals(4, results.size());
assertEquals("success", results.get("success").getName());
assertEquals(3, results.get("success").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("success").getClassName());
assertEquals("/WEB-INF/location/namespace/no-annotation-success.jsp", results.get("success").getParams().get("location"));
assertEquals(1, results.get("input").getParams().size());
assertEquals("org.apache.struts2.views.freemarker.FreemarkerResult", results.get("input").getClassName());
assertEquals("/WEB-INF/location/namespace/no-annotation.ftl", results.get("input").getParams().get("location"));
assertEquals(1, results.get("error").getParams().size());
assertEquals("org.apache.struts2.views.freemarker.FreemarkerResult", results.get("error").getClassName());
assertEquals("/WEB-INF/location/namespace/no-annotation.ftl", results.get("error").getParams().get("location"));
assertEquals(3, results.get("failure").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("success").getClassName());
assertEquals("/WEB-INF/location/namespace/no-annotation-failure.jsp", results.get("failure").getParams().get("location"));
EasyMock.verify(context);
}
public void testClassLevelSingleResultAnnotation() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(resources);
EasyMock.replay(context);
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(ClassLevelResultAction.class, null, "class-level-result", packageConfig);
assertEquals(1, results.size());
assertEquals("error", results.get("error").getName());
assertEquals(3, results.get("error").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("error").getClassName());
assertEquals("/WEB-INF/location/namespace/error.jsp", results.get("error").getParams().get("location"));
assertEquals("value", results.get("error").getParams().get("key"));
assertEquals("value1", results.get("error").getParams().get("key1"));
EasyMock.verify(context);
}
public void testClassLevelMultipleResultAnnotation() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(resources);
EasyMock.replay(context);
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(ClassLevelResultsAction.class, null, "class-level-results", packageConfig);
assertEquals(4, results.size());
assertEquals("error", results.get("error").getName());
assertEquals("input", results.get("input").getName());
assertEquals("success", results.get("success").getName());
assertEquals("failure", results.get("failure").getName());
assertEquals(3, results.get("error").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("error").getClassName());
assertEquals("/WEB-INF/location/namespace/error.jsp", results.get("error").getParams().get("location"));
assertEquals("ann-value", results.get("error").getParams().get("key"));
assertEquals("ann-value1", results.get("error").getParams().get("key1"));
assertEquals(1, results.get("input").getParams().size());
assertEquals("foo.action", results.get("input").getParams().get("actionName"));
assertEquals("org.apache.struts2.dispatcher.ServletActionRedirectResult", results.get("input").getClassName());
assertEquals(3, results.get("failure").getParams().size());
assertEquals("/WEB-INF/location/namespace/action-failure.jsp", results.get("failure").getParams().get("location"));
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("failure").getClassName());
assertEquals("value", results.get("failure").getParams().get("key"));
assertEquals("value1", results.get("failure").getParams().get("key1"));
assertEquals(3, results.get("success").getParams().size());
assertEquals("/WEB-INF/location/namespace/action-success.jsp", results.get("success").getParams().get("location"));
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("success").getClassName());
assertEquals("value", results.get("success").getParams().get("key"));
assertEquals("value1", results.get("success").getParams().get("key1"));
EasyMock.verify(context);
}
public void testActionLevelSingleResultAnnotation() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(resources);
EasyMock.replay(context);
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(ActionLevelResultAction.class, getAnnotation(ActionLevelResultAction.class, "execute", Action.class), "action-level-result", packageConfig);
assertEquals(1, results.size());
assertEquals("success", results.get("success").getName());
assertEquals(3, results.get("success").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("success").getClassName());
assertEquals("/WEB-INF/location/namespace/action-success.jsp", results.get("success").getParams().get("location"));
assertEquals("value", results.get("success").getParams().get("key"));
assertEquals("value1", results.get("success").getParams().get("key1"));
EasyMock.verify(context);
}
public void testActionLevelMultipleResultAnnotation() throws Exception {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
EasyMock.expect(context.getResourcePaths("/WEB-INF/location/namespace/")).andReturn(resources);
EasyMock.replay(context);
PackageConfig packageConfig = createPackageConfigBuilder("/namespace");
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/location"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(ActionLevelResultsAction.class, getAnnotation(ActionLevelResultsAction.class, "execute", Action.class), "action-level-results", packageConfig);
assertEquals(4, results.size());
assertEquals("error", results.get("error").getName());
assertEquals("input", results.get("input").getName());
assertEquals("success", results.get("success").getName());
assertEquals("failure", results.get("failure").getName());
assertEquals(3, results.get("error").getParams().size());
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("error").getClassName());
assertEquals("/WEB-INF/location/namespace/error.jsp", results.get("error").getParams().get("location"));
assertEquals("value", results.get("success").getParams().get("key"));
assertEquals("value1", results.get("success").getParams().get("key1"));
assertEquals(1, results.get("input").getParams().size());
assertEquals("foo.action", results.get("input").getParams().get("actionName"));
assertEquals("org.apache.struts2.dispatcher.ServletActionRedirectResult", results.get("input").getClassName());
assertEquals(3, results.get("failure").getParams().size());
assertEquals("/WEB-INF/location/namespace/action-failure.jsp", results.get("failure").getParams().get("location"));
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("failure").getClassName());
assertEquals(3, results.get("success").getParams().size());
assertEquals("/WEB-INF/location/namespace/action-success.jsp", results.get("success").getParams().get("location"));
assertEquals("org.apache.struts2.dispatcher.ServletDispatcherResult", results.get("success").getClassName());
EasyMock.verify(context);
}
public void testClassPath() throws Exception {
ServletContext context = EasyMock.createNiceMock(ServletContext.class);
ResultTypeConfig resultType = new ResultTypeConfig.Builder("freemarker", "org.apache.struts2.dispatcher.ServletDispatcherResult").
defaultResultParam("location").build();
PackageConfig packageConfig = new PackageConfig.Builder("package").
defaultResultType("dispatcher").addResultTypeConfig(resultType).build();
DefaultResultMapBuilder builder = new DefaultResultMapBuilder(context, new ConventionsServiceImpl("/WEB-INF/component"), "dispatcher,velocity,freemarker");
Map<String, ResultConfig> results = builder.build(NoAnnotationAction.class, null, "no-annotation", packageConfig);
assertEquals(4, results.size());
assertEquals("input", results.get("input").getName());
assertEquals("error", results.get("error").getName());
assertEquals("success", results.get("success").getName());
assertEquals("foo", results.get("foo").getName());
assertEquals(1, results.get("success").getParams().size());
assertEquals("/WEB-INF/component/no-annotation.ftl", results.get("success").getParams().get("location"));
assertEquals(1, results.get("input").getParams().size());
assertEquals("/WEB-INF/component/no-annotation.ftl", results.get("input").getParams().get("location"));
assertEquals(1, results.get("error").getParams().size());
assertEquals("/WEB-INF/component/no-annotation.ftl", results.get("error").getParams().get("location"));
assertEquals(1, results.get("foo").getParams().size());
assertEquals("/WEB-INF/component/no-annotation-foo.ftl", results.get("foo").getParams().get("location"));
}
private PackageConfig createPackageConfigBuilder(String namespace) {
ResultTypeConfig resultType = new ResultTypeConfig.Builder("dispatcher", "org.apache.struts2.dispatcher.ServletDispatcherResult").
addParam("key", "value").addParam("key1", "value1").defaultResultParam("location").build();
ResultTypeConfig redirect = new ResultTypeConfig.Builder("redirectAction",
"org.apache.struts2.dispatcher.ServletActionRedirectResult").defaultResultParam("actionName").build();
ResultTypeConfig ftlResultType = new ResultTypeConfig.Builder("freemarker",
"org.apache.struts2.views.freemarker.FreemarkerResult").defaultResultParam("location").build();
return new PackageConfig.Builder("package").
namespace(namespace).
defaultResultType("dispatcher").
addResultTypeConfig(resultType).
addResultTypeConfig(redirect).
addResultTypeConfig(ftlResultType).build();
}
private ServletContext mockServletContext(String resultPath) {
ServletContext context = EasyMock.createStrictMock(ServletContext.class);
// Setup some mock jsps
Set<String> resources = new HashSet<String>();
resources.add(resultPath + "/namespace/action.jsp");
resources.add(resultPath + "/namespace/action-success.jsp");
resources.add(resultPath + "/namespace/action-failure.jsp");
EasyMock.expect(context.getResourcePaths(resultPath + "/namespace/")).andReturn(resources);
EasyMock.replay(context);
return context;
}
private void verify(ServletContext context, String resultPath, Map<String, ResultConfig> results,
boolean redirect) {
assertEquals(4, results.size());
assertEquals("success", results.get("success").getName());
assertEquals("input", results.get("input").getName());
assertEquals("error", results.get("error").getName());
assertEquals("failure", results.get("failure").getName());
assertEquals(3, results.get("success").getParams().size());
assertEquals(resultPath + "/namespace/action-success.jsp", results.get("success").getParams().get("location"));
assertEquals("value", results.get("success").getParams().get("key"));
assertEquals("value1", results.get("success").getParams().get("key1"));
assertEquals(3, results.get("failure").getParams().size());
assertEquals(resultPath + "/namespace/action-failure.jsp", results.get("failure").getParams().get("location"));
assertEquals("value", results.get("failure").getParams().get("key"));
assertEquals("value1", results.get("failure").getParams().get("key1"));
if (redirect) {
assertEquals(1, results.get("input").getParams().size());
assertEquals("foo.action", results.get("input").getParams().get("actionName"));
} else {
assertEquals(3, results.get("input").getParams().size());
assertEquals(resultPath + "/namespace/action.jsp", results.get("input").getParams().get("location"));
assertEquals("value", results.get("input").getParams().get("key"));
assertEquals("value1", results.get("input").getParams().get("key1"));
}
assertEquals(3, results.get("error").getParams().size());
assertEquals(resultPath + "/namespace/action.jsp", results.get("error").getParams().get("location"));
assertEquals("value", results.get("error").getParams().get("key"));
assertEquals("value1", results.get("error").getParams().get("key1"));
EasyMock.verify(context);
}
}
@@ -0,0 +1,632 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import static org.apache.struts2.convention.ReflectionTools.getAnnotation;
import static org.easymock.EasyMock.checkOrder;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.verify;
import java.util.*;
import java.net.MalformedURLException;
import java.lang.reflect.Method;
import junit.framework.TestCase;
import org.apache.struts2.convention.actions.DefaultResultPathAction;
import org.apache.struts2.convention.actions.NoAnnotationAction;
import org.apache.struts2.convention.actions.Skip;
import org.apache.struts2.convention.actions.chain.ChainedAction;
import org.apache.struts2.convention.actions.action.ActionNameAction;
import org.apache.struts2.convention.actions.action.ActionNamesAction;
import org.apache.struts2.convention.actions.action.SingleActionNameAction;
import org.apache.struts2.convention.actions.action.TestAction;
import org.apache.struts2.convention.actions.action.TestExtends;
import org.apache.struts2.convention.actions.defaultinterceptor.SingleActionNameAction2;
import org.apache.struts2.convention.actions.exception.ExceptionsActionLevelAction;
import org.apache.struts2.convention.actions.exception.ExceptionsMethodLevelAction;
import org.apache.struts2.convention.actions.interceptor.ActionLevelInterceptor2Action;
import org.apache.struts2.convention.actions.interceptor.ActionLevelInterceptor3Action;
import org.apache.struts2.convention.actions.interceptor.ActionLevelInterceptorAction;
import org.apache.struts2.convention.actions.interceptor.InterceptorsAction;
import org.apache.struts2.convention.actions.namespace.ActionLevelNamespaceAction;
import org.apache.struts2.convention.actions.namespace.ClassLevelNamespaceAction;
import org.apache.struts2.convention.actions.namespace.PackageLevelNamespaceAction;
import org.apache.struts2.convention.actions.namespace2.DefaultNamespaceAction;
import org.apache.struts2.convention.actions.namespace3.ActionLevelNamespacesAction;
import org.apache.struts2.convention.actions.namespace4.ActionAndPackageLevelNamespacesAction;
import org.apache.struts2.convention.actions.params.ActionParamsMethodLevelAction;
import org.apache.struts2.convention.actions.parentpackage.ClassLevelParentPackageAction;
import org.apache.struts2.convention.actions.parentpackage.PackageLevelParentPackageAction;
import org.apache.struts2.convention.actions.result.ActionLevelResultAction;
import org.apache.struts2.convention.actions.result.ActionLevelResultsAction;
import org.apache.struts2.convention.actions.result.ClassLevelResultAction;
import org.apache.struts2.convention.actions.result.ClassLevelResultsAction;
import org.apache.struts2.convention.actions.resultpath.ClassLevelResultPathAction;
import org.apache.struts2.convention.actions.resultpath.PackageLevelResultPathAction;
import org.apache.struts2.convention.actions.skip.Index;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.easymock.EasyMock;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.ActionChainResult;
import com.opensymphony.xwork2.util.reflection.ReflectionException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
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.inject.Container;
import com.opensymphony.xwork2.inject.Scope.Strategy;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import javax.servlet.ServletContext;
/**
* <p>
* This is a test for the package based name builder.
* </p>
*/
public class PackageBasedActionConfigBuilderTest extends TestCase {
public void testActionPackages() throws MalformedURLException {
run("org.apache.struts2.convention.actions", null, null);
}
public void testPackageLocators() throws MalformedURLException {
run(null, "actions,dontfind", null);
}
private void run(String actionPackages, String packageLocators, String excludePackages) throws MalformedURLException {
//setup interceptors
List<InterceptorConfig> defaultInterceptors = new ArrayList<InterceptorConfig>();
defaultInterceptors.add(makeInterceptorConfig("interceptor-1"));
defaultInterceptors.add(makeInterceptorConfig("interceptor-2"));
defaultInterceptors.add(makeInterceptorConfig("interceptor-3"));
//setup interceptor stacks
List<InterceptorStackConfig> defaultInterceptorStacks = new ArrayList<InterceptorStackConfig>();
InterceptorMapping interceptor1 = new InterceptorMapping("interceptor-1", new TestInterceptor());
InterceptorMapping interceptor2 = new InterceptorMapping("interceptor-2", new TestInterceptor());
defaultInterceptorStacks.add(makeInterceptorStackConfig("stack-1", interceptor1, interceptor2));
//setup results
ResultTypeConfig[] defaultResults = new ResultTypeConfig[]{new ResultTypeConfig.Builder("dispatcher",
ServletDispatcherResult.class.getName()).defaultResultParam("location").build(),
new ResultTypeConfig.Builder("chain",
ActionChainResult.class.getName()).defaultResultParam("actionName").build()};
PackageConfig strutsDefault = makePackageConfig("struts-default", null, null, "dispatcher",
defaultResults, defaultInterceptors, defaultInterceptorStacks);
PackageConfig packageLevelParentPkg = makePackageConfig("package-level", null, null, null);
PackageConfig classLevelParentPkg = makePackageConfig("class-level", null, null, null);
PackageConfig rootPkg = makePackageConfig("org.apache.struts2.convention.actions#struts-default#",
"", strutsDefault, null);
PackageConfig paramsPkg = makePackageConfig("org.apache.struts2.convention.actions.params#struts-default#/params",
"/params", strutsDefault, null);
PackageConfig defaultInterceptorPkg = makePackageConfig("org.apache.struts2.convention.actions.defaultinterceptor#struts-default#/defaultinterceptor",
"/defaultinterceptor", strutsDefault, null);
PackageConfig exceptionPkg = makePackageConfig("org.apache.struts2.convention.actions.exception#struts-default#/exception",
"/exception", strutsDefault, null);
PackageConfig actionPkg = makePackageConfig("org.apache.struts2.convention.actions.action#struts-default#/action",
"/action", strutsDefault, null);
PackageConfig idxPkg = makePackageConfig("org.apache.struts2.convention.actions.idx#struts-default#/idx",
"/idx", strutsDefault, null);
PackageConfig idx2Pkg = makePackageConfig("org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2",
"/idx/idx2", strutsDefault, null);
PackageConfig interceptorRefsPkg = makePackageConfig("org.apache.struts2.convention.actions.interceptor#struts-default#/interceptor",
"/interceptor", strutsDefault, null);
PackageConfig packageLevelPkg = makePackageConfig("org.apache.struts2.convention.actions.parentpackage#package-level#/parentpackage",
"/parentpackage", packageLevelParentPkg, null);
PackageConfig differentPkg = makePackageConfig("org.apache.struts2.convention.actions.parentpackage#class-level#/parentpackage",
"/parentpackage", classLevelParentPkg, null);
PackageConfig pkgLevelNamespacePkg = makePackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/package-level",
"/package-level", strutsDefault, null);
PackageConfig classLevelNamespacePkg = makePackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/class-level",
"/class-level", strutsDefault, null);
PackageConfig actionLevelNamespacePkg = makePackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/action-level",
"/action-level", strutsDefault, null);
PackageConfig defaultNamespacePkg = makePackageConfig("org.apache.struts2.convention.actions.namespace2#struts-default#/namespace2",
"/namespace2", strutsDefault, null);
PackageConfig namespaces1Pkg = makePackageConfig("org.apache.struts2.convention.actions.namespace3#struts-default#/namespaces1",
"/namespaces1", strutsDefault, null);
PackageConfig namespaces2Pkg = makePackageConfig("org.apache.struts2.convention.actions.namespace3#struts-default#/namespaces2",
"/namespaces2", strutsDefault, null);
PackageConfig namespaces3Pkg = makePackageConfig("org.apache.struts2.convention.actions.namespace4#struts-default#/namespaces3",
"/namespaces3", strutsDefault, null);
PackageConfig namespaces4Pkg = makePackageConfig("org.apache.struts2.convention.actions.namespace4#struts-default#/namespaces4",
"/namespaces4", strutsDefault, null);
PackageConfig resultPkg = makePackageConfig("org.apache.struts2.convention.actions.result#struts-default#/result",
"/result", strutsDefault, null);
PackageConfig resultPathPkg = makePackageConfig("org.apache.struts2.convention.actions.resultpath#struts-default#/resultpath",
"/resultpath", strutsDefault, null);
PackageConfig skipPkg = makePackageConfig("org.apache.struts2.convention.actions.skip#struts-default#/skip",
"/skip", strutsDefault, null);
PackageConfig chainPkg = makePackageConfig("org.apache.struts2.convention.actions.chain#struts-default#/chain",
"/chain", strutsDefault, null);
ResultMapBuilder resultMapBuilder = createStrictMock(ResultMapBuilder.class);
checkOrder(resultMapBuilder, false);
Map<String, ResultConfig> results = new HashMap<String, ResultConfig>();
/* org.apache.struts2.convention.actions.action */
expect(resultMapBuilder.build(ActionNameAction.class, getAnnotation(ActionNameAction.class, "run1", Action.class), "action1", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionNameAction.class, getAnnotation(ActionNameAction.class, "run2", Action.class), "action2", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionNamesAction.class, getAnnotation(ActionNamesAction.class, "run", Actions.class).value()[0], "actions1", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionNamesAction.class, getAnnotation(ActionNamesAction.class, "run", Actions.class).value()[1], "actions2", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(SingleActionNameAction.class, getAnnotation(SingleActionNameAction.class, "run", Action.class), "action", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(TestAction.class, null, "test", actionPkg)).andReturn(results);
expect(resultMapBuilder.build(TestExtends.class, null, "test-extends", actionPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.idx */
/* org.apache.struts2.convention.actions.idx.idx2 */
expect(resultMapBuilder.build(org.apache.struts2.convention.actions.idx.Index.class, null, "index", idxPkg)).andReturn(results);
expect(resultMapBuilder.build(org.apache.struts2.convention.actions.idx.idx2.Index.class, null, "index", idx2Pkg)).andReturn(results);
/* org.apache.struts2.convention.actions.params */
expect(resultMapBuilder.build(ActionParamsMethodLevelAction.class, getAnnotation(ActionParamsMethodLevelAction.class, "run1", Action.class), "actionParam1", paramsPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.defaultinterceptor */
expect(resultMapBuilder.build(SingleActionNameAction2.class, getAnnotation(SingleActionNameAction2.class, "execute", Action.class), "action345", defaultInterceptorPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.exception */
expect(resultMapBuilder.build(ExceptionsMethodLevelAction.class, getAnnotation(ExceptionsMethodLevelAction.class, "run1", Action.class), "exception1", exceptionPkg)).andReturn(results);
expect(resultMapBuilder.build(ExceptionsActionLevelAction.class, getAnnotation(ExceptionsActionLevelAction.class, "execute", Action.class), "exceptions-action-level", exceptionPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.interceptor */
expect(resultMapBuilder.build(InterceptorsAction.class, getAnnotation(InterceptorsAction.class, "run1", Action.class), "action100", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(InterceptorsAction.class, getAnnotation(InterceptorsAction.class, "run2", Action.class), "action200", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(InterceptorsAction.class, getAnnotation(InterceptorsAction.class, "run3", Action.class), "action300", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(InterceptorsAction.class, getAnnotation(InterceptorsAction.class, "run4", Action.class), "action400", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelInterceptorAction.class, getAnnotation(ActionLevelInterceptorAction.class, "run1", Action.class), "action500", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelInterceptorAction.class, getAnnotation(ActionLevelInterceptorAction.class, "run2", Action.class), "action600", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelInterceptorAction.class, getAnnotation(ActionLevelInterceptorAction.class, "run3", Action.class), "action700", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelInterceptor2Action.class, getAnnotation(ActionLevelInterceptor2Action.class, "run1", Action.class), "action800", interceptorRefsPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelInterceptor3Action.class, getAnnotation(ActionLevelInterceptor3Action.class, "run1", Action.class), "action900", interceptorRefsPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.namespace */
expect(resultMapBuilder.build(ActionLevelNamespaceAction.class, getAnnotation(ActionLevelNamespaceAction.class, "execute", Action.class), "action", actionLevelNamespacePkg)).andReturn(results);
expect(resultMapBuilder.build(ClassLevelNamespaceAction.class, null, "class-level-namespace", classLevelNamespacePkg)).andReturn(results);
expect(resultMapBuilder.build(PackageLevelNamespaceAction.class, null, "package-level-namespace", pkgLevelNamespacePkg)).andReturn(results);
/* org.apache.struts2.convention.actions.namespace2 */
expect(resultMapBuilder.build(DefaultNamespaceAction.class, null, "default-namespace", defaultNamespacePkg)).andReturn(results);
/* org.apache.struts2.convention.actions.namespace3 */
expect(resultMapBuilder.build(ActionLevelNamespacesAction.class, null, "action-level-namespaces", namespaces1Pkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelNamespacesAction.class, null, "action-level-namespaces", namespaces2Pkg)).andReturn(results);
/* org.apache.struts2.convention.actions.namespace4 */
expect(resultMapBuilder.build(ActionAndPackageLevelNamespacesAction.class, null, "action-and-package-level-namespaces", namespaces3Pkg)).andReturn(results);
expect(resultMapBuilder.build(ActionAndPackageLevelNamespacesAction.class, null, "action-and-package-level-namespaces", namespaces4Pkg)).andReturn(results);
/* org.apache.struts2.convention.actions.parentpackage */
expect(resultMapBuilder.build(PackageLevelParentPackageAction.class, null, "package-level-parent-package", packageLevelPkg)).andReturn(results);
expect(resultMapBuilder.build(ClassLevelParentPackageAction.class, null, "class-level-parent-package", differentPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.result */
expect(resultMapBuilder.build(ClassLevelResultAction.class, null, "class-level-result", resultPkg)).andReturn(results);
expect(resultMapBuilder.build(ClassLevelResultsAction.class, null, "class-level-results", resultPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelResultAction.class, getAnnotation(ActionLevelResultAction.class, "execute", Action.class), "action-level-result", resultPkg)).andReturn(results);
expect(resultMapBuilder.build(ActionLevelResultsAction.class, getAnnotation(ActionLevelResultsAction.class, "execute", Action.class), "action-level-results", resultPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.resultpath */
expect(resultMapBuilder.build(ClassLevelResultPathAction.class, null, "class-level-result-path", resultPathPkg)).andReturn(results);
expect(resultMapBuilder.build(PackageLevelResultPathAction.class, null, "package-level-result-path", resultPathPkg)).andReturn(results);
/* org.apache.struts2.convention.actions */
expect(resultMapBuilder.build(NoAnnotationAction.class, null, "no-annotation", rootPkg)).andReturn(results);
expect(resultMapBuilder.build(DefaultResultPathAction.class, null, "default-result-path", rootPkg)).andReturn(results);
expect(resultMapBuilder.build(Skip.class, null, "skip", rootPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.skip */
expect(resultMapBuilder.build(Index.class, null, "index", skipPkg)).andReturn(results);
/* org.apache.struts2.convention.actions.chain */
expect(resultMapBuilder.build(ChainedAction.class, getAnnotation(ChainedAction.class, "foo", Action.class), "foo", chainPkg)).andReturn(results);
expect(resultMapBuilder.build(ChainedAction.class, getAnnotation(ChainedAction.class, "bar", Action.class), "foo-bar", chainPkg)).andReturn(results);
EasyMock.replay(resultMapBuilder);
Configuration configuration = new DefaultConfiguration() {
@Override
public Container getContainer() {
return new DummyContainer();
}
};
configuration.addPackageConfig("struts-default", strutsDefault);
configuration.addPackageConfig("package-level", packageLevelParentPkg);
configuration.addPackageConfig("class-level", classLevelParentPkg);
ActionNameBuilder actionNameBuilder = new SEOActionNameBuilder("true", "-");
ObjectFactory of = new ObjectFactory();
DefaultInterceptorMapBuilder interceptorBuilder = new DefaultInterceptorMapBuilder();
interceptorBuilder.setConfiguration(configuration);
PackageBasedActionConfigBuilder builder = new PackageBasedActionConfigBuilder(configuration,
actionNameBuilder, resultMapBuilder, interceptorBuilder ,of, "false", "struts-default");
builder.setDisableJarScanning("true");
if (actionPackages != null) {
builder.setActionPackages(actionPackages);
}
if (packageLocators != null) {
builder.setPackageLocators(packageLocators);
}
if (excludePackages != null) {
builder.setExcludePackages(excludePackages);
}
builder.setPackageLocatorsBase("org.apache.struts2.convention.actions");
builder.buildActionConfigs();
verify(resultMapBuilder);
/* org.apache.struts2.convention.actions.action */
PackageConfig pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.action#struts-default#/action");
assertNotNull(pkgConfig);
assertEquals(7, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action1", ActionNameAction.class, "run1", pkgConfig.getName());
verifyActionConfig(pkgConfig, "action2", ActionNameAction.class, "run2", pkgConfig.getName());
verifyActionConfig(pkgConfig, "actions1", ActionNamesAction.class, "run", pkgConfig.getName());
verifyActionConfig(pkgConfig, "actions2", ActionNamesAction.class, "run", pkgConfig.getName());
verifyActionConfig(pkgConfig, "action", SingleActionNameAction.class, "run", pkgConfig.getName());
verifyActionConfig(pkgConfig, "test", TestAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "test-extends", TestExtends.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace3 */
//action on namespace1 (action level)
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace3#struts-default#/namespaces1");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action-level-namespaces", ActionLevelNamespacesAction.class, "execute", pkgConfig.getName());
//action on namespace2 (action level)
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace3#struts-default#/namespaces2");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action-level-namespaces", ActionLevelNamespacesAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace4 */
//action on namespace3 (action level)
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace4#struts-default#/namespaces3");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action-and-package-level-namespaces", ActionAndPackageLevelNamespacesAction.class, "execute", pkgConfig.getName());
//action on namespace4 (package level)
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace4#struts-default#/namespaces4");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action-and-package-level-namespaces", ActionAndPackageLevelNamespacesAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.params */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.params#struts-default#/params");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
ActionConfig ac = pkgConfig.getAllActionConfigs().get("actionParam1");
assertNotNull(ac);
Map<String, String> params = ac.getParams();
assertNotNull(params);
assertEquals(2, params.size());
assertEquals("val1", params.get("param1"));
assertEquals("val2", params.get("param2"));
/* org.apache.struts2.convention.actions.params */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.exception#struts-default#/exception");
assertNotNull(pkgConfig);
assertEquals(2, pkgConfig.getActionConfigs().size());
ac = pkgConfig.getAllActionConfigs().get("exception1");
assertNotNull(ac);
List<ExceptionMappingConfig> exceptions = ac.getExceptionMappings();
assertNotNull(exceptions);
assertEquals(2, exceptions.size());
ExceptionMappingConfig exception = exceptions.get(0);
assertEquals("NPE1", exception.getExceptionClassName());
assertEquals("success", exception.getResult());
exception = exceptions.get(1);
assertEquals("NPE2", exception.getExceptionClassName());
assertEquals("success", exception.getResult());
params = exception.getParams();
assertNotNull(params);
assertEquals(1, params.size());
assertEquals("val1", params.get("param1"));
ac = pkgConfig.getAllActionConfigs().get("exceptions-action-level");
assertNotNull(ac);
exceptions = ac.getExceptionMappings();
assertNotNull(exceptions);
assertEquals(2, exceptions.size());
exception = exceptions.get(0);
assertEquals("NPE1", exception.getExceptionClassName());
assertEquals("success", exception.getResult());
exception = exceptions.get(1);
assertEquals("NPE2", exception.getExceptionClassName());
assertEquals("success", exception.getResult());
params = exception.getParams();
assertNotNull(params);
assertEquals(1, params.size());
assertEquals("val1", params.get("param1"));
/* org.apache.struts2.convention.actions.idx */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.idx#struts-default#/idx");
assertNotNull(pkgConfig);
assertEquals(3, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "", org.apache.struts2.convention.actions.idx.Index.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "index", org.apache.struts2.convention.actions.idx.Index.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "idx2", org.apache.struts2.convention.actions.idx.idx2.Index.class, "execute",
"org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2");
/* org.apache.struts2.convention.actions.defaultinterceptor */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.defaultinterceptor#struts-default#/defaultinterceptor");
assertNotNull(pkgConfig);
assertEquals("validationWorkflowStack", pkgConfig.getDefaultInterceptorRef());
/* org.apache.struts2.convention.actions.idx.idx2 */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2");
assertNotNull(pkgConfig);
assertEquals(2, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "", org.apache.struts2.convention.actions.idx.idx2.Index.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "index", org.apache.struts2.convention.actions.idx.idx2.Index.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace action level */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/action-level");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "action", ActionLevelNamespaceAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace class level */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/class-level");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "class-level-namespace", ClassLevelNamespaceAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace package level */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace#struts-default#/package-level");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "package-level-namespace", PackageLevelNamespaceAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.namespace2 */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.namespace2#struts-default#/namespace2");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "default-namespace", DefaultNamespaceAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.parentpackage class level */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.parentpackage#class-level#/parentpackage");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "class-level-parent-package", ClassLevelParentPackageAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.parentpackage package level */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.parentpackage#package-level#/parentpackage");
assertNotNull(pkgConfig);
assertEquals(1, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "package-level-parent-package", PackageLevelParentPackageAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.result */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.result#struts-default#/result");
assertNotNull(pkgConfig);
assertEquals(4, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "class-level-result", ClassLevelResultAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "class-level-results", ClassLevelResultsAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "action-level-result", ActionLevelResultAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "action-level-results", ActionLevelResultsAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.resultpath */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.resultpath#struts-default#/resultpath");
assertNotNull(pkgConfig);
assertEquals(2, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "class-level-result-path", ClassLevelResultPathAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "package-level-result-path", PackageLevelResultPathAction.class, "execute", pkgConfig.getName());
/* org.apache.struts2.convention.actions.interceptorRefs */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.interceptor#struts-default#/interceptor");
assertNotNull(pkgConfig);
assertEquals(9, pkgConfig.getActionConfigs().size());
verifyActionConfigInterceptors(pkgConfig, "action100", "interceptor-1");
verifyActionConfigInterceptors(pkgConfig, "action200", "interceptor-1", "interceptor-2");
verifyActionConfigInterceptors(pkgConfig, "action300", "interceptor-1", "interceptor-2");
verifyActionConfigInterceptors(pkgConfig, "action400", "interceptor-1", "interceptor-1", "interceptor-2");
// Interceptors at class level
verifyActionConfigInterceptors(pkgConfig, "action500", "interceptor-1");
verifyActionConfigInterceptors(pkgConfig, "action600", "interceptor-1", "interceptor-2");
verifyActionConfigInterceptors(pkgConfig, "action700", "interceptor-1", "interceptor-1", "interceptor-2");
//multiple interceptor at class level
verifyActionConfigInterceptors(pkgConfig, "action800", "interceptor-1", "interceptor-2");
verifyActionConfigInterceptors(pkgConfig, "action900", "interceptor-1", "interceptor-1", "interceptor-2");
/* org.apache.struts2.convention.actions */
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions#struts-default#");
assertNotNull(pkgConfig);
System.out.println("actions " + pkgConfig.getActionConfigs());
assertEquals(4, pkgConfig.getActionConfigs().size());
verifyActionConfig(pkgConfig, "no-annotation", NoAnnotationAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "default-result-path", DefaultResultPathAction.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "skip", Skip.class, "execute", pkgConfig.getName());
verifyActionConfig(pkgConfig, "idx", org.apache.struts2.convention.actions.idx.Index.class, "execute",
"org.apache.struts2.convention.actions.idx#struts-default#/idx");
//test unknown handler automatic chaining
pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.chain#struts-default#/chain");
ServletContext context = EasyMock.createNiceMock(ServletContext.class);
EasyMock.replay(context);
ObjectFactory workingFactory = configuration.getContainer().getInstance(ObjectFactory.class);
ConventionUnknownHandler uh = new ConventionUnknownHandler(configuration, workingFactory, context, resultMapBuilder, new ConventionsServiceImpl(""), "struts-default", null, "-");
ActionContext actionContext = new ActionContext(Collections.EMPTY_MAP);
Result result = uh.handleUnknownResult(actionContext, "foo", pkgConfig.getActionConfigs().get("foo"), "bar");
assertNotNull(result);
assertTrue(result instanceof ActionChainResult);
ActionChainResult chainResult = (ActionChainResult) result;
ActionChainResult chainResultToCompare = new ActionChainResult("/chain", "foo-bar", "bar");
}
private void verifyActionConfig(PackageConfig pkgConfig, String actionName, Class<?> actionClass,
String methodName, String packageName) {
ActionConfig ac = pkgConfig.getAllActionConfigs().get(actionName);
assertNotNull(ac);
assertEquals(actionClass.getName(), ac.getClassName());
assertEquals(methodName, ac.getMethodName());
assertEquals(packageName, ac.getPackageName());
}
private void verifyActionConfigInterceptors(PackageConfig pkgConfig, String actionName, String... refs) {
ActionConfig ac = pkgConfig.getAllActionConfigs().get(actionName);
assertNotNull(ac);
List<InterceptorMapping> interceptorMappings = ac.getInterceptors();
for (int i = 0; i < interceptorMappings.size(); i++) {
InterceptorMapping interceptorMapping = interceptorMappings.get(i);
assertEquals(refs[i], interceptorMapping.getName());
}
}
private PackageConfig makePackageConfig(String name, String namespace, PackageConfig parent,
String defaultResultType, ResultTypeConfig... results) {
return makePackageConfig(name, namespace, parent, defaultResultType, results, null, null);
}
private PackageConfig makePackageConfig(String name, String namespace, PackageConfig parent,
String defaultResultType, ResultTypeConfig[] results, List<InterceptorConfig> interceptors,
List<InterceptorStackConfig> interceptorStacks) {
PackageConfig.Builder builder = new PackageConfig.Builder(name);
if (namespace != null) {
builder.namespace(namespace);
}
if (parent != null) {
builder.addParent(parent);
}
if (defaultResultType != null) {
builder.defaultResultType(defaultResultType);
}
if (results != null) {
for (ResultTypeConfig result : results) {
builder.addResultTypeConfig(result);
}
}
if (interceptors != null) {
for (InterceptorConfig ref : interceptors) {
builder.addInterceptorConfig(ref);
}
}
if (interceptorStacks != null) {
for (InterceptorStackConfig ref : interceptorStacks) {
builder.addInterceptorStackConfig(ref);
}
}
return new MyPackageConfig(builder.build());
}
private InterceptorConfig makeInterceptorConfig(String name) {
InterceptorConfig.Builder builder = new InterceptorConfig.Builder(name, "org.apache.struts2.convention.TestInterceptor");
return builder.build();
}
private InterceptorStackConfig makeInterceptorStackConfig(String name, InterceptorMapping... interceptors) {
InterceptorStackConfig.Builder builder = new InterceptorStackConfig.Builder(name);
for (InterceptorMapping interceptor : interceptors)
builder.addInterceptor(interceptor);
return builder.build();
}
public class MyPackageConfig extends PackageConfig {
protected MyPackageConfig(PackageConfig packageConfig) {
super(packageConfig);
}
public boolean equals(Object obj) {
PackageConfig other = (PackageConfig) obj;
return getName().equals(other.getName()) && getNamespace().equals(other.getNamespace()) &&
getParents().get(0) == other.getParents().get(0) && getParents().size() == other.getParents().size();
}
}
public class DummyContainer implements Container {
public <T> T getInstance(Class<T> type) {
try {
T obj = type.newInstance();
if (obj instanceof ObjectFactory) {
((ObjectFactory)obj).setReflectionProvider(new OgnlReflectionProvider() {
@Override
public void setProperties(Map<String, String> properties, Object o) {
}
public void setProperties(Map<String, String> properties, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException {
if (o instanceof ActionChainResult) {
((ActionChainResult)o).setActionName(properties.get("actionName"));
}
}
});
}
return obj;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> T getInstance(Class<T> type, String name) {
return null;
}
public Set<String> getInstanceNames(Class<?> type) {
return null;
}
public void inject(Object o) {
}
public <T> T inject(Class<T> implementation) {
return null;
}
public void removeScopeStrategy() {
}
public void setScopeStrategy(Strategy scopeStrategy) {
}
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import junit.framework.TestCase;
/**
* <p>
* This tests the reflection tools.
* </p>
*/
public class ReflectionToolsTest extends TestCase {
public void testContainsMethod() {
assertTrue(ReflectionTools.containsMethod(this.getClass(), "testContainsMethod"));
assertFalse(ReflectionTools.containsMethod(this.getClass(), "badMethod"));
}
}
@@ -0,0 +1,46 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import junit.framework.TestCase;
/**
* <p>
* This class tests the SEO name builder.
* </p>
*/
public class SEOActionNameBuilderTest extends TestCase {
public void testBuild() throws Exception {
SEOActionNameBuilder builder = new SEOActionNameBuilder("true", "_");
assertEquals("foo", builder.build("Foo"));
assertEquals("foo", builder.build("FooAction"));
assertEquals("foo_bar", builder.build("FooBarAction"));
assertEquals("foo_bar_baz", builder.build("FooBarBazAction"));
}
public void testDash() throws Exception {
SEOActionNameBuilder builder = new SEOActionNameBuilder("true", "-");
assertEquals("foo", builder.build("Foo"));
assertEquals("foo", builder.build("FooAction"));
assertEquals("foo-bar", builder.build("FooBarAction"));
assertEquals("foo-bar-baz", builder.build("FooBarBazAction"));
}
}
@@ -0,0 +1,51 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention;
import junit.framework.TestCase;
/**
* <p>
* This class tests the string tools.
* </p>
*/
public class StringToolsTest extends TestCase {
public void testEmpty() {
assertTrue(StringTools.isTrimmedEmpty(null));
assertTrue(StringTools.isTrimmedEmpty(""));
assertTrue(StringTools.isTrimmedEmpty(" "));
assertFalse(StringTools.isTrimmedEmpty("f"));
assertFalse(StringTools.isTrimmedEmpty(" f "));
}
public void testLastToken() {
assertEquals("bar", StringTools.lastToken("/foo/bar", "/"));
assertEquals("baz", StringTools.lastToken("/foo/bar/baz", "/"));
assertEquals("baz", StringTools.lastToken("baz", "/"));
}
public void testUpToLastToken() {
assertEquals("/foo", StringTools.upToLastToken("/foo/bar", "/"));
assertEquals("/foo/bar", StringTools.upToLastToken("/foo/bar/baz", "/"));
assertEquals("", StringTools.upToLastToken("/foo", "/"));
assertEquals("", StringTools.upToLastToken("foo", "/"));
}
}
@@ -0,0 +1,21 @@
package org.apache.struts2.convention;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TestInterceptor extends AbstractInterceptor {
private String string1;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
return null;
}
public String getString1() {
return string1;
}
public void setString1(String string1) {
this.string1 = string1;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions;
/**
* <p>
* This class is a test action with the default result path.
* </p>
*/
public class DefaultResultPathAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions;
/**
* <p>
* This is a struts action with no annotations.
* </p>
*/
public class NoAnnotationAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,29 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions;
import com.opensymphony.xwork2.Action;
public class Skip implements Action {
public String execute() throws Exception {
return null;
}
}
@@ -0,0 +1,40 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
import org.apache.struts2.convention.annotation.Action;
/**
* <p>
* This is a test action.
* </p>
*/
public class ActionNameAction {
@Action("action1")
public String run1() {
return null;
}
@Action("action2")
public String run2() {
return null;
}
}
@@ -0,0 +1,39 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
/**
* <p>
* This class is a test action.
* </p>
*/
public class ActionNamesAction {
@Actions({
@Action("actions1"),
@Action("actions2")
})
public String run() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
import org.apache.struts2.convention.annotation.Action;
/**
* <p>
* This is a test action.
* </p>
*/
public class SingleActionNameAction {
@Action("action")
public String run() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
/**
* <p>
* This is a test action.
* </p>
*/
public class TestAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,27 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
import com.opensymphony.xwork2.ActionSupport;
public abstract class TestBase extends ActionSupport {
}
@@ -0,0 +1,25 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.action;
public class TestExtends extends TestBase {
}
@@ -0,0 +1,35 @@
/*
* $Id: ActionNamesAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.chain;
import org.apache.struts2.convention.annotation.Action;
public class ChainedAction {
@Action("foo")
public String foo() {
return "bar";
}
@Action("foo-bar")
public String bar() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.defaultinterceptor;
import org.apache.struts2.convention.annotation.Action;
/**
* <p>
* This is a test action.
* </p>
*/
public class SingleActionNameAction2 implements com.opensymphony.xwork2.Action{
@Action("action345")
public String execute() {
return null;
}
}
@@ -0,0 +1,22 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
@org.apache.struts2.convention.annotation.DefaultInterceptorRef("validationWorkflowStack")
package org.apache.struts2.convention.actions.defaultinterceptor;
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.exception;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
@ExceptionMappings({
@ExceptionMapping(exception = "NPE1", result = "success"),
@ExceptionMapping(exception = "NPE2", result = "success", params = {"param1", "val1"})
})
public class ExceptionsActionLevelAction {
public String execute() throws Exception {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.exception;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ExceptionMapping;
public class ExceptionsMethodLevelAction {
@Action(value = "exception1", exceptionMappings = {
@ExceptionMapping(exception = "NPE1", result = "success"),
@ExceptionMapping(exception = "NPE2", result = "success", params = {"param1", "val1"})
})
public String run1() throws Exception {
return null;
}
}
@@ -0,0 +1,29 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.idx;
import com.opensymphony.xwork2.Action;
public class Index implements Action {
public String execute() throws Exception {
return null;
}
}
@@ -0,0 +1,29 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.idx.idx2;
import com.opensymphony.xwork2.Action;
public class Index implements Action {
public String execute() throws Exception {
return null;
}
}
@@ -0,0 +1,42 @@
/*
* $Id: ActionLevelResultAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.interceptor;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
/**
* <p>
* This is a test action with 2 interceptors at the action level.
* </p>
*/
@InterceptorRefs({
@InterceptorRef("interceptor-1"),
@InterceptorRef("interceptor-2")
})
public class ActionLevelInterceptor2Action {
@Action(value = "action800")
public String run1() throws Exception {
return null;
}
}
@@ -0,0 +1,42 @@
/*
* $Id: ActionLevelResultAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.interceptor;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
/**
* <p>
* This is a test action with 1 interceptor and 1 stack at the action level.
* </p>
*/
@InterceptorRefs({
@InterceptorRef("interceptor-1"),
@InterceptorRef("stack-1")
})
public class ActionLevelInterceptor3Action {
@Action(value = "action900")
public String run1() throws Exception {
return null;
}
}
@@ -0,0 +1,48 @@
/*
* $Id: ActionLevelResultAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.interceptor;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
/**
* <p>
* This is a test action with one interceptor at the action level.
* </p>
*/
@InterceptorRef("interceptor-1")
public class ActionLevelInterceptorAction {
@Action(value = "action500")
public String run1() throws Exception {
return null;
}
@Action(value = "action600", interceptorRefs = @InterceptorRef("interceptor-2"))
public String run2() throws Exception {
return null;
}
@Action(value = "action700", interceptorRefs = @InterceptorRef("stack-1"))
public String run3() throws Exception {
return null;
}
}
@@ -0,0 +1,51 @@
/*
* $Id: ActionLevelResultAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.interceptor;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
/**
* <p>
* This is a test action with multiple interceptors.
* </p>
*/
public class InterceptorsAction {
@Action(value = "action100", interceptorRefs = @InterceptorRef("interceptor-1"))
public String run1() {
return null;
}
@Action(value = "action200", interceptorRefs = @InterceptorRef("stack-1"))
public String run2() {
return null;
}
@Action(value = "action300", interceptorRefs = {@InterceptorRef("interceptor-1"), @InterceptorRef("interceptor-2")})
public String run3() {
return null;
}
@Action(value = "action400", interceptorRefs = {@InterceptorRef("interceptor-1"), @InterceptorRef("stack-1")})
public String run4() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace;
import org.apache.struts2.convention.annotation.Action;
/**
* <p>
* This class uses the action level annotation override.
* </p>
*/
public class ActionLevelNamespaceAction {
@Action("/action-level/action")
public String execute() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace;
import org.apache.struts2.convention.annotation.Namespace;
/**
* <p>
* This class uses the class level annotation override.
* </p>
*/
@Namespace("/class-level")
public class ClassLevelNamespaceAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace;
/**
* <p>
* This class uses the package level annotation.
* </p>
*/
public class PackageLevelNamespaceAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,22 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
@org.apache.struts2.convention.annotation.Namespace("/package-level")
package org.apache.struts2.convention.actions.namespace;
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace2;
/**
* <p>
* This class uses the package level annotation.
* </p>
*/
public class DefaultNamespaceAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,34 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace3;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
@Namespaces({
@Namespace("/namespaces1"),
@Namespace("/namespaces2")
})
public class ActionLevelNamespacesAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,33 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.namespace4;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
@Namespaces({
@Namespace("/namespaces3")
})
public class ActionAndPackageLevelNamespacesAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,22 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
@org.apache.struts2.convention.annotation.Namespace("/namespaces4")
package org.apache.struts2.convention.actions.namespace4;
@@ -0,0 +1,49 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.params;
import org.apache.struts2.convention.annotation.Action;
public class ActionParamsMethodLevelAction {
private String param1;
private String param2;
@Action(value = "actionParam1", params = {"param1", "val1", "param2", "val2"})
public String run1() throws Exception {
return null;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
public String getParam2() {
return param2;
}
public void setParam2(String param2) {
this.param2 = param2;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.parentpackage;
import org.apache.struts2.convention.annotation.ParentPackage;
/**
* <p>
* This is a parent package usage action.
* </p>
*/
@ParentPackage("class-level")
public class ClassLevelParentPackageAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.parentpackage;
/**
* <p>
* This is a parent package usage action.
* </p>
*/
public class PackageLevelParentPackageAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,23 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
@org.apache.struts2.convention.annotation.ParentPackage("package-level")
package org.apache.struts2.convention.actions.parentpackage;
@@ -0,0 +1,38 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.result;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
/**
* <p>
* This is a test action with multiple results.
* </p>
*/
public class ActionLevelResultAction {
@Action(results = {
@Result(name="success", location="/WEB-INF/location/namespace/action-success.jsp")
})
public String execute() {
return null;
}
}
@@ -0,0 +1,41 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.result;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
/**
* <p>
* This is a test action with multiple results.
* </p>
*/
public class ActionLevelResultsAction {
@Action(results = {
@Result(name="error", location="error.jsp"),
@Result(name="input", location="foo.action", type="redirectAction"),
@Result(name="success", location="/WEB-INF/location/namespace/action-success.jsp"),
@Result(name="failure", location="/WEB-INF/location/namespace/action-failure.jsp")
})
public String execute() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.result;
import org.apache.struts2.convention.annotation.Result;
/**
* <p>
* This is a test action with multiple results.
* </p>
*/
@Result(name="error", location="error.jsp", params={"key", "value", "key1", "value1"})
public class ClassLevelResultAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,41 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.result;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
/**
* <p>
* This is a test action with multiple results.
* </p>
*/
@Results({
@Result(name="error", location="error.jsp", params={"key", "ann-value", "key1", "ann-value1"}),
@Result(name="input", location="foo.action", type="redirectAction"),
@Result(name="success", location="/WEB-INF/location/namespace/action-success.jsp"),
@Result(name="failure", location="/WEB-INF/location/namespace/action-failure.jsp")
})
public class ClassLevelResultsAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.resultpath;
import org.apache.struts2.convention.annotation.ResultPath;
/**
* <p>
* This class is a test action with the default result path.
* </p>
*/
@ResultPath("/class-level")
public class ClassLevelResultPathAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.resultpath;
/**
* <p>
* This class is a test action with the default result path.
* </p>
*/
public class PackageLevelResultPathAction {
public String execute() {
return null;
}
}
@@ -0,0 +1,23 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
@org.apache.struts2.convention.annotation.ResultPath("/package-level")
package org.apache.struts2.convention.actions.resultpath;
@@ -0,0 +1,29 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.actions.skip;
import com.opensymphony.xwork2.Action;
public class Index implements Action {
public String execute() throws Exception {
return null;
}
}
@@ -0,0 +1,45 @@
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.annotation;
import junit.framework.TestCase;
import org.apache.struts2.convention.actions.namespace.PackageLevelNamespaceAction;
import org.apache.struts2.convention.actions.resultpath.ClassLevelResultPathAction;
/**
* <p>
* This class tests the annotation tools.
* </p>
*/
public class AnnotationToolsTest extends TestCase {
public void testFindAnnotationOnClass() {
ResultPath rl = AnnotationTools.findAnnotation(ClassLevelResultPathAction.class, ResultPath.class);
assertNotNull(rl);
assertEquals("/class-level", rl.value());
}
public void testFindAnnotationOnPackage() {
Namespace ns = AnnotationTools.findAnnotation(PackageLevelNamespaceAction.class, Namespace.class);
assertNotNull(ns);
assertEquals("/package-level", ns.value());
}
}
@@ -0,0 +1,31 @@
/*
* $Id: ActionNamesAction.java 655902 2008-05-13 15:15:12Z bpontarelli $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.convention.dontfind;
import org.apache.struts2.convention.annotation.Action;
//should never be found by the scanner
public class DontFindMeAction {
@Action("foo")
public String foo() {
return "bar";
}
}