Saturday, February 11, 2012

JavaMail

JavaMail is a standard API for sending emails from Java programs.

To use JavaMail, download javamail.zip from Sun's website.

Explode this zip to get the following jars:
mail.jar
mailapi.jar
pop3.jar
smtp.jar

You will also need to download JavaBeans Actibation Framework - jaf.zip - from Sun's website.

Explode this zip to get the following jar:
activation.jar

Copy these jars to your /lib folder.

In Eclipse, go to Project->Properties->Java Build Path->Libraries->Add Jars
to add the above jars.

Now let us write a class to send an email using JavaMail.

In your /src/org/confucius, create a class EmailSender.java, like this:

 package org.confucius;  

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class EmailSender {

public static void main(String[] args) {
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);

MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("cori@confucius.org"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress("arthur@arthur.com"));
message.addRecipient(Message.RecipientType.CC, new InternetAddress("bob@bob.com"));
message.setSubject("Hello JavaMail");
message.setText("Welcome to JavaMail");
message.saveChanges();

Transport transport = session.getTransport("smtp");
transport.connect("localhost", "cori", "changeit");
transport.sendMessage(message, message.getAllRecipients());
transport.close();

} catch (AddressException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
}
}

}



What are we doing here?

We are creating an email message, filling its from/to/cc/subject/body fields.

Next we open a SMTP transport session by providing the smtp server (localhost), username and password.

Then we send the email and close the transport session.

To run this code, you will need a SMTP (Email) server running on your machine (localhost).

We recommend "Papercut" - it is a tiny SMTP server that is designed to test programs sending emails. It comes with the server and a email viewer client rolled into one - so you can immediately see all the emails it has received.

Once you have installed Papercut and it is running, you can run EmailSender.java by R-clicking on it in the Eclipse navigator view and selecting Runs As -> Java Application.

You should see an email popup in Papercut.

Threads and Executors

There are multiple ways to spawn threads in Java.

The most basic way is to implement the Runnable interface. Any class implementing this interface can be spawned off and executed as a separate thread.

For example, create a class in /src/or/confucius called "HelloRunnable.java" like this:

 package org.confucius;  

public class HelloRunnable implements Runnable {

public void run() {
System.out.println("Hello Runnable World!");
}

}




Another way, is to extend the Thread class.

In /src/org/confucius create a class HelloThread.java, like this:

 package org.confucius;  

public class HelloThread extends Thread {
public void run(){
System.out.println("Hello Thread World!");
}
}



The advantage is that the Thread class provides a whole bunch of useful methods. These become immediately available to HelloThread.

The disadvantage is that HelloThread cannot extend any other class.
(In which case, it will have to use method #1 and implement Runnable instead of extending Thread).

Let us write a class to create and execute these threads.

In /src/or/confucius, create a class ThreadTester.java, like this:

 package org.confucius;  

public class ThreadTester {

public static void main(String[] args) {
// Runnable
Runnable helloRunnable = new HelloRunnable();
new Thread(helloRunnable).start();


// Thread
Thread helloThread = new HelloThread();
System.out.println("Hello Thread state before: " + helloThread.getState());
helloThread.start();
System.out.println("Hello Thread state after: " + helloThread.getState());


}

}



Notice that we use the Thread.start() method to run the threads in both cases.
Notice that the HelloThread gives us some useful methods like getState().

To run this, right click on the ThreadTester.java file in Eclipse navigator, then select Run As -> Application.

If you look at the console, you might be surprised to see that the print statements are not in the order you expect! This is because your application is running 3 threads - the main application, the HelloRunnable thread and the HelloThread thread.

They print out at different times depending on how the JVM runs the threads.

Now for the next step - Executors.

Running threads using Thread.start() is OK for easy situations, but as the number of threads increase, you need some real thread management support.

Executors are classes which implement the Executor interface. They provide several thread management facilities like thread pooling, scheduling, termination, etc.

There are several Executors available. Let us try one of them.

In /src/org/confucius, create a class ExecutorTester.java, like this:

 package org.confucius;  

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExecutorTester {

public static void main(String[] args) {
Runnable helloRunnable = new HelloRunnable();

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
executor.scheduleAtFixedRate(helloRunnable, 3, 2, TimeUnit.SECONDS);

try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}

executor.shutdownNow();
}
}



Here we are using the ScheduledThreadPoolExecutor. We are setting a thread pool size of 10 (this is the maximum number of threads that can be running at any given time).

We use the scheduleAtFixedRate() method to spawn and execute HelloRunnable every 2 seconds, starting at 3 seconds.

Finally we use shutDownNow() to terminate the thread after 10 seconds (10000 milliseconds).

Right click on the ThreadExecutor.java file in Eclipse navigator view, then select Run As --> Java Application.

You will see "Hello Runnable World!" printed in the console every 2 seconds.

Streams and File I/O

In normal programming languages, if you want to read/write to a file, you open the file, read/write to it, the close the file.

Not so in Java.

Java has this notion of "Streams".

A Stream is something that you can read from or write to.

If you "connect" a stream to a File, you can read/write to that file.

Streams are of various types. The basic ones read/write bytes. Higher ones read/write characters. Still higher ones read/write lines (strings). And so on.

Then there are buffered streams which read/write buffer full of data at a time, which makes things more efficient.

Let us see this with an example.

Create a /data directory in your HelloWorld project, at the same level as /src.

In this directory, create a text file called "pinocchio.txt" containing the following text:

 THE ADVENTURES OF PINOCCHIO  

by C. Collodi

[Pseudonym of Carlo Lorenzini]

Translated from the Italian by Carol Della Chiesa

CHAPTER 1

How it happened that Mastro Cherry, carpenter, found a piece of wood that wept and laughed like a child.

Centuries ago there lived--

"A king!" my little readers will say immediately.

No, children, you are mistaken. Once upon a time there was a piece of wood. It was not an expensive piece of wood. Far from it. Just a common block of firewood, one of those thick, solid logs that are put on the fire in winter to make cold rooms cozy and warm.





** Leave some blank lines at the end of the text!

Suppose we wanted to read this file and then create a copy to /data/pinocchioOut.txt.

Create a class in /src/org/confucius called "FileIO.java", like this:

 package org.confucius;  

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;

public class FileIO {
public static void main(String[] args){
try {
File inFile = new File("data/pinocchio.txt");
File outFile = new File("data/pinocchioOut.txt");

// ----------------------------
InputStream in = new FileInputStream(inFile);
OutputStream out = new FileOutputStream(outFile);
int c;
while ((c = in.read()) != -1){
out.write(c);
}

in.close();
out.close();

// ----------------------------
Reader inR = new InputStreamReader(new FileInputStream(inFile));
Writer outW = new OutputStreamWriter(new FileOutputStream(outFile, true));

while ((c = inR.read()) != -1){
outW.write(c);
}

inR.close();
outW.close();

// ----------------------------
inR = new FileReader(inFile);
outW = new FileWriter(outFile, true);

while ((c = inR.read()) != -1){
outW.write(c);
}

inR.close();
outW.close();

// ----------------------------
BufferedReader inB = new BufferedReader(new InputStreamReader(new FileInputStream(inFile)));
BufferedWriter outB = new BufferedWriter (new OutputStreamWriter(new FileOutputStream(outFile, true)));

String s = null;
while ((s = inB.readLine()) != null){
outB.write(s);
outB.newLine();
}
inB.close();
outB.close();


// ----------------------------
inB = new BufferedReader(new FileReader(inFile));
outB = new BufferedWriter (new FileWriter(outFile, true));

s = null;
while ((s = inB.readLine()) != null){
outB.write(s);
outB.newLine();
}
inB.close();
outB.close();

// ----------------------------
File binaryFile = new File("data/binaryFile");
DataOutputStream outD = new DataOutputStream (new BufferedOutputStream (new FileOutputStream(binaryFile)));
outD.writeLong(outFile.length());
outD.close();

DataInputStream inD = new DataInputStream (new BufferedInputStream (new FileInputStream(binaryFile)));
long fileLength = inD.readLong();
System.out.println("File length = " + fileLength);


} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}




This class read pinocchio.txt in VARIOUS ways and writes to pinocchioOut.txt in VARIOUS ways!

Let us understand them all:
1. Using InputStream/OutputStream: This is the most basic Stream I/O - it reads/writes bytes

2. Using Reader/Writer: This is a character stream - it reads/writes characters.

3. Same as #2, except this time we use FileReader instead of InputStreamReader. There are always many ways to create the stream!


4. Using BufferedReader/BufferedWriter: This also is a character stream, but one that read/writes a bufferful at a time. This makes it more efficient.

5. Same as #4, but using a different way to create the stream.

6. Using DataStreams: To write primitive data type values (boolean, char, byte, short, int, long, float, and double) to a binary file, we use DataStreams. In this example, we write the length of the pinocchioOut.txt file to a binary file, then read it back.

If you right click on FileIO.java and select Run As-->Java Application, you will see new files created in /data folder: pinocchioOut.txt and binaryFile.

Needless to say, there are many more type of streams. There are ones for playing music, videos, etc.

Thursday, February 2, 2012

Tiles

Our UI consists of 3 JSPs - HelloWorld.jsp, Confirm.jsp and Error.jsp

What if we wanted a consistent layout for this, with a standard header, footer, styles, etc

What if we eventually have 100s of JSPs, which all need a consistent look and feel?

Enter Tiles.

Tiles is a layout framework which allows you to "dismantle" your UI into multiple little pieces - say a header, a footer, a copyright, a layout, etc

You can then build your JSPs by combining these pieces.

The nice thing is Tiles comes already bundled with Struts, so we can use it immediately. All we need to do is include the tiles TLD in the JSP, like this:

 <%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>   



Let us now dismantle our UI to use tiles.

In your /web-content/jsp folder, create a header.jsp.
It contains a single line, like this:

 <h1>Welcome to HelloWorld</h1>  




In your /web-content/jsp folder, create a footer.jsp.
It contains a single line, like this:

 <i>Copyright 2012 Project Confucius</i>  




In your /web-content/jsp folder, create a layout.jsp. It pulls in the header and footer, like this;

 <%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>      
<tiles:insert page="header.jsp"/>
<br/>
<tiles:insert attribute="body"/>
<br/>
<hr>
<tiles:insert page="footer.jsp"/>



Now, let us dismantle HelloWorld.jsp.

In your /web-content/jsp folder, create a registerForm.jsp.
It contains the Form, like this:
 <form method=post action="/HelloWorld/register.do">   
First Name: <input type=text name=firstName>
<br/>
Last Name: <input type=text name=lastName>
<br/>
<input type=submit value="Register">
</form>



Now, update HelloWorld.jsp to use the layout, like this:

 <html>    
<head>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
</head>
<body>
<tiles:insert page="layout.jsp">
<tiles:put name="body" value="registerForm.jsp" />
</tiles:insert>
</body>
</html>



Now, let us dismantle Confirm.jsp.

In your /web-content/jsp folder, create a ConfirmMessage.jsp.
It contains the confirmation message, like this:

 <%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>  

Confirmation: Registration accepted for <bean:write name="helloWorldForm" property="firstName"/> <bean:write name="helloWorldForm" property="lastName"/>



Now update Confirm.jsp to use the layout, like this:

 <html>    
<head>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
</head>
<body>
<tiles:insert page="layout.jsp">
<tiles:put name="body" value="ConfirmMessage.jsp" />
</tiles:insert>
</body>
</html>



Now, let us dismantle Error.jsp

In your /web-content/jsp folder, create a ErrorMessage.jsp.
It contains the error message, like this:

 Error! You entered names with numbers in them.  



Now update your Error.jsp to use the layout, like this:

 <html>    
<head>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
</head>
<body>
<tiles:insert page="layout.jsp">
<tiles:put name="body" value="ErrorMessage.jsp" />
</tiles:insert>
</body>
</html>


If you now build and deploy HelloWorld.war, and point your browser to:
http://localhost:8080/HelloWorld/jsp/HelloWorld.jsp

you will see that all the pages have a consistent header, footer and layout.

Tiles helps you create a consistent look and feel to all your pages.
It also helps keep individual JSPs small and easy to maintain.

Wednesday, February 1, 2012

Struts1- HelloWorld

Let us use Struts1 to update the MVC project we built in the earlier post.

First, we update the web.xml to direct all *.do URLs to the Struts controller.
Note that, by convention, Struts URLs have the suffix .do

Your web.xml will look like this:
 <web-app>   
  <servlet>  
   <servlet-name>action</servlet-name>  
   <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>  
   <init-param>  
     <param-name>config</param-name>  
     <param-value>/WEB-INF/struts-config.xml</param-value>  
   </init-param>  
  </servlet>  
    
  <servlet-mapping>  
     <servlet-name>action</servlet-name>  
     <url-pattern>*.do</url-pattern>  
  </servlet-mapping>  
    
 </web-app>   
     


We are directing the Struts ActionServlet to initialize itself from the WEB-INF/struts-config.xml file.

So let us now write this XML file.

Create a file struts-config.xml in your /WEB-INF folder, like this:
 <struts-config>  
    
      <form-beans>  
        <form-bean name="helloWorldForm"   
           type="org.confucius.HelloWorldForm"/>  
      </form-beans>  
    
      <action-mappings>  
        <action path="/register"  
           type="org.confucius.HelloWorldAction"  
           name="helloWorldForm">  
           <forward name="success" path="/jsp/Confirm.jsp"/>  
           <forward name="error" path="/jsp/Error.jsp"/>  
    
        </action>  
      </action-mappings>  
    
 </struts-config>  


This is a complicated looking configuration!
What are we doing here?

First we are declaring a Struts "Form" called HelloWorldForm and mapping it to a Struts "Bean" called "helloWorldForm"

Next we are mapping the URL /register to the action "HelloWorldAction" and to the form "helloWorldForm". Note that Struts will automatically add a ".do" suffix. So it expects the URL /register.do

An "Action" class is what the Struts Controller will direct the request to.
A "Form" class is what the Struts Controller will populate with the form parameters.

Finally, we have mapped the values returned by the HelloWorldAction, "success" and "error", to the relevant jsp Views.

In the /src/org/confucius folder, create the HelloWorldForm.java, like this:

 
package org.confucius;

import org.apache.struts.action.ActionForm;

public class HelloWorldForm extends ActionForm {  
      String firstName;  
      String lastName;  
        
      public String getFirstName() {  
           return firstName;  
      }  
      public void setFirstName(String firstName) {  
           this.firstName = firstName;  
      }  
      public String getLastName() {  
           return lastName;  
      }  
      public void setLastName(String lastName) {  
           this.lastName = lastName;  
      }  
 }  
   
   


This is the class where Struts will save the firstName and lastName from the form in HelloWorld.jsp

Next, create a new class HelloWorldAction.java in /src/org/confucius which looks like this:
 package org.confucius;  
   
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
   
 import org.apache.struts.action.Action;  
 import org.apache.struts.action.ActionForm;  
 import org.apache.struts.action.ActionForward;  
 import org.apache.struts.action.ActionMapping;  
   
 public class HelloWorldAction extends Action {  
        
      public ActionForward execute(ActionMapping mapping,ActionForm form,  
                HttpServletRequest request,HttpServletResponse response)  
          throws Exception {  
         
                HelloWorldForm helloWorldForm = (HelloWorldForm) form;  
   
                User user = new User(helloWorldForm.getFirstName(), helloWorldForm.getLastName());  
                  
                if (user.validate())  
                     return mapping.findForward("success");  
                else  
                     return mapping.findForward("error");  
           }  
   
 }  
   


This class is call by the Struts Action Servlet. It is passed the HelloWorldForm which is all setup with the request parameters from the form.

Instead of returning the actual View JSP, the Action class returns "success" or "error" - and Struts will select the appropriate JSP based on the configuration in struts-config.xml

Update your HelloWorld.jsp to point the form to register.do, like this:
 <html>    
     <head>    
     </head>    
     <body>    
       <form method=post action="/HelloWorld/register.do">   
         First Name: <input type=text name=firstName>   
         <br/>   
         Last Name: <input type=text name=lastName>   
         <br/>   
         <input type=submit value="Register">   
       </form>   
   </body>    
  </html>    


Finally, update your Confirm.jsp to pick the firstName and lastName from the Struts bean:

 <html>   
        <head>   
           <%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>  
        </head>   
        <body>   
             Confirmation: Registration accepted for <bean:write name="helloWorldForm" property="firstName"/> <bean:write name="helloWorldForm" property="lastName"/>   
   </body>   
 </html>   


Note that the HelloWorldForm bean is always available to the View. That is how Struts can make data available to the View. For example, you could have had a field called 'User' in the HelloWorldForm.java and HelloWorldAction could have populated it. It would have then been available in Confirm.jsp

As you can see, we have managed to do all the MVC wiring in struts-config.xml

We were able to connect the URL, the Form, the Action and the View declaratively in the XML.

We were able to pass data from the Form to the Action and to the View.

If you now build and deploy HelloWorld.war, the point your browser to:
http://localhost:8080/HelloWorld/jsp/HelloWorld.jsp

you will be able to play with the application and see the correct view show up, based on your inputs.

Struts1 - Install

We need the struts.jar, struts-taglib.jar and all the jars they depend on.

So update your ivy.xml to declare dependency on these jars, like this:

(Note that I had to manually include the other dependencies because of the way my repository is structured - you won't need to if you are pointing to the Maven central repository)

 <ivy-module version="2.0">  
   <info organisation="org.confucius" module="helloworld"/>  
   <dependencies>  
           <dependency org="struts" name="struts" rev="1.2.9"/>  
           <dependency org="org.apache.struts" name="struts-taglib" rev="1.3.10"/>            
           <dependency org="commons-collections" name="commons-collections" rev="20040616"/>  
           <dependency org="commons-digester" name="commons-digester" rev="2.1"/>  
           <dependency org="commons-beanutils" name="commons-beanutils" rev="20030211.134440"/>  
           <dependency org="commons-logging" name="commons-logging" rev="1.1.1"/>  
           <dependency org="org.apache.httpcomponents" name="httpcore" rev="4.2-alpha2"/>            
           <dependency org="org.apache.httpcomponents" name="httpclient" rev="4.2-alpha1"/>             
           <dependency org="org.apache.commons" name="commons-exec" rev="1.1"/>        
           <dependency org="com.google.guava" name="guava" rev="r09"/>  
           <dependency org="org.seleniumhq.selenium" name="selenium-api" rev="2.17.0"/>  
           <dependency org="org.seleniumhq.selenium" name="selenium-remote-driver" rev="2.17.0"/>  
           <dependency org="org.seleniumhq.selenium" name="selenium-firefox-driver" rev="2.17.0"/>  
           <dependency org="org.seleniumhq.selenium" name="selenium-java" rev="2.16.1"/>            
           <dependency org="junit" name="junit" rev="4.10"/>            
           <dependency org="org.json" name="json" rev="20090211"/>  
     <dependency org="javax.servlet" name="servlet-api" rev="2.5"/>  
     <dependency org="javax.servlet" name="jsp-api" rev="2.0"/>  
     <dependency org="jstl" name="jstl" rev="1.2"/>  
         <dependency org="log4j" name="log4j" rev="1.2.16"/>   
   </dependencies>  
 </ivy-module>  


Run the Ant 'resolve' target to download these jars to your /lib folder.

Now we need to include these jars in the Eclipse Classpath.
Go to Project->Properties->Java Build Path->Libraries->Add Jars

and add all the jars you downloaded.

We also need to include these jars in the Classpath of Ant build.xml, like this:

 <project name="HelloWorld" xmlns:ivy="antlib:org.apache.ivy.ant" >  
   
   <target name="resolve" description="--> retrieve dependencies with ivy">  
     <ivy:retrieve />  
   </target>  
   
      <target name="init" depends="resolve">  
           <mkdir dir="classes"/>  
           <mkdir dir="target"/>  
      </target>  
   
      <path id="build.classpath">  
       <pathelement location="lib/commons-collections-20040616.jar"/>  
       <pathelement location="lib/commons-digester-2.1.jar"/>  
       <pathelement location="lib/commons-beanutils-20030211.134440.jar"/>  
       <pathelement location="lib/struts-taglib-1.3.10.jar"/>  
       <pathelement location="lib/struts-1.2.9.jar"/>  
       <pathelement location="lib/commons-logging-1.1.1.jar"/>  
       <pathelement location="lib/httpcore-4.2-alpha2.jar"/>  
       <pathelement location="lib/httpclient-4.2-alpha1.jar"/>  
       <pathelement location="lib/commons-exec-1.1.jar"/>  
       <pathelement location="lib/guava-r09.jar"/>  
       <pathelement location="lib/selenium-api-2.17.0.jar"/>  
       <pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>  
       <pathelement location="lib/selenium-java-2.16.1.jar"/>  
       <pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>  
       <pathelement location="lib/junit-4.10.jar"/>  
       <pathelement location="lib/json-20090211.jar"/>  
       <pathelement location="lib/servlet-api-2.5.jar"/>  
       <pathelement location="lib/jsp-api-2.0.jar"/>  
       <pathelement location="lib/log4j-1.2.16.jar"/>  
      </path>  
   
      <target name="compile" depends="init">  
           <javac srcdir="." destdir="classes">  
                <classpath refid="build.classpath"/>  
           </javac>  
      </target>  
   
      <path id="test.classpath">  
       <pathelement location="classes"/>  
       <pathelement location="lib/junit-4.10.jar"/>  
       <pathelement location="lib/selenium-api-2.17.0.jar"/>  
       <pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>  
       <pathelement location="lib/selenium-java-2.16.1.jar"/>  
       <pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>  
       <pathelement location="lib/guava-r09.jar"/>  
       <pathelement location="lib/commons-exec-1.1.jar"/>  
       <pathelement location="lib/httpclient-4.2-alpha1.jar"/>  
       <pathelement location="lib/httpcore-4.2-alpha2.jar"/>  
       <pathelement location="lib/commons-logging-1.1.1.jar"/>  
       <pathelement location="lib/struts-1.2.9.jar"/>  
       <pathelement location="lib/struts-taglib-1.3.10.jar"/>  
       <pathelement location="lib/commons-beanutils-20030211.134440.jar"/>  
       <pathelement location="lib/commons-digester-2.1.jar"/>  
       <pathelement location="lib/commons-collections-20040616.jar"/>  
      </path>  
   
      <target name="test" depends="compile" >  
           <junit failureproperty="junit.failure">  
                <test name="org.confucius.TestCalculator"/>  
                <classpath refid="test.classpath"/>  
                <formatter type="plain" usefile="false" />  
           </junit>  
           <fail if="junit.failure" message="Unit test(s) failed. See reports!"/>  
      </target>  
        
      <target name="dist" depends="test">  
           <war destfile="target/HelloWorld.war" webxml="web.xml">  
                 <classes dir="classes"/>  
                 <lib dir="lib">  
                      <exclude name="jsp-api*.jar"/>  
                      <exclude name="servlet-api*.jar"/>  
                 </lib>       
                 <fileset dir="web-content"/>  
                 <webinf dir="WEB-INF"/>  
           </war>  
           <echo>Build executed at ${TIME_NOW}</echo>  
      </target>  
   
      <tstamp>   
           <format property="TIME_NOW" pattern="hh:mm:ss aa MM/dd/yyyy"/>   
      </tstamp>   
        
 </project>  



You are now ready to use Struts1.

MVC with Struts

In the earlier post on MVC, we learned the Model-View-Controller framework.

We also learned a way to pass the Model to the View in the next post.

There are certain things we did in the Controller that are applicable to most MVC style applications:
- Read form parameters
- Choose the View
- Pass the Model (or some other data) to the View

So the question arises:
Why not make a generic Controller that can do all these things?

In fact, why not write a framework that takes care of all the "MVC plumbing"?

Enter Struts.

Struts is a MVC framework. It allows you to declaratively "wire" the URLs/Controllers/Views, it parses all the form inputs, it makes it easy to pass data between Controllers and Views, it can validate user input, and do many other things.

It comes in two versions Struts 1 and Struts 2. We will play with both.