2014年12月9日 星期二

mybatis - 如何設定dynamic sql動態因應不同的database環境?

參考:Mybatis databaseIdProvider  Multi-db vendor support
  1. 首先於mybatis-config新增<databaseIdProvider>設定
  2. 
     
      
     
         
     
      
     
    
    
      
      
      
      
    
    
     
      
     
     
    
    
    
  3. 接者在欲使用的dynamic sql部分如下使用:
  4. 
    
    
      
        
          select seq_users.nextval from dual
        
        
          select nextval for seq_users from sysibm.sysdummy1"
        
      
      insert into users values (#{id}, #{name})
    
    
    

2014年11月15日 星期六

AngularJS Scope:use ng-include

此篇文章因為一開始使用mac pages製作,後來再轉成html輸出至此,格式很亂(因為我還不太會用pages),因此code的部分皆使用純文字貼上,請見諒

Scope:use ng-include
此篇所有測試sourceuse hg-include

angularJsScope繼承概念在一般情形下是很直觀的,然而在2-way-data binding的primitive情況下有些必須注意,以下來做些ng-include的小測試:

首先我們準備檔案index.html 
<!DOCTYPE html>
<html ng-app="myApp">
<!-- class="show-scope-demo"-->
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>


<body ng-controller="scopeCtrl as ctrl">
  <h1>Hello!Scope</h1>

  use myPrimitive: <input ng-model="myPrimitive">
  use myObject.aNumber: <input ng-model="myObject.aNumber">

  <p>myPrimitive:{{myPrimitive}}</p>
  <p>myObject.aNumber:{{myObject.aNumber}}</p>

</body>


再來準備script.js 

var app = angular.module('myApp',[]);
app.controller('scopeCtrl' ,['$scope', function($scope){
  $scope.myPrimitive = 50;
  $scope.myObject    = {aNumber: 11};
  
  
  $scope.setMyPrimitive = function(value) {
      $scope.myPrimitive = value;
  }

}]);
執行測試:

  1. 當一開始載入後,一切如我們所想,兩個input輸入框與下方顯示都很完美的反應我們設定的初始值:
  2. 接下來我們試驗者改變兩個輸入框,將它們各自改變為2266,一切如我們預期般執行 

此時內部的假想scope如下:
 




接下來我們試驗使用ng-include方式,首先新增tpl1.html 

myPrimitive:
<input ng-model="myPrimitive">

<br>

再新增tpl2.html 


myObject.aNumber:<input ng-model="myObject.aNumber">

變更原先index.htmlbody部分): 


<body ng-controller="scopeCtrl as ctrl">
  <h1>Use ng-include</h1>

  <div ng-include src="'tpl1.html'"></div>
  <div ng-include src="'tpl2.html'"></div>

  <p>myPrimitive:{{myPrimitive}}</p>
  <p>myObject.aNumber:{{myObject.aNumber}}</p>

</body>

準備完畢,再一次執行測試:

  1. 當一開始載入後,一切如我們所想,兩個input輸入框與下方顯示都很完美的反應我們設定的初始值:
  2. 然而,這次我們一樣改變兩個輸入框,將它們各自改變為2266,卻發生了如下的情況
     

第一個myPrimitive沒有如果們預想的變更成功,這是怎麼一回事?
首先,我們知道使用ng-include會增加新的scope,因此一開始(初始載入後)的內部結構如下:



此時除了原先的RootScopeParentScope以外,又多了兩個ChildScope,分別為ng-include=‘tpl1.html’ng-include=‘tpl12.html’所產生。

而當我們變更輸入框的數值的時候,輸入框2(myObject.aNumber)還是維持與之前一樣的方式,參考至$parent.myObject.aNumber並且更改了他;然而輸入框1(myPrimitive)就不是這麼一回事了,他首先確認所屬的ChildScope1是否擁有myPrimitive這個變數,發現沒有,此時並不會如我們預想般自動去尋找$parent.myPrimitive,而是直接在ChildScope1下建立了myPrimitive,並且assign新值:22,因此目前內部結構變更如下: 

以上的問題全部都不是AngularJS才有的,屬於AngularJS的只有使用ng-include,建立了新scope;而不如我們預想般尋找$parent.myPrimitive並且變更而是產生新的myPrimitive則是JavascriptPrototypal Inheritance的概念(請參考連結文章,有很詳盡的說明)。

那麼如何去變更$parent.myPrimitive呢?有以下兩種方式:
  1. 一定使用關鍵字$parent
  2. 使用function()
以下我們繼續變更code測試上面兩個方式,首先測試第一種方式:使用$parent

變更tpl1.html

myPrimitive:
<input ng-model=“$parent.myPrimitive">

<br>

測試,變更輸入框為2266

執行成功!接下來測試第二種方式:使用function()
變更tp1.html


myPrimitive:
<input ng-model="myPrimitive">
<button ng-click="setMyPrimitive(myPrimitive)">$scope.setMyPrimitive()</button>

<br>

變更script.js


var app = angular.module('myApp',[]);
app.controller('scopeCtrl' ,['$scope', function($scope){
  $scope.myPrimitive = 50;
  $scope.myObject    = {aNumber: 11};
  
  $scope.setMyPrimitive = function(value) {
      $scope.myPrimitive = value;

  }

注意在這邊tpl1.html中的<input ng-model=“myPrimitive”>不是使用$parent.myPrimitive
開始測試!首先變更為2266



因為沒有使用方法1$parent.myPrimitive 因此myPrimitive如上所說一樣維持50 
這時我們按下<button>,呼叫$scope.setMyPrimitive(),確實變更成我們要的22了! 


2014年10月21日 星期二

JAVA - 基本properties的運用


說起來真是慚愧,用java這麼久,一直都沒有真正使用過properties file,今天就來練習一下:(其實是因為專案需要多國語系的關係啦,不得不用...)


假設我們設計一個網站 or app 需要支援多國語系,但不可能有人願意把每個跟語系有關的文字harcode在程式裡,畢竟那太愚蠢了,變成一個專案多個版本,卻只是為了那些不同的顯示文字,使用properties可以很好地解決這個問題:

今天我需要依照不同的語系,輸出不同的招呼語(中文=你好,英文=hello,日文=こんにちは),我該怎麼做呢:

首先是專案架構:
很簡單src只有一個檔案,而resource下的3個properties就是今天的主角囉,properties.sh只是方便執行測試的sh檔案,先看唯一的code Main.java:

package com.example.shihanne.properties;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;

public class Main {

 public static void main(String[] args) throws IOException {
  Locale locale = Locale.getDefault();
  String key    = "hello";
  if(args.length == 1)
   key = args[0];
  if(args.length == 2){
   key = args[0];
   locale = Locale.forLanguageTag(args[1]);
  }
  System.out.println(new Main().getMessageByBundle(key,locale));
 }
    
    public String getMessageByBundle(String key,Locale local) throws UnsupportedEncodingException{
     ResourceBundle rs = ResourceBundle.getBundle("config",local); 
     return new String(rs.getString(key).getBytes("ISO-8859-1"),"UTF-8");
    }
}


以下是我的properties file:

config_en.properties:
hello=hello

config_zh_TW.properties:
hello=你好

config_ja.properties:
hello=こんにちは

很簡單,都各只有一行,=左邊的hello代表這個propertie的參數名稱(key),=右邊的也就是我們要輸出的各國語系value啦~

必須要注意的是properties的命名規則:[filename]_[locale].properties
在這裡我們使用的檔案名稱為config,繁體中文的locale=zh_TW,日文locale=ja,英文=en

程式很簡單,Main先偵測是否有指定參數:
第一個參數為欲讀取的properties key(此例為hello),第二個參數為語系(若沒有,則使用本機的預設語系(當然是zh_TW囉) )
之後我們再使用ResourceBundle.getBundle(basename,locale) 讀取properties file,最後再使用ResourceBundle.getString(key)讀出我們需要的value

properties.sh如下:

java -cp ../bin:../resource com.example.shihanne.properties.Main $1 $2

結果畫面:

2014年10月20日 星期一

Android GCM 某些裝置無法成功接收訊息

參考:
http://developer.android.com/google/gcm/client.html#manifest
http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/


將近一年沒有再碰過android,這兩天為了火燒屁股的專案不得已再撿回來碰,
這次測試的是使用Android GCM機制來發送Notification,但奇怪的是五台測試裝置,分別為Android:4.1.1,4.1.2,4.2.2,4.0.4,與2.3.3

但是,怎麼發送訊息,4.0.4與2.3.3的就是無法成功接收訊息,試驗了老半天,最後在上述參考網站2發現問題點,原來是AndroidManifest.xml設定的問題,我原先設定如下:



    
    
    
    
    

    
    

    
        
            
                
                
            
        
        
    



原因就出在其中的




因為我是直接偷懶貼google教學(如上參考網址1),結果沒有把其中的com.example.gcm替換為我的pkg:com.xxx
因此,只要將這兩行的pkg也替換掉為我的pkg,就可以順利運作了。

不過這種錯誤實在是太難找了..因為4.1.1,4.1.2,4.2.2的都運作正常啊...難道是因為版本比較高所以自動錯誤修正嗎....?

2014年9月17日 星期三

eclipse - java.lang.NoSuchMethodError: org.hsqldb.DatabaseURL.parseURL(Ljava/lang/String;ZZ)Lorg/hsqldb/persist/HsqlProperties;

前些時候重灌了電腦,當然重灌後某些安裝路徑都會改變,這時候原本使用hsqldb做UnitTest的專案出現了以下錯誤: java.lang.NoSuchMethodError: org.hsqldb.DatabaseURL.parseURL(Ljava/lang/String;ZZ)Lorg/hsqldb/persist/HsqlProperties; at org.hsqldb.jdbc.JDBCDriver.getConnection(Unknown Source) at org.hsqldb.jdbc.JDBCDriver.connect(Unknown Source) at java.sql.DriverManager.getConnection(DriverManager.java:571 ... 找了很久才發現,原來是我這個Test Proj有reference到主要被測試的Proj, 但是這個主要被測試的Proj某些設定在Eclipse的classpath卻因為電腦重灌的關係而失效了, 所以才會造成這個錯誤。 p.s使用者種與環境相關的設定還是簡單點好,儘量不要用絕對路徑設定的classpath 啊

2014年9月10日 星期三

ANT - build時出現 [javac] Compliance level '1.4' is incompatible with target level '1.8'. A compliance level '1.8' or better is required

參考:
http://stackoverflow.com/questions/1487978/setting-the-target-version-of-java-in-ant-javac

今天使用ant的時候出現了奇怪的錯誤:
  [javac] Compliance level '1.4' is incompatible with target level '1.8'. A compliance level '1.8' or better is required

我明明在javac中標明了target="1.8"如下:



  ...

後來找到上述討論區才知道原來可以加入以下這樣的property來解決這樣的問題:



2014年8月28日 星期四

使用 < jsp:include page="${pageContext.request.contextPath} />出現錯誤...javax.servlet.ServletException: JBWEB004036

使用類似以下的jsp:include方式:

< jsp:include
page="${pageContext.request.contextPath}/query/QueryUtil.jsp"/>
卻出現以下錯誤:

javax.servlet.ServletException: JBWEB004036: File &quot;/mx/query/QueryUtil.jsp&quot; not found
答案就是,jsp:include似乎不支援使用${pageContext.request.contextPath}

2014年8月9日 星期六

SVN - 如何修改已經commit的comment訊息?

參考:How do I edit a log message that I already committed in Subversion?
(編號38)


直接列出如下步驟:

  1. 首先進入該repository的hooks目錄
  2. mv pre-revprop-change.tmpl pre-revprop-change
  3. chmod 755 pre-revprop-change

2014年7月5日 星期六

Jenkins - svn commit時總是無法取到最新版本..

參考:Triggering build twice for each commit


    這問題困擾我蠻久的,就是每一次當我svn commit後呼叫post-commit 執行jenkins build的時候,『第一次』也就是由post-commit執行的那次jenkins build,總是無法取到最新的svn更新版本,一定要手動執行jenkins build才可以成功抓到svn最新版本。

    專案小的時候還無所謂,但隨者專案越來越大,且每一次build都還要執行unitTest的情況來說,每次的小修改,都要花上3~5分鐘(執行兩次build)才能build到最新版本開始測試,實在是很累。

   後來找到上述討論,似乎與time sync有關(不太明白為什麼),我用了討論串中最簡單的方式,也就是在jenkins build的組態中將svn url後面加上@HEAD的參數,解決了此問題。

   另一點奇怪的是,我有兩個專案,在一開始都沒這種問題,都是用到後面才開始出現.....

2014年6月18日 星期三

How do i show the leading zeros when exporting to excel..


參考:
http://www.raymondcamden.com/index.cfm?mode=entry&entry=432F11FE-06A0-BB83-6873F3EAD82A138F


因為案子有需要把網頁上的內容export成xls,但發生了個問題,我的資料有很多是"0"開頭的,例如:

0101000123


顯示的時候正常,但export xls後內容就變為:
101000123

很顯然的,excel將它當成了數字資料,因此截去了最左邊的0,
該怎麼解決呢??

看到網路上不少建議都是:自行打開excel然後更改整個col的資料形態,改為文字,
但這不符合我的需求(應該說客戶也不可能同意啊)。

最簡單的方式就是,在這樣的資料前面加上空白字元( &nbsp;)符號,就可以了。

2014年6月8日 星期日

ant - 如何在製作jar file時將一系列的*.jar 置入manifest classpath中?

參考:how-convert-list-jar-files-ant-classpath-build-jar

標題實在很不好下..總之就是,假設我有一堆的jar file置放於 lib 這個目錄下,今天我想打包一個jar檔案,class-path需要參考到這個lib目錄下的所有jar,但我又不希望一個一個手動輸入jar 名稱,該怎麼做?


原先一個一個輸入,build.xml可能如下:
 
  
  
     
       
           
   
       
       
   
  
    


若lib下的jar檔案數量很少,這樣做還沒什麼問題,但是當我今天lib下的jar檔假設有10多個以上,且常常要新增,那麼一直來維護這個build.xml就是很浪費時間的事情。

所以,參考上述網址,更改如下:
 
 
  
    
      
    
       
  
      
      
    
  
     
       
           
   
       
       
   
  
    


如果不放心,當執行完畢ant build後,可以解壓縮jar檔看manifest.mf正不正常:

2014年5月30日 星期五

Selenium - 如何移動『滑鼠』至隱藏的元件


今天偷到一點時間,繼續來研究selenium(再不研究會不敢改client了@@)。

遇到一個問題,例如這種常見的隱藏選單列:(圖片打馬賽克請見諒)

 MenuA,MenuB,MenuC是母選單,實際功能的子選單需將滑鼠移到各個母選單上才會在下方出現子選單:


今天我需要selenium模擬滑鼠移到其中的母選單『MenuB』,該怎麼做呢?
請參考如下片段:

        
 WebDriver _driver;
 //....
 WebElement element1 = _driver.findElement(By.xpath("//span[@id='MenuB']"));
 builder.moveToElement(element1).perform(); 

如上述,只要找到MenuB 母選單的element後,執行moveToElement()就可以了。

Cucumber - 使用時無法執行@Before 與 @After



最近開始嘗試使用cucumber + selenium,但測試的時候發現無法順利執行被
@Before或@After annotation標注的function,

原來當我們使用cucumber時,這些annotation的import位置為:

import cucumber.api.java.After;

import cucumber.api.java.Before;

而非JUNIT的
org.junit.After
org.junit.Before


雖然是小問題但沒想到還真要花些時間找呢~

2014年5月6日 星期二

JBOSS - web.xml中讀取環境變數

參考:JBOSS顧問

 如果我想要在web.xml設定環境變數可讀取,該怎麼做呢?參考以下的web.xml
  < web-app>
   
       
            log4jConfigLocation
            ${jboss.home.dir}/ap-config/xxxx/log4j.properties
      
   
  </ web-app>
如上述,我們希望在web.xml中指定log4j.properties的外部路徑,但需要使用環境變數讀取(避免不同客戶端機器安裝位置不同產生問題),
但在預設的JBOSS安裝是無法直接讀取的,修改方式很簡單,這裡以使用standalone的方式說明(若是使用domain就依照方式更改domain.xml就可)

找到${JBOSS_HOME}/standalone/configuration/standalone.xml,並找到以下片段(//profile/spec-descriptor-property-replacement):

        
            false
            true
        

重點就是spec-descriptor-property-replacement這個了,改成true,應該就可以順利運作了~

p.s跟有雄厚資金的客戶配合還是有好處的,因為他們有錢請顧問@@

2014年5月5日 星期一

Mybatis - if test dynamic sql出現 org.apache.ibatis.exceptions.PersistenceException : ###Error querying database. Cause: java.lang.NumberFormatException: For input string: "

參考:test expression throws NumberFormatException

今天使用mybatis if test出現個問題,原先使用如下:

 < select id="selectTable" parametertype="hashmap" resulttype="hashmap">
  select * from TABLE 
  
    
    < where>
      < if test="type == 'M' or type=='C' "> 
    
    ColA=#{volue} 
    < / if>
    < / where>  
        
  
 </ select> 

但卻出現了:

com.XXXX.util.exception.MIException: org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.lang.NumberFormatException: For input string: "M"

### Cause: java.lang.NumberFormatException: For input string: "M"

這就奇怪了,明明欲檢測的M與C應該是string怎會被轉換為number做檢查呢?
原來是"(雙引號)與'(單引號)解析的問題,改成如下:
 
< select id="selectTable" parametertype="hashmap" resulttype="hashmap">
  select * from TABLE 
  
  < where>
    < if test='type == "M" or type=="C" '> 
    
    ColA=#{volue} 
    < / if>
    < / where>        
  
</ select>
      

正常運作~ p.s話說就算使用了plugin貼xml語法還是會不正常啊...所以空格這種就自行刪除吧

2014年5月4日 星期日

JBOSS - 使用log4j失敗問題..

參考:Use my log4j under jboss 6

自從昨天移到JBOSS上後又發生怪問題...就是log問題。

因為客戶需求有各種不同的log,因此我利用點空閑時間將原本的log拆出區分為多個logger,
但怪問題發生了:

logger會時好時壞!!??

以下為我的log4j.properties:


log4j.rootLogger=DEBUG,total

log4j.appender.total=org.apache.log4j.FileAppender
log4j.logger.com.xxxx.mi.web.servlet.query=DEBUG,AOPLOG
log4j.logger.com.xxxx.mi.web.esb.util=DEBUG,AESBLOG
log4j.logger.com.xxxx.mi.web.omicard=DEBUG,AOMICARDLOG
log4j.logger.com.xxxx.mi.web.sql.mybatis=TRACE,ADBLOG


所謂的時好時壞就是,rootLogger一定是正常的,然而我其他定義的logger(如上com.xxxx.mi.web.servlet,logger.com.xxxx.mi.web.esb.util..這些),有時正常有時卻無法正確記錄,而且每次重啟jboss後正常或不正常的logger都不一定,
一開始以為是我的log4j.properties有問題,然而嘗試了半天實在找不到錯誤...後來看到以上參考網站,試者在tomcat上使用...果然tomcat一切正常,就只有jboss不正常。

後來找了些文章,好像jboss本身有自帶log,因此會衝突(?),先依照參考網站解決方式如下:


  1. 在META-INF下新增jboss-deployment-structure.xml,內容如下:

  2. 
      
        
            
            
        
      
    
    
  3. 更改JBOSS的設定:更改${JBOSS_HOME}/bin/standalone.bat(這裡使用win) 新增啟動參數-Dorg.jboss.as.logging.per-deployment=false:
    先找到以下這段:
    rem Setup JBoss specific properties 
    set "JAVA_OPTS=-Dprogram.name=%PROGNAME% %JAVA_OPTS% "
    


    更改為:
    rem Setup JBoss specific properties 
    set "JAVA_OPTS=-Dprogram.name=%PROGNAME% %JAVA_OPTS% -Dorg.jboss.as.logging.per-deployment=false"
    
  4. 重新啟動jboss,如果先前log4j的其餘部分沒有問題,應該就可以正常運作囉~~
這問題搞了我一天多....進度又整個延宕.............!@&^^$!@$@#

2014年5月3日 星期六

ESAPI - java.lang.reflect.InvocationTargetException....java.lang.ClassCastException: org.owasp.esapi.reference.Log4JLogger ...

參考: How to resolve the error "java.lang.reflect.InvocationTargetException"

今天ESAPI又出現了這個似曾相似(前篇)的錯誤:

org.owasp.esapi.errors.ConfigurationException: java.lang.reflect.InvocationTargetException HTTPUtilities class (org.owasp.esapi.reference.DefaultHTTPUtilities) CTOR threw exception.
at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:129)
at org.owasp.esapi.ESAPI.httpUtilities(ESAPI.java:121)
at..
.....
Caused by: java.lang.ClassCastException: org.owasp.esapi.reference.Log4JLogger cannot be cast to org.owasp.esapi.Logger
at org.owasp.esapi.reference.Log4JLogFactory.getLogger(Log4JLogFactory.java:88)
at org.owasp.esapi.ESAPI.getLogger(ESAPI.java:154)
at org.owasp.esapi.reference.DefaultHTTPUtilities.<init>(DefaultHTTPUtilities.java:112)
at org.owasp.esapi.reference.DefaultHTTPUtilities.getInstance(DefaultHTTPUtilities.java:68)
... 40 more

不同的是,此次root exception為:
java.lang.ClassCastException: org.owasp.esapi.reference.Log4JLogger

這...真是太匪夷所思了,這段code我可是改都沒改,且出錯的為esapi內部,而且TestCode又都運作正常全綠燈(確定有測試到這段!!),卻在實際環境上報錯,所以應該可以『很合理』的猜出是與『環境』有關造成的錯誤而非code(TestCode告訴我一定不是我code寫錯~~
),最後找到以上參考網站,看到此段:

By chance, do you happen to have an instance of Apache Tomcat already running, or do you also have Microsoft Outlook running while you are trying to startup Tomcat? If so, shut down Outlook, start Tomcat, then restart Outlook. I've had problems in the past with conflicts like this.

的確,因為我今天才把war從tomcat搬到jboss上,而原先的tomcat還在運作中(同一台機器),因此只要把tomcat給關了,移除掉原先deploy的webapp,再重啟jboss就可以順利運作了~~

p.s:話說這個標籤該分類到哪呢?好像分類在esapi也不太對....?

JBOSS - 無法從外部IP連線

參考:Jboss only works on localhost:8080 ,but doesnt reply when called by ip


今天嘗試安裝JBOSS(畢竟案子其實根本不是用tomcat,每次都要改web.xml有點煩),
安裝很簡單,直接下載後執行 ${JBOSS_HOME}/bin/standalone.bat

但這時候卻發現,除非我連上server本機使用瀏覽器瀏覽,從外部都不行(確定防火牆已開),原來是還要設定以下:


修改${JBOSS_HOME}/standalone/configuration/standalone.xml

找到以下這段

    <interfaces>
        <interface name="management">
            <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
        </interface>
        <interface name="public">
            <inet-address value="${jboss.bind.address:127.0.0.1}"/>
        </interface>

  </interfaces>

將其中的name="public"裡面的value改為:
<inet-address value="${jboss.bind.address:0.0.0.0}"/>

就可以了,同樣的管理console如果也要能外連,更改name="management"就可以了。

p.s最近文章有點少啊...所以有點拿文章亂充數了@@

2014年4月30日 星期三

ANT - build junit時出現Errors while applying transformations: Fatal error during transformation...


今天播點時間來實作『一年前』(沒錯...我拖了一年...)上課學習的東西,將我的TestCase搬到Jenkins上,並安裝emma plugin觀看Junit Testing Report,
首先第一件事情當然是要寫一個ANT Build,但執行的時候出現以下錯誤:

[junitreport] Processing /Users/wuanne/workspace/eclipse/XXXXTest/build/junitReport/TESTS-TestSuites.xml to /var/folders/59/jk74nmqj48n_6wksbv0lccw80000gn/T/null1033290394
[junitreport] Loading stylesheet jar:file:/Applications/eclipse/plugins/org.apache.ant_1.8.4.v201303080030/lib/ant-junit.jar!/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-frames.xsl
[junitreport] : Error! 非靜態 Java 函數 'replace' 的第一個引數不是有效的物件參照。
[junitreport] : Error! 無法編譯樣式表
[junitreport] : Fatal Error! 無法轉換 data-type 'void' 為 'reference'。 Cause: 無法轉換 data-type 'void' 為 'reference'。
[junitreport] Failed to process /Users/wuanne/workspace/eclipse/XXXXTest/build/junitReport/TESTS-TestSuites.xml

BUILD FAILED

/Users/wuanne/workspace/eclipse/XXXXTest/build/build.xml:90: Errors while applying transformations: Fatal error during transformation

報錯的build.xml:90為:
<target name="junit-report">
<junitreport todir="${junitReport.dir}">
...
</target>

當然這行看起來實在沒什麼問題,google了一下,參考上述討論網站的解答:
不要使用eclipse自帶的ANT,自行安裝ANT,並在eclipse->preference->Ant->Runtime,按下『Ant Home』設定自行安裝的ANT,重新build,就可以成功了~

ESAPI - 出現 Failed to load ESAPI.properties as a classloader resource...

參考:Configuring ESAPI for use with a Java Web Application

客戶需要使用ESAPI來驗證輸出入的參數是安全的,但我一開始嘗試使用就報錯:

org.owasp.esapi.errors.ConfigurationException: java.lang.reflect.InvocationTargetException SecurityConfiguration class (org.owasp.esapi.reference.DefaultSecurityConfiguration) CTOR threw exception.
at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:129)
at org.owasp.esapi.ESAPI.securityConfiguration(ESAPI.java:184)
at org.owasp.esapi.ESAPI.httpUtilities(ESAPI.java:121)
at com.ctbc.mi.web.util.ServletUtil.getESAPIRequestParameter(ServletUtil.java:169)

at com.ctbc.mi.web.servlet.query.MIQueryServletMain.doProcess(MIQueryServletMain.java:35)
.......
.......
Caused by: java.lang.IllegalArgumentException: Failed to load ESAPI.properties as a classloader resource.
at org.owasp.esapi.reference.DefaultSecurityConfiguration.loadConfigurationFromClasspath(DefaultSecurityConfiguration.java:667)
at org.owasp.esapi.reference.DefaultSecurityConfiguration.loadConfiguration(DefaultSecurityConfiguration.java:436)

... 48 more


查看root exception:
Caused by: java.lang.IllegalArgumentException: Failed to load ESAPI.properties as a classloader resource.
很明顯是設定錯誤,少了ESAPI.properties這個檔案,依照上述參考網站所述,建議放在src root下:
  1. Create a ESAPI.properties file in the root source directory of your web application. Do not place it in a package inside the root source directory because the DefaultSecurityConfiguration will not find it.
更改置放的位置後就可以順利執行了~



2014年4月19日 星期六

mybatis錯誤:The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

Myibatis 配置问题
這樣的mybatis-config.xml:

 
          ...  
 
 
 
          ...
 
 
          ...
            
 
            ....     
 

錯誤:
The content of element type "configuration" must match 

 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

原來mybatis的xml宣告是有順序的,務必要按照錯誤上所說的:
properties 再來是 settings 再來 typeAliases... 依此類推。

因此以我的問題來說,只要將settings改到properties下面environments之上就可以囉(也就是中間)
更改後如下:


 
          ...  
 
 
 
          ...
        

 
          ...
 
    
 
            ....     
 

2014年4月4日 星期五

Junit - 如何比較JsonArray是否相同?

參考:http://stackoverflow.com/questions/2253750/compare-two-json-objects-in-java

好久沒寫blog了,這陣子還是忙到昏天地暗,但也寫了一些HttpUnit的練習,有機會再來分享:)

json是很常使用的格式,我自己的專案也大量的運用它來傳遞訊息,那麼,如何比較兩個jsonArray是否相等呢?(或json)

用junit的assertEquals()是無法正確比較其中內容的,因此我們需要額外的方式:

  1. 下載JSONassert From Maven
  2. 使用:
    JSONAssert.assertEquals(expected,actual,false);
上述的第三個參數:boolean strict,說明,可參考doc上所寫:

There are two modes, strict and non-strict. In most cases, you will probably want to set strict to false, since that will make the tests less brittle.
Strict tests require all of the elements requested to be returned, and only those elements (ie, the tests are non-extensible). Arrays of elements must be returned in the same order as expected. For example, say I'm expecting:
{id:123,things['a','b','c']}The following would match when doing non-strict checking, but would fail on strict checking:
{id:123,things['c','b','a'],anotherfield:'blah'}

何時該使用strict(==true)就端看個人需求選擇囉~

2014年3月14日 星期五

cactus - 執行時出現 org.apache.cactus.util.ChainedRuntimeException: Failed to get the test results at...


今天撰寫FilterTest的時候執行cactus出現:

Failed to get the test results at [http://localhost:8080/CTBC_MI_Web/FilterRedirector]
org.apache.cactus.util.ChainedRuntimeException: Failed to get the test results at
...


其實原因就出在於web.xml沒有加上相對應的設定,因為我是filter出問題,因此應該加上下列:

    
        FilterRedirector
        org.apache.cactus.server.FilterTestRedirector
    

    
        FilterRedirector
        /FilterRedirector
    

之後再重新開啟WebServer就可以了.

2014年3月13日 星期四

selenium - 網頁有Alert視窗執行seleniumTest時出現:driver.close org.openqa.selenium.UnhandledAlertException: Modal dialog present:...


參考:忘記了

因為今天PM告知我一大堆檢核錯誤,看者他的手寫TestCase時我心裡想,那乾脆來寫個seleniumTest好了,幫助他也幫助到我自己,我檢核的流程如下:


  1. 輸入錯誤的使用者密碼,帳號
  2. 跳出相對應的alert訊息(須檢查各種錯誤訊息是否正確)

TestCase如下:

@Test
WebDriver _driver = null;
public void test() throws Exception {
//...
 WebElement textMerchno = _driver.findElement(By.xpath("//input[@id='merchno']"));
 textMerchno.sendKeys(_merchno);
 WebElement textUserid = _driver.findElement(By.xpath("//input[@id='userid']"));
        textUserid.sendKeys(_userid);

 WebElement textPwd = _driver.findElement(By.xpath("//input[@id='userpwd']"));
 textPwd.sendKeys(_userpwd);

 WebElement btn = _driver
  .findElement(By.xpath("//input[@type='button' and @class='table_button']"));
 btn.click();

 Alert alert = _driver.switchTo().alert();
 String actual = alert.getText();
 assertEquals(expectedMsg, actual);
//...
}
但是就在執行時出現了Exception:
 driver.close org.openqa.selenium.UnhandledAlertException: Modal dialog present:... 解決方式如下:
 
 Alert alert = _driver.switchTo().alert();
 String actual = alert.getText();
 _driver.switchTo().alert().accept();
 assertEquals(expectedMsg, actual);

只要加上_driver.switchTo().alert().accept(); 就可以啦~

2014年3月8日 星期六

jquery - 如何將日期字串轉換為Date Type

參考:忘記了@@


哇~~這是我的一篇jquery呢!!因為臨時同事無法支援,又急需只好硬者頭皮改了.


需求:
我有一個日期字串,2013-11-12我要如何轉換成yyyyMMdd的字串呢?

這有分兩個步驟:

  1. 將src String(2013-11-12)轉換為Date Type
  2. 將Date Type轉換為yyyyMMdd的格式
對應的程式就是如下兩行:

 var date = Date.parse($("#billDate").val());
 var szformatDate = $.datepicker.formatDate('yymmdd', new Date(date));

第一行就是做第一點,第二行就是做第二點.(我會用$("#billDate").val()是因為我需要的srcString是從一個id為billDate的<input>中取數值).

比較奇怪的是,jquery的date format字串似乎跟java不太一樣,明明我需要的年是四位數(yyyy),卻應該帶入yy


2014年3月7日 星期五

cactus - 基本安裝與使用(Servlet測試)

參考:Junit In Action 2rd  Tomcat Quickstart forewords

這篇主要使用Tomcat + eclipse做使用環境,因此當然得先確認eclipse有妥善安裝,以及妥善設定好對應的Tomcat,這邊就不多作說明了.


  1. 首先至官網下載欲使用的cactus版本,並且解壓縮
  2. 在Eclipse中建立好Dynamic Web Project
  3. 將步驟1中的 \lib\下的 這幾個檔案找出來:
    >>cactus.jar
    >>commons-httpclient.jar
    >>commons-logging.jar
    >>junit.jar
    >>aspectjrt.jar
    請注意以上都忽略版本號的名稱
  4. 將上述檔案置放於步驟2建立好的專案下的WebContent\WEB-INF\lib下
  5. 建立欲測試的Servlet,如下:
  6.  
    public class TestServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      try{
       String reqBillDate = request.getParameter("reqBillDate");
       process(reqBillDate);
       //...
      }catch(...){
      }
    }
    }
    

  7. 新增cactus test(這裡新增一個package專門置放cactus TestCas)
    package ut.cactus;
    public class CactusTestServlet extends ServletTestCase{
    
     TestServlet _sv = new TestServlet();
    
     public void setUp(){
      // do something..
     }
    
     public void beginDoGet(WebRequest request){
      request.addParameter("reqBillDate", "20131212");
     }
     
     public void testDoGet() throws Exception{  
      _sv.doGet(request, response);
     }
     
     public void endDoGet(WebResponse response) throws Exception {
      String result = response.getText();
      assertEquals("expectedResult",result);
     }
    }
    

    關於上述簡單說明如下:
    >>setUp()與tearDown()為Junit標準使用不另外敘述
    >>beginXXX()與testXXX(),endXXX()為一對的,例如上例中的:beginDoGet(),testDoGet(),endDoGet().beginXXX()代表為做測試之前(執行testXXX()之前)欲做的準備,例如我們會在這邊塞入request的測試參數;而endXXX是做測試之後會被執行的,在這裡我們可以用來檢查servlet Response中的輸出結果.
  8. 準備好web.xml,除了原先就必須建立的TestServlet宣告之外,還必須加入下列:
    
      ServletRedirector
      org.apache.cactus.server.ServletTestRedirector
      
        param1
        value1 used for testing
      
    
    
    
      ServletTestRunner
      org.apache.cactus.server.runner.ServletTestRunner
    
    
        ServletRedirector
        /ServletRedirector
    
    
    
        ServletTestRunner
        /ServletTestRunner
    
    

    p.s:其實我很不希望Test與正是專案綁在一起,而且像上面還一併要得修改web.xml,但目前剛接觸cactus還不知道如何把它區別出來到另一個Test專案執行...
    也許得搭配使用Ant..
  9. 執行Tomcat(在這裡我是使用Eclipse執行)
  10. 輸入網址如下:
    http://localhost:8080/WebProject/ServletTestRunner?suite=ut.cactus. CactusTestServlet&xsl=cactus-report.xsl
    suite參數帶入的就是欲執行的TestCase全名(包含package)
  11. 觀看測試報表囉~~大致如下畫面:

2014年3月6日 星期四

cactus - 執行時出現 javax.servlet.ServletException: Failed to load test suite [xxxxx], Reason is [Class not found "xxxxx"]

參考:Error : “javax.servlet.ServletException: Failed to load test suite [SampleTest], Reason is [Class not found ”SampleTest“]
Cactus - Tomcat Quickstart forewords

第一次使用Cactus,按照官方教學操作,搞了老半天開始執行時卻出現錯誤:
javax.servlet.ServletException: Failed to load test suite [InvoiceSvTDDTest], Reason is [Class not found "InvoiceSvTDDTest"] org.apache.cactus.server.runner.ServletTestRunner.run(ServletTestRunner.java:303) org.apache.cactus.server.runner.ServletTestRunner.doGet_aroundBody0(ServletTestRunner.java:187) org.apache.cactus.server.runner.ServletTestRunner.doGet_aroundBody1$advice(ServletTestRunner.java:218) org.apache.cactus.server.runner.ServletTestRunner.doGet(ServletTestRunner.java:1) javax.servlet.http.HttpServlet.service(HttpServlet.java:621) javax.servlet.http.HttpServlet.service(HttpServlet.java:728) com.ctbc.mi.web.filter.ClearTempFilter.doFilter(ClearTempFilter.java:43) 

找了半天原來,這東東不支援欲測試的TestCase是有package的(20140307更新:有支援,請參考下方更新)... 哎,可能是還沒弄懂,我覺得用這個cactus測試蠻麻煩的,為了使用還得破壞原先的web.xml,而且還一定得要先啟動web-server端...這樣子很難測試耶...光環境上就要搞一大堆... 可能我還沒弄清楚吧..

2013/3/7更新:是可以使用package的,只要如下使用: http://localhost:8080/test/ServletTestRunner?suite=packagename.TestSampleServlet 就可以囉,
例如: http://localhost:8080/test/ServletTestRunner?suite=ut.test.pkg.TestSampleServlet 其中ut.test.pkg就是packag的全名了

2014年3月1日 星期六

dbunit - 如何插入null的資料

參考:Importing data from XML to database using dbunit

使用dbunit的時候遇到一個問題,就是我要將NULL的資料insert進去,該如何做呢?

原來只要使用如:
  ReplacementDataSet dataSet = new ReplacementDataSet(srcDataSet); 
  dataSet.addReplacementObject("[NULL]", null);

就可以將指定的字串替換為null,之後再使用這個替換過的dataset依照原本使用dbunit的方式insert就可以囉~

mybatis - The content of elements must consist of well-formed character data or markup

參考:

mybatis异常:The content of elements must consist of well-formed character data or markup

使用查詢如下:

     select MERCHANT_NO from MI_MIDATRD where MERCHANT_NO = #{merchno}
      and ( (status = 'Y' or status = 'B') 
      and CREATE_DATE <= TO_DATE(#{billdate},'YYYYMMDD') 

執行時出現exception: ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 11; columnNumber: 24; The content of elements must consist of well-formed character data or markup. at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:79) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:71) at com.ctbc.util.sql.MyBatisUtil.getsqlSessionFactory(MyBatisUtil.java:51) .... 後來發現原來是“<"這個符號導致的問題,依照參考網頁改成如下:

   select MERCHANT_NO from MI_MIDATRD where MERCHANT_NO = #{merchno}
    and ( (status = 'Y' or status = 'B')
    <![CDATA[
      and CREATE_DATE <= TO_DATE(#{billdate},'YYYYMMDD')
      ]]> 
    and DELETE_DATE is null)

就解決囉~~

2014年2月28日 星期五

ANT - 出現錯誤error: constant string too long......Syntax error, parameterized types are only available if source level is 1.5 or greater


今天練習使用Ant 做Build,在compile的時候有錯誤:error: constant string too long
發生問題的訊息如下:

ReadInputDataTest.java:75: error: constant string too long

    [javac_data = "8220100300012013081400         154527          0       2797          0          0       2797        151730          08220100300012013081500         154527          0 
....
原來是_data這個數字assign的數值太長,無法compile,但是在eclipse上compile卻沒有任何問題,後來查詢原來是eclipse所使用的Compile並不是標準的jdk compile,因此會變成eclipse可以過Ant可以過,解決方式就是經查詢有兩種:
1.將原本assign constant的部分改為使用readFile
2.將eclipse所使用的compile

我選了第2個方式,解決方式如下:
1.不要使用eclipse的Ant Build
2.找出ANT_HOME的位置
3.下載ecj-4.2.1.jar 這個檔案(在spring與apache專案中都有<是不是4.2.1每個版本不同,端看使用版本而定>)
4.將ecj-4.2.1.jar此檔案置放於%ANT_HOME%\lib下
5.變更ant build.xml內容加上如下:
  


再來直接執行ant -file build.xml
但此時卻又出現另一個問題:Syntax error, parameterized types are only available if source level is 1.5 or greater

101. ERROR in /Users/wuanne/workspace/eclipse/CTCB_MI/New/CTBC_MI_Util/src/com/ctbc/util/xml/XmlProcess.java (at line 116)
    [javac] public static List<Node> selectNodesWithNameSpaceXML(Element ele,String szXpath,Map<String,String> uris){
    [javac]                    ^^^^
    [javac] Syntax error, parameterized types are only available if source level is 1.5 or greater

我實際上的確是使用jdk 1.7的版本卻報錯誤要我使用1.5以上?
解決方式就是在ant build.xml上加上使用版本的attr如下:
    
    ....
    

在上面直接加上target="1.7" 與source="1.7"的attr,
重新執行就解決囉~

2014年2月27日 星期四

linux - 如何找到佔據port的process並將它關閉?

參考:how to kill process running on particular port in linux?

直接貼解決方式:

Use the command
 netstat -plten |grep java
used grep java as tomcat uses java as their processes.
It will show the list of processes with port number and process id
tcp6       0      0 :::8080                 :::*                    LISTEN      
1000       30070621    16085/java
the number before /java is a process id. Now use kill command to kill the process
kill -9 16085
-9 implies the process will be killed forcefully.


2014年2月20日 星期四

dom4j - 新增,修改,刪除xml namespace屬性

新增:

Namespace ns2 = new Namespace("ns1","http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceBody");
((Element)nodeB).add(ns2);

修改:
Namespace ns2 = new Namespace("ns1","http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceBody");
QName newQName = new QName(nodeB.getName(), ns2);
((Element)nodeB).setQName(newQName);

刪除:
for(Namespace e : ((Element)nodeE).declaredNamespaces()){
  ((Element)nodeE).remove(e);
}

需注意的是,刪除,仍會保留當前使用的該namespace的定義,例如原有XML如下:

<ns0:ServiceEnvelope xmlns:ns0="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceEnvelope" xmlns:ns2="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/BSMF/ccMrchntProfileInqRq/01" xmlns:ns1="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceHeader">
</ns0:ServiceEnvelope >


但因為ServiceEnvelope本身是在ns0之下,因此就算執行了刪除,還是會保留住ns0的定義如下:
<ns0:ServiceEnvelope xmlns:ns0="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceEnvelope"></ns0:ServiceEnvelope >

如何以dom4j使用XPATH尋找擁有namespace的node節點?




<ns0:ServiceEnvelope
xmlns:ns0="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceEnvelope">
<ns1:ServiceHeader
xmlns:ns1="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceHeader">
<ns1:StandardType>BSMF</ns1:StandardType>
<ns1:StandardVersion>01</ns1:StandardVersion>
<ns1:ServiceName>ccMrchntProfileInq</ns1:ServiceName>
<ns1:ServiceVersion>01</ns1:ServiceVersion>
<ns1:SourceID>TWINMI</ns1:SourceID>
<ns1:TransactionID>INMI201402071434225</ns1:TransactionID>
<ns1:RqTimestamp>2014-02-07T14:34:25.11+08:00</ns1:RqTimestamp>
</ns1:ServiceHeader>

<ns1:ServiceBody
xmlns:ns1="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceHeader">
<ns2:ccMrchntProfileInqRq
xmlns:ns2="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/BSMF/ccMrchntProfileInqRq/01">
<ns2:REQHDR>
<ns2:TrnNum>INMI201402071434239</ns2:TrnNum>
<ns2:TrnCode>JCMI</ns2:TrnCode>
</ns2:REQHDR>
<ns2:REQBDY>
<ns2:FunCode>I</ns2:FunCode>
<ns2:MerchNBR>010100028</ns2:MerchNBR>
</ns2:REQBDY>
</ns2:ccMrchntProfileInqRq>
</ns1:ServiceBody>
</ns0:ServiceEnvelope>

(上述XML不知道要怎麼在blog中下語法如之前那樣貼...只好貼無排版純文字了...)

因為上述的<ServiceBody>我需要將其中的namespace換成xmlns:ns1="http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceBody,原先我依照以前使用xPath的方式如下:


      
Node  nodeB = root.selectSingleNode("//ns0:ServiceEnvelope/ns1:ServiceBody");


然而,光直行道selectSingleNode(..)就跳出了exception:

org.dom4j.XPathException: Exception occurred evaluting XPath: /ns0:ServiceEnvelope/ns1:ServiceBody. Exception: XPath expression uses unbound namespace prefix ns1

,參考上述的網站說明,原來這種擁有namespace的,要先告知namespace實際上代表的位置在哪,才能做搜尋,修改後方式如下:

        DefaultXPath xpath = new DefaultXPath("//ns0:ServiceEnvelope/ns1:ServiceBody");
Map uris = new HashMap();
uris.put("ns0","http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceEnvelope");
uris.put("ns1", "http://ns.chinatrust.com.tw/XSD/CTCB/ESB/Message/EMF/ServiceHeader");
xpath.setNamespaceURIs(uris); 
Node nodeB = xpath.selectSingleNode(root.getDocument());  

因為此節點,在//ns0:ServiceEnvelope/ns1:ServiceBody的位置,一共使用了ns0與ns1的namespace,因此在setNamespaceURIs(Map)中要將兩個namespace都置入,才能找到,
若一次要找的是多個Node,使用selectNodes()即可(本例因為ServiceBody只會有一個,因此使用selectSingleNode()即可.


p.s:XML其實不是很好懂呢...這兩天花了不少時間看XSD,XML關於namespace的問題...