001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.struts;
018:
019: import javax.servlet.http.HttpServletRequest;
020: import javax.servlet.http.HttpServletResponse;
021:
022: import org.apache.struts.action.Action;
023: import org.apache.struts.action.ActionForm;
024: import org.apache.struts.action.ActionForward;
025: import org.apache.struts.action.ActionMapping;
026: import org.apache.struts.action.ActionServlet;
027: import org.apache.struts.config.ModuleConfig;
028:
029: import org.springframework.beans.BeansException;
030: import org.springframework.web.context.WebApplicationContext;
031:
032: /**
033: * Proxy for a Spring-managed Struts <code>Action</code> that is defined in
034: * {@link ContextLoaderPlugIn ContextLoaderPlugIn's}
035: * {@link WebApplicationContext}.
036: *
037: * <p>The proxy is defined in the Struts config file, specifying this
038: * class as the action class. This class will delegate to a Struts
039: * <code>Action</code> bean in the <code>ContextLoaderPlugIn</code> context.
040: *
041: * <pre class="code"><action path="/login" type="org.springframework.web.struts.DelegatingActionProxy"/></pre>
042: *
043: * The name of the <code>Action</code> bean in the
044: * <code>WebApplicationContext</code> will be determined from the mapping
045: * path and module prefix. This can be customized by overriding the
046: * <code>determineActionBeanName</code> method.
047: *
048: * <p>Example:
049: * <ul>
050: * <li>mapping path "/login" -> bean name "/login"<br>
051: * <li>mapping path "/login", module prefix "/mymodule" ->
052: * bean name "/mymodule/login"
053: * </ul>
054: *
055: * <p>A corresponding bean definition in the <code>ContextLoaderPlugin</code>
056: * context would look as follows; notice that the <code>Action</code> is now
057: * able to leverage fully Spring's configuration facilities:
058: *
059: * <pre class="code">
060: * <bean name="/login" class="myapp.MyAction">
061: * <property name="...">...</property>
062: * </bean></pre>
063: *
064: * Note that you can use a single <code>ContextLoaderPlugIn</code> for all
065: * Struts modules. That context can in turn be loaded from multiple XML files,
066: * for example split according to Struts modules. Alternatively, define one
067: * <code>ContextLoaderPlugIn</code> per Struts module, specifying appropriate
068: * "contextConfigLocation" parameters. In both cases, the Spring bean name
069: * has to include the module prefix.
070: *
071: * <p>If you want to avoid having to specify <code>DelegatingActionProxy</code>
072: * as the <code>Action</code> type in your struts-config file (for example to
073: * be able to generate your Struts config file with XDoclet) consider using the
074: * {@link DelegatingRequestProcessor DelegatingRequestProcessor}.
075: * The latter's disadvantage is that it might conflict with the need
076: * for a different <code>RequestProcessor</code> subclass.
077: *
078: * <p>The default implementation delegates to the {@link DelegatingActionUtils}
079: * class as much as possible, to reuse as much code as possible with
080: * <code>DelegatingRequestProcessor</code> and
081: * {@link DelegatingTilesRequestProcessor}.
082: *
083: * <p>Note: The idea of delegating to Spring-managed Struts Actions originated in
084: * Don Brown's <a href="http://struts.sourceforge.net/struts-spring">Spring Struts Plugin</a>.
085: * <code>ContextLoaderPlugIn</code> and <code>DelegatingActionProxy</code>
086: * constitute a clean-room implementation of the same idea, essentially
087: * superseding the original plugin. Many thanks to Don Brown and Matt Raible
088: * for the original work and for the agreement to reimplement the idea in
089: * Spring proper!
090: *
091: * @author Juergen Hoeller
092: * @since 1.0.1
093: * @see #determineActionBeanName
094: * @see DelegatingRequestProcessor
095: * @see DelegatingTilesRequestProcessor
096: * @see DelegatingActionUtils
097: * @see ContextLoaderPlugIn
098: */
099: public class DelegatingActionProxy extends Action {
100:
101: /**
102: * Pass the execute call on to the Spring-managed delegate <code>Action</code>.
103: * @see #getDelegateAction
104: */
105: public ActionForward execute(ActionMapping mapping,
106: ActionForm form, HttpServletRequest request,
107: HttpServletResponse response) throws Exception {
108:
109: Action delegateAction = getDelegateAction(mapping);
110: return delegateAction.execute(mapping, form, request, response);
111: }
112:
113: /**
114: * Return the delegate <code>Action</code> for the given <code>mapping</code>.
115: * <p>The default implementation determines a bean name from the
116: * given <code>ActionMapping</code> and looks up the corresponding bean in
117: * the {@link WebApplicationContext}.
118: * @param mapping the Struts <code>ActionMapping</code>
119: * @return the delegate <code>Action</code>
120: * @throws BeansException if thrown by <code>WebApplicationContext</code> methods
121: * @see #determineActionBeanName
122: */
123: protected Action getDelegateAction(ActionMapping mapping)
124: throws BeansException {
125: WebApplicationContext wac = getWebApplicationContext(
126: getServlet(), mapping.getModuleConfig());
127: String beanName = determineActionBeanName(mapping);
128: return (Action) wac.getBean(beanName, Action.class);
129: }
130:
131: /**
132: * Fetch ContextLoaderPlugIn's {@link WebApplicationContext} from the
133: * <code>ServletContext</code>, falling back to the root
134: * <code>WebApplicationContext</code>.
135: * <p>This context is supposed to contain the Struts <code>Action</code>
136: * beans to delegate to.
137: * @param actionServlet the associated <code>ActionServlet</code>
138: * @param moduleConfig the associated <code>ModuleConfig</code>
139: * @return the <code>WebApplicationContext</code>
140: * @throws IllegalStateException if no <code>WebApplicationContext</code> could be found
141: * @see DelegatingActionUtils#findRequiredWebApplicationContext
142: * @see ContextLoaderPlugIn#SERVLET_CONTEXT_PREFIX
143: */
144: protected WebApplicationContext getWebApplicationContext(
145: ActionServlet actionServlet, ModuleConfig moduleConfig)
146: throws IllegalStateException {
147:
148: return DelegatingActionUtils.findRequiredWebApplicationContext(
149: actionServlet, moduleConfig);
150: }
151:
152: /**
153: * Determine the name of the <code>Action</code> bean, to be looked up in
154: * the <code>WebApplicationContext</code>.
155: * <p>The default implementation takes the
156: * {@link org.apache.struts.action.ActionMapping#getPath mapping path} and
157: * prepends the
158: * {@link org.apache.struts.config.ModuleConfig#getPrefix module prefix},
159: * if any.
160: * @param mapping the Struts <code>ActionMapping</code>
161: * @return the name of the Action bean
162: * @see DelegatingActionUtils#determineActionBeanName
163: * @see org.apache.struts.action.ActionMapping#getPath
164: * @see org.apache.struts.config.ModuleConfig#getPrefix
165: */
166: protected String determineActionBeanName(ActionMapping mapping) {
167: return DelegatingActionUtils.determineActionBeanName(mapping);
168: }
169:
170: }
|