Total Pageviews

2011/07/21

Wrapping JPQL Query Results with Instances of Custom Result Classes

JPA supports wrapping JPQL query results with instances of custom result classes. This is mainly useful for queries with multiple SELECT expressions, where custom result objects can provide an object oriented alternative to representing results as Object[] or Object elements.

The fully qualified name of the result class is specified in a NEW expression, as follows:
1
2
3
4
5
6
7
8
   StringBuilder sql = new StringBuilder();
   sql.append(" select new gov.fdc.nig.bean.NIG451Bean(t1.manageCd, t1.baTaxMk, ");
   sql.append(" t1.vioHostNm, t1.vioHostIdnBan, t1.vioYr, t1.addrHsnNm, ");
   sql.append(" t1.addrTownNm, t1.addrVillNm, t1.addrLin, t1.addrRoadNo, ");
   sql.append(" t1.respNm, t1.respIdn, t1.prstAdtrCd, t1.nigt007.adtrNm, ");
   sql.append(" t1.authMk, t1.adtSgstMk) from Nigt001 t1");
   sql.append(" where t1.id.dlvUnit = ?1 and t1.id.taxCd = ?2 and t1.id.dlvYr = ?3 ");
   sql.append(" and t1.id.flgTp = ?4 and t1.id.serialNo = ?5 ");


The result class must have a compatible constructor that matches the SELECT result expressions, as follows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
   public class NIG451Bean extends BeanBase {
       // 管理代號
       private String manageCd;
       // 本稅註記
       private String baTaxMk;
       // 違章主體名稱
       private String vioHostNm;
       // 違章主體統一編號
       private String vioHostIdnBan;
       // 違章年度
       private String vioYr;
       // 地址縣市名稱
       private String addrHsnNm;
       // 地址鄉鎮市區名稱
       private String addrTownNm;
       // 地址村里名稱
       private String addrVillNm;
       // 地址鄰
       private String addrLin;
       // 地址街道門牌
       private String addrRoadNo;
       // 負責人姓名
       private String respNm;
       // 負責人身分證統一編號
       private String respIdn;
       // 目前審理人員代號
       private String prstAdtrCd;
       // 審理人員姓名
       private String adtrNm;
       // 授權註記
       private String authMk;
       // 審查意見補頁註記
       private String adtSgstMk;
       
       public NIG451Bean(String manageCd, String baTaxMk, String vioHostNm,
                         String vioHostIdnBan, String vioYr, String addrHsnNm,
                         String addrTownNm, String addrVillNm, String addrLin,
                         String addrRoadNo, String respNm, String respIdn,
                         String prstAdtrCd, String adtrNm, String authMk, String adtSgstMk) {
           super();
           this.manageCd = manageCd;
           this.baTaxMk = baTaxMk;
           this.vioHostNm = vioHostNm;
           this.vioHostIdnBan = vioHostIdnBan;
           this.vioYr = vioYr;
           this.addrHsnNm = addrHsnNm;
           this.addrTownNm = addrTownNm;
           this.addrVillNm = addrVillNm;
           this.addrLin = addrLin;
           this.addrRoadNo = addrRoadNo;
           this.respNm = respNm;
           this.respIdn = respIdn;
           this.prstAdtrCd = prstAdtrCd;
           this.adtrNm = adtrNm;
           this.authMk = authMk;
           this.adtSgstMk = adtSgstMk;
       }
       //setter and getter method
   }


The following code demonstrates running this query:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
   public NIG451Bean autoCompleteForNIG451(Nigt001PK nigt001pk) {
       StringBuilder sql = new StringBuilder();
       sql.append(" select new gov.fdc.nig.bean.NIG451Bean(t1.manageCd, t1.baTaxMk, ");
       sql.append(" t1.vioHostNm, t1.vioHostIdnBan, t1.vioYr, t1.addrHsnNm, ");
       sql.append(" t1.addrTownNm, t1.addrVillNm, t1.addrLin, t1.addrRoadNo, ");
       sql.append(" t1.respNm, t1.respIdn, t1.prstAdtrCd, t1.nigt007.adtrNm, ");
       sql.append(" t1.authMk, t1.adtSgstMk) from Nigt001 t1");
       sql.append(" where t1.id.dlvUnit = ?1 and t1.id.taxCd = ?2 and t1.id.dlvYr = ?3 ");
       sql.append(" and t1.id.flgTp = ?4 and t1.id.serialNo = ?5 ");
       
       EntityManager entityManager = getEntityManager_A05_TAX();
       Query query = entityManager.createQuery(sql.toString());
       query.setParameter(1, nigt001pk.getDlvUnit());
       query.setParameter(2, nigt001pk.getTaxCd());
       query.setParameter(3, nigt001pk.getDlvYr());
       query.setParameter(4, nigt001pk.getFlgTp());
       query.setParameter(5, nigt001pk.getSerialNo());
       
       return (NIG451Bean)query.getSingleResult();
   }


Any class with a compatible constructor can be used as a result class. It could be a JPA managed class (e.g. an entity class) but it could also be a lightweight 'transfer' class that is only used for collecting and processing query results.

If an entity class is used as a result class, the result entity objects are created in the NEW state, which means that they are not managed. Such entity objects are missing the JPA functionality of managed entity objects (e.g. transparent navigation and transparent update detection), but they are more lightweight, they are built faster and they consume less memory.

2011/07/19

Avoid Producing Unnecessary Empty Columns as Export CSV File

Problem
it's our report resign in iReport

export to csv file

it has many unnecessary empty columns in csv file


Root Caluse / Solution
Because the width of text field should be aligned from top to down

So some text field will be split to more than one.

Or some text fields will be merged.

Check the result

Fail to Concat String with $V{PAGE_NUMBER}

Problem
I would like to concat String with $V{PAGE_NUMBER}

As I want to do preview in iReport, it show this error message in iReport console

Root Cause
Because of the Expression Class of $V{PAGE_NUMBER} is java.lang.Integer, so it will throw this error message.

Solution
Change the Expression Class of $V{PAGE_NUMBER} from java.lang.Integer java.lang.String, then the problem will be resolved.


2011/07/13

Apache Tiles Quick Start

Tiles concepts
Tiles is an implementation of the Composite View pattern. Tiles adds to this pattern its own concepts to make the pattern concrete. The implementation of Tiles around the Composite View pattern consists of the Template, Attribute and Definition concepts. The View Helper pattern is implemented by the View Preparer concept.

Template
In Tiles, a template is the layout part of a page. You can see as a page structure with some gaps, called attributes, to be filled.

For instance, consider the page structure.


You can replicate this structure by creating a JSP page, as you can see below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<html>
   <head>
      <title>
         <tiles:getAsString name="title"/>
      </title>
      <meta name="pagebase" content="<tiles:insertAttribute name="pagebase"/>
      "/>
      <meta name="funccode" content="<tiles:insertAttribute name="funccode"/>
      "/>
      <!-- ... -->
   </head>
   <body>
      <a name="top" ></a>
      <tiles:insertAttribute name="header" />
      <tiles:insertAttribute name="menu" />
      <div id="contentBase">
         <form>
            <!-- ... -->
         </form>
         <tiles:insertAttribute name="content" />
      </div>
   </body>
</html>


tiles-config.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- 裁處書 預設樣板 -->
<definition name="punishmentForm" template="/WEB-INF/jsp/common/punishmentFormMain.jsp">
   <put-attribute name="pagebase" value="nig" type="string" />
   <put-attribute name="funccode" value="Template" type="string" />
   <put-attribute name="title" value="Template" type="string" />
   <put-attribute name="header" value="/WEB-INF/jsp/common/header.jsp" />
   <put-attribute name="menu" value="/WEB-INF/jsp/common/menu.jsp" />
   <put-attribute name="content" value="/WEB-INF/jsp/common/content.jsp" />
</definition>
<!-- extends "punishmentForm" template -->
<definition name="/front/NIG451W" extends="punishmentForm">
   <!-- override funccode -->
   <put-attribute name="funccode" value="NIG451W" type="string" />
   <!-- override title -->
   <put-attribute name="title" value="營所稅基本稅額" type="string" />
   <!-- override content --> 
   <put-attribute name="content" value="/WEB-INF/jsp/front/punishment/NIG451W.jsp" />
</definition>


Check the result

A Short Intorduction to JasperReports

2011/07/12

Browser Compatibility Check for Internet Explorer Versions from 5.5 to 10

IETester is a free (both for personal and professional usage) WebBrowser that allows you to have the rendering and javascript engines of IE10 preview, IE9, IE8, IE7 IE 6 and IE5.5 on Windows 7, Vista and XP, as well as the installed IE in the same process.

http://www.my-debugbar.com/wiki/IETester/HomePage

2011/07/07

meioMask – a jQuery mask plugin


Motivation
I would like to find a jQuery plugin for handling input mask for inputting violation number(違章編號).

Solution
Go to download meioMask, http://www.meiocodigo.com/projects/meiomask/, to use.
It's very easy to use, and is compatible with :
  • Firefox2, Firefox3, Firefox3.5 (Win, Mac, Linux);
  • IE6, IE7, IE8 (Win);
  • Chrome (Win, Mac, Linux);
  • Safari3.2, Safari4 (Win, Mac, iPhone, yes, it supports iPhone!);
  • Opera (Win, Mac, Linux).
How to do it
1. download and copy jquery.meio.mask.js to src/webapp/js

2. import jquery.meio.mask.js into your jsp page


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="cui" uri="http://fdc.gov/jsp/taglib/commonui" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<script type="text/javascript" src="<c:url value="/res/js/nig/nig.nig451w.js"/>"></script>
<!-- import jquery.meio.mask.js -->
<script type="text/javascript" src="<c:url value="/res/js/jquery.meio.mask.js"/>"></script>
3. insert input text field for violation number


<cui:portlet titlekey="nig451.title" align="center">
<table class="table-redmond-left" width="98%">
<tr>
<th style="width: 150px;"><cui:msg key="nig451.vioNum" />
</th>
<td><input id="vioNum" name="vioNum" type="text" alt="vioNum"/></td>
</tr>
.....
</table>
</cui:portlet>
4. edit js file, and define input mask for violation number (違章編號)


//文件載入完成後執行
$(document).ready(function() {
...
//define input mask for violation number
$("#vioNum").setMask('999-99-999-9-99999');
});
5. check the result

2011/07/04

25 Handy HTML Cheat Sheets

This article has the ultimate collection of HTML Cheat sheets that will handy to you always. A help sheet or more often known as cheat sheet is a great reference tool that cut the crap and gives only the required and important information.

2011/06/30

COBOL Editor - CobTree


web site: http://ynohoo.braindog.org/CobTree/
It's a pretty good Cobol editor.

Features
. multi-window tabbed interface
. navigable tree view of division, sections, paragraphs etc.
. calculates the offsets of selected record definitions
. handle flat file editing of selected record definitions
. standalone installation (can run from a floppy disc)

Screenshot

2011/06/28

Utilize Maven 2 JasperReports Plugin to Compile jrxml Automatically

Motivation
We would like to compile jrxml automatically as we build our web application.


Mechanics
We use Maven 2 JasperReports Plugin to compiles JasperReports xml design files to Java source and .jasper serialized files.

How to do it
1. edit nig-web/pom.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
   <dependencies>
      <dependency>
         <groupId>net.sf.jasperreports</groupId>
         <artifactId>jasperreports</artifactId>
         <version>4.0.1</version>
      </dependency>
   </dependencies>
   
   
   <build>
      ...
      <plugins>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jasperreports-maven-plugin</artifactId>
            <version>1.0-beta-2</version>
            <executions>
               <execution>
                  <goals>
                     <goal>compile-reports</goal>
                  </goals>
                  <configuration>
                     <!-- define where is your jrxml file -->
                     <sourceDirectory>src\\main\\java\\gov\\fdc\\nig\\report\\templates</sourceDirectory>
                     <sourceFileExt>.jrxml</sourceFileExt>
                     <compiler>net.sf.jasperreports.compilers.JRGroovyCompiler</compiler>
                     <!-- define where is the jasper file will be generated -->
                     <outputDirectory>src\\main\\resources\\report\\jasper</outputDirectory>
                  </configuration>
               </execution>
            </executions>
            <dependencies>
               <!--note this must be repeated here to pick up correct xml validation -->
               <dependency>
                  <groupId>net.sf.jasperreports</groupId>
                  <artifactId>jasperreports</artifactId>
                  <version>4.0.1</version>
               </dependency>
               <dependency>
                  <groupId>org.codehaus.groovy</groupId>
                  <artifactId>groovy-all</artifactId>
                  <version>1.7.5</version>
               </dependency>
            </dependencies>
         </plugin>
      </plugins>
   </build>

2. mvn install
3. check result

Benefits
1. Automate jrxml compilation process
2. We won't forget to compile jrxml file and lead to our program read old jasper file

2011/06/22

Refactoring: Decompose Conditional

Motivation

if-else條件判斷式,一直是程式看起來很複雜的地方。當功能更複雜,條件式就會更多,判斷式就會顯得又臭又長。無論是程式的可讀性,或是可維護性,都大大的降低。另外,在測試的時候(如code coverage test),也會增加測試時間。


Mechanics
  • 將condition 的部份extract到獨立的method.
  • 將if-else的部份,extract到獨立的method.

Example

Before Refactor


 public List doPrint(String evdcCdStart, String evdcCdEnd) throws FDCDataAccessException {  
      List params = new ArrayList();  
      StringBuffer sql = new StringBuffer();  
      sql.append(" SELECT EVADE_CD, EVADE_NM FROM Nigt042 ");  
      String where = " WHERE ", and = " ";  
      if(StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd)){  
           sql.append(where).append(and).append(" EVADE_CD between ?1 and ?2 ");  
           params.add(evdcCdStart);  
           params.add(evdcCdEnd);  
           where = " ";  
           and = " AND ";  
      }else if(StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isEmpty(evdcCdEnd)){  
           sql.append(where).append(and).append(" EVADE_CD >= ?1 ");  
           params.add(evdcCdStart);  
           where = " ";  
           and = " AND ";  
      }else if(StringUtils.isEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd)){  
           sql.append(where).append(and).append(" EVADE_CD <= ?1 ");  
           params.add(evdcCdEnd);  
           where = " ";  
           and = " AND ";  
      }  
      sql.append(" ORDER BY EVADE_CD ");  
      return Nigt042Dao.queryNativeSQL(new Nigt042(), sql.toString(), params.toArray());  
 }  

從上述code snippet可以看出有三段判斷式

判斷式1: StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd)

判斷式2: StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isEmpty(evdcCdEnd)

判斷式3: StringUtils.isEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd)

為了讓if-else邏輯判斷增加可讀性,分別將此三的判斷式抽離成三個private method,並賦予有意義的名字,此三個method分別為:

before extract after extract
StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd) hasStartAndEndCode
StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isEmpty(evdcCdEnd) onlyHasStartCode
StringUtils.isEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd) onlyHasEndCode



After Refactor


 public List doPrint(String evdcCdStart, String evdcCdEnd) throws FDCDataAccessException {  
      List params = new ArrayList();  
      StringBuffer sql = new StringBuffer();  
      sql.append(" SELECT EVDC_CD, EVDC_NM FROM NIGT042 ");  
      String where = " WHERE ", and = " ";  
      if(hasStartAndEndCode(evdcCdStart, evdcCdEnd)){  
           sql.append(where).append(and).append(" EVDC_CD between ?1 and ?2 ");  
           params.add(evdcCdStart);  
           params.add(evdcCdEnd);  
           where = " ";  
           and = " AND ";  
      }else if(onlyHasStartCode(evdcCdStart, evdcCdEnd)){  
           sql.append(where).append(and).append(" EVDC_CD >= ?1 ");  
           params.add(evdcCdStart);  
           where = " ";  
           and = " AND ";  
      }else if(onlyHasEndCode(evdcCdStart, evdcCdEnd)){  
           sql.append(where).append(and).append(" EVDC_CD <= ?1 ");  
           params.add(evdcCdEnd);  
           where = " ";  
           and = " AND ";  
      }  
      sql.append(" ORDER BY EVDC_CD ");  
      return nigt042Dao.queryNativeSQL(new Nigt042(), sql.toString(), params.toArray());  
 }  
 private boolean onlyHasEndCode(String evdcCdStart, String evdcCdEnd) {  
      return StringUtils.isEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd);  
 }  
 private boolean onlyHasStartCode(String evdcCdStart, String evdcCdEnd) {  
      return StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isEmpty(evdcCdEnd);  
 }  
 private boolean hasStartAndEndCode(String evdcCdStart, String evdcCdEnd) {  
      return StringUtils.isNotEmpty(evdcCdStart) && StringUtils.isNotEmpty(evdcCdEnd);  
 }  

2011/06/20

[Java] VarArgs 的應用


Varargs
varargs是JDK 1.5開始才提供的新功能,其定義如下:The varargs, or variable arguments, feature allows a developer to declare that a method can take a variable number of parameters for a given argument. The vararg must be the last argument in the formal argument list.
白話一點來說,varargs就是用來處理輸入參數數量無法預知的情形,以下是一個簡單的範例。
Scenario
為了因應客戶需求,將前端的刪除功能從單筆變成多筆,此時要從前端傳多筆資料的primary key給controller來進行資料刪除。

Class Diagram
由於多筆刪除是統一的作法,且每一個Controller都會繼承Abstract Controller,於是就把getPKList定義於AbstractController中,這樣每個controller都可以直接取用,如下圖:



Sequence Diagram
從Sequence diagram可以看出,當使用者選擇某幾筆資料,然後按下刪除按鈕,Controller會呼叫getPKList來取得從前端回傳回來的primary key,當接收到以後,再呼叫doDelete來刪除資料。


Sample Code
從以下的code snippet可以看出,getPKList有帶兩個參數,第二個參數就是varargs,由於每張table擁有的primary key的數量不一,有可能一個或一個以上,如NIGT036只有一個primary key, NIGT001則有四個
NIGT036

NIGT001



透過varargs,就可以一個method 滿足所有controller 的需求
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
   /** 
   * Get primary key list 
   * 
   * @param delArray delete array 
   * @param keys primary keys 
   * @return List of String 
   */ 
   public List getPKList(final String delArray, final String...keys) { 
       List arr = JSONArray.fromObject(delArray); 
       List pkList = new ArrayList(); 
       for (Map map : arr) { 
           //若有多個keys,就add多個primary key到pkLisk;若只有一個primary key,則只做一次 
           for(String str : keys){ 
               pkList.add(map.get(str)); 
           } 
       } 
       return pkList; 
   } 

2011/06/15

net.sf.jasperreports.engine.JRException: Provider com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl not found

Problem
As I use iReport 4.0.2 to open jrxml file, but it show this error message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
    at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:134)
    at org.netbeans.lib.uihandler.LogRecords.scan(LogRecords.java:127)
    at org.netbeans.modules.uihandler.Installer.getLogs(Installer.java:681)
    at org.netbeans.modules.uihandler.Installer$Submit.(Installer.java:1317)
    at org.netbeans.modules.uihandler.Installer$SubmitInteractive.(Installer.java:1771)
    at org.netbeans.modules.uihandler.Installer.doDisplaySummary(Installer.java:983)
    at org.netbeans.modules.uihandler.Installer.displaySummary(Installer.java:912)
    at org.netbeans.modules.uihandler.Installer.displaySummary(Installer.java:920)
    at org.netbeans.modules.uihandler.UIHandler.run(UIHandler.java:140)
    at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:573)
    at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:1005)


Solution
Change your JAVA_HOME to standard JDK instead of IBM JDK.
WHAT THE HELL!

2011/06/14

ReportWrapper Class for JasperReport Implementation

在JasperReport開發,主要會分成五個部分:
  1. Get data source: 傳入List of value object,並建立JRBeanCollectionDataSource
  2. Read Jasper File: 讀取.jasper file
  3. Generate JasperPrint: 讀取report input stream並建立JasperPrint物件
  4. Set response content type and header: 根據user要輸出的格式,指定content type and header
  5. Export Report: 根據user要輸出的格式,用相對應的API進行報表輸出


其實每份報表都不外乎這五大步驟,這邊我建立一個ReportWrapper class,如下


 package gov.fdc.nig.report;  
 import gov.fdc.nig.enumeration.ExportFormatEnum;  
 import java.io.IOException;  
 import java.io.InputStream;  
 import java.io.OutputStream;  
 import java.util.List;  
 import java.util.Map;  
 import javax.servlet.http.HttpServletResponse;  
 import net.sf.jasperreports.engine.JRDataSource;  
 import net.sf.jasperreports.engine.JRException;  
 import net.sf.jasperreports.engine.JRExporter;  
 import net.sf.jasperreports.engine.JRExporterParameter;  
 import net.sf.jasperreports.engine.JasperFillManager;  
 import net.sf.jasperreports.engine.JasperPrint;  
 import net.sf.jasperreports.engine.JasperReport;  
 import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;  
 import net.sf.jasperreports.engine.export.JRCsvExporter;  
 import net.sf.jasperreports.engine.export.JRPdfExporter;  
 import net.sf.jasperreports.engine.util.JRLoader;  
 /**  
 * Report Wrapper Class  
 *  
 * @author albert  
 *  
 */  
 public class ReportWrapper {  
      private transient JRDataSource dataSource;  
      private final static String JASPER_CLASSPATH = "..\\..\\..\\..\\report\\templates\\";  
      private transient InputStream reportStream;  
      private transient JasperPrint print;  
      /**  
      * Set report data source  
      *  
      * @param data  
      * List of value object  
      */  
      private void setDataSource(final List data) {  
           dataSource = new JRBeanCollectionDataSource(data);  
      }  
      /**  
      * Get report input stream  
      */  
      private void getReportInputStream(final String fileName) {  
           reportStream = getClass().getResourceAsStream(  
           JASPER_CLASSPATH.concat(fileName.concat(".jasper")));  
      }  
      /**  
      * generate JasperPrint  
      *  
      * @param params parameters  
      * @throws JRException  
      */  
      public void generateJasperPrint(final Map params)  
      throws JRException {  
           final JasperReport report = (JasperReport) JRLoader  
           .loadObject(reportStream);  
           print = JasperFillManager.fillReport(report, params, dataSource);  
      }  
      /**  
      * Set content type and header  
      *  
      * @param fileName is File name  
      * @param response is HttpServletResponse  
      * @param exportEnum is ExportFormatEnum  
      */  
      public void setResponse(final String fileName,  
           final HttpServletResponse response,  
           final ExportFormatEnum exportEnum) {  
           response.setCharacterEncoding("UTF-8");  
           if ("pdf".equals(exportEnum.getValue())) {  
                response.setContentType("application/pdf");  
                response.setHeader("Content-Disposition", "attachment;filename=\""  
                .concat(fileName).concat(".pdf\""));  
           } else if ("csv".equals(exportEnum.getValue())) {  
                response.setContentType("application/octet-stream;charset=UTF-8");  
                response.setHeader("Content-Disposition", "attachment;filename="  
                .concat(fileName).concat(".csv"));  
           }  
      }  
      /**  
      * Export report  
      *  
      * @param exportEnum is ExportFormatEnum  
      * @param outputStream is OutputStream  
      * @throws JRException  
      * @throws IOException  
      */  
      public void exportReport(final ExportFormatEnum exportEnum,  
      final OutputStream outputStream) throws JRException, IOException {  
           if ("pdf".equals(exportEnum.getValue())) {  
                final JRExporter pdfExporter = new JRPdfExporter();  
                pdfExporter.setParameter(JRExporterParameter.JASPER_PRINT, print);  
                pdfExporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);  
                pdfExporter.exportReport();  
           } else if ("csv".equals(exportEnum.getValue())) {  
                // byte-order marker (BOM)  
                final byte bomByteArr[] = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };  
                // insert BOM byte array into outputStream  
                outputStream.write(bomByteArr);  
                final JRExporter csvExporter = new JRCsvExporter();  
                csvExporter.setParameter(JRExporterParameter.JASPER_PRINT, print);  
                csvExporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);  
                csvExporter.exportReport();  
           }  
           outputStream.flush();  
      }  
      /**  
      * do export report based on the parameters  
      *  
      * @param data is List of VO  
      * @param fileName is file name  
      * @param params is parameters  
      * @param response is HttpServletResponse  
      * @param exportEnum is ExportFormatEnum  
      * @param outputStream is OutputStream  
      * @throws JRException  
      * @throws IOException  
      */  
      public void execute(final List data, final String fileName,  
      final Map params,  
      final HttpServletResponse response,  
      final ExportFormatEnum exportEnum, final OutputStream outputStream)  
      throws JRException, IOException {  
           setDataSource(data);  
           getReportInputStream(fileName);  
           generateJasperPrint(params);  
           setResponse(fileName, response, exportEnum);  
           exportReport(exportEnum, outputStream);  
           if (reportStream != null) {  
                reportStream.close();  
           }  
      }  
 }  

未來我們在Contoller的processF7Key,只要建立ReportWrapper class,並將相對應的參數給execute method即可,請參考底下的程式片段
 /**  
 * Will be executed as user click Print button  
 *  
 * @param formBean  
 * NIG135DataBean  
 * @param session  
 * NIG135DataBean  
 * @return JSONObject  
 * @throws IllegalAccessException  
 * @throws InvocationTargetException  
 * @throws IOException  
 */  
 private @ResponseBody  
 void processF7Key(final NIG135DataBean formBean,  
 final HttpServletResponse response, final HttpSession session)  
 throws IllegalAccessException, InvocationTargetException,  
 IOException {  
      String jasperXMLFileName = "NIG135P1";  
      OutputStream outputStream = response.getOutputStream();  
      try {  
           if ("135".equals(formBean.getReportOption())) {  
                Map params = new HashMap();  
                params.put("rejectPeriod", formBean.doPrintForNIG135P1(covertToNIG135P1ReportBean(formBean));  
                if ("1".equals(formBean.getReportType())) {  
                     // export csv file  
                     if (ExportFormatEnum.CSV.getValue().equals(  
                     formBean.getExportFormat())) {  
                          new ReportWrapper().execute(data,  
                          jasperXMLFileName, params, response,  
                          ExportFormatEnum.CSV, outputStream);  
                     }  
                          // export pdf file  
                          else if (ExportFormatEnum.PDF.getValue().equals(  
                          formBean.getExportFormat())) {  
                          new ReportWrapper().execute(data,  
                          jasperXMLFileName, params, response,  
                          ExportFormatEnum.PDF, outputStream);  
                     }  
                }  
           }  
      }catch(Exception e){  
      //............  
      }  
 }  

nig.nig135w.js
 function nig135wFormF7(){  
      $("#fn").val("7");  
      $("#nig135wForm").attr("target", "_self");  
      $("#nig135wForm").attr("action", "/nig/front/NIG135W");  
      $("#nig135wForm").submit();  
 }  

Benefits
所有的報表程式,都透過這一支ReportWrapper class來處理。
我們只要專注於前端的business logic以及templates,後端如何去產生pdf, csv等格式,就交給ReportWrapper 處理就好。

How to Access .jasper file from EAR File


Problem
根據標準組所提供的讀取.jasper的方式,是將.jasper的目錄寫死,此寫法未來會有很大的問題。如:
1. 若有多位開發人員同時進行開發,大家都存放於不同的地方,會導致某人的環境可以執行,其他人卻無法執行的狀況。
2. 若開發機、測試機、正式機所放置的目錄不一樣,這樣deploy到不同的機器,又忘記手動去更改目錄,會導致在測試機可以執行,正式機卻無法執行的窘境。

為了改善上述的潛在問題,以下是解決方式:

File Structure
My ReportWrapper class is under gov.fdc.nig.report

.japser files are under resources/report


After we build our application to ear file, the file structure looks like this:


Source Code

1
2
3
4
5
    private final static String JASPER_CLASSPATH = "..\\..\\..\\..\\report\\templates\\";
    InputStream reportStream
     = getClass().getResourceAsStream(JASPER_CLASSPATH.concat(fileName.concat(".jasper")));
    JasperReport report = (JasperReport) JRLoader.loadObject(reportStream);
    


Benefit
透過上述的寫法,採相對路徑的方式來讀取.jasper file,避免上述的問題

2011/06/10

How to Write CSV Files as UTF-8 for Excel

Problem
As JasperReport export CSV file with UTF-8 encoding, Microsoft Excel wasn't displaying a CSV (comma separated values) file correctly.
The screenshot is as bellows:

But it can display correctly for notepad:


Root Cause
In the absence of any charset identification, Excel must guess about a file's content encoding. Therefore, Excel uses that default to read and display CSV files.


Solution
Use the byte-order marker (BOM) to identify the CSV file as a Unicode file. So I write an byte array into OutputStream, then Excel should uses UTF-8 read and display CSV files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  //byte-order marker (BOM)
  byte b[] = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
  //insert BOM byte array into outputStream
  outputStream.write(b);
  JRExporter csvExporter = new JRCsvExporter();
  csvExporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
  csvExporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
  csvExporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, reportName.concat(".csv"));
  csvExporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8");
  csvExporter.exportReport();



See....it's correct now!