mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
Move XWork core under Struts 2 to simplify relese proces
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@911280 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.opensymphony</groupId>
|
||||
<artifactId>xwork-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>XWork: Core</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.opensymphony</groupId>
|
||||
<artifactId>xwork-parent</artifactId>
|
||||
<version>2.1.7-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.opensymphony.com/svn/xwork/trunk/core</connection>
|
||||
<developerConnection>
|
||||
scm:svn:https://svn.opensymphony.com/svn/xwork/trunk/core
|
||||
</developerConnection>
|
||||
<url>https://svn.opensymphony.com/svn/xwork/trunk/core</url>
|
||||
</scm>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The OpenSymphony Software License 1.1</name>
|
||||
<url>../src/resources/LICENSE.txt</url>
|
||||
<comments>
|
||||
This license is derived and fully compatible with the Apache Software
|
||||
License - see http://www.apache.org/LICENSE.txt
|
||||
</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!--
|
||||
Run the translator for Java 1.4 compatiblity
|
||||
|
||||
OS/X Sample:
|
||||
$ cd xwork/
|
||||
$ mvn clean install -Pj4 -Djava14.jar=$JAVA_HOME/../Classes/classes.jar
|
||||
|
||||
-->
|
||||
<id>j4</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>retrotranslator-maven-plugin</artifactId>
|
||||
<version>1.0-alpha-4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>translate-project</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<verify>false</verify>
|
||||
<failonwarning>true</failonwarning>
|
||||
<lazy>true</lazy>
|
||||
<advanced>true</advanced>
|
||||
<verbose>false</verbose>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<!--
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>sun.jdk</groupId>
|
||||
<artifactId>rt</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${java14.jar}</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.retrotranslator</groupId>
|
||||
<artifactId>retrotranslator-runtime</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.retrotranslator</groupId>
|
||||
<artifactId>retrotranslator-transformer</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
-->
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>${basedir}/src/test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
<additionalClasspathElements>
|
||||
<element>${project.build.testOutputDirectory}/xwork-jar.jar</element>
|
||||
<element>${project.build.testOutputDirectory}/xwork-zip.zip</element>
|
||||
<element>${project.build.testOutputDirectory}/xwork - jar.jar</element>
|
||||
<element>${project.build.testOutputDirectory}/xwork - zip.zip</element>
|
||||
</additionalClasspathElements>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/XWorkTestCase.java</exclude>
|
||||
<exclude>**/TestBean.java</exclude>
|
||||
<exclude>**/TestBean2.java</exclude>
|
||||
<exclude>**/TestInterceptor.java</exclude>
|
||||
<exclude>**/AnnotatedTestBean.java</exclude>
|
||||
<exclude>**/ContainerImplTest.java</exclude>
|
||||
<exclude>**/URLUtilTest.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactId>xwork-core</shadedArtifactId>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
<exclude>commons-logging:commons-logging</exclude>
|
||||
<exclude>opensymphony:ognl</exclude>
|
||||
<exclude>ognl:ognl</exclude>
|
||||
<exclude>jboss:javassist</exclude>
|
||||
<exclude>org.springframework:spring-core</exclude>
|
||||
<exclude>org.springframework:spring-aop</exclude>
|
||||
<exclude>org.springframework:spring-aspects</exclude>
|
||||
<exclude>org.springframework:spring-beans</exclude>
|
||||
<exclude>org.springframework:spring-context</exclude>
|
||||
<exclude>org.springframework:spring-context-support</exclude>
|
||||
<exclude>org.springframework:spring-web</exclude>
|
||||
<exclude>org.springframework:spring-test</exclude>
|
||||
<exclude>mockobjects:mockobjects-core</exclude>
|
||||
<exclude>org.easymock:easymock</exclude>
|
||||
<exclude>aopalliance:aopalliance</exclude>
|
||||
<exclude>aspectwerkz:aspectwerkz-core</exclude>
|
||||
<exclude>org.aspectj:aspectjrt</exclude>
|
||||
<exclude>org.aspectj:aspectjweaver</exclude>
|
||||
<exclude>cglib:cglib</exclude>
|
||||
<exclude>cglib:cglib-nodep</exclude>
|
||||
<exclude>asm:asm-util</exclude>
|
||||
<exclude>org.testng:testng:jdk15</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>commons-lang:commons-lang</artifact>
|
||||
<includes>
|
||||
<include>org/apache/commons/lang/StringUtils.class</include>
|
||||
<include>org/apache/commons/lang/math/NumberUtils.class</include>
|
||||
<include>org/apache/commons/lang/ObjectUtils*.class</include>
|
||||
<include>org/apache/commons/lang/StringEscapeUtils.class</include>
|
||||
<include>org/apache/commons/lang/exception/NestableRuntimeException.class</include>
|
||||
<include>org/apache/commons/lang/exception/Nestable.class</include>
|
||||
<include>org/apache/commons/lang/Entities*class</include>
|
||||
<include>org/apache/commons/lang/UnhandledException.class</include>
|
||||
<include>org/apache/commons/lang/IntHashMap*class</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.objectweb.asm</pattern>
|
||||
<shadedPattern>org.objectweb.asm.xwork</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.apache.commons.lang</pattern>
|
||||
<shadedPattern>org.apache.commons.lang.xwork</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.apache.commons.lang.xwork.*,com.opensymphony.xwork2.*</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
</build>
|
||||
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
/**
|
||||
* All actions <b>may</b> implement this interface, which exposes the <code>execute()</code> method.
|
||||
* <p/>
|
||||
* However, as of XWork 1.1, this is <b>not</b> required and is only here to assist users. You are free to create POJOs
|
||||
* that honor the same contract defined by this interface without actually implementing the interface.
|
||||
*/
|
||||
public interface Action {
|
||||
|
||||
/**
|
||||
* The action execution was successful. Show result
|
||||
* view to the end user.
|
||||
*/
|
||||
public static final String SUCCESS = "success";
|
||||
|
||||
/**
|
||||
* The action execution was successful but do not
|
||||
* show a view. This is useful for actions that are
|
||||
* handling the view in another fashion like redirect.
|
||||
*/
|
||||
public static final String NONE = "none";
|
||||
|
||||
/**
|
||||
* The action execution was a failure.
|
||||
* Show an error view, possibly asking the
|
||||
* user to retry entering data.
|
||||
*/
|
||||
public static final String ERROR = "error";
|
||||
|
||||
/**
|
||||
* The action execution require more input
|
||||
* in order to succeed.
|
||||
* This result is typically used if a form
|
||||
* handling action has been executed so as
|
||||
* to provide defaults for a form. The
|
||||
* form associated with the handler should be
|
||||
* shown to the end user.
|
||||
* <p/>
|
||||
* This result is also used if the given input
|
||||
* params are invalid, meaning the user
|
||||
* should try providing input again.
|
||||
*/
|
||||
public static final String INPUT = "input";
|
||||
|
||||
/**
|
||||
* The action could not execute, since the
|
||||
* user most was not logged in. The login view
|
||||
* should be shown.
|
||||
*/
|
||||
public static final String LOGIN = "login";
|
||||
|
||||
|
||||
/**
|
||||
* Where the logic of the action is executed.
|
||||
*
|
||||
* @return a string representing the logical result of the execution.
|
||||
* See constants in this interface for a list of standard result values.
|
||||
* @throws Exception thrown if a system level exception occurs.
|
||||
* <b>Note:</b> Application level exceptions should be handled by returning
|
||||
* an error value, such as <code>Action.ERROR</code>.
|
||||
*/
|
||||
public String execute() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
*
|
||||
* This result invokes an entire other action, complete with it's own interceptor stack and result.
|
||||
*
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <b>This result type takes the following parameters:</b>
|
||||
*
|
||||
* <!-- START SNIPPET: params -->
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><b>actionName (default)</b> - the name of the action that will be chained to</li>
|
||||
*
|
||||
* <li><b>namespace</b> - used to determine which namespace the Action is in that we're chaining. If namespace is null,
|
||||
* this defaults to the current namespace</li>
|
||||
*
|
||||
* <li><b>method</b> - used to specify another method on target action to be invoked.
|
||||
* If null, this defaults to execute method</li>
|
||||
*
|
||||
* <li><b>skipActions</b> - (optional) the list of comma separated action names for the
|
||||
* actions that could be chained to</li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <!-- END SNIPPET: params -->
|
||||
*
|
||||
* <b>Example:</b>
|
||||
*
|
||||
* <pre><!-- START SNIPPET: example -->
|
||||
* <package name="public" extends="struts-default">
|
||||
* <!-- Chain creatAccount to login, using the default parameter -->
|
||||
* <action name="createAccount" class="...">
|
||||
* <result type="chain">login</result>
|
||||
* </action>
|
||||
*
|
||||
* <action name="login" class="...">
|
||||
* <!-- Chain to another namespace -->
|
||||
* <result type="chain">
|
||||
* <param name="actionName">dashboard</param>
|
||||
* <param name="namespace">/secure</param>
|
||||
* </result>
|
||||
* </action>
|
||||
* </package>
|
||||
*
|
||||
* <package name="secure" extends="struts-default" namespace="/secure">
|
||||
* <action name="dashboard" class="...">
|
||||
* <result>dashboard.jsp</result>
|
||||
* </action>
|
||||
* </package>
|
||||
* <!-- END SNIPPET: example --></pre>
|
||||
*
|
||||
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
|
||||
*/
|
||||
public class ActionChainResult implements Result {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ActionChainResult.class);
|
||||
|
||||
/**
|
||||
* The result parameter name to set the name of the action to chain to.
|
||||
*/
|
||||
public static final String DEFAULT_PARAM = "actionName";
|
||||
|
||||
/**
|
||||
* The action context key to save the chain history.
|
||||
*/
|
||||
private static final String CHAIN_HISTORY = "CHAIN_HISTORY";
|
||||
|
||||
/**
|
||||
* The result parameter name to set the name of the action to chain to.
|
||||
*/
|
||||
public static final String SKIP_ACTIONS_PARAM = "skipActions";
|
||||
|
||||
|
||||
private ActionProxy proxy;
|
||||
private String actionName;
|
||||
|
||||
private String namespace;
|
||||
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* The list of actions to skip.
|
||||
*/
|
||||
private String skipActions;
|
||||
|
||||
private ActionProxyFactory actionProxyFactory;
|
||||
|
||||
public ActionChainResult() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ActionChainResult(String namespace, String actionName, String methodName) {
|
||||
this.namespace = namespace;
|
||||
this.actionName = actionName;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public ActionChainResult(String namespace, String actionName, String methodName, String skipActions) {
|
||||
this.namespace = namespace;
|
||||
this.actionName = actionName;
|
||||
this.methodName = methodName;
|
||||
this.skipActions = skipActions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param actionProxyFactory the actionProxyFactory to set
|
||||
*/
|
||||
@Inject
|
||||
public void setActionProxyFactory(ActionProxyFactory actionProxyFactory) {
|
||||
this.actionProxyFactory = actionProxyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action name.
|
||||
*
|
||||
* @param actionName The action name.
|
||||
*/
|
||||
public void setActionName(String actionName) {
|
||||
this.actionName = actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the namespace of the Action that we're chaining to. if namespace
|
||||
* is null, this defaults to the current namespace.
|
||||
*
|
||||
* @param namespace the name of the namespace we're chaining to
|
||||
*/
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of actions to skip.
|
||||
* To test if an action should not throe an infinite recursion,
|
||||
* only the action name is used, not the namespace.
|
||||
*
|
||||
* @param actions The list of action name separated by a white space.
|
||||
*/
|
||||
public void setSkipActions(String actions) {
|
||||
this.skipActions = actions;
|
||||
}
|
||||
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.methodName = method;
|
||||
}
|
||||
|
||||
public ActionProxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the XWork chain history.
|
||||
* The stack is a list of <code>namespace/action!method</code> keys.
|
||||
*/
|
||||
public static LinkedList<String> getChainHistory() {
|
||||
LinkedList<String> chainHistory = (LinkedList<String>) ActionContext.getContext().get(CHAIN_HISTORY);
|
||||
// Add if not exists
|
||||
if (chainHistory == null) {
|
||||
chainHistory = new LinkedList<String>();
|
||||
ActionContext.getContext().put(CHAIN_HISTORY, chainHistory);
|
||||
}
|
||||
|
||||
return chainHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param invocation the DefaultActionInvocation calling the action call stack
|
||||
*/
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
// if the finalNamespace wasn't explicitly defined, assume the current one
|
||||
if (this.namespace == null) {
|
||||
this.namespace = invocation.getProxy().getNamespace();
|
||||
}
|
||||
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
String finalNamespace = TextParseUtil.translateVariables(namespace, stack);
|
||||
String finalActionName = TextParseUtil.translateVariables(actionName, stack);
|
||||
String finalMethodName = this.methodName != null
|
||||
? TextParseUtil.translateVariables(this.methodName, stack)
|
||||
: null;
|
||||
|
||||
if (isInChainHistory(finalNamespace, finalActionName, finalMethodName)) {
|
||||
addToHistory(finalNamespace, finalActionName, finalMethodName);
|
||||
throw new XWorkException("Infinite recursion detected: "
|
||||
+ ActionChainResult.getChainHistory().toString());
|
||||
}
|
||||
|
||||
if (ActionChainResult.getChainHistory().isEmpty() && invocation != null && invocation.getProxy() != null) {
|
||||
addToHistory(finalNamespace, invocation.getProxy().getActionName(), invocation.getProxy().getMethod());
|
||||
}
|
||||
addToHistory(finalNamespace, finalActionName, finalMethodName);
|
||||
|
||||
HashMap<String, Object> extraContext = new HashMap<String, Object>();
|
||||
extraContext.put(ActionContext.VALUE_STACK, ActionContext.getContext().getValueStack());
|
||||
extraContext.put(ActionContext.PARAMETERS, ActionContext.getContext().getParameters());
|
||||
extraContext.put(CHAIN_HISTORY, ActionChainResult.getChainHistory());
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Chaining to action " + finalActionName);
|
||||
}
|
||||
|
||||
proxy = actionProxyFactory.createActionProxy(finalNamespace, finalActionName, finalMethodName, extraContext);
|
||||
proxy.execute();
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final ActionChainResult that = (ActionChainResult) o;
|
||||
|
||||
if (actionName != null ? !actionName.equals(that.actionName) : that.actionName != null) return false;
|
||||
if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
|
||||
if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
int result;
|
||||
result = (actionName != null ? actionName.hashCode() : 0);
|
||||
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
|
||||
result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isInChainHistory(String namespace, String actionName, String methodName) {
|
||||
LinkedList<? extends String> chainHistory = ActionChainResult.getChainHistory();
|
||||
|
||||
if (chainHistory == null) {
|
||||
return false;
|
||||
} else {
|
||||
// Actions to skip
|
||||
Set<String> skipActionsList = new HashSet<String>();
|
||||
if (skipActions != null && skipActions.length() > 0) {
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
String finalSkipActions = TextParseUtil.translateVariables(this.skipActions, stack);
|
||||
skipActionsList.addAll(TextParseUtil.commaDelimitedStringToSet(finalSkipActions));
|
||||
}
|
||||
if (!skipActionsList.contains(actionName)) {
|
||||
// Get if key is in the chain history
|
||||
return chainHistory.contains(makeKey(namespace, actionName, methodName));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addToHistory(String namespace, String actionName, String methodName) {
|
||||
List<String> chainHistory = ActionChainResult.getChainHistory();
|
||||
chainHistory.add(makeKey(namespace, actionName, methodName));
|
||||
}
|
||||
|
||||
private String makeKey(String namespace, String actionName, String methodName) {
|
||||
if (null == methodName) {
|
||||
return namespace + "/" + actionName;
|
||||
}
|
||||
|
||||
return namespace + "/" + actionName + "!" + methodName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* The ActionContext is the context in which an {@link Action} is executed. Each context is basically a
|
||||
* container of objects an action needs for execution like the session, parameters, locale, etc. <p>
|
||||
* <p/>
|
||||
* The ActionContext is thread local which means that values stored in the ActionContext are
|
||||
* unique per thread. See the {@link ThreadLocal} class for more information. The benefit of
|
||||
* this is you don't need to worry about a user specific action context, you just get it:
|
||||
* <p/>
|
||||
* <ul><code>ActionContext context = ActionContext.getContext();</code></ul>
|
||||
* <p/>
|
||||
* Finally, because of the thread local usage you don't need to worry about making your actions thread safe.
|
||||
*
|
||||
* @author Patrick Lightbody
|
||||
* @author Bill Lynch (docs)
|
||||
*/
|
||||
public class ActionContext implements Serializable {
|
||||
static ThreadLocal actionContext = new ThreadLocal();
|
||||
|
||||
/**
|
||||
* Constant that indicates the action is running under a "development mode".
|
||||
* This mode provides more feedback that is useful for developers but probably
|
||||
* too verbose/error prone for production.
|
||||
*/
|
||||
//public static final String DEV_MODE = "__devMode";
|
||||
|
||||
/**
|
||||
* Constant for the name of the action being executed.
|
||||
*/
|
||||
public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
|
||||
|
||||
/**
|
||||
* Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
|
||||
*/
|
||||
public static final String VALUE_STACK = ValueStack.VALUE_STACK;
|
||||
|
||||
/**
|
||||
* Constant for the action's session.
|
||||
*/
|
||||
public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
|
||||
|
||||
/**
|
||||
* Constant for the action's application context.
|
||||
*/
|
||||
public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
|
||||
|
||||
/**
|
||||
* Constant for the action's parameters.
|
||||
*/
|
||||
public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
|
||||
|
||||
/**
|
||||
* Constant for the action's locale.
|
||||
*/
|
||||
public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
|
||||
|
||||
/**
|
||||
* Constant for the action's type converter.
|
||||
*/
|
||||
public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter";
|
||||
|
||||
/**
|
||||
* Constant for the action's {@link com.opensymphony.xwork2.ActionInvocation invocation} context.
|
||||
*/
|
||||
public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
|
||||
|
||||
/**
|
||||
* Constant for the map of type conversion errors.
|
||||
*/
|
||||
public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
|
||||
|
||||
|
||||
/**
|
||||
* Constant for the container
|
||||
*/
|
||||
public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
|
||||
|
||||
Map<String, Object> context;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new ActionContext initialized with another context.
|
||||
*
|
||||
* @param context a context map.
|
||||
*/
|
||||
public ActionContext(Map<String, Object> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the action invocation (the execution state).
|
||||
*
|
||||
* @param actionInvocation the action execution state.
|
||||
*/
|
||||
public void setActionInvocation(ActionInvocation actionInvocation) {
|
||||
put(ACTION_INVOCATION, actionInvocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the action invocation (the execution state).
|
||||
*
|
||||
* @return the action invocation (the execution state).
|
||||
*/
|
||||
public ActionInvocation getActionInvocation() {
|
||||
return (ActionInvocation) get(ACTION_INVOCATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action's application context.
|
||||
*
|
||||
* @param application the action's application context.
|
||||
*/
|
||||
public void setApplication(Map<String, Object> application) {
|
||||
put(APPLICATION, application);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of the ServletContext when in a servlet environment or a generic application level Map otherwise.
|
||||
*
|
||||
* @return a Map of ServletContext or generic application level Map
|
||||
*/
|
||||
public Map<String, Object> getApplication() {
|
||||
return (Map<String, Object>) get(APPLICATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action context for the current thread.
|
||||
*
|
||||
* @param context the action context.
|
||||
*/
|
||||
public static void setContext(ActionContext context) {
|
||||
actionContext.set(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ActionContext specific to the current thread.
|
||||
*
|
||||
* @return the ActionContext for the current thread, is never <tt>null</tt>.
|
||||
*/
|
||||
public static ActionContext getContext() {
|
||||
return (ActionContext) actionContext.get();
|
||||
|
||||
// Don't do lazy context creation, as it requires container; the creation of which may
|
||||
// precede the context creation
|
||||
//if (context == null) {
|
||||
// ValueStack vs = ValueStackFactory.getFactory().createValueStack();
|
||||
// context = new ActionContext(vs.getContext());
|
||||
// setContext(context);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action's context map.
|
||||
*
|
||||
* @param contextMap the context map.
|
||||
*/
|
||||
public void setContextMap(Map<String, Object> contextMap) {
|
||||
getContext().context = contextMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context map.
|
||||
*
|
||||
* @return the context map.
|
||||
*/
|
||||
public Map<String, Object> getContextMap() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets conversion errors which occurred when executing the action.
|
||||
*
|
||||
* @param conversionErrors a Map of errors which occurred when executing the action.
|
||||
*/
|
||||
public void setConversionErrors(Map<String, Object> conversionErrors) {
|
||||
put(CONVERSION_ERRORS, conversionErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map of conversion errors which occurred when executing the action.
|
||||
*
|
||||
* @return the map of conversion errors which occurred when executing the action or an empty map if
|
||||
* there were no errors.
|
||||
*/
|
||||
public Map<String, Object> getConversionErrors() {
|
||||
Map<String, Object> errors = (Map) get(CONVERSION_ERRORS);
|
||||
|
||||
if (errors == null) {
|
||||
errors = new HashMap<String, Object>();
|
||||
setConversionErrors(errors);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Locale for the current action.
|
||||
*
|
||||
* @param locale the Locale for the current action.
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
put(LOCALE, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Locale of the current action. If no locale was ever specified the platform's
|
||||
* {@link java.util.Locale#getDefault() default locale} is used.
|
||||
*
|
||||
* @return the Locale of the current action.
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
Locale locale = (Locale) get(LOCALE);
|
||||
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the current Action in the ActionContext.
|
||||
*
|
||||
* @param name the name of the current action.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
put(ACTION_NAME, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the current Action.
|
||||
*
|
||||
* @return the name of the current action.
|
||||
*/
|
||||
public String getName() {
|
||||
return (String) get(ACTION_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action parameters.
|
||||
*
|
||||
* @param parameters the parameters for the current action.
|
||||
*/
|
||||
public void setParameters(Map<String, Object> parameters) {
|
||||
put(PARAMETERS, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of the HttpServletRequest parameters when in a servlet environment or a generic Map of
|
||||
* parameters otherwise.
|
||||
*
|
||||
* @return a Map of HttpServletRequest parameters or a multipart map when in a servlet environment, or a
|
||||
* generic Map of parameters otherwise.
|
||||
*/
|
||||
public Map<String, Object> getParameters() {
|
||||
return (Map<String, Object>) get(PARAMETERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a map of action session values.
|
||||
*
|
||||
* @param session the session values.
|
||||
*/
|
||||
public void setSession(Map<String, Object> session) {
|
||||
put(SESSION, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
|
||||
*
|
||||
* @return the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
|
||||
*/
|
||||
public Map<String, Object> getSession() {
|
||||
return (Map<String, Object>) get(SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OGNL value stack.
|
||||
*
|
||||
* @param stack the OGNL value stack.
|
||||
*/
|
||||
public void setValueStack(ValueStack stack) {
|
||||
put(VALUE_STACK, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OGNL value stack.
|
||||
*
|
||||
* @return the OGNL value stack.
|
||||
*/
|
||||
public ValueStack getValueStack() {
|
||||
return (ValueStack) get(VALUE_STACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the container for this request
|
||||
*
|
||||
* @param cont The container
|
||||
*/
|
||||
public void setContainer(Container cont) {
|
||||
put(CONTAINER, cont);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the container for this request
|
||||
*
|
||||
* @return The container
|
||||
*/
|
||||
public Container getContainer() {
|
||||
return (Container) get(CONTAINER);
|
||||
}
|
||||
|
||||
public <T> T getInstance(Class<T> type) {
|
||||
Container cont = getContainer();
|
||||
if (cont != null) {
|
||||
return cont.getInstance(type);
|
||||
} else {
|
||||
throw new XWorkException("Cannot find an initialized container for this request.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value that is stored in the current ActionContext by doing a lookup using the value's key.
|
||||
*
|
||||
* @param key the key used to find the value.
|
||||
* @return the value that was found using the key or <tt>null</tt> if the key was not found.
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return context.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a value in the current ActionContext. The value can be looked up using the key.
|
||||
*
|
||||
* @param key the key of the value.
|
||||
* @param value the value to be stored.
|
||||
*/
|
||||
public void put(String key, Object value) {
|
||||
context.put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
/**
|
||||
* Provides hooks for handling key action events
|
||||
*/
|
||||
public interface ActionEventListener {
|
||||
/**
|
||||
* Called after an action has been created.
|
||||
*
|
||||
* @param action The action
|
||||
* @param stack The current value stack
|
||||
* @return The action to use
|
||||
*/
|
||||
public Object prepare(Object action, ValueStack stack);
|
||||
|
||||
/**
|
||||
* Called when an exception is thrown by the action
|
||||
*
|
||||
* @param t The exception/error that was thrown
|
||||
* @param stack The current value stack
|
||||
* @return A result code to execute, can be null
|
||||
*/
|
||||
public String handleException(Throwable t, ValueStack stack);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link ActionInvocation} represents the execution state of an {@link Action}. It holds the Interceptors and the Action instance.
|
||||
* By repeated re-entrant execution of the <code>invoke()</code> method, initially by the {@link ActionProxy}, then by the Interceptors, the
|
||||
* Interceptors are all executed, and then the {@link Action} and the {@link Result}.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @see com.opensymphony.xwork2.ActionProxy
|
||||
*/
|
||||
public interface ActionInvocation extends Serializable {
|
||||
|
||||
/**
|
||||
* Get the Action associated with this ActionInvocation.
|
||||
*
|
||||
* @return the Action
|
||||
*/
|
||||
Object getAction();
|
||||
|
||||
/**
|
||||
* Gets whether this ActionInvocation has executed before.
|
||||
* This will be set after the Action and the Result have executed.
|
||||
*
|
||||
* @return <tt>true</tt> if this ActionInvocation has executed before.
|
||||
*/
|
||||
boolean isExecuted();
|
||||
|
||||
/**
|
||||
* Gets the ActionContext associated with this ActionInvocation. The ActionProxy is
|
||||
* responsible for setting this ActionContext onto the ThreadLocal before invoking
|
||||
* the ActionInvocation and resetting the old ActionContext afterwards.
|
||||
*
|
||||
* @return the ActionContext.
|
||||
*/
|
||||
ActionContext getInvocationContext();
|
||||
|
||||
/**
|
||||
* Get the ActionProxy holding this ActionInvocation.
|
||||
*
|
||||
* @return the ActionProxy.
|
||||
*/
|
||||
ActionProxy getProxy();
|
||||
|
||||
/**
|
||||
* If the ActionInvocation has been executed before and the Result is an instance of {@link ActionChainResult}, this method
|
||||
* will walk down the chain of <code>ActionChainResult</code>s until it finds a non-chain result, which will be returned. If the
|
||||
* ActionInvocation's result has not been executed before, the Result instance will be created and populated with
|
||||
* the result params.
|
||||
*
|
||||
* @return the result.
|
||||
* @throws Exception can be thrown.
|
||||
*/
|
||||
Result getResult() throws Exception;
|
||||
|
||||
/**
|
||||
* Gets the result code returned from this ActionInvocation.
|
||||
*
|
||||
* @return the result code
|
||||
*/
|
||||
String getResultCode();
|
||||
|
||||
/**
|
||||
* Sets the result code, possibly overriding the one returned by the
|
||||
* action.
|
||||
* <p/>
|
||||
* The "intended" purpose of this method is to allow PreResultListeners to
|
||||
* override the result code returned by the Action.
|
||||
* <p/>
|
||||
* If this method is used before the Action executes, the Action's returned
|
||||
* result code will override what was set. However the Action could (if
|
||||
* specifically coded to do so) inspect the ActionInvocation to see that
|
||||
* someone "upstream" (e.g. an Interceptor) had suggested a value as the
|
||||
* result, and it could therefore return the same value itself.
|
||||
* <p/>
|
||||
* If this method is called between the Action execution and the Result
|
||||
* execution, then the value set here will override the result code the
|
||||
* action had returned. Creating an Interceptor that implements
|
||||
* {@link PreResultListener} will give you this oportunity.
|
||||
* <p/>
|
||||
* If this method is called after the Result has been executed, it will
|
||||
* have the effect of raising an IllegalStateException.
|
||||
*
|
||||
* @param resultCode the result code.
|
||||
* @throws IllegalStateException if called after the Result has been executed.
|
||||
* @see #isExecuted()
|
||||
*/
|
||||
void setResultCode(String resultCode);
|
||||
|
||||
/**
|
||||
* Gets the ValueStack associated with this ActionInvocation.
|
||||
*
|
||||
* @return the ValueStack
|
||||
*/
|
||||
ValueStack getStack();
|
||||
|
||||
/**
|
||||
* Register a {@link PreResultListener} to be notified after the Action is executed and
|
||||
* before the Result is executed.
|
||||
* <p/>
|
||||
* The ActionInvocation implementation must guarantee that listeners will be called in
|
||||
* the order in which they are registered.
|
||||
* <p/>
|
||||
* Listener registration and execution does not need to be thread-safe.
|
||||
*
|
||||
* @param listener the listener to add.
|
||||
*/
|
||||
void addPreResultListener(PreResultListener listener);
|
||||
|
||||
/**
|
||||
* Invokes the next step in processing this ActionInvocation.
|
||||
* <p/>
|
||||
* If there are more Interceptors, this will call the next one. If Interceptors choose not to short-circuit
|
||||
* ActionInvocation processing and return their own return code, they will call invoke() to allow the next Interceptor
|
||||
* to execute. If there are no more Interceptors to be applied, the Action is executed.
|
||||
* If the {@link ActionProxy#getExecuteResult()} method returns <tt>true</tt>, the Result is also executed.
|
||||
*
|
||||
* @throws Exception can be thrown.
|
||||
* @return the return code.
|
||||
*/
|
||||
String invoke() throws Exception;
|
||||
|
||||
/**
|
||||
* Invokes only the Action (not Interceptors or Results).
|
||||
* <p/>
|
||||
* This is useful in rare situations where advanced usage with the interceptor/action/result workflow is
|
||||
* being manipulated for certain functionality.
|
||||
*
|
||||
* @return the return code.
|
||||
* @throws Exception can be thrown.
|
||||
*/
|
||||
String invokeActionOnly() throws Exception;
|
||||
|
||||
/**
|
||||
* Sets the action event listener to respond to key action events.
|
||||
*
|
||||
* @param listener the listener.
|
||||
*/
|
||||
void setActionEventListener(ActionEventListener listener);
|
||||
|
||||
void init(ActionProxy proxy) ;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
|
||||
|
||||
/**
|
||||
* ActionProxy is an extra layer between XWork and the action so that different proxies are possible.
|
||||
* <p/>
|
||||
* An example of this would be a remote proxy, where the layer between XWork and the action might be RMI or SOAP.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public interface ActionProxy {
|
||||
|
||||
/**
|
||||
* Gets the Action instance for this Proxy.
|
||||
*
|
||||
* @return the Action instance
|
||||
*/
|
||||
Object getAction();
|
||||
|
||||
/**
|
||||
* Gets the alias name this ActionProxy is mapped to.
|
||||
*
|
||||
* @return the alias name
|
||||
*/
|
||||
String getActionName();
|
||||
|
||||
/**
|
||||
* Gets the ActionConfig this ActionProxy is built from.
|
||||
*
|
||||
* @return the ActionConfig
|
||||
*/
|
||||
ActionConfig getConfig();
|
||||
|
||||
/**
|
||||
* Sets whether this ActionProxy should also execute the Result after executing the Action.
|
||||
*
|
||||
* @param executeResult <tt>true</tt> to also execute the Result.
|
||||
*/
|
||||
void setExecuteResult(boolean executeResult);
|
||||
|
||||
/**
|
||||
* Gets the status of whether the ActionProxy is set to execute the Result after the Action is executed.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
boolean getExecuteResult();
|
||||
|
||||
/**
|
||||
* Gets the ActionInvocation associated with this ActionProxy.
|
||||
*
|
||||
* @return the ActionInvocation
|
||||
*/
|
||||
ActionInvocation getInvocation();
|
||||
|
||||
/**
|
||||
* Gets the namespace the ActionConfig for this ActionProxy is mapped to.
|
||||
*
|
||||
* @return the namespace
|
||||
*/
|
||||
String getNamespace();
|
||||
|
||||
/**
|
||||
* Execute this ActionProxy. This will set the ActionContext from the ActionInvocation into the ActionContext
|
||||
* ThreadLocal before invoking the ActionInvocation, then set the old ActionContext back into the ThreadLocal.
|
||||
*
|
||||
* @return the result code returned from executing the ActionInvocation
|
||||
* @throws Exception can be thrown.
|
||||
* @see ActionInvocation
|
||||
*/
|
||||
String execute() throws Exception;
|
||||
|
||||
/**
|
||||
* Gets the method name to execute, or <tt>null</tt> if no method has been specified (meaning <code>execute</code> will be invoked).
|
||||
*
|
||||
* @return the method to execute
|
||||
*/
|
||||
String getMethod();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* The {@link ActionProxyFactory} is used to create {@link ActionProxy}s to be executed.
|
||||
* <p/>
|
||||
* It is the entry point to XWork that is used by a dispatcher to create an {@link ActionProxy} to execute
|
||||
* for a particular namespace and action name.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @see DefaultActionProxyFactory
|
||||
*/
|
||||
public interface ActionProxyFactory {
|
||||
|
||||
/**
|
||||
* Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
|
||||
* should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
|
||||
* <p/>
|
||||
* <b>Note:</b> This is the most used create method.
|
||||
*
|
||||
* @param namespace the namespace of the action, can be <tt>null</tt>
|
||||
* @param actionName the name of the action
|
||||
* @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
|
||||
* @return ActionProxy the created action proxy
|
||||
* @deprecated Since 2.1.1, use {@link #createActionProxy(String,String,String,Map) instead}
|
||||
*/
|
||||
@Deprecated public ActionProxy createActionProxy(String namespace, String actionName, Map<String, Object> extraContext);
|
||||
|
||||
/**
|
||||
* Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
|
||||
* should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
|
||||
* <p/>
|
||||
* <b>Note:</b> This is the most used create method.
|
||||
*
|
||||
* @param namespace the namespace of the action, can be <tt>null</tt>
|
||||
* @param actionName the name of the action
|
||||
* @param methodName the name of the method to execute
|
||||
* @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
|
||||
* @return ActionProxy the created action proxy
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext);
|
||||
|
||||
/**
|
||||
* Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
|
||||
* should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
|
||||
*
|
||||
* @param namespace the namespace of the action, can be <tt>null</tt>
|
||||
* @param actionName the name of the action
|
||||
* @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
|
||||
* @param executeResult flag which tells whether the result should be executed after the action
|
||||
* @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
|
||||
* @return ActionProxy the created action proxy
|
||||
* @deprecated Since 2.1.1, use {@link #createActionProxy(String,String,String,Map,boolean,boolean)} instead
|
||||
*/
|
||||
@Deprecated public ActionProxy createActionProxy(String namespace, String actionName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext);
|
||||
|
||||
/**
|
||||
* Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
|
||||
* should be fully initialized when it is returned, including having an {@link ActionInvocation} instance associated.
|
||||
*
|
||||
* @param namespace the namespace of the action, can be <tt>null</tt>
|
||||
* @param actionName the name of the action
|
||||
* @param methodName the name of the method to execute
|
||||
* @param extraContext a Map of extra parameters to be provided to the ActionProxy, can be <tt>null</tt>
|
||||
* @param executeResult flag which tells whether the result should be executed after the action
|
||||
* @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
|
||||
* @return ActionProxy the created action proxy
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext);
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@link ActionProxy} for the given namespace and action name by looking up the configuration.The ActionProxy
|
||||
* should be fully initialized when it is returned, including passed {@link ActionInvocation} instance.
|
||||
*
|
||||
* @param actionInvocation the action invocation instance to associate with
|
||||
* @param namespace the namespace of the action, can be <tt>null</tt>
|
||||
* @param actionName the name of the action
|
||||
* @param methodName the name of the method to execute
|
||||
* @param executeResult flag which tells whether the result should be executed after the action
|
||||
* @param cleanupContext flag which tells whether the original context should be preserved during execution of the proxy.
|
||||
* @return ActionProxy the created action proxy
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public ActionProxy createActionProxy(ActionInvocation actionInvocation, String namespace, String actionName, String methodName,
|
||||
boolean executeResult, boolean cleanupContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
|
||||
/**
|
||||
* Provides a default implementation for the most common actions.
|
||||
* See the documentation for all the interfaces this class implements for more detailed information.
|
||||
*/
|
||||
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
|
||||
|
||||
protected static Logger LOG = LoggerFactory.getLogger(ActionSupport.class);
|
||||
|
||||
private final ValidationAwareSupport validationAware = new ValidationAwareSupport();
|
||||
|
||||
private transient TextProvider textProvider;
|
||||
private Container container;
|
||||
|
||||
public void setActionErrors(Collection<String> errorMessages) {
|
||||
validationAware.setActionErrors(errorMessages);
|
||||
}
|
||||
|
||||
public Collection<String> getActionErrors() {
|
||||
return validationAware.getActionErrors();
|
||||
}
|
||||
|
||||
public void setActionMessages(Collection<String> messages) {
|
||||
validationAware.setActionMessages(messages);
|
||||
}
|
||||
|
||||
public Collection<String> getActionMessages() {
|
||||
return validationAware.getActionMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getActionErrors()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<String> getErrorMessages() {
|
||||
return getActionErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getFieldErrors()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Map<String, List<String>> getErrors() {
|
||||
return getFieldErrors();
|
||||
}
|
||||
|
||||
public void setFieldErrors(Map<String, List<String>> errorMap) {
|
||||
validationAware.setFieldErrors(errorMap);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getFieldErrors() {
|
||||
return validationAware.getFieldErrors();
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
if (ctx != null) {
|
||||
return ctx.getLocale();
|
||||
} else {
|
||||
LOG.debug("Action context not initialized");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
return getTextProvider().hasKey(key);
|
||||
}
|
||||
|
||||
public String getText(String aTextName) {
|
||||
return getTextProvider().getText(aTextName);
|
||||
}
|
||||
|
||||
public String getText(String aTextName, String defaultValue) {
|
||||
return getTextProvider().getText(aTextName, defaultValue);
|
||||
}
|
||||
|
||||
public String getText(String aTextName, String defaultValue, String obj) {
|
||||
return getTextProvider().getText(aTextName, defaultValue, obj);
|
||||
}
|
||||
|
||||
public String getText(String aTextName, List<Object> args) {
|
||||
return getTextProvider().getText(aTextName, args);
|
||||
}
|
||||
|
||||
public String getText(String key, String[] args) {
|
||||
return getTextProvider().getText(key, args);
|
||||
}
|
||||
|
||||
public String getText(String aTextName, String defaultValue, List<Object> args) {
|
||||
return getTextProvider().getText(aTextName, defaultValue, args);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, String[] args) {
|
||||
return getTextProvider().getText(key, defaultValue, args);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, List<Object> args, ValueStack stack) {
|
||||
return getTextProvider().getText(key, defaultValue, args, stack);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
|
||||
return getTextProvider().getText(key, defaultValue, args, stack);
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts() {
|
||||
return getTextProvider().getTexts();
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts(String aBundleName) {
|
||||
return getTextProvider().getTexts(aBundleName);
|
||||
}
|
||||
|
||||
public void addActionError(String anErrorMessage) {
|
||||
validationAware.addActionError(anErrorMessage);
|
||||
}
|
||||
|
||||
public void addActionMessage(String aMessage) {
|
||||
validationAware.addActionMessage(aMessage);
|
||||
}
|
||||
|
||||
public void addFieldError(String fieldName, String errorMessage) {
|
||||
validationAware.addFieldError(fieldName, errorMessage);
|
||||
}
|
||||
|
||||
public String input() throws Exception {
|
||||
return INPUT;
|
||||
}
|
||||
|
||||
public String doDefault() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* A default implementation that does nothing an returns "success".
|
||||
* <p/>
|
||||
* Subclasses should override this method to provide their business logic.
|
||||
* <p/>
|
||||
* See also {@link com.opensymphony.xwork2.Action#execute()}.
|
||||
*
|
||||
* @return returns {@link #SUCCESS}
|
||||
* @throws Exception can be thrown by subclasses.
|
||||
*/
|
||||
public String execute() throws Exception {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
public boolean hasActionErrors() {
|
||||
return validationAware.hasActionErrors();
|
||||
}
|
||||
|
||||
public boolean hasActionMessages() {
|
||||
return validationAware.hasActionMessages();
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return validationAware.hasErrors();
|
||||
}
|
||||
|
||||
public boolean hasFieldErrors() {
|
||||
return validationAware.hasFieldErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears field errors. Useful for Continuations and other situations
|
||||
* where you might want to clear parts of the state on the same action.
|
||||
*/
|
||||
public void clearFieldErrors() {
|
||||
validationAware.clearFieldErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears action errors. Useful for Continuations and other situations
|
||||
* where you might want to clear parts of the state on the same action.
|
||||
*/
|
||||
public void clearActionErrors() {
|
||||
validationAware.clearActionErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears messages. Useful for Continuations and other situations
|
||||
* where you might want to clear parts of the state on the same action.
|
||||
*/
|
||||
public void clearMessages() {
|
||||
validationAware.clearMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all errors. Useful for Continuations and other situations
|
||||
* where you might want to clear parts of the state on the same action.
|
||||
*/
|
||||
public void clearErrors() {
|
||||
validationAware.clearErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all errors and messages. Useful for Continuations and other situations
|
||||
* where you might want to clear parts of the state on the same action.
|
||||
*/
|
||||
public void clearErrorsAndMessages() {
|
||||
validationAware.clearErrorsAndMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* A default implementation that validates nothing.
|
||||
* Subclasses should override this method to provide validations.
|
||||
*/
|
||||
public void validate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: pause-method -->
|
||||
* Stops the action invocation immediately (by throwing a PauseException) and causes the action invocation to return
|
||||
* the specified result, such as {@link #SUCCESS}, {@link #INPUT}, etc.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* The next time this action is invoked (and using the same continuation ID), the method will resume immediately
|
||||
* after where this method was called, with the entire call stack in the execute method restored.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Note: this method can <b>only</b> be called within the {@link #execute()} method.
|
||||
* <!-- END SNIPPET: pause-method -->
|
||||
*
|
||||
* @param result the result to return - the same type of return value in the {@link #execute()} method.
|
||||
*/
|
||||
public void pause(String result) {
|
||||
}
|
||||
|
||||
/**
|
||||
* If called first time it will create {@link com.opensymphony.xwork2.TextProviderFactory},
|
||||
* inject dependency (if {@link com.opensymphony.xwork2.inject.Container} is accesible) into in,
|
||||
* then will create new {@link com.opensymphony.xwork2.TextProvider} and store it in a field
|
||||
* for future references and at the returns reference to that field
|
||||
*
|
||||
* @return reference to field with TextProvider
|
||||
*/
|
||||
private TextProvider getTextProvider() {
|
||||
if (textProvider == null) {
|
||||
TextProviderFactory tpf = new TextProviderFactory();
|
||||
if (container != null) {
|
||||
container.inject(tpf);
|
||||
}
|
||||
textProvider = tpf.createInstance(getClass(), this);
|
||||
}
|
||||
return textProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* This is a composite {@link TextProvider} that takes in an array or {@link java.util.List} of {@link TextProvider}s, it will
|
||||
* consult each of them in order to get a composite result. To know how each method behaves, please refer to the
|
||||
* javadoc for each methods.
|
||||
*
|
||||
* @author tmjee
|
||||
* @version $Date$ $Id$
|
||||
*/
|
||||
public class CompositeTextProvider implements TextProvider {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CompositeTextProvider.class);
|
||||
|
||||
private List<TextProvider> textProviders = new ArrayList<TextProvider>();
|
||||
|
||||
/**
|
||||
* Instantiates a {@link CompositeTextProvider} with some predefined <code>textProviders</code>.
|
||||
*
|
||||
* @param textProviders
|
||||
*/
|
||||
public CompositeTextProvider(List<TextProvider> textProviders) {
|
||||
this.textProviders.addAll(textProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a {@link CompositeTextProvider} with some predefined <code>textProviders</code>.
|
||||
*
|
||||
* @param textProviders
|
||||
*/
|
||||
public CompositeTextProvider(TextProvider[] textProviders) {
|
||||
this(Arrays.asList(textProviders));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key to lookup in ressource bundles.
|
||||
* @return <tt>true</tt>, if the requested key is found in one of the ressource bundles.
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#hasKey(String)}
|
||||
* It will consult each individual {@link TextProvider}s and return true if either one of the
|
||||
* {@link TextProvider} has such a <code>key></code> else false.
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
// if there's a key in either text providers we are ok, else try the next text provider
|
||||
for (TextProvider tp : textProviders) {
|
||||
if (tp.hasKey(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>
|
||||
*
|
||||
* @param key The key to lookup in ressource bundles.
|
||||
* @return The i18n text for the requested key.
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String)}
|
||||
*/
|
||||
public String getText(String key) {
|
||||
return getText(key, key, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code> before returning <code>defaultValue</code> if every else fails.
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String)}
|
||||
*/
|
||||
public String getText(String key, String defaultValue) {
|
||||
return getText(key, defaultValue, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>, before returining <code>defaultValue</code>
|
||||
* if every else fails.
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @param obj
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String, String)}
|
||||
*/
|
||||
public String getText(String key, String defaultValue, final String obj) {
|
||||
return getText(key, defaultValue, new ArrayList<Object>() {
|
||||
{
|
||||
add(obj);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>.
|
||||
*
|
||||
* @param key
|
||||
* @param args
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, java.util.List)}
|
||||
*/
|
||||
public String getText(String key, List<Object> args) {
|
||||
return getText(key, key, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>.
|
||||
*
|
||||
* @param key
|
||||
* @param args
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String[])}
|
||||
*/
|
||||
public String getText(String key, String[] args) {
|
||||
return getText(key, key, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>, before returining <code>defaultValue</code>
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @param args
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText#getText(String, String, java.util.List)}
|
||||
*/
|
||||
public String getText(String key, String defaultValue, List<Object> args) {
|
||||
// if there's one text provider that gives us a msg not the same as defaultValue
|
||||
// for this key, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
String msg = textProvider.getText(key, defaultValue, args);
|
||||
if (msg != null && (!msg.equals(defaultValue))) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>, before returining <code>defaultValue</code>.
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @param args
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String, String[])}
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String[] args) {
|
||||
// if there's one text provider that gives us a msg not the same as defaultValue
|
||||
// for this key, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
String msg = textProvider.getText(key, defaultValue, args);
|
||||
if (msg != null && (!msg.equals(defaultValue))) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>, before returining <code>defaultValue</code>
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @param args
|
||||
* @param stack
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String, java.util.List, com.opensymphony.xwork2.util.OgnlValueStack)}
|
||||
*/
|
||||
public String getText(String key, String defaultValue, List<Object> args, ValueStack stack) {
|
||||
// if there's one text provider that gives us a msg not the same as defaultValue
|
||||
// for this key, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
String msg = textProvider.getText(key, defaultValue, args, stack);
|
||||
if (msg != null && (!msg.equals(defaultValue))) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first valid message for this
|
||||
* <code>key</code>, before returining <code>defaultValue</code>
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @param args
|
||||
* @param stack
|
||||
* @return
|
||||
* @see {@link com.opensymphony.xwork2.TextProvider#getText(String, String, String[], com.opensymphony.xwork2.util.ValueStack)}
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
|
||||
// if there's one text provider that gives us a msg not the same as defaultValue
|
||||
// for this key, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
String msg = textProvider.getText(key, defaultValue, args, stack);
|
||||
if (msg != null && (!msg.equals(defaultValue))) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It will consult each {@link TextProvider}s and return the first non-null {@link ResourceBundle}.
|
||||
*
|
||||
* @param bundleName
|
||||
* @return
|
||||
* @see {@link TextProvider#getTexts(String)}
|
||||
*/
|
||||
public ResourceBundle getTexts(String bundleName) {
|
||||
// if there's one text provider that gives us a non-null resource bunlde for this bundleName, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
ResourceBundle bundle = textProvider.getTexts(bundleName);
|
||||
if (bundle != null) {
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will consult each {@link com.opensymphony.xwork2.TextProvider}s and return the first non-null {@link ResourceBundle}.
|
||||
*
|
||||
* @return
|
||||
* @see {@link TextProvider#getTexts()}
|
||||
*/
|
||||
public ResourceBundle getTexts() {
|
||||
// if there's one text provider that gives us a non-null resource bundle, we are ok, else try the next
|
||||
// text provider
|
||||
for (TextProvider textProvider : textProviders) {
|
||||
ResourceBundle bundle = textProvider.getTexts();
|
||||
if (bundle != null) {
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
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.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* The Default ActionInvocation implementation
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @author tmjee
|
||||
* @version $Date$ $Id$
|
||||
* @see com.opensymphony.xwork2.DefaultActionProxy
|
||||
*/
|
||||
public class DefaultActionInvocation implements ActionInvocation {
|
||||
|
||||
private static final long serialVersionUID = -585293628862447329L;
|
||||
|
||||
//static {
|
||||
// if (ObjectFactory.getContinuationPackage() != null) {
|
||||
// continuationHandler = new ContinuationHandler();
|
||||
// }
|
||||
//}
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultActionInvocation.class);
|
||||
|
||||
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
|
||||
protected Object action;
|
||||
protected ActionProxy proxy;
|
||||
protected List<PreResultListener> preResultListeners;
|
||||
protected Map<String, Object> extraContext;
|
||||
protected ActionContext invocationContext;
|
||||
protected Iterator<InterceptorMapping> interceptors;
|
||||
protected ValueStack stack;
|
||||
protected Result result;
|
||||
protected Result explicitResult;
|
||||
protected String resultCode;
|
||||
protected boolean executed = false;
|
||||
protected boolean pushAction = true;
|
||||
protected ObjectFactory objectFactory;
|
||||
protected ActionEventListener actionEventListener;
|
||||
protected ValueStackFactory valueStackFactory;
|
||||
protected Container container;
|
||||
private Configuration configuration;
|
||||
protected UnknownHandlerManager unknownHandlerManager;
|
||||
|
||||
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
|
||||
DefaultActionInvocation.this.extraContext = extraContext;
|
||||
DefaultActionInvocation.this.pushAction = pushAction;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setUnknownHandlerManager(UnknownHandlerManager unknownHandlerManager) {
|
||||
this.unknownHandlerManager = unknownHandlerManager;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory fac) {
|
||||
this.valueStackFactory = fac;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory fac) {
|
||||
this.objectFactory = fac;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container cont) {
|
||||
this.container = cont;
|
||||
}
|
||||
|
||||
@Inject(required=false)
|
||||
public void setActionEventListener(ActionEventListener listener) {
|
||||
this.actionEventListener = listener;
|
||||
}
|
||||
|
||||
public Object getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
public ActionContext getInvocationContext() {
|
||||
return invocationContext;
|
||||
}
|
||||
|
||||
public ActionProxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the DefaultActionInvocation has been executed before and the Result is an instance of ActionChainResult, this method
|
||||
* will walk down the chain of ActionChainResults until it finds a non-chain result, which will be returned. If the
|
||||
* DefaultActionInvocation's result has not been executed before, the Result instance will be created and populated with
|
||||
* the result params.
|
||||
*
|
||||
* @return a Result instance
|
||||
* @throws Exception
|
||||
*/
|
||||
public Result getResult() throws Exception {
|
||||
Result returnResult = result;
|
||||
|
||||
// If we've chained to other Actions, we need to find the last result
|
||||
while (returnResult instanceof ActionChainResult) {
|
||||
ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();
|
||||
|
||||
if (aProxy != null) {
|
||||
Result proxyResult = aProxy.getInvocation().getResult();
|
||||
|
||||
if ((proxyResult != null) && (aProxy.getExecuteResult())) {
|
||||
returnResult = proxyResult;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
public String getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public void setResultCode(String resultCode) {
|
||||
if (isExecuted())
|
||||
throw new IllegalStateException("Result has already been executed.");
|
||||
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
|
||||
public ValueStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a com.opensymphony.xwork2.interceptor.PreResultListener to be notified after the Action is executed and before the
|
||||
* Result is executed. The ActionInvocation implementation must guarantee that listeners will be called in the order
|
||||
* in which they are registered. Listener registration and execution does not need to be thread-safe.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addPreResultListener(PreResultListener listener) {
|
||||
if (preResultListeners == null) {
|
||||
preResultListeners = new ArrayList<PreResultListener>(1);
|
||||
}
|
||||
|
||||
preResultListeners.add(listener);
|
||||
}
|
||||
|
||||
public Result createResult() throws Exception {
|
||||
|
||||
if (explicitResult != null) {
|
||||
Result ret = explicitResult;
|
||||
explicitResult = null;
|
||||
|
||||
return ret;
|
||||
}
|
||||
ActionConfig config = proxy.getConfig();
|
||||
Map<String, ResultConfig> results = config.getResults();
|
||||
|
||||
ResultConfig resultConfig = null;
|
||||
|
||||
try {
|
||||
resultConfig = results.get(resultCode);
|
||||
} catch (NullPointerException e) {
|
||||
// swallow
|
||||
}
|
||||
|
||||
if (resultConfig == null) {
|
||||
// If no result is found for the given resultCode, try to get a wildcard '*' match.
|
||||
resultConfig = results.get("*");
|
||||
}
|
||||
|
||||
if (resultConfig != null) {
|
||||
try {
|
||||
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
|
||||
} catch (Exception e) {
|
||||
LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
|
||||
throw new XWorkException(e, resultConfig);
|
||||
}
|
||||
} else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
|
||||
return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConfigurationException If no result can be found with the returned code
|
||||
*/
|
||||
public String invoke() throws Exception {
|
||||
String profileKey = "invoke: ";
|
||||
try {
|
||||
UtilTimerStack.push(profileKey);
|
||||
|
||||
if (executed) {
|
||||
throw new IllegalStateException("Action has already executed");
|
||||
}
|
||||
|
||||
if (interceptors.hasNext()) {
|
||||
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
|
||||
String interceptorMsg = "interceptor: " + interceptor.getName();
|
||||
UtilTimerStack.push(interceptorMsg);
|
||||
try {
|
||||
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
|
||||
}
|
||||
finally {
|
||||
UtilTimerStack.pop(interceptorMsg);
|
||||
}
|
||||
} else {
|
||||
resultCode = invokeActionOnly();
|
||||
}
|
||||
|
||||
// this is needed because the result will be executed, then control will return to the Interceptor, which will
|
||||
// return above and flow through again
|
||||
if (!executed) {
|
||||
if (preResultListeners != null) {
|
||||
for (Object preResultListener : preResultListeners) {
|
||||
PreResultListener listener = (PreResultListener) preResultListener;
|
||||
|
||||
String _profileKey = "preResultListener: ";
|
||||
try {
|
||||
UtilTimerStack.push(_profileKey);
|
||||
listener.beforeResult(this, resultCode);
|
||||
}
|
||||
finally {
|
||||
UtilTimerStack.pop(_profileKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now execute the result, if we're supposed to
|
||||
if (proxy.getExecuteResult()) {
|
||||
executeResult();
|
||||
}
|
||||
|
||||
executed = true;
|
||||
}
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
finally {
|
||||
UtilTimerStack.pop(profileKey);
|
||||
}
|
||||
}
|
||||
|
||||
public String invokeActionOnly() throws Exception {
|
||||
return invokeAction(getAction(), proxy.getConfig());
|
||||
}
|
||||
|
||||
protected void createAction(Map<String, Object> contextMap) {
|
||||
// load action
|
||||
String timerKey = "actionCreate: " + proxy.getActionName();
|
||||
try {
|
||||
UtilTimerStack.push(timerKey);
|
||||
action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
|
||||
} catch (InstantiationException e) {
|
||||
throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
|
||||
} catch (Exception e) {
|
||||
String gripe = "";
|
||||
|
||||
if (proxy == null) {
|
||||
gripe = "Whoa! No ActionProxy instance found in current ActionInvocation. This is bad ... very bad";
|
||||
} else if (proxy.getConfig() == null) {
|
||||
gripe = "Sheesh. Where'd that ActionProxy get to? I can't find it in the current ActionInvocation!?";
|
||||
} else if (proxy.getConfig().getClassName() == null) {
|
||||
gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
|
||||
} else {
|
||||
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
|
||||
}
|
||||
|
||||
gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
|
||||
throw new XWorkException(gripe, e, proxy.getConfig());
|
||||
} finally {
|
||||
UtilTimerStack.pop(timerKey);
|
||||
}
|
||||
|
||||
if (actionEventListener != null) {
|
||||
action = actionEventListener.prepare(action, stack);
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> createContextMap() {
|
||||
Map<String, Object> contextMap;
|
||||
|
||||
if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
|
||||
// In case the ValueStack was passed in
|
||||
stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
|
||||
|
||||
if (stack == null) {
|
||||
throw new IllegalStateException("There was a null Stack set into the extra params.");
|
||||
}
|
||||
|
||||
contextMap = stack.getContext();
|
||||
} else {
|
||||
// create the value stack
|
||||
// this also adds the ValueStack to its context
|
||||
stack = valueStackFactory.createValueStack();
|
||||
|
||||
// create the action context
|
||||
contextMap = stack.getContext();
|
||||
}
|
||||
|
||||
// put extraContext in
|
||||
if (extraContext != null) {
|
||||
contextMap.putAll(extraContext);
|
||||
}
|
||||
|
||||
//put this DefaultActionInvocation into the context map
|
||||
contextMap.put(ActionContext.ACTION_INVOCATION, this);
|
||||
contextMap.put(ActionContext.CONTAINER, container);
|
||||
|
||||
return contextMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses getResult to get the final Result and executes it
|
||||
*
|
||||
* @throws ConfigurationException If not result can be found with the returned code
|
||||
*/
|
||||
private void executeResult() throws Exception {
|
||||
result = createResult();
|
||||
|
||||
String timerKey = "executeResult: " + getResultCode();
|
||||
try {
|
||||
UtilTimerStack.push(timerKey);
|
||||
if (result != null) {
|
||||
result.execute(this);
|
||||
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
|
||||
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
|
||||
+ " and result " + getResultCode(), proxy.getConfig());
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
UtilTimerStack.pop(timerKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void init(ActionProxy proxy) {
|
||||
this.proxy = proxy;
|
||||
Map<String, Object> contextMap = createContextMap();
|
||||
|
||||
// Setting this so that other classes, like object factories, can use the ActionProxy and other
|
||||
// contextual information to operate
|
||||
ActionContext actionContext = ActionContext.getContext();
|
||||
|
||||
if (actionContext != null) {
|
||||
actionContext.setActionInvocation(this);
|
||||
}
|
||||
|
||||
createAction(contextMap);
|
||||
|
||||
if (pushAction) {
|
||||
stack.push(action);
|
||||
contextMap.put("action", action);
|
||||
}
|
||||
|
||||
invocationContext = new ActionContext(contextMap);
|
||||
invocationContext.setName(proxy.getActionName());
|
||||
|
||||
// get a new List so we don't get problems with the iterator if someone changes the list
|
||||
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
|
||||
interceptors = interceptorList.iterator();
|
||||
}
|
||||
|
||||
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
|
||||
String methodName = proxy.getMethod();
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Executing action method = " + actionConfig.getMethodName());
|
||||
}
|
||||
|
||||
String timerKey = "invokeAction: " + proxy.getActionName();
|
||||
try {
|
||||
UtilTimerStack.push(timerKey);
|
||||
|
||||
boolean methodCalled = false;
|
||||
Object methodResult = null;
|
||||
Method method = null;
|
||||
try {
|
||||
method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// hmm -- OK, try doXxx instead
|
||||
try {
|
||||
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
|
||||
method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
|
||||
} catch (NoSuchMethodException e1) {
|
||||
// well, give the unknown handler a shot
|
||||
if (unknownHandlerManager.hasUnknownHandlers()) {
|
||||
try {
|
||||
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
|
||||
methodCalled = true;
|
||||
} catch (NoSuchMethodException e2) {
|
||||
// throw the original one
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!methodCalled) {
|
||||
methodResult = method.invoke(action, new Object[0]);
|
||||
}
|
||||
|
||||
if (methodResult instanceof Result) {
|
||||
this.explicitResult = (Result) methodResult;
|
||||
|
||||
// Wire the result automatically
|
||||
container.inject(explicitResult);
|
||||
return null;
|
||||
} else {
|
||||
return (String) methodResult;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
|
||||
} catch (InvocationTargetException e) {
|
||||
// We try to return the source exception.
|
||||
Throwable t = e.getTargetException();
|
||||
|
||||
if (actionEventListener != null) {
|
||||
String result = actionEventListener.handleException(t, getStack());
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (t instanceof Exception) {
|
||||
throw (Exception) t;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
UtilTimerStack.pop(timerKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* The Default ActionProxy implementation
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @author Revised by <a href="mailto:hu_pengfei@yahoo.com.cn">Henry Hu</a>
|
||||
* @author tmjee
|
||||
*
|
||||
* @version $Date$ $Id$
|
||||
* @since 2005-8-6
|
||||
*/
|
||||
public class DefaultActionProxy implements ActionProxy, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3293074152487468527L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultActionProxy.class);
|
||||
|
||||
protected Configuration configuration;
|
||||
protected ActionConfig config;
|
||||
protected ActionInvocation invocation;
|
||||
protected UnknownHandlerManager unknownHandlerManager;
|
||||
protected String actionName;
|
||||
protected String namespace;
|
||||
protected String method;
|
||||
protected boolean executeResult;
|
||||
protected boolean cleanupContext;
|
||||
|
||||
protected ObjectFactory objectFactory;
|
||||
|
||||
protected ActionEventListener actionEventListener;
|
||||
|
||||
/**
|
||||
* This constructor is private so the builder methods (create*) should be used to create an DefaultActionProxy.
|
||||
* <p/>
|
||||
* The reason for the builder methods is so that you can use a subclass to create your own DefaultActionProxy instance
|
||||
* (like a RMIActionProxy).
|
||||
*/
|
||||
protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
|
||||
|
||||
this.invocation = inv;
|
||||
this.cleanupContext = cleanupContext;
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Creating an DefaultActionProxy for namespace " + namespace + " and action name " + actionName);
|
||||
}
|
||||
|
||||
this.actionName = actionName;
|
||||
this.namespace = namespace;
|
||||
this.executeResult = executeResult;
|
||||
this.method = methodName;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory factory) {
|
||||
this.objectFactory = factory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setConfiguration(Configuration config) {
|
||||
this.configuration = config;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setUnknownHandler(UnknownHandlerManager unknownHandlerManager) {
|
||||
this.unknownHandlerManager = unknownHandlerManager;
|
||||
}
|
||||
|
||||
@Inject(required=false)
|
||||
public void setActionEventListener(ActionEventListener listener) {
|
||||
this.actionEventListener = listener;
|
||||
}
|
||||
|
||||
public Object getAction() {
|
||||
return invocation.getAction();
|
||||
}
|
||||
|
||||
public String getActionName() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
public ActionConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setExecuteResult(boolean executeResult) {
|
||||
this.executeResult = executeResult;
|
||||
}
|
||||
|
||||
public boolean getExecuteResult() {
|
||||
return executeResult;
|
||||
}
|
||||
|
||||
public ActionInvocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String execute() throws Exception {
|
||||
ActionContext nestedContext = ActionContext.getContext();
|
||||
ActionContext.setContext(invocation.getInvocationContext());
|
||||
|
||||
String retCode = null;
|
||||
|
||||
String profileKey = "execute: ";
|
||||
try {
|
||||
UtilTimerStack.push(profileKey);
|
||||
|
||||
retCode = invocation.invoke();
|
||||
} finally {
|
||||
if (cleanupContext) {
|
||||
ActionContext.setContext(nestedContext);
|
||||
}
|
||||
UtilTimerStack.pop(profileKey);
|
||||
}
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
private void resolveMethod() {
|
||||
// if the method is set to null, use the one from the configuration
|
||||
// if the one from the configuration is also null, use "execute"
|
||||
if (StringUtils.isEmpty(this.method)) {
|
||||
this.method = config.getMethodName();
|
||||
if (StringUtils.isEmpty(this.method)) {
|
||||
this.method = "execute";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepare() {
|
||||
String profileKey = "create DefaultActionProxy: ";
|
||||
try {
|
||||
UtilTimerStack.push(profileKey);
|
||||
config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
|
||||
|
||||
if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
|
||||
config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
|
||||
}
|
||||
if (config == null) {
|
||||
String message;
|
||||
|
||||
if ((namespace != null) && (namespace.trim().length() > 0)) {
|
||||
message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
|
||||
namespace, actionName
|
||||
});
|
||||
} else {
|
||||
message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
|
||||
actionName
|
||||
});
|
||||
}
|
||||
throw new ConfigurationException(message);
|
||||
}
|
||||
|
||||
resolveMethod();
|
||||
|
||||
if (!config.isAllowedMethod(method)) {
|
||||
throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);
|
||||
}
|
||||
|
||||
invocation.init(this);
|
||||
|
||||
} finally {
|
||||
UtilTimerStack.pop(profileKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Default factory for {@link com.opensymphony.xwork2.ActionProxyFactory}.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public class DefaultActionProxyFactory implements ActionProxyFactory {
|
||||
|
||||
protected Container container;
|
||||
|
||||
public DefaultActionProxyFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, Map<String, Object> extraContext) {
|
||||
return createActionProxy(namespace, actionName, null, extraContext, true, true);
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext) {
|
||||
return createActionProxy(namespace, actionName, methodName, extraContext, true, true);
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
|
||||
return createActionProxy(namespace, actionName, null, extraContext, executeResult, cleanupContext);
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
|
||||
|
||||
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
|
||||
container.inject(inv);
|
||||
return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, boolean executeResult, boolean cleanupContext) {
|
||||
|
||||
return createActionProxy(inv, namespace, actionName, null, executeResult, cleanupContext);
|
||||
}
|
||||
|
||||
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
|
||||
|
||||
DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
|
||||
container.inject(proxy);
|
||||
proxy.prepare();
|
||||
return proxy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* DefaultTextProvider gets texts from only the default resource bundles associated with the
|
||||
* LocalizedTextUtil.
|
||||
*
|
||||
* @author Jason Carreira <jcarreira@gmail.com>
|
||||
* @author Rainer Hermanns
|
||||
* @see LocalizedTextUtil#addDefaultResourceBundle(String)
|
||||
*/
|
||||
public class DefaultTextProvider implements TextProvider, Serializable, Unchainable {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
public DefaultTextProvider() {
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
return getText(key) != null;
|
||||
}
|
||||
|
||||
public String getText(String key) {
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale());
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue) {
|
||||
String text = getText(key);
|
||||
if (text == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public String getText(String key, List<Object> args) {
|
||||
Object[] params;
|
||||
if (args != null) {
|
||||
params = args.toArray();
|
||||
} else {
|
||||
params = EMPTY_ARGS;
|
||||
}
|
||||
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
}
|
||||
|
||||
public String getText(String key, String[] args) {
|
||||
Object[] params;
|
||||
if (args != null) {
|
||||
params = args;
|
||||
} else {
|
||||
params = EMPTY_ARGS;
|
||||
}
|
||||
|
||||
return LocalizedTextUtil.findDefaultText(key, ActionContext.getContext().getLocale(), params);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, List<Object> args) {
|
||||
String text = getText(key, args);
|
||||
if(text == null && defaultValue == null) {
|
||||
defaultValue = key;
|
||||
}
|
||||
if (text == null && defaultValue != null) {
|
||||
|
||||
MessageFormat format = new MessageFormat(defaultValue);
|
||||
format.setLocale(ActionContext.getContext().getLocale());
|
||||
format.applyPattern(defaultValue);
|
||||
|
||||
Object[] params;
|
||||
if (args != null) {
|
||||
params = args.toArray();
|
||||
} else {
|
||||
params = EMPTY_ARGS;
|
||||
}
|
||||
|
||||
return format.format(params);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, String[] args) {
|
||||
String text = getText(key, args);
|
||||
if (text == null) {
|
||||
MessageFormat format = new MessageFormat(defaultValue);
|
||||
format.setLocale(ActionContext.getContext().getLocale());
|
||||
format.applyPattern(defaultValue);
|
||||
|
||||
if (args == null) {
|
||||
return format.format(EMPTY_ARGS);
|
||||
}
|
||||
|
||||
return format.format(args);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
public String getText(String key, String defaultValue, String obj) {
|
||||
List<Object> args = new ArrayList<Object>(1);
|
||||
args.add(obj);
|
||||
return getText(key, defaultValue, args);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, List<Object> args, ValueStack stack) {
|
||||
//we're not using the value stack here
|
||||
return getText(key, defaultValue, args);
|
||||
}
|
||||
|
||||
public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
|
||||
//we're not using the value stack here
|
||||
List<Object> values = new ArrayList<Object>(Arrays.asList(args));
|
||||
return getText(key, defaultValue, values);
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts(String bundleName) {
|
||||
return LocalizedTextUtil.findResourceBundle(bundleName, ActionContext.getContext().getLocale());
|
||||
}
|
||||
|
||||
public ResourceBundle getTexts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.UnknownHandler;
|
||||
import com.opensymphony.xwork2.UnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
/**
|
||||
* Default implementation of UnknownHandlerManager
|
||||
*
|
||||
* @see com.opensymphony.xwork2.UnknownHandlerManager
|
||||
*/
|
||||
public class DefaultUnknownHandlerManager implements UnknownHandlerManager {
|
||||
protected ArrayList<UnknownHandler> unknownHandlers;
|
||||
private Configuration configuration;
|
||||
private Container container;
|
||||
|
||||
@Inject
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
build();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of UnknowHandlers in the order specified by the configured "unknown-handler-stack".
|
||||
* If "unknown-handler-stack" was not configured, all UnknowHandlers will be returned, in no specific order
|
||||
*/
|
||||
protected void build() {
|
||||
if (configuration != null && container != null) {
|
||||
List<UnknownHandlerConfig> unkownHandlerStack = configuration.getUnknownHandlerStack();
|
||||
unknownHandlers = new ArrayList<UnknownHandler>();
|
||||
|
||||
if (unkownHandlerStack != null && !unkownHandlerStack.isEmpty()) {
|
||||
//get UnknownHandlers in the specified order
|
||||
for (UnknownHandlerConfig unknownHandlerConfig : unkownHandlerStack) {
|
||||
UnknownHandler uh = container.getInstance(UnknownHandler.class, unknownHandlerConfig.getName());
|
||||
unknownHandlers.add(uh);
|
||||
}
|
||||
} else {
|
||||
//add all available UnknownHandlers
|
||||
Set<String> unknowHandlerNames = container.getInstanceNames(UnknownHandler.class);
|
||||
if (unknowHandlerNames != null) {
|
||||
for (String unknowHandlerName : unknowHandlerNames) {
|
||||
UnknownHandler uh = container.getInstance(UnknownHandler.class, unknowHandlerName);
|
||||
unknownHandlers.add(uh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over UnknownHandlers and return the result of the first one that can handle it
|
||||
*/
|
||||
public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig, String resultCode) {
|
||||
for (UnknownHandler unknownHandler : unknownHandlers) {
|
||||
Result result = unknownHandler.handleUnknownResult(actionContext, actionName, actionConfig, resultCode);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over UnknownHandlers and return the result of the first one that can handle it
|
||||
*
|
||||
* @throws NoSuchMethodException
|
||||
*/
|
||||
public Object handleUnknownMethod(Object action, String methodName) throws NoSuchMethodException {
|
||||
for (UnknownHandler unknownHandler : unknownHandlers) {
|
||||
Object result = unknownHandler.handleUnknownActionMethod(action, methodName);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over UnknownHandlers and return the result of the first one that can handle it
|
||||
*
|
||||
* @throws NoSuchMethodException
|
||||
*/
|
||||
public ActionConfig handleUnknownAction(String namespace, String actionName) {
|
||||
for (UnknownHandler unknownHandler : unknownHandlers) {
|
||||
ActionConfig result = unknownHandler.handleUnknownAction(namespace, actionName);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasUnknownHandlers() {
|
||||
return unknownHandlers != null && !unknownHandlers.isEmpty();
|
||||
}
|
||||
|
||||
public List<UnknownHandler> getUnknownHandlers() {
|
||||
return unknownHandlers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
/**
|
||||
* <code>InvalidMetadataException</code>
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InvalidMetadataException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Create a new <code>InvalidMetadataException</code> with the supplied error message.
|
||||
*
|
||||
* @param msg the error message
|
||||
*/
|
||||
public InvalidMetadataException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that the implementing class can provide its own {@link Locale}.
|
||||
* <p/>
|
||||
* This is useful for when an action may wish override the default locale. All that is
|
||||
* needed is to implement this interface and return your own custom locale.
|
||||
* The {@link TextProvider} interface uses this interface heavily for retrieving
|
||||
* internationalized messages from resource bundles.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public interface LocaleProvider {
|
||||
|
||||
/**
|
||||
* Gets the provided locale.
|
||||
*
|
||||
* @return the locale.
|
||||
*/
|
||||
Locale getLocale();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
/**
|
||||
* Mock for an {@link ActionInvocation}.
|
||||
*
|
||||
* @author plightbo
|
||||
* @deprecated Please use @see com.opensymphony.xwork2.mock.MockActionInvocation instead
|
||||
*/
|
||||
@Deprecated public class MockActionInvocation extends com.opensymphony.xwork2.mock.MockActionInvocation {
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
|
||||
/**
|
||||
* ModelDriven Actions provide a model object to be pushed onto the ValueStack
|
||||
* in addition to the Action itself, allowing a FormBean type approach like Struts.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public interface ModelDriven<T> {
|
||||
|
||||
/**
|
||||
* Gets the model to be pushed onto the ValueStack instead of the Action itself.
|
||||
*
|
||||
* @return the model
|
||||
*/
|
||||
T getModel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionException;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import com.opensymphony.xwork2.validator.Validator;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* ObjectFactory is responsible for building the core framework objects. Users may register their
|
||||
* own implementation of the ObjectFactory to control instantiation of these Objects.
|
||||
* <p/>
|
||||
* This default implementation uses the {@link #buildBean(Class,java.util.Map) buildBean}
|
||||
* method to create all classes (interceptors, actions, results, etc).
|
||||
* <p/>
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public class ObjectFactory implements Serializable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ObjectFactory.class);
|
||||
|
||||
private transient ClassLoader ccl;
|
||||
private Container container;
|
||||
protected ReflectionProvider reflectionProvider;
|
||||
|
||||
@Inject(value="objectFactory.classloader", required=false)
|
||||
public void setClassLoader(ClassLoader cl) {
|
||||
this.ccl = cl;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setReflectionProvider(ReflectionProvider prov) {
|
||||
this.reflectionProvider = prov;
|
||||
}
|
||||
|
||||
public ObjectFactory() {
|
||||
}
|
||||
|
||||
public ObjectFactory(ReflectionProvider prov) {
|
||||
this.reflectionProvider = prov;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Since 2.1
|
||||
*/
|
||||
@Deprecated public static ObjectFactory getObjectFactory() {
|
||||
return ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for ObjectFactory implementations that support
|
||||
* Actions without no-arg constructors.
|
||||
*
|
||||
* @return true if no-arg constructor is required, false otherwise
|
||||
*/
|
||||
public boolean isNoArgConstructorRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to obtain the class matched to className. Caches look ups so that subsequent
|
||||
* lookups will be faster.
|
||||
*
|
||||
* @param className The fully qualified name of the class to return
|
||||
* @return The class itself
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public Class getClassInstance(String className) throws ClassNotFoundException {
|
||||
if (ccl != null) {
|
||||
return ccl.loadClass(className);
|
||||
}
|
||||
|
||||
return ClassLoaderUtil.loadClass(className, this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an instance of the action class to handle a particular request (eg. web request)
|
||||
* @param actionName the name the action configuration is set up with in the configuration
|
||||
* @param namespace the namespace the action is configured in
|
||||
* @param config the action configuration found in the config for the actionName / namespace
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
* @return instance of the action class to handle a web request
|
||||
* @throws Exception
|
||||
*/
|
||||
public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
|
||||
return buildBean(config.getClassName(), extraContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a generic Java object of the given type.
|
||||
*
|
||||
* @param clazz the type of Object to build
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
*/
|
||||
public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param obj
|
||||
*/
|
||||
protected Object injectInternalBeans(Object obj) {
|
||||
if (obj != null && container != null) {
|
||||
container.inject(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a generic Java object of the given type.
|
||||
*
|
||||
* @param className the type of Object to build
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
*/
|
||||
public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
|
||||
return buildBean(className, extraContext, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a generic Java object of the given type.
|
||||
*
|
||||
* @param className the type of Object to build
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
*/
|
||||
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
|
||||
Class clazz = getClassInstance(className);
|
||||
Object obj = buildBean(clazz, extraContext);
|
||||
if (injectInternal) {
|
||||
injectInternalBeans(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an Interceptor from the InterceptorConfig and the Map of
|
||||
* parameters from the interceptor reference. Implementations of this method
|
||||
* should ensure that the Interceptor is parameterized with both the
|
||||
* parameters from the Interceptor config and the interceptor ref Map (the
|
||||
* interceptor ref params take precedence), and that the Interceptor.init()
|
||||
* method is called on the Interceptor instance before it is returned.
|
||||
*
|
||||
* @param interceptorConfig the InterceptorConfig from the configuration
|
||||
* @param interceptorRefParams a Map of params provided in the Interceptor reference in the
|
||||
* Action mapping or InterceptorStack definition
|
||||
*/
|
||||
public Interceptor buildInterceptor(InterceptorConfig interceptorConfig, Map<String, String> interceptorRefParams) throws ConfigurationException {
|
||||
String interceptorClassName = interceptorConfig.getClassName();
|
||||
Map<String, String> thisInterceptorClassParams = interceptorConfig.getParams();
|
||||
Map<String, String> params = (thisInterceptorClassParams == null) ? new HashMap<String, String>() : new HashMap<String, String>(thisInterceptorClassParams);
|
||||
params.putAll(interceptorRefParams);
|
||||
|
||||
String message;
|
||||
Throwable cause;
|
||||
|
||||
try {
|
||||
// interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
|
||||
Interceptor interceptor = (Interceptor) buildBean(interceptorClassName, null);
|
||||
reflectionProvider.setProperties(params, interceptor);
|
||||
interceptor.init();
|
||||
|
||||
return interceptor;
|
||||
} catch (InstantiationException e) {
|
||||
cause = e;
|
||||
message = "Unable to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
|
||||
} catch (IllegalAccessException e) {
|
||||
cause = e;
|
||||
message = "IllegalAccessException while attempting to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
|
||||
} catch (ClassCastException e) {
|
||||
cause = e;
|
||||
message = "Class [" + interceptorClassName + "] does not implement com.opensymphony.xwork2.interceptor.Interceptor";
|
||||
} catch (Exception e) {
|
||||
cause = e;
|
||||
message = "Caught Exception while registering Interceptor class " + interceptorClassName;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
cause = e;
|
||||
message = "Could not load class " + interceptorClassName + ". Perhaps it exists but certain dependencies are not available?";
|
||||
}
|
||||
|
||||
throw new ConfigurationException(message, cause, interceptorConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Result using the type in the ResultConfig and set the parameters in the ResultConfig.
|
||||
*
|
||||
* @param resultConfig the ResultConfig found for the action with the result code returned
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
*/
|
||||
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
|
||||
String resultClassName = resultConfig.getClassName();
|
||||
Result result = null;
|
||||
|
||||
if (resultClassName != null) {
|
||||
result = (Result) buildBean(resultClassName, extraContext);
|
||||
Map<String, String> params = resultConfig.getParams();
|
||||
if (params != null) {
|
||||
for (Map.Entry<String, String> paramEntry : params.entrySet()) {
|
||||
try {
|
||||
reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);
|
||||
} catch (ReflectionException ex) {
|
||||
if (result instanceof ReflectionExceptionHandler) {
|
||||
((ReflectionExceptionHandler) result).handle(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Validator of the given type and set the parameters on it
|
||||
*
|
||||
* @param className the type of Validator to build
|
||||
* @param params property name -> value Map to set onto the Validator instance
|
||||
* @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
|
||||
*/
|
||||
public Validator buildValidator(String className, Map<String, String> params, Map<String, Object> extraContext) throws Exception {
|
||||
Validator validator = (Validator) buildBean(className, null);
|
||||
reflectionProvider.setProperties(params, validator);
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
static class ContinuationsClassLoader extends ClassLoader {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
|
||||
/**
|
||||
* Preparable Actions will have their <code>prepare()</code> method called if the {@link com.opensymphony.xwork2.interceptor.PrepareInterceptor}
|
||||
* is applied to the ActionConfig.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @see com.opensymphony.xwork2.interceptor.PrepareInterceptor
|
||||
*/
|
||||
public interface Preparable {
|
||||
|
||||
/**
|
||||
* This method is called to allow the action to prepare itself.
|
||||
*
|
||||
* @throws Exception thrown if a system level exception occurs.
|
||||
*/
|
||||
void prepare() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Extension Interface for TextProvider to help supporting ResourceBundles.
|
||||
*
|
||||
* @author Rene Gielen
|
||||
*/
|
||||
public interface ResourceBundleTextProvider extends TextProvider {
|
||||
|
||||
/**
|
||||
* Set the resource bundle to use.
|
||||
*
|
||||
* @param bundle the bundle to use.
|
||||
*/
|
||||
void setBundle(ResourceBundle bundle);
|
||||
|
||||
/**
|
||||
* Set the class to use for reading the resource bundle.
|
||||
*
|
||||
* @param clazz the class to use for loading.
|
||||
*/
|
||||
void setClazz(Class clazz);
|
||||
|
||||
/**
|
||||
* Set the LocaleProvider to use.
|
||||
*
|
||||
* @param localeProvider the LocaleProvider to use.
|
||||
*/
|
||||
void setLocaleProvider(LocaleProvider localeProvider);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* All results (except for <code>Action.NONE</code>) of an {@link Action} are mapped to a View implementation.
|
||||
* <p/>
|
||||
* Examples of Views might be:
|
||||
* <ul>
|
||||
* <li>SwingPanelView - pops up a new Swing panel</li>
|
||||
* <li>ActionChainView - executes another action</li>
|
||||
* <li>SerlvetRedirectView - redirects the HTTP response to a URL</li>
|
||||
* <li>ServletDispatcherView - dispatches the HTTP response to a URL</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author plightbo
|
||||
*/
|
||||
public interface Result extends Serializable {
|
||||
|
||||
/**
|
||||
* Represents a generic interface for all action execution results.
|
||||
* Whether that be displaying a webpage, generating an email, sending a JMS message, etc.
|
||||
*
|
||||
* @param invocation the invocation context.
|
||||
* @throws Exception can be thrown.
|
||||
*/
|
||||
public void execute(ActionInvocation invocation) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.impl.MockConfiguration;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
|
||||
/**
|
||||
* Base test class for TestNG unit tests. Provides common XWork variables
|
||||
* and performs XWork setup and teardown processes
|
||||
*/
|
||||
public class TestNGXWorkTestCase {
|
||||
|
||||
protected ConfigurationManager configurationManager;
|
||||
protected Configuration configuration;
|
||||
protected Container container;
|
||||
protected ActionProxyFactory actionProxyFactory;
|
||||
|
||||
@BeforeTest
|
||||
protected void setUp() throws Exception {
|
||||
configurationManager = XWorkTestCaseHelper.setUp();
|
||||
configuration = new MockConfiguration();
|
||||
((MockConfiguration) configuration).selfRegister();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
protected void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
configurationManager = null;
|
||||
configuration = null;
|
||||
container = null;
|
||||
actionProxyFactory = null;
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
|
||||
/**
|
||||
* Provides access to {@link ResourceBundle}s and their underlying text messages.
|
||||
* Implementing classes can delegate {@link TextProviderSupport}. Messages will be
|
||||
* searched in multiple resource bundles, startinag with the one associated with
|
||||
* this particular class (action in most cases), continuing to try the message
|
||||
* bundle associated with each superclass as well. It will stop once a bundle is
|
||||
* found that contains the given text. This gives a cascading style that allow
|
||||
* global texts to be defined for an application base class.
|
||||
* <p/>
|
||||
* You can override {@link LocaleProvider#getLocale()} to change the behaviour of how
|
||||
* to choose locale for the bundles that are returned. Typically you would
|
||||
* use the {@link LocaleProvider} interface to get the users configured locale.
|
||||
* <p/>
|
||||
* When you want to use your own implementation for Struts 2 project you have to define following
|
||||
* bean and constant in struts.xml:
|
||||
* <bean class="org.demo.MyTextProvider" name="myTextProvider" type="com.opensymphony.xwork2.TextProvider" />
|
||||
* <constant name="struts.xworkTextProvider" value="myTextProvider" />
|
||||
* <p/>
|
||||
* if you want to also use your implemntation for framework's messages define another constant (remember to put
|
||||
* into it all framework messages)
|
||||
* <constant name="system" value="myTextProvider" />
|
||||
* <p/>
|
||||
* Take a look on {@link com.opensymphony.xwork2.ActionSupport} for example TextProvider implemntation.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author Rainer Hermanns
|
||||
* @see LocaleProvider
|
||||
* @see TextProviderSupport
|
||||
*/
|
||||
public interface TextProvider {
|
||||
|
||||
/**
|
||||
* Checks if a message key exists.
|
||||
*
|
||||
* @param key message key to check for
|
||||
* @return boolean true if key exists, false otherwise.
|
||||
*/
|
||||
boolean hasKey(String key);
|
||||
|
||||
/**
|
||||
* Gets a message based on a message key, or null if no message is found.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @return the message as found in the resource bundle, or null if none is found.
|
||||
*/
|
||||
String getText(String key);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key, or, if the message is not found, a supplied
|
||||
* default value is returned.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied obj, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param obj obj to be used in a {@link java.text.MessageFormat} message
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue, String obj);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or null if no message is found.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param args a list args to be used in a {@link java.text.MessageFormat} message
|
||||
* @return the message as found in the resource bundle, or null if none is found.
|
||||
*/
|
||||
String getText(String key, List<Object> args);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or null if no message is found.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param args an array args to be used in a {@link java.text.MessageFormat} message
|
||||
* @return the message as found in the resource bundle, or null if none is found.
|
||||
*/
|
||||
String getText(String key, String[] args);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args a list args to be used in a {@link java.text.MessageFormat} message
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue, List<Object> args);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args an array args to be used in a {@link java.text.MessageFormat} message
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue, String[] args);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned. Instead of using the value stack in the ActionContext
|
||||
* this version of the getText() method uses the provided value stack.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args a list args to be used in a {@link java.text.MessageFormat} message
|
||||
* @param stack the value stack to use for finding the text
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue, List<Object> args, ValueStack stack);
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned. Instead of using the value stack in the ActionContext
|
||||
* this version of the getText() method uses the provided value stack.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args an array args to be used in a {@link java.text.MessageFormat} message
|
||||
* @param stack the value stack to use for finding the text
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
String getText(String key, String defaultValue, String[] args, ValueStack stack);
|
||||
|
||||
/**
|
||||
* Get the named bundle, such as "com/acme/Foo".
|
||||
*
|
||||
* @param bundleName the name of the resource bundle, such as <code>"com/acme/Foo"</code>.
|
||||
* @return the bundle
|
||||
*/
|
||||
ResourceBundle getTexts(String bundleName);
|
||||
|
||||
/**
|
||||
* Get the resource bundle associated with the implementing class (usually an action).
|
||||
*
|
||||
* @return the bundle
|
||||
*/
|
||||
ResourceBundle getTexts();
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* This factory enables users to provide and correctly initialize a custom TextProvider.
|
||||
*
|
||||
* @author Oleg Gorobets
|
||||
* @author Rene Gielen
|
||||
*/
|
||||
public class TextProviderFactory {
|
||||
|
||||
private TextProvider textProvider;
|
||||
|
||||
@Inject
|
||||
public void setTextProvider(TextProvider textProvider) {
|
||||
this.textProvider = textProvider;
|
||||
}
|
||||
|
||||
protected TextProvider getTextProvider() {
|
||||
if (this.textProvider == null) {
|
||||
return new TextProviderSupport();
|
||||
} else {
|
||||
return textProvider;
|
||||
}
|
||||
}
|
||||
|
||||
public TextProvider createInstance(Class clazz, LocaleProvider provider) {
|
||||
TextProvider instance = getTextProvider();
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setClazz(clazz);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public TextProvider createInstance(ResourceBundle bundle, LocaleProvider provider) {
|
||||
TextProvider instance = getTextProvider();
|
||||
if (instance instanceof ResourceBundleTextProvider) {
|
||||
((ResourceBundleTextProvider) instance).setBundle(bundle);
|
||||
((ResourceBundleTextProvider) instance).setLocaleProvider(provider);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.LocalizedTextUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Default TextProvider implementation.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author Rainer Hermanns
|
||||
*/
|
||||
public class TextProviderSupport implements ResourceBundleTextProvider {
|
||||
|
||||
private Class clazz;
|
||||
private LocaleProvider localeProvider;
|
||||
private ResourceBundle bundle;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public TextProviderSupport() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param clazz a clazz to use for reading the resource bundle.
|
||||
* @param provider a locale provider.
|
||||
*/
|
||||
public TextProviderSupport(Class clazz, LocaleProvider provider) {
|
||||
this.clazz = clazz;
|
||||
this.localeProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param bundle the resource bundle.
|
||||
* @param provider a locale provider.
|
||||
*/
|
||||
public TextProviderSupport(ResourceBundle bundle, LocaleProvider provider) {
|
||||
this.bundle = bundle;
|
||||
this.localeProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bundle the resource bundle.
|
||||
*/
|
||||
public void setBundle(ResourceBundle bundle) {
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz a clazz to use for reading the resource bundle.
|
||||
*/
|
||||
public void setClazz(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param localeProvider a locale provider.
|
||||
*/
|
||||
public void setLocaleProvider(LocaleProvider localeProvider) {
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a key is available in the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class.
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
String message;
|
||||
if (clazz != null) {
|
||||
message = LocalizedTextUtil.findText(clazz, key, getLocale(), null, new Object[0] );
|
||||
} else {
|
||||
message = LocalizedTextUtil.findText(bundle, key, getLocale(), null, new Object[0]);
|
||||
}
|
||||
return message != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key) {
|
||||
return getText(key, key, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param defaultValue the default value which will be returned if no text is found
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, String defaultValue) {
|
||||
return getText(key, defaultValue, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param defaultValue the default value which will be returned if no text is found
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String arg) {
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add(arg);
|
||||
return getText(key, defaultValue, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param args a List of args to be used in a MessageFormat message
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, List<Object> args) {
|
||||
return getText(key, key, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param args an array of args to be used in a MessageFormat message
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, String[] args) {
|
||||
return getText(key, key, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param defaultValue the default value which will be returned if no text is found
|
||||
* @param args a List of args to be used in a MessageFormat message
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, String defaultValue, List<Object> args) {
|
||||
Object[] argsArray = ((args != null && !args.equals(Collections.emptyList())) ? args.toArray() : null);
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, argsArray);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, argsArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a text from the resource bundles associated with this action.
|
||||
* The resource bundles are searched, starting with the one associated
|
||||
* with this particular action, and testing all its superclasses' bundles.
|
||||
* It will stop once a bundle is found that contains the given text. This gives
|
||||
* a cascading style that allow global texts to be defined for an application base
|
||||
* class. If no text is found for this text name, the default value is returned.
|
||||
*
|
||||
* @param key name of text to be found
|
||||
* @param defaultValue the default value which will be returned if no text is found
|
||||
* @param args an array of args to be used in a MessageFormat message
|
||||
* @return value of named text
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String[] args) {
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, getLocale(), defaultValue, args);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, getLocale(), defaultValue, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned. Instead of using the value stack in the ActionContext
|
||||
* this version of the getText() method uses the provided value stack.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args a list args to be used in a {@link java.text.MessageFormat} message
|
||||
* @param stack the value stack to use for finding the text
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
public String getText(String key, String defaultValue, List<Object> args, ValueStack stack) {
|
||||
Object[] argsArray = ((args != null) ? args.toArray() : null);
|
||||
Locale locale;
|
||||
if (stack == null){
|
||||
locale = getLocale();
|
||||
}else{
|
||||
locale = (Locale) stack.getContext().get(ActionContext.LOCALE);
|
||||
}
|
||||
if (locale == null) {
|
||||
locale = getLocale();
|
||||
}
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, argsArray, stack);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, argsArray, stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a message based on a key using the supplied args, as defined in
|
||||
* {@link java.text.MessageFormat}, or, if the message is not found, a supplied
|
||||
* default value is returned. Instead of using the value stack in the ActionContext
|
||||
* this version of the getText() method uses the provided value stack.
|
||||
*
|
||||
* @param key the resource bundle key that is to be searched for
|
||||
* @param defaultValue the default value which will be returned if no message is found
|
||||
* @param args an array args to be used in a {@link java.text.MessageFormat} message
|
||||
* @param stack the value stack to use for finding the text
|
||||
* @return the message as found in the resource bundle, or defaultValue if none is found
|
||||
*/
|
||||
public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
|
||||
Locale locale;
|
||||
if (stack == null){
|
||||
locale = getLocale();
|
||||
}else{
|
||||
locale = (Locale) stack.getContext().get(ActionContext.LOCALE);
|
||||
}
|
||||
if (locale == null) {
|
||||
locale = getLocale();
|
||||
}
|
||||
if (clazz != null) {
|
||||
return LocalizedTextUtil.findText(clazz, key, locale, defaultValue, args, stack);
|
||||
} else {
|
||||
return LocalizedTextUtil.findText(bundle, key, locale, defaultValue, args, stack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the named bundle.
|
||||
* <p/>
|
||||
* You can override the getLocale() methodName to change the behaviour of how
|
||||
* to choose locale for the bundles that are returned. Typically you would
|
||||
* use the TextProvider interface to get the users configured locale, or use
|
||||
* your own methodName to allow the user to select the locale and store it in
|
||||
* the session (by using the SessionAware interface).
|
||||
*
|
||||
* @param aBundleName bundle name
|
||||
* @return a resource bundle
|
||||
*/
|
||||
public ResourceBundle getTexts(String aBundleName) {
|
||||
return LocalizedTextUtil.findResourceBundle(aBundleName, getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource bundle associated with this action.
|
||||
* This will be based on the actual subclass that is used.
|
||||
*
|
||||
* @return resouce bundle
|
||||
*/
|
||||
public ResourceBundle getTexts() {
|
||||
if (clazz != null) {
|
||||
return getTexts(clazz.getName());
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the locale from the localeProvider.
|
||||
*
|
||||
* @return the locale from the localeProvider.
|
||||
*/
|
||||
private Locale getLocale() {
|
||||
return localeProvider.getLocale();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
/**
|
||||
* Simple marker interface to indicate an object should <b>not</b> have its properties copied during chaining.
|
||||
*
|
||||
* @see com.opensymphony.xwork2.interceptor.ChainingInterceptor
|
||||
*/
|
||||
public interface Unchainable {
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
|
||||
/**
|
||||
* Handles cases when the result or action is unknown.
|
||||
* <p/>
|
||||
* This allows other classes like Struts plugins to provide intelligent defaults easier.
|
||||
*/
|
||||
public interface UnknownHandler {
|
||||
|
||||
/**
|
||||
* Handles the case when an action configuration is unknown. Implementations can return a new ActionConfig
|
||||
* to be used to process the request.
|
||||
*
|
||||
* @param namespace The namespace
|
||||
* @param actionName The action name
|
||||
* @return An generated ActionConfig, can return <tt>null</tt>
|
||||
* @throws XWorkException
|
||||
*/
|
||||
public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException;
|
||||
|
||||
/**
|
||||
* Handles the case when a result cannot be found for an action and result code.
|
||||
*
|
||||
* @param actionContext The action context
|
||||
* @param actionName The action name
|
||||
* @param actionConfig The action config
|
||||
* @param resultCode The returned result code
|
||||
* @return A result to be executed, can return <tt>null</tt>
|
||||
* @throws XWorkException
|
||||
*/
|
||||
public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig, String resultCode) throws XWorkException;
|
||||
|
||||
/**
|
||||
* Handles the case when an action method cannot be found. This method is responsible both for finding the method and executing it.
|
||||
*
|
||||
* @since 2.1
|
||||
* @param action The action object
|
||||
* @param methodName The method name to call
|
||||
* @return The result returned from invoking the action method
|
||||
* @throws NoSuchMethodException If the method cannot be found
|
||||
*/
|
||||
public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An unknown handler manager contains a list of UnknownHandler and iterates on them by order
|
||||
*
|
||||
* @see com.opensymphony.xwork2.DefaultUnknownHandlerManager
|
||||
*/
|
||||
public interface UnknownHandlerManager {
|
||||
Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig, String resultCode);
|
||||
|
||||
Object handleUnknownMethod(Object action, String methodName) throws NoSuchMethodException;
|
||||
|
||||
ActionConfig handleUnknownAction(String namespace, String actionName);
|
||||
|
||||
boolean hasUnknownHandlers();
|
||||
|
||||
List<UnknownHandler> getUnknownHandlers();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
|
||||
/**
|
||||
* Provides an interface in which a call for a validation check can be done.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @see ActionSupport
|
||||
* @see com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor
|
||||
*/
|
||||
public interface Validateable {
|
||||
|
||||
/**
|
||||
* Performs validation.
|
||||
*/
|
||||
void validate();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ValidationAware classes can accept Action (class level) or field level error messages. Action level messages are kept
|
||||
* in a Collection. Field level error messages are kept in a Map from String field name to a List of field error msgs.
|
||||
*
|
||||
* @author plightbo
|
||||
*/
|
||||
public interface ValidationAware {
|
||||
|
||||
/**
|
||||
* Set the Collection of Action-level String error messages.
|
||||
*
|
||||
* @param errorMessages Collection of String error messages
|
||||
*/
|
||||
void setActionErrors(Collection<String> errorMessages);
|
||||
|
||||
/**
|
||||
* Get the Collection of Action-level error messages for this action. Error messages should not
|
||||
* be added directly here, as implementations are free to return a new Collection or an
|
||||
* Unmodifiable Collection.
|
||||
*
|
||||
* @return Collection of String error messages
|
||||
*/
|
||||
Collection<String> getActionErrors();
|
||||
|
||||
/**
|
||||
* Set the Collection of Action-level String messages (not errors).
|
||||
*
|
||||
* @param messages Collection of String messages (not errors).
|
||||
*/
|
||||
void setActionMessages(Collection<String> messages);
|
||||
|
||||
/**
|
||||
* Get the Collection of Action-level messages for this action. Messages should not be added
|
||||
* directly here, as implementations are free to return a new Collection or an Unmodifiable
|
||||
* Collection.
|
||||
*
|
||||
* @return Collection of String messages
|
||||
*/
|
||||
Collection<String> getActionMessages();
|
||||
|
||||
/**
|
||||
* Set the field error map of fieldname (String) to Collection of String error messages.
|
||||
*
|
||||
* @param errorMap field error map
|
||||
*/
|
||||
void setFieldErrors(Map<String, List<String>> errorMap);
|
||||
|
||||
/**
|
||||
* Get the field specific errors associated with this action. Error messages should not be added
|
||||
* directly here, as implementations are free to return a new Collection or an Unmodifiable
|
||||
* Collection.
|
||||
*
|
||||
* @return Map with errors mapped from fieldname (String) to Collection of String error messages
|
||||
*/
|
||||
Map<String, List<String>> getFieldErrors();
|
||||
|
||||
/**
|
||||
* Add an Action-level error message to this Action.
|
||||
*
|
||||
* @param anErrorMessage the error message
|
||||
*/
|
||||
void addActionError(String anErrorMessage);
|
||||
|
||||
/**
|
||||
* Add an Action-level message to this Action.
|
||||
*
|
||||
* @param aMessage the message
|
||||
*/
|
||||
void addActionMessage(String aMessage);
|
||||
|
||||
/**
|
||||
* Add an error message for a given field.
|
||||
*
|
||||
* @param fieldName name of field
|
||||
* @param errorMessage the error message
|
||||
*/
|
||||
void addFieldError(String fieldName, String errorMessage);
|
||||
|
||||
/**
|
||||
* Check whether there are any Action-level error messages.
|
||||
*
|
||||
* @return true if any Action-level error messages have been registered
|
||||
*/
|
||||
boolean hasActionErrors();
|
||||
|
||||
/**
|
||||
* Checks whether there are any Action-level messages.
|
||||
*
|
||||
* @return true if any Action-level messages have been registered
|
||||
*/
|
||||
boolean hasActionMessages();
|
||||
|
||||
/**
|
||||
* Checks whether there are any action errors or field errors.
|
||||
* <p/>
|
||||
* <b>Note</b>: that this does not have the same meaning as in WW 1.x.
|
||||
*
|
||||
* @return <code>(hasActionErrors() || hasFieldErrors())</code>
|
||||
*/
|
||||
boolean hasErrors();
|
||||
|
||||
/**
|
||||
* Check whether there are any field errors associated with this action.
|
||||
*
|
||||
* @return whether there are any field errors
|
||||
*/
|
||||
boolean hasFieldErrors();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Provides a default implementation of ValidationAware. Returns new collections for
|
||||
* errors and messages (defensive copy).
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author tm_jee
|
||||
* @version $Date$ $Id$
|
||||
*/
|
||||
public class ValidationAwareSupport implements ValidationAware, Serializable {
|
||||
|
||||
private Collection<String> actionErrors;
|
||||
private Collection<String> actionMessages;
|
||||
private Map<String, List<String>> fieldErrors;
|
||||
|
||||
|
||||
public synchronized void setActionErrors(Collection<String> errorMessages) {
|
||||
this.actionErrors = errorMessages;
|
||||
}
|
||||
|
||||
public synchronized Collection<String> getActionErrors() {
|
||||
return new ArrayList<String>(internalGetActionErrors());
|
||||
}
|
||||
|
||||
public synchronized void setActionMessages(Collection<String> messages) {
|
||||
this.actionMessages = messages;
|
||||
}
|
||||
|
||||
public synchronized Collection<String> getActionMessages() {
|
||||
return new ArrayList<String>(internalGetActionMessages());
|
||||
}
|
||||
|
||||
public synchronized void setFieldErrors(Map<String, List<String>> errorMap) {
|
||||
this.fieldErrors = errorMap;
|
||||
}
|
||||
|
||||
public synchronized Map<String, List<String>> getFieldErrors() {
|
||||
return new LinkedHashMap<String, List<String>>(internalGetFieldErrors());
|
||||
}
|
||||
|
||||
public synchronized void addActionError(String anErrorMessage) {
|
||||
internalGetActionErrors().add(anErrorMessage);
|
||||
}
|
||||
|
||||
public synchronized void addActionMessage(String aMessage) {
|
||||
internalGetActionMessages().add(aMessage);
|
||||
}
|
||||
|
||||
public synchronized void addFieldError(String fieldName, String errorMessage) {
|
||||
final Map<String, List<String>> errors = internalGetFieldErrors();
|
||||
List<String> thisFieldErrors = errors.get(fieldName);
|
||||
|
||||
if (thisFieldErrors == null) {
|
||||
thisFieldErrors = new ArrayList<String>();
|
||||
errors.put(fieldName, thisFieldErrors);
|
||||
}
|
||||
|
||||
thisFieldErrors.add(errorMessage);
|
||||
}
|
||||
|
||||
public synchronized boolean hasActionErrors() {
|
||||
return (actionErrors != null) && !actionErrors.isEmpty();
|
||||
}
|
||||
|
||||
public synchronized boolean hasActionMessages() {
|
||||
return (actionMessages != null) && !actionMessages.isEmpty();
|
||||
}
|
||||
|
||||
public synchronized boolean hasErrors() {
|
||||
return (hasActionErrors() || hasFieldErrors());
|
||||
}
|
||||
|
||||
public synchronized boolean hasFieldErrors() {
|
||||
return (fieldErrors != null) && !fieldErrors.isEmpty();
|
||||
}
|
||||
|
||||
private Collection<String> internalGetActionErrors() {
|
||||
if (actionErrors == null) {
|
||||
actionErrors = new ArrayList<String>();
|
||||
}
|
||||
|
||||
return actionErrors;
|
||||
}
|
||||
|
||||
private Collection<String> internalGetActionMessages() {
|
||||
if (actionMessages == null) {
|
||||
actionMessages = new ArrayList<String>();
|
||||
}
|
||||
|
||||
return actionMessages;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> internalGetFieldErrors() {
|
||||
if (fieldErrors == null) {
|
||||
fieldErrors = new LinkedHashMap<String, List<String>>();
|
||||
}
|
||||
|
||||
return fieldErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears field errors map.
|
||||
* <p/>
|
||||
* Will clear the map that contains field errors.
|
||||
*/
|
||||
public synchronized void clearFieldErrors() {
|
||||
internalGetFieldErrors().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears action errors list.
|
||||
* <p/>
|
||||
* Will clear the list that contains action errors.
|
||||
*/
|
||||
public synchronized void clearActionErrors() {
|
||||
internalGetActionErrors().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears messages list.
|
||||
* <p/>
|
||||
* Will clear the list that contains action messages.
|
||||
*/
|
||||
public synchronized void clearMessages() {
|
||||
internalGetActionMessages().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all error list/maps.
|
||||
* <p/>
|
||||
* Will clear the map and list that contain
|
||||
* field errors and action errors.
|
||||
*/
|
||||
public synchronized void clearErrors() {
|
||||
internalGetFieldErrors().clear();
|
||||
internalGetActionErrors().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all error and messages list/maps.
|
||||
* <p/>
|
||||
* Will clear the maps/lists that contain
|
||||
* field errors, action errors and action messages.
|
||||
*/
|
||||
public synchronized void clearErrorsAndMessages() {
|
||||
internalGetFieldErrors().clear();
|
||||
internalGetActionErrors().clear();
|
||||
internalGetActionMessages().clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Simple facade to make using XWork standalone easier
|
||||
*/
|
||||
public class XWork {
|
||||
|
||||
ConfigurationManager configurationManager;
|
||||
|
||||
public XWork() {
|
||||
this(new ConfigurationManager());
|
||||
}
|
||||
|
||||
public XWork(ConfigurationManager mgr) {
|
||||
this.configurationManager = mgr;
|
||||
}
|
||||
|
||||
public void setLoggerFactory(LoggerFactory factory) {
|
||||
LoggerFactory.setLoggerFactory(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an action
|
||||
*
|
||||
* @param namespace The namespace
|
||||
* @param name The action name
|
||||
* @param method The method name
|
||||
* @throws Exception If anything goes wrong
|
||||
*/
|
||||
public void executeAction(String namespace, String name, String method) throws XWorkException {
|
||||
Map<String, Object> extraContext = Collections.emptyMap();
|
||||
executeAction(namespace, name, method, extraContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an action with extra context information
|
||||
*
|
||||
* @param namespace The namespace
|
||||
* @param name The action name
|
||||
* @param method The method name
|
||||
* @param extraContext A map of extra context information
|
||||
* @throws Exception If anything goes wrong
|
||||
*/
|
||||
public void executeAction(String namespace, String name, String method, Map<String, Object> extraContext) throws XWorkException {
|
||||
Configuration config = configurationManager.getConfiguration();
|
||||
try {
|
||||
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
|
||||
namespace, name, method, extraContext, true, false);
|
||||
|
||||
proxy.execute();
|
||||
} catch (Exception e) {
|
||||
throw new XWorkException(e);
|
||||
} finally {
|
||||
ActionContext.setContext(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Locatable;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import com.opensymphony.xwork2.util.location.LocationUtils;
|
||||
|
||||
|
||||
/**
|
||||
* A generic runtime exception that optionally contains Location information
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public class XWorkException extends RuntimeException implements Locatable {
|
||||
|
||||
private Location location;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with no detail message.
|
||||
*/
|
||||
public XWorkException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public XWorkException(String s) {
|
||||
this(s, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message and target.
|
||||
*
|
||||
* @param s the detail message.
|
||||
* @param target the target of the exception.
|
||||
*/
|
||||
public XWorkException(String s, Object target) {
|
||||
this(s, (Throwable) null, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the root cause
|
||||
*
|
||||
* @param cause The wrapped exception
|
||||
*/
|
||||
public XWorkException(Throwable cause) {
|
||||
this(null, cause, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the root cause and target
|
||||
*
|
||||
* @param cause The wrapped exception
|
||||
* @param target The target of the exception
|
||||
*/
|
||||
public XWorkException(Throwable cause, Object target) {
|
||||
this(null, cause, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message and exception cause.
|
||||
*
|
||||
* @param s the detail message.
|
||||
* @param cause the wrapped exception
|
||||
*/
|
||||
public XWorkException(String s, Throwable cause) {
|
||||
this(s, cause, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message, cause, and target
|
||||
*
|
||||
* @param s the detail message.
|
||||
* @param cause The wrapped exception
|
||||
* @param target The target of the exception
|
||||
*/
|
||||
public XWorkException(String s, Throwable cause, Object target) {
|
||||
super(s, cause);
|
||||
|
||||
this.location = LocationUtils.getLocation(target);
|
||||
if (this.location == Location.UNKNOWN) {
|
||||
this.location = LocationUtils.getLocation(cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the underlying cause
|
||||
*
|
||||
* @return the underlying cause, <tt>null</tt> if no cause
|
||||
* @deprecated Use {@link #getCause()}
|
||||
*/
|
||||
@Deprecated public Throwable getThrowable() {
|
||||
return getCause();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the location of the error, if available
|
||||
*
|
||||
* @return the location, <tt>null</tt> if not available
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a short description of this throwable object, including the
|
||||
* location. If no detailed message is available, it will use the message
|
||||
* of the underlying exception if available.
|
||||
*
|
||||
* @return a string representation of this <code>Throwable</code>.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String msg = getMessage();
|
||||
if (msg == null && getCause() != null) {
|
||||
msg = getCause().getMessage();
|
||||
}
|
||||
|
||||
if (location != null) {
|
||||
if (msg != null) {
|
||||
return msg + " - " + location.toString();
|
||||
} else {
|
||||
return location.toString();
|
||||
}
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
|
||||
/**
|
||||
* Contains constants for some default XWork messages.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public interface XWorkMessages {
|
||||
|
||||
public static final String ACTION_EXECUTION_ERROR = "xwork.error.action.execution";
|
||||
public static final String MISSING_ACTION_EXCEPTION = "xwork.exception.missing-action";
|
||||
public static final String MISSING_PACKAGE_ACTION_EXCEPTION = "xwork.exception.missing-package-action";
|
||||
public static final String DEFAULT_INVALID_FIELDVALUE = "xwork.default.invalid.fieldvalue";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.test.StubConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
|
||||
* the generic test setup for logic.
|
||||
*
|
||||
* @author plightbo
|
||||
*/
|
||||
public abstract class XWorkTestCase extends TestCase {
|
||||
|
||||
protected ConfigurationManager configurationManager;
|
||||
protected Configuration configuration;
|
||||
protected Container container;
|
||||
protected ActionProxyFactory actionProxyFactory;
|
||||
|
||||
public XWorkTestCase() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
configurationManager = XWorkTestCaseHelper.setUp();
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
XWorkTestCaseHelper.tearDown(configurationManager);
|
||||
configurationManager = null;
|
||||
configuration = null;
|
||||
container = null;
|
||||
actionProxyFactory = null;
|
||||
}
|
||||
|
||||
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
|
||||
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
|
||||
configuration = configurationManager.getConfiguration();
|
||||
container = configuration.getContainer();
|
||||
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
|
||||
}
|
||||
|
||||
protected void loadButAdd(final Class<?> type, final Object impl) {
|
||||
loadButAdd(type, Container.DEFAULT_NAME, impl);
|
||||
}
|
||||
|
||||
protected void loadButAdd(final Class<?> type, final String name, final Object impl) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
public void register(ContainerBuilder builder,
|
||||
LocatableProperties props) throws ConfigurationException {
|
||||
builder.factory(type, name, new Factory() {
|
||||
public Object create(Context context) throws Exception {
|
||||
return impl;
|
||||
}
|
||||
|
||||
}, Scope.SINGLETON);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* XWork configuration.
|
||||
*
|
||||
* @author Mike
|
||||
*/
|
||||
public interface Configuration extends Serializable {
|
||||
|
||||
void rebuildRuntimeConfiguration();
|
||||
|
||||
PackageConfig getPackageConfig(String name);
|
||||
|
||||
Set<String> getPackageConfigNames();
|
||||
|
||||
Map<String, PackageConfig> getPackageConfigs();
|
||||
|
||||
/**
|
||||
* The current runtime configuration. Currently, if changes have been made to the Configuration since the last
|
||||
* time buildRuntimeConfiguration() was called, you'll need to make sure to.
|
||||
*
|
||||
* @return the current runtime configuration
|
||||
*/
|
||||
RuntimeConfiguration getRuntimeConfiguration();
|
||||
|
||||
void addPackageConfig(String name, PackageConfig packageConfig);
|
||||
|
||||
/**
|
||||
* Removes a package from the the list of packages. Changes to the configuration won't take effect until buildRuntimeConfiguration
|
||||
* is called.
|
||||
* @param packageName the name of the package to remove
|
||||
* @return the package removed (if any)
|
||||
*/
|
||||
PackageConfig removePackageConfig(String packageName);
|
||||
|
||||
/**
|
||||
* Allow the Configuration to clean up any resources that have been used.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @deprecated Since 2.1
|
||||
* @param providers
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
@Deprecated void reload(List<ConfigurationProvider> providers) throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* @since 2.1
|
||||
* @param containerProviders
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
List<PackageProvider> reloadContainer(List<ContainerProvider> containerProviders) throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* @return the container
|
||||
*/
|
||||
Container getContainer();
|
||||
|
||||
Set<String> getLoadedFileNames();
|
||||
|
||||
/**
|
||||
* @since 2.1
|
||||
* @return list of unknown handlers
|
||||
*/
|
||||
List<UnknownHandlerConfig> getUnknownHandlerStack();
|
||||
|
||||
/**
|
||||
* @since 2.1
|
||||
* @param unknownHandlerStack
|
||||
*/
|
||||
void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
|
||||
|
||||
/**
|
||||
* ConfigurationException
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public class ConfigurationException extends XWorkException {
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with no detail message.
|
||||
*/
|
||||
public ConfigurationException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public ConfigurationException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public ConfigurationException(String s, Object target) {
|
||||
super(s, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with no detail message.
|
||||
*/
|
||||
public ConfigurationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with no detail message.
|
||||
*/
|
||||
public ConfigurationException(Throwable cause, Object target) {
|
||||
super(cause, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public ConfigurationException(String s, Throwable cause) {
|
||||
super(s, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>ConfigurationException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public ConfigurationException(String s, Throwable cause, Object target) {
|
||||
super(s, cause, target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
||||
import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.FileManager;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
/**
|
||||
* ConfigurationManager - central for XWork Configuration management, including
|
||||
* its ConfigurationProvider.
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author tm_jee
|
||||
* @version $Date$ $Id$
|
||||
*/
|
||||
public class ConfigurationManager {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class);
|
||||
protected Configuration configuration;
|
||||
protected Lock providerLock = new ReentrantLock();
|
||||
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
|
||||
private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<PackageProvider>();
|
||||
protected String defaultFrameworkBeanName;
|
||||
|
||||
public ConfigurationManager() {
|
||||
this("xwork");
|
||||
}
|
||||
|
||||
public ConfigurationManager(String name) {
|
||||
this.defaultFrameworkBeanName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current XWork configuration object. By default an instance of DefaultConfiguration will be returned
|
||||
*
|
||||
* @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
|
||||
*/
|
||||
public synchronized Configuration getConfiguration() {
|
||||
if (configuration == null) {
|
||||
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
|
||||
try {
|
||||
configuration.reloadContainer(getContainerProviders());
|
||||
} catch (ConfigurationException e) {
|
||||
setConfiguration(null);
|
||||
throw new ConfigurationException("Unable to load configuration.", e);
|
||||
}
|
||||
} else {
|
||||
conditionalReload();
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public synchronized void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
|
||||
* will return a list containing only the default ConfigurationProvider, XMLConfigurationProvider. if a custom
|
||||
* ConfigurationProvider has been added, then the XmlConfigurationProvider must be added by hand.
|
||||
* </p>
|
||||
* <p/>
|
||||
* WARNING: This returns only ContainerProviders that can be cast into ConfigurationProviders
|
||||
*
|
||||
* @return the list of registered ConfigurationProvider objects
|
||||
* @see ConfigurationProvider
|
||||
* @deprecated Since 2.1, use {@link #getContainerProviders()}
|
||||
*/
|
||||
@Deprecated public List<ConfigurationProvider> getConfigurationProviders() {
|
||||
List<ContainerProvider> contProviders = getContainerProviders();
|
||||
List<ConfigurationProvider> providers = new ArrayList<ConfigurationProvider>();
|
||||
for (ContainerProvider prov : contProviders) {
|
||||
if (prov instanceof ConfigurationProvider) {
|
||||
providers.add((ConfigurationProvider) prov);
|
||||
}
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current list of ConfigurationProviders. If no custom ConfigurationProviders have been added, this method
|
||||
* will return a list containing only the default ConfigurationProvider, XMLConfigurationProvider. if a custom
|
||||
* ConfigurationProvider has been added, then the XmlConfigurationProvider must be added by hand.
|
||||
* </p>
|
||||
* <p/>
|
||||
* TODO: the lazy instantiation of XmlConfigurationProvider should be refactored to be elsewhere. the behavior described above seems unintuitive.
|
||||
*
|
||||
* @return the list of registered ConfigurationProvider objects
|
||||
* @see ConfigurationProvider
|
||||
*/
|
||||
public List<ContainerProvider> getContainerProviders() {
|
||||
providerLock.lock();
|
||||
try {
|
||||
if (containerProviders.size() == 0) {
|
||||
containerProviders.add(new XWorkConfigurationProvider());
|
||||
containerProviders.add(new XmlConfigurationProvider("xwork.xml", false));
|
||||
}
|
||||
|
||||
return containerProviders;
|
||||
} finally {
|
||||
providerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of configuration providers
|
||||
*
|
||||
* @param configurationProviders
|
||||
* @deprecated Since 2.1, use {@link #setContainerProvider()}
|
||||
*/
|
||||
@Deprecated public void setConfigurationProviders(List<ConfigurationProvider> configurationProviders) {
|
||||
// Silly copy necessary due to lack of ability to cast generic lists
|
||||
List<ContainerProvider> contProviders = new ArrayList<ContainerProvider>();
|
||||
contProviders.addAll(configurationProviders);
|
||||
|
||||
setContainerProviders(contProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of configuration providers
|
||||
*
|
||||
* @param containerProviders
|
||||
*/
|
||||
public void setContainerProviders(List<ContainerProvider> containerProviders) {
|
||||
providerLock.lock();
|
||||
try {
|
||||
this.containerProviders = new CopyOnWriteArrayList<ContainerProvider>(containerProviders);
|
||||
} finally {
|
||||
providerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a configuration provider to the List of ConfigurationProviders. a given ConfigurationProvider may be added
|
||||
* more than once
|
||||
*
|
||||
* @param provider the ConfigurationProvider to register
|
||||
* @deprecated Since 2.1, use {@link #addContainerProvider()}
|
||||
*/
|
||||
@Deprecated public void addConfigurationProvider(ConfigurationProvider provider) {
|
||||
addContainerProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a configuration provider to the List of ConfigurationProviders. a given ConfigurationProvider may be added
|
||||
* more than once
|
||||
*
|
||||
* @param provider the ConfigurationProvider to register
|
||||
*/
|
||||
public void addContainerProvider(ContainerProvider provider) {
|
||||
if (!containerProviders.contains(provider)) {
|
||||
containerProviders.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clears the registered ConfigurationProviders. this method will call destroy() on each of the registered
|
||||
* ConfigurationProviders
|
||||
*
|
||||
* @see com.opensymphony.xwork2.config.ConfigurationProvider#destroy
|
||||
* @deprecated Since 2.1, use {@link #clearContainerProviders()}
|
||||
*/
|
||||
@Deprecated public void clearConfigurationProviders() {
|
||||
clearContainerProviders();
|
||||
}
|
||||
|
||||
public void clearContainerProviders() {
|
||||
for (ContainerProvider containerProvider : containerProviders) {
|
||||
try {
|
||||
containerProvider.destroy();
|
||||
}
|
||||
catch(Exception e) {
|
||||
LOG.warn("error while destroying container provider ["+containerProvider+"]", e);
|
||||
}
|
||||
}
|
||||
containerProviders.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy its managing Configuration instance
|
||||
*/
|
||||
public synchronized void destroyConfiguration() {
|
||||
clearConfigurationProviders(); // let's destroy the ConfigurationProvider first
|
||||
containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
|
||||
if (configuration != null)
|
||||
configuration.destroy(); // let's destroy it first, before nulling it.
|
||||
configuration = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reloads the Configuration files if the configuration files indicate that they need to be reloaded.
|
||||
*/
|
||||
public synchronized void conditionalReload() {
|
||||
if (FileManager.isReloadingConfigs()) {
|
||||
boolean reload;
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Checking ConfigurationProviders for reload.");
|
||||
}
|
||||
|
||||
reload = false;
|
||||
|
||||
List<ContainerProvider> providers = getContainerProviders();
|
||||
for (ContainerProvider provider : providers) {
|
||||
if (provider.needsReload()) {
|
||||
if (LOG.isInfoEnabled()) {
|
||||
LOG.info("Detected container provider "+provider+" needs to be reloaded. Reloading all providers.");
|
||||
}
|
||||
reload = true;
|
||||
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packageProviders != null && reload) {
|
||||
for (PackageProvider provider : packageProviders) {
|
||||
if (provider.needsReload()) {
|
||||
if (LOG.isInfoEnabled()) {
|
||||
LOG.info("Detected package provider "+provider+" needs to be reloaded. Reloading all providers.");
|
||||
}
|
||||
reload = true;
|
||||
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
for (ContainerProvider containerProvider : containerProviders) {
|
||||
try {
|
||||
containerProvider.destroy();
|
||||
}
|
||||
catch(Exception e) {
|
||||
LOG.warn("error while destroying configuration provider ["+containerProvider+"]", e);
|
||||
}
|
||||
}
|
||||
packageProviders = configuration.reloadContainer(providers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void reload() {
|
||||
packageProviders = getConfiguration().reloadContainer(getContainerProviders());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all forms of XWork configuration classes.
|
||||
*/
|
||||
public interface ConfigurationProvider extends ContainerProvider, PackageProvider {
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* ConfigurationUtil
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* Created May 23, 2003 11:22:49 PM
|
||||
*/
|
||||
public class ConfigurationUtil {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationUtil.class);
|
||||
|
||||
|
||||
private ConfigurationUtil() {
|
||||
}
|
||||
|
||||
|
||||
public static List<PackageConfig> buildParentsFromString(Configuration configuration, String parent) {
|
||||
if ((parent == null) || ("".equals(parent))) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(parent, ", ");
|
||||
List<PackageConfig> parents = new ArrayList<PackageConfig>();
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String parentName = tokenizer.nextToken().trim();
|
||||
|
||||
if (!"".equals(parentName)) {
|
||||
PackageConfig parentPackageContext = configuration.getPackageConfig(parentName);
|
||||
|
||||
if (parentPackageContext != null) {
|
||||
parents.add(parentPackageContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parents;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
|
||||
|
||||
/**
|
||||
* Provides beans and constants/properties for the Container
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface ContainerProvider {
|
||||
|
||||
/**
|
||||
* Called before removed from the configuration manager
|
||||
*/
|
||||
public void destroy();
|
||||
|
||||
/**
|
||||
* Initializes with the configuration
|
||||
* @param configuration The configuration
|
||||
* @throws ConfigurationException If anything goes wrong
|
||||
*/
|
||||
public void init(Configuration configuration) throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Tells whether the ContainerProvider should reload its configuration
|
||||
*
|
||||
* @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
|
||||
*/
|
||||
public boolean needsReload();
|
||||
|
||||
/**
|
||||
* Registers beans and properties for the Container
|
||||
*
|
||||
* @param builder The builder to register beans with
|
||||
* @param props The properties to register constants with
|
||||
* @throws ConfigurationException If anything goes wrong
|
||||
*/
|
||||
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
/**
|
||||
* Provides configuration packages. The separate init and loadPackages calls are due to the need to
|
||||
* preserve backwards compatibility with the 2.0 {@link ConfigurationProvider} interface
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface PackageProvider {
|
||||
|
||||
/**
|
||||
* Initializes with the configuration
|
||||
* @param configuration The configuration
|
||||
* @throws ConfigurationException If anything goes wrong
|
||||
*/
|
||||
public void init(Configuration configuration) throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Tells whether the PackageProvider should reload its configuration
|
||||
*
|
||||
* @return <tt>true</tt>, whether the PackageProvider should reload its configuration, <tt>false</tt>otherwise.
|
||||
*/
|
||||
public boolean needsReload();
|
||||
|
||||
/**
|
||||
* Loads the packages for the configuration.
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public void loadPackages() throws ConfigurationException;
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
|
||||
|
||||
/**
|
||||
* Exception when a reference can't be resolved.
|
||||
*
|
||||
* @author Mike
|
||||
*/
|
||||
public class ReferenceResolverException extends XWorkException {
|
||||
|
||||
public ReferenceResolverException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ReferenceResolverException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public ReferenceResolverException(String s, Throwable cause) {
|
||||
super(s, cause);
|
||||
}
|
||||
|
||||
public ReferenceResolverException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* RuntimeConfiguration
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* Created Feb 25, 2003 10:56:02 PM
|
||||
*/
|
||||
public interface RuntimeConfiguration extends Serializable {
|
||||
|
||||
/**
|
||||
* get the fully expanded ActionConfig for a specified namespace and (action) name
|
||||
*
|
||||
* @param namespace the namespace of the Action. if this is null, then the empty namespace, "", will be used
|
||||
* @param name the name of the Action. may not be null.
|
||||
* @return the requested ActionConfig or null if there was no ActionConfig associated with the specified namespace
|
||||
* and name
|
||||
*/
|
||||
ActionConfig getActionConfig(String namespace, String name);
|
||||
|
||||
/**
|
||||
* returns a Map of all the registered ActionConfigs. Again, these ActionConfigs are fully expanded so that any
|
||||
* inherited interceptors, results, etc. will be included
|
||||
*
|
||||
* @return a Map of Map keyed by namespace and name respectively such that
|
||||
* <pre>
|
||||
* ActionConfig config = (ActionConfig)((Map)getActionConfigs.get(namespace)).get(name);
|
||||
* </pre>
|
||||
* should return a valid config for valid namespace/name pairs
|
||||
*/
|
||||
Map<String, Map<String, ActionConfig>> getActionConfigs();
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Contains everything needed to configure and execute an action:
|
||||
* <ul>
|
||||
* <li>methodName - the method name to execute on the action. If this is null, the Action will be cast to the Action
|
||||
* Interface and the execute() method called</li>
|
||||
* <li>clazz - the class name for the action</li>
|
||||
* <li>params - the params to be set for this action just before execution</li>
|
||||
* <li>results - the result map {String -> View class}</li>
|
||||
* <li>resultParameters - params for results {String -> Map}</li>
|
||||
* <li>typeConverter - the Ognl TypeConverter to use when getting/setting properties</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Mike
|
||||
* @author Rainer Hermanns
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class ActionConfig extends Located implements Serializable {
|
||||
|
||||
public static final String WILDCARD = "*";
|
||||
|
||||
protected List<InterceptorMapping> interceptors; // a list of interceptorMapping Objects eg. List<InterceptorMapping>
|
||||
protected Map<String,String> params;
|
||||
protected Map<String, ResultConfig> results;
|
||||
protected List<ExceptionMappingConfig> exceptionMappings;
|
||||
protected String className;
|
||||
protected String methodName;
|
||||
protected String packageName;
|
||||
protected String name;
|
||||
protected Set<String> allowedMethods;
|
||||
|
||||
protected ActionConfig(String packageName, String name, String className) {
|
||||
this.packageName = packageName;
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
params = new LinkedHashMap<String, String>();
|
||||
results = new LinkedHashMap<String, ResultConfig>();
|
||||
interceptors = new ArrayList<InterceptorMapping>();
|
||||
exceptionMappings = new ArrayList<ExceptionMappingConfig>();
|
||||
allowedMethods = new HashSet<String>();
|
||||
allowedMethods.add(WILDCARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones an ActionConfig, copying data into new maps and lists
|
||||
* @param orig The ActionConfig to clone
|
||||
* @Since 2.1
|
||||
*/
|
||||
protected ActionConfig(ActionConfig orig) {
|
||||
this.name = orig.name;
|
||||
this.className = orig.className;
|
||||
this.methodName = orig.methodName;
|
||||
this.packageName = orig.packageName;
|
||||
this.params = new LinkedHashMap<String,String>(orig.params);
|
||||
this.interceptors = new ArrayList<InterceptorMapping>(orig.interceptors);
|
||||
this.results = new LinkedHashMap<String,ResultConfig>(orig.results);
|
||||
this.exceptionMappings = new ArrayList<ExceptionMappingConfig>(orig.exceptionMappings);
|
||||
this.allowedMethods = new HashSet<String>(orig.allowedMethods);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public List<ExceptionMappingConfig> getExceptionMappings() {
|
||||
return exceptionMappings;
|
||||
}
|
||||
|
||||
public List<InterceptorMapping> getInterceptors() {
|
||||
return interceptors;
|
||||
}
|
||||
|
||||
public Set<String> getAllowedMethods() {
|
||||
return allowedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the action method
|
||||
*
|
||||
* @return name of the method to execute
|
||||
*/
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the packageName.
|
||||
*/
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public Map<String, String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public Map<String, ResultConfig> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public boolean isAllowedMethod(String method) {
|
||||
if (allowedMethods.size() == 1 && WILDCARD.equals(allowedMethods.iterator().next())) {
|
||||
return true;
|
||||
} else {
|
||||
return allowedMethods.contains(method);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof ActionConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ActionConfig actionConfig = (ActionConfig) o;
|
||||
|
||||
if ((className != null) ? (!className.equals(actionConfig.className)) : (actionConfig.className != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((name != null) ? (!name.equals(actionConfig.name)) : (actionConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((interceptors != null) ? (!interceptors.equals(actionConfig.interceptors)) : (actionConfig.interceptors != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((methodName != null) ? (!methodName.equals(actionConfig.methodName)) : (actionConfig.methodName != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((params != null) ? (!params.equals(actionConfig.params)) : (actionConfig.params != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((results != null) ? (!results.equals(actionConfig.results)) : (actionConfig.results != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((allowedMethods != null) ? (!allowedMethods.equals(actionConfig.allowedMethods)) : (actionConfig.allowedMethods != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override public int hashCode() {
|
||||
int result;
|
||||
result = (interceptors != null ? interceptors.hashCode() : 0);
|
||||
result = 31 * result + (params != null ? params.hashCode() : 0);
|
||||
result = 31 * result + (results != null ? results.hashCode() : 0);
|
||||
result = 31 * result + (exceptionMappings != null ? exceptionMappings.hashCode() : 0);
|
||||
result = 31 * result + (className != null ? className.hashCode() : 0);
|
||||
result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
|
||||
result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
|
||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||
result = 31 * result + (allowedMethods != null ? allowedMethods.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{ActionConfig ");
|
||||
sb.append(name).append(" (");
|
||||
sb.append(className);
|
||||
if (methodName != null) {
|
||||
sb.append(".").append(methodName).append("()");
|
||||
}
|
||||
sb.append(")");
|
||||
sb.append(" - ").append(location);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static class Builder implements InterceptorListHolder{
|
||||
|
||||
private ActionConfig target;
|
||||
|
||||
public Builder(ActionConfig toClone) {
|
||||
target = new ActionConfig(toClone);
|
||||
}
|
||||
|
||||
public Builder(String packageName, String name, String className) {
|
||||
target = new ActionConfig(packageName, name, className);
|
||||
}
|
||||
|
||||
public Builder packageName(String name) {
|
||||
target.packageName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder className(String name) {
|
||||
target.className = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultClassName(String name) {
|
||||
if (StringUtils.isEmpty(target.className)) {
|
||||
target.className = name;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder methodName(String method) {
|
||||
target.methodName = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addExceptionMapping(ExceptionMappingConfig exceptionMapping) {
|
||||
target.exceptionMappings.add(exceptionMapping);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addExceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) {
|
||||
target.exceptionMappings.addAll(mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder exceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) {
|
||||
target.exceptionMappings.clear();
|
||||
target.exceptionMappings.addAll(mappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addInterceptor(InterceptorMapping interceptor) {
|
||||
target.interceptors.add(interceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addInterceptors(List<InterceptorMapping> interceptors) {
|
||||
target.interceptors.addAll(interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder interceptors(List<InterceptorMapping> interceptors) {
|
||||
target.interceptors.clear();
|
||||
target.interceptors.addAll(interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParam(String name, String value) {
|
||||
target.params.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParams(Map<String,String> params) {
|
||||
target.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addResultConfig(ResultConfig resultConfig) {
|
||||
target.results.put(resultConfig.getName(), resultConfig);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addResultConfigs(Collection<ResultConfig> configs) {
|
||||
for (ResultConfig rc : configs) {
|
||||
target.results.put(rc.getName(), rc);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addResultConfigs(Map<String,ResultConfig> configs) {
|
||||
target.results.putAll(configs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addAllowedMethod(String methodName) {
|
||||
target.allowedMethods.add(methodName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addAllowedMethod(Collection<String> methods) {
|
||||
target.allowedMethods.addAll(methods);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActionConfig build() {
|
||||
target.params = Collections.unmodifiableMap(target.params);
|
||||
target.results = Collections.unmodifiableMap(target.results);
|
||||
target.interceptors = Collections.unmodifiableList(target.interceptors);
|
||||
target.exceptionMappings = Collections.unmodifiableList(target.exceptionMappings);
|
||||
target.allowedMethods = Collections.unmodifiableSet(target.allowedMethods);
|
||||
ActionConfig result = target;
|
||||
target = new ActionConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Configuration for exception mapping.
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @author Matthew E. Porter (matthew dot porter at metissian dot com)
|
||||
*/
|
||||
public class ExceptionMappingConfig extends Located implements Serializable {
|
||||
|
||||
private String name;
|
||||
private String exceptionClassName;
|
||||
private String result;
|
||||
private Map<String,String> params;
|
||||
|
||||
|
||||
protected ExceptionMappingConfig(String name, String exceptionClassName, String result) {
|
||||
this.name = name;
|
||||
this.exceptionClassName = exceptionClassName;
|
||||
this.result = result;
|
||||
this.params = new LinkedHashMap<String,String>();
|
||||
}
|
||||
|
||||
protected ExceptionMappingConfig(ExceptionMappingConfig target) {
|
||||
this.name = target.name;
|
||||
this.exceptionClassName = target.exceptionClassName;
|
||||
this.result = target.result;
|
||||
this.params = new LinkedHashMap<String,String>(target.params);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getExceptionClassName() {
|
||||
return exceptionClassName;
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String,String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof ExceptionMappingConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) o;
|
||||
|
||||
if ((name != null) ? (!name.equals(exceptionMappingConfig.name)) : (exceptionMappingConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((exceptionClassName != null) ? (!exceptionClassName.equals(exceptionMappingConfig.exceptionClassName)) : (exceptionMappingConfig.exceptionClassName != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((result != null) ? (!result.equals(exceptionMappingConfig.result)) : (exceptionMappingConfig.result != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((params != null) ? (!params.equals(exceptionMappingConfig.params)) : (exceptionMappingConfig.params != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode;
|
||||
hashCode = ((name != null) ? name.hashCode() : 0);
|
||||
hashCode = (29 * hashCode) + ((exceptionClassName != null) ? exceptionClassName.hashCode() : 0);
|
||||
hashCode = (29 * hashCode) + ((result != null) ? result.hashCode() : 0);
|
||||
hashCode = (29 * hashCode) + ((params != null) ? params.hashCode() : 0);
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static class Builder{
|
||||
|
||||
private ExceptionMappingConfig target;
|
||||
|
||||
public Builder(ExceptionMappingConfig toClone) {
|
||||
target = new ExceptionMappingConfig(toClone);
|
||||
}
|
||||
|
||||
public Builder(String name, String exceptionClassName, String result) {
|
||||
target = new ExceptionMappingConfig(name, exceptionClassName, result);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder exceptionClassName(String name) {
|
||||
target.exceptionClassName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder result(String result) {
|
||||
target.result = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParam(String name, String value) {
|
||||
target.params.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParams(Map<String,String> params) {
|
||||
target.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExceptionMappingConfig build() {
|
||||
target.params = Collections.unmodifiableMap(target.params);
|
||||
ExceptionMappingConfig result = target;
|
||||
target = new ExceptionMappingConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Configuration for Interceptors.
|
||||
* <p/>
|
||||
* In the xml configuration file this is defined as the <code>interceptors</code> tag.
|
||||
*
|
||||
* @author Mike
|
||||
*/
|
||||
public class InterceptorConfig extends Located implements Serializable {
|
||||
|
||||
Map<String,String> params;
|
||||
String className;
|
||||
String name;
|
||||
|
||||
|
||||
protected InterceptorConfig(String name, String className) {
|
||||
this.params = new LinkedHashMap<String,String>();
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
protected InterceptorConfig(InterceptorConfig orig) {
|
||||
this.name = orig.name;
|
||||
this.className = orig.className;
|
||||
this.params = new LinkedHashMap<String,String>(orig.params);
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String,String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof InterceptorConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final InterceptorConfig interceptorConfig = (InterceptorConfig) o;
|
||||
|
||||
if ((className != null) ? (!className.equals(interceptorConfig.className)) : (interceptorConfig.className != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((name != null) ? (!name.equals(interceptorConfig.name)) : (interceptorConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((params != null) ? (!params.equals(interceptorConfig.params)) : (interceptorConfig.params != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ((name != null) ? name.hashCode() : 0);
|
||||
result = (29 * result) + ((className != null) ? className.hashCode() : 0);
|
||||
result = (29 * result) + ((params != null) ? params.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private InterceptorConfig target;
|
||||
|
||||
public Builder(String name, String className) {
|
||||
target = new InterceptorConfig(name, className);
|
||||
}
|
||||
|
||||
public Builder(InterceptorConfig orig) {
|
||||
target = new InterceptorConfig(orig);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder className(String name) {
|
||||
target.className = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParam(String name, String value) {
|
||||
target.params.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParams(Map<String,String> params) {
|
||||
target.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InterceptorConfig build() {
|
||||
target.params = Collections.unmodifiableMap(target.params);
|
||||
InterceptorConfig result = target;
|
||||
target = new InterceptorConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* InterceptorListHolder
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* Created Jun 1, 2003 1:02:48 AM
|
||||
*/
|
||||
public interface InterceptorListHolder {
|
||||
|
||||
InterceptorListHolder addInterceptor(InterceptorMapping interceptor);
|
||||
|
||||
InterceptorListHolder addInterceptors(List<InterceptorMapping> interceptors);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
/**
|
||||
* Defines an object that can be used to retrieve interceptor configuration
|
||||
*/
|
||||
public interface InterceptorLocator {
|
||||
|
||||
/**
|
||||
* Gets an interceptor configuration object.
|
||||
* @param name The interceptor or interceptor stack name
|
||||
* @return Either an {@link InterceptorConfig} or {@link InterceptorStackConfig} object
|
||||
*/
|
||||
Object getInterceptorConfig(String name);
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <code>InterceptorMapping</code>
|
||||
*
|
||||
* @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InterceptorMapping implements Serializable {
|
||||
|
||||
private String name;
|
||||
private Interceptor interceptor;
|
||||
|
||||
public InterceptorMapping(String name, Interceptor interceptor) {
|
||||
this.name = name;
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Interceptor getInterceptor() {
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final InterceptorMapping that = (InterceptorMapping) o;
|
||||
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (name != null ? name.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration for InterceptorStack.
|
||||
* <p/>
|
||||
* In the xml configuration file this is defined as the <code>interceptor-stack</code> tag.
|
||||
*
|
||||
* @author Mike
|
||||
* @author Rainer Hermanns
|
||||
*/
|
||||
public class InterceptorStackConfig extends Located implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2897260918170270343L;
|
||||
|
||||
/**
|
||||
* A list of InterceptorMapping object
|
||||
*/
|
||||
private List<InterceptorMapping> interceptors;
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an InterceptorStackConfig object.
|
||||
*/
|
||||
protected InterceptorStackConfig() {
|
||||
this.interceptors = new ArrayList<InterceptorMapping>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an InterceptorStackConfig object with a particular <code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
protected InterceptorStackConfig(InterceptorStackConfig orig) {
|
||||
this.name = orig.name;
|
||||
this.interceptors = new ArrayList<InterceptorMapping>(orig.interceptors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a <code>Collection</code> of InterceptorMapping objects.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Collection<InterceptorMapping> getInterceptors() {
|
||||
return interceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this interceptor stack configuration.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InterceptorStackConfig object is equals with <code>o</code> only if
|
||||
* <ul>
|
||||
* <li>o is an InterceptorStackConfig object</li>
|
||||
* <li>both names are equals</li>
|
||||
* <li>all of their <code>InterceptorMapping</code>s are equals</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof InterceptorStackConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final InterceptorStackConfig interceptorStackConfig = (InterceptorStackConfig) o;
|
||||
|
||||
if ((interceptors != null) ? (!interceptors.equals(interceptorStackConfig.interceptors)) : (interceptorStackConfig.interceptors != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((name != null) ? (!name.equals(interceptorStackConfig.name)) : (interceptorStackConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate hashcode based on <code>InterceptorStackConfig</code>'s name and its
|
||||
* <code>InterceptorMapping</code>s.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ((name != null) ? name.hashCode() : 0);
|
||||
result = (29 * result) + ((interceptors != null) ? interceptors.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static class Builder implements InterceptorListHolder {
|
||||
private InterceptorStackConfig target;
|
||||
|
||||
public Builder(String name) {
|
||||
target = new InterceptorStackConfig();
|
||||
target.name = name;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an <code>InterceptorMapping</code> object.
|
||||
*/
|
||||
public Builder addInterceptor(InterceptorMapping interceptor) {
|
||||
target.interceptors.add(interceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a List of <code>InterceptorMapping</code> objects.
|
||||
*/
|
||||
public Builder addInterceptors(List<InterceptorMapping> interceptors) {
|
||||
target.interceptors.addAll(interceptors);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InterceptorStackConfig build() {
|
||||
target.interceptors = Collections.unmodifiableList(target.interceptors);
|
||||
InterceptorStackConfig result = target;
|
||||
target = new InterceptorStackConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration for Package.
|
||||
* <p/>
|
||||
* In the xml configuration file this is defined as the <code>package</code> tag.
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class PackageConfig extends Located implements Comparable, Serializable, InterceptorLocator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PackageConfig.class);
|
||||
|
||||
private Map<String, ActionConfig> actionConfigs;
|
||||
private Map<String, ResultConfig> globalResultConfigs;
|
||||
private Map<String, Object> interceptorConfigs;
|
||||
private Map<String, ResultTypeConfig> resultTypeConfigs;
|
||||
private List<ExceptionMappingConfig> globalExceptionMappingConfigs;
|
||||
private List<PackageConfig> parents;
|
||||
private String defaultInterceptorRef;
|
||||
private String defaultActionRef;
|
||||
private String defaultResultType;
|
||||
private String defaultClassRef;
|
||||
private String name;
|
||||
private String namespace = "";
|
||||
private boolean isAbstract = false;
|
||||
private boolean needsRefresh;
|
||||
|
||||
|
||||
protected PackageConfig(String name) {
|
||||
this.name = name;
|
||||
actionConfigs = new LinkedHashMap<String, ActionConfig>();
|
||||
globalResultConfigs = new LinkedHashMap<String, ResultConfig>();
|
||||
interceptorConfigs = new LinkedHashMap<String, Object>();
|
||||
resultTypeConfigs = new LinkedHashMap<String, ResultTypeConfig>();
|
||||
globalExceptionMappingConfigs = new ArrayList<ExceptionMappingConfig>();
|
||||
parents = new ArrayList<PackageConfig>();
|
||||
}
|
||||
|
||||
|
||||
protected PackageConfig(PackageConfig orig) {
|
||||
this.defaultInterceptorRef = orig.defaultInterceptorRef;
|
||||
this.defaultActionRef = orig.defaultActionRef;
|
||||
this.defaultResultType = orig.defaultResultType;
|
||||
this.defaultClassRef = orig.defaultClassRef;
|
||||
this.name = orig.name;
|
||||
this.namespace = orig.namespace;
|
||||
this.isAbstract = orig.isAbstract;
|
||||
this.needsRefresh = orig.needsRefresh;
|
||||
this.actionConfigs = new LinkedHashMap<String, ActionConfig>(orig.actionConfigs);
|
||||
this.globalResultConfigs = new LinkedHashMap<String, ResultConfig>(orig.globalResultConfigs);
|
||||
this.interceptorConfigs = new LinkedHashMap<String, Object>(orig.interceptorConfigs);
|
||||
this.resultTypeConfigs = new LinkedHashMap<String, ResultTypeConfig>(orig.resultTypeConfigs);
|
||||
this.globalExceptionMappingConfigs = new ArrayList<ExceptionMappingConfig>(orig.globalExceptionMappingConfigs);
|
||||
this.parents = new ArrayList<PackageConfig>(orig.parents);
|
||||
}
|
||||
|
||||
public boolean isAbstract() {
|
||||
return isAbstract;
|
||||
}
|
||||
|
||||
public Map<String, ActionConfig> getActionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the Map of all the ActionConfigs available in the current package.
|
||||
* ActionConfigs defined in ancestor packages will be included in this Map.
|
||||
*
|
||||
* @return a Map of ActionConfig Objects with the action name as the key
|
||||
* @see ActionConfig
|
||||
*/
|
||||
public Map<String, ActionConfig> getAllActionConfigs() {
|
||||
Map<String, ActionConfig> retMap = new LinkedHashMap<String, ActionConfig>();
|
||||
|
||||
if (!parents.isEmpty()) {
|
||||
for (PackageConfig parent : parents) {
|
||||
retMap.putAll(parent.getAllActionConfigs());
|
||||
}
|
||||
}
|
||||
|
||||
retMap.putAll(getActionConfigs());
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the Map of all the global ResultConfigs available in the current package.
|
||||
* Global ResultConfigs defined in ancestor packages will be included in this Map.
|
||||
*
|
||||
* @return a Map of Result Objects with the result name as the key
|
||||
* @see ResultConfig
|
||||
*/
|
||||
public Map<String, ResultConfig> getAllGlobalResults() {
|
||||
Map<String, ResultConfig> retMap = new LinkedHashMap<String, ResultConfig>();
|
||||
|
||||
if (!parents.isEmpty()) {
|
||||
for (PackageConfig parentConfig : parents) {
|
||||
retMap.putAll(parentConfig.getAllGlobalResults());
|
||||
}
|
||||
}
|
||||
|
||||
retMap.putAll(getGlobalResultConfigs());
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the Map of all InterceptorConfigs and InterceptorStackConfigs available in the current package.
|
||||
* InterceptorConfigs defined in ancestor packages will be included in this Map.
|
||||
*
|
||||
* @return a Map of InterceptorConfig and InterceptorStackConfig Objects with the ref-name as the key
|
||||
* @see InterceptorConfig
|
||||
* @see InterceptorStackConfig
|
||||
*/
|
||||
public Map<String, Object> getAllInterceptorConfigs() {
|
||||
Map<String, Object> retMap = new LinkedHashMap<String, Object>();
|
||||
|
||||
if (!parents.isEmpty()) {
|
||||
for (PackageConfig parentContext : parents) {
|
||||
retMap.putAll(parentContext.getAllInterceptorConfigs());
|
||||
}
|
||||
}
|
||||
|
||||
retMap.putAll(getInterceptorConfigs());
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the Map of all the ResultTypeConfigs available in the current package.
|
||||
* ResultTypeConfigs defined in ancestor packages will be included in this Map.
|
||||
*
|
||||
* @return a Map of ResultTypeConfig Objects with the result type name as the key
|
||||
* @see ResultTypeConfig
|
||||
*/
|
||||
public Map<String, ResultTypeConfig> getAllResultTypeConfigs() {
|
||||
Map<String, ResultTypeConfig> retMap = new LinkedHashMap<String, ResultTypeConfig>();
|
||||
|
||||
if (!parents.isEmpty()) {
|
||||
for (PackageConfig parentContext : parents) {
|
||||
retMap.putAll(parentContext.getAllResultTypeConfigs());
|
||||
}
|
||||
}
|
||||
|
||||
retMap.putAll(getResultTypeConfigs());
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the List of all the ExceptionMappingConfigs available in the current package.
|
||||
* ExceptionMappingConfigs defined in ancestor packages will be included in this list.
|
||||
*
|
||||
* @return a List of ExceptionMappingConfigs Objects with the result type name as the key
|
||||
* @see ExceptionMappingConfig
|
||||
*/
|
||||
public List<ExceptionMappingConfig> getAllExceptionMappingConfigs() {
|
||||
List<ExceptionMappingConfig> allExceptionMappings = new ArrayList<ExceptionMappingConfig>();
|
||||
|
||||
if (!parents.isEmpty()) {
|
||||
for (PackageConfig parentContext : parents) {
|
||||
allExceptionMappings.addAll(parentContext.getAllExceptionMappingConfigs());
|
||||
}
|
||||
}
|
||||
|
||||
allExceptionMappings.addAll(getGlobalExceptionMappingConfigs());
|
||||
|
||||
return allExceptionMappings;
|
||||
}
|
||||
|
||||
|
||||
public String getDefaultInterceptorRef() {
|
||||
return defaultInterceptorRef;
|
||||
}
|
||||
|
||||
public String getDefaultActionRef() {
|
||||
return defaultActionRef;
|
||||
}
|
||||
|
||||
public String getDefaultClassRef() {
|
||||
if((defaultClassRef == null) && !parents.isEmpty()) {
|
||||
for (PackageConfig parent : parents) {
|
||||
String parentDefault = parent.getDefaultClassRef();
|
||||
if (parentDefault != null) {
|
||||
return parentDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultClassRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default result type for this package.
|
||||
*/
|
||||
public String getDefaultResultType() {
|
||||
return defaultResultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the default interceptor-ref name. If this is not set on this PackageConfig, it searches the parent
|
||||
* PackageConfigs in order until it finds one.
|
||||
*/
|
||||
public String getFullDefaultInterceptorRef() {
|
||||
if ((defaultInterceptorRef == null) && !parents.isEmpty()) {
|
||||
for (PackageConfig parent : parents) {
|
||||
String parentDefault = parent.getFullDefaultInterceptorRef();
|
||||
|
||||
if (parentDefault != null) {
|
||||
return parentDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultInterceptorRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the default action-ref name. If this is not set on this PackageConfig, it searches the parent
|
||||
* PackageConfigs in order until it finds one.
|
||||
*/
|
||||
public String getFullDefaultActionRef() {
|
||||
if ((defaultActionRef == null) && !parents.isEmpty()) {
|
||||
for (PackageConfig parent : parents) {
|
||||
String parentDefault = parent.getFullDefaultActionRef();
|
||||
|
||||
if (parentDefault != null) {
|
||||
return parentDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultActionRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default result type for this package.
|
||||
* <p/>
|
||||
* If there is no default result type, but this package has parents - we will try to
|
||||
* look up the default result type of a parent.
|
||||
*/
|
||||
public String getFullDefaultResultType() {
|
||||
if ((defaultResultType == null) && !parents.isEmpty()) {
|
||||
for (PackageConfig parent : parents) {
|
||||
String parentDefault = parent.getFullDefaultResultType();
|
||||
|
||||
if (parentDefault != null) {
|
||||
return parentDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultResultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the global ResultConfigs local to this package
|
||||
*
|
||||
* @return a Map of ResultConfig objects keyed by result name
|
||||
* @see ResultConfig
|
||||
*/
|
||||
public Map<String, ResultConfig> getGlobalResultConfigs() {
|
||||
return globalResultConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the InterceptorConfigs and InterceptorStackConfigs local to this package
|
||||
*
|
||||
* @return a Map of InterceptorConfig and InterceptorStackConfig objects keyed by ref-name
|
||||
* @see InterceptorConfig
|
||||
* @see InterceptorStackConfig
|
||||
*/
|
||||
public Map<String, Object> getInterceptorConfigs() {
|
||||
return interceptorConfigs;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public List<PackageConfig> getParents() {
|
||||
return new ArrayList<PackageConfig>(parents);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the ResultTypeConfigs local to this package
|
||||
*
|
||||
* @return a Map of ResultTypeConfig objects keyed by result name
|
||||
* @see ResultTypeConfig
|
||||
*/
|
||||
public Map<String, ResultTypeConfig> getResultTypeConfigs() {
|
||||
return resultTypeConfigs;
|
||||
}
|
||||
|
||||
|
||||
public boolean isNeedsRefresh() {
|
||||
return needsRefresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the ExceptionMappingConfigs local to this package
|
||||
*
|
||||
* @return a Map of ExceptionMappingConfig objects keyed by result name
|
||||
* @see ExceptionMappingConfig
|
||||
*/
|
||||
public List<ExceptionMappingConfig> getGlobalExceptionMappingConfigs() {
|
||||
return globalExceptionMappingConfigs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof PackageConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PackageConfig packageConfig = (PackageConfig) o;
|
||||
|
||||
if (isAbstract != packageConfig.isAbstract) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((actionConfigs != null) ? (!actionConfigs.equals(packageConfig.actionConfigs)) : (packageConfig.actionConfigs != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((defaultResultType != null) ? (!defaultResultType.equals(packageConfig.defaultResultType)) : (packageConfig.defaultResultType != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((defaultClassRef != null) ? (!defaultClassRef.equals(packageConfig.defaultClassRef)) : (packageConfig.defaultClassRef != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((globalResultConfigs != null) ? (!globalResultConfigs.equals(packageConfig.globalResultConfigs)) : (packageConfig.globalResultConfigs != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((interceptorConfigs != null) ? (!interceptorConfigs.equals(packageConfig.interceptorConfigs)) : (packageConfig.interceptorConfigs != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((name != null) ? (!name.equals(packageConfig.name)) : (packageConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((namespace != null) ? (!namespace.equals(packageConfig.namespace)) : (packageConfig.namespace != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((parents != null) ? (!parents.equals(packageConfig.parents)) : (packageConfig.parents != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((resultTypeConfigs != null) ? (!resultTypeConfigs.equals(packageConfig.resultTypeConfigs)) : (packageConfig.resultTypeConfigs != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((globalExceptionMappingConfigs != null) ? (!globalExceptionMappingConfigs.equals(packageConfig.globalExceptionMappingConfigs)) : (packageConfig.globalExceptionMappingConfigs != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ((name != null) ? name.hashCode() : 0);
|
||||
result = (29 * result) + ((parents != null) ? parents.hashCode() : 0);
|
||||
result = (29 * result) + ((actionConfigs != null) ? actionConfigs.hashCode() : 0);
|
||||
result = (29 * result) + ((globalResultConfigs != null) ? globalResultConfigs.hashCode() : 0);
|
||||
result = (29 * result) + ((interceptorConfigs != null) ? interceptorConfigs.hashCode() : 0);
|
||||
result = (29 * result) + ((resultTypeConfigs != null) ? resultTypeConfigs.hashCode() : 0);
|
||||
result = (29 * result) + ((globalExceptionMappingConfigs != null) ? globalExceptionMappingConfigs.hashCode() : 0);
|
||||
result = (29 * result) + ((defaultResultType != null) ? defaultResultType.hashCode() : 0);
|
||||
result = (29 * result) + ((defaultClassRef != null) ? defaultClassRef.hashCode() : 0);
|
||||
result = (29 * result) + ((namespace != null) ? namespace.hashCode() : 0);
|
||||
result = (29 * result) + (isAbstract ? 1 : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{PackageConfig Name:" + name + " namespace:" + namespace + " parents:" + parents + "}";
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
PackageConfig other = (PackageConfig) o;
|
||||
String full = namespace + "!" + name;
|
||||
String otherFull = other.namespace + "!" + other.name;
|
||||
|
||||
// note, this isn't perfect (could come from different parents), but it is "good enough"
|
||||
return full.compareTo(otherFull);
|
||||
}
|
||||
|
||||
public Object getInterceptorConfig(String name) {
|
||||
return getAllInterceptorConfigs().get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static class Builder implements InterceptorLocator {
|
||||
|
||||
private PackageConfig target;
|
||||
|
||||
public Builder(String name) {
|
||||
target = new PackageConfig(name);
|
||||
}
|
||||
|
||||
public Builder(PackageConfig config) {
|
||||
target = new PackageConfig(config);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder isAbstract(boolean isAbstract) {
|
||||
target.isAbstract = isAbstract;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultInterceptorRef(String name) {
|
||||
target.defaultInterceptorRef = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultActionRef(String name) {
|
||||
target.defaultActionRef = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultClassRef( String defaultClassRef ) {
|
||||
target.defaultClassRef = defaultClassRef;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the default Result type for this package
|
||||
*
|
||||
* @param defaultResultType
|
||||
*/
|
||||
public Builder defaultResultType(String defaultResultType) {
|
||||
target.defaultResultType = defaultResultType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder namespace(String namespace) {
|
||||
if (namespace == null) {
|
||||
target.namespace = "";
|
||||
} else {
|
||||
target.namespace = namespace;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder needsRefresh(boolean needsRefresh) {
|
||||
target.needsRefresh = needsRefresh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addActionConfig(String name, ActionConfig action) {
|
||||
target.actionConfigs.put(name, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParents(List<PackageConfig> parents) {
|
||||
for (PackageConfig config : parents) {
|
||||
addParent(config);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addGlobalResultConfig(ResultConfig resultConfig) {
|
||||
target.globalResultConfigs.put(resultConfig.getName(), resultConfig);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addGlobalResultConfigs(Map<String, ResultConfig> resultConfigs) {
|
||||
target.globalResultConfigs.putAll(resultConfigs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addExceptionMappingConfig(ExceptionMappingConfig exceptionMappingConfig) {
|
||||
target.globalExceptionMappingConfigs.add(exceptionMappingConfig);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addGlobalExceptionMappingConfigs(List<ExceptionMappingConfig> exceptionMappingConfigs) {
|
||||
target.globalExceptionMappingConfigs.addAll(exceptionMappingConfigs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addInterceptorConfig(InterceptorConfig config) {
|
||||
target.interceptorConfigs.put(config.getName(), config);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addInterceptorStackConfig(InterceptorStackConfig config) {
|
||||
target.interceptorConfigs.put(config.getName(), config);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParent(PackageConfig parent) {
|
||||
if (this.equals(parent)) {
|
||||
LOG.error("A package cannot extend itself: " + target.name);
|
||||
}
|
||||
|
||||
target.parents.add(0, parent);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addResultTypeConfig(ResultTypeConfig config) {
|
||||
target.resultTypeConfigs.put(config.getName(), config);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNeedsRefresh() {
|
||||
return target.needsRefresh;
|
||||
}
|
||||
|
||||
public String getDefaultClassRef() {
|
||||
return target.defaultClassRef;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return target.name;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return target.namespace;
|
||||
}
|
||||
|
||||
public String getFullDefaultResultType() {
|
||||
return target.getFullDefaultResultType();
|
||||
}
|
||||
|
||||
public ResultTypeConfig getResultType(String type) {
|
||||
return target.getAllResultTypeConfigs().get(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Object getInterceptorConfig(String name) {
|
||||
return target.getAllInterceptorConfigs().get(name);
|
||||
}
|
||||
|
||||
public PackageConfig build() {
|
||||
target.actionConfigs = Collections.unmodifiableMap(target.actionConfigs);
|
||||
target.globalResultConfigs = Collections.unmodifiableMap(target.globalResultConfigs);
|
||||
target.interceptorConfigs = Collections.unmodifiableMap(target.interceptorConfigs);
|
||||
target.resultTypeConfigs = Collections.unmodifiableMap(target.resultTypeConfigs);
|
||||
target.globalExceptionMappingConfigs = Collections.unmodifiableList(target.globalExceptionMappingConfigs);
|
||||
target.parents = Collections.unmodifiableList(target.parents);
|
||||
|
||||
PackageConfig result = target;
|
||||
target = new PackageConfig(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[BUILDER] "+target.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* Actions implementing Parameterizable will receive a map of the static parameters defined in the action
|
||||
* configuration.
|
||||
* <p/>
|
||||
* <p/> The {@link com.opensymphony.xwork2.interceptor.StaticParametersInterceptor} must be in the action's interceptor
|
||||
* queue for this to work.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
* @author Jason Carreira
|
||||
*/
|
||||
public interface Parameterizable {
|
||||
|
||||
public void addParam(String name, String value);
|
||||
|
||||
void setParams(Map<String, String> params);
|
||||
|
||||
Map<String, String> getParams();
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration for Result.
|
||||
* <p/>
|
||||
* In the xml configuration file this is defined as the <code>result</code> tag.
|
||||
*
|
||||
* @author Mike
|
||||
*/
|
||||
public class ResultConfig extends Located implements Serializable {
|
||||
|
||||
private Map<String,String> params;
|
||||
private String className;
|
||||
private String name;
|
||||
|
||||
|
||||
protected ResultConfig(String name, String className) {
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
params = new LinkedHashMap<String, String>();
|
||||
}
|
||||
|
||||
protected ResultConfig(ResultConfig orig) {
|
||||
this.params = orig.params;
|
||||
this.name = orig.name;
|
||||
this.className = orig.className;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String,String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof ResultConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ResultConfig resultConfig = (ResultConfig) o;
|
||||
|
||||
if ((className != null) ? (!className.equals(resultConfig.className)) : (resultConfig.className != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((name != null) ? (!name.equals(resultConfig.name)) : (resultConfig.name != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((params != null) ? (!params.equals(resultConfig.params)) : (resultConfig.params != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ((name != null) ? name.hashCode() : 0);
|
||||
result = (29 * result) + ((className != null) ? className.hashCode() : 0);
|
||||
result = (29 * result) + ((params != null) ? params.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private ResultConfig target;
|
||||
|
||||
public Builder(String name, String className) {
|
||||
target = new ResultConfig(name, className);
|
||||
}
|
||||
|
||||
public Builder(ResultConfig orig) {
|
||||
target = new ResultConfig(orig);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder className(String name) {
|
||||
target.className = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParam(String name, String value) {
|
||||
target.params.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParams(Map<String,String> params) {
|
||||
target.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultConfig build() {
|
||||
target.params = Collections.unmodifiableMap(target.params);
|
||||
ResultConfig result = target;
|
||||
target = new ResultConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration class for result types.
|
||||
* <p/>
|
||||
* In the xml configuration file this is defined as the <code>result-type</code> tag.
|
||||
*
|
||||
* @author Mike
|
||||
* @author Rainer Hermanns
|
||||
* @author Neo
|
||||
*/
|
||||
public class ResultTypeConfig extends Located implements Serializable {
|
||||
|
||||
private String className;
|
||||
private String name;
|
||||
private String defaultResultParam;
|
||||
|
||||
private Map<String,String> params;
|
||||
|
||||
protected ResultTypeConfig(String name, String className) {
|
||||
this.name = name;
|
||||
this.className = className;
|
||||
params = new LinkedHashMap<String,String>();
|
||||
}
|
||||
|
||||
protected ResultTypeConfig(ResultTypeConfig orig) {
|
||||
this.name = orig.name;
|
||||
this.className = orig.className;
|
||||
this.defaultResultParam = orig.defaultResultParam;
|
||||
this.params = orig.params;
|
||||
}
|
||||
|
||||
|
||||
public void setDefaultResultParam(String defaultResultParam) {
|
||||
this.defaultResultParam = defaultResultParam;
|
||||
}
|
||||
|
||||
public String getDefaultResultParam() {
|
||||
return this.defaultResultParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Since 2.1, use {@link #getClassName()} instead
|
||||
*/
|
||||
@Deprecated public String getClazz() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String,String> getParams() {
|
||||
return this.params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final ResultTypeConfig that = (ResultTypeConfig) o;
|
||||
|
||||
if (className != null ? !className.equals(that.className) : that.className != null) return false;
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
if (params != null ? !params.equals(that.params) : that.params != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (className != null ? className.hashCode() : 0);
|
||||
result = 29 * result + (name != null ? name.hashCode() : 0);
|
||||
result = 29 * result + (params != null ? params.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for this object. An instance of this object is the only way to construct a new instance. The
|
||||
* purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining.
|
||||
* After setting any values you need, call the {@link #build()} method to create the object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private ResultTypeConfig target;
|
||||
|
||||
public Builder(String name, String className) {
|
||||
target = new ResultTypeConfig(name, className);
|
||||
}
|
||||
|
||||
public Builder(ResultTypeConfig orig) {
|
||||
target = new ResultTypeConfig(orig);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
target.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder className(String name) {
|
||||
target.className = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParam(String name, String value) {
|
||||
target.params.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParams(Map<String,String> params) {
|
||||
target.params.putAll(params);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder defaultResultParam(String defaultResultParam) {
|
||||
target.defaultResultParam = defaultResultParam;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder location(Location loc) {
|
||||
target.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultTypeConfig build() {
|
||||
target.params = Collections.unmodifiableMap(target.params);
|
||||
ResultTypeConfig result = target;
|
||||
target = new ResultTypeConfig(target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.entities;
|
||||
|
||||
public class UnknownHandlerConfig {
|
||||
private String name;
|
||||
|
||||
public UnknownHandlerConfig(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Configuration entity classes. All objects ending in "Config" are immutable and must be constructed using
|
||||
their inner "Builder" class. For example, a PackageConfig object can be created via:
|
||||
</p>
|
||||
<pre>
|
||||
PackageConfig config = new PackageConfig.Builder("myPackage").build();
|
||||
</pre>
|
||||
<p>
|
||||
The methods on the builder object are chainable to support constructions like this:
|
||||
</p>
|
||||
<pre>
|
||||
ResultConfig config = new ResultConfig.Builder("success", "myapp.MyResult")
|
||||
.addParam("location", "/foo.jsp")
|
||||
.build();
|
||||
</pre>
|
||||
</body>
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2003,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
||||
/**
|
||||
* <p> Matches patterns against pre-compiled wildcard expressions pulled from
|
||||
* target objects. It uses the wildcard matcher from the Apache Cocoon
|
||||
* project. Patterns will be matched in the order they were added. The first
|
||||
* match wins, so more specific patterns should be defined before less specific
|
||||
* patterns.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public abstract class AbstractMatcher<E> implements Serializable {
|
||||
/**
|
||||
* <p> The logging instance </p>
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(AbstractMatcher.class);
|
||||
|
||||
/**
|
||||
* <p> Handles all wildcard pattern matching. </p>
|
||||
*/
|
||||
PatternMatcher<Object> wildcard;
|
||||
|
||||
/**
|
||||
* <p> The compiled patterns and their associated target objects </p>
|
||||
*/
|
||||
List<Mapping<E>> compiledPatterns = new ArrayList<Mapping<E>>();;
|
||||
|
||||
public AbstractMatcher(PatternMatcher<?> helper) {
|
||||
this.wildcard = (PatternMatcher<Object>) helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds and precompiles the wildcard patterns. Patterns will be evaluated
|
||||
* in the order they were added. Only patterns that actually contain a
|
||||
* wildcard will be compiled.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Patterns can optionally be matched "loosely". When the end of the pattern
|
||||
* matches \*[^*]\*$ (wildcard, no wildcard, wildcard), if the pattern
|
||||
* fails, it is also matched as if the last two characters didn't exist. The
|
||||
* goal is to support the legacy "*!*" syntax, where the "!*" is optional.
|
||||
* </p>
|
||||
*
|
||||
* @param name The pattern
|
||||
* @param target The object to associate with the pattern
|
||||
* @param looseMatch
|
||||
* To loosely match wildcards or not
|
||||
*/
|
||||
public void addPattern(String name, E target, boolean looseMatch) {
|
||||
|
||||
Object pattern;
|
||||
|
||||
if (!wildcard.isLiteral(name)) {
|
||||
if (looseMatch && (name.length() > 0) && (name.charAt(0) == '/')) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Compiling pattern '" + name + "'");
|
||||
}
|
||||
|
||||
pattern = wildcard.compilePattern(name);
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
|
||||
if (looseMatch) {
|
||||
int lastStar = name.lastIndexOf('*');
|
||||
if (lastStar > 1 && lastStar == name.length() - 1) {
|
||||
if (name.charAt(lastStar - 1) != '*') {
|
||||
pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
|
||||
compiledPatterns.add(new Mapping<E>(name, pattern, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
compiledPatterns = Collections.unmodifiableList(new ArrayList<Mapping<E>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Matches the path against the compiled wildcard patterns. </p>
|
||||
*
|
||||
* @param potentialMatch The portion of the request URI for selecting a config.
|
||||
* @return The action config if matched, else null
|
||||
*/
|
||||
public E match(String potentialMatch) {
|
||||
E config = null;
|
||||
|
||||
if (compiledPatterns.size() > 0) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Attempting to match '" + potentialMatch
|
||||
+ "' to a wildcard pattern, "+ compiledPatterns.size()
|
||||
+ " available");
|
||||
}
|
||||
|
||||
Map<String,String> vars = new LinkedHashMap<String,String>();
|
||||
for (Mapping<E> m : compiledPatterns) {
|
||||
if (wildcard.match(vars, potentialMatch, m.getPattern())) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Value matches pattern '"
|
||||
+ m.getOriginalPattern() + "'");
|
||||
}
|
||||
|
||||
config =
|
||||
convert(potentialMatch, m.getTarget(), vars);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Clones the target object and its children, replacing various
|
||||
* properties with the values of the wildcard-matched strings. </p>
|
||||
*
|
||||
* @param path The requested path
|
||||
* @param orig The original object
|
||||
* @param vars A Map of wildcard-matched strings
|
||||
* @return A cloned object with appropriate properties replaced with
|
||||
* wildcard-matched values
|
||||
*/
|
||||
protected abstract E convert(String path, E orig, Map<String, String> vars);
|
||||
|
||||
/**
|
||||
* <p> Replaces parameter values
|
||||
* </p>
|
||||
*
|
||||
* @param orig The original parameters with placehold values
|
||||
* @param vars A Map of wildcard-matched strings
|
||||
*/
|
||||
protected Map<String,String> replaceParameters(Map<String, String> orig, Map<String,String> vars) {
|
||||
Map<String,String> map = new LinkedHashMap<String,String>();
|
||||
|
||||
//this will set the group index references, like {1}
|
||||
for (String key : orig.keySet()) {
|
||||
map.put(key, convertParam(orig.get(key), vars));
|
||||
}
|
||||
|
||||
//the values map will contain entries like name->"Lex Luthor" and 1->"Lex Luthor"
|
||||
//now add the non-numeric values
|
||||
for (String key: vars.keySet()) {
|
||||
if (!NumberUtils.isNumber(key)) {
|
||||
map.put(key, vars.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Inserts into a value wildcard-matched strings where specified
|
||||
* with the {x} syntax. If a wildcard-matched value isn't found, the
|
||||
* replacement token is turned into an empty string.
|
||||
* </p>
|
||||
*
|
||||
* @param val The value to convert
|
||||
* @param vars A Map of wildcard-matched strings
|
||||
* @return The new value
|
||||
*/
|
||||
protected String convertParam(String val, Map<String, String> vars) {
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = val.length();
|
||||
StringBuilder ret = new StringBuilder();
|
||||
char c;
|
||||
String varVal;
|
||||
for (int x=0; x<len; x++) {
|
||||
c = val.charAt(x);
|
||||
if (x < len - 2 &&
|
||||
c == '{' && '}' == val.charAt(x+2)) {
|
||||
varVal = (String)vars.get(String.valueOf(val.charAt(x + 1)));
|
||||
if (varVal != null) {
|
||||
ret.append(varVal);
|
||||
}
|
||||
x += 2;
|
||||
} else {
|
||||
ret.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Stores a compiled wildcard pattern and the object it came
|
||||
* from. </p>
|
||||
*/
|
||||
private static class Mapping<E> implements Serializable {
|
||||
/**
|
||||
* <p> The original pattern. </p>
|
||||
*/
|
||||
private String original;
|
||||
|
||||
|
||||
/**
|
||||
* <p> The compiled pattern. </p>
|
||||
*/
|
||||
private Object pattern;
|
||||
|
||||
/**
|
||||
* <p> The original object. </p>
|
||||
*/
|
||||
private E config;
|
||||
|
||||
/**
|
||||
* <p> Contructs a read-only Mapping instance. </p>
|
||||
*
|
||||
* @param original The original pattern
|
||||
* @param pattern The compiled pattern
|
||||
* @param config The original object
|
||||
*/
|
||||
public Mapping(String original, Object pattern, E config) {
|
||||
this.original = original;
|
||||
this.pattern = pattern;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Gets the compiled wildcard pattern. </p>
|
||||
*
|
||||
* @return The compiled pattern
|
||||
*/
|
||||
public Object getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Gets the object that contains the pattern. </p>
|
||||
*
|
||||
* @return The associated object
|
||||
*/
|
||||
public E getTarget() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Gets the original wildcard pattern. </p>
|
||||
*
|
||||
* @return The original pattern
|
||||
*/
|
||||
public String getOriginalPattern() {
|
||||
return this.original;
|
||||
}
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2003,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.WildcardHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p> Matches paths against pre-compiled wildcard expressions pulled from
|
||||
* action configs. It uses the wildcard matcher from the Apache Cocoon
|
||||
* project. Patterns will be matched in the order they exist in the
|
||||
* config file. The first match wins, so more specific patterns should be
|
||||
* defined before less specific patterns.
|
||||
*/
|
||||
public class ActionConfigMatcher extends AbstractMatcher<ActionConfig> implements Serializable {
|
||||
|
||||
/**
|
||||
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
|
||||
* "path" attributes. ActionConfig's will be evaluated in the order they
|
||||
* exist in the config file. Only paths that actually contain a
|
||||
* wildcard will be compiled. Patterns will matched strictly.</p>
|
||||
*
|
||||
* @param configs An array of ActionConfig's to process
|
||||
* @deprecated Since 2.1, use {@link #ActionConfigMatcher(PatternMatcher, Map, boolean)} instead
|
||||
*/
|
||||
@Deprecated public ActionConfigMatcher(Map<String, ActionConfig> configs) {
|
||||
this(configs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
|
||||
* "path" attributes. ActionConfig's will be evaluated in the order they
|
||||
* exist in the config file. Only paths that actually contain a
|
||||
* wildcard will be compiled. </p>
|
||||
*
|
||||
* <p>Patterns can optionally be matched "loosely". When
|
||||
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
|
||||
* wildcard), if the pattern fails, it is also matched as if the
|
||||
* last two characters didn't exist. The goal is to support the
|
||||
* legacy "*!*" syntax, where the "!*" is optional.</p>
|
||||
*
|
||||
* @param configs An array of ActionConfig's to process
|
||||
* @param looseMatch To loosely match wildcards or not
|
||||
* @deprecated Since 2.1, use {@link #ActionConfigMatcher(PatternMatcher, Map, boolean)} instead
|
||||
*/
|
||||
@Deprecated public ActionConfigMatcher(Map<String, ActionConfig> configs,
|
||||
boolean looseMatch) {
|
||||
|
||||
this(new WildcardHelper(), configs, looseMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Finds and precompiles the wildcard patterns from the ActionConfig
|
||||
* "path" attributes. ActionConfig's will be evaluated in the order they
|
||||
* exist in the config file. Only paths that actually contain a
|
||||
* wildcard will be compiled. </p>
|
||||
*
|
||||
* <p>Patterns can optionally be matched "loosely". When
|
||||
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
|
||||
* wildcard), if the pattern fails, it is also matched as if the
|
||||
* last two characters didn't exist. The goal is to support the
|
||||
* legacy "*!*" syntax, where the "!*" is optional.</p>
|
||||
*
|
||||
* @param configs An array of ActionConfig's to process
|
||||
* @param looseMatch To loosely match wildcards or not
|
||||
*/
|
||||
public ActionConfigMatcher(PatternMatcher<?> patternMatcher,
|
||||
Map<String, ActionConfig> configs,
|
||||
boolean looseMatch) {
|
||||
super(patternMatcher);
|
||||
for (String name : configs.keySet()) {
|
||||
addPattern(name, configs.get(name), looseMatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Clones the ActionConfig and its children, replacing various
|
||||
* properties with the values of the wildcard-matched strings. </p>
|
||||
*
|
||||
* @param path The requested path
|
||||
* @param orig The original ActionConfig
|
||||
* @param vars A Map of wildcard-matched strings
|
||||
* @return A cloned ActionConfig with appropriate properties replaced with
|
||||
* wildcard-matched values
|
||||
*/
|
||||
@Override public ActionConfig convert(String path, ActionConfig orig,
|
||||
Map<String, String> vars) {
|
||||
|
||||
String className = convertParam(orig.getClassName(), vars);
|
||||
String methodName = convertParam(orig.getMethodName(), vars);
|
||||
String pkgName = convertParam(orig.getPackageName(), vars);
|
||||
|
||||
Map<String,String> params = replaceParameters(orig.getParams(), vars);
|
||||
|
||||
Map<String,ResultConfig> results = new LinkedHashMap<String,ResultConfig>();
|
||||
for (String name : orig.getResults().keySet()) {
|
||||
ResultConfig result = orig.getResults().get(name);
|
||||
name = convertParam(name, vars);
|
||||
ResultConfig r = new ResultConfig.Builder(name, convertParam(result.getClassName(), vars))
|
||||
.addParams(replaceParameters(result.getParams(), vars))
|
||||
.build();
|
||||
results.put(name, r);
|
||||
}
|
||||
|
||||
List<ExceptionMappingConfig> exs = new ArrayList<ExceptionMappingConfig>();
|
||||
for (ExceptionMappingConfig ex : orig.getExceptionMappings()) {
|
||||
String name = convertParam(ex.getName(), vars);
|
||||
String exClassName = convertParam(ex.getExceptionClassName(), vars);
|
||||
String exResult = convertParam(ex.getResult(), vars);
|
||||
Map<String,String> exParams = replaceParameters(ex.getParams(), vars);
|
||||
ExceptionMappingConfig e = new ExceptionMappingConfig.Builder(name, exClassName, exResult).addParams(exParams).build();
|
||||
exs.add(e);
|
||||
}
|
||||
|
||||
return new ActionConfig.Builder(pkgName, orig.getName(), className)
|
||||
.methodName(methodName)
|
||||
.addParams(params)
|
||||
.addResultConfigs(results)
|
||||
.addInterceptors(orig.getInterceptors())
|
||||
.addExceptionMappings(exs)
|
||||
.location(orig.getLocation())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+484
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.config.*;
|
||||
import com.opensymphony.xwork2.config.entities.*;
|
||||
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.*;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import ognl.PropertyAccessor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* DefaultConfiguration
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* Created Feb 24, 2003 7:38:06 AM
|
||||
*/
|
||||
public class DefaultConfiguration implements Configuration {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(DefaultConfiguration.class);
|
||||
|
||||
|
||||
// Programmatic Action Configurations
|
||||
protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();
|
||||
protected RuntimeConfiguration runtimeConfiguration;
|
||||
protected Container container;
|
||||
protected String defaultFrameworkBeanName;
|
||||
protected Set<String> loadedFileNames = new TreeSet<String>();
|
||||
protected List<UnknownHandlerConfig> unknownHandlerStack;
|
||||
|
||||
|
||||
ObjectFactory objectFactory;
|
||||
|
||||
public DefaultConfiguration() {
|
||||
this("xwork");
|
||||
}
|
||||
|
||||
public DefaultConfiguration(String defaultBeanName) {
|
||||
this.defaultFrameworkBeanName = defaultBeanName;
|
||||
}
|
||||
|
||||
|
||||
public PackageConfig getPackageConfig(String name) {
|
||||
return packageContexts.get(name);
|
||||
}
|
||||
|
||||
public List<UnknownHandlerConfig> getUnknownHandlerStack() {
|
||||
return unknownHandlerStack;
|
||||
}
|
||||
|
||||
public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
|
||||
this.unknownHandlerStack = unknownHandlerStack;
|
||||
}
|
||||
|
||||
public Set<String> getPackageConfigNames() {
|
||||
return packageContexts.keySet();
|
||||
}
|
||||
|
||||
public Map<String, PackageConfig> getPackageConfigs() {
|
||||
return packageContexts;
|
||||
}
|
||||
|
||||
public Set<String> getLoadedFileNames() {
|
||||
return loadedFileNames;
|
||||
}
|
||||
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
return runtimeConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the container
|
||||
*/
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
public void addPackageConfig(String name, PackageConfig packageContext) {
|
||||
PackageConfig check = packageContexts.get(name);
|
||||
if (check != null) {
|
||||
if (check.getLocation() != null && packageContext.getLocation() != null
|
||||
&& check.getLocation().equals(packageContext.getLocation())) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The package name '" + name
|
||||
+ "' is already been loaded by the same location and could be removed: "
|
||||
+ packageContext.getLocation());
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("The package name '" + name
|
||||
+ "' at location "+packageContext.getLocation()
|
||||
+ " is already been used by another package at location " + check.getLocation(),
|
||||
packageContext);
|
||||
}
|
||||
}
|
||||
packageContexts.put(name, packageContext);
|
||||
}
|
||||
|
||||
public PackageConfig removePackageConfig(String packageName) {
|
||||
return packageContexts.remove(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the configuration to clean up any resources used
|
||||
*/
|
||||
public void destroy() {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
}
|
||||
|
||||
public void rebuildRuntimeConfiguration() {
|
||||
runtimeConfiguration = buildRuntimeConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
|
||||
* buildRuntimeConfiguration().
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public synchronized void reload(List<ConfigurationProvider> providers) throws ConfigurationException {
|
||||
|
||||
// Silly copy necessary due to lack of ability to cast generic lists
|
||||
List<ContainerProvider> contProviders = new ArrayList<ContainerProvider>();
|
||||
contProviders.addAll(providers);
|
||||
|
||||
reloadContainer(contProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
|
||||
* buildRuntimeConfiguration().
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
|
||||
packageContexts.clear();
|
||||
loadedFileNames.clear();
|
||||
List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
|
||||
|
||||
ContainerProperties props = new ContainerProperties();
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
for (final ContainerProvider containerProvider : providers)
|
||||
{
|
||||
containerProvider.init(this);
|
||||
containerProvider.register(builder, props);
|
||||
}
|
||||
props.setConstants(builder);
|
||||
|
||||
builder.factory(Configuration.class, new Factory<Configuration>() {
|
||||
public Configuration create(Context context) throws Exception {
|
||||
return DefaultConfiguration.this;
|
||||
}
|
||||
});
|
||||
|
||||
ActionContext oldContext = ActionContext.getContext();
|
||||
try {
|
||||
// Set the bootstrap container for the purposes of factory creation
|
||||
Container bootstrap = createBootstrapContainer();
|
||||
setContext(bootstrap);
|
||||
container = builder.create(false);
|
||||
setContext(container);
|
||||
objectFactory = container.getInstance(ObjectFactory.class);
|
||||
|
||||
// Process the configuration providers first
|
||||
for (final ContainerProvider containerProvider : providers)
|
||||
{
|
||||
if (containerProvider instanceof PackageProvider) {
|
||||
container.inject(containerProvider);
|
||||
((PackageProvider)containerProvider).loadPackages();
|
||||
packageProviders.add((PackageProvider)containerProvider);
|
||||
}
|
||||
}
|
||||
|
||||
// Then process any package providers from the plugins
|
||||
Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
|
||||
if (packageProviderNames != null) {
|
||||
for (String name : packageProviderNames) {
|
||||
PackageProvider provider = container.getInstance(PackageProvider.class, name);
|
||||
provider.init(this);
|
||||
provider.loadPackages();
|
||||
packageProviders.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
rebuildRuntimeConfiguration();
|
||||
} finally {
|
||||
if (oldContext == null) {
|
||||
ActionContext.setContext(null);
|
||||
}
|
||||
}
|
||||
return packageProviders;
|
||||
}
|
||||
|
||||
protected ActionContext setContext(Container cont) {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
if (context == null) {
|
||||
ValueStack vs = cont.getInstance(ValueStackFactory.class).createValueStack();
|
||||
context = new ActionContext(vs.getContext());
|
||||
ActionContext.setContext(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
protected Container createBootstrapContainer() {
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
builder.factory(ObjectFactory.class, Scope.SINGLETON);
|
||||
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
|
||||
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
|
||||
builder.factory(XWorkConverter.class, Scope.SINGLETON);
|
||||
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON);
|
||||
builder.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON);
|
||||
builder.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON);
|
||||
builder.factory(OgnlUtil.class, Scope.SINGLETON);
|
||||
builder.constant("devMode", "false");
|
||||
builder.constant("logMissingProperties", "false");
|
||||
return builder.create(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
|
||||
* programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
|
||||
*
|
||||
* <p>
|
||||
* It basically flattens the data structures to make the information easier to access. It will take
|
||||
* an {@link ActionConfig} and combine its data with all inherited dast. For example, if the {@link ActionConfig}
|
||||
* is in a package that contains a global result and it also contains a result, the resulting {@link ActionConfig}
|
||||
* will have two results.
|
||||
*/
|
||||
protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
|
||||
Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
|
||||
Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();
|
||||
|
||||
for (PackageConfig packageConfig : packageContexts.values()) {
|
||||
|
||||
if (!packageConfig.isAbstract()) {
|
||||
String namespace = packageConfig.getNamespace();
|
||||
Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
|
||||
|
||||
if (configs == null) {
|
||||
configs = new LinkedHashMap<String, ActionConfig>();
|
||||
}
|
||||
|
||||
Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();
|
||||
|
||||
for (Object o : actionConfigs.keySet()) {
|
||||
String actionName = (String) o;
|
||||
ActionConfig baseConfig = actionConfigs.get(actionName);
|
||||
configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespaceActionConfigs.put(namespace, configs);
|
||||
if (packageConfig.getFullDefaultActionRef() != null) {
|
||||
namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
|
||||
}
|
||||
|
||||
private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
|
||||
String defaultResult = packageContext.getFullDefaultResultType();
|
||||
|
||||
for (Map.Entry<String, ResultConfig> entry : results.entrySet()) {
|
||||
|
||||
if (entry.getValue() == null) {
|
||||
ResultTypeConfig resultTypeConfig = packageContext.getAllResultTypeConfigs().get(defaultResult);
|
||||
entry.setValue(new ResultConfig.Builder(null, resultTypeConfig.getClassName()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the full runtime actionconfig with all of the defaults and inheritance
|
||||
*
|
||||
* @param packageContext the PackageConfig which holds the base config we're building from
|
||||
* @param baseConfig the ActionConfig which holds only the configuration specific to itself, without the defaults
|
||||
* and inheritance
|
||||
* @return a full ActionConfig for runtime configuration with all of the inherited and default params
|
||||
* @throws com.opensymphony.xwork2.config.ConfigurationException
|
||||
*
|
||||
*/
|
||||
private ActionConfig buildFullActionConfig(PackageConfig packageContext, ActionConfig baseConfig) throws ConfigurationException {
|
||||
Map<String, String> params = new TreeMap<String, String>(baseConfig.getParams());
|
||||
Map<String, ResultConfig> results = new TreeMap<String, ResultConfig>();
|
||||
|
||||
if (!baseConfig.getPackageName().equals(packageContext.getName()) && packageContexts.containsKey(baseConfig.getPackageName())) {
|
||||
results.putAll(packageContexts.get(baseConfig.getPackageName()).getAllGlobalResults());
|
||||
} else {
|
||||
results.putAll(packageContext.getAllGlobalResults());
|
||||
}
|
||||
|
||||
results.putAll(baseConfig.getResults());
|
||||
|
||||
setDefaultResults(results, packageContext);
|
||||
|
||||
List<InterceptorMapping> interceptors = new ArrayList<InterceptorMapping>(baseConfig.getInterceptors());
|
||||
|
||||
if (interceptors.size() <= 0) {
|
||||
String defaultInterceptorRefName = packageContext.getFullDefaultInterceptorRef();
|
||||
|
||||
if (defaultInterceptorRefName != null) {
|
||||
interceptors.addAll(InterceptorBuilder.constructInterceptorReference(new PackageConfig.Builder(packageContext), defaultInterceptorRefName,
|
||||
new LinkedHashMap<String, String>(), packageContext.getLocation(), objectFactory));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new ActionConfig.Builder(baseConfig)
|
||||
.addParams(params)
|
||||
.addResultConfigs(results)
|
||||
.defaultClassName(packageContext.getDefaultClassRef()) // fill in default if non class has been provided
|
||||
.interceptors(interceptors)
|
||||
.addExceptionMappings(packageContext.getAllExceptionMappingConfigs())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private class RuntimeConfigurationImpl implements RuntimeConfiguration {
|
||||
private Map<String, Map<String, ActionConfig>> namespaceActionConfigs;
|
||||
private Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
|
||||
private NamespaceMatcher namespaceMatcher;
|
||||
private Map<String, String> namespaceConfigs;
|
||||
|
||||
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs, Map<String, String> namespaceConfigs) {
|
||||
this.namespaceActionConfigs = namespaceActionConfigs;
|
||||
this.namespaceConfigs = namespaceConfigs;
|
||||
|
||||
PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
|
||||
|
||||
this.namespaceActionConfigMatchers = new LinkedHashMap<String, ActionConfigMatcher>();
|
||||
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());
|
||||
|
||||
for (String ns : namespaceActionConfigs.keySet()) {
|
||||
namespaceActionConfigMatchers.put(ns,
|
||||
new ActionConfigMatcher(matcher,
|
||||
namespaceActionConfigs.get(ns), true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the configuration information for an action name, or returns null if the
|
||||
* name is not recognized.
|
||||
*
|
||||
* @param name the name of the action
|
||||
* @param namespace the namespace for the action or null for the empty namespace, ""
|
||||
* @return the configuration information for action requested
|
||||
*/
|
||||
public synchronized ActionConfig getActionConfig(String namespace, String name) {
|
||||
ActionConfig config = findActionConfigInNamespace(namespace, name);
|
||||
|
||||
// try wildcarded namespaces
|
||||
if (config == null) {
|
||||
NamespaceMatch match = namespaceMatcher.match(namespace);
|
||||
if (match != null) {
|
||||
config = findActionConfigInNamespace(match.getPattern(), name);
|
||||
|
||||
// If config found, place all the matches found in the namespace processing in the action's parameters
|
||||
if (config != null) {
|
||||
config = new ActionConfig.Builder(config)
|
||||
.addParams(match.getVariables())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fail over to empty namespace
|
||||
if ((config == null) && (namespace != null) && (!"".equals(namespace.trim()))) {
|
||||
config = findActionConfigInNamespace("", name);
|
||||
}
|
||||
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ActionConfig findActionConfigInNamespace(String namespace, String name) {
|
||||
ActionConfig config = null;
|
||||
if (namespace == null) {
|
||||
namespace = "";
|
||||
}
|
||||
Map<String, ActionConfig> actions = namespaceActionConfigs.get(namespace);
|
||||
if (actions != null) {
|
||||
config = actions.get(name);
|
||||
// Check wildcards
|
||||
if (config == null) {
|
||||
config = namespaceActionConfigMatchers.get(namespace).match(name);
|
||||
// fail over to default action
|
||||
if (config == null) {
|
||||
String defaultActionRef = namespaceConfigs.get(namespace);
|
||||
if (defaultActionRef != null) {
|
||||
config = actions.get(defaultActionRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration settings for every action.
|
||||
*
|
||||
* @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
|
||||
*/
|
||||
public synchronized Map<String, Map<String, ActionConfig>> getActionConfigs() {
|
||||
return namespaceActionConfigs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buff = new StringBuilder("RuntimeConfiguration - actions are\n");
|
||||
|
||||
for (String namespace : namespaceActionConfigs.keySet()) {
|
||||
Map<String, ActionConfig> actionConfigs = namespaceActionConfigs.get(namespace);
|
||||
|
||||
for (String s : actionConfigs.keySet()) {
|
||||
buff.append(namespace).append("/").append(s).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return buff.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerProperties extends LocatableProperties {
|
||||
private static final long serialVersionUID = -7320625750836896089L;
|
||||
|
||||
@Override
|
||||
public Object setProperty(String key, String value) {
|
||||
String oldValue = getProperty(key);
|
||||
if (oldValue != null && !oldValue.equals(value) && !defaultFrameworkBeanName.equals(oldValue)) {
|
||||
LOG.info("Overriding property "+key+" - old value: "+oldValue+" new value: "+value);
|
||||
}
|
||||
return super.setProperty(key, value);
|
||||
}
|
||||
|
||||
public void setConstants(ContainerBuilder builder) {
|
||||
for (Object keyobj : keySet()) {
|
||||
String key = (String)keyobj;
|
||||
builder.factory(String.class, key,
|
||||
new LocatableConstantFactory<String>(getProperty(key), getPropertyLocation(key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Context;
|
||||
import com.opensymphony.xwork2.inject.Factory;
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.LocationUtils;
|
||||
|
||||
/**
|
||||
* Factory that remembers where a constant came from
|
||||
*/
|
||||
public class LocatableConstantFactory<T> extends Located implements Factory {
|
||||
T constant;
|
||||
public LocatableConstantFactory(T constant, Object location) {
|
||||
this.constant = constant;
|
||||
setLocation(LocationUtils.getLocation(location));
|
||||
}
|
||||
|
||||
public T create(Context ignored) {
|
||||
return constant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(" defined at ");
|
||||
sb.append(getLocation().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Context;
|
||||
import com.opensymphony.xwork2.inject.Factory;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.util.location.Located;
|
||||
import com.opensymphony.xwork2.util.location.LocationUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Attaches location information to the factory.
|
||||
*/
|
||||
public class LocatableFactory<T> extends Located implements Factory<T> {
|
||||
|
||||
|
||||
private Class implementation;
|
||||
private Class type;
|
||||
private String name;
|
||||
private Scope scope;
|
||||
|
||||
public LocatableFactory(String name, Class type, Class implementation, Scope scope, Object location) {
|
||||
this.implementation = implementation;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.scope = scope;
|
||||
setLocation(LocationUtils.getLocation(location));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T create(Context context) {
|
||||
Object obj = context.getContainer().inject(implementation);
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String fields = new LinkedHashMap<String, Object>() {
|
||||
{
|
||||
put("type", type);
|
||||
put("name", name);
|
||||
put("implementation", implementation);
|
||||
put("scope", scope);
|
||||
}
|
||||
}.toString();
|
||||
StringBuilder sb = new StringBuilder(fields);
|
||||
sb.append(super.toString());
|
||||
sb.append(" defined at ");
|
||||
sb.append(getLocation().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2002-2003,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.ContainerProvider;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.RuntimeConfiguration;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.config.providers.XWorkConfigurationProvider;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Simple configuration used for unit testing
|
||||
*/
|
||||
public class MockConfiguration implements Configuration {
|
||||
|
||||
private Map<String, PackageConfig> packages = new HashMap<String, PackageConfig>();
|
||||
private Set<String> loadedFiles = new HashSet<String>();
|
||||
private Container container;
|
||||
protected List<UnknownHandlerConfig> unknownHandlerStack;
|
||||
private ContainerBuilder builder;
|
||||
|
||||
public MockConfiguration() {
|
||||
builder = new ContainerBuilder();
|
||||
}
|
||||
|
||||
public void selfRegister() {
|
||||
//this cannot be done in the constructor, as it causes an infinite loop
|
||||
builder.factory(Configuration.class, MockConfiguration.class, Scope.SINGLETON);
|
||||
LocatableProperties props = new LocatableProperties();
|
||||
new XWorkConfigurationProvider().register(builder, props);
|
||||
builder.constant("devMode", "false");
|
||||
container = builder.create(true);
|
||||
}
|
||||
|
||||
public PackageConfig getPackageConfig(String name) {
|
||||
return packages.get(name);
|
||||
}
|
||||
|
||||
public Set<String> getPackageConfigNames() {
|
||||
return packages.keySet();
|
||||
}
|
||||
|
||||
public Map<String, PackageConfig> getPackageConfigs() {
|
||||
return packages;
|
||||
}
|
||||
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void addPackageConfig(String name, PackageConfig packageContext) {
|
||||
packages.put(name, packageContext);
|
||||
}
|
||||
|
||||
public void buildRuntimeConfiguration() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void rebuildRuntimeConfiguration() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void reload(List<ConfigurationProvider> providers) throws ConfigurationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public PackageConfig removePackageConfig(String name) {
|
||||
return packages.remove(name);
|
||||
}
|
||||
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
public Set<String> getLoadedFileNames() {
|
||||
return loadedFiles;
|
||||
}
|
||||
|
||||
public List<PackageProvider> reloadContainer(
|
||||
List<ContainerProvider> containerProviders)
|
||||
throws ConfigurationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public List<UnknownHandlerConfig> getUnknownHandlerStack() {
|
||||
return unknownHandlerStack;
|
||||
}
|
||||
|
||||
public void setUnknownHandlerStack(List<UnknownHandlerConfig> unknownHandlerStack) {
|
||||
this.unknownHandlerStack = unknownHandlerStack;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a match from a namespace pattern matching.
|
||||
*
|
||||
* @Since 2.1
|
||||
*/
|
||||
public class NamespaceMatch {
|
||||
private String pattern;
|
||||
private Map<String,String> variables;
|
||||
|
||||
public NamespaceMatch(String pattern, Map<String, String> variables) {
|
||||
this.pattern = pattern;
|
||||
this.variables = variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The pattern that was matched
|
||||
*/
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The variables containing the matched values
|
||||
*/
|
||||
public Map<String, String> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.impl;
|
||||
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Matches namespace strings against a wildcard pattern matcher
|
||||
*
|
||||
* @Since 2.1
|
||||
*/
|
||||
public class NamespaceMatcher extends AbstractMatcher<NamespaceMatch> {
|
||||
public NamespaceMatcher(PatternMatcher<?> patternMatcher,
|
||||
Set<String> namespaces) {
|
||||
super(patternMatcher);
|
||||
for (String name : namespaces) {
|
||||
if (!patternMatcher.isLiteral(name)) {
|
||||
addPattern(name, new NamespaceMatch(name, null), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamespaceMatch convert(String path, NamespaceMatch orig, Map<String, String> vars) {
|
||||
/*Map<String,String> origVars = (Map<String,String>)vars;
|
||||
Map<String,String> map = new HashMap<String,String>();
|
||||
for (Map.Entry<String,String> entry : origVars.entrySet()) {
|
||||
if (entry.getKey().length() == 1) {
|
||||
map.put("ns"+entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
*/
|
||||
return new NamespaceMatch(orig.getPattern(), vars);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<body>Configuration implementation classes.</body>
|
||||
@@ -0,0 +1 @@
|
||||
<body>Configuration core classes.</body>
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.providers;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorLocator;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig.
|
||||
*
|
||||
* @author Mike
|
||||
* @author Rainer Hermanns
|
||||
* @author tmjee
|
||||
* @version $Date$ $Id$
|
||||
*/
|
||||
public class InterceptorBuilder {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InterceptorBuilder.class);
|
||||
|
||||
|
||||
/**
|
||||
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig (InterceptorMapping object).
|
||||
*
|
||||
* @param interceptorLocator
|
||||
* @param refName
|
||||
* @param refParams
|
||||
* @return list of interceptors referenced by the refName in the supplied PackageConfig (InterceptorMapping object).
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public static List<InterceptorMapping> constructInterceptorReference(InterceptorLocator interceptorLocator,
|
||||
String refName, Map<String,String> refParams, Location location, ObjectFactory objectFactory) throws ConfigurationException {
|
||||
Object referencedConfig = interceptorLocator.getInterceptorConfig(refName);
|
||||
List<InterceptorMapping> result = new ArrayList<InterceptorMapping>();
|
||||
|
||||
if (referencedConfig == null) {
|
||||
throw new ConfigurationException("Unable to find interceptor class referenced by ref-name " + refName, location);
|
||||
} else {
|
||||
if (referencedConfig instanceof InterceptorConfig) {
|
||||
InterceptorConfig config = (InterceptorConfig) referencedConfig;
|
||||
Interceptor inter = null;
|
||||
try {
|
||||
|
||||
inter = objectFactory.buildInterceptor(config, refParams);
|
||||
result.add(new InterceptorMapping(refName, inter));
|
||||
} catch (ConfigurationException ex) {
|
||||
LOG.warn("Unable to load config class " + config.getClassName() + " at " +
|
||||
ex.getLocation() + " probably due to a missing jar, which might " +
|
||||
"be fine if you never plan to use the " + config.getName() + " interceptor");
|
||||
LOG.error("Actual exception", ex);
|
||||
}
|
||||
|
||||
} else if (referencedConfig instanceof InterceptorStackConfig) {
|
||||
InterceptorStackConfig stackConfig = (InterceptorStackConfig) referencedConfig;
|
||||
|
||||
if ((refParams != null) && (refParams.size() > 0)) {
|
||||
result = constructParameterizedInterceptorReferences(interceptorLocator, stackConfig, refParams, objectFactory);
|
||||
} else {
|
||||
result.addAll(stackConfig.getInterceptors());
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG.error("Got unexpected type for interceptor " + refName + ". Got " + referencedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of interceptors referenced by the refName in the supplied PackageConfig overriding the properties
|
||||
* of the referenced interceptor with refParams.
|
||||
*
|
||||
* @param interceptorLocator
|
||||
* @param stackConfig
|
||||
* @param refParams The overridden interceptor properies
|
||||
* @return list of interceptors referenced by the refName in the supplied PackageConfig overridden with refParams.
|
||||
*/
|
||||
private static List<InterceptorMapping> constructParameterizedInterceptorReferences(
|
||||
InterceptorLocator interceptorLocator, InterceptorStackConfig stackConfig, Map<String,String> refParams,
|
||||
ObjectFactory objectFactory) {
|
||||
List<InterceptorMapping> result;
|
||||
Map<String, Map<String, String>> params = new LinkedHashMap<String, Map<String, String>>();
|
||||
|
||||
/*
|
||||
* We strip
|
||||
*
|
||||
* <interceptor-ref name="someStack">
|
||||
* <param name="interceptor1.param1">someValue</param>
|
||||
* <param name="interceptor1.param2">anotherValue</param>
|
||||
* </interceptor-ref>
|
||||
*
|
||||
* down to map
|
||||
* interceptor1 -> [param1 -> someValue, param2 -> anotherValue]
|
||||
*
|
||||
* or
|
||||
* <interceptor-ref name="someStack">
|
||||
* <param name="interceptorStack1.interceptor1.param1">someValue</param>
|
||||
* <param name="interceptorStack1.interceptor1.param2">anotherValue</param>
|
||||
* </interceptor-ref>
|
||||
*
|
||||
* down to map
|
||||
* interceptorStack1 -> [interceptor1.param1 -> someValue, interceptor1.param2 -> anotherValue]
|
||||
*
|
||||
*/
|
||||
for (String key : refParams.keySet()) {
|
||||
String value = refParams.get(key);
|
||||
|
||||
try {
|
||||
String name = key.substring(0, key.indexOf('.'));
|
||||
key = key.substring(key.indexOf('.') + 1);
|
||||
|
||||
Map<String, String> map;
|
||||
if (params.containsKey(name)) {
|
||||
map = params.get(name);
|
||||
} else {
|
||||
map = new LinkedHashMap<String, String>();
|
||||
}
|
||||
|
||||
map.put(key, value);
|
||||
params.put(name, map);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.warn("No interceptor found for name = " + key);
|
||||
}
|
||||
}
|
||||
|
||||
result = new ArrayList<InterceptorMapping>(stackConfig.getInterceptors());
|
||||
|
||||
for (String key : params.keySet()) {
|
||||
|
||||
Map<String, String> map = params.get(key);
|
||||
|
||||
|
||||
Object interceptorCfgObj = interceptorLocator.getInterceptorConfig(key);
|
||||
|
||||
/*
|
||||
* Now we attempt to separate out param that refers to Interceptor
|
||||
* and Interceptor stack, eg.
|
||||
*
|
||||
* <interceptor-ref name="someStack">
|
||||
* <param name="interceptor1.param1">someValue</param>
|
||||
* ...
|
||||
* </interceptor-ref>
|
||||
*
|
||||
* vs
|
||||
*
|
||||
* <interceptor-ref name="someStack">
|
||||
* <param name="interceptorStack1.interceptor1.param1">someValue</param>
|
||||
* ...
|
||||
* </interceptor-ref>
|
||||
*/
|
||||
if (interceptorCfgObj instanceof InterceptorConfig) { // interceptor-ref param refer to an interceptor
|
||||
InterceptorConfig cfg = (InterceptorConfig) interceptorCfgObj;
|
||||
Interceptor interceptor = objectFactory.buildInterceptor(cfg, map);
|
||||
|
||||
InterceptorMapping mapping = new InterceptorMapping(key, interceptor);
|
||||
if (result != null && result.contains(mapping)) {
|
||||
// if an existing interceptor mapping exists,
|
||||
// we remove from the result Set, just to make sure
|
||||
// there's always one unique mapping.
|
||||
int index = result.indexOf(mapping);
|
||||
result.set(index, mapping);
|
||||
} else {
|
||||
result.add(mapping);
|
||||
}
|
||||
} else
|
||||
if (interceptorCfgObj instanceof InterceptorStackConfig) { // interceptor-ref param refer to an interceptor stack
|
||||
|
||||
// If its an interceptor-stack, we call this method recursively untill,
|
||||
// all the params (eg. interceptorStack1.interceptor1.param etc.)
|
||||
// are resolved down to a specific interceptor.
|
||||
|
||||
InterceptorStackConfig stackCfg = (InterceptorStackConfig) interceptorCfgObj;
|
||||
List<InterceptorMapping> tmpResult = constructParameterizedInterceptorReferences(interceptorLocator, stackCfg, map, objectFactory);
|
||||
for (InterceptorMapping tmpInterceptorMapping : tmpResult) {
|
||||
if (result.contains(tmpInterceptorMapping)) {
|
||||
int index = result.indexOf(tmpInterceptorMapping);
|
||||
result.set(index, tmpInterceptorMapping);
|
||||
} else {
|
||||
result.add(tmpInterceptorMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package com.opensymphony.xwork2.config.providers;
|
||||
|
||||
import com.opensymphony.xwork2.ActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultActionProxyFactory;
|
||||
import com.opensymphony.xwork2.DefaultTextProvider;
|
||||
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.TextProviderSupport;
|
||||
import com.opensymphony.xwork2.UnknownHandlerManager;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.ContainerBuilder;
|
||||
import com.opensymphony.xwork2.inject.Scope;
|
||||
import com.opensymphony.xwork2.ognl.ObjectProxy;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
|
||||
import com.opensymphony.xwork2.ognl.OgnlUtil;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
|
||||
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
import com.opensymphony.xwork2.util.PatternMatcher;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.WildcardHelper;
|
||||
import com.opensymphony.xwork2.util.location.LocatableProperties;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.DefaultActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.DefaultValidatorFileParser;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFactory;
|
||||
import com.opensymphony.xwork2.validator.ValidatorFileParser;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void init(Configuration configuration) throws ConfigurationException {
|
||||
}
|
||||
|
||||
public void loadPackages() throws ConfigurationException {
|
||||
}
|
||||
|
||||
public boolean needsReload() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void register(ContainerBuilder builder, LocatableProperties props)
|
||||
throws ConfigurationException {
|
||||
|
||||
builder.factory(com.opensymphony.xwork2.ObjectFactory.class)
|
||||
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
|
||||
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
|
||||
.factory(XWorkConverter.class, Scope.SINGLETON)
|
||||
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
|
||||
.factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
|
||||
.factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
|
||||
.factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
|
||||
.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
|
||||
.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
|
||||
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
|
||||
|
||||
// silly workarounds for ognl since there is no way to flush its caches
|
||||
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
|
||||
.factory(TextProvider.class, TextProviderSupport.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(XWorkBasicConverter.class, Scope.SINGLETON);
|
||||
props.setProperty("devMode", Boolean.FALSE.toString());
|
||||
props.setProperty("logMissingProperties", Boolean.FALSE.toString());
|
||||
props.setProperty("enableOGNLExpressionCache", Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
}
|
||||
+1004
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.config.providers;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.w3c.dom.Document;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* XML utilities.
|
||||
*
|
||||
* @author Mike
|
||||
*/
|
||||
public class XmlHelper {
|
||||
|
||||
|
||||
/**
|
||||
* This method will find all the parameters under this <code>paramsElement</code> and return them as
|
||||
* Map<String, String>. For example,
|
||||
* <pre>
|
||||
* <result ... >
|
||||
* <param name="param1">value1</param>
|
||||
* <param name="param2">value2</param>
|
||||
* <param name="param3">value3</param>
|
||||
* </result>
|
||||
* </pre>
|
||||
* will returns a Map<String, String> with the following key, value pairs :-
|
||||
* <ul>
|
||||
* <li>param1 - value1</li>
|
||||
* <li>param2 - value2</li>
|
||||
* <li>param3 - value3</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param paramsElement
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> getParams(Element paramsElement) {
|
||||
LinkedHashMap<String, String> params = new LinkedHashMap<String, String>();
|
||||
|
||||
if (paramsElement == null) {
|
||||
return params;
|
||||
}
|
||||
|
||||
NodeList childNodes = paramsElement.getChildNodes();
|
||||
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node childNode = childNodes.item(i);
|
||||
|
||||
if ((childNode.getNodeType() == Node.ELEMENT_NODE) && "param".equals(childNode.getNodeName())) {
|
||||
Element paramElement = (Element) childNode;
|
||||
String paramName = paramElement.getAttribute("name");
|
||||
|
||||
String val = getContent(paramElement);
|
||||
if (val.length() > 0) {
|
||||
params.put(paramName, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the content of this particular <code>element</code>.
|
||||
* For example,
|
||||
* <p/>
|
||||
* <pre>
|
||||
* <result>something_1</result>
|
||||
* </pre>
|
||||
* When the {@link org.w3c.dom.Element} <code><result></code> is passed in as
|
||||
* argument (<code>element</code> to this method, it returns the content of it,
|
||||
* namely, <code>something_1</code> in the example above.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getContent(Element element) {
|
||||
StringBuilder paramValue = new StringBuilder();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int j = 0; j < childNodes.getLength(); j++) {
|
||||
Node currentNode = childNodes.item(j);
|
||||
if (currentNode != null &&
|
||||
currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
String val = currentNode.getNodeValue();
|
||||
if (val != null) {
|
||||
paramValue.append(val.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
return paramValue.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the "order" attribute from the root element
|
||||
*/
|
||||
public static Integer getLoadOrder(Document doc) {
|
||||
Element rootElement = doc.getDocumentElement();
|
||||
String number = rootElement.getAttribute("order");
|
||||
if (StringUtils.isNotBlank(number)) {
|
||||
try {
|
||||
return Integer.parseInt(number);
|
||||
} catch (NumberFormatException e) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
} else {
|
||||
//no order specified
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<body>Configuration provider classes.</body>
|
||||
@@ -0,0 +1,54 @@
|
||||
//--------------------------------------------------------------------------
|
||||
//Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
|
||||
//All rights reserved.
|
||||
//
|
||||
//Redistribution and use in source and binary forms, with or without
|
||||
//modification, are permitted provided that the following conditions are
|
||||
//met:
|
||||
//
|
||||
//Redistributions of source code must retain the above copyright notice,
|
||||
//this list of conditions and the following disclaimer.
|
||||
//Redistributions in binary form must reproduce the above copyright
|
||||
//notice, this list of conditions and the following disclaimer in the
|
||||
//documentation and/or other materials provided with the distribution.
|
||||
//Neither the name of the Drew Davidson nor the names of its contributors
|
||||
//may be used to endorse or promote products derived from this software
|
||||
//without specific prior written permission.
|
||||
//
|
||||
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
//COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
//OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
//AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
//OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
//THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
//DAMAGE.
|
||||
//--------------------------------------------------------------------------
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for handling null results from Chains.
|
||||
* Object has the opportunity to substitute an object for the
|
||||
* null and continue.
|
||||
* @author Luke Blanshard (blanshlu@netscape.net)
|
||||
* @author Drew Davidson (drew@ognl.org)
|
||||
*/
|
||||
public interface NullHandler
|
||||
{
|
||||
/**
|
||||
Method called on target returned null.
|
||||
*/
|
||||
public Object nullMethodResult(Map<String, Object> context, Object target, String methodName, Object[] args);
|
||||
|
||||
/**
|
||||
Property in target evaluated to null. Property can be a constant
|
||||
String property name or a DynamicSubscript.
|
||||
*/
|
||||
public Object nullPropertyValue(Map<String, Object> context, Object target, Object property);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
/**
|
||||
* Determines what the key and and element class of a Map or Collection should be. For Maps, the elements are the
|
||||
* values. For Collections, the elements are the elements of the collection.
|
||||
* <p/>
|
||||
* See the implementations for javadoc description for the methods as they are dependent on the concrete implementation.
|
||||
*
|
||||
* @author Gabriel Zimmerman
|
||||
*/
|
||||
public interface ObjectTypeDeterminer {
|
||||
|
||||
public Class getKeyClass(Class parentClass, String property);
|
||||
|
||||
public Class getElementClass(Class parentClass, String property, Object key);
|
||||
|
||||
public String getKeyProperty(Class parentClass, String property);
|
||||
|
||||
public boolean shouldCreateIfNew(Class parentClass, String property, Object target, String keyProperty, boolean isIndexAccessed);
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
|
||||
|
||||
/**
|
||||
* TypeConversionException should be thrown by any TypeConverters which fail to convert values
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* Created Oct 3, 2003 12:18:33 AM
|
||||
*/
|
||||
public class TypeConversionException extends XWorkException {
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with no detail message.
|
||||
*/
|
||||
public TypeConversionException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public TypeConversionException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with no detail message.
|
||||
*/
|
||||
public TypeConversionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>XWorkException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
*/
|
||||
public TypeConversionException(String s, Throwable cause) {
|
||||
super(s, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//--------------------------------------------------------------------------
|
||||
// Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// Neither the name of the Drew Davidson nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
// DAMAGE.
|
||||
//--------------------------------------------------------------------------
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for accessing the type conversion facilities within a context.
|
||||
*
|
||||
* This interface was copied from OGNL's TypeConverter
|
||||
*
|
||||
* @author Luke Blanshard (blanshlu@netscape.net)
|
||||
* @author Drew Davidson (drew@ognl.org)
|
||||
*/
|
||||
public interface TypeConverter
|
||||
{
|
||||
/**
|
||||
* Converts the given value to a given type. The OGNL context, target, member and
|
||||
* name of property being set are given. This method should be able to handle
|
||||
* conversion in general without any context, target, member or property name specified.
|
||||
* @param context context under which the conversion is being done
|
||||
* @param target target object in which the property is being set
|
||||
* @param member member (Constructor, Method or Field) being set
|
||||
* @param propertyName property name being set
|
||||
* @param value value to be converted
|
||||
* @param toType type to which value is converted
|
||||
* @return Converted value of type toType or TypeConverter.NoConversionPossible to indicate that the
|
||||
conversion was not possible.
|
||||
*/
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
|
||||
|
||||
public static final Object NO_CONVERSION_POSSIBLE = "ognl.NoConversionPossible";
|
||||
|
||||
public static final String TYPE_CONVERTER_CONTEXT_KEY = "_typeConverter";
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
* <p/>A marker annotation for type conversions at Type level.
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p/> <u>Annotation usage:</u>
|
||||
*
|
||||
* <!-- START SNIPPET: usage -->
|
||||
* <p/>The Conversion annotation must be applied at Type level.
|
||||
* <!-- END SNIPPET: usage -->
|
||||
*
|
||||
* <p/> <u>Annotation parameters:</u>
|
||||
*
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>Parameter</th>
|
||||
* <th>Required</th>
|
||||
* <th>Default</th>
|
||||
* <th>Description</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>conversion</td>
|
||||
* <td>no</td>
|
||||
* <td> </td>
|
||||
* <td>used for Type Conversions applied at Type level.</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p/> <u>Example code:</u>
|
||||
*
|
||||
* <pre>
|
||||
* <!-- START SNIPPET: example -->
|
||||
* @Conversion()
|
||||
* public class ConversionAction implements Action {
|
||||
* }
|
||||
*
|
||||
* <!-- END SNIPPET: example -->
|
||||
* </pre>
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Id$
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Conversion {
|
||||
|
||||
/**
|
||||
* Allow Type Conversions being applied at Type level.
|
||||
*/
|
||||
TypeConversion[] conversions() default {};
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.annotations;
|
||||
|
||||
/**
|
||||
* <code>ConversionRule</code>
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Id$
|
||||
*/
|
||||
public enum ConversionRule {
|
||||
|
||||
PROPERTY, COLLECTION, MAP, KEY, KEY_PROPERTY, ELEMENT, CREATE_IF_NULL;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString().toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.annotations;
|
||||
|
||||
/**
|
||||
* <code>ConversionType</code>
|
||||
*
|
||||
* @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
|
||||
* @version $Id$
|
||||
*/
|
||||
public enum ConversionType {
|
||||
|
||||
|
||||
APPLICATION, CLASS;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString().toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
* <p/>This annotation is used for class and application wide conversion rules.
|
||||
* <p>
|
||||
* Class wide conversion:<br/>
|
||||
* The conversion rules will be assembled in a file called <code>XXXAction-conversion.properties</code>
|
||||
* within the same package as the related action class.
|
||||
* Set type to: <code>type = ConversionType.CLASS</code>
|
||||
* </p>
|
||||
* <p>
|
||||
* Allication wide conversion:<br/>
|
||||
* The conversion rules will be assembled within the <code>xwork-conversion.properties</code> file within the classpath root.
|
||||
* Set type to: <code>type = ConversionType.APPLICATION</code>
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p/> <u>Annotation usage:</u>
|
||||
*
|
||||
* <!-- START SNIPPET: usage -->
|
||||
* The TypeConversion annotation can be applied at property and method level.
|
||||
* <!-- END SNIPPET: usage -->
|
||||
*
|
||||
* <p/> <u>Annotation parameters:</u>
|
||||
*
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>Parameter</th>
|
||||
* <th>Required</th>
|
||||
* <th>Default</th>
|
||||
* <th>Description</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>key</td>
|
||||
* <td>no</td>
|
||||
* <td>The annotated property/key name</td>
|
||||
* <td>The optional property name mostly used within TYPE level annotations.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>type</td>
|
||||
* <td>no</td>
|
||||
* <td>ConversionType.CLASS</td>
|
||||
* <td>Enum value of ConversionType. Determines whether the conversion should be applied at application or class level.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>rule</td>
|
||||
* <td>no</td>
|
||||
* <td>ConversionRule.PROPERTY</td>
|
||||
* <td>Enum value of ConversionRule. The ConversionRule can be a property, a Collection or a Map.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>converter</td>
|
||||
* <td>either this or value</td>
|
||||
* <td> </td>
|
||||
* <td>The class name of the TypeConverter to be used as converter.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>value</td>
|
||||
* <td>either converter or this</td>
|
||||
* <td> </td>
|
||||
* <td>The value to set for ConversionRule.KEY_PROPERTY.</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p/> <u>Example code:</u>
|
||||
*
|
||||
* <pre>
|
||||
* <!-- START SNIPPET: example -->
|
||||
* @Conversion()
|
||||
* public class ConversionAction implements Action {
|
||||
*
|
||||
* private String convertInt;
|
||||
*
|
||||
* private String convertDouble;
|
||||
* private List users = null;
|
||||
*
|
||||
* private HashMap keyValues = null;
|
||||
*
|
||||
* @TypeConversion(type = ConversionType.APPLICATION, converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* public void setConvertInt( String convertInt ) {
|
||||
* this.convertInt = convertInt;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* public void setConvertDouble( String convertDouble ) {
|
||||
* this.convertDouble = convertDouble;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(rule = ConversionRule.COLLECTION, converter = "java.util.String")
|
||||
* public void setUsers( List users ) {
|
||||
* this.users = users;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(rule = ConversionRule.MAP, converter = "java.math.BigInteger")
|
||||
* public void setKeyValues( HashMap keyValues ) {
|
||||
* this.keyValues = keyValues;
|
||||
* }
|
||||
*
|
||||
* @TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converter = "com.opensymphony.xwork2.util.XWorkBasicConverter")
|
||||
* public String execute() throws Exception {
|
||||
* return SUCCESS;
|
||||
* }
|
||||
* }
|
||||
* <!-- END SNIPPET: example -->
|
||||
* </pre>
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Id$
|
||||
*/
|
||||
@Target({ ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TypeConversion {
|
||||
|
||||
/**
|
||||
* The optional key name used within TYPE level annotations.
|
||||
* Defaults to the property name.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* The ConversionType can be either APPLICATION or CLASS.
|
||||
* Defaults to CLASS.
|
||||
*
|
||||
* Note: If you use ConversionType.APPLICATION, you can not set a value!
|
||||
*/
|
||||
ConversionType type() default ConversionType.CLASS;
|
||||
|
||||
/**
|
||||
* The ConversionRule can be a PROPERTY, KEY, KEY_PROPERTY, ELEMENT, COLLECTION (deprecated) or a MAP.
|
||||
* Note: Collection and Map vonversion rules can be determined via com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer.
|
||||
*
|
||||
* @see com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer
|
||||
*/
|
||||
ConversionRule rule() default ConversionRule.PROPERTY;
|
||||
|
||||
/**
|
||||
* The class of the TypeConverter to be used as converter.
|
||||
*
|
||||
* Note: This can not be used with ConversionRule.KEY_PROPERTY!
|
||||
*/
|
||||
String converter() default "";
|
||||
|
||||
/**
|
||||
* If used with ConversionRule.KEY_PROPERTY specify a value here!
|
||||
*
|
||||
* Note: If you use ConversionType.APPLICATION, you can not set a value!
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<body>Type conversion annotations.</body>
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* Type conversion is great for situations where you need to turn a String in to a more complex object. Because the web
|
||||
* is type-agnostic (everything is a string in HTTP), XWork's type conversion features are very useful. For instance,
|
||||
* if you were prompting a user to enter in coordinates in the form of a string (such as "3, 22"), you could have
|
||||
* XWork do the conversion both from String to Point and from Point to String.
|
||||
* <p/>
|
||||
* <p/> Using this "point" example, if your action (or another compound object in which you are setting properties on)
|
||||
* has a corresponding ClassName-conversion.properties file, XWork will use the configured type converters for
|
||||
* conversion to and from strings. So turning "3, 22" in to new Point(3, 22) is done by merely adding the following
|
||||
* entry to <b>ClassName-conversion.properties</b> (Note that the PointConverter should impl the TypeConverter
|
||||
* interface):
|
||||
* <p/>
|
||||
* <p/><b>point = com.acme.PointConverter</b>
|
||||
* <p/>
|
||||
* <p/> Your type converter should be sure to check what class type it is being requested to convert. Because it is used
|
||||
* for both to and from strings, you will need to split the conversion method in to two parts: one that turns Strings in
|
||||
* to Points, and one that turns Points in to Strings.
|
||||
* <p/>
|
||||
* <p/> After this is done, you can now reference your point (using <ww:property value="post"/> in JSP or ${point}
|
||||
* in FreeMarker) and it will be printed as "3, 22" again. As such, if you submit this back to an action, it will be
|
||||
* converted back to a Point once again.
|
||||
* <p/>
|
||||
* <p/> In some situations you may wish to apply a type converter globally. This can be done by editing the file
|
||||
* <b>xwork-conversion.properties</b> in the root of your class path (typically WEB-INF/classes) and providing a
|
||||
* property in the form of the class name of the object you wish to convert on the left hand side and the class name of
|
||||
* the type converter on the right hand side. For example, providing a type converter for all Point objects would mean
|
||||
* adding the following entry:
|
||||
* <p/>
|
||||
* <p/><b>com.acme.Point = com.acme.PointConverter</b>
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <!-- START SNIPPET: i18n-note -->
|
||||
* <p/>
|
||||
* Type conversion should not be used as a substitute for i18n. It is not recommended to use this feature to print out
|
||||
* properly formatted dates. Rather, you should use the i18n features of XWork (and consult the JavaDocs for JDK's
|
||||
* MessageFormat object) to see how a properly formatted date should be displayed.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: i18n-note -->
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <!-- START SNIPPET: error-reporting -->
|
||||
* <p/>
|
||||
* Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the
|
||||
* input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string,
|
||||
* "", cannot be converted to a number might not be important - especially in a web environment where it is hard to
|
||||
* distinguish between a user not entering a value vs. entering a blank value.
|
||||
* <p/>
|
||||
* <p/> By default, all conversion errors are reported using the generic i18n key <b>xwork.default.invalid.fieldvalue</b>,
|
||||
* which you can override (the default text is <i>Invalid field value for field "xxx"</i>, where xxx is the field name)
|
||||
* in your global i18n resource bundle.
|
||||
* <p/>
|
||||
* <p/>However, sometimes you may wish to override this message on a per-field basis. You can do this by adding an i18n
|
||||
* key associated with just your action (Action.properties) using the pattern <b>invalid.fieldvalue.xxx</b>, where xxx
|
||||
* is the field name.
|
||||
* <p/>
|
||||
* <p/>It is important to know that none of these errors are actually reported directly. Rather, they are added to a map
|
||||
* called <i>conversionErrors</i> in the ActionContext. There are several ways this map can then be accessed and the
|
||||
* errors can be reported accordingly.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: error-reporting -->
|
||||
*
|
||||
* @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
|
||||
* @author Rainer Hermanns
|
||||
* @see com.opensymphony.xwork2.conversion.impl.XWorkConverter
|
||||
* @deprecated Since XWork 2.0.4, the implementation of XWorkConverter handles the processing of annotations.
|
||||
*/
|
||||
@Deprecated public class AnnotationXWorkConverter extends XWorkConverter {
|
||||
}
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.CreateIfNull;
|
||||
import com.opensymphony.xwork2.util.Element;
|
||||
import com.opensymphony.xwork2.util.Key;
|
||||
import com.opensymphony.xwork2.util.KeyProperty;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionException;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
*
|
||||
* This {@link ObjectTypeDeterminer} looks at the <b>Class-conversion.properties</b> for entries that indicated what
|
||||
* objects are contained within Maps and Collections. For Collections, such as Lists, the element is specified using the
|
||||
* pattern <b>Element_xxx</b>, where xxx is the field name of the collection property in your action or object. For
|
||||
* Maps, both the key and the value may be specified by using the pattern <b>Key_xxx</b> and <b>Element_xxx</b>,
|
||||
* respectively.
|
||||
*
|
||||
* <p/> From WebWork 2.1.x, the <b>Collection_xxx</b> format is still supported and honored, although it is deprecated
|
||||
* and will be removed eventually.
|
||||
*
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
*
|
||||
* @author Gabriel Zimmerman
|
||||
*/
|
||||
public class DefaultObjectTypeDeterminer implements ObjectTypeDeterminer {
|
||||
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(DefaultObjectTypeDeterminer.class);
|
||||
|
||||
public static final String KEY_PREFIX = "Key_";
|
||||
public static final String ELEMENT_PREFIX = "Element_";
|
||||
public static final String KEY_PROPERTY_PREFIX = "KeyProperty_";
|
||||
public static final String CREATE_IF_NULL_PREFIX = "CreateIfNull_";
|
||||
public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
|
||||
|
||||
private ReflectionProvider reflectionProvider;
|
||||
private XWorkConverter xworkConverter;
|
||||
|
||||
@Inject
|
||||
public DefaultObjectTypeDeterminer(@Inject XWorkConverter conv, @Inject ReflectionProvider prov) {
|
||||
this.reflectionProvider = prov;
|
||||
this.xworkConverter = conv;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the key class by looking for the value of @Key annotation for the given class.
|
||||
* If no annotation is found, the key class is determined by using the generic parametrics.
|
||||
*
|
||||
* As fallback, it determines the key class by looking for the value of Key_${property} in the properties
|
||||
* file for the given class.
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @see com.opensymphony.xwork2.conversion.ObjectTypeDeterminer#getKeyClass(Class, String)
|
||||
*/
|
||||
public Class getKeyClass(Class parentClass, String property) {
|
||||
Key annotation = getAnnotation(parentClass, property, Key.class);
|
||||
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
|
||||
Class clazz = getClass(parentClass, property, false);
|
||||
|
||||
if (clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
return (Class) xworkConverter.getConverter(parentClass, KEY_PREFIX + property);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the element class by looking for the value of @Element annotation for the given
|
||||
* class.
|
||||
* If no annotation is found, the element class is determined by using the generic parametrics.
|
||||
*
|
||||
* As fallback, it determines the key class by looking for the value of Element_${property} in the properties
|
||||
* file for the given class. Also looks for the deprecated Collection_${property}
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @see com.opensymphony.xwork2.conversion.ObjectTypeDeterminer#getElementClass(Class, String, Object)
|
||||
*/
|
||||
public Class getElementClass(Class parentClass, String property, Object key) {
|
||||
Element annotation = getAnnotation(parentClass, property, Element.class);
|
||||
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
|
||||
Class clazz = getClass(parentClass, property, true);
|
||||
|
||||
if (clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
clazz = (Class) xworkConverter.getConverter(parentClass, ELEMENT_PREFIX + property);
|
||||
|
||||
if (clazz == null) {
|
||||
clazz = (Class) xworkConverter
|
||||
.getConverter(parentClass, DEPRECATED_ELEMENT_PREFIX + property);
|
||||
|
||||
if (clazz != null) {
|
||||
LOG.info("The Collection_xxx pattern for collection type conversion is deprecated. Please use Element_xxx!");
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the key property for a Collection by getting it from the @KeyProperty annotation.
|
||||
*
|
||||
* As fallback, it determines the String key property for a Collection by getting it from the conversion properties
|
||||
* file using the KeyProperty_ prefix. KeyProperty_${property}=somePropertyOfBeansInTheSet
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @see com.opensymphony.xwork2.conversion.ObjectTypeDeterminer#getKeyProperty(Class, String)
|
||||
*/
|
||||
public String getKeyProperty(Class parentClass, String property) {
|
||||
KeyProperty annotation = getAnnotation(parentClass, property, KeyProperty.class);
|
||||
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
|
||||
return (String) xworkConverter.getConverter(parentClass, KEY_PROPERTY_PREFIX + property);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the createIfNull property for a Collection or Map by getting it from the @CreateIfNull annotation.
|
||||
*
|
||||
* As fallback, it determines the boolean CreateIfNull property for a Collection or Map by getting it from the
|
||||
* conversion properties file using the CreateIfNull_ prefix. CreateIfNull_${property}=true|false
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @param target the target object
|
||||
* @param keyProperty the keyProperty value
|
||||
* @param isIndexAccessed <tt>true</tt>, if the collection or map is accessed via index, <tt>false</tt> otherwise.
|
||||
* @return <tt>true</tt>, if the Collection or Map should be created, <tt>false</tt> otherwise.
|
||||
* @see ObjectTypeDeterminer#getKeyProperty(Class, String)
|
||||
*/
|
||||
public boolean shouldCreateIfNew(Class parentClass,
|
||||
String property,
|
||||
Object target,
|
||||
String keyProperty,
|
||||
boolean isIndexAccessed) {
|
||||
|
||||
CreateIfNull annotation = getAnnotation(parentClass, property, CreateIfNull.class);
|
||||
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
|
||||
String configValue = (String) xworkConverter.getConverter(parentClass, CREATE_IF_NULL_PREFIX + property);
|
||||
//check if a value is in the config
|
||||
if (configValue!=null) {
|
||||
if ("true".equalsIgnoreCase(configValue)) {
|
||||
return true;
|
||||
}
|
||||
if ("false".equalsIgnoreCase(configValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//default values depend on target type
|
||||
//and whether this is accessed by an index
|
||||
//in the case of List
|
||||
if ((target instanceof Map) || isIndexAccessed) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an annotation for the specified property of field, setter or getter.
|
||||
*
|
||||
* @param <T> the annotation type to be retrieved
|
||||
* @param parentClass the class
|
||||
* @param property the property
|
||||
* @param annotationClass the annotation
|
||||
* @return the field or setter/getter annotation or <code>null</code> if not found
|
||||
*/
|
||||
protected <T extends Annotation> T getAnnotation(Class parentClass, String property, Class<T> annotationClass) {
|
||||
T annotation = null;
|
||||
Field field = reflectionProvider.getField(parentClass, property);
|
||||
|
||||
if (field != null) {
|
||||
annotation = field.getAnnotation(annotationClass);
|
||||
}
|
||||
if (annotation == null) { // HINT: try with setter
|
||||
annotation = getAnnotationFromSetter(parentClass, property, annotationClass);
|
||||
}
|
||||
if (annotation == null) { // HINT: try with getter
|
||||
annotation = getAnnotationFromGetter(parentClass, property, annotationClass);
|
||||
}
|
||||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an annotation for the specified field of getter.
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @param annotationClass The annotation
|
||||
* @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
|
||||
*/
|
||||
private <T extends Annotation>T getAnnotationFromGetter(Class parentClass, String property, Class<T> annotationClass) {
|
||||
try {
|
||||
Method getter = reflectionProvider.getGetMethod(parentClass, property);
|
||||
|
||||
if (getter != null) {
|
||||
return getter.getAnnotation(annotationClass);
|
||||
}
|
||||
}
|
||||
catch (ReflectionException ognle) {
|
||||
; // ignore
|
||||
}
|
||||
catch (IntrospectionException ie) {
|
||||
; // ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an annotation for the specified field of setter.
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @param annotationClass The annotation
|
||||
* @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
|
||||
*/
|
||||
private <T extends Annotation>T getAnnotationFromSetter(Class parentClass, String property, Class<T> annotationClass) {
|
||||
try {
|
||||
Method setter = reflectionProvider.getSetMethod(parentClass, property);
|
||||
|
||||
if (setter != null) {
|
||||
return setter.getAnnotation(annotationClass);
|
||||
}
|
||||
}
|
||||
catch (ReflectionException ognle) {
|
||||
; // ignore
|
||||
}
|
||||
catch (IntrospectionException ie) {
|
||||
; // ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class for the given field via generic type check.
|
||||
*
|
||||
* @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
|
||||
* @param property the property of the Map or Collection for the given parent class
|
||||
* @param element <tt>true</tt> for indexed types and Maps.
|
||||
* @return Class of the specified field.
|
||||
*/
|
||||
private Class getClass(Class parentClass, String property, boolean element) {
|
||||
|
||||
|
||||
try {
|
||||
|
||||
Field field = reflectionProvider.getField(parentClass, property);
|
||||
|
||||
Type genericType = null;
|
||||
|
||||
// Check fields first
|
||||
if (field != null) {
|
||||
genericType = field.getGenericType();
|
||||
}
|
||||
|
||||
// Try to get ParameterType from setter method
|
||||
if (genericType == null || !(genericType instanceof ParameterizedType)) {
|
||||
try {
|
||||
Method setter = reflectionProvider.getSetMethod(parentClass, property);
|
||||
genericType = setter.getGenericParameterTypes()[0];
|
||||
}
|
||||
catch (ReflectionException ognle) {
|
||||
; // ignore
|
||||
}
|
||||
catch (IntrospectionException ie) {
|
||||
; // ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get ReturnType from getter method
|
||||
if (genericType == null || !(genericType instanceof ParameterizedType)) {
|
||||
try {
|
||||
Method getter = reflectionProvider.getGetMethod(parentClass, property);
|
||||
genericType = getter.getGenericReturnType();
|
||||
}
|
||||
catch (ReflectionException ognle) {
|
||||
; // ignore
|
||||
}
|
||||
catch (IntrospectionException ie) {
|
||||
; // ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
|
||||
|
||||
ParameterizedType type = (ParameterizedType) genericType;
|
||||
|
||||
int index = (element && type.getRawType().toString().contains(Map.class.getName())) ? 1 : 0;
|
||||
|
||||
Type resultType = type.getActualTypeArguments()[index];
|
||||
|
||||
if ( resultType instanceof ParameterizedType) {
|
||||
return (Class) ((ParameterizedType) resultType).getRawType();
|
||||
}
|
||||
return (Class) resultType;
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if ( LOG.isDebugEnabled()) {
|
||||
LOG.debug("Error while retrieving generic property class for property=" + property, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
//--------------------------------------------------------------------------
|
||||
// Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// Neither the name of the Drew Davidson nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
// DAMAGE.
|
||||
//--------------------------------------------------------------------------
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Member;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default type conversion. Converts among numeric types and also strings. Contains the basic
|
||||
* type mapping code from OGNL.
|
||||
*
|
||||
* @author Luke Blanshard (blanshlu@netscape.net)
|
||||
* @author Drew Davidson (drew@ognl.org)
|
||||
*/
|
||||
public class DefaultTypeConverter implements TypeConverter {
|
||||
private static final String NULL_STRING = "null";
|
||||
|
||||
private final Map<Class, Object> primitiveDefaults;
|
||||
|
||||
public DefaultTypeConverter() {
|
||||
Map<Class, Object> map = new HashMap<Class, Object>();
|
||||
map.put(Boolean.TYPE, Boolean.FALSE);
|
||||
map.put(Byte.TYPE, Byte.valueOf((byte) 0));
|
||||
map.put(Short.TYPE, Short.valueOf((short) 0));
|
||||
map.put(Character.TYPE, new Character((char) 0));
|
||||
map.put(Integer.TYPE, Integer.valueOf(0));
|
||||
map.put(Long.TYPE, Long.valueOf(0L));
|
||||
map.put(Float.TYPE, new Float(0.0f));
|
||||
map.put(Double.TYPE, new Double(0.0));
|
||||
map.put(BigInteger.class, new BigInteger("0"));
|
||||
map.put(BigDecimal.class, new BigDecimal(0.0));
|
||||
primitiveDefaults = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
|
||||
return convertValue(value, toType);
|
||||
}
|
||||
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member,
|
||||
String propertyName, Object value, Class toType) {
|
||||
return convertValue(context, value, toType);
|
||||
}
|
||||
|
||||
public TypeConverter getTypeConverter( Map<String, Object> context )
|
||||
{
|
||||
Object obj = context.get(TypeConverter.TYPE_CONVERTER_CONTEXT_KEY);
|
||||
if (obj instanceof TypeConverter) {
|
||||
return (TypeConverter) obj;
|
||||
|
||||
// for backwards-compatibility
|
||||
} else if (obj instanceof ognl.TypeConverter) {
|
||||
return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value converted numerically to the given class type
|
||||
*
|
||||
* This method also detects when arrays are being converted and converts the
|
||||
* components of one array to the type of the other.
|
||||
*
|
||||
* @param value
|
||||
* an object to be converted to the given type
|
||||
* @param toType
|
||||
* class type to be converted to
|
||||
* @return converted value of the type given, or value if the value cannot
|
||||
* be converted to the given type.
|
||||
*/
|
||||
public Object convertValue(Object value, Class toType) {
|
||||
Object result = null;
|
||||
|
||||
if (value != null) {
|
||||
/* If array -> array then convert components of array individually */
|
||||
if (value.getClass().isArray() && toType.isArray()) {
|
||||
Class componentType = toType.getComponentType();
|
||||
|
||||
result = Array.newInstance(componentType, Array
|
||||
.getLength(value));
|
||||
for (int i = 0, icount = Array.getLength(value); i < icount; i++) {
|
||||
Array.set(result, i, convertValue(Array.get(value, i),
|
||||
componentType));
|
||||
}
|
||||
} else {
|
||||
if ((toType == Integer.class) || (toType == Integer.TYPE))
|
||||
result = Integer.valueOf((int) longValue(value));
|
||||
if ((toType == Double.class) || (toType == Double.TYPE))
|
||||
result = new Double(doubleValue(value));
|
||||
if ((toType == Boolean.class) || (toType == Boolean.TYPE))
|
||||
result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
|
||||
if ((toType == Byte.class) || (toType == Byte.TYPE))
|
||||
result = Byte.valueOf((byte) longValue(value));
|
||||
if ((toType == Character.class) || (toType == Character.TYPE))
|
||||
result = new Character((char) longValue(value));
|
||||
if ((toType == Short.class) || (toType == Short.TYPE))
|
||||
result = Short.valueOf((short) longValue(value));
|
||||
if ((toType == Long.class) || (toType == Long.TYPE))
|
||||
result = Long.valueOf(longValue(value));
|
||||
if ((toType == Float.class) || (toType == Float.TYPE))
|
||||
result = new Float(doubleValue(value));
|
||||
if (toType == BigInteger.class)
|
||||
result = bigIntValue(value);
|
||||
if (toType == BigDecimal.class)
|
||||
result = bigDecValue(value);
|
||||
if (toType == String.class)
|
||||
result = stringValue(value);
|
||||
if (Enum.class.isAssignableFrom(toType))
|
||||
result = enumValue((Class<Enum>)toType, value);
|
||||
}
|
||||
} else {
|
||||
if (toType.isPrimitive()) {
|
||||
result = primitiveDefaults.get(toType);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a boolean: if it is a Boolean object, it's
|
||||
* easy; if it's a Number or a Character, returns true for non-zero objects;
|
||||
* and otherwise returns true for non-null objects.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a boolean
|
||||
* @return the boolean value implied by the given object
|
||||
*/
|
||||
public static boolean booleanValue(Object value) {
|
||||
if (value == null)
|
||||
return false;
|
||||
Class c = value.getClass();
|
||||
if (c == Boolean.class)
|
||||
return ((Boolean) value).booleanValue();
|
||||
// if ( c == String.class )
|
||||
// return ((String)value).length() > 0;
|
||||
if (c == Character.class)
|
||||
return ((Character) value).charValue() != 0;
|
||||
if (value instanceof Number)
|
||||
return ((Number) value).doubleValue() != 0;
|
||||
return true; // non-null
|
||||
}
|
||||
|
||||
public Enum<?> enumValue(Class toClass, Object o) {
|
||||
Enum<?> result = null;
|
||||
if (o == null) {
|
||||
result = null;
|
||||
} else if (o instanceof String[]) {
|
||||
result = Enum.valueOf(toClass, ((String[]) o)[0]);
|
||||
} else if (o instanceof String) {
|
||||
result = Enum.valueOf(toClass, (String) o);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a long integer.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a long integer
|
||||
* @return the long integer value implied by the given object
|
||||
* @throws NumberFormatException
|
||||
* if the given object can't be understood as a long integer
|
||||
*/
|
||||
public static long longValue(Object value) throws NumberFormatException {
|
||||
if (value == null)
|
||||
return 0L;
|
||||
Class c = value.getClass();
|
||||
if (c.getSuperclass() == Number.class)
|
||||
return ((Number) value).longValue();
|
||||
if (c == Boolean.class)
|
||||
return ((Boolean) value).booleanValue() ? 1 : 0;
|
||||
if (c == Character.class)
|
||||
return ((Character) value).charValue();
|
||||
return Long.parseLong(stringValue(value, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a double-precision floating-point number.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a double
|
||||
* @return the double value implied by the given object
|
||||
* @throws NumberFormatException
|
||||
* if the given object can't be understood as a double
|
||||
*/
|
||||
public static double doubleValue(Object value) throws NumberFormatException {
|
||||
if (value == null)
|
||||
return 0.0;
|
||||
Class c = value.getClass();
|
||||
if (c.getSuperclass() == Number.class)
|
||||
return ((Number) value).doubleValue();
|
||||
if (c == Boolean.class)
|
||||
return ((Boolean) value).booleanValue() ? 1 : 0;
|
||||
if (c == Character.class)
|
||||
return ((Character) value).charValue();
|
||||
String s = stringValue(value, true);
|
||||
|
||||
return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
|
||||
/*
|
||||
* For 1.1 parseDouble() is not available
|
||||
*/
|
||||
// return Double.valueOf( value.toString() ).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a BigInteger.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a BigInteger
|
||||
* @return the BigInteger value implied by the given object
|
||||
* @throws NumberFormatException
|
||||
* if the given object can't be understood as a BigInteger
|
||||
*/
|
||||
public static BigInteger bigIntValue(Object value)
|
||||
throws NumberFormatException {
|
||||
if (value == null)
|
||||
return BigInteger.valueOf(0L);
|
||||
Class c = value.getClass();
|
||||
if (c == BigInteger.class)
|
||||
return (BigInteger) value;
|
||||
if (c == BigDecimal.class)
|
||||
return ((BigDecimal) value).toBigInteger();
|
||||
if (c.getSuperclass() == Number.class)
|
||||
return BigInteger.valueOf(((Number) value).longValue());
|
||||
if (c == Boolean.class)
|
||||
return BigInteger.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
|
||||
if (c == Character.class)
|
||||
return BigInteger.valueOf(((Character) value).charValue());
|
||||
return new BigInteger(stringValue(value, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a BigDecimal.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a BigDecimal
|
||||
* @return the BigDecimal value implied by the given object
|
||||
* @throws NumberFormatException
|
||||
* if the given object can't be understood as a BigDecimal
|
||||
*/
|
||||
public static BigDecimal bigDecValue(Object value)
|
||||
throws NumberFormatException {
|
||||
if (value == null)
|
||||
return BigDecimal.valueOf(0L);
|
||||
Class c = value.getClass();
|
||||
if (c == BigDecimal.class)
|
||||
return (BigDecimal) value;
|
||||
if (c == BigInteger.class)
|
||||
return new BigDecimal((BigInteger) value);
|
||||
if (c.getSuperclass() == Number.class)
|
||||
return new BigDecimal(((Number) value).doubleValue());
|
||||
if (c == Boolean.class)
|
||||
return BigDecimal.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
|
||||
if (c == Character.class)
|
||||
return BigDecimal.valueOf(((Character) value).charValue());
|
||||
return new BigDecimal(stringValue(value, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a String and trims it if the trim flag is
|
||||
* true.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a String
|
||||
* @return the String value implied by the given object as returned by the
|
||||
* toString() method, or "null" if the object is null.
|
||||
*/
|
||||
public static String stringValue(Object value, boolean trim) {
|
||||
String result;
|
||||
|
||||
if (value == null) {
|
||||
result = NULL_STRING;
|
||||
} else {
|
||||
result = value.toString();
|
||||
if (trim) {
|
||||
result = result.trim();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given object as a String.
|
||||
*
|
||||
* @param value
|
||||
* an object to interpret as a String
|
||||
* @return the String value implied by the given object as returned by the
|
||||
* toString() method, or "null" if the object is null.
|
||||
*/
|
||||
public static String stringValue(Object value) {
|
||||
return stringValue(value, false);
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* <code>EnumTypeConverter</code>
|
||||
*
|
||||
* <!-- START SNIPPET: description -->
|
||||
* This class converts java 5 enums to String and from String[] to enum.
|
||||
* <p/>
|
||||
* One of Java 5's improvements is providing enumeration facility.
|
||||
* Up to now, there existed no enumerations. The only way to simulate was the so-called int Enum pattern:
|
||||
* {code}
|
||||
* public static final int SEASON_WINTER = 0;
|
||||
* public static final int SEASON_SPRING = 1;
|
||||
* public static final int SEASON_SUMMER = 2;
|
||||
* public static final int SEASON_FALL = 3;
|
||||
* {code}
|
||||
* <p/>
|
||||
* Java 5.0 now provides the following construct:
|
||||
* {code}
|
||||
* public static enum Season { WINTER, SPRING, SUMMER, FALL };
|
||||
* {code}
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <!-- START SNIPPET: example -->
|
||||
* h3. Implementing Java 5 Enumeration Type Conversion
|
||||
* <p/>
|
||||
* 1. myAction-conversion.properties*
|
||||
* <p/>
|
||||
* Place a myAction-conversion.properties-file in the path of your Action.
|
||||
* Add the following entry to the properties-file:
|
||||
* {code}
|
||||
* nameOfYourField=fullyClassifiedNameOfYourConverter
|
||||
* {code}
|
||||
*
|
||||
* <p/>
|
||||
* 2. myAction.java*
|
||||
* Your action contains the _enumeration_:
|
||||
* {code}
|
||||
* public enum Criticality {DEBUG, INFO, WARNING, ERROR, FATAL}
|
||||
* {code}
|
||||
*
|
||||
* * Your action contains the _private field_:
|
||||
* {code}
|
||||
* private myEnum myFieldForEnum;
|
||||
* {code}
|
||||
*
|
||||
* Your action contains _getters and setters_ for your field:
|
||||
* {code}
|
||||
* public myEnum getCriticality() {
|
||||
* return myFieldForEnum;
|
||||
* }
|
||||
*
|
||||
* public void setCriticality(myEnum myFieldForEnum) {
|
||||
* this.myFieldForEnum= myFieldForEnum;
|
||||
* }
|
||||
* {code}
|
||||
* <p/>
|
||||
* 3. JSP*
|
||||
* <p/>
|
||||
* In your jsp you can access an enumeration value just normal by using the known <ww:property>-Tag:
|
||||
* {code}
|
||||
* <ww:property value="myField"/>
|
||||
* {code}
|
||||
* <!-- END SNIPPET: example -->
|
||||
*
|
||||
* @author Tamara Cattivelli
|
||||
* @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
|
||||
* @version $Id$
|
||||
* @deprecated Since Struts 2.1.0 as enum support is now built into XWork
|
||||
*/
|
||||
@Deprecated public class EnumTypeConverter extends DefaultTypeConverter {
|
||||
|
||||
/**
|
||||
* Converts the given object to a given type. How this is to be done is implemented in toClass. The OGNL context, o
|
||||
* and toClass are given. This method should be able to handle conversion in general without any context or object
|
||||
* specified.
|
||||
*
|
||||
* @param context - OGNL context under which the conversion is being done
|
||||
* @param o - the object to be converted
|
||||
* @param toClass - the class that contains the code to convert to enumeration
|
||||
* @return Converted value of type declared in toClass or TypeConverter.NoConversionPossible to indicate that the
|
||||
* conversion was not possible.
|
||||
*/
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> context, Object o, Class toClass) {
|
||||
if (o instanceof String[]) {
|
||||
return convertFromString(((String[]) o)[0], toClass);
|
||||
} else if (o instanceof String) {
|
||||
return convertFromString((String) o, toClass);
|
||||
}
|
||||
|
||||
return super.convertValue(context, o, toClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one or more String values to the specified class.
|
||||
* @param value - the String values to be converted, such as those submitted from an HTML form
|
||||
* @param toClass - the class to convert to
|
||||
* @return the converted object
|
||||
*/
|
||||
public java.lang.Enum convertFromString(String value, Class toClass) {
|
||||
return Enum.valueOf(toClass, value);
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
|
||||
|
||||
/**
|
||||
* GenericsObjectTypeDeterminer
|
||||
*
|
||||
* @author Patrick Lightbody
|
||||
* @author Rainer Hermanns
|
||||
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
|
||||
*
|
||||
* @deprecated Use DefaultObjectTypeDeterminer instead. Since XWork 2.0.4 the DefaultObjectTypeDeterminer handles the
|
||||
* annotation processing.
|
||||
*/
|
||||
@Deprecated public class GenericsObjectTypeDeterminer extends DefaultObjectTypeDeterminer {
|
||||
|
||||
public GenericsObjectTypeDeterminer(XWorkConverter conv,
|
||||
XWorkBasicConverter basicConv, ReflectionProvider prov) {
|
||||
super(conv, prov);
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
*
|
||||
* Provided that the key {@link ReflectionContextState#CREATE_NULL_OBJECTS} is in the action context with a value of true (this key is set
|
||||
* only during the execution of the {@link com.opensymphony.xwork2.interceptor.ParametersInterceptor}), OGNL expressions
|
||||
* that have caused a NullPointerException will be temporarily stopped for evaluation while the system automatically
|
||||
* tries to solve the null references by automatically creating the object.
|
||||
*
|
||||
* <p/> The following rules are used when handling null references:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then an ArrayList shall be
|
||||
* returned and assigned to the null references.</li>
|
||||
*
|
||||
* <li>If the property is declared as a {@link Map}, then a HashMap will be returned and assigned to the null
|
||||
* references.</li>
|
||||
*
|
||||
* <li>If the null property is a simple bean with a no-arg constructor, it will simply be created using the {@link
|
||||
* ObjectFactory#buildBean(java.lang.Class, java.util.Map)} method.</li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
* <!-- START SNIPPET: example -->
|
||||
*
|
||||
* For example, if a form element has a text field named <b>person.name</b> and the expression <i>person</i> evaluates
|
||||
* to null, then this class will be invoked. Because the <i>person</i> expression evaluates to a <i>Person</i> class, a
|
||||
* new Person is created and assigned to the null reference. Finally, the name is set on that object and the overall
|
||||
* effect is that the system automatically created a Person object for you, set it by calling setUsers() and then
|
||||
* finally called getUsers().setName() as you would typically expect.
|
||||
*
|
||||
* <!-- END SNIPPET: example>
|
||||
*
|
||||
* @author Matt Ho
|
||||
* @author Patrick Lightbody
|
||||
*/
|
||||
public class InstantiatingNullHandler implements NullHandler {
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ReflectionContextState#CREATE_NULL_OBJECTS} instead
|
||||
*/
|
||||
@Deprecated public static final String CREATE_NULL_OBJECTS = ReflectionContextState.CREATE_NULL_OBJECTS;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InstantiatingNullHandler.class);
|
||||
private ReflectionProvider reflectionProvider;
|
||||
private ObjectFactory objectFactory;
|
||||
private ObjectTypeDeterminer objectTypeDeterminer;
|
||||
|
||||
@Inject
|
||||
public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
|
||||
this.objectTypeDeterminer = det;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setReflectionProvider(ReflectionProvider prov) {
|
||||
this.reflectionProvider = prov;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory fac) {
|
||||
this.objectFactory = fac;
|
||||
}
|
||||
|
||||
public Object nullMethodResult(Map<String, Object> context, Object target, String methodName, Object[] args) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Entering nullMethodResult ");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object nullPropertyValue(Map<String, Object> context, Object target, Object property) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Entering nullPropertyValue [target="+target+", property="+property+"]");
|
||||
}
|
||||
|
||||
boolean c = ReflectionContextState.isCreatingNullObjects(context);
|
||||
|
||||
if (!c) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((target == null) || (property == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
String propName = property.toString();
|
||||
Object realTarget = reflectionProvider.getRealTarget(propName, context, target);
|
||||
Class clazz = null;
|
||||
|
||||
if (realTarget != null) {
|
||||
PropertyDescriptor pd = reflectionProvider.getPropertyDescriptor(realTarget.getClass(), propName);
|
||||
if (pd == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
clazz = pd.getPropertyType();
|
||||
}
|
||||
|
||||
if (clazz == null) {
|
||||
// can't do much here!
|
||||
return null;
|
||||
}
|
||||
|
||||
Object param = createObject(clazz, realTarget, propName, context);
|
||||
|
||||
reflectionProvider.setValue(propName, context, realTarget, param);
|
||||
|
||||
return param;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Could not create and/or set value back on to object", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object createObject(Class clazz, Object target, String property, Map<String, Object> context) throws Exception {
|
||||
if (Collection.class.isAssignableFrom(clazz)) {
|
||||
return new ArrayList();
|
||||
} else if (clazz == Map.class) {
|
||||
return new HashMap();
|
||||
} else if (clazz == EnumMap.class) {
|
||||
Class keyClass = objectTypeDeterminer.getKeyClass(target.getClass(), property);
|
||||
return new EnumMap(keyClass);
|
||||
}
|
||||
|
||||
return objectFactory.buildBean(clazz, context);
|
||||
}
|
||||
}
|
||||
+583
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright 2002-2007,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.XWorkList;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Member;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* XWork will automatically handle the most common type conversion for you. This includes support for converting to
|
||||
* and from Strings for each of the following:
|
||||
* <p/>
|
||||
* <ul>
|
||||
* <li>String</li>
|
||||
* <li>boolean / Boolean</li>
|
||||
* <li>char / Character</li>
|
||||
* <li>int / Integer, float / Float, long / Long, double / Double</li>
|
||||
* <li>dates - uses the SHORT format for the Locale associated with the current request</li>
|
||||
* <li>arrays - assuming the individual strings can be coverted to the individual items</li>
|
||||
* <li>collections - if not object type can be determined, it is assumed to be a String and a new ArrayList is
|
||||
* created</li>
|
||||
* </ul>
|
||||
* <p/> Note that with arrays the type conversion will defer to the type of the array elements and try to convert each
|
||||
* item individually. As with any other type conversion, if the conversion can't be performed the standard type
|
||||
* conversion error reporting is used to indicate a problem occured while processing the type conversion.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
*
|
||||
* @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
|
||||
* @author Mike Mosiewicz
|
||||
* @author Rainer Hermanns
|
||||
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
|
||||
*/
|
||||
public class XWorkBasicConverter extends DefaultTypeConverter {
|
||||
|
||||
private static String MILLISECOND_FORMAT = ".SSS";
|
||||
|
||||
private ObjectTypeDeterminer objectTypeDeterminer;
|
||||
private XWorkConverter xworkConverter;
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
@Inject
|
||||
public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
|
||||
this.objectTypeDeterminer = det;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setXWorkConverter(XWorkConverter conv) {
|
||||
this.xworkConverter = conv;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory fac) {
|
||||
this.objectFactory = fac;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
|
||||
Object result = null;
|
||||
|
||||
if (value == null || toType.isAssignableFrom(value.getClass())) {
|
||||
// no need to convert at all, right?
|
||||
return value;
|
||||
}
|
||||
|
||||
if (toType == String.class) {
|
||||
/* the code below has been disabled as it causes sideffects in Struts2 (XW-512)
|
||||
// if input (value) is a number then use special conversion method (XW-490)
|
||||
Class inputType = value.getClass();
|
||||
if (Number.class.isAssignableFrom(inputType)) {
|
||||
result = doConvertFromNumberToString(context, value, inputType);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}*/
|
||||
// okay use default string conversion
|
||||
result = doConvertToString(context, value);
|
||||
} else if (toType == boolean.class) {
|
||||
result = doConvertToBoolean(value);
|
||||
} else if (toType == Boolean.class) {
|
||||
result = doConvertToBoolean(value);
|
||||
} else if (toType.isArray()) {
|
||||
result = doConvertToArray(context, o, member, s, value, toType);
|
||||
} else if (Date.class.isAssignableFrom(toType)) {
|
||||
result = doConvertToDate(context, value, toType);
|
||||
} else if (Calendar.class.isAssignableFrom(toType)) {
|
||||
Date dateResult = (Date) doConvertToDate(context, value, Date.class);
|
||||
if (dateResult != null) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(dateResult);
|
||||
result = calendar;
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(toType)) {
|
||||
result = doConvertToCollection(context, o, member, s, value, toType);
|
||||
} else if (toType == Character.class) {
|
||||
result = doConvertToCharacter(value);
|
||||
} else if (toType == char.class) {
|
||||
result = doConvertToCharacter(value);
|
||||
} else if (Number.class.isAssignableFrom(toType) || toType.isPrimitive()) {
|
||||
result = doConvertToNumber(context, value, toType);
|
||||
} else if (toType == Class.class) {
|
||||
result = doConvertToClass(value);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
if (value instanceof Object[]) {
|
||||
Object[] array = (Object[]) value;
|
||||
|
||||
if (array.length >= 1) {
|
||||
value = array[0];
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
|
||||
// let's try to convert the first element only
|
||||
result = convertValue(context, o, member, s, value, toType);
|
||||
} else if (!"".equals(value)) { // we've already tried the types we know
|
||||
result = super.convertValue(context, value, toType);
|
||||
}
|
||||
|
||||
if (result == null && value != null && !"".equals(value)) {
|
||||
throw new XWorkException("Cannot create type " + toType + " from value " + value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Locale getLocale(Map<String, Object> context) {
|
||||
if (context == null) {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
Locale locale = (Locale) context.get(ActionContext.LOCALE);
|
||||
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Collection of the specified type.
|
||||
*
|
||||
* @param fromObject
|
||||
* @param propertyName
|
||||
* @param toType the type of Collection to create
|
||||
* @param memberType the type of object elements in this collection must be
|
||||
* @param size the initial size of the collection (ignored if 0 or less)
|
||||
* @return a Collection of the specified type
|
||||
*/
|
||||
private Collection createCollection(Object fromObject, String propertyName, Class toType, Class memberType, int size) {
|
||||
// try {
|
||||
// Object original = Ognl.getValue(OgnlUtil.compile(propertyName),fromObject);
|
||||
// if (original instanceof Collection) {
|
||||
// Collection coll = (Collection) original;
|
||||
// coll.clear();
|
||||
// return coll;
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// // fail back to creating a new one
|
||||
// }
|
||||
|
||||
Collection result;
|
||||
|
||||
if (toType == Set.class) {
|
||||
if (size > 0) {
|
||||
result = new HashSet(size);
|
||||
} else {
|
||||
result = new HashSet();
|
||||
}
|
||||
} else if (toType == SortedSet.class) {
|
||||
result = new TreeSet();
|
||||
} else {
|
||||
if (size > 0) {
|
||||
result = new XWorkList(objectFactory, xworkConverter, memberType, size);
|
||||
} else {
|
||||
result = new XWorkList(objectFactory, xworkConverter, memberType);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object doConvertToArray(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
|
||||
Object result = null;
|
||||
Class componentType = toType.getComponentType();
|
||||
|
||||
if (componentType != null) {
|
||||
TypeConverter converter = getTypeConverter(context);
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
int length = Array.getLength(value);
|
||||
result = Array.newInstance(componentType, length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object valueItem = Array.get(value, i);
|
||||
Array.set(result, i, converter.convertValue(context, o, member, s, valueItem, componentType));
|
||||
}
|
||||
} else {
|
||||
result = Array.newInstance(componentType, 1);
|
||||
Array.set(result, 0, converter.convertValue(context, o, member, s, value, componentType));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object doConvertToCharacter(Object value) {
|
||||
if (value instanceof String) {
|
||||
String cStr = (String) value;
|
||||
|
||||
return (cStr.length() > 0) ? new Character(cStr.charAt(0)) : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object doConvertToBoolean(Object value) {
|
||||
if (value instanceof String) {
|
||||
String bStr = (String) value;
|
||||
|
||||
return Boolean.valueOf(bStr);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class doConvertToClass(Object value) {
|
||||
Class clazz = null;
|
||||
|
||||
if (value instanceof String && value != null && ((String) value).length() > 0) {
|
||||
try {
|
||||
clazz = Class.forName((String) value);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new XWorkException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private Collection doConvertToCollection(Map<String, Object> context, Object o, Member member, String prop, Object value, Class toType) {
|
||||
Collection result;
|
||||
Class memberType = String.class;
|
||||
|
||||
if (o != null) {
|
||||
//memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop);
|
||||
memberType = objectTypeDeterminer.getElementClass(o.getClass(), prop, null);
|
||||
|
||||
if (memberType == null) {
|
||||
memberType = String.class;
|
||||
}
|
||||
}
|
||||
|
||||
if (toType.isAssignableFrom(value.getClass())) {
|
||||
// no need to do anything
|
||||
result = (Collection) value;
|
||||
} else if (value.getClass().isArray()) {
|
||||
Object[] objArray = (Object[]) value;
|
||||
TypeConverter converter = getTypeConverter(context);
|
||||
result = createCollection(o, prop, toType, memberType, objArray.length);
|
||||
|
||||
for (Object anObjArray : objArray) {
|
||||
result.add(converter.convertValue(context, o, member, prop, anObjArray, memberType));
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(value.getClass())) {
|
||||
Collection col = (Collection) value;
|
||||
TypeConverter converter = getTypeConverter(context);
|
||||
result = createCollection(o, prop, toType, memberType, col.size());
|
||||
|
||||
for (Object aCol : col) {
|
||||
result.add(converter.convertValue(context, o, member, prop, aCol, memberType));
|
||||
}
|
||||
} else {
|
||||
result = createCollection(o, prop, toType, memberType, -1);
|
||||
result.add(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object doConvertToDate(Map<String, Object> context, Object value, Class toType) {
|
||||
Date result = null;
|
||||
|
||||
if (value instanceof String && value != null && ((String) value).length() > 0) {
|
||||
String sa = (String) value;
|
||||
Locale locale = getLocale(context);
|
||||
|
||||
DateFormat df = null;
|
||||
if (java.sql.Time.class == toType) {
|
||||
df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
|
||||
} else if (java.sql.Timestamp.class == toType) {
|
||||
Date check = null;
|
||||
SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
|
||||
DateFormat.MEDIUM,
|
||||
locale);
|
||||
SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
|
||||
locale);
|
||||
|
||||
SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
|
||||
locale);
|
||||
|
||||
SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
|
||||
for (SimpleDateFormat fmt : fmts) {
|
||||
try {
|
||||
check = fmt.parse(sa);
|
||||
df = fmt;
|
||||
if (check != null) {
|
||||
break;
|
||||
}
|
||||
} catch (ParseException ignore) {
|
||||
}
|
||||
}
|
||||
} else if (java.util.Date.class == toType) {
|
||||
Date check = null;
|
||||
DateFormat[] dfs = getDateFormats(locale);
|
||||
for (DateFormat df1 : dfs) {
|
||||
try {
|
||||
check = df1.parse(sa);
|
||||
df = df1;
|
||||
if (check != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ParseException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
//final fallback for dates without time
|
||||
if (df == null) {
|
||||
df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
|
||||
}
|
||||
try {
|
||||
df.setLenient(false); // let's use strict parsing (XW-341)
|
||||
result = df.parse(sa);
|
||||
if (!(Date.class == toType)) {
|
||||
try {
|
||||
Constructor constructor = toType.getConstructor(new Class[]{long.class});
|
||||
return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())});
|
||||
} catch (Exception e) {
|
||||
throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new XWorkException("Could not parse date", e);
|
||||
}
|
||||
} else if (Date.class.isAssignableFrom(value.getClass())) {
|
||||
result = (Date) value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateFormat[] getDateFormats(Locale locale) {
|
||||
DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
|
||||
DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
|
||||
DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
|
||||
|
||||
DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale);
|
||||
DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
|
||||
DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);
|
||||
|
||||
DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
|
||||
DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added RFC 3339 date format (XW-473)
|
||||
return dfs;
|
||||
}
|
||||
|
||||
private Object doConvertToNumber(Map<String, Object> context, Object value, Class toType) {
|
||||
if (value instanceof String) {
|
||||
if (toType == BigDecimal.class) {
|
||||
return new BigDecimal((String) value);
|
||||
} else if (toType == BigInteger.class) {
|
||||
return new BigInteger((String) value);
|
||||
} else if (toType.isPrimitive()) {
|
||||
Object convertedValue = super.convertValue(context, value, toType);
|
||||
String stringValue = (String) value;
|
||||
if (!isInRange((Number)convertedValue, stringValue, toType))
|
||||
throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName());
|
||||
|
||||
return convertedValue;
|
||||
} else {
|
||||
String stringValue = (String) value;
|
||||
if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
|
||||
return null;
|
||||
}
|
||||
NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
|
||||
ParsePosition parsePos = new ParsePosition(0);
|
||||
if (isIntegerType(toType)) {
|
||||
numFormat.setParseIntegerOnly(true);
|
||||
}
|
||||
numFormat.setGroupingUsed(true);
|
||||
Number number = numFormat.parse(stringValue, parsePos);
|
||||
|
||||
if (parsePos.getIndex() != stringValue.length()) {
|
||||
throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position "
|
||||
+ parsePos.getIndex());
|
||||
} else {
|
||||
if (!isInRange(number, stringValue, toType))
|
||||
throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + number.getClass().getName());
|
||||
|
||||
value = super.convertValue(context, number, toType);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof Object[]) {
|
||||
Object[] objArray = (Object[]) value;
|
||||
|
||||
if (objArray.length == 1) {
|
||||
return doConvertToNumber(context, objArray[0], toType);
|
||||
}
|
||||
}
|
||||
|
||||
// pass it through DefaultTypeConverter
|
||||
return super.convertValue(context, value, toType);
|
||||
}
|
||||
|
||||
protected boolean isInRange(Number value, String stringValue, Class toType) {
|
||||
Number bigValue = null;
|
||||
Number lowerBound = null;
|
||||
Number upperBound = null;
|
||||
|
||||
try {
|
||||
if (double.class == toType || Double.class == toType) {
|
||||
bigValue = new BigDecimal(stringValue);
|
||||
// Double.MIN_VALUE is the smallest positive non-zero number
|
||||
lowerBound = BigDecimal.valueOf(Double.MAX_VALUE).negate();
|
||||
upperBound = BigDecimal.valueOf(Double.MAX_VALUE);
|
||||
} else if (float.class == toType || Float.class == toType) {
|
||||
bigValue = new BigDecimal(stringValue);
|
||||
// Float.MIN_VALUE is the smallest positive non-zero number
|
||||
lowerBound = BigDecimal.valueOf(Float.MAX_VALUE).negate();
|
||||
upperBound = BigDecimal.valueOf(Float.MAX_VALUE);
|
||||
} else if (byte.class == toType || Byte.class == toType) {
|
||||
bigValue = new BigInteger(stringValue);
|
||||
lowerBound = BigInteger.valueOf(Byte.MIN_VALUE);
|
||||
upperBound = BigInteger.valueOf(Byte.MAX_VALUE);
|
||||
} else if (char.class == toType || Character.class == toType) {
|
||||
bigValue = new BigInteger(stringValue);
|
||||
lowerBound = BigInteger.valueOf(Character.MIN_VALUE);
|
||||
upperBound = BigInteger.valueOf(Character.MAX_VALUE);
|
||||
} else if (short.class == toType || Short.class == toType) {
|
||||
bigValue = new BigInteger(stringValue);
|
||||
lowerBound = BigInteger.valueOf(Short.MIN_VALUE);
|
||||
upperBound = BigInteger.valueOf(Short.MAX_VALUE);
|
||||
} else if (int.class == toType || Integer.class == toType) {
|
||||
bigValue = new BigInteger(stringValue);
|
||||
lowerBound = BigInteger.valueOf(Integer.MIN_VALUE);
|
||||
upperBound = BigInteger.valueOf(Integer.MAX_VALUE);
|
||||
} else if (long.class == toType || Long.class == toType) {
|
||||
bigValue = new BigInteger(stringValue);
|
||||
lowerBound = BigInteger.valueOf(Long.MIN_VALUE);
|
||||
upperBound = BigInteger.valueOf(Long.MAX_VALUE);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
//shoult it fail here? BigInteger doesnt seem to be so nice parsing numbers as NumberFormat
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0;
|
||||
}
|
||||
|
||||
protected boolean isIntegerType(Class type) {
|
||||
if (double.class == type || float.class == type || Double.class == type || Float.class == type
|
||||
|| char.class == type || Character.class == type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the input as a number using java's number formatter to a string output.
|
||||
*/
|
||||
private String doConvertFromNumberToString(Map<String, Object> context, Object value, Class toType) {
|
||||
// XW-409: If the input is a Number we should format it to a string using the choosen locale and use java's numberformatter
|
||||
if (Number.class.isAssignableFrom(toType)) {
|
||||
NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
|
||||
if (isIntegerType(toType)) {
|
||||
numFormat.setParseIntegerOnly(true);
|
||||
}
|
||||
numFormat.setGroupingUsed(true);
|
||||
numFormat.setMaximumFractionDigits(99); // to be sure we include all digits after decimal seperator, otherwise some of the fractions can be chopped
|
||||
|
||||
String number = numFormat.format(value);
|
||||
if (number != null) {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // no number
|
||||
}
|
||||
|
||||
|
||||
private String doConvertToString(Map<String, Object> context, Object value) {
|
||||
String result = null;
|
||||
|
||||
if (value instanceof int[]) {
|
||||
int[] x = (int[]) value;
|
||||
List<Integer> intArray = new ArrayList<Integer>(x.length);
|
||||
|
||||
for (int aX : x) {
|
||||
intArray.add(Integer.valueOf(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(intArray, ", ");
|
||||
} else if (value instanceof long[]) {
|
||||
long[] x = (long[]) value;
|
||||
List<Long> longArray = new ArrayList<Long>(x.length);
|
||||
|
||||
for (long aX : x) {
|
||||
longArray.add(Long.valueOf(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(longArray, ", ");
|
||||
} else if (value instanceof double[]) {
|
||||
double[] x = (double[]) value;
|
||||
List<Double> doubleArray = new ArrayList<Double>(x.length);
|
||||
|
||||
for (double aX : x) {
|
||||
doubleArray.add(new Double(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(doubleArray, ", ");
|
||||
} else if (value instanceof boolean[]) {
|
||||
boolean[] x = (boolean[]) value;
|
||||
List<Boolean> booleanArray = new ArrayList<Boolean>(x.length);
|
||||
|
||||
for (boolean aX : x) {
|
||||
booleanArray.add(new Boolean(aX));
|
||||
}
|
||||
|
||||
result = StringUtils.join(booleanArray, ", ");
|
||||
} else if (value instanceof Date) {
|
||||
DateFormat df = null;
|
||||
if (value instanceof java.sql.Time) {
|
||||
df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
|
||||
} else if (value instanceof java.sql.Timestamp) {
|
||||
SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
|
||||
DateFormat.MEDIUM,
|
||||
getLocale(context));
|
||||
df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT);
|
||||
} else {
|
||||
df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
|
||||
}
|
||||
result = df.format(value);
|
||||
} else if (value instanceof String[]) {
|
||||
result = StringUtils.join((String[]) value, ", ");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.XWorkMessages;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.annotations.Conversion;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
|
||||
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;
|
||||
import com.opensymphony.xwork2.util.*;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
|
||||
/**
|
||||
* XWorkConverter is a singleton used by many of the Struts 2's Ognl extention points,
|
||||
* such as InstantiatingNullHandler, XWorkListPropertyAccessor etc to do object
|
||||
* conversion.
|
||||
* <p/>
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* Type conversion is great for situations where you need to turn a String in to a more complex object. Because the web
|
||||
* is type-agnostic (everything is a string in HTTP), Struts 2's type conversion features are very useful. For instance,
|
||||
* if you were prompting a user to enter in coordinates in the form of a string (such as "3, 22"), you could have
|
||||
* Struts 2 do the conversion both from String to Point and from Point to String.
|
||||
* <p/>
|
||||
* <p/> Using this "point" example, if your action (or another compound object in which you are setting properties on)
|
||||
* has a corresponding ClassName-conversion.properties file, Struts 2 will use the configured type converters for
|
||||
* conversion to and from strings. So turning "3, 22" in to new Point(3, 22) is done by merely adding the following
|
||||
* entry to <b>ClassName-conversion.properties</b> (Note that the PointConverter should impl the TypeConverter
|
||||
* interface):
|
||||
* <p/>
|
||||
* <p/><b>point = com.acme.PointConverter</b>
|
||||
* <p/>
|
||||
* <p/> Your type converter should be sure to check what class type it is being requested to convert. Because it is used
|
||||
* for both to and from strings, you will need to split the conversion method in to two parts: one that turns Strings in
|
||||
* to Points, and one that turns Points in to Strings.
|
||||
* <p/>
|
||||
* <p/> After this is done, you can now reference your point (using <s:property value="point"/> in JSP or ${point}
|
||||
* in FreeMarker) and it will be printed as "3, 22" again. As such, if you submit this back to an action, it will be
|
||||
* converted back to a Point once again.
|
||||
* <p/>
|
||||
* <p/> In some situations you may wish to apply a type converter globally. This can be done by editing the file
|
||||
* <b>xwork-conversion.properties</b> in the root of your class path (typically WEB-INF/classes) and providing a
|
||||
* property in the form of the class name of the object you wish to convert on the left hand side and the class name of
|
||||
* the type converter on the right hand side. For example, providing a type converter for all Point objects would mean
|
||||
* adding the following entry:
|
||||
* <p/>
|
||||
* <p/><b>com.acme.Point = com.acme.PointConverter</b>
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: javadoc -->
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <!-- START SNIPPET: i18n-note -->
|
||||
* <p/>
|
||||
* Type conversion should not be used as a substitute for i18n. It is not recommended to use this feature to print out
|
||||
* properly formatted dates. Rather, you should use the i18n features of Struts 2 (and consult the JavaDocs for JDK's
|
||||
* MessageFormat object) to see how a properly formatted date should be displayed.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: i18n-note -->
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <!-- START SNIPPET: error-reporting -->
|
||||
* <p/>
|
||||
* Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the
|
||||
* input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string,
|
||||
* "", cannot be converted to a number might not be important - especially in a web environment where it is hard to
|
||||
* distinguish between a user not entering a value vs. entering a blank value.
|
||||
* <p/>
|
||||
* <p/> By default, all conversion errors are reported using the generic i18n key <b>xwork.default.invalid.fieldvalue</b>,
|
||||
* which you can override (the default text is <i>Invalid field value for field "xxx"</i>, where xxx is the field name)
|
||||
* in your global i18n resource bundle.
|
||||
* <p/>
|
||||
* <p/>However, sometimes you may wish to override this message on a per-field basis. You can do this by adding an i18n
|
||||
* key associated with just your action (Action.properties) using the pattern <b>invalid.fieldvalue.xxx</b>, where xxx
|
||||
* is the field name.
|
||||
* <p/>
|
||||
* <p/>It is important to know that none of these errors are actually reported directly. Rather, they are added to a map
|
||||
* called <i>conversionErrors</i> in the ActionContext. There are several ways this map can then be accessed and the
|
||||
* errors can be reported accordingly.
|
||||
* <p/>
|
||||
* <!-- END SNIPPET: error-reporting -->
|
||||
*
|
||||
* @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
|
||||
* @author Rainer Hermanns
|
||||
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
|
||||
* @author tm_jee
|
||||
* @version $Date$ $Id$
|
||||
* @see XWorkBasicConverter
|
||||
*/
|
||||
public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(XWorkConverter.class);
|
||||
public static final String REPORT_CONVERSION_ERRORS = "report.conversion.errors";
|
||||
public static final String CONVERSION_PROPERTY_FULLNAME = "conversion.property.fullName";
|
||||
public static final String CONVERSION_ERROR_PROPERTY_PREFIX = "invalid.fieldvalue.";
|
||||
public static final String CONVERSION_COLLECTION_PREFIX = "Collection_";
|
||||
|
||||
public static final String LAST_BEAN_CLASS_ACCESSED = "last.bean.accessed";
|
||||
public static final String LAST_BEAN_PROPERTY_ACCESSED = "last.property.accessed";
|
||||
public static final String MESSAGE_INDEX_PATTERN = "\\[\\d+\\]\\.";
|
||||
public static final String MESSAGE_INDEX_BRACKET_PATTERN = "[\\[\\]\\.]";
|
||||
public static final String PERIOD = ".";
|
||||
public static final Pattern messageIndexPattern = Pattern.compile(MESSAGE_INDEX_PATTERN);
|
||||
|
||||
/**
|
||||
* Target class conversion Mappings.
|
||||
* <pre>
|
||||
* Map<Class, Map<String, Object>>
|
||||
* - Class -> convert to class
|
||||
* - Map<String, Object>
|
||||
* - String -> property name
|
||||
* eg. Element_property, property etc.
|
||||
* - Object -> String to represent properties
|
||||
* eg. value part of
|
||||
* KeyProperty_property=id
|
||||
* -> TypeConverter to represent an Ognl TypeConverter
|
||||
* eg. value part of
|
||||
* property=foo.bar.MyConverter
|
||||
* -> Class to represent a class
|
||||
* eg. value part of
|
||||
* Element_property=foo.bar.MyObject
|
||||
* </pre>
|
||||
*/
|
||||
protected HashMap<Class, Map<String, Object>> mappings = new HashMap<Class, Map<String, Object>>(); // action
|
||||
|
||||
/**
|
||||
* Unavailable target class conversion mappings, serves as a simple cache.
|
||||
*/
|
||||
protected HashSet<Class> noMapping = new HashSet<Class>(); // action
|
||||
|
||||
/**
|
||||
* Record class and its type converter mapping.
|
||||
* <pre>
|
||||
* - String - classname as String
|
||||
* - TypeConverter - instance of TypeConverter
|
||||
* </pre>
|
||||
*/
|
||||
protected HashMap<String, TypeConverter> defaultMappings = new HashMap<String, TypeConverter>(); // non-action (eg. returned value)
|
||||
|
||||
/**
|
||||
* Record classes that doesn't have conversion mapping defined.
|
||||
* <pre>
|
||||
* - String -> classname as String
|
||||
* </pre>
|
||||
*/
|
||||
protected HashSet<String> unknownMappings = new HashSet<String>(); // non-action (eg. returned value)
|
||||
|
||||
private TypeConverter defaultTypeConverter;
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
|
||||
protected XWorkConverter() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory factory) {
|
||||
this.objectFactory = factory;
|
||||
// note: this file is deprecated
|
||||
loadConversionProperties("xwork-default-conversion.properties");
|
||||
|
||||
loadConversionProperties("xwork-conversion.properties");
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setDefaultTypeConverter(XWorkBasicConverter conv) {
|
||||
this.defaultTypeConverter = conv;
|
||||
}
|
||||
|
||||
public static String getConversionErrorMessage(String propertyName, ValueStack stack) {
|
||||
String defaultMessage = LocalizedTextUtil.findDefaultText(XWorkMessages.DEFAULT_INVALID_FIELDVALUE,
|
||||
ActionContext.getContext().getLocale(),
|
||||
new Object[]{
|
||||
propertyName
|
||||
});
|
||||
|
||||
List<String> indexValues = getIndexValues(propertyName);
|
||||
|
||||
propertyName = removeAllIndexesInProperyName(propertyName);
|
||||
|
||||
String getTextExpression = "getText('" + CONVERSION_ERROR_PROPERTY_PREFIX + propertyName + "','" + defaultMessage + "')";
|
||||
String message = (String) stack.findValue(getTextExpression);
|
||||
|
||||
if (message == null) {
|
||||
message = defaultMessage;
|
||||
} else {
|
||||
message = MessageFormat.format(message, indexValues.toArray());
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String removeAllIndexesInProperyName(String propertyName) {
|
||||
return propertyName.replaceAll(MESSAGE_INDEX_PATTERN, PERIOD);
|
||||
}
|
||||
|
||||
private static List<String> getIndexValues(String propertyName) {
|
||||
Matcher matcher = messageIndexPattern.matcher(propertyName);
|
||||
List<String> indexes = new ArrayList<String>();
|
||||
while (matcher.find()) {
|
||||
Integer index = new Integer(matcher.group().replaceAll(MESSAGE_INDEX_BRACKET_PATTERN, "")) + 1;
|
||||
indexes.add(Integer.toString(index));
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
public static String buildConverterFilename(Class clazz) {
|
||||
String className = clazz.getName();
|
||||
return className.replace('.', '/') + "-conversion.properties";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> map, Object o, Class aClass) {
|
||||
return convertValue(map, null, null, null, o, aClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert value from one form to another.
|
||||
* Minimum requirement of arguments:
|
||||
* <ul>
|
||||
* <li>supplying context, toClass and value</li>
|
||||
* <li>supplying context, target and value.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see TypeConverter#convertValue(java.util.Map, java.lang.Object, java.lang.reflect.Member, java.lang.String, java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Object convertValue(Map<String, Object> context, Object target, Member member, String property, Object value, Class toClass) {
|
||||
//
|
||||
// Process the conversion using the default mappings, if one exists
|
||||
//
|
||||
TypeConverter tc = null;
|
||||
|
||||
if ((value != null) && (toClass == value.getClass())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// allow this method to be called without any context
|
||||
// i.e. it can be called with as little as "Object value" and "Class toClass"
|
||||
if (target != null) {
|
||||
Class clazz = target.getClass();
|
||||
|
||||
Object[] classProp = null;
|
||||
|
||||
// this is to handle weird issues with setValue with a different type
|
||||
if ((target instanceof CompoundRoot) && (context != null)) {
|
||||
classProp = getClassProperty(context);
|
||||
}
|
||||
|
||||
if (classProp != null) {
|
||||
clazz = (Class) classProp[0];
|
||||
property = (String) classProp[1];
|
||||
}
|
||||
|
||||
tc = (TypeConverter) getConverter(clazz, property);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("field-level type converter for property [" + property + "] = " + (tc == null ? "none found" : tc));
|
||||
}
|
||||
|
||||
if (tc == null && context != null) {
|
||||
// ok, let's see if we can look it up by path as requested in XW-297
|
||||
Object lastPropertyPath = context.get(ReflectionContextState.CURRENT_PROPERTY_PATH);
|
||||
Class clazz = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
|
||||
if (lastPropertyPath != null && clazz != null) {
|
||||
String path = lastPropertyPath + "." + property;
|
||||
tc = (TypeConverter) getConverter(clazz, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (tc == null) {
|
||||
if (toClass.equals(String.class) && (value != null) && !(value.getClass().equals(String.class) || value.getClass().equals(String[].class))) {
|
||||
// when converting to a string, use the source target's class's converter
|
||||
tc = lookup(value.getClass());
|
||||
} else {
|
||||
// when converting from a string, use the toClass's converter
|
||||
tc = lookup(toClass);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("global-level type converter for property [" + property + "] = " + (tc == null ? "none found" : tc));
|
||||
}
|
||||
|
||||
|
||||
if (tc != null) {
|
||||
try {
|
||||
return tc.convertValue(context, target, member, property, value, toClass);
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("unable to convert value using type converter [#0]", e, tc.getClass().getName());
|
||||
handleConversionException(context, property, value, target);
|
||||
|
||||
return TypeConverter.NO_CONVERSION_POSSIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultTypeConverter != null) {
|
||||
try {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("falling back to default type converter [" + defaultTypeConverter + "]");
|
||||
return defaultTypeConverter.convertValue(context, target, member, property, value, toClass);
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("unable to convert value using type converter [#0]", e, defaultTypeConverter.getClass().getName());
|
||||
handleConversionException(context, property, value, target);
|
||||
|
||||
return TypeConverter.NO_CONVERSION_POSSIBLE;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("falling back to Ognl's default type conversion");
|
||||
return super.convertValue(value, toClass);
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("unable to convert value using type converter [#0]", e, super.getClass().getName());
|
||||
handleConversionException(context, property, value, target);
|
||||
|
||||
return TypeConverter.NO_CONVERSION_POSSIBLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for a TypeConverter in the default mappings.
|
||||
*
|
||||
* @param className name of the class the TypeConverter must handle
|
||||
* @return a TypeConverter to handle the specified class or null if none can be found
|
||||
*/
|
||||
public TypeConverter lookup(String className) {
|
||||
if (unknownMappings.contains(className) && !defaultMappings.containsKey(className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeConverter result = defaultMappings.get(className);
|
||||
|
||||
//Looks for super classes
|
||||
if (result == null) {
|
||||
Class clazz = null;
|
||||
|
||||
try {
|
||||
clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
//swallow
|
||||
}
|
||||
|
||||
result = lookupSuper(clazz);
|
||||
|
||||
if (result != null) {
|
||||
//Register now, the next lookup will be faster
|
||||
registerConverter(className, result);
|
||||
} else {
|
||||
// if it isn't found, never look again (also faster)
|
||||
registerConverterNotFound(className);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for a TypeConverter in the default mappings.
|
||||
*
|
||||
* @param clazz the class the TypeConverter must handle
|
||||
* @return a TypeConverter to handle the specified class or null if none can be found
|
||||
*/
|
||||
public TypeConverter lookup(Class clazz) {
|
||||
return lookup(clazz.getName());
|
||||
}
|
||||
|
||||
protected Object getConverter(Class clazz, String property) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Property: " + property);
|
||||
LOG.debug("Class: " + clazz.getName());
|
||||
}
|
||||
synchronized (clazz) {
|
||||
if ((property != null) && !noMapping.contains(clazz)) {
|
||||
try {
|
||||
Map<String, Object> mapping = mappings.get(clazz);
|
||||
|
||||
if (mapping == null) {
|
||||
mapping = buildConverterMapping(clazz);
|
||||
} else {
|
||||
mapping = conditionalReload(clazz, mapping);
|
||||
}
|
||||
|
||||
Object converter = mapping.get(property);
|
||||
if (LOG.isDebugEnabled() && converter == null) {
|
||||
LOG.debug("converter is null for property " + property + ". Mapping size: " + mapping.size());
|
||||
for (String next : mapping.keySet()) {
|
||||
LOG.debug(next + ":" + mapping.get(next));
|
||||
}
|
||||
}
|
||||
return converter;
|
||||
} catch (Throwable t) {
|
||||
noMapping.add(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void handleConversionException(Map<String, Object> context, String property, Object value, Object object) {
|
||||
if (context != null && (Boolean.TRUE.equals(context.get(REPORT_CONVERSION_ERRORS)))) {
|
||||
String realProperty = property;
|
||||
String fullName = (String) context.get(CONVERSION_PROPERTY_FULLNAME);
|
||||
|
||||
if (fullName != null) {
|
||||
realProperty = fullName;
|
||||
}
|
||||
|
||||
Map<String, Object> conversionErrors = (Map<String, Object>) context.get(ActionContext.CONVERSION_ERRORS);
|
||||
|
||||
if (conversionErrors == null) {
|
||||
conversionErrors = new HashMap<String, Object>();
|
||||
context.put(ActionContext.CONVERSION_ERRORS, conversionErrors);
|
||||
}
|
||||
|
||||
conversionErrors.put(realProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerConverter(String className, TypeConverter converter) {
|
||||
defaultMappings.put(className, converter);
|
||||
if (unknownMappings.contains(className)) {
|
||||
unknownMappings.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void registerConverterNotFound(String className) {
|
||||
unknownMappings.add(className);
|
||||
}
|
||||
|
||||
private Object[] getClassProperty(Map<String, Object> context) {
|
||||
Object lastClass = context.get(LAST_BEAN_CLASS_ACCESSED);
|
||||
Object lastProperty = context.get(LAST_BEAN_PROPERTY_ACCESSED);
|
||||
return (lastClass != null && lastProperty != null) ? new Object[] {lastClass, lastProperty} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for converter mappings for the specified class and adds it to an existing map. Only new converters are
|
||||
* added. If a converter is defined on a key that already exists, the converter is ignored.
|
||||
*
|
||||
* @param mapping an existing map to add new converter mappings to
|
||||
* @param clazz class to look for converter mappings for
|
||||
*/
|
||||
protected void addConverterMapping(Map<String, Object> mapping, Class clazz) {
|
||||
try {
|
||||
String converterFilename = buildConverterFilename(clazz);
|
||||
InputStream is = FileManager.loadFile(converterFilename, clazz);
|
||||
|
||||
if (is != null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("processing conversion file [" + converterFilename + "] [class=" + clazz + "]");
|
||||
}
|
||||
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
|
||||
for (Map.Entry<Object, Object> entry : prop.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
if (mapping.containsKey(key)) {
|
||||
break;
|
||||
}
|
||||
// for keyProperty of Set
|
||||
if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX)
|
||||
|| key.startsWith(DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as String]");
|
||||
}
|
||||
mapping.put(key, entry.getValue());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(key.startsWith(DefaultObjectTypeDeterminer.ELEMENT_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX))
|
||||
) {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {
|
||||
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + converterClass + "]");
|
||||
}
|
||||
mapping.put(key, converterClass);
|
||||
}
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
Class _c = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + _c + "]");
|
||||
}
|
||||
mapping.put(key, _c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Problem loading properties for " + clazz.getName(), ex);
|
||||
}
|
||||
|
||||
// Process annotations
|
||||
Annotation[] annotations = clazz.getAnnotations();
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof Conversion) {
|
||||
Conversion conversion = (Conversion) annotation;
|
||||
|
||||
for (TypeConversion tc : conversion.conversions()) {
|
||||
|
||||
String key = tc.key();
|
||||
|
||||
if (mapping.containsKey(key)) {
|
||||
break;
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(key + ":" + key);
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
defaultMappings.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY) || tc.rule().toString().equals(ConversionRule.CREATE_IF_NULL)) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
|
||||
tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
|
||||
tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
|
||||
) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
|
||||
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Converter class: " + converterClass);
|
||||
}
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Object placed in mapping for key "
|
||||
+ key
|
||||
+ " is "
|
||||
+ mapping.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method[] methods = clazz.getMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
|
||||
annotations = method.getAnnotations();
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof TypeConversion) {
|
||||
TypeConversion tc = (TypeConversion) annotation;
|
||||
|
||||
String key = tc.key();
|
||||
if (mapping.containsKey(key)) {
|
||||
break;
|
||||
}
|
||||
// Default to the property name
|
||||
if (key != null && key.length() == 0) {
|
||||
key = AnnotationUtils.resolvePropertyName(method);
|
||||
LOG.debug("key from method name... " + key + " - " + method.getName());
|
||||
}
|
||||
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(key + ":" + key);
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
defaultMappings.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY)) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
|
||||
tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
|
||||
tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
|
||||
) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Converter class: " + converterClass);
|
||||
}
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Object placed in mapping for key "
|
||||
+ key
|
||||
+ " is "
|
||||
+ mapping.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for converter mappings for the specified class, traversing up its class hierarchy and interfaces and adding
|
||||
* any additional mappings it may find. Mappings lower in the hierarchy have priority over those higher in the
|
||||
* hierarcy.
|
||||
*
|
||||
* @param clazz the class to look for converter mappings for
|
||||
* @return the converter mappings
|
||||
*/
|
||||
protected Map<String, Object> buildConverterMapping(Class clazz) throws Exception {
|
||||
Map<String, Object> mapping = new HashMap<String, Object>();
|
||||
|
||||
// check for conversion mapping associated with super classes and any implemented interfaces
|
||||
Class curClazz = clazz;
|
||||
|
||||
while (!curClazz.equals(Object.class)) {
|
||||
// add current class' mappings
|
||||
addConverterMapping(mapping, curClazz);
|
||||
|
||||
// check interfaces' mappings
|
||||
Class[] interfaces = curClazz.getInterfaces();
|
||||
|
||||
for (Class anInterface : interfaces) {
|
||||
addConverterMapping(mapping, anInterface);
|
||||
}
|
||||
|
||||
curClazz = curClazz.getSuperclass();
|
||||
}
|
||||
|
||||
if (mapping.size() > 0) {
|
||||
mappings.put(clazz, mapping);
|
||||
} else {
|
||||
noMapping.add(clazz);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private Map<String, Object> conditionalReload(Class clazz, Map<String, Object> oldValues) throws Exception {
|
||||
Map<String, Object> mapping = oldValues;
|
||||
|
||||
if (FileManager.isReloadingConfigs()) {
|
||||
if (FileManager.fileNeedsReloading(buildConverterFilename(clazz), clazz)) {
|
||||
mapping = buildConverterMapping(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
TypeConverter createTypeConverter(String className) throws Exception {
|
||||
// type converters are used across users
|
||||
Object obj = objectFactory.buildBean(className, null);
|
||||
if (obj instanceof TypeConverter) {
|
||||
return (TypeConverter) obj;
|
||||
|
||||
// For backwards compatibility
|
||||
} else if (obj instanceof ognl.TypeConverter) {
|
||||
return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type converter class " + obj.getClass() + " doesn't implement com.opensymphony.xwork2.conversion.TypeConverter");
|
||||
}
|
||||
}
|
||||
|
||||
public void loadConversionProperties(String propsName) {
|
||||
loadConversionProperties(propsName, false);
|
||||
}
|
||||
|
||||
public void loadConversionProperties(String propsName, boolean require) {
|
||||
try {
|
||||
Iterator<URL> resources = ClassLoaderUtil.getResources(propsName, getClass(), true);
|
||||
while (resources.hasNext()) {
|
||||
URL url = resources.next();
|
||||
Properties props = new Properties();
|
||||
props.load(url.openStream());
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("processing conversion file [" + propsName + "]");
|
||||
}
|
||||
|
||||
for (Object o : props.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
try {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + " [treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
defaultMappings.put(key, _typeConverter);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Conversion registration error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (require) {
|
||||
throw new XWorkException("Cannot load conversion properties file: "+propsName, ex);
|
||||
} else {
|
||||
LOG.debug("Cannot load conversion properties file: "+propsName, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurses through a class' interfaces and class hierarchy looking for a TypeConverter in the default mapping that
|
||||
* can handle the specified class.
|
||||
*
|
||||
* @param clazz the class the TypeConverter must handle
|
||||
* @return a TypeConverter to handle the specified class or null if none can be found
|
||||
*/
|
||||
TypeConverter lookupSuper(Class clazz) {
|
||||
TypeConverter result = null;
|
||||
|
||||
if (clazz != null) {
|
||||
result = defaultMappings.get(clazz.getName());
|
||||
|
||||
if (result == null) {
|
||||
// Looks for direct interfaces (depth = 1 )
|
||||
Class[] interfaces = clazz.getInterfaces();
|
||||
|
||||
for (Class anInterface : interfaces) {
|
||||
if (defaultMappings.containsKey(anInterface.getName())) {
|
||||
result = (TypeConverter) defaultMappings.get(anInterface.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
// Looks for the superclass
|
||||
// If 'clazz' is the Object class, an interface, a primitive type or void then clazz.getSuperClass() returns null
|
||||
result = lookupSuper(clazz.getSuperclass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2002-2006,2009 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.conversion.metadata;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* <code>ConversionDescription</code>
|
||||
*
|
||||
* @author Rainer Hermanns
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ConversionDescription {
|
||||
|
||||
/**
|
||||
* Jakarta commons-logging reference.
|
||||
*/
|
||||
protected static Logger log = null;
|
||||
|
||||
|
||||
public static final String KEY_PREFIX = "Key_";
|
||||
public static final String ELEMENT_PREFIX = "Element_";
|
||||
public static final String KEY_PROPERTY_PREFIX = "KeyProperty_";
|
||||
public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
|
||||
|
||||
/**
|
||||
* Key used for type conversion of maps.
|
||||
*/
|
||||
String MAP_PREFIX = "Map_";
|
||||
|
||||
public String property;
|
||||
public String typeConverter = "";
|
||||
public String rule = "";
|
||||
public String value = "";
|
||||
public String fullQualifiedClassName;
|
||||
public String type = null;
|
||||
|
||||
public ConversionDescription() {
|
||||
log = LoggerFactory.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ConversionDescription with the specified property name.
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
public ConversionDescription(String property) {
|
||||
this.property = property;
|
||||
log = LoggerFactory.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the property name to be inserted into the related conversion.properties file.<br/>
|
||||
* Note: Do not add COLLECTION_PREFIX or MAP_PREFIX keys to property names.
|
||||
* </p>
|
||||
*
|
||||
* @param property The property to be converted.
|
||||
*/
|
||||
public void setProperty(String property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class name of the type converter to be used.
|
||||
*
|
||||
* @param typeConverter The class name of the type converter.
|
||||
*/
|
||||
public void setTypeConverter(String typeConverter) {
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rule prefix for COLLECTION_PREFIX or MAP_PREFIX key.
|
||||
* Defaults to en emtpy String.
|
||||
*
|
||||
* @param rule
|
||||
*/
|
||||
public void setRule(String rule) {
|
||||
if (rule != null && rule.length() > 0) {
|
||||
if (rule.equals(ConversionRule.COLLECTION.toString())) {
|
||||
this.rule = DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX;
|
||||
} else if (rule.equals(ConversionRule.ELEMENT.toString())) {
|
||||
this.rule = DefaultObjectTypeDeterminer.ELEMENT_PREFIX;
|
||||
} else if (rule.equals(ConversionRule.KEY.toString())) {
|
||||
this.rule = DefaultObjectTypeDeterminer.KEY_PREFIX;
|
||||
} else if (rule.equals(ConversionRule.KEY_PROPERTY.toString())) {
|
||||
this.rule = DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX;
|
||||
} else if (rule.equals(ConversionRule.MAP.toString())) {
|
||||
this.rule = MAP_PREFIX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the conversion description as property entry.
|
||||
* <p>
|
||||
* Example:<br/>
|
||||
* property.name = converter.className<br/>
|
||||
* Collection_property.name = converter.className<br/>
|
||||
* Map_property.name = converter.className
|
||||
* KeyProperty_name = id
|
||||
* </p>
|
||||
*
|
||||
* @return the conversion description as property entry.
|
||||
*/
|
||||
public String asProperty() {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
writer = new PrintWriter(sw);
|
||||
writer.print(rule);
|
||||
writer.print(property);
|
||||
writer.print("=");
|
||||
if ( rule.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX) && value != null && value.length() > 0 ) {
|
||||
writer.print(value);
|
||||
} else {
|
||||
writer.print(typeConverter);
|
||||
}
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
return sw.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fullQualifiedClassName attribute is used to create the special <code>conversion.properties</code> file name.
|
||||
*
|
||||
* @return fullQualifiedClassName
|
||||
*/
|
||||
public String getFullQualifiedClassName() {
|
||||
return fullQualifiedClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fullQualifiedClassName attribute is used to create the special <code>conversion.properties</code> file name.
|
||||
*
|
||||
* @param fullQualifiedClassName
|
||||
*/
|
||||
public void setFullQualifiedClassName(String fullQualifiedClassName) {
|
||||
this.fullQualifiedClassName = fullQualifiedClassName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<body>Type conversion meta data classes.</body>
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Context of a dependency construction. Used to manage circular references.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
class ConstructionContext<T> {
|
||||
|
||||
T currentReference;
|
||||
boolean constructing;
|
||||
|
||||
List<DelegatingInvocationHandler<T>> invocationHandlers;
|
||||
|
||||
T getCurrentReference() {
|
||||
return currentReference;
|
||||
}
|
||||
|
||||
void removeCurrentReference() {
|
||||
this.currentReference = null;
|
||||
}
|
||||
|
||||
void setCurrentReference(T currentReference) {
|
||||
this.currentReference = currentReference;
|
||||
}
|
||||
|
||||
boolean isConstructing() {
|
||||
return constructing;
|
||||
}
|
||||
|
||||
void startConstruction() {
|
||||
this.constructing = true;
|
||||
}
|
||||
|
||||
void finishConstruction() {
|
||||
this.constructing = false;
|
||||
invocationHandlers = null;
|
||||
}
|
||||
|
||||
Object createProxy(Class<? super T> expectedType) {
|
||||
// TODO: if I create a proxy which implements all the interfaces of
|
||||
// the implementation type, I'll be able to get away with one proxy
|
||||
// instance (as opposed to one per caller).
|
||||
|
||||
if (!expectedType.isInterface()) {
|
||||
throw new DependencyException(
|
||||
expectedType.getName() + " is not an interface.");
|
||||
}
|
||||
|
||||
if (invocationHandlers == null) {
|
||||
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
|
||||
}
|
||||
|
||||
DelegatingInvocationHandler<T> invocationHandler =
|
||||
new DelegatingInvocationHandler<T>();
|
||||
invocationHandlers.add(invocationHandler);
|
||||
|
||||
return Proxy.newProxyInstance(
|
||||
expectedType.getClassLoader(),
|
||||
new Class[] { expectedType },
|
||||
invocationHandler
|
||||
);
|
||||
}
|
||||
|
||||
void setProxyDelegates(T delegate) {
|
||||
if (invocationHandlers != null) {
|
||||
for (DelegatingInvocationHandler<T> invocationHandler
|
||||
: invocationHandlers) {
|
||||
invocationHandler.setDelegate(delegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DelegatingInvocationHandler<T> implements InvocationHandler {
|
||||
|
||||
T delegate;
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
if (delegate == null) {
|
||||
throw new IllegalStateException(
|
||||
"Not finished constructing. Please don't call methods on this"
|
||||
+ " object until the caller's construction is complete.");
|
||||
}
|
||||
|
||||
try {
|
||||
return method.invoke(delegate, args);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
void setDelegate(T delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Injects dependencies into constructors, methods and fields annotated with
|
||||
* {@link Inject}. Immutable.
|
||||
*
|
||||
* <p>When injecting a method or constructor, you can additionally annotate
|
||||
* its parameters with {@link Inject} and specify a dependency name. When a
|
||||
* parameter has no annotation, the container uses the name from the method or
|
||||
* constructor's {@link Inject} annotation respectively.
|
||||
*
|
||||
* <p>For example:
|
||||
*
|
||||
* <pre>
|
||||
* class Foo {
|
||||
*
|
||||
* // Inject the int constant named "i".
|
||||
* @Inject("i") int i;
|
||||
*
|
||||
* // Inject the default implementation of Bar and the String constant
|
||||
* // named "s".
|
||||
* @Inject Foo(Bar bar, @Inject("s") String s) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // Inject the default implementation of Baz and the Bob implementation
|
||||
* // named "foo".
|
||||
* @Inject void initialize(Baz baz, @Inject("foo") Bob bob) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // Inject the default implementation of Tee.
|
||||
* @Inject void setTee(Tee tee) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>To create and inject an instance of {@code Foo}:
|
||||
*
|
||||
* <pre>
|
||||
* Container c = ...;
|
||||
* Foo foo = c.inject(Foo.class);
|
||||
* </pre>
|
||||
*
|
||||
* @see ContainerBuilder
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public interface Container extends Serializable {
|
||||
|
||||
/**
|
||||
* Default dependency name.
|
||||
*/
|
||||
String DEFAULT_NAME = "default";
|
||||
|
||||
/**
|
||||
* Injects dependencies into the fields and methods of an existing object.
|
||||
*/
|
||||
void inject(Object o);
|
||||
|
||||
/**
|
||||
* Creates and injects a new instance of type {@code implementation}.
|
||||
*/
|
||||
<T> T inject(Class<T> implementation);
|
||||
|
||||
/**
|
||||
* Gets an instance of the given dependency which was declared in
|
||||
* {@link com.opensymphony.xwork2.inject.ContainerBuilder}.
|
||||
*/
|
||||
<T> T getInstance(Class<T> type, String name);
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code getInstance(type,
|
||||
* DEFAULT_NAME)}.
|
||||
*/
|
||||
<T> T getInstance(Class<T> type);
|
||||
|
||||
/**
|
||||
* Gets a set of all registered names for the given type
|
||||
* @param type The instance type
|
||||
* @return A set of registered names
|
||||
*/
|
||||
Set<String> getInstanceNames(Class<?> type);
|
||||
|
||||
/**
|
||||
* Sets the scope strategy for the current thread.
|
||||
*/
|
||||
void setScopeStrategy(Scope.Strategy scopeStrategy);
|
||||
|
||||
/**
|
||||
* Removes the scope strategy for the current thread.
|
||||
*/
|
||||
void removeScopeStrategy();
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Builds a dependency injection {@link Container}. The combination of
|
||||
* dependency type and name uniquely identifies a dependency mapping; you can
|
||||
* use the same name for two different types. Not safe for concurrent use.
|
||||
*
|
||||
* <p>Adds the following factories by default:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Injects the current {@link Container}.
|
||||
* <li>Injects the {@link Logger} for the injected member's declaring class.
|
||||
* </ul>
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public final class ContainerBuilder {
|
||||
|
||||
final Map<Key<?>, InternalFactory<?>> factories =
|
||||
new HashMap<Key<?>, InternalFactory<?>>();
|
||||
final List<InternalFactory<?>> singletonFactories =
|
||||
new ArrayList<InternalFactory<?>>();
|
||||
final List<Class<?>> staticInjections = new ArrayList<Class<?>>();
|
||||
boolean created;
|
||||
boolean allowDuplicates = false;
|
||||
|
||||
private static final InternalFactory<Container> CONTAINER_FACTORY =
|
||||
new InternalFactory<Container>() {
|
||||
public Container create(InternalContext context) {
|
||||
return context.getContainer();
|
||||
}
|
||||
};
|
||||
|
||||
private static final InternalFactory<Logger> LOGGER_FACTORY =
|
||||
new InternalFactory<Logger>() {
|
||||
public Logger create(InternalContext context) {
|
||||
Member member = context.getExternalContext().getMember();
|
||||
return member == null ? Logger.getAnonymousLogger()
|
||||
: Logger.getLogger(member.getDeclaringClass().getName());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a new builder.
|
||||
*/
|
||||
public ContainerBuilder() {
|
||||
// In the current container as the default Container implementation.
|
||||
factories.put(Key.newInstance(Container.class, Container.DEFAULT_NAME),
|
||||
CONTAINER_FACTORY);
|
||||
|
||||
// Inject the logger for the injected member's declaring class.
|
||||
factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME),
|
||||
LOGGER_FACTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a dependency. All methods in this class ultimately funnel through
|
||||
* here.
|
||||
*/
|
||||
private <T> ContainerBuilder factory(final Key<T> key,
|
||||
InternalFactory<? extends T> factory, Scope scope) {
|
||||
ensureNotCreated();
|
||||
checkKey(key);
|
||||
final InternalFactory<? extends T> scopedFactory =
|
||||
scope.scopeFactory(key.getType(), key.getName(), factory);
|
||||
factories.put(key, scopedFactory);
|
||||
if (scope == Scope.SINGLETON) {
|
||||
singletonFactories.add(new InternalFactory<T>() {
|
||||
public T create(InternalContext context) {
|
||||
try {
|
||||
context.setExternalContext(ExternalContext.newInstance(
|
||||
null, key, context.getContainerImpl()));
|
||||
return scopedFactory.create(context);
|
||||
} finally {
|
||||
context.setExternalContext(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a key isn't already mapped.
|
||||
*/
|
||||
private void checkKey(Key<?> key) {
|
||||
if (factories.containsKey(key) && !allowDuplicates) {
|
||||
throw new DependencyException(
|
||||
"Dependency mapping for " + key + " already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a factory to a given dependency type and name.
|
||||
*
|
||||
* @param type of dependency
|
||||
* @param name of dependency
|
||||
* @param factory creates objects to inject
|
||||
* @param scope scope of injected instances
|
||||
* @return this builder
|
||||
*/
|
||||
public <T> ContainerBuilder factory(final Class<T> type, final String name,
|
||||
final Factory<? extends T> factory, Scope scope) {
|
||||
InternalFactory<T> internalFactory =
|
||||
new InternalFactory<T>() {
|
||||
|
||||
public T create(InternalContext context) {
|
||||
try {
|
||||
Context externalContext = context.getExternalContext();
|
||||
return factory.create(externalContext);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new LinkedHashMap<String, Object>() {{
|
||||
put("type", type);
|
||||
put("name", name);
|
||||
put("factory", factory);
|
||||
}}.toString();
|
||||
}
|
||||
};
|
||||
|
||||
return factory(Key.newInstance(type, name), internalFactory, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, factory, scope)}.
|
||||
*
|
||||
* @see #factory(Class, String, Factory, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type,
|
||||
Factory<? extends T> factory, Scope scope) {
|
||||
return factory(type, Container.DEFAULT_NAME, factory, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type, name, factory,
|
||||
* Scope.DEFAULT)}.
|
||||
*
|
||||
* @see #factory(Class, String, Factory, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type, String name,
|
||||
Factory<? extends T> factory) {
|
||||
return factory(type, name, factory, Scope.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, factory, Scope.DEFAULT)}.
|
||||
*
|
||||
* @see #factory(Class, String, Factory, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type,
|
||||
Factory<? extends T> factory) {
|
||||
return factory(type, Container.DEFAULT_NAME, factory, Scope.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an implementation class to a given dependency type and name. Creates
|
||||
* instances using the container, recursively injecting dependencies.
|
||||
*
|
||||
* @param type of dependency
|
||||
* @param name of dependency
|
||||
* @param implementation class
|
||||
* @param scope scope of injected instances
|
||||
* @return this builder
|
||||
*/
|
||||
public <T> ContainerBuilder factory(final Class<T> type, final String name,
|
||||
final Class<? extends T> implementation, final Scope scope) {
|
||||
// This factory creates new instances of the given implementation.
|
||||
// We have to lazy load the constructor because the Container
|
||||
// hasn't been created yet.
|
||||
InternalFactory<? extends T> factory = new InternalFactory<T>() {
|
||||
|
||||
volatile ContainerImpl.ConstructorInjector<? extends T> constructor;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T create(InternalContext context) {
|
||||
if (constructor == null) {
|
||||
this.constructor =
|
||||
context.getContainerImpl().getConstructor(implementation);
|
||||
}
|
||||
return (T) constructor.construct(context, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new LinkedHashMap<String, Object>() {{
|
||||
put("type", type);
|
||||
put("name", name);
|
||||
put("implementation", implementation);
|
||||
put("scope", scope);
|
||||
}}.toString();
|
||||
}
|
||||
};
|
||||
|
||||
return factory(Key.newInstance(type, name), factory, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an implementation class to a given dependency type and name. Creates
|
||||
* instances using the container, recursively injecting dependencies.
|
||||
*
|
||||
* <p>Sets scope to value from {@link Scoped} annotation on the
|
||||
* implementation class. Defaults to {@link Scope#DEFAULT} if no annotation
|
||||
* is found.
|
||||
*
|
||||
* @param type of dependency
|
||||
* @param name of dependency
|
||||
* @param implementation class
|
||||
* @return this builder
|
||||
*/
|
||||
public <T> ContainerBuilder factory(final Class<T> type, String name,
|
||||
final Class<? extends T> implementation) {
|
||||
Scoped scoped = implementation.getAnnotation(Scoped.class);
|
||||
Scope scope = scoped == null ? Scope.DEFAULT : scoped.value();
|
||||
return factory(type, name, implementation, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, implementation)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type,
|
||||
Class<? extends T> implementation) {
|
||||
return factory(type, Container.DEFAULT_NAME, implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, type)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type) {
|
||||
return factory(type, Container.DEFAULT_NAME, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type, name, type)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type, String name) {
|
||||
return factory(type, name, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, implementation, scope)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type,
|
||||
Class<? extends T> implementation, Scope scope) {
|
||||
return factory(type, Container.DEFAULT_NAME, implementation, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type,
|
||||
* Container.DEFAULT_NAME, type, scope)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type, Scope scope) {
|
||||
return factory(type, Container.DEFAULT_NAME, type, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code factory(type, name, type,
|
||||
* scope)}.
|
||||
*
|
||||
* @see #factory(Class, String, Class, Scope)
|
||||
*/
|
||||
public <T> ContainerBuilder factory(Class<T> type, String name, Scope scope) {
|
||||
return factory(type, name, type, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code alias(type, Container.DEFAULT_NAME,
|
||||
* type)}.
|
||||
*
|
||||
* @see #alias(Class, String, String)
|
||||
*/
|
||||
public <T> ContainerBuilder alias(Class<T> type, String alias) {
|
||||
return alias(type, Container.DEFAULT_NAME, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an existing factory to a new name.
|
||||
*
|
||||
* @param type of dependency
|
||||
* @param name of dependency
|
||||
* @param alias of to the dependency
|
||||
* @return this builder
|
||||
*/
|
||||
public <T> ContainerBuilder alias(Class<T> type, String name, String alias) {
|
||||
return alias(Key.newInstance(type, name), Key.newInstance(type, alias));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an existing dependency. All methods in this class ultimately funnel through
|
||||
* here.
|
||||
*/
|
||||
private <T> ContainerBuilder alias(final Key<T> key,
|
||||
final Key<T> aliasKey) {
|
||||
ensureNotCreated();
|
||||
checkKey(aliasKey);
|
||||
|
||||
final InternalFactory<? extends T> scopedFactory =
|
||||
(InternalFactory<? extends T>)factories.get(key);
|
||||
if (scopedFactory == null) {
|
||||
throw new DependencyException(
|
||||
"Dependency mapping for " + key + " doesn't exists.");
|
||||
}
|
||||
factories.put(aliasKey, scopedFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, String value) {
|
||||
return constant(String.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, int value) {
|
||||
return constant(int.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, long value) {
|
||||
return constant(long.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, boolean value) {
|
||||
return constant(boolean.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, double value) {
|
||||
return constant(double.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, float value) {
|
||||
return constant(float.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, short value) {
|
||||
return constant(short.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, char value) {
|
||||
return constant(char.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a class to the given name.
|
||||
*/
|
||||
public ContainerBuilder constant(String name, Class value) {
|
||||
return constant(Class.class, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an enum to the given name.
|
||||
*/
|
||||
public <E extends Enum<E>> ContainerBuilder constant(String name, E value) {
|
||||
return constant(value.getDeclaringClass(), name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a constant value to the given type and name.
|
||||
*/
|
||||
private <T> ContainerBuilder constant(final Class<T> type, final String name,
|
||||
final T value) {
|
||||
InternalFactory<T> factory = new InternalFactory<T>() {
|
||||
public T create(InternalContext ignored) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new LinkedHashMap<String, Object>() {
|
||||
{
|
||||
put("type", type);
|
||||
put("name", name);
|
||||
put("value", value);
|
||||
}
|
||||
}.toString();
|
||||
}
|
||||
};
|
||||
|
||||
return factory(Key.newInstance(type, name), factory, Scope.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon creation, the {@link Container} will inject static fields and methods
|
||||
* into the given classes.
|
||||
*
|
||||
* @param types for which static members will be injected
|
||||
*/
|
||||
public ContainerBuilder injectStatics(Class<?>... types) {
|
||||
staticInjections.addAll(Arrays.asList(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this builder contains a mapping for the given type and
|
||||
* name.
|
||||
*/
|
||||
public boolean contains(Class<?> type, String name) {
|
||||
return factories.containsKey(Key.newInstance(type, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method. Equivalent to {@code contains(type,
|
||||
* Container.DEFAULT_NAME)}.
|
||||
*/
|
||||
public boolean contains(Class<?> type) {
|
||||
return contains(type, Container.DEFAULT_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Container} instance. Injects static members for classes
|
||||
* which were registered using {@link #injectStatics(Class...)}.
|
||||
*
|
||||
* @param loadSingletons If true, the container will load all singletons
|
||||
* now. If false, the container will lazily load singletons. Eager loading
|
||||
* is appropriate for production use while lazy loading can speed
|
||||
* development.
|
||||
* @throws IllegalStateException if called more than once
|
||||
*/
|
||||
public Container create(boolean loadSingletons) {
|
||||
ensureNotCreated();
|
||||
created = true;
|
||||
final ContainerImpl container = new ContainerImpl(
|
||||
new HashMap<Key<?>, InternalFactory<?>>(factories));
|
||||
if (loadSingletons) {
|
||||
container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
|
||||
public Void call(InternalContext context) {
|
||||
for (InternalFactory<?> factory : singletonFactories) {
|
||||
factory.create(context);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
container.injectStatics(staticInjections);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently we only support creating one Container instance per builder.
|
||||
* If we want to support creating more than one container per builder,
|
||||
* we should move to a "factory factory" model where we create a factory
|
||||
* instance per Container. Right now, one factory instance would be
|
||||
* shared across all the containers, singletons synchronize on the
|
||||
* container when lazy loading, etc.
|
||||
*/
|
||||
private void ensureNotCreated() {
|
||||
if (created) {
|
||||
throw new IllegalStateException("Container already created.");
|
||||
}
|
||||
}
|
||||
|
||||
public void setAllowDuplicates(boolean val) {
|
||||
allowDuplicates = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented by classes which participate in building a container.
|
||||
*/
|
||||
public interface Command {
|
||||
|
||||
/**
|
||||
* Contributes factories to the given builder.
|
||||
*
|
||||
* @param builder
|
||||
*/
|
||||
void build(ContainerBuilder builder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,621 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import com.opensymphony.xwork2.inject.util.ReferenceCache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.security.AccessControlException;
|
||||
|
||||
/**
|
||||
* Default {@link Container} implementation.
|
||||
*
|
||||
* @see ContainerBuilder
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
class ContainerImpl implements Container {
|
||||
|
||||
final Map<Key<?>, InternalFactory<?>> factories;
|
||||
final Map<Class<?>,Set<String>> factoryNamesByType;
|
||||
|
||||
ContainerImpl(Map<Key<?>, InternalFactory<?>> factories) {
|
||||
this.factories = factories;
|
||||
Map<Class<?>,Set<String>> map = new HashMap<Class<?>,Set<String>>();
|
||||
for (Key<?> key : factories.keySet()) {
|
||||
Set<String> names = map.get(key.getType());
|
||||
if (names == null) {
|
||||
names = new HashSet<String>();
|
||||
map.put(key.getType(), names);
|
||||
}
|
||||
names.add(key.getName());
|
||||
}
|
||||
|
||||
for (Entry<Class<?>,Set<String>> entry : map.entrySet()) {
|
||||
entry.setValue(Collections.unmodifiableSet(entry.getValue()));
|
||||
}
|
||||
|
||||
this.factoryNamesByType = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> InternalFactory<? extends T> getFactory(Key<T> key) {
|
||||
return (InternalFactory<T>) factories.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field and method injectors.
|
||||
*/
|
||||
final Map<Class<?>, List<Injector>> injectors =
|
||||
new ReferenceCache<Class<?>, List<Injector>>() {
|
||||
@Override
|
||||
protected List<Injector> create(Class<?> key) {
|
||||
List<Injector> injectors = new ArrayList<Injector>();
|
||||
addInjectors(key, injectors);
|
||||
return injectors;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively adds injectors for fields and methods from the given class to
|
||||
* the given list. Injects parent classes before sub classes.
|
||||
*/
|
||||
void addInjectors(Class clazz, List<Injector> injectors) {
|
||||
if (clazz == Object.class) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add injectors for superclass first.
|
||||
addInjectors(clazz.getSuperclass(), injectors);
|
||||
|
||||
// TODO (crazybob): Filter out overridden members.
|
||||
addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
|
||||
addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
|
||||
}
|
||||
|
||||
void injectStatics(List<Class<?>> staticInjections) {
|
||||
final List<Injector> injectors = new ArrayList<Injector>();
|
||||
|
||||
for (Class<?> clazz : staticInjections) {
|
||||
addInjectorsForFields(clazz.getDeclaredFields(), true, injectors);
|
||||
addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
|
||||
}
|
||||
|
||||
callInContext(new ContextualCallable<Void>() {
|
||||
public Void call(InternalContext context) {
|
||||
for (Injector injector : injectors) {
|
||||
injector.inject(context, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addInjectorsForMethods(Method[] methods, boolean statics,
|
||||
List<Injector> injectors) {
|
||||
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
|
||||
new InjectorFactory<Method>() {
|
||||
public Injector create(ContainerImpl container, Method method,
|
||||
String name) throws MissingDependencyException {
|
||||
return new MethodInjector(container, method, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addInjectorsForFields(Field[] fields, boolean statics,
|
||||
List<Injector> injectors) {
|
||||
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
|
||||
new InjectorFactory<Field>() {
|
||||
public Injector create(ContainerImpl container, Field field,
|
||||
String name) throws MissingDependencyException {
|
||||
return new FieldInjector(container, field, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
|
||||
List<M> members, boolean statics, List<Injector> injectors,
|
||||
InjectorFactory<M> injectorFactory) {
|
||||
for (M member : members) {
|
||||
if (isStatic(member) == statics) {
|
||||
Inject inject = member.getAnnotation(Inject.class);
|
||||
if (inject != null) {
|
||||
try {
|
||||
injectors.add(injectorFactory.create(this, member, inject.value()));
|
||||
} catch (MissingDependencyException e) {
|
||||
if (inject.required()) {
|
||||
throw new DependencyException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface InjectorFactory<M extends Member & AnnotatedElement> {
|
||||
Injector create(ContainerImpl container, M member, String name)
|
||||
throws MissingDependencyException;
|
||||
}
|
||||
|
||||
private boolean isStatic(Member member) {
|
||||
return Modifier.isStatic(member.getModifiers());
|
||||
}
|
||||
|
||||
static class FieldInjector implements Injector {
|
||||
|
||||
final Field field;
|
||||
final InternalFactory<?> factory;
|
||||
final ExternalContext<?> externalContext;
|
||||
|
||||
public FieldInjector(ContainerImpl container, Field field, String name)
|
||||
throws MissingDependencyException {
|
||||
this.field = field;
|
||||
if (!field.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
|
||||
field.setAccessible(true);
|
||||
} catch(AccessControlException e) {
|
||||
throw new DependencyException("Security manager in use, could not access field: "
|
||||
+ field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
Key<?> key = Key.newInstance(field.getType(), name);
|
||||
factory = container.getFactory(key);
|
||||
if (factory == null) {
|
||||
throw new MissingDependencyException(
|
||||
"No mapping found for dependency " + key + " in " + field + ".");
|
||||
}
|
||||
|
||||
this.externalContext = ExternalContext.newInstance(field, key, container);
|
||||
}
|
||||
|
||||
public void inject(InternalContext context, Object o) {
|
||||
ExternalContext<?> previous = context.getExternalContext();
|
||||
context.setExternalContext(externalContext);
|
||||
try {
|
||||
field.set(o, factory.create(context));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
} finally {
|
||||
context.setExternalContext(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameter injectors.
|
||||
*
|
||||
* @param member to which the parameters belong
|
||||
* @param annotations on the parameters
|
||||
* @param parameterTypes parameter types
|
||||
* @return injections
|
||||
*/
|
||||
<M extends AccessibleObject & Member> ParameterInjector<?>[]
|
||||
getParametersInjectors(M member,
|
||||
Annotation[][] annotations, Class[] parameterTypes, String defaultName)
|
||||
throws MissingDependencyException {
|
||||
List<ParameterInjector<?>> parameterInjectors =
|
||||
new ArrayList<ParameterInjector<?>>();
|
||||
|
||||
Iterator<Annotation[]> annotationsIterator =
|
||||
Arrays.asList(annotations).iterator();
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
Inject annotation = findInject(annotationsIterator.next());
|
||||
String name = annotation == null ? defaultName : annotation.value();
|
||||
Key<?> key = Key.newInstance(parameterType, name);
|
||||
parameterInjectors.add(createParameterInjector(key, member));
|
||||
}
|
||||
|
||||
return toArray(parameterInjectors);
|
||||
}
|
||||
|
||||
<T> ParameterInjector<T> createParameterInjector(
|
||||
Key<T> key, Member member) throws MissingDependencyException {
|
||||
InternalFactory<? extends T> factory = getFactory(key);
|
||||
if (factory == null) {
|
||||
throw new MissingDependencyException(
|
||||
"No mapping found for dependency " + key + " in " + member + ".");
|
||||
}
|
||||
|
||||
ExternalContext<T> externalContext =
|
||||
ExternalContext.newInstance(member, key, this);
|
||||
return new ParameterInjector<T>(externalContext, factory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ParameterInjector<?>[] toArray(
|
||||
List<ParameterInjector<?>> parameterInjections) {
|
||||
return parameterInjections.toArray(
|
||||
new ParameterInjector[parameterInjections.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the {@link Inject} annotation in an array of annotations.
|
||||
*/
|
||||
Inject findInject(Annotation[] annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType() == Inject.class) {
|
||||
return Inject.class.cast(annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static class MethodInjector implements Injector {
|
||||
|
||||
final Method method;
|
||||
final ParameterInjector<?>[] parameterInjectors;
|
||||
|
||||
public MethodInjector(ContainerImpl container, Method method, String name)
|
||||
throws MissingDependencyException {
|
||||
this.method = method;
|
||||
if (!method.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
|
||||
method.setAccessible(true);
|
||||
} catch(AccessControlException e) {
|
||||
throw new DependencyException("Security manager in use, could not access method: "
|
||||
+ name + "(" + method.getName() + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length == 0) {
|
||||
throw new DependencyException(
|
||||
method + " has no parameters to inject.");
|
||||
}
|
||||
parameterInjectors = container.getParametersInjectors(
|
||||
method, method.getParameterAnnotations(), parameterTypes, name);
|
||||
}
|
||||
|
||||
public void inject(InternalContext context, Object o) {
|
||||
try {
|
||||
method.invoke(o, getParameters(method, context, parameterInjectors));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Class<?>, ConstructorInjector> constructors =
|
||||
new ReferenceCache<Class<?>, ConstructorInjector>() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ConstructorInjector<?> create(Class<?> implementation) {
|
||||
return new ConstructorInjector(ContainerImpl.this, implementation);
|
||||
}
|
||||
};
|
||||
|
||||
static class ConstructorInjector<T> {
|
||||
|
||||
final Class<T> implementation;
|
||||
final List<Injector> injectors;
|
||||
final Constructor<T> constructor;
|
||||
final ParameterInjector<?>[] parameterInjectors;
|
||||
|
||||
ConstructorInjector(ContainerImpl container, Class<T> implementation) {
|
||||
this.implementation = implementation;
|
||||
|
||||
constructor = findConstructorIn(implementation);
|
||||
if (!constructor.isAccessible()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
|
||||
constructor.setAccessible(true);
|
||||
} catch(AccessControlException e) {
|
||||
throw new DependencyException("Security manager in use, could not access constructor: "
|
||||
+ implementation.getName() + "(" + constructor.getName() + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
MissingDependencyException exception = null;
|
||||
Inject inject = null;
|
||||
ParameterInjector<?>[] parameters = null;
|
||||
|
||||
try {
|
||||
inject = constructor.getAnnotation(Inject.class);
|
||||
parameters = constructParameterInjector(inject, container, constructor);
|
||||
} catch (MissingDependencyException e) {
|
||||
exception = e;
|
||||
}
|
||||
parameterInjectors = parameters;
|
||||
|
||||
if ( exception != null) {
|
||||
if ( inject != null && inject.required()) {
|
||||
throw new DependencyException(exception);
|
||||
}
|
||||
}
|
||||
injectors = container.injectors.get(implementation);
|
||||
}
|
||||
|
||||
ParameterInjector<?>[] constructParameterInjector(
|
||||
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException{
|
||||
return constructor.getParameterTypes().length == 0
|
||||
? null // default constructor.
|
||||
: container.getParametersInjectors(
|
||||
constructor,
|
||||
constructor.getParameterAnnotations(),
|
||||
constructor.getParameterTypes(),
|
||||
inject.value()
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Constructor<T> findConstructorIn(Class<T> implementation) {
|
||||
Constructor<T> found = null;
|
||||
Constructor<T>[] declaredConstructors = (Constructor<T>[]) implementation
|
||||
.getDeclaredConstructors();
|
||||
for(Constructor<T> constructor : declaredConstructors) {
|
||||
if (constructor.getAnnotation(Inject.class) != null) {
|
||||
if (found != null) {
|
||||
throw new DependencyException("More than one constructor annotated"
|
||||
+ " with @Inject found in " + implementation + ".");
|
||||
}
|
||||
found = constructor;
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
|
||||
// If no annotated constructor is found, look for a no-arg constructor
|
||||
// instead.
|
||||
try {
|
||||
return implementation.getDeclaredConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new DependencyException("Could not find a suitable constructor"
|
||||
+ " in " + implementation.getName() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance. Returns {@code Object} instead of {@code T}
|
||||
* because it may return a proxy.
|
||||
*/
|
||||
Object construct(InternalContext context, Class<? super T> expectedType) {
|
||||
ConstructionContext<T> constructionContext =
|
||||
context.getConstructionContext(this);
|
||||
|
||||
// We have a circular reference between constructors. Return a proxy.
|
||||
if (constructionContext.isConstructing()) {
|
||||
// TODO (crazybob): if we can't proxy this object, can we proxy the
|
||||
// other object?
|
||||
return constructionContext.createProxy(expectedType);
|
||||
}
|
||||
|
||||
// If we're re-entering this factory while injecting fields or methods,
|
||||
// return the same instance. This prevents infinite loops.
|
||||
T t = constructionContext.getCurrentReference();
|
||||
if (t != null) {
|
||||
return t;
|
||||
}
|
||||
|
||||
try {
|
||||
// First time through...
|
||||
constructionContext.startConstruction();
|
||||
try {
|
||||
Object[] parameters =
|
||||
getParameters(constructor, context, parameterInjectors);
|
||||
t = constructor.newInstance(parameters);
|
||||
constructionContext.setProxyDelegates(t);
|
||||
} finally {
|
||||
constructionContext.finishConstruction();
|
||||
}
|
||||
|
||||
// Store reference. If an injector re-enters this factory, they'll
|
||||
// get the same reference.
|
||||
constructionContext.setCurrentReference(t);
|
||||
|
||||
// Inject fields and methods.
|
||||
for (Injector injector : injectors) {
|
||||
injector.inject(context, t);
|
||||
}
|
||||
|
||||
return t;
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
constructionContext.removeCurrentReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ParameterInjector<T> {
|
||||
|
||||
final ExternalContext<T> externalContext;
|
||||
final InternalFactory<? extends T> factory;
|
||||
|
||||
public ParameterInjector(ExternalContext<T> externalContext,
|
||||
InternalFactory<? extends T> factory) {
|
||||
this.externalContext = externalContext;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
T inject(Member member, InternalContext context) {
|
||||
ExternalContext<?> previous = context.getExternalContext();
|
||||
context.setExternalContext(externalContext);
|
||||
try {
|
||||
return factory.create(context);
|
||||
} finally {
|
||||
context.setExternalContext(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object[] getParameters(Member member, InternalContext context,
|
||||
ParameterInjector[] parameterInjectors) {
|
||||
if (parameterInjectors == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object[] parameters = new Object[parameterInjectors.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = parameterInjectors[i].inject(member, context);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
void inject(Object o, InternalContext context) {
|
||||
List<Injector> injectors = this.injectors.get(o.getClass());
|
||||
for (Injector injector : injectors) {
|
||||
injector.inject(context, o);
|
||||
}
|
||||
}
|
||||
|
||||
<T> T inject(Class<T> implementation, InternalContext context) {
|
||||
try {
|
||||
ConstructorInjector<T> constructor = getConstructor(implementation);
|
||||
return implementation.cast(
|
||||
constructor.construct(context, implementation));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> T getInstance(Class<T> type, String name, InternalContext context) {
|
||||
ExternalContext<?> previous = context.getExternalContext();
|
||||
Key<T> key = Key.newInstance(type, name);
|
||||
context.setExternalContext(ExternalContext.newInstance(null, key, this));
|
||||
try {
|
||||
InternalFactory o = getFactory(key);
|
||||
if (o != null) {
|
||||
return getFactory(key).create(context);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
context.setExternalContext(previous);
|
||||
}
|
||||
}
|
||||
|
||||
<T> T getInstance(Class<T> type, InternalContext context) {
|
||||
return getInstance(type, DEFAULT_NAME, context);
|
||||
}
|
||||
|
||||
public void inject(final Object o) {
|
||||
callInContext(new ContextualCallable<Void>() {
|
||||
public Void call(InternalContext context) {
|
||||
inject(o, context);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public <T> T inject(final Class<T> implementation) {
|
||||
return callInContext(new ContextualCallable<T>() {
|
||||
public T call(InternalContext context) {
|
||||
return inject(implementation, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public <T> T getInstance(final Class<T> type, final String name) {
|
||||
return callInContext(new ContextualCallable<T>() {
|
||||
public T call(InternalContext context) {
|
||||
return getInstance(type, name, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public <T> T getInstance(final Class<T> type) {
|
||||
return callInContext(new ContextualCallable<T>() {
|
||||
public T call(InternalContext context) {
|
||||
return getInstance(type, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Set<String> getInstanceNames(final Class<?> type) {
|
||||
return factoryNamesByType.get(type);
|
||||
}
|
||||
|
||||
ThreadLocal<Object[]> localContext =
|
||||
new ThreadLocal<Object[]>() {
|
||||
@Override
|
||||
protected Object[] initialValue() {
|
||||
return new Object[1];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Looks up thread local context. Creates (and removes) a new context if
|
||||
* necessary.
|
||||
*/
|
||||
<T> T callInContext(ContextualCallable<T> callable) {
|
||||
Object[] reference = localContext.get();
|
||||
if (reference[0] == null) {
|
||||
reference[0] = new InternalContext(this);
|
||||
try {
|
||||
return callable.call((InternalContext)reference[0]);
|
||||
} finally {
|
||||
// Only remove the context if this call created it.
|
||||
reference[0] = null;
|
||||
}
|
||||
} else {
|
||||
// Someone else will clean up this context.
|
||||
return callable.call((InternalContext)reference[0]);
|
||||
}
|
||||
}
|
||||
|
||||
interface ContextualCallable<T> {
|
||||
T call(InternalContext context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a constructor function for a given implementation class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
|
||||
return constructors.get(implementation);
|
||||
}
|
||||
|
||||
final ThreadLocal<Object> localScopeStrategy =
|
||||
new ThreadLocal<Object>();
|
||||
|
||||
public void setScopeStrategy(Scope.Strategy scopeStrategy) {
|
||||
this.localScopeStrategy.set(scopeStrategy);
|
||||
}
|
||||
|
||||
public void removeScopeStrategy() {
|
||||
this.localScopeStrategy.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a field or method in a given object.
|
||||
*/
|
||||
interface Injector extends Serializable {
|
||||
void inject(InternalContext context, Object o);
|
||||
}
|
||||
|
||||
static class MissingDependencyException extends Exception {
|
||||
|
||||
MissingDependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
|
||||
/**
|
||||
* Context of the current injection.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public interface Context {
|
||||
|
||||
/**
|
||||
* Gets the {@link Container}.
|
||||
*/
|
||||
Container getContainer();
|
||||
|
||||
/**
|
||||
* Gets the current scope strategy. See {@link
|
||||
* Container#setScopeStrategy(Scope.Strategy)}.
|
||||
*
|
||||
* @throws IllegalStateException if no strategy has been set
|
||||
*/
|
||||
Scope.Strategy getScopeStrategy();
|
||||
|
||||
/**
|
||||
* Gets the field, method or constructor which is being injected. Returns
|
||||
* {@code null} if the object currently being constructed is pre-loaded as
|
||||
* a singleton or requested from {@link Container#getInstance(Class)}.
|
||||
*/
|
||||
Member getMember();
|
||||
|
||||
/**
|
||||
* Gets the type of the field or parameter which is being injected.
|
||||
*/
|
||||
Class<?> getType();
|
||||
|
||||
/**
|
||||
* Gets the name of the injection specified by {@link Inject#value()}.
|
||||
*/
|
||||
String getName();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
/**
|
||||
* Thrown when a dependency is misconfigured.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public class DependencyException extends RuntimeException {
|
||||
|
||||
public DependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DependencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DependencyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (C) 2006 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.opensymphony.xwork2.inject;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* An immutable snapshot of the current context which is safe to
|
||||
* expose to client code.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
class ExternalContext<T> implements Context {
|
||||
|
||||
final Member member;
|
||||
final Key<T> key;
|
||||
final ContainerImpl container;
|
||||
|
||||
public ExternalContext(Member member, Key<T> key, ContainerImpl container) {
|
||||
this.member = member;
|
||||
this.key = key;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return key.getType();
|
||||
}
|
||||
|
||||
public Scope.Strategy getScopeStrategy() {
|
||||
return (Scope.Strategy) container.localScopeStrategy.get();
|
||||
}
|
||||
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
public Member getMember() {
|
||||
return member;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return key.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Context" + new LinkedHashMap<String, Object>() {{
|
||||
put("member", member);
|
||||
put("type", getType());
|
||||
put("name", getName());
|
||||
put("container", container);
|
||||
}}.toString();
|
||||
}
|
||||
|
||||
static <T> ExternalContext<T> newInstance(Member member, Key<T> key,
|
||||
ContainerImpl container) {
|
||||
return new ExternalContext<T>(member, key, container);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user