Using Image Button Beans in Struts

The following example modifies the Struts EJB example to demonstrate the use of Image Button Beans.refer post – “EJB with STRUTS”

ImageButtonBeans are specific to struts.
Normally when you have a submit button like

<input type = “submit” name=”buttonName”/>

you do a request.getParameter(“buttonName”);
The thing is that when an image is submitted, the image name is not available in the request. You map the image button name to the name of an element in your form bean:
<html:image property = “blah”…
Form bean has an attribute called “blah” :
private ImageButtonBean blah = new ImageButtonBean();
If you have two submit image buttons on the page, the ImageButtonBean blah will have x and y values populated, so you will know which image button was clicked to submit the form.
If blah.getX() is not null then blah was clicked.
i.e. In the action class if you have

formBean.getReset().getX()!= null

then for instance, reset button was clicked.

The work directory for this example looks as follows:

mysql-ds.xml
jbosscmp-jdbc.xml
ejb-jar.xml
jboss.xml
application.xml
ApplicationResources.properties
build.xml
build.properties
build.bat
struts-config.xml
web.xml
<build>
<images>
|find.jpg
|add.jpg
<pages>
|authors.jsp
|success.jsp
<src>
|shoppingcart
|AuthorsBean.java
|Authors.java
|AuthorsHome.java
|AuthorsKey.java
|authorsForm.java
|authorsAction.java
|ImageButtonBean.java

All files have been reused except for minor modifications in authorsAction.java and authorsForm.java; and an extra class has been introduced.
The following files remain unchanged:
application.xml, ejb-jar.xml, jboss.xml, jbosscmp-jdbc.xml, mysql-ds.xml, struts-config.xml, web.xml
and ApplicationResources.properties.
build.xml has been slightly modified and it is reproduced below:
Changes have been highlighted in bold.
build.xml

<project name="Authors" default="clean" basedir=".">
	<property environment="env" />
	<property file="./build.properties" />

	<!-- the build path -->
	<path id="build.path">
		<pathelement location="${jboss.dist}/server/default/lib" />
		<pathelement location="${jboss.dist}/server/default/lib/jboss-j2ee.jar" />
		<pathelement location="${jboss.dist}/client/javax.servlet.jar" />
		<pathelement location="${build.classes.dir}" />
		<pathelement location="${struts.dir}" />
		<pathelement location="${struts.dir}/struts.jar" />
	</path>

	<target name="war" depends="compile">
		<war warfile="${war}" webxml="web.xml">
			<fileset dir="${basedir}/pages">
				<include name="*.jsp" />
			</fileset>
			<b>
				<fileset dir="${basedir}/images">
					<include name="*.jpg" />
				</fileset>
			</b>
				<webinf dir="${struts.dir}">
					<include name="*.tld" />
				</webinf>
				<webinf dir="${basedir}">
					<include name="struts-config.xml" />
				</webinf>
				<classes dir="${build.classes.dir}">
					<include name="shoppingcart/*.class" />
					<exclude name="shoppingcart/${appname}Bean.class" />
				</classes>
				<classes dir="${basedir}">
					<include name="ApplicationResources.properties" />
				</classes>
				<lib dir="${jboss.client.dir}">
					<include name="jboss-client.jar" />
					<include name="jnp-client.jar" />
				</lib>
				<lib dir="${struts.dir}">
					<include name="struts.jar" />
				</lib>
		</war>
	</target>

	<target name="jar" depends="compile">
		<jar jarfile="${jar}">
			<fileset dir="${build.classes.dir}">
				<include name="shoppingcart/${appname}.class" />
				<include name="shoppingcart/${appname}Home.class" />
				<include name="shoppingcart/${appname}Bean.class" />
				<include name="shoppingcart/${appname}Key.class" />
			</fileset>
			<metainf dir="${basedir}" includes="jboss.xml,ejb-jar.xml,jbosscmp-jdbc.xml" />
		</jar>
	</target>

	<!-- build all, and copy to the jboss/deploy directory -->

	<target name="ear" depends="jar,war">
		<ear earfile="${ear}" appxml="application.xml">
			<fileset dir="${basedir}" includes="${jar},${war}" />
		</ear>
	</target>

	<!-- compilation options -->

	<target name="compile">
		<mkdir dir="${build.classes.dir}" />
		<javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="on"
			deprecation="on" classpathref="build.path" optimize="off" />
	</target>
	<target name="build-all" depends="ear">
		<copy file="${ear}" todir="${jboss.deploy.dir}" />
		<copy file="mysql-ds.xml" todir="${jboss.deploy.dir}" />
	</target>
	<target name="clean">
		<delete file="${jar}" />
		<delete file="${ear}" />
		<delete file="${war}" />
		<delete file="${jboss.deploy.dir}/${ear}" />
		<delete file="${jboss.deploy.dir}/mysql-ds.xml" />
		<delete dir="${build.classes.dir}" />
	</target>
</project>

The entity bean classes remain unchanged.
There is a slight modification to authorsForm.java. Changes have been highlighted.
authorsForm.java

package shoppingcart;

import javax.servlet.http.*;

public class authorsForm extends org.apache.struts.action.ActionForm {
	private ImageButtonBean findButton = new ImageButtonBean();
	private ImageButtonBean addButton = new ImageButtonBean();
	String author;
	String isbnCode;

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getAuthor() {
		return this.author;
	}

	public void setIsbnCode(String isbnCode) {
		this.isbnCode = isbnCode;
	}

	public String getIsbnCode() {
		return this.isbnCode;
	}

	public void setFindButton(ImageButtonBean button) {
		this.findButton = button;
	}

	public ImageButtonBean getFindButton() {
		return this.findButton;
	}

	public void setAddButton(ImageButtonBean button) {
		this.addButton = button;
	}

	public ImageButtonBean getAddButton() {
		return this.addButton;
	}
}

There is a slight modification to authorsAction.java. Changes have been highlighted.
authorsAction.java

package shoppingcart;

import shoppingcart.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import org.apache.struts.action.*;
import java.util.*;

public class authorsAction extends Action {
	// public ActionForward perform(ActionMapping mapping, ActionForm
	// form,HttpServletRequest req, HttpServletResponse res) throws
	// java.io.IOException,javax.servlet.ServletException
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest req, HttpServletResponse res)
			throws java.lang.Exception {
		String action = null;
		String auth = ((authorsForm) form).getAuthor();
		String isbn = ((authorsForm) form).getIsbnCode();
		if ((((authorsForm) form).getAddButton().getX() != null)
				|| (((authorsForm) form).getAddButton().getY() != null)) {
			action = "add";
		}
		if ((((authorsForm) form).getFindButton().getX() != null)
				|| (((authorsForm) form).getFindButton().getY() != null)) {
			action = "find";
		}
		AuthorsHome authorsHome = null;
		Authors bean = null;
		try {
			Properties p = new Properties();
			p.setProperty("java.naming.factory.initial",
					"org.jnp.interfaces.NamingContextFactory");
			p.setProperty("java.naming.provider.url", "localhost:1099");
			p.setProperty("java.naming,factory.url.pkgs",
					"org.jboss.naming:org.jnp.interfaces");
			InitialContext jndiContext = new InitialContext(p);
			Object ref = jndiContext.lookup("shoppingcart/Authors");
			authorsHome = (AuthorsHome) PortableRemoteObject.narrow(ref,
					AuthorsHome.class);
		} catch (Exception e) {
			throw new ServletException("failed to lookup shoppingcart/Authors",
					e);
		}
		req.setAttribute("author", auth);
		req.setAttribute("isbnCode", isbn);
		if (action.equals("add")) {
			try {
				AuthorsKey key = new AuthorsKey(isbn);
				bean = authorsHome.create(key);
				if (auth != null) {
					bean.setAuthorName(auth);
				}
				req.setAttribute("author", auth);
				req.setAttribute("isbnCode", isbn);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (action.equals("find")) {
			try {
				if (isbn != null) {
					AuthorsKey key = new AuthorsKey(isbn);
					bean = authorsHome.findByPrimaryKey(key);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			req.setAttribute("author", bean.getAuthorName());
			req.setAttribute("isbnCode", isbn);
		}
		return mapping.findForward("successful");
	}
};

An extra class called ImageButtonBean.java has been added:
ImageButtonBean.java

package shoppingcart;

public final class ImageButtonBean extends Object {
	private String x = null;
	private String y = null;

	public String getX() {
		return (this.x);
	}

	public void setX(String x) {
		this.x = x;
	}

	public String getY() {
		return this.y;
	}

	public void setY(String y) {
		this.y = y;
	}
}// end imagebuttonbean

success.jsp remains unchanged. authors.jsp has been changed as follows:
authors.jsp

<html>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
<head>
</head>
<body>
	<html:form action="/testout.matty">
		<p>
			<html:errors />
			<br />
			<bean:message key="isbnCode.label" />
			<html:text property="isbnCode" size='10' />
			<br />
			<bean:message key="author.label" />
			<html:text property="author" size='10' />
			<br /> <b> <html:image property="addButton" src="./add.jpg" />
				<html:image property="findButton" src="./find.jpg" /> </b>
	</html:form>
</body>
</html>

The ear file has the following structure:

authors.ear
    |<meta-INF>
        |application.xml
    |authors.jar
        |<meta-INF>
             |ejb-jar.xml
             |jboss.xml
             |jbosscmp-jdbc.xml
        |<shoppingcart>
             |AuthorsBean.class
             |Authors.class
             |AuthorsHome.class
             |AuthorsKey.class
    |authors.war
        |add.jpg
        |find.jpg
        |authors.jsp
        |success.jsp
        |<meta-INF>
        |<web-INF>
             |struts-config.xml
             |web.xml
             |struts-bean.tld
             |struts-html.tld
             |struts-logic.tld
             |struts-nested.tld
             |struts-logic.tld
             |<classes>
                     |ApplicationResources.properties
                     |<shoppingcart>
                          |Authors.class
                          |AuthorsKey.class
                          |AuthorsHome.class
                          |authorsForm.class
                          |authorsAction.class
                          |ImageButtonBean.class
              |<lib>
                     |jnp-client.java
                     |jboss-client.jar
                     |struts.jar

The application be started by using the following url on Jboss.
http://localhost:8080/authors.jsp

More On Tiles

The purpose of tiles is to have a common layout for all pages in an application (or module). All pages should have a similar look and feel with respect to position of header/ footer/ sidebar/ title components.
Also it encourages reuse of component JSPs that are inserted into multiple pages. For example there is a separate header.jsp defined that is inserted into all pages of the application at runtime. This makes more it more reusable than copying/ pasting the contents of the footer JSP into each of the pages.
Another reason for using tiles is to give pseudo names for JSP pages so that JSPs are not referred to by their physical names.
In the stuts-config.xml add this entry (if a similar entry exists, modify it to look like this)

<!—Tiles controller-->
        <controller processorClass=“org.apache.struts.tiles.TilesRequestProcessor”
	      nocache=“true” />
<!-- Plug Ins Configuration-->
        <plug-in className = “org.apache.struts.tiles.TilesPlugin”>
	      <set-property property = “definitionConfigFiles” value = “/WEB-INF/tiles-defs.xml/>
	</plug-in>

This directive instructs struts to use Tiles (It’s an optional thing).
In tiles, there is a “Layout” JSP (which is the main JSP) which defines how the page should look like, that is, defines the positions of the footer, header, sidebar, title, etc.

<!—DOCTYPE HTML PUBLIC = “-//W3C//DTD HTML 4.01 Transitional//EN”>
<%@ taglib uri=“/WEB_INF/tiles.tld” prefix=“tiles”%>
<%@ taglib uri = “/WEB_INF/struts-html.tld prefix = “html”%>
<html>
<head>
<title><tiles:getAsString name=“title”>
</title>
<!—this means that title is a string that goes here and the value will be specified in tiles-defs.xml-->
</head>
<tiles: insert attribute=“header” />
<tiles: insert attribute=“body”
	<!—this means that body is a jsp that goes here and the jsp name will be specified in tiles-defs.xml-->

</html>

Here as you can see, the layout defines that the title is a string that appears in the

<head>

tag and that the header and body are JSPs that get inserted after that.
In addition you can define more inserts for other JSPs that constitute the footer, sidebar, etc.
The /WEB-INF/tiles-defs.xml file will define the layout pages and individual JSP pages used by the application.

It looks like this

<!DOCTYPE tiles-definitions PUBLIC “//Apache Software Foundation//DTD Tiles Configuration//EN” http://jakarta.apache.org/struts/dtds/tiles-config.dtd>
<tiles-definitions> <!—Master Template-->
	<definition name=“layout” path=“/WEB-INF/views/layout/Layout.jsp”>
		<put name=“title” value=“” />
	</definition> <!—Page Definitions-->
	<definition name=“searchPage” extends=“layout”>
		<put name=“body” value=“/views/body/search/SearchForm.jsp” />
		<put name=“title” value=“Search” />
		< put name = header” value =
		“/WEB-INF/views/layout/Header.jsp”>
	</definition>
	<definition name=“displayPage” extends=“layout”>
		<put name=“body” value=“/views/body/display/DisplayForm.jsp” />
		<put name=“title” value=“Display” />
		<put name=“header” value=“/WEB-INF/views/layout/Header.jsp” />
	</definition> <!—similarly more page definition-->
<!—all these pages use the same layout that defines position of title and body. The pages use a different body but the same header-->
</tiles-definitions>

struts-config.xml
In the action mappings, instead of mentioning the names of the JSPs directly, use the pseudo names, specified in tiles-defs.xml.
eg. Use searchPage and displayPage instead of using the JSP file names “/views/body/search/SearchForm.jsp and /views/body/display/DisplayForm.jsp.

Log4j Logging

Put the log4j and commons-logging.jar in the build path. These jar files contain the information for logging set up.
Declare the following as a class attribute in the class where you want to implement the logging, say SomeClass.java

private static Logger logger = Logger.getLogger(com.mattiz.SomeClass.class);

You would have a sample log4j properties configuration file which you should put in your classpath.
Suppose you wanted the words “Test Debug” in the following line to go to your log file, then use logger.error or logger.debug or logger.warn.

Viz. logger.error(“Test Debug”);

For warning/ debugging messages you could use

logger.warn(“This is a warning”);

or

logger.debug(“Variable value is”+var);

In the log4j configuration file you can set the level of logging.
In the sample configuration file you will find words like ERROR, WARN or DEBUG.
If you set the level to WARN, then only warnings and errors will print, while debug won’t print. If you set the level to DEBUG level then debug, info, warn and error will print.
These may go to the console or to a separate log output file based on the settings in the log4j configuration file. The log4j.properties contains settings to “rollover” files. If file becomes more than 1 MB then copying to another file and creating a new file is done automatically by log4j.
With the settings in the log4j.properties file you can control what is being logged. By setting it to ERROR level, debug statements won’t be printed in the production system.
For development use DEBUG level because you want to see debug messages.
The hierarchy is

DEBUG<info<warn<error<fatal

If set to WARN level, all warn, error and fatal messages will be printed but no info and debug messages will be printed.
You can set logging levels for each package. So if you are working on a certain package you can set the package’s logging level to DEBUG, and another package’s logging level to WARN in the log4j properties file.
For example

log4j.category.com.mattiz.security = WARN

You need not use all the debugging levels; mostly people use DEBUG and ERROR.
You can put error level messages inside the catch block.
Error logs are like this

logger.error(“Exception critical”+ex.toString());

You can do this for production systems.

Contents of a simple log4j.properties configuration file

# Print FATAL, ERROR and WARN messages - do not print DEBUG and INFO messages
# the sequence is FATAL &gt; ERROR &gt; WARN &gt; INFO &gt; DEBUG
# since the level is set to WARN - message levels above it will be printed
# while levels below it will not be printed
log4j.rootCategory=WARN, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# the conversion pattern will be used to format the timestamp see below example
# 2004-05-13 17:15:13,318 [Servlet.Engine.Transports : 1] DEBUG
# this will pre-pend all logging messages
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c{1} - %m%n

Contents of a more elaborate log4j.properties file

# the general level is set to WARN
# WARN, ERROR, FATAL will be printed
# In addition to printing to System Out, also print to "RollingFile"
log4j.rootCategory=WARN, stdout, RollingFile

# the level for the com.mattiz.web package (and sub-packages) is set to DEBUG
# DEBUG, WARN, ERROR, FATAL will be printed for the web package
log4j.category.com.mattiz.web=DEBUG

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Print the date and time for systemOut
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c{1} - %m%n

# Save log to Rolling File Also
log4j.appender.RollingFile=org.apache.log4j.RollingFileAppender
# Location of rolling file
log4j.appender.RollingFile.File=d:/mattiz/mattiz.log

# if the file becomes greater than 500KB then create a new file and backup the old file
log4j.appender.RollingFile.MaxFileSize=500KB
# Keep 5 back up files
log4j.appender.RollingFile.MaxBackupIndex=5

log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout

#Print the date and time for RollingFile
log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p %c{1} - %m%n