Customizing WebSphere Studio to use the Struts Scripting tool with ActionClasses in Jython

This article describes how to set up WebSphere Studio Application Developer V5.1.2 (hereafter called Application Developer) to use the Struts Scripting tool to implement ActionClasses in Jython. Developing Struts actions using a scripting language gives you a faster write-and-test cycle and lets you reuse existing Jython/Python scripts without translating them into Java. Choosing Jython as your scripting language lets you easily interact with Java and use the Struts framework classes. The configuration, however, is a bit tricky, because it is based on the Bean Scripting Framework, the Struts scripting tool, and Jython. This article provides a step-by-step guide to customize the environment. You should have some experience with WebSphere Studio, Struts, and Jython or Python.

Share:

Laura Hervàs (laura_hervas@es.ibm.com), IT Specialist, IBM Spain

Photo of Laura HervàsLaura Hervàs is a software developer working on Eclipse with IBM Spain. Laura received a Bachelor of Engineering degree in 2000 and has been working since then on the development of IBM SecureEntry. Currently, she is developing a design tool for building graphical business flows that runs as an Eclipse plug-in. You can reach Laura at laura_hervas@es.ibm.com.



Luis Alvarez (luis.alvarez@es.ibm.com), IT Specialist, IBM Spain

Photo of Luis Carlos AlvarezLuis Carlos Alvarez is a software developer working on WebSphere Studio Application Developer with IBM Spain. Luis received a Bachelor of Engineering degree in 1998 and worked for five years on the development of IBM SecureEntry. Currently, he is working on the development of a financial terminal. You can reach Luis at luis.alvarez@es.ibm.com.



30 March 2005

Introduction

Struts is a Model-View-Controller (MVC) framework implementation that uses servlets and JavaServer Pages (JSPs). A Web application using the Struts framework typically has multiple ActionClasses, each of which represents some business processing. The ActionClass delegates the response to a client using the logical name defined in struts-config.xml. The ActionClasses are typically written in Java, but you can write them in popular scripting languages using the Struts scripting tool and reuse your JavaScript, Python, Jython, or TCL code. Python is a high-level, extensible, interpreted, object-oriented programming language that combines remarkable power with clear syntax. It supports a wide range of applications, from simple text processing scripts to interactive Web browsers. Jython is a 100% pure Java implementation of Python. By using Jython, you can use both the Java and the Python libraries, reuse your existing scripts, and shorten your test-and-code cycle because the scripts are loaded at run time. In a normal Web application using Struts, when you change an ActionClass, Application Developer notices it and reloads the modified classes. Using a scripting language, Application Developer doesn't need to reload anything: the ActionClass is always the same and you should only change the script. But the most important advantage of using Python is the productivity improvement. Here is a side-by-side feature comparison of Java and Python.


Configuring your workspace

You will need to install and configure four software packages (see Resources below).

Required software

  1. WebSphere Studio Application Developer V5, which includes Struts support.
  2. The Jython interpreter. To install it, download jython-21.class file and execute java jython-21. Modify this command depending on your JDK version -- to get more information about the installation, see Resources below.
  3. The Struts scripting tool lets Struts developers write Actions using popular scripting languages and customize the environment where the script will be executed.
  4. The Bean Scripting Framework is a Java framework for using and interacting with scripting languages in Java. The Struts scripting tool uses the Bean Scripting Framework to load and invoke scripts.

Optional software

  1. Python plug-in for Application Developer. We installed pyeclipse, but you can choose the one you like. In is not required, but it does give you a more friendly workspace. You can install it by copying the directory org.python.pyeclipse_0.0.1 into eclipse\plugins in your Application Developer installation and restart it. For information on working with Python and Eclipse, see Resources below.
  2. Latest version of the Struts framework. You can download it, but for this article, we used the one shipped with Application Developer.

Building your Web application

Creating the projects

  1. Start Application Developer.
  2. Open the Web perspective.
  3. Create a new Web project: select File => New => Dynamic Web Project. You can add Struts either now or later. To add it now, check Configure advanced options, go to the Features page and select Add Struts support.
  4. Create a new Enterprise Application Project: select File => New => Project => J2EE => Enterprise Application Project. Add the Web project to the application project.

Configuring the project

  1. If you did not create the project with Struts support or you are reusing an old project, you can add it using the Web Project Features in the Properties page of the Web project.
  2. Import the JAR files StrutsScripting11.jar, bsf.jar, and jython.jar into the WebContent\WEB-INF\lib directory.

Creating the main JSP

  1. In the Web project, right-click the WebContent folder and select New JSP File.
  2. Name it Login.jsp (the main page of our Web application will be a logon page).
  3. Application Developer has created a JSP skeleton. You have to fill the <BODY> section with your JSP contents:
  <h1>Login</h1>
      <html:errors/>
      <html:form action="/login.do">
           User: <html:text property="user"/><br>
           Password: <html:password property="password"/><br>
           <html:submit property="submit" value="Ok"/>
           <html:reset/>
      </html:form>

Basically, this JSP calls the Action class linked to the logic action login when the user clicks OK.

The scripted action

At this point, a Struts developer would write the login action in Java in order to load the next Web page. However, we want to write the action in a scripting language, so we will use the ScriptedAction class, which extends the Struts Action to execute a script.

The ScriptedAction class uses the Bean Scripting Framework (BSF) to load and invoke scripts. Currently, only those languages that BSF supports are supported by the ScriptedAction class.

Configuring the ScriptedAction

To configure a ScriptedAction, the type attribute must reference the com.ibm.etools.struts.scripting.ScriptedAction class and the parameter attribute must contain the context root relative path to a script file that will be executed by the ScriptedAction. Modify struts-config.xml to add:

  <action-mappings>
      <action name="loginForm"
              path="/login"
              scope="session"
              type="com.ibm.etools.struts.scripting.ScriptedAction"
              input="/Login.jsp"
              parameter="/scripts/LoginAction.py">
          <forward name="success" path="./YourNextJSP.jsp">
          </forward>
          <forward name="failure" path="./Login.jsp">
          </forward>
      </action>
  </action-mappings>

You can also do this through the Struts Configuration File Editor by selecting the Action Mappings tab. There you can create an Action Mapping called /login:

Figure 1. Configure the Action Mapping
Configure the Action Mapping

Afterwards, change to the Local Forwards tab and create the forward success that loads YourNextJPS.jsp:

Figure 2. Configure the local forward success
Configure the local forward success

Finally, create the forward failure that reloads Login.jsp in the same way as with the forward success.

When the user clicks OK, the Struts framework (using the Struts Scripting tool, which uses the Bean Scripting Framework) will execute the script LoginAction.py.

Developing scripts for ScriptedAction

The next step is to write LoginAction.py. You will need to interact with the Struts framework to get and set information in the bean form, specify the next action, and so on. And you will be able to do this using Struts, a context object provided by the ScriptedAction class. This object has the following methods:

String getScriptPath()path to invoked script
void setForward(ActionForward)set the forward to be returned by the ScriptedAction when the script completes execution
ActionMapping getMapping()the mapping parameter passed to Action.execute()
ActionForm getForm()the form parameter passed to Action.execute()
ServletRequest getRequest()the request parameter passed to Action.execute()
ServletResponse getResponse()the response parameter passed to Action.execute()
boolean isHttpCall()if the invocation of this Action came through Action.execute(ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse)
ScriptedAction getAction()the ScriptedAction that invoked the script
ActionForward findForward(String name)lookup a forward by name (calls mapping.findForward())

When developing a script for ScriptedAction, the Struts object provides all the context and parameters required to do the job. All of the protected methods of the Action class are available from the script: calling Struts.getAction(), you will get the Action instance.

Specifying the scripting language

The Struts Scripting tool can execute Scripted Actions written in several scripting languages, and you need to specify which one you will use. Edit struts-config.xml and add the ScriptedEnvironmentPlugin and two mapping extensions: one to specify the configuration file and the other to enable the debug mode (required to see Jython-specific language errors).

To do this, open the Struts Configuration File Editor and fill the Plug-ins page with:

Figure 3. Configure the ScriptEnvirontmentPlugin
Configure the ScriptEnvirontmentPlugin

Alternatively, you can add the following lines to struts-config.xml:

  <plug-in className="com.ibm.etools.struts.scripting.ScriptEnvironmentPlugin">
      <set-property property="scriptEnvironmentFile"
                    value="/WEB-INF/script-env.xml"/>
      <set-property property="debug" value="true"/>
  </plug-in>

Afterwards, create the plug-in configuration file and add a new tag for the scripting language to use:

  <script-environment>
      <language name="jython">
      </language>
  </script-environment>

Writing the script

The script should do the same job as a normal Struts Action class: save some data in the bean form (in this example, the user), do some processing (for instance, a primitive credentials validation), and specify the local forward to execute or, in other words, which page should be loaded next (in our example, if the validation is successful, the first application Web page, otherwise, reload the login page). You can put the scripts in a folder named scripts to keep your workspace tidy. In fact, if you review Figure 2, you will see that the attribute parameter of the action, which is the script path (relative to WebContent), assumes that the scripts are in that folder. Here is the script:

  import sys

  sys.path.append("YourExistingPythonCodeDirectory")
  sys.add_extdir("YourWebProjectDirectory\\webcontent\\web-inf\\lib")

  import org.apache.struts.action
  import repository

  form = Struts.getForm()
  mapping = Struts.getMapping()
  request = Struts.getRequest()

  userPassword = {"admin":"psswd1", "g01":"psswd2", "u01":"psswd3"}
  userRoles = {"admin":"Administrator", "g01":"Guest", "u01":"User"}

  user = form.getUser()
  password = form.getPassword()
  forward = "failure"

  if (user in userPassword):
      if (password == userPassword[user]):
          request.setAttribute("user", user)
          role = userRoles[user]
          request.setAttribute("role", role)
          forward = "success"
      else:
          errors = org.apache.struts.action.ActionErrors()
          errors.add("invalidpassword", org.apache.struts.action.ActionError("error.invalid.password"))
          action.saveErrors(request,errors)
  else:
    errors = org.apache.struts.action.ActionErrors()
    errors.add("invaliduser", org.apache.struts.action.ActionError("error.invalid.user"))
    action.saveErrors(request,errors)

  Struts.setForward(mapping.findForward(forward))

We are performing a simple validation and setting the attributes user and role to show them when we will open the next JSP. If you look at the script, you will see that you can create ActionErrors like in a normal ActionClass. In this case, we are processing the errors invalid user and invalid password. In fact, you can use all the Java classes you need, like Java traces (log4j, for instance, although we did not test it). If you want to use errors, edit the ApplicationResources.properties file to add the suitable lines. This is the one we are using:

  title=Login
  <hr><h3>Errors</h3><ul>
  errors.header=<hr><h3>Errors</h3><ul>
  errors.footer=</ul><hr>
  errors.required={0} is required
  loginForm.password=Password
  loginForm.user=User name
  errors.minlength={0} cannot be less than {1}
  errors.maxlength={0} cannot be greater than {2}
  error.invalid.user=<li>Invalid user
  error.invalid.password=<li>Invalid password

Creating the ActionForm

The previous script stores the information in the loginForm (see Figure 2 above). Later, the next JSP loaded will access this information. Build the form using the Application Developer wizard: select File => New => Other => Web => Struts => ActionForm class. In this example, we named the form LoginForm and include it in the Java package com.ibm.strutswithjythonweb.forms. It should look like this (we removed some comments generated by the wizard):

  package com.ibm.strutswithjythonweb.forms;

  import javax.servlet.http.HttpServletRequest;
  import org.apache.struts.action.ActionErrors;
  import org.apache.struts.action.ActionForm;
  import org.apache.struts.action.ActionMapping;

  public class LoginForm extends ActionForm {
      private String user = null;
      private String password = null;
      private String role = null;
      private String submit = null;

      public String getUser() {
          return user;
      }

      public void setUser(String u) {
          this.user = u;
      }

      public String getPassword() {
          return password;
      }

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

      public String getRole() {
          return Role;
	  }

	  public void setRole(String role) {
	      this.role = role;
      }

      public String getSubmit() {
          return submit;
      }

      public void setSubmit(String s) {
          this.submit = s;
      }

      public void reset(ActionMapping mapping,
                        HttpServletRequest request) {
          // Reset values are provided as samples only. Change as appropriate.
          user = null;
          password = null;
          role = null;
          submit = null;
      }

      public ActionErrors validate(ActionMapping mapping,
                                   HttpServletRequest request) {
           ActionErrors errors = new ActionErrors();
           // Validate the fields in your form, adding
           // adding each error to this.errors as found
           return errors;
      }
  }

If you do not want to use Java, you can use the DynaActionForm class, which is configured through struts-config.xml. Using both this Action Form and the ScriptedAction lets you make the job without Java.

Configuring the framework to use the bean form

Edit struts-config.xml and add these lines:

  <form-beans>
      <form-bean name="loginForm"
                 type="com.ibm.strutswithjythonweb.struts.LoginForm">
      </form-bean>
  </form-beans>

You can use the Struts Configuration File Editor, as shown in Figure 4: select the Form Beans tab and click New.

Figure 4. Configure the form
Configure the form

Check that your form attributes name and type match with the ActionForm name and package. The names are case-sensitive.

Creating the next JSP to be loaded

In this case we will only create the JSP that will be loaded if the local forward success is executed (if the validation was successful). This JSP accesses the ActionForm to get the user. The action saved the user if the credentials validation was successful (see LoginAction.py). You can create the JSP as Login.jsp and add the next lines in the body section:

Click to see code listing

  <P>The user <%=request.getAttribute("user")%>> logged in with role <%=request.getAttribute("role")%>></P>

Testing the Web application

The last step before testing your Struts Web application is creating a server and customizing its environment, as shown in Figure 5. Basically, you must add the JAR files enumerated in the section "Configuring the project to the WebSphere-specific classpath," set up some environment variables to find the Jython code (python.home) and your Python code (python.path), and then set the Python mode to debug.

Figure 5. Configure the server
Configure the server

Now you can start the server and load Login.jsp in the Web browser, or right-click on Login.jsp and select Run on server. If you click OK, LoginAction.py will be executed and YourNextJSP.jsp will be loaded.

Making the environment more flexible

As your application grows and you have to write a lot of Struts Actions, you will notice that the beginning and end of the scripts are the same for all Jython scripts. As you can see in the script, there are common tasks that will be repeated in all scripts. To avoid this repetition, use a prefix script (a script that executes before the script specified in the Scripted Action) and a suffix script (a script that executes after). You will be able to share variables among the Scripted Action script and the prefix and suffix scripts. Here is a sample of prefix.py with the most common objects you will use in a Scripted Action:

  form = Struts.getForm()
  action = Struts.getAction()
  mapping = Struts.getMapping()
  request = Struts.getRequest()
  response = Struts.getResponse()

You can also use this script to add the Python package imports. In our example, the script suffix.py is not necessary, because we don't repeat a lot of lines at the end of the scripts. But we do have to specify which page we will be loaded next:

  Struts.setForward(mapping.findForward(forward))

Finally, you must tell the framework to use this script: edit script-env.xml and add the prefix-script and suffix-script tags for the Jython language:

  <script-environment>
    <language name="jython">
      <prefix-script path="/WEB-INF/script-env/prefix.py"/>
      <suffix-script path="/WEB-INF/script-env/suffix.py"/>
    </language>
  </script-environment>

Conclusion

Writing Struts Actions in Jython makes the Struts framework more flexible, but the installation and configuration of the required packages is not easy. This article explained how to configure them so that you can benefit from the power and clear syntax of Python. Moreover, it provides a faster code cycle: you need only modify the Action Class, reload the page, and perform the user action that executes that action. Restarting the server will no longer be necessary if the changes only affect the Scripted Action.


Acknowledgements

The authors wish to thank Xavier Verg Farrero for encouraging us to write this article and for his corrections, Jordi Buj Aris for his critical reading and useful discussions about Struts, and David Charboneau for his help in configuring the StrutsScripting tool.


Download

DescriptionNameSize
Code sampleStrutsScripting11.zip  ( HTTP | FTP )12 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Java technology
ArticleID=87031
ArticleTitle=Customizing WebSphere Studio to use the Struts Scripting tool with ActionClasses in Jython
publish-date=03302005