Thursday, September 18, 2008

Quartz Scheduler First Impression

Quartz Scheduler is used to make a scheduling job. In this post we will make a scheduler to generate daily report for persons report that have we made before for each one minute. Here we start. Download first Quartz library and copy it into spring lib folder. In package com.ndung.service create two interfaces named EODManager.java and Executable.java.


package com.ndung.service;

public interface EODManager {
void executeEOD();
}

package com.ndung.service;

public interface Executable {
void execute() throws Exception;
}

In package com.ndung.service.impl add a class named EODManagerImpl.java

package com.ndung.service.impl;

import java.util.List;
import com.ndung.service.EODManager;
import com.ndung.service.Executable;

public class EODManagerImpl implements EODManager {
private List<Executable> executables;
public void setExecutables(List<Executable> executables) {
this.executables = executables;
}
public void executeEOD() {
for (Executable executable : executables) {
try { executable.execute(); }
catch (Exception e) { e.printStackTrace(); }
}
}
}


Edit PersonManager.java and PersonManagerImpl.java like belows.

public interface PersonManager extends Manager, Executable{
...
public void generateDailyReport();
}

PersonManagerImpl.java

public void generateDailyReport() {
File dirPath = new File("report");
if (!dirPath.exists()) {
dirPath.mkdirs();
}
File filePath = new File(dirPath+"/PersonReport.pdf");
Map<String, Object> mapReport = new HashMap<String, Object>();
mapReport.put("title", "Persons Report");
mapReport.put("date", new Date());
ReportUtil.generateReport("PersonsReport.jrxml", filePath.getAbsolutePath(),
dao.getPersons(null), mapReport, "pdf");
}
public void execute() throws Exception {
generateDailyReport();
}

Add a bean in applicationContext-service.xml for eodManager.

<bean id="eodManager" class="com.ndung.service.impl.EODManagerImpl">
<property name="executables">
<list>
<ref bean="personManager" />
</list>
</property>
</bean>

In folder web/WEB-INF create applicationContext file named applicationContext-schedule.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="cronTrigger" />
</list>
</property>
</bean>

<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="methodInvokingJobDetail" />
</property>
<property name="cronExpression">
<value>0 0/1 * * * ?</value>
</property>
</bean>

<bean id="methodInvokingJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<ref bean="eodManager" />
</property>
<property name="targetMethod">
<value>executeEOD</value>
</property>
</bean>
</beans>

Run our application and check out if there a file in report/PersonReport.pdf in our computer.

Read More..

Wednesday, September 17, 2008

JasperReports First Impression

In previous post we have made Person model (POJO) and DAO also manager to perform CRUD on person object. Now we will generate report of person list by using JasperReports and IReport. We use IReport to make .jrxml file that needed by JasperReports. This tool is so powerful because easy to use and GUI based. Here we start. Download first JasperReports libraries and IReport application. Copy JasperReports libraries folder into AppfuseExample lib folder. Edit lib.properties file.


#
#Jasper
#
jasper.version=1.2.0
jasper.dir=${lib.dir}/jasperreports-${jasper.version}
jasper.jar=${jasper.dir}/jasperreports-1.2.0.jar

Create a class to generate report in package com.ndung.util named ReportUtil.java.

package com.ndung.util;

import java.io.*;
import java.util.*;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.engine.util.JRLoader;

public class ReportUtil {
public static void generateReport(String templateFilename, String outputFilename, List data,
Map<String, Object> reportParameters, String fileType) {
try {
OutputStream os = new FileOutputStream(outputFilename);
generateReport(templateFilename, os, data, reportParameters, fileType);
os.close();
} catch (IOException e) { e.printStackTrace(); }
}
public static void generateReport(String templateFilename, OutputStream os, List data,
Map<String, Object> reportParameters, String fileType) {
String reportFileName = ReportUtil.class.getResource("/" + templateFilename).getFile();
if(reportParameters == null)
reportParameters = new HashMap<String, Object>();
try {
JasperPrint jasperPrint;
JasperReport jasperReport = getCompiledReport(reportFileName);
jasperPrint = JasperFillManager.fillReport(jasperReport, reportParameters,
new JRBeanCollectionDataSource(data));
JRExporter exporter = null;
if (fileType.equals("txt")) exporter = new JRTextExporter();
else if (fileType.equals("csv")) exporter = new JRCsvExporter();
else if (fileType.equals("xls")) exporter = new JRXlsExporter();
else exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os);
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.exportReport();
} catch (Exception e) { e.printStackTrace(); } }
private static JasperReport getCompiledReport(String reportFileName) throws JRException {
File reportFile = new File(reportFileName.replace("jrxml", "jasper"));
if (!reportFile.exists())
JasperCompileManager.compileReportToFile(reportFileName);
return (JasperReport) JRLoader.loadObject(reportFile.getPath());
}
}

Now open IReport to make jrxml file. Click File -> New Document. Fill report name with PersonsReport.jrxml. Click Tools -> Classpath. Add jar/folder where our classes built. As example: /home/ndung/workspace/AppfuseExample/bin. Click Datasource -> Report Query, in tab JavaBean Datasource, fill class name with com.ndung.model.Person and click Read javabeans attributes. If your classpath right, it will read attributes of person. Create document like picture below by dragging fields or variables or parameters in right panel (library). Save this file into folder src/web. Try to compile this file by clicking button compile. If there is no error, it will be compiled successfully. If this file has been compiled it will generate file .jasper. Check out PersonsReport.jasper in your computer folder.


We will add a page special that can be called from menu directly. In package com.ndung.webapp.action create a class named PersonReport.java. In folder web/pages create two files named personReport.html and personReport.page.

package com.ndung.webapp.action;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import com.ndung.model.Person;
import com.ndung.service.Manager;
import com.ndung.util.ReportUtil;

public abstract class PersonReport extends BasePage{
public abstract Manager getManager();
public void generate() throws IOException{
List persons = getManager().getObjects(Person.class);
OutputStream os = getResponse().getOutputStream();
getResponse().addHeader( "Content-Disposition", "attachment; filename=report.zip");
getResponse().setContentType( "application/zip" ) ;
os = new ZipOutputStream( os );
String templateFilename = "PersonsReport.jrxml";
Map<String, Object> mapReport = new HashMap<String, Object>();
mapReport.put("title", "Persons Report"); mapReport.put("date", new Date());
((ZipOutputStream)os).putNextEntry( new ZipEntry("PersonsReport.pdf" ) ) ;
ReportUtil.generateReport(templateFilename, os, persons, mapReport, "pdf");
((ZipOutputStream)os).closeEntry() ;
os.flush();
os.close();
}
}


<span jwcid="@ShowValidationError" delegate="ognl:beans.delegate"/>
<span jwcid="@ShowMessage"/>
<form jwcid="form">
<input type="submit" class="button" jwcid="@Submit" value="message:button.generate" id="save" action="listener:generate"/>
</form>


<?xml version="1.0"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 4.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
<page-specification class="com.ndung.webapp.action.PersonReport">
<inject property="request" object="service:tapestry.globals.HttpServletRequest"/>
<inject property="response" object="service:tapestry.globals.HttpServletResponse"/>
<inject property="manager" type="spring" object="manager"/>
<bean name="delegate" class="org.apache.tapestry.valid.ValidationDelegate"/>
<component id="form" type="Form">
<binding name="delegate" value="ognl:beans.delegate"/>
<binding name="clientValidationEnabled" value="true"/>
</component>
</page-specification>

In tapestry application add mapping file for .page and .html.

<page name="personReport" specification-path="pages/personReport.page"/>

Edit ApplicationResources.properties and build.xml like belows.

menu.person=Person
menu.person.list=Person List
menu.person.report=Person Report
personreport.heading=Person Report
personreport.title=Person Report
button.generate=Generate


<target name="package-web" ...>
...
<copy todir="${build.dir}/web/classes">
<fileset dir="src/web">
...
<include name="**/*.jrxml"/>
<include name="**/*.jasper"/>
</fileset>
</copy>
...
<war destfile="${webapp.dist}/${webapp.war}" duplicate="preserve" ,,,>
...
<lib dir="${jasper.dir}" includes="*.jar"/>
</war>
</target>

Last, add menu for this feature.

insert into menu(id,name,url,parent) values(10,"menu.person",null,null);
insert into menu(id,name,url,parent) values(11,"menu.person.list","persons.html",10);
insert into menu(id,name,url,parent) values(12,"menu.person.report","personsReport.html",10);
insert into role_menu(role_id,menu_id) values(1,10);
insert into role_menu(role_id,menu_id) values(2,10);
insert into role_menu(role_id,menu_id) values(1,11);
insert into role_menu(role_id,menu_id) values(2,11);
insert into role_menu(role_id,menu_id) values(1,12);
insert into role_menu(role_id,menu_id) values(2,12);

Read More..

Tuesday, September 16, 2008

Performing CRUD on Object in AppFuse

We have created a model named Person before. Besides using this, we also can use Middlegen that have been integrated in AppGen AppFuse to generate POJOs from database tables. Now, we will add a CRUD (create, retrieve, update, delete) application for Person. Firstly, we have must to create a new DAO to perform CRUD on the object and then create a new manager for creating Business Facades that talk to the database tier (DAOs) and handle transaction management. The last, we make web presentation to do CRUD on that object. We will use AppFuse AppGen tool that I have talked before to do all of this.

Here, we start. Open terminal and enter extras/appgen directory. Run "ant" (It'll generate all the files that we need. However, we can just grab the ones we need) or "ant install" (to use the generic DAO and manager) or instead ant-install we can use better way, "ant install-detailed" (to general a DAO and Manager specifically for our model object). We will use the last one. After we run this command, We will be prompted to generate from a POJO or a Table. If we choose pojo, the .java file should already exist in our "model" package. Because person POJO has been in our model package, we can use this or we can use this command directly:
ant -Dobject.name=Person -Dappgen.type=pojo.
ant install -Dobject.name=Person -Dappgen.type=pojo.
ant install-detailed -Dobject.name=Person -Dappgen.type=pojo.

But if we choose table, Middlegen will be used to create a POJO from an existing database table. This will generate all the files that needed to test and perform CRUD on an object. We will use this second way, by using Middlegen. Delete first our Person.java in our model package.


Finally, we will be asked to enter an application module or sub-package name. This is an optional feature that will allow us to organize our classes into sub-packages. For example, for a POJO "model" package of "org.appfuse.foo.model", just enter foo when prompted.

Here are the list of new files or files that have been edited after we refresh our project in Eclipse:
(in src folder)
1. com.ndung.Constant (edit)
2. com.ndung.model.Person
3. com.ndung.dao.PersonDao
4. com.ndung.dao.PersonDaoHibernate
5. applicationContext-hibernate.xml (edit)
Note: In this file, we also must have to add "com/ndung/model/Person.hbm.xml" in list of value of mappingResources if there is no before.
6. com.ndung.service.PersonManager
7. com.ndung.service.PersonManagerImpl
8. applicationContext-service.xml (edit)
9. com.ndung.webapp.action.PersonForm
10. com.ndung.webapp.action.PersonList

(in test folder)
1. com.ndung.dao.PersonDaoTest
2. com.ndung.dao.PersonManagerTest
3. com.ndung.webapp.action.PersonFormTest
4. com.ndung.webapp.action.PersonListTest

(in web folder)
1. pages/web/personForm.html
2. pages/web/personForm.page
3. pages/web/persons.html
4. pages/web/persons.page
5. WEB-INF/tapestry.application (edit)

Last, we add a new menu in our database menu table. Also we have to add roles and that menu in role_menu table.
insert into menu(id,name,url,parent) values(10,"menu.person","persons.html",null);
insert into role_menu(role_id,menu_id) values(1,10);
insert into role_menu(role_id,menu_id) values(2,10);

Build our project through ANT (by run "ant deploy" command in our project package). Here is a screenshot of http://localhost:8080/AppfuseExample after our engine (Tomcat) started and log in as mraible.


UPDATE:
I'm trying syntax highlighter and modified by FaziBear for Blogspot. We will add search feature in this application. Edit files below by adding these codes.

PersonDao.java


public List findPersons(Person param);

PersonDaoHibernate.java

public List findPersons(Person param) {
DetachedCriteria criteria = DetachedCriteria.forClass(Person.class);
if (param.getId()!=null){
criteria.add(Restrictions.eq("id", param.getId()));
}
if (param.getFirstName()!=null){
criteria.add(Restrictions.eq("firstName", param.getFirstName()));
}
if (param.getLastName()!=null){
criteria.add(Restrictions.eq("lastName", param.getLastName()));
}
criteria.addOrder(Order.desc("id"));
return getHibernateTemplate().findByCriteria(criteria);
}

PersonManager.java

public List findPersons(Person param);

PersonManagerImpl.java

public List findPersons(Person param) {
return dao.findPersons(param);
}

PersonList.java

public abstract class PersonList extends BasePage implements PageBeginRenderListener{
public abstract PersonManager getPersonManager();
public abstract List getPersons();
public abstract void setPersons(List persons);
public abstract Person getParam();
public abstract void setParam(Person param);
public void pageBeginRender(PageEvent event) {
search(event.getRequestCycle());
}
public void search(IRequestCycle cycle) {
if (getParam() == null) setParam(new Person());
List personList = getPersonManager().findPersons(getParam());
PersonList nextPage = (PersonList) cycle.getPage();
nextPage.setPersons(personList);
cycle.activate(nextPage);
}
...

persons.html

<form jwcid="personForm@Form">
<table class="form">
<tr>
<td><label><span key="personList.namesearched"/></label></td>
<td>
<input jwcid="@TextField" value="ognl:param.firstName" />
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" class="button" jwcid="@Submit" value="message:button.search" id="search"
listener="ognl:listeners.search"/>
</td>
</tr>
</table>
...
</form>

ApplicationResources.properties

# -- person list page--
menu.person=Person List
personList.namesearched=Person First Name


Here is a screenshot:

Read More..

Monday, September 15, 2008

Hibernate XDoclet Class From Table Generator

Still remember Hibernate XDoclet? In this post, we will create an AppGen in AppFuse. Imagine if we have a hundred of columns in our table in database and we have must to write a model of that class, it's fields, it's getter and setter one by one. So, here we start. In this example, our table (person) just have 3 columns.


Create folder util in src folder. In Eclipse, create source folder that refer to src/util. Create a package in that source folder named com.ndung.gen. For the first step, we must prepare our model template for class.template, field.template, getter.template, and also setter.template. We also have to prepare our jdbc properties to connect to our database.

class.template


field.template


getter.template


setter.template


jdbc.properties

After that, we create two classes named TableInspector.java and ClassGenerator.java. TableInspector is used to inspect data type in each columns in table and map it in to Java class type. In another hand, ClassGenerator is used to write a Java model class file based on our template above.

TableInspector.java


ClassGenerator.java

Now, we create a main Java class named HibernateXDocletClassFromTableGenerator.java.


Run that class above and refresh our project. Look in folder src/dao in package com.ndung.model. There is a model class named Person.java. We must change XDoclet attribute for id from hibernate.property to become hibernate.id.

Person.java

Read More..

Thursday, September 11, 2008

Struts-Menu First Impression

We will talk about Struts-Menu tag library. It's demo can be seen in here and it's library can be downloaded in here. We will integrate it in AppFuse. Before we start, you must do this first. We will try build a simple menu from a database table. It's demo can be seen in here. In our that project (AppfuseExample), by default we can add menu and menu item in menu-config.xml. But how we can add dynamic driven menu from database? Okay, we will start here. There are 3 tables by default in our database (NDUNGDB). They are app_user, role, and user_role. We will add 2 tables more for our configuration menu. They are menu and role_menu. Table menu will store menus and table role_menu will store which menus can be accessed by which roles. Table menu will have 4 columns they are id, name, url, and parent. In other hand role_menu table will have 2 columns. They are role_id and menu_id.

DDL SQL file for table menu and role_menu

Edit table menu and role_menu. Add fields for them. Here is my menu and role_menu table based on default configuration of AppFuse. I'm using MySQL Query Browser.

menu table.


role_menu table.

We will add persistence model for menu by mapping table menu and edit role model in addition role_menu mapping table. In package com.ndung.model, add file Menu.java and edit Role.java by adding attribute menus.

Menu.java


Role.java (edit)

In applicationContext-hibernate.xml add mapping model for menu. Add this line in mapping resources.

applicationContext-hibernate.xml (edit)

We will add a filter in package com.ndung.webapp.filter named AuthenticationProcessingFilter by extend AcegiSecurity default filter. This filter is used to store all menus (menu names and it's url) from database and all roles that can access those menus in repository. Then, edit security.xml by adding this filter.

AuthenticationProcessingFilter.java


security.xml (edit)

We will use ListMenu. Add mapping menu in menu-config.xml. Add this line in displayers.

menu-config.xml (edit)

In folder web/pages add jsp file named menu.jsp (the other menu.jsp file is in folder web/common) and in folder web/decorators/ backup our default.jsp and edit it like this. Also you must have to need menuExpandable.css and menuExpandable.js. Place menuExpandable.css in folder web/styles and menuExpandable.js in folder web/scripts.

menu.jsp


default.jsp

Add these lines in ApplicationResources.properties.

ApplicationResources.properties (edit)

Here is some screenshot after we login as mraible. We must add a default css for a better view for sure.


Read More..

Monday, September 01, 2008

AppFuse First Impression (An Introduction to MVC Design Pattern)

Now, we have learned about Tapestry, Hibernate, and Spring. So, let me introduce a MVC architecture. An architecture such as MVC is a design pattern that describes a recurring problem and its solution that used 3 main components. MVC, They are Model (business logic goes here), View (presentation logic goes here), and Controller (application logic goes here). One of MVC framework is Brick/ AppFuse Framework. By default, AppFuse use Hibernate framework as Model (object persisting framework), Tapestry framework as View (web presentation), and Spring framework as Controller. So, here it is...

All that we need are AppFuse framework, Apache Ant, MySQL as database, Apache Tomcat as application server, Eclipse as IDE and JUnit as testing tools. Extract Apache Ant in a specific location, as example in /home/ndung/Java/ant/apache-ant-1.7.0. Configure our environment variables.
~$ sudo gedit /etc/bash.bashrc

Add these lines in that file:
export ANT_HOME=/home/ndung/Java/ant/apache-ant-1.7.0
export PATH=$PATH:$ANT_HOME/bin


To configure Apache Tomcat and Eclipse read this. Extract Junit in specific folder and copy junit-x.x.jar into Apache Ant libraries (apache-ant-1.7.0/lib). Extract AppFuse in our eclipse workspace as example /home/ndung/workspace/appfuse. Open properties.xml and edit tomcat properties (line 37-40) based on our tomcat configuration mainly in user name and password. Also edit our database properties (line 43-59) based on our MySQL or our other database configuration mainly in user name and password. Open terminal and run:
~$ ant new


After that enter our project workspace folder. Run setup and test-all.
~$ cd /home/ndung/workspace/AppfuseExample
~$ ant setup test-all


Finish! Now start our tomcat. We will start it manually by using terminal. Enter Tomcat installation bin folder.
~$ cd /opt/apache-tomcat-5.5.26/bin

Add the execute permission to all of the .sh files in the $CATALINA_HOME/bin directory.
~$ sudo chmod u+x startup.sh
~$ sudo chmod u+x catalina.sh
~$ sudo chmod u+x setclasspath.sh
~$ sudo chmod u+x shutdown.sh


Start our engine (tomcat) by using this command:
~$ ./startup.sh

Now, point our browser into http://localhost:8080/AppfuseExample. Use our tomcat user name and password to log in as user, as example in my tomcat configuration, user name: tomcat; password: tomcat. To log in as administrator use mraible as user name and tomcat as password.
Here is some screen shot:


Don't forget to shut down our engine:
~$ ./shutdown.sh

Now, how to edit this project by using IDE. Open our Eclipse. Click File -> Import. Select Existing Projects into workspace under General item as an import source. Click next. Select our eclipse workspace (example:/home/ndung/workspace) as root directory. Check AppfuseExample in Projects then click Finish. Now, we can easily to add whatever in our project. In the next posts we will add other feature in this our project.

Read More..