bob's latest API

git-svn-id: https://svn.apache.org/repos/asf/struts/action2/trunk@408662 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Lightbody
2006-05-22 13:47:57 +00:00
parent 431d293071
commit d9f29b3d57
20 changed files with 426 additions and 303 deletions
+2 -1
View File
@@ -16,18 +16,19 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<!-- has to be compile for WebWorkTestCase, which is part of the base package so others can write unit tests -->
<optional>true</optional>
</dependency>
<dependency>
<groupId>easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
<version>2.0</version>
</dependency>
</dependencies>
<build>
@@ -1,45 +0,0 @@
package org.apache.struts.action2;
/**
* Implemented by actions that may need to record error messages. For example:
*
* <pre>
* static import ResultNames.*;
*
* public class SetName implements ErrorAware {
*
* Messages errors;
* String name;
*
* public String execute() {
* if ("".equals(name) {
* errors.add("name.required");
* return INPUT;
* }
*
* ...
* return SUCCESS;
* }
*
* public void setErrors(Messages errors) {
* this.errors = errors;
* }
*
* public void setName(String name) {
* this.name = name;
* }
* }
* </pre>
*
* @see MessageAware
* @author crazybob@google.com (Bob Lee)
*/
public interface ErrorAware {
/**
* Sets error messages.
*
* @param errors error messages
*/
void setErrors(Messages errors);
}
@@ -1,27 +1,33 @@
package org.apache.struts.action2;
/**
* Implemented by actions that may need to record messages.
* Implemented by actions which may need to record errors or messages.
*
* <pre>
* static import ResultNames.*;
*
* public class Welcome implements MessageAware {
* public class SetName implements MessageAware {
*
* Messages messages;
* String name;
*
* public String execute() {
* messages.add("welcome");
* return SUCCESS;
* }
*
* public void setName(String name) {
* if ("".equals(name))
* messages.forField("name").addError("name.required");
*
* this.name = name;
* }
*
* public void setMessages(Messages messages) {
* this.messages = messages;
* }
* }
* </pre>
*
* @see ErrorAware
* @author crazybob@google.com (Bob Lee)
*/
public interface MessageAware {
@@ -2,71 +2,193 @@ package org.apache.struts.action2;
import java.util.List;
import java.util.Set;
import java.util.Map;
/**
* Request and field-scoped messages or errors. Uses keys instead of actual messages to decouple code from messages.
* Messages may come from multiple actions and interceptors.
* Collection of messages. Supports nesting messages by field name.
*
* <p>Uses keys when adding instead of actual messages to decouple code from messages.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface Messages {
/**
* Adds request-scoped message.
* Message severity.
*/
public enum Severity {
/**
* Informational messages.
*/
INFO,
/**
* Warning messages.
*/
WARN,
/**
* Error messages.
*/
ERROR,
}
/**
* Gets nested messages for the given field.
*
* <p>Supports dot notation to represent nesting. For example:
*
* <pre>
* messages.forField("foo").forField("bar") == messages.forField("foo.bar")
* </pre>
*
* @param fieldName name of the field
* @return nested {@code Messages} for given field name
*/
Messages forField(String fieldName);
/**
* Gets map of field name to messages for that field.
*
* @return map of field name to {@code Messages}
*/
Map<String, Messages> forFields();
/**
* Adds informational message.
*
* @param key message key
* @see Severity.INFO
*/
void add(String key);
void addInformation(String key);
/**
* Adds informational message.
*
* @param key message key
* @param arguments message arguments
* @see Severity.INFO
*/
void addInformation(String key, Object... arguments);
/**
* Adds warning message.
*
* @param key message key
* @see Severity.WARN
*/
void addWarning(String key);
/**
* Adds warning message.
*
* @param key message key
* @param arguments message arguments
* @see Severity.WARN
*/
void addWarning(String key, Object... arguments);
/**
* Adds error message.
*
* @param key message key
* @see Severity.ERROR
*/
void addError(String key);
/**
* Adds error message.
*
* @param key message key
* @param arguments message arguments
* @see Severity.ERROR
*/
void addError(String key, Object... arguments);
/**
* Adds message.
*
* @param severity message severity
* @param key message key
*/
void add(Severity severity, String key);
/**
* Adds request-scoped message.
*
* @param severity message severity
* @param key message key
* @param arguments message arguments
*/
void add(String key, Object... arguments);
void add(Severity severity, String key, Object... arguments);
/**
* Adds field-scoped message.
* Gets set of severities for which this {@code Messages} instance has messages. Not recursive.
*
* @param fieldName name of field to attach message to
* @param key message key
* @return unmodifiable set of {@link Severity} sorted from least to most severe
*/
void add(String fieldName, String key);
Set<Severity> getSeverities();
/**
* Adds field-scoped message.
* Gets message strings for the given severity. Not recursive.
*
* @param fieldName name of field to attach message to
* @param key message key
* @param arguments message arguments
* @param severity message severity
* @return unmodifiable list of messages
*/
void add(String fieldName, String key, Object... arguments);
List<String> forSeverity(Severity severity);
/**
* Gets request-scoped messages.
* Gets error message strings for this {@code Messages} instance. Not recursive.
*
* @return unmodifiable list of messages for this request.
* @return unmodifiable list of messages
*/
List<String> forRequest();
List<String> getErrors();
/**
* Gets field-scoped messages.
* Gets error message strings for this {@code Messages} instance. Not recursive.
*
* @param fieldName field name
* @return unmodifiable list of messages for the given field name.
* @return unmodifiable list of messages
*/
List<String> forField(String fieldName);
List<String> getWarnings();
/**
* Gets names of fields which have messages attached.
* Gets informational message strings for this {@code Messages} instance. Not recursive.
*
* @return unmodifiable set of field names with messages attached.
* @return unmodifiable list of messages
*/
Set<String> getFieldNames();
List<String> getInformation();
/**
* Returns true if no request or field-scoped messages have been added.
* Returns true if this or a nested {@code Messages} instance has error messages.
*
* @see Severity.ERROR
*/
boolean hasErrors();
/**
* Returns true if this or a nested {@code Messages} instance has warning messages.
*
* @see Severity.WARN
*/
boolean hasWarnings();
/**
* Returns true if this or a nested {@code Messages} instance has informational messages.
*
* @see Severity.INFO
*/
boolean hasInformation();
/**
* Returns true if this and all nested {@code Messages} instances have no messages.
*/
boolean isEmpty();
}
/**
* Returns true if this and all nested {@code Messages} instances have no messages for the given severity.
*
* @param severity message severity
*/
boolean isEmpty(Severity severity);
}
@@ -4,10 +4,9 @@ package org.apache.struts.action2;
* Implemented by actions which wish to execute some validation logic before their action method. Useful for
* cross-field validations.
*
* @see ErrorAware
* @author crazybob@google.com (Bob Lee)
*/
public interface Validatable {
public interface Validatable extends MessageAware {
/**
* Validates input. Executes before action method.
@@ -1,7 +1,7 @@
package org.apache.struts.action2.attribute;
import org.apache.struts.action2.spi.Request;
import org.apache.struts.action2.spi.ThreadLocalRequest;
import org.apache.struts.action2.spi.RequestContext;
import org.apache.struts.action2.spi.ThreadLocalRequestContext;
/**
* A servlet context attribute. Synchronizes on the underlying {@code ServletContext} instance.
@@ -20,9 +20,9 @@ public class ApplicationAttribute<T> extends AbstractAttribute<T> {
}
T execute(UnitOfWork<T> unitOfWork) {
Request request = ThreadLocalRequest.get();
synchronized (request.getServletContext()) {
return unitOfWork.execute(request.getApplicationMap());
RequestContext requestContext = ThreadLocalRequestContext.get();
synchronized (requestContext.getServletContext()) {
return unitOfWork.execute(requestContext.getApplicationMap());
}
}
}
@@ -1,7 +1,7 @@
package org.apache.struts.action2.attribute;
import org.apache.struts.action2.spi.Request;
import org.apache.struts.action2.spi.ThreadLocalRequest;
import org.apache.struts.action2.spi.RequestContext;
import org.apache.struts.action2.spi.ThreadLocalRequestContext;
/**
* A request attribute. Synchronizes on the underlying {@code HttpServletRequest} instance.
@@ -20,9 +20,9 @@ public class RequestAttribute<T> extends AbstractAttribute<T> {
}
T execute(UnitOfWork<T> unitOfWork) {
Request request = ThreadLocalRequest.get();
synchronized (request.getServletRequest()) {
return unitOfWork.execute(request.getAttributeMap());
RequestContext requestContext = ThreadLocalRequestContext.get();
synchronized (requestContext.getServletRequest()) {
return unitOfWork.execute(requestContext.getAttributeMap());
}
}
}
@@ -1,7 +1,7 @@
package org.apache.struts.action2.attribute;
import org.apache.struts.action2.spi.Request;
import org.apache.struts.action2.spi.ThreadLocalRequest;
import org.apache.struts.action2.spi.RequestContext;
import org.apache.struts.action2.spi.ThreadLocalRequestContext;
/**
* A session attribute. Synchronizes on the underlying {@code HttpSession}.
@@ -20,9 +20,9 @@ public class SessionAttribute<T> extends AbstractAttribute<T> {
}
T execute(UnitOfWork<T> unitOfWork) {
Request request = ThreadLocalRequest.get();
synchronized (request.getServletRequest().getSession()) {
return unitOfWork.execute(request.getSessionMap());
RequestContext requestContext = ThreadLocalRequestContext.get();
synchronized (requestContext.getServletRequest().getSession()) {
return unitOfWork.execute(requestContext.getSessionMap());
}
}
}
@@ -1,4 +1,4 @@
/**
* Struts Action 2.0 user API.
* Struts Action 2.0 user API
*/
package org.apache.struts.action2;
@@ -0,0 +1,59 @@
package org.apache.struts.action2.spi;
import java.lang.reflect.Method;
/**
* Context of an action execution.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface ActionContext {
/**
* Gets action instance.
*/
Object getAction();
/**
* Gets action method.
*/
Method getMethod();
/**
* Gets action name.
*/
String getActionName();
/**
* Gets the path for the action's namespace.
*/
String getNamespacePath();
/**
* Gets the {@link Result} instance for the action.
*
* @return {@link Result} instance or {@code null} if we don't have a result yet.
*/
Result getResult();
/**
* Adds a result interceptor for the action. Enables executing code before and after a result, executing an
* alternate result, etc.
*/
void addResultInterceptor(Result interceptor);
/**
* Gets context of action which chained to us.
*
* @return context of previous action or {@code null} if this is the first action in the chain
*/
ActionContext getPrevious();
/**
* Gets context of action which this action chained to.
*
* @return context of next action or {@code null} if we haven't chained to another action yet or this is the last
* action in the chain.
*/
ActionContext getNext();
}
@@ -10,7 +10,7 @@ public interface Interceptor {
/**
* Intercepts an action request.
*
* @param request current request
* @param requestContext current request context
*/
String intercept(Request request) throws Exception;
String intercept(RequestContext requestContext) throws Exception;
}
@@ -1,126 +0,0 @@
package org.apache.struts.action2.spi;
import org.apache.struts.action2.Messages;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;
import java.util.Locale;
import java.util.Map;
import java.util.List;
import java.lang.reflect.Method;
/**
* A Struts request. A single request may span multiple actions with action chaining.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface Request {
/**
* Gets current action instance.
*/
Object getAction();
/**
* Gets current action method.
*/
Method getMethod();
/**
* Gets current action name.
*/
String getActionName();
/**
* Gets the path for the current action's namespace.
*/
String getNamespacePath();
/**
* Gets the {@link Result} instance for the current action.
*
* @return {@link Result} instance or {@code null} if we don't have a result yet.
*/
Result getResult();
/**
* Adds a result interceptor for the current action. Enables executing code before and after a result, executing
* an alternate result, etc.
*/
void addResultInterceptor(Result interceptor);
/**
* Gets map of request parameters.
*/
Map<String, String[]> getParameterMap();
/**
* Gets map of request attributes.
*/
Map<String, Object> getAttributeMap();
/**
* Gets map of session attributes.
*/
Map<String, Object> getSessionMap();
/**
* Gets map of application (servlet context) attributes.
*/
Map<String, Object> getApplicationMap();
/**
* Finds cookies with the given name,
*/
List<Cookie> findCookiesForName(String name);
/**
* Gets locale.
*/
Locale getLocale();
/**
* Sets locale. Stores the locale in the session for future requests.
*/
void setLocale(Locale locale);
/**
* Gets messages.
*/
Messages getMessages();
/**
* Gets error messages.
*/
Messages getErrors();
/**
* Gets the servlet request.
*/
HttpServletRequest getServletRequest();
/**
* Gets the servlet response.
*/
HttpServletResponse getServletResponse();
/**
* Gets the servlet context.
*/
ServletContext getServletContext();
/**
* Gets the value stack.
*/
ValueStack getValueStack();
/**
* Invokes the next interceptor or the action method if no more interceptors remain.
*
* @return result name
* @throws IllegalStateException if already invoked or called from the action
*/
String proceed() throws Exception;
}
@@ -1,16 +0,0 @@
package org.apache.struts.action2.spi;
/**
* Implemented by actions that need access to the current {@link Request}. Use judiciously.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface RequestAware {
/**
* Sets {@link Request}.
*
* @param request
*/
void setRequest(Request request);
}
@@ -0,0 +1,106 @@
package org.apache.struts.action2.spi;
import org.apache.struts.action2.Messages;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Request context. A single request may span multiple actions with action chaining.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface RequestContext {
/**
* Gets context of the currently executing action.
*
* @return current action context
*/
ActionContext getActionContext();
/**
* Convenience method.&nbsp;Equivalent to {@code getActionContext().getAction()}.
*
* @return currently executing action
*/
Object getAction();
/**
* Gets map of request parameters.
*/
Map<String, String[]> getParameterMap();
/**
* Gets map of request attributes.
*/
Map<String, Object> getAttributeMap();
/**
* Gets map of session attributes.
*/
Map<String, Object> getSessionMap();
/**
* Gets map of application (servlet context) attributes.
*/
Map<String, Object> getApplicationMap();
/**
* Finds cookies with the given name,
*/
List<Cookie> findCookiesForName(String name);
/**
* Gets locale.
*/
Locale getLocale();
/**
* Sets locale. Stores the locale in the session for future requests.
*/
void setLocale(Locale locale);
/**
* Gets messages.
*/
Messages getMessages();
/**
* Gets error messages.
*/
Messages getErrors();
/**
* Gets the servlet request.
*/
HttpServletRequest getServletRequest();
/**
* Gets the servlet response.
*/
HttpServletResponse getServletResponse();
/**
* Gets the servlet context.
*/
ServletContext getServletContext();
/**
* Gets the value stack.
*/
ValueStack getValueStack();
/**
* Invokes the next interceptor or the action method if no more interceptors remain.
*
* @return result name
* @throws IllegalStateException if already invoked or called from the action
*/
String proceed() throws Exception;
}
@@ -0,0 +1,17 @@
package org.apache.struts.action2.spi;
/**
* Implemented by actions that need access to the current {@link org.apache.struts.action2.spi.RequestContext}. Use
* judiciously.
*
* @author crazybob@google.com (Bob Lee)
*/
public interface RequestContextAware {
/**
* Sets {@link org.apache.struts.action2.spi.RequestContext}.
*
* @param requestContext
*/
void setRequestContext(RequestContext requestContext);
}
@@ -10,7 +10,7 @@ public interface Result {
/**
* Executes result.
*
* @param request
* @param requestContext
*/
void execute(Request request) throws Exception;
void execute(RequestContext requestContext) throws Exception;
}
@@ -1,54 +0,0 @@
package org.apache.struts.action2.spi;
import java.util.concurrent.Callable;
/**
* Provides a reference to the current {@link Request} for this thread.
*
* <p>Actions which spawn additional threads are responsible for setting this value if access to Struts from the
* additional thread is needed.
*
* @author crazybob@google.com (Bob Lee)
*/
public final class ThreadLocalRequest {
static ThreadLocal<Request> threadLocalRequest = new ThreadLocal<Request>();
private ThreadLocalRequest() {}
/**
* Sets {@link Request} for the current thread and invokes the provided {@link Callable}. Restores previous {@code
* Request} (if any) when finished.
*
* @param request for current thread
* @param callable
* @return result of {@code callable}
* @throws Exception from {@code callable}
*/
public static <T> T setAndCall(Request request, Callable<T> callable) throws Exception {
Request old = threadLocalRequest.get();
try {
threadLocalRequest.set(request);
return callable.call();
} finally {
if (old == null)
threadLocalRequest.remove();
else
threadLocalRequest.set(old);
}
}
/**
* Gets the {@link Request} for the current thread.
*
* @return request for current thread
* @throws IllegalStateException if no request has been set
*/
public static Request get() {
Request request = threadLocalRequest.get();
if (request == null) {
throw new IllegalStateException(ThreadLocalRequest.class.getName() + " has not been set.");
}
return request;
}
}
@@ -0,0 +1,54 @@
package org.apache.struts.action2.spi;
import java.util.concurrent.Callable;
/**
* Provides a reference to the current {@link RequestContext} for this thread.
*
* <p>Actions which spawn additional threads are responsible for setting this value if access to Struts from the
* additional thread is needed.
*
* @author crazybob@google.com (Bob Lee)
*/
public final class ThreadLocalRequestContext {
static ThreadLocal<RequestContext> threadLocalRequestContext = new ThreadLocal<RequestContext>();
private ThreadLocalRequestContext() {}
/**
* Sets {@link RequestContext} for the current thread and invokes the provided {@link Callable}. Restores previous
* {@code RequestContext} (if any) when finished.
*
* @param requestContext for current thread
* @param callable
* @return result of {@code callable}
* @throws Exception from {@code callable}
*/
public static <T> T setAndCall(RequestContext requestContext, Callable<T> callable) throws Exception {
RequestContext old = threadLocalRequestContext.get();
try {
threadLocalRequestContext.set(requestContext);
return callable.call();
} finally {
if (old == null)
threadLocalRequestContext.remove();
else
threadLocalRequestContext.set(old);
}
}
/**
* Gets the {@link RequestContext} for the current thread.
*
* @return request for current thread
* @throws IllegalStateException if no request has been set
*/
public static RequestContext get() {
RequestContext requestContext = threadLocalRequestContext.get();
if (requestContext == null) {
throw new IllegalStateException(ThreadLocalRequestContext.class.getName() + " has not been set.");
}
return requestContext;
}
}
@@ -1,4 +1,4 @@
/**
* Struts Action 2.0 service provider API.
* Struts Action 2.0 service provider API
*/
package org.apache.struts.action2.spi;
@@ -11,21 +11,21 @@ import java.util.concurrent.Callable;
public class ThreadLocalRequestTest extends TestCase {
public void testSetAndCall() throws Exception {
final Request r1 = createMock(Request.class);
final Request r2 = createMock(Request.class);
final RequestContext r1 = createMock(RequestContext.class);
final RequestContext r2 = createMock(RequestContext.class);
ensureNotSet();
String result = ThreadLocalRequest.setAndCall(r1, new Callable<String>() {
String result = ThreadLocalRequestContext.setAndCall(r1, new Callable<String>() {
public String call() throws Exception {
assertSame(r1, ThreadLocalRequest.get());
String result = ThreadLocalRequest.setAndCall(r2, new Callable<String>() {
assertSame(r1, ThreadLocalRequestContext.get());
String result = ThreadLocalRequestContext.setAndCall(r2, new Callable<String>() {
public String call() throws Exception {
assertSame(r2, ThreadLocalRequest.get());
assertSame(r2, ThreadLocalRequestContext.get());
return "foo";
}
});
assertSame(r1, ThreadLocalRequest.get());
assertSame(r1, ThreadLocalRequestContext.get());
return result;
}
});
@@ -37,7 +37,7 @@ public class ThreadLocalRequestTest extends TestCase {
private void ensureNotSet() {
try {
ThreadLocalRequest.get();
ThreadLocalRequestContext.get();
fail();
}
catch (IllegalStateException e) { /* ignore */ }