Showing posts with label struts2. Show all posts
Showing posts with label struts2. Show all posts

Sunday, February 24, 2008

Struts2: dynamic file download

(there is a better method for this at the end of this post)

In struts2, file is downloaded as stream using StreamResult class.
To download file of dynamic name, size or type we need to extend this StreamResult class.

StreamResult result type takes the following parameters:

* contentType - the stream mime-type as sent to the web browser (default = text/plain).
* contentLength - the stream length in bytes (the browser displays a progress bar).
* contentDispostion - the content disposition header value for specifing the file name (default = inline, values are typically filename="document.pdf".
* inputName - the name of the InputStream property from the chained action (default = inputStream).
* bufferSize - the size of the buffer to copy from input to output (default = 1024).

Example:

<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">filename="document.pdf"</param>

<param name="bufferSize">1024</param>
</result>


So in the extended StreamResult class we need to set these parameters dynamically.
You could download source code from here, its same as following:

package downloadexample;
import org.apache.struts2.dispatcher.StreamResult;

import com.opensymphony.xwork2.ActionInvocation;

/**
* This class for result-type="myStream"

*
* <result-types> <result-type name="myStream" default="false"
* class="downloadexample.DynamicStreamResult"/>
*
* </result-types>

*
* It extends StreamResult Used to download file as a stream.
*
* @author sheetal
*
*/


public class DynamicStreamResult extends StreamResult{
@Override

protected void doExecute(String finalLocation,
ActionInvocation invocation)
throws Exception {

//get name of downloaded file
String downloadedFileName = invocation.getStack().
findValue(conditionalParse
("name", invocation));

contentDisposition = "filename=\""

+downloadedFileName + "\"";

//get file size
contentLength = ""+ invocation.getStack().findValue(
conditionalParse("size", invocation));
// get type of file
contentType = ""+ invocation.getStack().
findValue(
conditionalParse("description", invocation));
/*
Executes the result given a final location
(jsp page, action, etc) and
the action invocation (the state in which
the action was executed).
*/

super.doExecute(finalLocation, invocation);

}

}



Let, our site is a search site where user inputs name of a file. our system searches the file in server's local directory and lets the user download it if found.

To do this, .jsp file should be include the following:


<s:form action="downloadFile" validate="true">

<s:textfield label="Search file"

name="name" required="true"/>

<s:textfield label="Define file type (image/jpeg, text/plain, application/pdf)"
name="description" required="true"/>

<s:submit value="Find file"/>

</s:form>


where downloadFile is the action for downloading file.

In your action class, add the following lines:

private String name;
//holds name of downloaded file

private InputStream inputStream;
//holds stream of downloaded file
private String description;
//holds the content type of the downloaded file

private long size;
//holds the content size of the downloaded file


//method for downloading file
public String downloadFile()
{

/*
let, method searchFile(String fileName)
does the searching for us
& returns InputStream of the file if found
and null otherwise.
*/

this.inputStream = searchFile(name);

if(inputStream !=null)
{

return Action.SUCCESS;

}
else
{
//handle error
return Action.ERROR;

}


}

//write setter getter methods
public InputStream getInputStream() throws Exception

{
return inputStream;

}

public String getName()

{
return name;
}
public void setName(String name)

{
this.name = name;
}
public String getDescription()

{
return this.description;
}
public void setDescription(String description)

{
this.description = description;
}
// write getter setter for attribute size


Now, edit your struts.xml file:


<!-- custom result type for file download -->
<result-types>
<result-type name="myStream"

default="false"
class="downloadexample.DynamicStreamResult"/>

</result-types>
<!-- action for downloading file-->
<action name="downloadFile"

method="downloadFile"
class="<action-class-name>
">

<result type="myStream"/>
<result name="error">jsps/your_error_page.jsp</result>


</action>



syntax highlighted by Code2HTML, v. 0.9.1

& we are done :D.

...................................................
May be a Better method:
the time i wrote this post i was too naive to find other solution for this. One person commented a quick solution on the blog.... thank you again. i haven't tried it, so i'm not sure if it works, check yourself...here is the comment.........

Actually, there's no need to extend the StreamResult class. You can dynamically pass the contentType (and other Stream parameters) by using parameter substitution in your Action mapping, like so:


<result name="success" type="stream">

<param name="contentType">${contentType}</param>
<param name="inputName">imageStream</param>

<param name="contentDisposition">filename="${fileName}"</param>

<param name="bufferSize">${bufferSize}</param>

</result>



You then add methods set/getContentType(), set/getFileName() and set/getBufferSize() to your Action class. In the Action method which handles your business logic, all you have to do is call this.setContentType(), this.setFileName() and this.setBufferSize() and supply whatever values you like.

---------------------------------------

Saturday, December 22, 2007

struts2+Spring workflow

1. An initial request goes to the Servlet container (such as Jetty or Resin) which is passed through a standard filter chain.

2. the required FilterDispatcher is called, which in turn consults the ActionMapper to determine if the request should invoke an action.


3. The ActionMapper interface provides a mapping between HTTP requests and action invocation requests and vice-versa.

When given an HttpServletRequest, the ActionMapper may return null if no action invocation request matches, or it may return an ActionMapping that describes an action invocation for the framework to try.

If the ActionMapper determines that an Action should be invoked, the FilterDispatcher delegates control to the ActionProxy.

4. The ActionProxy consults the framework Configuration Files manager (initialized from the struts.xml file).

5. the ActionProxy creates an ActionInvocation, which is responsible for the command pattern implementation. This includes invoking any Interceptors (the before clause) in advance of invoking the Action itself.

6. Once the Action returns, the ActionInvocation is responsible for looking up the proper result associated with the Action result code mapped in struts.xml.

7. The result is then executed, which often (but not always, as is the case for Action Chaining) involves a template written in JSP or FreeMarker to be rendered.


8. While rendering, the templates can use the Struts Tags provided by the framework.

9. All objects in this architecture (Actions, Results, Interceptors, and so forth) are created by an ObjectFactory.

This ObjectFactory is pluggable. We can provide our own ObjectFactory
A popular ObjectFactory implementation uses Spring as provided by the Spring Plugin.

Normally, in struts.xml you specify the class for each Action. When using the default SpringObjectFactory, the framework will ask Spring to create the Action and wire up dependencies as specified by the default auto-wire behavior.


10. Interceptors are executed again (in reverse order, calling the after clause).


11. Finally, the response returns through the filters configured in the web.xml.


N.B:
don't know how to setup simple struts2 project? see simple setup

Monday, July 02, 2007

Migrating from struts1 to struts2

www.denverjug.org/meetings/files/200611_MigratingFromStruts1ToStruts2.pdf
--
Sheetal

struts2+spring+hibernate(part2)

useful links :
a sample code (by me)
struts2+spring workflow
struts2+spring+jpa
struts2 online book
hibernate tutorial
integrating spring,hibernate

struts2 + spring + hibernate

(If u don't how struts2+spring works, see struts2+spring workflow)

What i'm trying do to here:
i have a table named "User" in our database.
now i want to build a web-application using struts2+spring+hibernate
1. to add new user to database and
2. to log in to home page for existing user.

Download sourcecodes

Files (java/xml) i'll create for this:

1. "User" table in database.

2. User.java:
which will be mapped with that "User" table.
3. User.hbm.xml:
that maps User table with User.java.
4. IUserDao.java:
interface for data access object.
5. UserDAOHibernateImpl.java:
which implements IUserDao.java
6. IUserService.java:
interface for service
7. UserServiceImpl.java:
which implements IUserService
8. UserAction.java:
action class for struts2
9. applicationContext.xml:
Register your objects via the Spring configuration.
all the above files and data source will be configured here.
10. struts.xml:
its the configuration file for struts2 (which was struts-config.xml for struts1)
11. output *.jsp files, i've used 3 here:
11.i) addUser.jsp: to add new user in DB
11.ii) login.jsp: to log in existing user
11.iii) hello.jsp: says hello to the user after successful log in.
12. xml files for validation
13. and good old web.xml

jar files we need are same as this example

so now.......Ready... Set... GO!

1.Let, database used is mysql. database name ExampleDB and table name User.


create database ExampleDB;
use ExampleDB;
create table User(
name varchar(20),
id
varchar(20) not null primary key
);





2. write the web.xml file:

In the web.xml file, Struts defines its FilterDispatcher, the Servlet Filter class that initializes the Struts framework and handles all requests.

If you don't know how to write web.xml for struts2, see web.xml

In most cases, we can simply use web.xml for struts2-blank template. we just need to add the following lines to configure the Spring listener.

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener

</listener-class>
</listener>



Everything else is same as any other web.xml for struts2.

<?xml version="1.0" encoding="UTF-8"?>

<web-app id="example" version="2.4"
xmlns=
"http://java.sun.com/xml/ns/j2ee"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>


<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>

<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>

</filter-mapping>

<welcome-file-list>
<welcome-file>/login.jsp</welcome-file>
</welcome-file-list>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener

</listener-class>
</listener>

</web-app>





3.write User.java file that will be used to map the User table.

package com.example.user; 

public class User {

private String id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {

this.name = name;
}
/** * @return Returns the userId. */

public String getId() {
return id;
}

/** * @param userId The userId to set. */
public void setId(String userId) {
this.id = userId;
}

}






4.Now maps the database table with .java file. To do this, write the mapping file, User.hbm.xml :

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >


<!--Maps User.java class with User
database table -->


<hibernate-mapping>
<class name="com.example.user.User"

table="User">


<id name="id" column="id" >
<generator class="assigned" />

</id>
<!--
id tag represents the primary key of
database table generator class defines
how key will be generated.
Here we've choose "assigned", that is key will be
assigned before insert.
Other generator classes are:
1.native
2.increment : auto increment the key value
-->


<property name="name" column="name" />

<!--
property tag maps columns other than
the primary key column.
-->


</class>
</hibernate-mapping>



Put all your *.hbm.xml files in com/example/user/hbm folder.

5.Write the Data Access Object that would be used to access database using hibernate.
At first we would write the interface : IUserDao.java

package com.example.user ; 
public interface IUserDao {
public String addUser(User user);
public User findUserById(final String Id);


}

//Now write UserDAOHibernateImpl.java :

package com.example.user;
import org.springframework.orm.
hibernate3.HibernateTemplate;

public class UserDAOHibernateImpl
implements IUserDao
extends HibernateDaoSupport
{


public String addUser(User user){
String success;
try{
getHibernateTemplate().save(user);
success="Welcome !";
}
catch(Exception e){
success="Sorry, user cannot be added";
}
return success;
}

public User findUserById(final String id){

return (User)

getHibernateTemplate().get(User.class, id);
}

}






getHibernateTemplate().save(user) would insert user into the User database table
we've used HibernateTemplate because it provides easy method to do basic queries and also it is thread safe.
get method will return the user object with given id if such an user existed, otherwise null will be returned.

6. Write the service class (it would seem a little redundant at first, but it's the best practice to provide service layer between action and dao class).
Usually database transaction management is applied on this layer.
//Interface for service class:

package com.example.user;

public interface IUserService {
public String add(User user);
public User findById(final String Id);

}

//Service class: UserServiceImpl.java

package com.example.user;
public class UserServiceImpl implements IUserService{

private IUserDao userDao;

public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {

//userDao will be set by applicationContext.xml

this.userDao = userDao;
}

public String add(User user){
String result=userDao.addUser(user);
return result;
}
public User findById(String Id){
User user=userDao.findUserById(Id);
return user;
}

}





7.At last, now write the action class: UserAction.java
Here our action class will
1.take the user id
2.retrieve user name from database
3.and say " Hello <user name>"
4.add new user

package com.example.user; 

import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext ;


public class UserAction {
private IUserService userService;
private User user;
private String id;
private String name;

/** * Constructor * @param userService */
public UserAction(IUserService userService)
{

this.userService=userService;

// will be set by applicationContext.xml
}
public String execute() {
this.user=

(User)userService.findById(this.id);
String result;
if(user==null){

// returns error if user doesn't exist.

result="User id does not exist";
ActionContext.getContext().
getSession().

put("result", result);
return Action.ERROR;
}
else {
result="";
ActionContext.getContext().
getSession().
put("user", user);

// puts user object in session
return Action.SUCCESS;
}
}
public String signup(){
return Action.SUCCESS;
}
public String addUser(){
String result;
this.user=

(User)userService.findById(id);
if(user==null){
user=new User();
user.setId(this.id);
user.setName(this.name);
result=userService.add (user);
ActionContext.getContext().
getSession().
put("user", user);
}
else{
result="Id already used,
Choose another Id"
;
ActionContext.getContext().
getSession().
put("result",result);

return Action.ERROR;
}
ActionContext.getContext().
getSession().
put("result", result);
return Action.SUCCESS;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}


}



8.Write the jsp files: we will use 3 jsps here
1.login.jsp: take the id from user
2.hello.jsp: say hello
3.addUser.jsp: add a user



login.jsp:
<%@ taglib prefix="s" uri="/struts-tags"%>

<html >
<head>
<title>Log in</title>
</head>
<body>
<h2><s:property value="#session.result"/></h2>
<s:form action="SignIn" validate="true">
<s:textfield label="User Id" name="id" required="true"/>
<s:submit value="Login" />
</s:form>
<a href=" <s:url action="SignUp"/> "> Sign Up As A New User </a>
</body>

</html>
---------------------------------------------------------------
hello.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<html >
<head>
<title>Hello</title>
</head>
<body>
<s:if test="#session.user!=null">
<h2>Hello <s:property value="#session.user.name"/></h2>
</s:if>
</body>

</html>
--------------------------------------------------------------------
addUser.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<html >
<head>
<title>Sign Up</title>
</head>
<body>
<s:property value="#session.result"/></h2>
<s:form action="add" validate="true">
<s:textfield label="User Name " name="name" required="true"/>
<s:textfield label="User Id" name="id" required="true"/>
<s:submit value="Submit"/>
</s:form>
</body>

</html>





9. Write the applicationContext.xml file:

<?xml version=" 1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">


<!--
- Application context definition
-->
<beans>
<!-- ========================= RESOURCE DEFINITIONS ========================= -->

<!-- Creating a data source connection pool-->

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource ">


<property name="driverClassName">

<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/ExampleDB</value>

</property>
<!-- Provide username and password for mysql -->
<property name="username" value="root"/>
<property name="password" value=""/>



</bean>


<!-- Hibernate SessionFactory -->

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">


<property name="dataSource"><ref bean="dataSource"/></property>
<property name="mappingDirectoryLocations">
<list>

<value>classpath:/com/example/user/hbm</value>
</list>
</property>

<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">

org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
<!-- show the sql command in console -->
</props>
</property>

</bean>



<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->

<!--
Data access object: Hibernate implementation.
-->


<!-- with transaction -->
<bean id="hibernateTemplate"

class="org.springframework.orm.hibernate3.HibernateTemplate">

<property name="sessionFactory">
<ref bean="sessionFactory"/>

</property>
</bean>

<bean id="UserDaoImpl" class="com.example.user.UserDAOHibernateImpl">
<property name="hibernateTemplate">

<ref bean="hibernateTemplate"/>
</property>
<!-- set HibernateTemplate in UserDAOHibernateImpl.java class-->
</bean>

<!--
Defination of service targets
-->

<bean id="UserServiceTarget"

class="com.example.user.UserServiceImpl">

<property name="userDao">
<ref bean="UserDaoImpl"/>

</property>
<!-- set IUserDao in UserServiceImpl.java class-->
</bean>


<!--
Definition of Transaction Manager
-->

<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">


<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<!--
Base service object
-->


<bean id="Service"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">

<property name="transactionManager">

<ref bean="transactionManager"/>
</property>

<property name="transactionAttributes">
<props>


<prop key="add">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<!-- specifies the methods on which transaction management will be applied-->

</props>
</property>
</bean>

<!--
Children service objects using base service and service
targets
-->


<bean id="UserService" parent="Service">

<property name="target">
<ref bean="UserServiceTarget"/>
</property>

</bean>


<!--
Definition of Action objects
-->


<bean id="userAction"
class=" com.example.user.UserAction" singleton="false">

<constructor-arg>

<ref bean="UserService"/>
</constructor-arg>
<!-- set IUserService in UserAction.java's constructor-->
</bean>

</beans>




syntax highlighted by Code2HTML, v. 0.9.1


10. Last step :) now, write the struts.xml file:


<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="user" extends="struts-default">


<action name="SignIn" method="execute" class="userAction" >
<result>/hello.jsp</result>

<result name="error">/login.jsp</result>
</action>
<action name="SignUp" method="signup" class="userAction">

<result>/addUser.jsp</result>
</action>

<action name="add" method="addUser" class="userAction">

<result>/hello.jsp</result>
<result name="error">/addUser.jsp</result>
</action>

</package>

</struts>



syntax highlighted by Code2HTML, v. 0.9.1


------------------------------------------------------------------------------------------------
And here's the validation file, just save it under the same folder as the action.
filename should be <action-classname>-<action-name>-validation.xml
For login action, validation filename would be UserAction-SignIn-validation.xml


<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">


<!--
Validation rules for sign in action
-->

<validators>
<field name="id">
<field-validator type="requiredstring">
<message>You must enter user id</message>

</field-validator>
</field>
</validators>



syntax highlighted by Code2HTML, v. 0.9.1



--
Sheetal