Total Pageviews

2011/07/30

Java Tools: Source Code Optimization and Analysis


Below is a list of some tools that can help you examine your Java source code for potential problems:

2011/07/29

[Java] 字串半型轉全型

Problem
使用者希望所輸入的地址,在存入資料庫的時候,能夠將數字的部份轉成全型,這樣列印起來比較美觀。

How-To
 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
    package gov.fdc.nig.web.util;  
    import org.apache.commons.lang.StringUtils;  
    /**  
    * 用於處理NIG系統內部字串  
    *  
    * @author albert  
    *  
    */  
    public class NIGStringUtils {  
          private static NIGStringUtils instance = new NIGStringUtils();  
          public static NIGStringUtils getInstance(){  
               return instance;  
          }  
          /**  
          * 將地址半型的數字轉成全型數字  
          *  
          * @param str String  
          * @return  
          */  
          public String covertToChineseFullChar(String str) {  
               String result = "";  
               if (StringUtils.isNotEmpty(str)) {  
                    char[] chars = str.toCharArray();  
                    for (int i = 0; i < chars.length; i++) { //bypass Chinese character   
                         if (chars[i] > '\200') {  
                              continue;  
                         }  
                         // 半型空白轉成全型空白  
                         if (chars[i] == 32) {  
                              chars[i] = (char) 12288;  
                              continue;  
                         }  
                         // 有定義的字、數字及符號  
                         if (Character.isLetterOrDigit(chars[i])) {  
                              chars[i] = (char) (chars[i] + 65248);  
                              continue;  
                         }  
                         // 其它不合要求的,全部轉成全型空白。  
                         chars[i] = (char) 12288;  
                    }  
                    result = String.valueOf(chars);  
               }  
               return result;  
          }  
     }  



2011/07/28

Handling JPA lifecycle event using listeners and callbacks

Problem
As we manipulate table, we need to insert values into user id(使用者代號), timestamp(西元年) and update date(民國年) as we do insert or update.
It's prone to forget to insert values to these columns.

Solution
Define setting value method and utilize JPA lifecyle to invoked automatically by JPA when these events occur.

JPA Lifecycle Events
Callback methods are user defined methods that are attached to entity lifecycle events and are invoked automatically by JPA when these events occur.

Internal callback methods should always return void and take no arguments. They can have any name and any access level (public, protected, package and private) but should not be static.

The annotation specifies when the callback method is invoked:
  • @PrePersist - before a new entity is persisted (added to the EntityManager).
  • @PostPersist - after storing a new entity in the database (during commit or flush).
  • @PostLoad - after an entity has been retrieved from the database.
  • @PreUpdate - when an entity is identified as modified by the EntityManager.
  • @PostUpdate - after updating an entity in the database (during commit or flush).
  • @PreRemove - when an entity is marked for removal in the EntityManager.
  • @PostRemove - after deleting an entity from the database (during commit or flush).

Implementation Restrictions
To avoid conflicts with the original database operation that fires the entity lifecycle event (which is still in progress) callback methods should not call EntityMan­ager or Query methods and should not access any other entity objects.

If a callback method throws an exception within an active transaction, the transaction is marked for rollback and no more callback methods are invoked for that operation.

How to do it
1. Define EntityListener class
 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
package gov.fdc.nig.domain.listener;

import gov.fdc.common.util.DateUtil;
import gov.fdc.nig.util.NigUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Calendar;

import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;

public class EntityListener {

 /**
  * Set date time and user id
  *
  * This method will be invoke before a new entity is persisted or
  * when an entity is identified as modified by the EntityManager
  *
  * @param obj Object
  * @throws IllegalArgumentException
  * @throws IllegalAccessException
  * @throws InvocationTargetException
  */
 @SuppressWarnings("unused")
 @PrePersist
 @PreUpdate
 private void setTimeAndUser(Object obj) throws IllegalArgumentException,
  IllegalAccessException, InvocationTargetException {
   Method methods[] = obj.getClass().getDeclaredMethods();
   String methodName = "";
   for (Method method: methods) {
    methodName = method.getName();
    if (methodName.matches("setUpdateDate")) {
     method.invoke(obj, DateUtil.getFormattedDate(NigUtils.getCurrentTimestamp(), "twyMMdd"));
    } else if (methodName.matches("setTimeStamp")) {
     method.invoke(obj, new Timestamp(Calendar.getInstance().getTime().getTime()));
    } else if (methodName.matches("setUserId")) {
     method.invoke(obj, NigUtils.getUser().getUserID());
    } else if (methodName.matches("setUpdateUserCd")) {
     method.invoke(obj, NigUtils.getUser().getUserID());
    }
   }
  }
}


2. add @EntityListeners({EntityListener.class}) annotation to each entity class

1
2
3
4
5
6
7
8
9
package gov.fdc.nig.domain;
@Entity
@EntityListeners({
 EntityListener.class
})
@Table(name = "NIGT036", schema = "AP_TAX")
public class Nigt036 extends DomainBase {
 //............
}

Therefore, we don't need to set these columns in controller class. And we won't forget to set values to theses columns again.

Using jQuery and Ajax to load sub-sections of a webpage

Ajax powered tab sets

Tabs can; broadly-speaking, be loaded in three different ways:
  • Immediately loaded – i.e. when the webpage has loaded, it is the default selected tab.
  • Later (deferred loading) – i.e. after the user clicks a tab, go off to the server and retrieve the contents of a tab, or
  • Lazy loading – e.g. load in the background before the user clicks a tab. This enhances the user experience as the contents of a tab are immediately shown, but on the flip side means requires more traffic and the user may never click the tab anyway, effectively wasting bandwidth.
In this case, we choose the third approach to implement (Lazy loading)

Scenario
We have four tabs in our page, the screen shots are as bellowing:




Solution
Here’s a javascript (using jQuery) implementation of the above which supports "lazy loading"

nig.nig451w.js

$(document).ready(function() {
$( "#tabs" ).tabs();
}


JSP relationships

Then the next thing to do is to add some HMTL to the page to act as tabs:


WELL DONE!

2011/07/26

Controlling the width/height of the button

Problem
By default, the dimensions of the button is automatically set to the dimensions required to accommodate the content inside, such as an image. But my length of text is larger than the default width of button.
The html is as bellows:

Solution
There will often be times when you want to manually control this dimension to fit your design. By using another CSS declaration, you can. To control the width/height of the button, use the following style declaration:

Therefore, my html will be looked like this way

Demo

The Principles of Good Programming

http://www.artima.com/weblogs/viewpost.jsp?thread=331531

1. DRY - Don’t repeat yourself
2. Abstraction Principle
3. KISS (Keep it simple, stupid!)
4. Avoid Creating a YAGNI (You aren’t going to need it)
5. Do the simplest thing that could possibly work
6. Don’t make me think
7. Open/Closed Principle
8. Write Code for the Maintainer
9. Principle of least astonishment
10. Single Responsibility Principle
11. Minimize Coupling
12. Maximize Cohesion
13. Hide Implementation Details
14. Law of Demeter
15. Avoid Premature Optimization
16. Code Reuse is Good
17. Separation of Concerns
18. Embrace Change

2011/07/25

Building DropDown Menu Using jQuery and JSON

Scenario
We have six dropdown menus where we populate violation number (違章編號) and will be triggered on onBlur event.

Screenshot


jQuery
jQuery is a light weight JavaScript library that simplifies the client side DOM manipulation, event handling, ajax communication easier and faster.

JSON
JSON means JavaScript Object Notation. It is a simple text based, human readable data format that is widely used in AJAX application to transfer data as an alternative to XML format. In simple words, it is simply key- value pairs, separated by commas and enclosed in curly braces.

NIG451W.jsp

nig.nig451w.js


Sequence Diagram


NIG451Controller.java
 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
  @Controller
  @RequestMapping("/front/NIG451W")
  public class NIG451Controller extends AbstractController {
      @SuppressWarnings("unused")
      @RequestMapping(value = "/autoCompleteDropdownMenu", method = RequestMethod.POST)
      private @ResponseBody
      void autoCompeleteDropdownMenu(final NIG451DataBean formBean,
      final HttpServletResponse response) throws FDCDefineException,
      IllegalAccessException, InvocationTargetException, IOException {
          //setup primary key
          Nigt001PK nigt001PK = new Nigt001PK(formBean.getDlvUnit(),
          formBean.getTaxCd(), formBean.getDlvYr(), formBean.getFlgTp(),
          formBean.getSerialNo());
          //do query based on the search criteria
          List nig451Beans = nig451Manager.autoCompeleteDropdownMenu(nigt001PK);
          
          JSONArray jsonArray = new JSONArray();
          
          for(NIG451Bean vo : nig451Beans){
              Map map = new HashMap();
              map.put("name", vo.getVioOrgNm());
              map.put("value", vo.getVioOrgCd());
              jsonArray.add(map);
          }
          
          response.setContentType(NigAccessKey.JKEY_CONTENT_TYPE);
          response.getWriter().write(jsonArray.toString());
      }
  }


NIG451ManagerImpl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  @Service
  public class NIG451ManagerImpl implements NIG451Manager {
      /*
      * (non-Javadoc)
      * @see gov.fdc.nig.punishment.service.NIG451Manager#autoCompeleteDropdownMenu(gov.fdc.nig.domain.Nigt001PK)
      */
      @Override
      public List autoCompeleteDropdownMenu(Nigt001PK nigt001pk) {
          return nigt001Dao.autoCompeleteDropdownMenu(nigt001pk);
      }
  }




Demo

Eugene H. Krabs

昨天看到一本書,上面是這樣寫的:『當一個人被剝奪做出重要決策的機會,它就會開始把所有能做的決定,都當成是重要決策。他會開始挑剔歸檔的方式,強烈希望每支鉛筆都被削的很尖,渴望確定窗戶事打開(或關上)的,並且傾向使用兩到三種不同顏色的筆。』

ㄎㄎㄎ,有點身歷其境的感覺唷

2011/07/22

Project Plan

長官們往往把規劃擱置一旁,總是先做當天沒這麼重要卻比較緊急的工作。
最後,等到不能再拖下去的時候,就匆匆忙忙地為重大任務訂出計畫,然後很不明智的把拙劣的計畫變成具體的、不能改變的目標,進而帶領團隊往這個目標前進。結果,任務還沒開始正式執行,整個團隊就因為準備不周而筋疲力竭。

緊急 != 重要

緊急的事情,不等於重要的事情。
常常總是在下午三、四點接到email,要在下午五點以前繳交報告或統計數據。此時,就要放下手邊重要的事情,去做長官交辦的緊急的事情,反倒讓重要的事情進度落後,長官在回過頭來檢討為什麼進度落後。這樣豈不荒謬、可笑!

Project Delay

在專案進度嚴重落後時,長官們總是為了要更進一步掌控各個組別的細部進度,了解問題出在哪裡,導致開會次數與報告書變多,因此最重要的工作反而無法進行。更嚴重的是,工作場所的工作氣氛會變得更惡劣,員工的工作動機也會下降,反而讓專案進度更加落後。
這樣的錯誤,總是在不同的公司,重複上演,沒有謝幕的一天。

Building Auto-Completion Using jQuery and JSON

Scenario
As I filled in violation number, system should fill out its related column on onBlur event of violation number(違章編號). [it will return single value, i.e. a Java bean class]


Sequence Diagram

How to do it
nig.nig451.js: will be triggered on onBlur event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  $(document).ready(function() {
      //略....
      $("#vioNumDlg").blur(function() {
       
         $.ajax({
           url: 'NIG451W/autoComplete',
           type: gsAjaxPost,
           data: formData,
           dataType: gsAjaxJson,
           contentType: gsAjaxContentType,
           //略...
           });
      });
  });


NIG451Controller: will be triggered when doing post over 'NIG451W/autoComplete' url pattern

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
   @RequestMapping(value = "/autoComplete", method = RequestMethod.POST)
   private @ResponseBody
   void autoCompletForm(final NIG451DataBean formBean,
                        final HttpServletResponse response) throws FDCDefineException,
                        IllegalAccessException, InvocationTargetException, IOException {
       //primary key
       Nigt001PK nigt001PK = new Nigt001PK(formBean.getDlvUnit(),
       formBean.getTaxCd(), formBean.getDlvYr(), formBean.getFlgTp(),
       formBean.getSerialNo());
       
       //service class will search by primary key and return a POJO class
       NIG451Bean result = nig451Manager.autoCompleteForNIG451(nigt001PK);
       
       //instantiate JSONObject object, and set result to it.
       JSONObject json = new JSONObject();
       json.put("result", result);
       
       //write json string to HttpServletResponse
       response.setContentType(NigAccessKey.JKEY_CONTENT_TYPE);
       response.getWriter().write(json.toString());
   }

nig.nig451.js:

 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
   $(document).ready(function() {
       //略....
       
       $("#vioNumDlg").blur(function() {
           //略....
           $.ajax({
               url: 'NIG451W/autoComplete',
               type: gsAjaxPost,
               data: formData,
               dataType: gsAjaxJson,
               contentType: gsAjaxContentType,
               success: function(jsonResult, status) {
                   //get values from json result
                   //syntax: jsonResult.[key value we set in JSON].[bean class attribute]
                   $("#manageCd").val(jsonResult.result.manageCd);
                   $("#vioYr").val(jsonResult.result.vioYr);
                   $("#vioHostNm").val(jsonResult.result.vioHostNm);
                   $("#vioHostIdnBan").val(jsonResult.result.vioHostIdnBan);
                   $("#respNm").val(jsonResult.result.respNm);
                   $("#respIdn").val(jsonResult.result.respIdn);
                   $("#respAddr").val(jsonResult.result.respAddr);
               },
               error: function(xhrInstance, status, xhrException) {
               }
           });
       });
   });



NIG451W.jsp

Check the result

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