Friday, March 6, 2009

Struts 2

I am back after a long time with Struts 2 framework. Struts 1.x has been quite old and I never thought it was necessary to write about it as it always has enough tutorials in the net. But with the latest struts 2 framework, I have tried few things and came up with this blog.

Without any hesitation, just download the struts2-blank project and there you have everything required to start with.

Most of the information that is required for starting with struts 2 can be obtained from the official documentation of struts 2

Before you start with some real development, there are some notable differences between Struts 1.x and Struts2
If you have not use struts 1.x earlier, well and good for you because anyways the basic concepts on which struts 2 is built are quite different.

First of all There are no more formbeans. But do not wonder where you'll capture your form data, it all resides in your Action class. You can define all the attributes of your forms in Action class itself and framework will take care of populating them to and fro.
Action Class:
Now when I speak of Action classes, these are nothing but POJOs. The new framework does not need you to extend to class org.apache.struts.action.Action. But since this is a POJO, you cannot directly access the request or session object. Remember in old version these were provided by the framework through super Action class.

But dont be frowned, you can still access everything, with a little efforts. Struts 2 heavily uses Dependency Injection. That is, it will provide you with the stuff if you need, you dont have to get them. And there are interceptors for this. In our case, if you need a request object, just implement org.apache.struts2.interceptor.RequestAware; and for session object, implement org.apache.struts2.interceptor.SessionAware.
Implementing those two interfaces will let the framework know that you need this objects and the framework would inject the respective objects. So there you go, ready with an Action class.

Let's see a simple action class for login:

package mypkg;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.SessionAware;

import com.myapp.delegate.BusinessDelegate;
import com.myapp.vo.UserInfoVO;
import com.myapp.web.UserInfo;

/**
* Sample Login Action Class in Struts 2.
*
* @author Husain Basrawala
*/

public class LoginAction implements SessionAware {

private static Log logger = LogFactory.getLog(LoginAction.class);

// Form attributes
private String username = null;
private String password = null;

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

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

// Map representing the Session object

private Map session = null;

public void setSession(Map session_) {
this.session = session_;
}

/**
* Execute method called on action.
*/
public String execute() throws Exception {
session.put("PROCESS_ID", "ABC");

BusinessDelegate delegate = new BusinessDelegate();
UserInfoVO userInfoVO = delegate.getUserInfo(username);

UserInfo userInfo = new UserInfo();
userInfo.setCode(userInfoVO.getCode());
userInfo.setName(userInfoVO.getName());
userInfo.setLevel(userInfoVO.getLevel());
session.put("USER_INFO", userInfo);
return SUCCESS;
}

/**
*
* Before calling each action method, framework looks for a validate method
* in the class.
*
* The validate mehod should follow a naming convention such as
* validateMethodName()
*
* or doValidateMethodName.
*
* The framework will call the validate method and if it does not add any
*
* error using addActionError then only the action method is called.
*
*/
public void validateExecute() throws BaseException {

if (FWUtilities.isBlank(username)) {
addFieldError("username", "username " + getText("error.required"));
return;
}

if (FWUtilities.isBlank(password)) {
addFieldError("password", "password " + getText("error.required"));
return;
}

BusinessDelegate delegate = new BusinessDelegate();
if (!delegate.isUserAuthentic(username, password)) {
addActionError(getText("error.invalidlogin"));
logger.debug("Invalid user login tried");
}
}

public String load() throws Exception {
return SUCCESS;
}
}

Tuesday, January 13, 2009

Log4j for Developers

Log4j has proven to be very simple and useful tool for logging.

Log4j is quite easy to integrate with your application. Follow these two simple steps and you are ready to use the power of logging.

1. Include commons logging api in your classes.

org.commons.logging.Log logger = org.commons.logging.LogFactory.getLog(ThisClass.class);

Now you are ready to use the logging by using following methods...

logger.debug("Logging statement");
logger.info("Logging statement");
logger.fatal("Logging statement");

2. Add the following file to your classpath: log4j.properties
log4j.logger.com.mypkg=DEBUG, LOG_NAME
log4j.appender.LOG_NAME.File=C:\\log\\file.log
log4j.appender.LOG_NAME=org.apache.log4j.DailyRollingFileAppender
log4j.appender.LOG_NAME.layout=org.apache.log4j.PatternLayout
log4j.appender.LOG_NAME.Append=true


There are some more properties that you can specify. Refer Log4j documentation available on the net.

Monday, June 25, 2007

ANT as a automation tool

When it come to build automations, ANT is one of the most powerful tool that can be used. There might be other tools too, but ANT is most widely available and usable. If you are new to ANT then visit http://ant.apache.org. If you want to explore the power of ANT then visit http://ant.apache.org/manual/index.html.
To write or use ANT first you will have to install it. To find out from where to download and how to isntall ANT please visit http://ant.apache.org/manual/install.html.

Here I will take an process as an example and we'll see how we can automate the process using ANT and make our life easier.

Consider the following process:
You have some bunch of programmers working on a project and you are using a central repository; CVS or VSS. At the end of the day when all the programmers are done with their devlopment/bug fixing task for the day a monotonous process is required to be followed. All the source code from the repository needs to be taken and sent to your team at other end (Onsite) through ftp. This process can be broken down in to following steps:
1. Get the latest source code from the repository.
2. Label the source code appropriately to indicate today's work.
3. Filter the files that need to be send.
4. Archive your source code to zip or gzip.
5. Upload the zip file to ftp.
6. And finally send a mail to all the stakeholders stating the upload path and a release note.

Now if we perform this process manually, it will take considerable time and might also cause problems due to human errors.
But if we automate this process, believe me it will not take more than 30 secs to release a project with around 500 source files. What we need to do is identify the atomic tasks to be performed and automate them.

As you can see, the steps mentioned above are atomic tasks that needs to be performed. ANT provides a huge set of core as well as optional tasks [].
I hope that you have basic knowledge about targets, tasks, taskdef etc.

We will define each step or set of steps as a target. Though the complete script can be writting in one target, I prefer it former way for obvious reasons.

The paramters specified in the code snippets are explained in the end.
Considering the above process;

1. Get the latest source code from the repository.
If you are using CVS, ANT provides core task to perform CVS operations.
This can be written in ANT as:

<cvspass cvsRoot="${cvs.root}" password="${cvs.pass}" passfile=".cvs-pass"/>
<cvs command="login" cvsRoot="${cvs.root}" passfile=".cvs-pass"/>
<cvs dest="${base.dir}" cvsRoot="${cvs.root}" command="update -A -dPC" passfile=".cvspass"/>

If you are using VSS, ANT provides optional task for the same.
<vssget serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" localpath="${base.dir}" login="${vss.username},${vss.pass}" recursive="true" />
The piece of code above will get the latest version of code from repository into the specified destination folder.

2. Label the source code appropriately to indicate released work.

<echo message="Tag ${vss.path}/${app.name} with ${app.name}-${timestamp}"/>
<cvs cvsRoot="${cvs.root}" command="tag ${app.name}-${timestamp}" package="${app.name}" passfile=".cvspass"/>

If you are using VSS, ANT provides optional task for the same.
<echo message="Label ${vss.path}/${app.name} with ${app.name}-${timestamp}"/>
<vsslabel serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" login="${vss.username},${vss.pass}" label="${app.name}-${timestamp}" />

The above two tasks can be specified in one target for modularity and hence the reaulting target will be:

<!-- CVS task Starts-->
<target name="cvs" description="get the latest source code from CVS" >
<cvspass cvsRoot="${cvs.root}" password="${cvs.pass}" passfile=".cvs-pass"/>
<cvs command="login" cvsRoot="${cvs.root}" passfile=".cvs-pass"/>
<cvs dest="${base.dir}" cvsRoot="${cvs.root}" command="update -A -dPC" passfile=".cvspass"/>
<cvs cvsRoot="${cvs.root}" command="tag ${app.name}-${timestamp}" package="${app.name}" passfile=".cvspass"/>
</target>
<!-- CVS task ends -->

<!-- VSS task starts -->
<target name="vss" description="get the latest source code from VSS" >
<vssget serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" localpath="${base.dir}" login="${vss.username},${vss.pass}" recursive="true" />
<echo message="Label ${vss.path}/${app.name} with ${app.name}-${timestamp}"/>
<vsslabel serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" login="${vss.username},${vss.pass}" label="${app.name}-${timestamp}" />
</target>
<!-- VSS task ends -->

Now moving to next steps:
3. Filter the files that need to be send.
4. Archive your source code to zip or gzip.
These two steps include filtering out those files which need to be sent and bundle those files.
The target to accomplish this task would look something like this:

<target name="package" description="Package the source code and Contextroot" >
<property name="target.filename" value="${app.name}-${timestamp}.zip"/>
<zip zipfile="${zip.dir}/${app.name}/${target.filename}" >
<fileset dir="${base.dir}">
<include name="src/**"/>
<include name="ContextRoot/jsp/**"/>
<include name="ContextRoot/WEB-INF/**"/>
<include name="src/META_INF/ejb-jar.xml"/>
<exclude name="ContextRoot/WEB-INF/classes/**"/>
<exclude name="ContextRoot/WEB-INF/lib/**"/>
<exclude name="ContextRoot/jsp/**/*.gif"/>
<exclude name="ContextRoot/jsp/image/**"/>
<exclude name="src/META_INF/**"/>
</fileset>
</zip>
</target>

We have used here ANT task to zip a set of files. The set of files can be specified
Here you need to specify the parameters like the target path and name of the zip file, the source files to be zipped, etc.

5. Upload the zip file to ftp server.
ANT provides a task that allows you to upload files to ftp server. The ANT target for the same is:

<target name="upload" description="uploads to FTP">
<echo message="Uploading file to ftp : ${target.filename}"/>
<ftp server="${ftp.url}" userid="${ftp.username}" password="${ftp.password}" binary="yes" remotedir="${ftp.remotedir}">
<fileset dir="${zip.dir}/${app.name}">
<include name="${target.filename}"/>
</fileset>
</ftp>
<echo message="Release uploaded successfully "/>
</target>

As you can see, the parameters that needs to be provided are connection details for ftp server, transfer mode, files upload path and source file.

The next and final step:
6. And finally send a mail to all the stakeholders stating the upload path and a release note.
ANT provides a optional task for sending MIME mail. But this task highly depends on what contents you require in your mail.
For this task, I have written a class that generates a release note for me which I attach to my mail. Though this highly depends on your requirement but here I have used a very important feature of ANT that is developing custom tasks.
To know how to develop a custoom task, please visit ant.apache.org/manual/develop.html
Here taskdef is used to declare a new Task to ANT and which class should be loaded to implement this task.
The below code declares a new task which is named as releasenote and then this task is to create the release note.

<taskdef name="releasenote" classname="com.note.ReleaseNoteCreator"/>
<releasenote appName="${app.name}" noteName="${note.name}" releasedFile="ftp://ftp.zensar.com/${ftp.remotedir}/${target.filename}"/>

Once the release note is created, the following task will release a mail for you according to the paramters provided.

<mail from="${mail.from}" tolist="${mail.to}" cclist="${mail.cc}" files="${note.name}" user="${mail.user}" password="${mail.pass}" ssl="no" replyto="${mail.reply}" subject="${mail.subject}" mailhost="${mail.host}" mailport="${mail.port}" messagefile="${mail.message}"/> <!--message="${mail.message}"/-->

The complete target will look something like this:

<target name="mail" description="Send release mail" depends="init">
<echo message="Generating Release Note"/>
<property name="note.name" value="${zip.dir}/${app.name}/ReleaseNote-${timestamp}.txt"/>
<mail from="${mail.from}" tolist="${mail.to}" cclist="${mail.cc}" files="${note.name}" user="${mail.user}" password="${mail.pass}" ssl="no" replyto="${mail.reply}" subject="${mail.subject}" mailhost="${mail.host}" mailport="${mail.port}" messagefile="${mail.message}"/> <!--message="${mail.message}"/-->
</target>

As you can see each target depends on a "init" target. This "init" target is used to perform some chores before starting the reelase process. Here it defines a timestamp variable which is used to generate distinct filesnames. The init target will look something like this
Here the record task is used for ANT logging.

<target name="init" description="Create password file and try Login">
<tstamp/>
<property name="timestamp" value="${DSTAMP}-${TSTAMP}"/>
<record name="${log.file}-${timestamp}.log" action="start" append="yes" loglevel="debug"/>
<echo message="Update, Tag, Package, Upload, and release latest source code from ${rep.type} for ${app.name}"/>
</target>

And finally you can either define a target which will call other targets or set the target execution sequence while executing ANT build file.

My final ANT build file for this process comes out to be:

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="RELEASE_BUILD" default="getsource" basedir=".">

<property file="${arg-app}-build.properties"/>
<property name="rep.type" value="${arg-rep}"/>
<property name="base.dir" value="${ws.dir}/${app.name}"/>

<taskdef name="releasenote" classname="com.note.CreateReleaseNote"/>
<target name="init" description="Create password file and try Login">
<tstamp/>
<property name="timestamp" value="${DSTAMP}-${TSTAMP}"/>
<record name="${log.file}-${timestamp}.log" action="start" append="yes" loglevel="debug"/>
<echo message="Update, Tag, Package, Upload, and release latest source code from ${rep.type} for ${app.name}"/>
</target>

<!-- CVS Starts-->
<target name="cvs" description="get the latest source code from CVS" >
<cvspass cvsRoot="${cvs.root}" password="${cvs.pass}" passfile=".cvs-pass"/>
<cvs command="login" cvsRoot="${cvs.root}" passfile=".cvs-pass"/>
<cvs dest="${base.dir}" cvsRoot="${cvs.root}" command="update -A -dPC" passfile=".cvspass"/>
<cvs cvsRoot="${cvs.root}" command="tag ${app.name}-${timestamp}" package="${app.name}" passfile=".cvspass"/>
</target>
<!-- CVS ends -->

<!-- VSS starts -->
<target name="vss" description="get the latest source code from VSS" >
<vssget serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" localpath="${base.dir}" login="${vss.username},${vss.pass}" recursive="true" />
<echo message="Label ${vss.path}/${app.name} with ${app.name}-${timestamp}"/>
<vsslabel serverpath="${vss.server}" vsspath="${vss.path}/${app.name}" ssdir="${vss.ssdir}" login="${vss.username},${vss.pass}" label="${app.name}-${timestamp}" />
</target>
<!-- VSS ends -->

<!-- Common Packaging, uploading and mail starts -->
<taskdef name="releasenote" classname="com.note.CreateReleaseNote"/>

<target name="package" description="Package the source code and Contextroot" >
<property name="target.filename" value="${app.name}-${timestamp}.zip"/>
<zip zipfile="${zip.dir}/${app.name}/${target.filename}" >
<fileset dir="${base.dir}">
<include name="src/**"/>
<include name="ContextRoot/jsp/**"/>
<include name="ContextRoot/WEB-INF/**"/>
<include name="src/META_INF/ejb-jar.xml"/>
<exclude name="ContextRoot/WEB-INF/classes/**"/>
<exclude name="ContextRoot/WEB-INF/lib/**"/>
<exclude name="ContextRoot/jsp/**/*.gif"/>
<exclude name="ContextRoot/jsp/image/**"/>
<exclude name="src/META_INF/**"/>
</fileset>
</zip>
</target>

<target name="upload" description="uploads to FTP">
<echo message="Uploading file to ftp : ${target.filename}"/>
<ftp server="${ftp.url}" userid="${ftp.username}" password="${ftp.password}" binary="yes" remotedir="${ftp.remotedir}">
<fileset dir="${zip.dir}/${app.name}">
<include name="${target.filename}"/>
</fileset>
</ftp>
<echo message=" Release uploaded successfully "/>
</target>

<target name="mail" description="Send release mail" depends="init">
<echo message="Generating Release Note"/>
<property name="note.name" value="${zip.dir}/${app.name}/ReleaseNote-${timestamp}.txt"/>
<releasenote appName="${app.name}" noteName="${note.name}" releasedFile="ftp://ftp.zensar.com/${ftp.remotedir}/${target.filename}"/>
<mail from="${mail.from}" tolist="${mail.to}" cclist="${mail.cc}" files="${note.name}" user="${mail.user}" password="${mail.pass}" ssl="no" replyto="${mail.reply}" subject="${mail.subject}" mailhost="${mail.host}" mailport="${mail.port}" messagefile="${mail.message}"/> <!--message="${mail.message}"/-->
</target>

<!-- Common Packaging, uploading and mail ends -->
<target name="getsource" description="Update, Tag, Package, Upload, and release latest source code from repository" depends="init">
<antcall target="${rep.type}" />
<echo message="Update Latest Source code completed"/>
<antcall target="release" />
</target>

<target name="release" description="Package, Upload, and release Mail" depends="package,upload,mail">
<echo message="**Release of ${app.name} latest source code completed successfully**"/>
</target>

</project>

And the properties file that specifies all the paramters required is...

# Application Name as used in APWORKS.
app=MyProject
app.name=MyProject


###### CVS Path (Optional) ########
cvs.root=:pserver:husain@192.168.1.155:/app/cvs/cvsroot
#CVS Password
cvs.pass=

# The cvs module path of the application required for checkout operation.
cvs.module=DEVELOPMENT/MyProject
######## VSS Details #########
# As specified in VSS server path
vss.server=\\\\servername\\repository\\
vss.path=$/VSS/DEVELOPMENT
vss.ssdir=D:/VSS Client
vss.username=vssuser
vss.pass=vssuserpass

####### Workspace Details ##########
#Workspace location. The code from CVS is updated at ws.dir/app.name
ws.dir=D:/ide/workspace
#Target local as well as backup Directory to store zip files to be uploaded to ftp server. Used as parent dir to app.name dir.
zip.dir=D:/BackUp


######## ftp Details ########
ftp.url=ftp.ind.zensar.com
#Target ftp directory
ftp.remotedir=SOURCE/MyProject
ftp.username=user
#ftp password.
ftp.password=password

##############Mail Settings. Used to mail the release note.
mail.message=mailBody.txt
mail.host=mail.mailserver.com
mail.port=25
# list of mail ids
mail.to=target1@domain.com,cm@domain.com
mail.cc=target3@domain.com,cm2@domain.com

#Senders mail-id
mail.user=myself@domain.com
mail.from=myreplymail@domain.com

#Reply mail-id
mail.reply=myreplymail@domain.com
#Senders mail-id password.
mail.pass=mypassword
mail.subject=Source Code Release [Auto-Generated]

#Logger
log.file=D:/BackUp/buildlog


Resources:
Ant Home Page: http://ant.apache.org
Ant installation Manual: http://ant.apache.org/manual/install.html
Ant Manual: http://ant.apache.org/manual/index.html
A good article on ANT Automation: http://www.onjava.com/pub/a/onjava/2002/07/24/antauto.html