CARVIEW |
![]() |
![]() |
ADC Home > Internet & Web > Java > | |
![]() |
Struts helps you organize your Java-based web applications by providing a framework based on a version of the Model-View-Controller design pattern. With Struts (or more formally, the Jakarta Projects Struts Framework), you can combine Servlets, JSP, custom Struts tag libraries and other components using a unified framework that helps you design, create, and deploy stable web applications quickly. It requires a bit more work initially, to set everything up, but the value of the framework is realized later, when the ordered and componentized nature of your code lets you maintain, update, and reuse it easily. Design Patterns and the Model-View-Controller PatternDesign patterns are conceptual structures that you can use to better organize how you think about, plan, and talk about your code. Many applications, especially web applications, have core elements in common. Design patterns give developers a shared vocabulary, can help keep us from re-inventing versions of the same solutions for each new application. Apples development environments have used the Model-View-Controller pattern for many years. Apples documentation includes a discussion of how the Model-View-Controller applies to Cocoa applications. The Model-View-Controller pattern is used (whether implicitly or explicitly) in nearly every sophisticated web application. Its a sensible way to organize your application architecture. Simply put, the Model is your data and your application logic, the View corresponds to your presentation elements (HTML, JSP, display beans, etc.), and the Controller(s) handle the applications flow. Installing and Using Struts on Mac OS XTo complete the installation steps below, youll need items on the Apple Developer Tools CD. So if you havent installed it yet, do so now. To obtain Struts, go to https://jakarta.apache.org/struts/acquiring.html. In these examples, Im using the current binary distribution, which at the time of this writing is 1.1. If youre using a later version, then, of course, youll have to change some of the paths in the commands below. First, use curl to download the Struts archive: liz@localhost:/usr/local/src> curl -O https://jakarta.apache.org/builds/jakarta-struts/release/v1.1/jakarta-struts-1.1.tar.gz Next, extract the files using gnutar. The version of tar shipped with Mac OS X frequently does not work with tarfiles from the Apache project, but gnutar always will. liz@localhost:/usr/local/src> gnutar -xzvf jakarta-struts-1.1.tar.gz Before installing, check Jakartas list of prerequisites to make sure you have everything you need. Struts requires a Java2 Java Development Kit - version 1.2 or later. Since you have installed the Apple Developer Tools, youre all set. Struts also requires a servlet container. Ive chosen to use the Apache Projects Tomcat servlet container, and to run it as a stand-alone server for simplicitys sake. At the time of this writing, the latest stable version was 4.1.24, although another major version is on the horizion. No matter which version you use, your installation process should be much like mine below. Since the recent Developer Tools include a JDK greater than 1.4 (type java -version to find out which you have), I can install a lightweight version of Tomcat. I want the server to live under /usr/local/tomcat/, and the source tarball to stay in /usr/local/src. liz@localhost:/usr/local/src> curl -O https://jakarta.apache.org/builds/jakarta-tomcat-4.0 /release/v4.1.24/bin/jakarta-tomcat-4.1.24-LE-jdk14.tar.gz liz@localhost:/usr/local/src> gnutar -xzvf jakarta-tomcat-4.1.24-LE-jdk14.tar.gz liz@localhost:/usr/local/src> sudo mkdir /usr/local/tomcat Password: liz@localhost:/usr/local/src> sudo mv jakarta-tomcat-4.1.24-LE-jdk14 /usr/local/tomcat liz@localhost:/usr/local/src> export CATALINA_HOME=/usr/local/tomcat/jakarta-tomcat-4.1.24-LE-jdk14 liz@localhost:/usr/local/src> cd $CATALINA_HOME/bin liz@localhost:/usr/local/tomcat/jakarta-tomcat-4.1.24-LE-jdk14/bin> ./startup.sh Now I can confirm that Tomcat is running on port 8080 on my local machine by going to https://localhost:8080 in a browser: The next prerequisite is the Ant build system. Note that Im still using gnutar to extract files, and not Apples standard tar executable. Im installing Ant under /usr/local/ant, and adding its binary files directory to my $PATH environment variable. liz@localhost:/usr/local/src> curl -O https://apache.mirrorcentral.com/dist/ant/binaries/apache-ant-1.5.3-1-bin.tar.gz liz@localhost:/usr/local/src> gnutar -xzvf apache-ant-1.5.3-1-bin.tar.gz liz@localhost:/usr/local/src> sudo mkdir /usr/local/ant liz@localhost:/usr/local/src> sudo mv apache-ant-1.5.3-1 /usr/local/ant/ liz@localhost:/usr/local/src> export ANT_HOME=/usr/local/ant/apache-ant-1.5.3-1 liz@localhost:/usr/local/src> export PATH=$PATH:$ANT_HOME/bin/ At this point, if youve been following along with me, youve set up two environment variables ($CATALINA_HOME and $ANT_HOME) and changed one ($PATH). When you exit your terminal session, these variables will go away. No fun. Youll probably want to set up a shell startup file that loads these variables for you on each new terminal session. Since I use the bash shell, my startup file lives in /Users/liz/.bash_profile and looks (in part) like this: export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/libexec:/System/Library/CoreServices export JAVA_HOME=/usr export PS1="\u@\h:\w> " Since I wanted to keep the new environment variables for future use, I changed my .bash_profile to contain the lines below. You can also see that I added aliases for starting and stopping tomcat. export ANT_HOME=/usr/local/ant/apache-ant-1.5.3-1 export CATALINA_HOME=/usr/local/tomcat/jakarta-tomcat-4.1.24-LE-jdk14 export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/libexec: /System/Library/CoreServices:$ANT_HOME/bin export JAVA_HOME=/usr alias start_tomcat='$CATALINA_HOME/bin/startup.sh' alias stop_tomcat='$CATALINA_HOME/bin/shutdown.sh' If youre using the bash shell, you can copy and use this file as is. Otherwise, see the manpage for your favorite shell program (for example, man tcsh). To use bash and the .bash_profile file Im using, open the Terminal application, click Preferences, and set your preferences to be like these: Its finally time to install Struts itself. The version of Tomcat Im using comes with an integrated version of Struts. Still, there are some tag library descriptors and example files that I want to install. Note: If you arent using Tomcat (or if youre using an older version of Tomcat without Struts), follow the Struts installation guide. Youll just have to copy a few extra files - its not much harder. Here are the commands I used to add the extra Struts files to my new Tomcat installation. Again, if you arent using Tomcat with Struts built-in, see the installation link in the line above. liz@localhost:~> stop_tomcat liz@localhost:~> cd /usr/local/src/jakarta-struts-1.1 liz@localhost:/usr/local/src/jakarta-struts-1.1> cp lib/*.tld $CATALINA_HOME/server/webapps/admin/WEB-INF/ liz@localhost:/usr/local/src/jakarta-struts-1.1> cp webapps/*.war $CATALINA_HOME/webapps liz@localhost:/usr/local/src/jakarta-struts-1.1> start_tomcat With that step completed, you should be able to view the example applications (which came bundled in those .war files referenced above). For example, look at https://localhost:8080/struts-example/index.jsp Hello World Now we can build a hello world mini-application, using a few Struts components. Since the setup process is fairly complicated, this simple application wont take advantage of many of Struts useful features. Ill focus on those in the next section. One of the .war files in your Struts source directory is called struts-blank.war. This file is meant to serve as a template for new Struts applications. I want to extract the archive so I can modify it to make my own simple Hello World application. So I copy struts-blank.war into a new empty folder, and type jar -xf struts-blank.war. Here are the contents: You can see that the archive includes the struts.jar file (necessary for any Struts application), some tag libraries, the standard web.xml and manifest files, and a configuration file called struts-config.xml. This config file is actually functionally empty, but it includes lots of useful examples in commented sections. Lets look at the other two files in the archive. First, the index.jsp file that comes with the blank Struts application. In this file, you can see the Struts tag libraries in use. (If you arent familiar with the taglibs concept yet, you may want to read Suns explanation.) One interesting thing to note is that all the English text on the page is stored separately and presented via a custom tag. While this is by no means required in a Struts application, it does simplify Internationalization or text changes. You dont need to touch any Java components in order to alter the page copy. <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html:html locale="true"> <head> <title><bean:message key="index.title"/></title> <html:base/> </head> <body bgcolor="white"> <logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application"> <font color="red"> ERROR: Application resources not loaded -- check servlet container logs for error messages. </font> </logic:notPresent> <h3><bean:message key="index.heading"/></h3> <p><bean:message key="index.message"/></p> </body> </html:html> The copy for the file above is stored in ApplicationResources.properties. Since Im making my own application, Ill start by altering that text. Here are the contents of my new properties file: index.title=My Own Hello World, Using Struts index.heading=Heya Next, I want to make a simple Java Bean that my application can reference. Its about as simple as a Bean gets - it stores a hard-coded Magic Number, and tells the user whether or not they guessed that number correctly. package foo; public class GuessBean { public int magicNumber; public GuessBean() { this.magicNumber = 2; } public String evaluateGuess(int g) { if (g == this.magicNumber) { return "You guessed it!"; } else { return "Sorry, wrong number."; } } } Now I can edit the index.jsp file and add a basic form, and create a new file called guess.jsp which calls the beans evaluateGuess() method. (You can find those files in the hello.war archive.) The final step is to create a build.xml file that Ant can use to bundle the all applications files into a WAR archive, and deploy them to Tomcat. If you are using a Java IDE that can build WAR files for you, you wont need to bother with this section. Otherwise, heres the build file: <project name="hello world" default="build-all" basedir="."> <property environment="env"/> <property name="appname" value="hello"/> <property name="src.dir" value="${basedir}/WEB-INF/src"/> <property name="app.name" value="hello"/> <property name="app.path" value="/${app.name}"/> <property name="app.version" value="0.1-dev"/> <property name="build.dir" value="${basedir}/build"/> <property name="classes.dir" value="${basedir}/WEB-INF/classes"/> <property name="tomcat.home" value="${env.CATALINA_HOME}"/> <property name="servlet.jar" value="${tomcat.home}/common/lib/servlet.jar"/> <property name="deploy.dir" value="${tomcat.home}/webapps"/> <path id="build.path"> <fileset dir="./WEB-INF/lib/"> <include name="**/*.jar"/> </fileset> <pathelement path="${src.dir}"/> <pathelement path="${servlet.jar}"/> <pathelement path="${classes.dir}"/> </path> <target name="cleanlocal"> <delete dir="${build.dir}" includeEmptyDirs="true" /> <delete file="${appname}.war" /> <mkdir dir="${build.dir}"/> </target> <target name="compile"> <javac srcdir="${src.dir}" destdir="${classes.dir}" debug="on" deprecation="on"> <include name="**/*.java"/> <classpath refid="build.path"/> </javac> </target> <target name="cleanserver"> <delete file="${deploy.dir}/${appname}.war" /> <delete dir="${deploy.dir}/${appname}" includeEmptyDirs="true" /> </target> <target name="war"> <war warfile="${appname}.war" webxml="./WEB-INF/web.xml"> <fileset dir="./" includes="**/*.*" excludes="*.war, **/*.nbattrs, web.xml, **/WEB-INF/**/*.*, **/project-files/**/*.*"/> <webinf dir="./WEB-INF" includes="**/*" excludes="web.xml, **/*.jar, **/*.class"/> <lib dir="./WEB-INF/lib"/> <classes dir="${classes.dir}" includes="**/*.properties, **/*.class" /> </war> </target> <target name="build-all" depends="cleanlocal, cleanserver, compile, war"> <copy file="${appname}.war" todir="${tomcat.home}/webapps"/> </target> </project> To deploy my new application, I simply need to run Ant and restart Tomcat. liz@localhost:~/hello> ant liz@localhost:~/hello> stop_tomcat liz@localhost:~/hello> start_tomcat Sample Application: A RSS Newsfeed ConsolidatorMany news sites and weblogs make their headlines available as RSS feeds. The RSS format is (more-or-less standardized) XML, generally made available over HTTP. You can read more about RSS, for example, on the OReilly xml.com site and the webreference.com site (and elsewhere). In this section, Ill show you how you can use Servlets, JSP, and Struts to create an application that will allow a user to log in, assemble a list of weblogs and/or news sites, and read headlines fetched and displayed by the application. Headlines are links to the remote content. For simplicitys sake, Ive left off several real-world aspects of the application, most notably the database backend. User data is simply stored in the session, and vanishes when the user closes their browser or you re-start Tomcat. Lets look at the application with the Model-View-Controller design paradigm in mind. In this case, the model is the session data plus the application logic. The view corresponds to the JSP pages and beans used for display, and the controller is a special SiteAction class. The SiteAction class is a subclass of a Struts Action class, and it is responsible for handling user input and dispatching requests to the correct places. Heres an overview of the files which make up the application. (You can download the WAR file and a tarball of the whole application.) In this application, I want to take advantage of a few of Struts cooler features. For instance, I can create custom Form Beans and tie them to Action objects. Lets start with a Form Bean. The beans properties map to form fields, and each property has appropriately-named accessor methods. package foo; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public final class SiteForm extends ActionForm { private String index = null; private String url = null; private String title = null; private String dispatch = null; // accessor methods public void setUrl(String url) { this.url = url; } public void setTitle(String title) { this.title = title; } public void setIndex(String index) { this.index = index; } public void setDispatch(String dispatch) { this.dispatch = dispatch; } public String getUrl() { return (this.url); } public String getTitle() { return (this.title); } public String getIndex() { return (this.index); } public String getDispatch() { return (this.dispatch); } public void reset(ActionMapping mapping, HttpServletRequest request) { this.url = null; this.title = null; this.index = null; } // for this application, we aren't validating user input, but you could easily extend this method to do form validation public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { // I'm leaving this example code in place so you can see how form validation would be done using Struts ActionErrors errors = new ActionErrors(); // if (url == null) // errors.add("url", new ActionError("error.url.required")); return errors; } } One of the most interesting things about Struts is its set of Action classes. The code below extends the Struts Action class, and uses simple conditionals to dispatch user requests. There are other kinds of Action classes, notably DispatchAction, which you can read about in the Struts user guide and on Ted Husteds husted.com. // this controller responds to user input, handles it, and redirects // to a location specified in struts-config.xml package foo; import java.io.IOException; import java.util.Vector; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; public final class SiteAction extends Action { public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ActionErrors errors = new ActionErrors(); // get the RSS Site List object from the session, or create a new one if necessary HttpSession session = request.getSession(); SiteList siteList = (SiteList)session.getAttribute("siteList"); if (siteList == null) { siteList = new SiteList(); } String dispatch = ((SiteForm) form).getDispatch(); String forward = "alter"; if ("add".equals(dispatch)) { String url = ((SiteForm) form).getUrl(); String title = ((SiteForm) form).getTitle(); if (url != null && title != null) { SiteListItem it = new SiteListItem(); it.setURL(url); it.setTitle(title); siteList.addSite(it); } } else if ("show".equals(dispatch)) { String index = ((SiteForm) form).getIndex(); if (index != null) { // put the selected site index in the session for use by the next page session.setAttribute("siteIndex",index); forward = "view"; } } else if ("delete".equals(dispatch)) { String index = ((SiteForm) form).getIndex(); if (index != null) { int ind = Integer.parseInt(index); siteList.removeSite(ind); } } // Forward to the specified success URL return (mapping.findForward(forward)); } } At this point, we tie the Action and Form objects together using directives in struts-config.xml <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN" "https://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"> <struts-config> <!-- ========== Form Bean Definitions =================================== --> <form-beans> <!-- our SiteList controller --> <form-bean name="SiteForm" type="foo.SiteForm"/> </form-beans> <!-- ========== Action Mapping Definitions ============================== --> <action-mappings> <action path="/siteAction" type="foo.SiteAction" name="SiteForm"> <forward name="alter" path="/index.jsp"/> <forward name="view" path="/show.jsp"/> </action> </action-mappings> </struts-config> Now we can use Struts to create a JSP page with user input elements that call the SiteAction object. <%@ page language="java" %> <%@ page import="foo.SiteListItem" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <head> <title>Manage Sites</title> <html:base/> </head> <body bgcolor="white"> <logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application"> <font color="red"> ERROR: Application resources not loaded -- check servlet container logs for error messages. </font> </logic:notPresent> <br /> <jsp:useBean id="login" scope="session" class="foo.LoginBean" /> <jsp:useBean id="siteList" scope="session" class="foo.SiteList" /> <h1>RSS Site Manager</h1> <% // user validation is being handled by a session bean if (login.validate() == false) { out.println("<script language=\"javascript\"> document.location='/headlines/login.jsp'</script>"); return; } int size = siteList.getSize(); for (int i=0; i < size; i++) { SiteListItem it = siteList.getItemAt(i); out.println("<a href=\"/headlines/siteAction.do?dispatch=show&index=" + i + "\">" + it.getTitle() + "</a><br>"); } %> <br> <hr> <h2>Add a Site</h2> <html:form type="foo.SiteAction" name="SiteForm" action="/siteAction.do" focus="url"> <table border="0"> <tr> <td>RSS URL</td> <td><input type="text" name="url" size="60"/></td> </tr> <tr> <td>Title</td> <td><input type="text" name="title" size="60"/></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Submit"/></td> </tr> </table> <input type="hidden" name="dispatch" value="add"> </html:form> </body> </html:html> These files work together with beans that store and parse the list of RSS links and manage user login. Those helper objects are available in the tar archive. Note that in a production-quality application, user logins would have been handled by an Action object instead of the lightweight bean Im using for example purposes. With all these pieces assembled, you can compile and deploy the application using the same build.xml file referenced above. Just change the project name and appname at the top of the file, and youre good to go. Heres the RSS manager in action: Conclusion, and Suggestions for Further ReadingThis article has given you the information you need to get up and running with Struts on Mac OS X. Still, theres much more to Struts than I can cover in one article. If youre interested in learning more, visit the Struts User Guide. You might also want to pick up Programming Jakarta Struts, by Chuck Cavaness, or Struts in Action, by Ted Husted. Finally, I would be remiss in not recommending the now-classic book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides. |
![]() ![]() |
Get information on Apple products. Copyright © 2008 Apple Inc. |
![]() |