• No results found

Model-Driven Action–ModelAction

In document Struts2 Black Book (Page 159-167)

This example mainly focuses on the implementation of the model-driven and prepares Interceptors. This Interceptor works only when the action implements com.opensymphony.xwork2.ModelDriven and com.opensymphony.xwork2.Preparable interfaces, respectively. The action class created here is ModelAction.

This class implements ModelDriven and Preparable interfaces. The implementation of ModelDriven interface exposes Object getModel() method in the action class which returns the object of the separate model class and makes it available in the action for processing. This approach enables the implementation of form bean like approach in which an ActionForm object is separately created to hold and validate the input data. In this way, we can use a POJO as a form bean class, which can be injection for our action using getModel() method.

So, let’s create our POJO form first which is a JavaBean having a set of fields and getter/setter methods.

The fields are ‘rollno’, ‘name’, ‘password’, ‘course’, and ‘city’.

Here’s the code, given in Listing 4.34, for Student class (you can find Student.java file in Code\Chapter 4\struts2_i\WEB-INF\src\com\kogent\action folder in CD):

Listing 4.34: Student.java

public String getName() {

return name;

}

public void setName(String name) { this.name = name;

}

public String getCourse() {

return course;

}

public void setCourse(String course) { this.course = course;

}

public String getCity() {

return city;

}

public void setCity(String city) { this.city = city;

}

public String getPassword() {

return password;

}

public void setPassword(String password) { this.password = password;

}

public int getRollno() {

return rollno;

}

public void setRollno(int rollno) { this.rollno = rollno;

} }

The ModelAction action class declares a member object of Student type, which represents the model for this action here. The getModel() method of ModelDriven interface is defined to return the model object, which happens to be student here.

However, before the model object is returned and populated with data, the object must be created and initialized first. For this purpose, we have implemented the Preparable interface that provides the prepare() method. We can use this method to prepare our model object before the invocation of getModel() method.

Here’s the code, given in Listing 4.35, for ModelAction action class (you can find ModelAction.java file in Code\Chapter 4\struts2_i\WEB-INF\src\com\kogent\action folder in CD):

Listing 4.35: ModelAction.java

package com.kogent.action;

import com.opensymphony.xwork2.ActionSupport;

import com.opensymphony.xwork2.ModelDriven;

import com.opensymphony.xwork2.Preparable;

public class ModelAction extends ActionSupport implements ModelDriven, Preparable{

private Student student;

public Object getModel(){

return student;

}

public void prepare(){

student=new Student();

}

public String execute() throws Exception {

if(student.getPassword().length()>=6)

return SUCCESS;

else{

this.addActionError(getText("app.invalid.password.length"));

return ERROR;

} }

public void validate() {

if ((student.getRollno()== 0) ) {

this.addFieldError("rollno", getText("app.rollno.blank"));

}

if ( (student.getName() == null ) || (student.getName().length() == 0) ) { this.addFieldError("name", getText("app.name.blank"));

}

if ( (student.getPassword() == null ) ||

(student.getPassword().length() == 0) ) {

this.addFieldError("password", getText("app.password.blank"));

}

} }

The Student class does not have any validate method, so the data is still validated in the action class itself. But the real logic of executing different methods of the action lies in the implementation of various Interceptors configured for the action, while providing action mapping.

Configuring Action and Interceptors

In the Interceptor Example 2 being created here, we are implementing the following Interceptors describing their objective and way of working:

‰ exception

‰ prepare

‰ debugging

‰ model-driven

‰ params

‰ conversionError

‰ workflow

Add a new action mapping in struts.xml file to enable the invocation of ModelAction action class.

The name of the action used here is modelAction which matches with the value action attribute of

<s:form> tag used in Listing 4.31 for model.jsp. In addition, an <exception-mapping> has also been provided under <global-exception-mappings>.

Here’s Listing 4.36 showing the new action mapping and exception mapping added:

Listing 4.36: Action mapping for ModelAction action class in struts.xml

<struts>

<include file="struts-default.xml"/>

<package name="default" extends="struts-default">

<global-exception-mappings>

<exception-mapping exception="java.lang.Exception" result="exception"/>

</global-exception-mappings>

<action name="aliasing" class="com.kogent.action.AliasAction">

. . . . . .

</action>

<action name="modelAction" class="com.kogent.action.ModelAction">

<interceptor-ref name="exception"/>

<interceptor-ref name="prepare"/>

<interceptor-ref name="debugging"/>

<interceptor-ref name="model-driven"/>

<interceptor-ref name="params"/>

<interceptor-ref name="conversionError"/>

<interceptor-ref name="workflow"/>

<result name="success">/student_info.jsp</result>

<result name="error">/model.jsp</result>

<result name="exception">/exception.jsp</result>

<result name="input">/model.jsp</result>

</action>

</package>

</struts>

We have declared a set of results for this action mapping having names, success, error, exception, and input. The Interceptors configured here are executed in the order they are placed here (top to bottom) in the Interceptor stack. Now let’s discuss what is being done by each Interceptor.

The exception Interceptor

This Interceptor is made the first Interceptor in the stack ensuring that it has full access to any exception caused by action class and other Interceptors also. With the use of this Interceptor, the exception mapping provided enables action to return a result code exception, instead of throwing unexpected exception. The exception mapping provided here maps all exceptions of type Exception (their subclasses too) with the result code exception. The action mapping also includes a result with the name exception, which causes rendering of exception.jsp page.

The prepare Interceptor

This Interceptor invokes the prepare() method of the action, if it implements Preparable interface.

Placing this Interceptor before Model-Driven Interceptor makes sure that the prepare() method will be executed earlier to getModel() method.

The debugging Interceptor

This Interceptor provides different debugging screens according to the value of the debug parameter passed with the request to the action. This Interceptor is activated only when the devMode is enabled in struts.properties file. This can be done by adding the text, struts.devMode=true in struts.properties. The parameter debug=xml causes the dumping of parameters, context, session and value stack as an XML document.

The model-driven Interceptor

The model-driven Interceptor adds the model of action on the value stack given that the action implements ModelDriven interface. This invokes the getModel() method of action. If the parameters are to be applied to the model, the model-driven Interceptor must come before params and static-params Interceptors in the Interceptor stack.

The params Interceptor

The params Interceptor gets all parameters from ActionContext using its getParameters() methods and sets them on the value stack using the ValueStack.setValue(String, Object) method.

The conversionError Interceptor

This Interceptor adds conversionErrors map as the field errors. The condition for this Interceptor to be functional is that the action should be implementing the ValidationAware interface. The original value of the fields are saved for the subsequent requests.

The workflow Interceptor

This Interceptor invokes the validate() method, prior to execute() method, and prohibits its execution, if some error is added in validate() method. This Interceptor works with the following two functions:

‰ If the action implements Validateable interface, it invokes the validate() method of the action.

‰ If the action implements ValidationAware interface, and if the hasError() method returns true, the Interceptor stops the execution of action and returns Action.INPUT.

Working of Example

After this discussion about the action and Interceptor configuration for this example, we can run this example by clicking over ‘Interceptor Example 2’ shown in Figure 4.3 and created in Listing 4.26. The page, model.jsp, will appear, as shown in Figure 4.6. If we click on the ‘Submit’ button after leaving fields blank, the workflow Interceptors invokes the validate() method of ModelAction class. The validate() method adds different field errors and the Interceptor returns INPUT as result code. This results is showing of model.jsp with error displayed with the corresponding fields, as shown in Figure 4.7.

Figure 4.7: The model.jsp with field errors set by validate() method.

The data entered into input fields are converted according to the types of fields defined in model class, which has to populate with this data. For example, the ‘Roll No.’ field of Student class is of type int.

When some data is entered in ‘Roll No.’ field, which cannot be converted into int,, an entry is added into conversionErrors map of ActionContext. These errors are added as field errors by the conversionError Interceptor. You can see the field errors set for ‘Roll No.’ field in Figure 4.8.

Figure 4.8: The model.jsp showing conversion error for Roll No. field.

If every thing goes fine and the execute() method returns SUCCESS, the student_success.jsp page is shown with data entered in model.jsp page. Figure 4.9 shows the output of student_info.jsp page.

Figure 4.9: The student_info.jsp showing student data.

The only page, which has not yet been executed, is exception.jsp. This page will be shown only when some exception occurs during the processing of Interceptors and action. Let’s generate an exception in this example.

When the form from model.jsp is submitted, the sequence of method invocation is prepare(), getModel(), validate() and then execute(). Assume that the prepare() method is never executed. This will result in the setting of model student as null. Any method invocation over this object will cause a NullPointerException as the object is referencing to null. We can get this exception by removing the Prepare Interceptor from the Interceptor stack defined for this action, making sure that the prepare() method is not executed and the model remains null. This Exception Interceptor checks for any exception occurred during the processing, which is pushed on the stack. A <exception-mapping> is searched for the exception caused and the corresponding result code is returned. The result code returned here is exception, which results in the display of the exception.jsp page, as shown in Figure 4.10.

Figure 4.10: The exception.jsp showing message.

Finally to check how the debugging Interceptor behaves, change the action attribute of <s:form> tag in model.jsp to /modelAction.action?debug=xml and again submit the form from model.jsp filling it with some data, say ‘101’, ‘Prakash’, ‘kogentindia’, ‘MCA’ and ‘Delhi’. You’ll get an XML output displayed in your browser, as shown in Listing 4.37.

Listing 4.37: parameters, session, request, context and value stack dumped in XML format

- <debug>

- <parameters>

- <course>

<arrayitem>MCA</arrayitem>

</course>

- <rollno>

<arrayitem>101</arrayitem>

</rollno>

- <name>

<arrayitem>Prakash</arrayitem>

</name>

- <password>

<arrayitem>kogentindia</arrayitem>

</password>

- <city>

<arrayitem>Delhi</arrayitem>

</city>

</parameters>

- <context>

<attr />

<report.conversion.errors>false</report.conversion.errors>

- <struts.actionMapping>

<class>

class org.apache.struts2.dispatcher.mapper.ActionMapping</class>

<name>modelAction</name>

<namespace>/</namespace>

</struts.actionMapping>

<city>Delhi</city>

<class>class com.kogent.action.Student</class>

<course>MCA</course>

<name>Prakash</name>

<password>kogentindia</password>

<rollno>101</rollno>

</value>

- <value>

<actionErrors />

<actionMessages />

<class>class com.kogent.action.ModelAction</class>

<errorMessages />

<errors />

<fieldErrors />

- <locale>

<ISO3Country>USA</ISO3Country>

<ISO3Language>eng</ISO3Language>

<class>class java.util.Locale</class>

<country>US</country>

<displayCountry>United States</displayCountry>

<displayLanguage>English</displayLanguage>

<displayName>English (United States)</displayName>

<displayVariant />

<language>en</language>

<variant />

</locale>

- <model>

<city>Delhi</city>

<class>class com.kogent.action.Student</class>

<course>MCA</course>

<name>Prakash</name>

<password>kogentindia</password>

<rollno>101</rollno>

</model>

</value>

- <value>

<class>class com.opensymphony.xwork2.DefaultTextProvider</class>

</value>

</valueStack>

</debug>

This helps in debugging, as it provides information about parameters, context, request, session, and value stack.

In document Struts2 Black Book (Page 159-167)