Cleaner URLs
------------
Key: STS-268
URL: http://mc4j.org/jira/browse/STS-268
Project: Stripes
Issue Type: New Feature
Components: ActionBean Dispatching
Affects Versions: Release 1.4
Reporter: Patrick Lightbody
Assigned To: Tim Fennell
To implement "cleaner URLs", I had to set up both a filter and a servlet in
web.xml. I also had to create a custom ActionNameResolver.
web.xml:
---------------
<filter>
<filter-name>StripesActionFilter</filter-name>
<filter-class>com.cparty.stripes.util.CPartyActionBeanFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>StripesActionFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>ServletHelper</servlet-name>
<servlet-class>com.cparty.stripes.util.CPartyServletHelper</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
---------------
Servlet:
---------------
package com.cparty.stripes.util;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
public class CPartyServletHelper extends HttpServlet {
public static CPartyServletHelper servlet;
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
CPartyServletHelper.servlet = this;
}
}
---------------
Filter:
---------------
package com.cparty.stripes.util;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.config.Configuration;
import net.sourceforge.stripes.controller.*;
import net.sourceforge.stripes.exception.StripesServletException;
import net.sourceforge.stripes.validation.BooleanTypeConverter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.PageContext;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Stack;
public class CPartyActionBeanFilter implements Filter {
public static final String RUN_CUSTOM_VALIDATION_WHEN_ERRORS =
"Validation.InvokeValidateWhenErrorsExist";
private Boolean alwaysInvokeValidate;
private FilterConfig filterConfig;
public void init(final FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException {
// It sucks that we have to do this here (in the request cycle), but
there doesn't
// seem to be a good way to get at the Configuration from the Filter in
init()
doOneTimeConfiguration();
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if ("/".equals(request.getServletPath())) {
filterChain.doFilter(request, response);
return;
}
Configuration config = StripesFilter.getConfiguration();
ActionBeanContext context =
config.getActionBeanContextFactory().getContextInstance(request, response);
try {
// we look up the bean name to see if we should pass the request
through or not
StripesFilter.getConfiguration().getActionResolver().getActionBean(context);
} catch (StripesServletException e) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
///////////////////////////////////////////////////////////////////////
// Here beings the reall processing of the request!
///////////////////////////////////////////////////////////////////////
PageContext pageContext = null;
try {
context.setServletContext(filterConfig.getServletContext());
// Then setup the ExecutionContext that we'll use to process this
request
final ExecutionContext ctx = new ExecutionContext();
ctx.setInterceptors(config.getInterceptors(LifecycleStage.ActionBeanResolution));
ctx.setLifecycleStage(LifecycleStage.ActionBeanResolution);
ctx.setActionBeanContext(context);
// It's unclear whether this usage of the JspFactory will work in
all containers. It looks
// like it should, but still, we should be careful not to screw up
regular request
// processing if it should fail. Why do we do this? So we can have
a container-agnostic
// way of getting an ExpressionEvaluator to do expression based
validation
try {
ActionBeanContext abc = ctx.getActionBeanContext();
pageContext =
JspFactory.getDefaultFactory().getPageContext(CPartyServletHelper.servlet, //
the servlet inst
abc.getRequest(), // req
abc.getResponse(), // res
null, // error page url
true, // need session
abc.getResponse().getBufferSize(),
true); // autoflush
DispatcherHelper.setPageContext(pageContext);
}
catch (Exception e) {
// Don't even log this, this failure gets reported if action
beans actually
// try and make use of expression validation, otherwise this is
just noise
}
// Resolve the ActionBean, and if an interceptor returns a
resolution, bail now
Resolution resolution = resolveActionBean(ctx);
saveActionBean(request);
if (resolution == null) {
resolution = resolveHandler(ctx);
if (resolution == null) {
// Then run binding and validation
resolution = doBindingAndValidation(ctx);
if (resolution == null) {
// Then continue on to custom validation
resolution = doCustomValidation(ctx);
if (resolution == null) {
// And then validation error handling
resolution = handleValidationErrors(ctx);
if (resolution == null) {
// And finally(ish) invoking of the event
handler
resolution = invokeEventHandler(ctx);
// If the event produced errors, fill them in
DispatcherHelper.fillInValidationErrors(ctx);
}
}
}
}
}
// Whatever stage it came from, execute the resolution
if (resolution != null) {
executeResolution(ctx, resolution);
}
}
catch (ServletException se) {
throw se;
}
catch (RuntimeException re) {
throw re;
}
catch (InvocationTargetException ite) {
if (ite.getTargetException() instanceof ServletException) {
throw (ServletException) ite.getTargetException();
} else if (ite.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) ite.getTargetException();
} else {
throw new StripesServletException
("ActionBean execution threw an exception.",
ite.getTargetException());
}
}
catch (Exception e) {
throw new StripesServletException("Exception encountered processing
request.", e);
}
finally {
// Make sure to release the page context
if (pageContext != null) {
JspFactory.getDefaultFactory().releasePageContext(pageContext);
DispatcherHelper.setPageContext(null);
}
restoreActionBean(request);
}
}
public void destroy() {
}
/**
* Responsible for resolving the ActionBean for the current request.
Delegates to
* [EMAIL PROTECTED] DispatcherHelper#resolveActionBean(ExecutionContext)}.
*/
protected Resolution resolveActionBean(ExecutionContext ctx) throws
Exception {
return DispatcherHelper.resolveActionBean(ctx);
}
/**
* Responsible for resolving the event handler method for the current
request. Delegates to
* [EMAIL PROTECTED] DispatcherHelper#resolveHandler(ExecutionContext)}.
*/
protected Resolution resolveHandler(ExecutionContext ctx) throws Exception {
return DispatcherHelper.resolveHandler(ctx);
}
/**
* Responsible for executing binding and validation for the current
request. Delegates to
* [EMAIL PROTECTED]
DispatcherHelper#doBindingAndValidation(ExecutionContext, boolean)}.
*/
protected Resolution doBindingAndValidation(ExecutionContext ctx) throws
Exception {
return DispatcherHelper.doBindingAndValidation(ctx, true);
}
/**
* Responsible for executing custom validation methods for the current
request. Delegates to
* [EMAIL PROTECTED] DispatcherHelper#doCustomValidation(ExecutionContext,
boolean)}.
*/
protected Resolution doCustomValidation(ExecutionContext ctx) throws
Exception {
return DispatcherHelper.doCustomValidation(ctx, alwaysInvokeValidate);
}
/**
* Responsible for handling any validation errors that arise during
validation. Delegates to
* [EMAIL PROTECTED]
DispatcherHelper#handleValidationErrors(ExecutionContext)}.
*/
protected Resolution handleValidationErrors(ExecutionContext ctx) throws
Exception {
return DispatcherHelper.handleValidationErrors(ctx);
}
/**
* Responsible for invoking the event handler if no validation errors
occur. Delegates to
* [EMAIL PROTECTED] DispatcherHelper#invokeEventHandler(ExecutionContext)}.
*/
protected Resolution invokeEventHandler(ExecutionContext ctx) throws
Exception {
return DispatcherHelper.invokeEventHandler(ctx);
}
/**
* Responsible for executing the Resolution for the current request.
Delegates to
* [EMAIL PROTECTED] DispatcherHelper#executeResolution(ExecutionContext,
Resolution)}.
*/
protected void executeResolution(ExecutionContext ctx, Resolution
resolution) throws Exception {
DispatcherHelper.executeResolution(ctx, resolution);
}
/**
* Performs a simple piece of one time configuration that requires access
to the
* Configuration object delivered through the Stripes Filter.
*/
private void doOneTimeConfiguration() {
if (alwaysInvokeValidate == null) {
// Check to see if, in this application, validate() methods should
always be run
// even when validation errors already exist
String callValidateWhenErrorsExist =
StripesFilter.getConfiguration()
.getBootstrapPropertyResolver().getProperty(RUN_CUSTOM_VALIDATION_WHEN_ERRORS);
if (callValidateWhenErrorsExist != null) {
BooleanTypeConverter c = new BooleanTypeConverter();
this.alwaysInvokeValidate =
c.convert(callValidateWhenErrorsExist, Boolean.class, null);
}
else {
this.alwaysInvokeValidate = false; // Default behaviour
}
}
}
/**
* Fetches, and lazily creates if required, a Stack in the request to store
ActionBeans
* should the current request involve forwards or includes to other
ActionBeans.
*
* @param request the current HttpServletRequest
* @return the Stack if present, or if creation is requested
*/
protected Stack getActionBeanStack(HttpServletRequest request, boolean
create) {
Stack stack = (Stack)
request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN_STACK);
if (stack == null && create) {
stack = new Stack();
request.setAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN_STACK,
stack);
}
return stack;
}
/**
* Saves the current value of the 'actionBean' attribute in the request so
that it
* can be restored at a later date by calling [EMAIL PROTECTED]
#restoreActionBean(HttpServletRequest)}.
* If no ActionBean is currently stored in the request, nothing is changed.
*
* @param request the current HttpServletRequest
*/
protected void saveActionBean(HttpServletRequest request) {
if (request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN) !=
null) {
Stack stack = getActionBeanStack(request, true);
stack.push(request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN));
}
}
/**
* Restores the previous value of the 'actionBean' attribute in the
request. If no
* ActionBeans have been saved using [EMAIL PROTECTED]
#saveActionBean(HttpServletRequest)} then this
* method has no effect.
*
* @param request the current HttpServletRequest
*/
protected void restoreActionBean(HttpServletRequest request) {
Stack stack = getActionBeanStack(request, false);
if (stack != null && !stack.empty()) {
request.setAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN,
stack.pop());
}
}
}
---------------
ActionNameResolver:
---------------
package com.cparty.stripes.util;
import net.sourceforge.stripes.controller.NameBasedActionResolver;
/**
* We prefer "home" rather than "Home.action", which this class takes care of.
*/
public class CPartyNameBasedActionResolver extends NameBasedActionResolver {
protected String getUrlBinding(String string) {
String original = super.getUrlBinding(string);
int end = original.indexOf(".action");
String temp = original.substring(0, end);
int begin = temp.lastIndexOf("/");
String name = original.substring(begin + 1, end);
name = name.substring(0, 1).toLowerCase() + name.substring(1);
return original.substring(0, begin + 1).toLowerCase() + name ;
}
}
---------------
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://mc4j.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development