Dear Wiki user, You have subscribed to a wiki page or wiki category on "Struts Wiki" for change notification.
The following page has been changed by MichaelJouravlev: http://wiki.apache.org/struts/StrutsManualActionWebComponentSync ------------------------------------------------------------------------------ == Component Configuration == - First let us define the Login Component in {{{struts-config.xml}}} file: + Defining configuration before creating a component seems backwards, but different configuration options affect component itself, so for the sake of this example let us first define the parameters composite page and the login component in {{{struts-config.xml}}} file. Do not forget to turn automatic validation off, otherwise an event handler will not be called if associated form bean failed validation. In Struts 1.4 event definitions are first-class elements of an action mapping: - {{{ - <struts-config> + {{{<struts-config> <form-beans> <!-- Login form --> @@ -46, +45 @@ </struts-config> }}} - Notice action mapping attributes and properties that are new for Struts 1.4: + Observe attributes and properties that are new for Struts 1.4: - * "component" attribute identifies an action as a component manager, such actions are processed differently by Action class. This name is also used in generated HTML for in-place update in Ajax mode. + * "component" attribute contains the name of a component. It identifies an action as a component manager, it is processed differently than a regular action or behavioral action. * "view" attribute identifies a default view for a component. Must be a JSP page. Often consists from several subviews, in our case the Login Component has two subviews "Not Logged In" and "Logged In", they will be defined in JSP file. * "form" is just another name for "name" property - * "event" property allows to define request parameters as events, and corresponding method handlers. This is made possible by supporting dispatching functionality directly in Action class. + * "event" property allows to define request parameters as events, and corresponding method handlers. + + In Struts 1.2.9 - 1.3.x Action class does not implement dispatching functionality, and action mapping does not allow to define events. It is recommended to use EventDispatchAction to handle incoming events, its events are configured through {{{parameter}}} attribute: + + '''Struts 1.2.9, 1.3.x:''' + {{{<struts-config> + + <form-beans> + <!-- Login form --> + <form-bean name = "loginform" type="samples.login.LoginForm"/> + </form-beans> + + <action-mappings> + + <!-- Composite page containing login component --> + <action path="/login-struts" + forward="/login-struts/index.jsp"/> + + <!-- Login component --> + <action path = "/logincomponent" + type = "samples.login.LoginAction" + name = "loginform" + scope = "session" + validate = "false" + parameter = "loginEvent=login,logoutEvent=logout,default=unspecified"> + <forward name = "view" path = "/login-struts/loginComponent.jsp"/> + </action> + + </action-mappings> + </struts-config> + }}} + + Of course, Struts 1.2.9, 1.3.x do not support new "component", "view", "form" and "event" properties. Use regular {{{forward}}} element to define a component view. Use {{{name}}} attribute to define associated form bean. == Component Action == - The action class is deceptively simple. It handles only two events, the corresponding handlers are called automatically by Action class. The location of component's view is defined in the action mapping, so render method is not needed. On the other hand, most non-trivial components need to process data before rendering themselves or to exchange data with other components. For these cases you can use render" method. Its default implementation does nothing. + With Struts 1.4 the action class is deceptively simple. It handles only two events, the corresponding handlers are called automatically by Action class. The location of component's view is defined in the action mapping, so render method is not needed. On the other hand, most non-trivial components need to process data before rendering themselves or to exchange data with other components. For these cases you can use render" method. Its default implementation does nothing. {{{ public class LoginAction extends Action { @@ -102, +133 @@ // Always return null. return null; + } + } + }}} + + In Struts 1.2.9 - 1.3.x Action class does not implement dispatching functionality, you need to extend a dispatching action. Use EventDispatchAction as the simplest and the most flexible. Another difference is that you need to specify an {{{ActionForward}}} object that points to a component view. You must do it in {{{unspecified}}} method or whatever method you selected as default in event definition. + + Using EventDispatchAction to dispatch events gets you only halfway, because there code that automatically distinguishes the address of a composite page and then redirects to it is not present in older Struts versions. You need an add-on library that contains this code. Then you need to call it from {{{execute}}} method of your action. '''TODO''' + + '''Struts 1.2.9, 1.3.x:''' + {{{ + public class LoginAction extends EventDispatchAction { + + public ActionForward execute (...) throws Exception { + ComponentUtils.componentReload(...); // TODO + } + + public ActionForward login (...) throws Exception { + ... + } + + public ActionForward logout (...) throws Exception { + ... + } + + public ActionForward unspecified (ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) throws Exception { + return "view"; } } }}} @@ -136, +196 @@ } }}} - == Login/Logout JSP page == + A common misconception is that components must use session-scoped form beans. This is not exactly true, but you might want to save data between request somehow, because Struts uses redirection to reload the composite page. Whether you store the data in session-scoped form bean or in database or in disk file is your choice. Obviously, request-scoped data will be lost after redirect. - The Login/Logout component has two subviews, both defined in one JSP page. Notice that content type is set to "text/xml", this is important for Ajax mode. + == Login And Logout Views == + + The Login Component has two subviews, one for a non-logged-in user, another for a logged-in user. In this example views are defined in one JSP page. It is possible to define them in different JSP files, but in this case you will not be able to use {{{view}}} attribute of an action mapping, and you will have to explicitly forward to view locations in similar manner as Struts 1.3.x {{{unspecified}}} method does. {{{ <%@ page contentType="text/xml;charset=UTF-8" language="java" %> - - <%@ page import="java.util.ArrayList, org.apache.struts.Globals"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> @@ -164, +224 @@ </html:messages> </logic:messagesPresent><br/> + <html:form action="/loginintegrated.do"> - <%-- "strutsCommand" CSS class is a hook for Ajax-mode handler, it is set - in runtime using Behaviour library. --%> - - <html:form method="get" styleClass="strutsCommand" action="/loginintegrated.do"> <label for="username">Username:</label> - <input type="text" name="username" value="${loginform.username}" class="datavalue"/><br/> + <input type="text" name="username" value="${loginform.username}"/> <label for="password">Password:</label> - <input type="text" name="password" value="" class="datavalue"/><br/> + <input type="text" name="password" value="" class="datavalue"/> - <input type="submit" name="loginEvent" value="Log In" class="strutsCommand"/> + <input type="submit" name="loginEvent" value="Log In"/> </html:form> - <p><em>Username is "guest", password is "pass".</em></p> </c:if> <%-- "Logged In" subview --%> @@ -184, +240 @@ <c:if test='${not empty USER}'> <h3>User Information</h3> + <html:form action="/loginintegrated.do"> + Current user: ${USER} <br/> - <html:form method="post" styleClass="strutsCommand" action="/loginintegrated.do"> - <label>Current user:</label> - <div class="datavalue">${USER}</div><br/> - <input type="submit" name="logoutEvent" value="Log Out" class="strutsCommand"/><br/> + <input type="submit" name="logoutEvent" value="Log Out"/><br/> </html:form> </c:if> @@ -196, +251 @@ == Composite Page == - Now we need to include the Login Component into a larger page (composite page). This is done with JSTL c:import tag. Do not use jsp:include, it may not work on some containers: + Now we need to include the Login Component into a larger page (composite page). This is done with JSTL <c:import> tag. Do not use <jsp:include>, it may not work on some containers: - {{{ - <%@ page contentType="text/html;charset=UTF-8" language="java" %> + {{{<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> - <%@ taglib uri="http://struts.apache.org/tags-comp" prefix="comp" %> <html> <body> <p>This paragraph is defined directly in the parent page and should precede the content of login control.</p> - <%-- The login component, notice that DIV has the same ID as - the component's name --%> - <div id="Login"> - <c:import url="/loginintegrated.do" /> + <c:import url="/loginintegrated.do" /> - </div> <p>This paragraph is defined directly in the parent page and should follow the content of login control.</p> </body> - </html> + </html>}}} - }}} == Done! == - This is pretty much it. Now run the application and navigate to composite page. The included component will evaluate user's state and will display a login form. Try to log in. The submitted credentials are sent directly to a component, if they are not correct, the composite page is redisplayed. How? Behind the scenes the improved Action class as well as JSP tags work together to distinguish the address of a composite page during first render. This address is saved automatically. Then after component finishes, it reloads the composite page using saved address. Now you can develop independent Struts components! + This is pretty much it. Run the application and navigate to composite page. The included component will evaluate user's state and will display a login form. Try to log in. The submitted credentials are sent directly to a component; if they are not correct, the composite page is redisplayed. How does it happen? The improved Action class as well as JSP tags work together behind the scenes to distinguish the address of a composite page during first render. This address is saved automatically. Then after component finishes, it reloads the composite page using saved address. + Next: [:StrutsManualActionWebComponentAsync:Building dual-mode Struts web component] +