顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2015年11月10日 星期二

修正Jenkins在JDK與Maven自動安裝無法選擇的問題

最近使用Jenkins時,發現會有時候在「設定系統」時,安裝JDK與Maven的自動安裝的版本列表是空的TextField,導致無法設定。經過Google之後發現可以用以下方式修正:

  1. 到「管理Jenkins」->「Script主控台」,貼上以下程式碼並執行:
hudson.model.DownloadService.signatureCheck = false
hudson.model.DownloadService.Downloadable.all().each { it.updateNow() }
hudson.model.DownloadService.signatureCheck = true
return
  1. 然後再到「管理Jenkins」->「設定系統」,就會發現版本List已經正常了
以上解決方法是參考:Auto-installer for JDK no longer works

2015年4月7日 星期二

如何刪除Git的Remote branch

如果使用git 1.7以上版本,可以使用以下指令:
 git push origin --delete <branchName>

或是:
git push origin :<branchName>

如果是使用eclipse的egit,可以照以下步驟
  1. 將eclipse切換是Git Repositories的操作畫面下,選定要刪除branch的repository。然後開啟滑鼠右鍵選單,選擇「Remote」中的Push
  2. 確認跳出的對話視窗的目標Repository是正確,然後按下Next
  3. 在中間的「Remote Ref to delete」,選擇想刪除的branch,然後按下「Add Spec」。確認該branch出現在「Specifications for push」中
  4. 然後繼續按下Next,出現Push Confirmation視窗,若一切正確,則按下Finsh,就會對Git Remote Repository送出;若無錯誤則完成刪除Remote branch。

2014年10月27日 星期一

Java GC Tuning Guide episode 1 - GC logging

Java的GC讓開發人員減少處理記憶體配置的負擔,但是GC的運作方式與調整也關係到整個Java程式的運作效率與穩定性。先前一直有點半逃避的忽視這部份的東西,大都是用網路搜尋到的一些既有調整參數直接設了,不過該來的終究是要來,只靠一些別人的設定與調整方法,果然還是難以達到最佳的情況。要設定針對自己開發的程式/系統的最佳GC設定,還是得自己來追蹤與分析。

既然要自己追蹤與分析GC的情況,首先就必須有可以分析的資料,而最直接有用的就是GC log。以下說明如何透過JVM option啟用GC log來紀錄GC的內容:

  • -XX:+PrintGCDetails
    • 啟用此參數可讓JVM輸出所有詳細的GC行為,包含GC的類型(一般 or Full, Full GC會造成程式/系統暫停),以及JVM內各記憶體配置區塊的GC動作情況與耗費時間等,資訊相當完整。
  • -XX:+PrintGCTimeStamps 與-XX:+PrintGCDateStamps
    • 這兩個參數是讓JVM在輸出GC log時,也順便加上時間資訊;PrintGCTimeStamps 是以程式啟動時間的相對時間做紀錄,而PrintGCDateStamps則是以系統的絕對時間做紀錄。兩者可擇一或並存。加上時間紀錄有助於分析與判斷。
  • -Xloggc:<file name>
    • 預設啟用gc log時,是輸出到console上,但是這樣不便於閱讀與分析,所以此參數可以指定輸出到檔案上
  • -XX:+UseGCLogFileRotation
    • 使用上一個參數時,會將所有的gc log寫到同一個檔案。如果不想讓檔案不斷成長,可以使用此參數將gc log檔固定在一定數量大小。注意:使用此參數,也必須搭配以下兩個參數才能正確運作
  • -XX:NumberOfGCLogFiles=<number of files>
    • 指定要保留的gc log檔數量,預設為1
  • -XX:GCLogFileSize=<file size>
    • 指定要保留的單一gc log檔大小,預設512K,可用M or K指定大小,如128M。
設定好之後啟動程式,即可在指定檔案中存下完整的gc log。以下是gc log範例(注意:輸出格式可能因不同JVM版本而有所出入):
 0.011: [G1Ergonomics (Heap Sizing) expand the heap, requested expansion amount: 128974848 bytes, attempted expansion amount: 128974848 bytes]
2014-10-27T13:49:46.166+0800: 1.114: [GC pause (young) 1.114: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 2283, predicted base time: 23.70 ms, remaining time: 176.30 ms, target pause time: 200.00 ms]
 1.114: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 14 regions, survivors: 0 regions, predicted young region time: 356.52 ms]
 1.114: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 14 regions, survivors: 0 regions, old: 0 regions, predicted pause time: 380.22 ms, target pause time: 200.00 ms]
, 0.0200610 secs]
   [Parallel Time: 13.7 ms, GC Workers: 4]
      [GC Worker Start (ms): Min: 1113.8, Avg: 1117.5, Max: 1123.2, Diff: 9.4]
      [Ext Root Scanning (ms): Min: 0.0, Avg: 1.3, Max: 2.8, Diff: 2.8, Sum: 5.3]
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Processed Buffers: Min: 0, Avg: 2.8, Max: 11, Diff: 11, Sum: 11]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.2, Max: 0.6, Diff: 0.6, Sum: 0.7]
      [Object Copy (ms): Min: 0.4, Avg: 5.6, Max: 10.8, Diff: 10.4, Sum: 22.6]
      [Termination (ms): Min: 0.0, Avg: 2.6, Max: 3.8, Diff: 3.8, Sum: 10.4]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [GC Worker Total (ms): Min: 4.1, Avg: 9.8, Max: 13.6, Diff: 9.5, Sum: 39.2]
      [GC Worker End (ms): Min: 1127.3, Avg: 1127.3, Max: 1127.5, Diff: 0.2]
   [Code Root Fixup: 0.3 ms]
   [Code Root Migration: 1.1 ms]
   [Clear CT: 0.1 ms]
   [Other: 4.8 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 4.6 ms]
      [Ref Enq: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 14.0M(14.0M)->0.0B(15.0M) Survivors: 0.0B->2048.0K Heap: 14.0M(123.0M)->4084.5K(123.0M)]
 [Times: user=0.03 sys=0.01, real=0.02 secs] 
透過這些log資訊,就可以了解程式/系統運作時的gc行為,進而調整至最適合的設定。
當然,以人工方式來分析gc log,是很困難的,因此也有一些工具可以幫忙。 目前個人使用GCViewer來幫忙處理與分析,雖然不算是很完美,但是也幫了不少忙。

2014年7月8日 星期二

JVM參數調教

官方的JDK裡的JVM已經越來越成熟,效能也越來越好,但是還是有可調教的空間。可調教的參數也跟著JDK演進而有所不同。根據網路上一些文章,大概比較普遍的調教參數有以下幾個:
  • -XX:+AggressiveOpts
  • -XX:+UseCompressedOops
  • -XX:+DoEscapeAnalysis
  • -XX:+UseBiasedLocking
  • -XX:+EliminateLocks
其中,第一個參數是用來調整一些預設的效能參數,啟用之後相當於調整了AutoBoxCacheMax, BiasedLockingStartupDelay, EliminateAutoBox, OptimizeFill, OptimizeStringComcat這幾個參數。第二個參數則是使用在64bit環境中,可以降低JVM在64-bit環境裡的記憶體使用量,並提升效能。後三個參數則是用來減少lock的使用情況,以提升效能。

另外還有一些參數也可能可以改善效能,但是因為沒有太多實務案例,所以無法100%肯定。
  • -XX:MaxGCPauseMillis:設定GC的暫停時間目標值,此數值可限制JVM的GC造成的暫停時間,但是無法精準限制。降低此數值可以減少JVM的延遲,但是可能會造成過多的GC與Throughput下降。
  • -XX:+UseG1GC:啟用JDK7以後新增加的G1GC功能,據說效果比原本的CMS好
  • -XX:+UseStringCache:就名稱來看應該是跟String有關的,但是實際效果不明,有人提過可搭配-XX:+PrintStringTableStatistics 和 -XX:StringTableSize=來調整StringCache大小
  • -XX:CompileThreshold=n:設定method在多少次呼叫後,轉換為機械碼。此數值越大,代表有越多profile資訊來轉換,可轉換出越高效率的機械碼。越低的話,代表越快進行轉換。JVM預設如果是client模式為1000,server模式為10000
  • -XX:+TieredCompilation:使用多層式的機械碼轉換方式。啟用此設定,可改設原本一次性轉換後的機械碼效率。在server模式預設啟用。
  • -XX:+UseLargePages:啟用Large Page的記憶體存取能力,需要OS支援。
  • -XX:MaxInlineSize=n:設定在n byte內的method會被用inline方式編譯,此數值越大,越能改善執行效率。但過大的數值會造成JVM的記憶體佔用過大。
  • -XX:ReservedCodeCacheSize=n:設定預設保留的CodeCache大小。當設定-XX:MaxInlineSize=n較大,或是-XX:CompileThreshold=n較小時,最好可以將此數值加大,否則可能會發生問題。
  • -XX:+UseCodeCacheFlushing:會將CodeCache進行清理,但實際上怎麼清理並不清楚。
  • -XX:+UseFastAccessorMethods, -XX:+UseFastEmptyMethods, -XX:+UseFastJNIAccessors:加速這些method的呼叫與執行
  • -Dawt.nativeDoubleBuffering=true:在GUI模式下,使用Double buffering來改善繪製效能
  • -Djava.net.preferIPv4Stack=true:讓JVM優先使用IPv4。可避免部分因IPv6造成的問題,以及改善連線效能。
  • -Djdk.map.althashing.threshold=n:啟用JDk7u6的新Hash function演算法,可改進效能。n值是指當Set或Map的大小達到時,才使用新演算法。若n=0,則表示強制使用新演算法。注意:新演算法可能造成在iterator順序上的改變(對LinkedHashSet與LinkedHashMap不造成影響)。
  • -XX:SoftRefLRUPolicyMSPerMB=n:指Soft Reference物件的生命週期。預設為1000ms,此數值設定越小,可加快Soft Reference物件被回收的速度。
以上是目前大略整理出的一些參數。上述參數的預設值可能因版本不同而有所不同。可以下列命令列出目前JVM支援的參數與預設值:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version
或是
java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version (包含實驗性功能)
可另用pipeline將結果輸出至檔案。

以下是一些參考資料:

2013年4月22日 星期一

在Linux上啟用Tomcat JMX Remote之二三事

對於Tomcat要啟動JMX Remote的功能,只要遵照官網說明,在catalina.sh or catalina.bat設定系統參數就可以。
不過,在Linux上在啟用就麻煩多了。首先,在linux上可能無法正確的bind到正確的ip,所以如果發現設定後無法連上JMX MBean,就在catalina.sh中,多加一個系統參數
-Djava.rmi.server.hostname=<正確的host ip>
此處的ip不可為localhost或是127.0.0.1
如果Linux是有開防火牆的話,就會更麻煩。因為JMX預設的情況是會使用一個固定的port作為registry用,另外會再使用一個隨機的port作為service通訊用。registry用的port,可以透過系統參數指定,但是service通訊用的卻不行,所以在此情況下,是無法開通防火牆的。所幸,Tomcat有提供一個元件可以來設定。到Tomcat下載頁面下載Tomcat JMX Remote jar,並放到lib資料夾中,然後修改conf/server.xml,加入以下內容
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
,其中rmiRegistryPortPlatform就是指registry port, 而rmiServerPortPlatform就是指真正service通訊用的port

只要將這兩個port在防火牆中開通,就可以從外部來監控Tomcat了。

2011年8月28日 星期日

JMX & Spring

用Spring要產生一個MBean並且註冊到MBeanServer是蠻容易的。基本上,幾乎只要使用XML設定就可以了。作法如下:
  1. 確定有使用context的XML schema。在XML設定檔中加入
    <context:mbean-server id="mbeanServer">
    , 其中id是用來表示該mbean server。這個xml tag是Spring 2.5以後多的,可以簡化設定。當然也可以用傳統的spring bean的定義方式:
    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
  2. 加入RMI registry的宣告,一般來說使用JMX的remoting,在沒特別處理的情況,都是以RMI protocol來做通訊:
    <bean id="rmiRegistry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"><property name="port" value="1099"/></bean>
  3. 宣告MBean的connector server,這是用來讓外部可以連線到MBeanServer:
    <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="threaded" value="true"/>
    <property name="objectName" value="connector:name=rmi"/>
    <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
    </bean>
    ,其中,service url的定義跟JMX的定義跟格式是一樣的。
  4. 最後就是宣告MBeanExporter,然後指定要export的bean跟ObjectName:
    <bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
    <property name="server" ref="mbeanServer"/>
    <property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
    <property name="beans">
    <map>
    <entry key="heero:name=msService" value-ref="mobileSuitService"/>
    </map>
    </property>
    </bean>
而連線的方式,可以用一般標準的JMX client連線方式,或是使用Spring的方式。Spring的方式就是透過MBeanProxyFactoryBean來產生MBean的proxy。
最後,有幾點注意:
  1. mbean server的宣告,如果不是使用<context:mbean-server/>,則如果再產生mbean server發現已經有同id的server存在時,還是會繼續產生。所以可能會造成混淆。所以必須額外加上
    <property name="locateExistingServerIfPossible" value="true"/>
  2. 在實際上使用發現,如果有使用Hibernate跟Spring JMX support,則用傳統bean定義的方式產生mbean server,會一直警告該server已經存在。
  3. 經測試發現,當mbean server啟動時,立刻去連線會失敗,但是若是等待幾秒則OK
  4. 根據Spring文件,MBeanExporter是不可以lazy initialization的。所以必須設定為lazy-init="false"
這是最簡單容易的作法之一,在幾乎不改code的情況下就可以產生JMX的支援。當然Spring也有其他的作法,像是使用annotation。詳情就參考Spring文件。

Property file & Spring

Spring設定除了透過annotation跟xml之外,也可以透過property file來讀取。SpringFramework有提供這樣的機制。
首先,定義設定檔內容,例如:
(jmx.properties)
jmx.host=localhost
jmx.port=1099

然後在spring的xml設定檔內,先加入PropertyPlaceholderConfigurer的定義:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jmx.properties"/>
</bean>
其中,location是指要讀取的設定檔檔名與位置。範例中的寫法是指在classpath內找尋jmx.properties。
然後在需要使用的spring xml設定檔中,使用${property name}的方式即可。例如:
<bean id="example" class="...">
<property name="host" value="${jmx.host}"/>
<property name="port" value="${jmx.port}"/>
</bean>

2010年5月12日 星期三

DB實作心得---SQL JOIN語法

因為都是用Hibernate,所以好久沒碰這種語法了...老實說以前在學SQL的時候,也一直沒很認真的搞懂。一直到最近為了案子,才又比較認真的去看了...
找了幾篇網友分享的文章,都講得很清楚,所以就不在贅述:
一般來說,我們通常都會儘量用一個SQL statement的方式來完成所需要的查詢。而其中都儘量可以用JOIN的方式來把所需要的資料串出來。不過我也有看到有網友發表心得,表示在MySQL上,進行較複雜的JOIN時,效率會很糟糕,甚至會造成DB table被lock過久的情況。事實上,幾年前我自己也用過MySQL,也是發現它在作較複雜JOIN時,效率真的不好...所以後來我就都改用PostgreSQL。

而在Hibernate中,我所常用的Criteria是一種物件式的查詢方式,它建立跟其他table JOIN的方式是透過createCriteria()或是createAlias()。透過這兩種method,可以把要查詢的root entity跟child entity作JOIN。在建立這種JOIN關係時,Hibernate預設是使用inner join的方式,所以在某些情況會無法正確的找到所要資料,甚至會出現LazyInitializationException。此時,可以試試看在這兩種method的呼叫時,加上Criteria.LEFT_JOIN,這樣Hibernate會以Left outer join的方式處理,也許就可以成功的找到所要的資料了。

我個人認為與其使用一個很複雜的SQL statement來增加查詢效率,未必比使用多個簡單的SQL statement來取出資料再透過自己程式邏輯作處理來的容易跟快速...

Database系統實做心得---SQL調校

做了很久跟DB有關的開發工作,但是其實後來大部分都是靠Hibernate為主。透過它,開發者不再需要傷神有關DB底層的設定與SQL語法的管理與維護。不過也變得比較偷懶,比較不會去在意SQL語法上的調校。
在網路上有許多高手有此道的獨到心得,我只也是從中獲得一點點,這兩偏是個人覺得還蠻不錯的:Database Index 筆記調校 SQL 以徹底改善應用程式效能

其中有些跟DB table schema設計或是SQL語法的技巧,不過在Hibernate中,要使用這些技巧可能就不是那麼容易。Hibernate是允許使用者自行設定這些東西產生的方法,甚至可以自訂SQL statement。不過個人覺得Hibernate自己所產生的schema跟SQL statement已經很不錯了,除非真的很有特殊的需求,不然實在也沒必要自己搞(如果都要自己搞,那就失去用Hibernate的意義了)。

不過兩篇文章都有提到的一個重點:index。在Hibernate中,也有支援產生index的機制,但是只會對primary key跟unique column自動產生。雖然在Hibernate Annotation Extensions中有@index的annotation可以指定對某個property產生db index,但是這功能只有在透過Hibernate作第一次DB schema建立時有效;若是指定的index的table已經存在的話,這機制不會有作用,就必須手動產生。根據上面兩篇文章的內容,產生與刪除index的SQL DML為:
create index index_mytable_mycol on mytable(mycol)
drop index index_mytable_mycol

而根據上述兩篇文章的講法,幾乎都認為應該對FK也設定index比較好。當然有些但書,例如在主table資料量有到一定量以上的時候才會比較有效果等等的。

2009年4月14日 星期二

JAX-WS & Apache CXF 二三事(3) - 額外設定與功能

Apache CXF也支援fast infoset,而且只需要少少的設定即可,在service side的spring設定檔中加上:
<cxf:bus>
<cxf:features>
<bean class="org.apache.cxf.feature.FastInfosetFeature"></bean>
</cxf:features>
</cxf:bus>

其他詳細有關CXF bus的設定可以參考:Bus Configuration

若是有需要傳輸大量資料,可以考慮使用MTOM
可以參考:Using MTOM Upload big attachment (document) using CXF ( MTOM ) Spring and Tomcat
基本上,就是在service-side與client-side上都設定mtom-enabled=true的property。而service-side上,可以考慮用DataHandler作為傳輸的媒介

JAX-WS & Apache CXF 二三事(2) - Authentication

Apache CXF也有支援WS-Security來為Web service做傳輸加解密與身份認證的工作。設定與程式部份並不難。可以參考WS-SecurityApache CXF Tutorial - WS-Security with Spring

首先,必須先確定classpath中有wss4j的jar。然後在原本的spring設定檔中的<jaxws:endpoint>加上以下內容:
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />

<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass" value="com.company.auth.service.ServerPasswordCallback" />
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>

其中必須注意的是WSS4JInInterceptor的設定。action是設定認證的方式,使用UsernameToken是表示使用username與password的認證方式,這是最簡單也是常用的方式;passwordType是設定密碼型態,使用PasswordText是指明碼password;passwordCallbackClass是指實作密碼驗證的classname,此處亦可以用passwordCallbackRef來代替,而設定值也將變為已經設定好的bean。以上就是在service端必須要的設定。而除此之外,也必須撰寫對應的PasswordCallback class。例如:
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;

public class ServerPasswordCallback implements CallbackHandler {

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

if (pc.getIdentifer().equals("joe") {
// set the password on the callback. This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
}
}

}
而client的部份也是使用類似的設定但是是for WSS4JOutInterceptor;而password callback class是必須for client的,因為是要設定傳送給service的username跟password。
在client的部份,如果不是透過spring的方式去取得service,則必須自己手動加入有關WSS4JOutInterceptor的設定。

心得:
  1. 透過WSS4JOutInterceptor跟WSS4JInInterceptor,可以讓CXF支援身份認證的功能。但是這類的身份認證是在每次連線時都會進行,所以如果是需要查詢DB的話,可能會導致執行效率低落
  2. service端使用WSS4JInInterceptor; client使用WSS4JOutInterceptor.
  3. 若是透過JaxWsServerFactoryBean來取得service,則手動設定WSS4JOutInterceptor時,雖然會有設定username的行為,但是client password callback class還是必須做設定identifier的動作,否則會有連線問題
  4. 當passwordType是PasswordText時,service side 必須自己取出密碼並且驗證,若是驗證失敗則必須丟出適當的exception;而若是passwordType為PasswordDigest,則密碼是處理過的,無法透過getPassword取出密碼,此時service side也必須將service端的密碼透過setPassword設定給callback class,然後交由framework去驗證;若是驗證失敗,framework會自動丟出exception

JAX-WS & Apache CXF 二三事(1) - Basic

最近因為工作上的需要,所以研究了目前Java對於SOAP Web Services的東西,最後選擇了Apache CXF作為JAX-WS的implementation.

基本上,因為JAX-WS運用了Annotation,所以在開發上算是簡化了許多,比較不需要使用大量的XML來定義。而且JAX-WS & Apache CXF預設會自動使用JAXB來做訊息格式的轉換,所以基本上只要不是複雜的JavaBean,都可以自動轉換,甚至也不會自己去定義JAXB的東西,算是相當方便。而Apache CXF本身也是支援Spring framework,所以在設定上也是遵照Spring 2.0的設定檔標準,倒是不用另外學習。

Apache CXF其實本身支援相當多的格式與標準,但是目前我只是使用JAX-WS的部份。基本入門可以參考:
Web Services Tutorial with Apache CXF A simple JAX-WS service

基本上就是在service class加上@WebService等適當的annotation,然後在spring的設定檔中加上以下內容:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint id="auth"
implementor="com.company.auth.service.AuthServiceImpl"
address="/swAuth"/>
</beans>

其中,前三行的import是必要的,用來載入CXF自己本身的一些預設設定。主要的Web service定義在<jaxws:endpoint/>裏面。其中的implementor是用來指定service implementation class,而address是指service對外公開的名稱,也就是被取用的名稱。spring會根據這個設定,將implementor所描述的class建立出instance,並設定成為JAX-WS的service。如果不想透過指定class name的方式而是想reuse已經存在的bean,則可以省略implementor,而改用<jaxws:implementor>的方式來reference到已定義好的bean

然後在web.xml中加上必要的listener跟servlet設定,如下:
<web-app>
<display-name>Auth Manager</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>

<param-value>classpath:com/company/auth/service/cxf.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>

</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>

</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

</web-app>


其中listener的設定是為了spring,而serlvet的部份才是為了CXF。
而client也可以透過spring來取得web service或是透過JaxWsProxyFactoryBean。

目前有幾點需要注意:
  1. 之前使用2.1.4版會有無法連線的問題,改用2.1.3版之後就正常;2.2.0版雖然release,但是還尚未測試
  2. 即使取得service object,但是並不表示連線就已經建立完成。真正的連線會等到method call進行時,才做連線。
  3. 預設的client的Connection timeout跟receive timeout分別是30s跟60s,所以如果需要等待較長的呼叫時,可能會出現連線中斷。可以透過在client的classpath中加入cxf.xml來覆蓋預設值。詳細的設定可以參考:Client HTTP Transport (including SSL support)

2008年6月15日 星期日

Google Developer Day 2008~(會場花絮)

昨天是Google Developer Day 2008在台北舉辦的日子,算是一件大事吧,因為我看ZdNet跟ITHome的電子新聞在6/13的時候都有報導。不過昨天的天氣其實蠻糟糕的,一直都在下雨...一大早起來搭高鐵的時候就開始下,到了台北還是在下...所以最後就很懶的從市政府捷運站坐小黃過去台北國際會議中心。不過到的時候就看到有不少人在門口對著今天的招牌拍照留念了,所以我們當然也就插一腳囉~
哈~同事Jack忍住沒吃早餐的飢餓,來擔當照片男主角。一進去大廳,果然是蠻多人的啊~跟前年的JavaTwo有的比。報到後想說要快點進去聽開幕的主題演講,沒想到...居然被趕出來!原來...是場地太小...所以晚到的人只能去另外的較小的演講廳看實況轉播....@#$%...。在主題演講中,Google台灣研究所所長簡立峰先生也提到,報名的人數遠超出他們的預估,所以預定的場地完全不夠用...Google啊~你們未免也太小看自己了吧~

在主題演講之後的中場休息,到場外晃晃,馬上就見識到所謂的「百步之內必有食物」,Google!,算你狠!有Google mark的冰箱,沒看過~拍一張。帥氣人學弟,做男主角。
冰箱裡都是Coke Cola跟雪碧,自己取用啦~兩旁還有兩個鐵架,上面也都是滿滿的零食,也都是自己取用啦...當自己家,別客氣啊~

Google的休息區就是這樣嗎?怎麼可能!當然,有吃就有有玩囉~Google非常了解大家的在現場擺了一台Wii跟XBOX360~當然也準備了沙發,讓大家可以舒服的玩囉~(可惡!忘記帶片子來了...)
休息區裡面還有一堆懶骨頭跟氣球讓大家坐。帥氣人當然也就上去湊湊熱鬧了(手上還有Google招待的雪碧XD)。帥氣人,你看到沒,你後面的帥哥被你嚇到了!可別小看這些東西啊~在場地座位不夠的時候,這些東西可是幫上大忙呢~

人真的是大爆滿啊~據說總共是1千多人(我的序號都是1322了,我看搞不好有破1500)。所以,很多場次都是人多到要坐在地板.....

最後,下午4點結束的時候,大家開始等著拿今天最期待的禮物:Google T-Shirt~看到了嗎?這還不是全部的人啊~只是我排很久之後拿到才拍的一部分人啊!衣服質料還不錯啦~整體來說,算是還ok啦~只是場地真的太小啦!>_<

2007年10月22日 星期一

Sun Tech Day - Java 2007參加心得

2007.10.19台灣昇陽公司在台北國際會議中心舉辦了今年的Java大會。原本在8月份的時候想說今年是不是停辦了,因為往年都是在7~8月份的時候辦,沒想到今年卻是拖到了10月中。今年的規模變小了,往年都是2天的議程,今年變成只有一天,不過費用也變便宜XD。另外今年的贊助廠商也變少許多,不過另一個好處是今年不像前兩年,有半強迫逛攤位的活動。今年的議題主要當然是Java(廢話!),不過另一個主題應該是Solaris吧!除了在大會一開始的演講裡有不少對Solaris的推薦與吹捧,在當天的議程裡也有安排了兩場跟Solaris有關的,只是因為我個人目前還不需要去熟悉與了解Solaris,所以沒去~~。
老實說,今年的題目有些是不錯,但是講師的表現卻不是很理想。今年似乎大部分的場次都是由中國過來的Sun的講師主講居多?!然而,這些講師的表現,說實在的,只能說是「差強人意」,當天看到的幾個他們做的Demo,居然沒幾個是在沒出錯的情況下完成的...唉...。至於有關主題的心得,有去參與了JDK發展情況的那場,大致上對未來的JDK 7有更多一點的了解,算是還蠻期待的吧;而最讓我驚艷的,莫過於JavaFX那場了,雖然說講師並沒有講到太多有用的東西,但是那些Demo的效果實在是太驚人了!對JavaFX開始有更多的興趣與期待了!另一個覺得還不錯的,就是JavaME與Mobile Ajax、SVG的結合應用,感覺也是讓人耳目一新。
說真的,今年我並沒有去參與server-side相關的,除了最後一場有關SOA、BPEL。因為今年server-side上面的技術發展似乎沒有啥太大的改變。反倒是client-side上面的UI與互動性這方面的技術的進展,令人感到興奮!個人認為,JavaFX的出現讓原本在client-side比較貧弱的Java重新露出一線曙光,也許真的可以讓Sun也有機會與Adobe、M$一較高下也說不定。不過,就怕Sun自己又亂玩,把它搞砸了。現在心理想的,就是希望找個時間好好玩一下JavaFX~

2007年9月17日 星期一

動態產生form欄位

在wicket中,幾乎所有的web元件都要有對應的html element+wicket id,因此要做到動態產生form內的欄位幾乎沒辦法。不過,最近同事在研究最新版的jtrac的source code,發現透過ListView + Fragment + BoundCompoundPropertyModel就可以做到:
  • ListView主要是用來作為動態新增項目layout的部份,因為它可以根據所給予的model或是List內的內容來產生應該要有的項目。
  • Fragment主要是用來簡化設計,因為原本Fragment就是一種不需要獨立的html template file的一種特殊Panel,所以可以運用來呈現每個ListItem裡的欄位
  • BoundCompoundPropertyModel主要是用來將POJO跟wicket component之間的綁定,採取更彈性的方式來實現。
事實上,應該不是只有以上3種東西的結合才做的出來,只是目前這三種東西的結合是比較容易的作法。主要的關鍵應該是在於如何在runtime的時候,才去產生form欄位的html與必要的code,這通常是需要像是ListView或是Loop這類可以重複產生item的元件。而雖然可以產生重複的item,但是item的內容還必須不一樣,所以才會需要Fragment(這邊也可以用Panel來達成,只是用Panel就會比較麻煩)。有了不同的item之後,另一個問題就是元件與實際data model之間的連結關係,使用BoundCompoundPropertyModel是可以用比較彈性的方式去做到,但是用其他的Model應該也可以,只是可能會比較困難。

有一點需要注意的是:由於是透過ListView來產生item,所以當發生validate error時而回到form時,ListView是會重新再產生新的item,而新的item會去讀取model內的值。但是,由於validation失敗,所以model是不會被更新,所以所有的item都會回到一開始的值,而不是之前輸入的值,這對於很大的form來說是會對user造成困擾的。所幸,可以透過ListViewsetReuseItem(true)來避免重新產生新的item,而是沿用先前產生的,所以就可以保留validation之前的輸入值。

P.S: 雖然說理論上不一定要用ListView,但是由於所知只有ListView才有setReuseItems(),所以若是使用Loop或是其他非ListView的subclasses的元件,可能要自己想辦法處理重複產生新item的問題。

2007年9月6日 星期四

Eclipse & Ant

Eclipse是一套相當受歡迎的IDE,他的refactoring跟code completion做的相當不錯,而且也有許多不錯用的plugin,重點還是免費的(不過老實說,他其他的很多功能並不好用,而且3.2版之後似乎越來越肥大)。Ant是許多Java developer常用的工具,在Netbeans跟Eclipse都有支援。事實上,透過Ant,可以來協助解決很多Eclipse本身沒做好的事。但是,問題在於Ant所需要的build.xml並不容易撰寫,常常可能會發生為了作一件小事,結果花費很多時間去撰寫build.xml。所幸,Eclipse本身有支援將project export出build.xml的功能,透過此功能就可以將使用中的project的相關的設定(像是lib設定等等)一併產生。因此,我們只需要在匯出的build.xml上再加上我們所需要的target去執行我們要的task即可。
以下列出幾個在開發Java project常用的target的語法:
  1. 產生jar:
    <target name="build-jar" depends="build-project">
    <jar destfile="your_jar.jar" basedir="${basedir}/build/classes" compress="true"></jar>
    </target>
  2. 產生javadoc:
    <target name="build-docs" depends="build-project" description="--> build-docs">
    <javadoc author="true" source="1.5" classpathref="Your_project.classpath" sourcepath="${basedir}/src" destdir="${basedir}/doc" charset="UTF-8" version="true" packagenames="*">
    <link href="http://java.sun.com/j2se/1.5.0/docs/api/"/>
    </javadoc>
    </target>
    其中,classpathref可以填入由eclipse幫忙產生的路徑變數,通常是以你的proejct_name.classpath命名。
  3. 產生war:war的建立比前兩個稍微複雜,原因在於third-party lib的import,ant的war task並沒有辦法以<path>來讀取project有用到的lib路徑。所以我們必須自行把這些lib先複製到某個資料夾之後,在用war task去建立war檔,建立完畢之後再刪除。
    <target name="build-war" depends="build-project" description="--> description">
    <copy todir="${basedir}/WebContent/WEB-INF/lib" flatten="true">
    <path refid="Apache Commons Logging-1.1.0.userclasspath"/>
    <path refid="Apache Log4J-1.2.13.userclasspath"/>
    <path refid="Wicket-1.2.6.userclasspath"/>
    </copy>
    <war destfile="wicket.war" webxml="${basedir}/WebContent/WEB-INF/web.xml">
    <lib dir="${basedir}/WebContent/WEB-INF/lib">
    <classes dir="${basedir}/build/classes">
    </war>
    <delete>
    <fileset dir="${basedir}/WebContent/WEB-INF/lib" includes="**/*">
    </delete>
    </target>
透過以上的語法,可以有效簡化jar, javadoc, war的產生。
P.S: 以上語法內的task與attribute的用法與意義,請查閱Ant的說明文件

2007年8月17日 星期五

Wicket 心得(2)

  1. 動態加入javascript:
    1. 呼叫AjaxRequestTarget的appendJavascript(),不過僅能在Ajax component上使用
    2. 使用JavascriptUtils.writeJavascript(), 需要有wicket.Response作為參數。一般可以透過實做IHeaderContributor來獲得。然後在需要加入Javascript的page,加入以下code:
      add(new HeaderContributor(new YourHeaderContributorImpl()));
      即可
  2. Component可以透過override onComponentTag()來將原本的HTML tag進行置換。例如把無效的AjaxLink的<a>換成以<span>替代
  3. 關閉輸出wicket id到頁面:
    getMarkupSettings().setStripWicketTags(true);
  4. Wicket extension中的Wizard並不支援Ajax,因此當在ModalWindow中使用時,無法再按下Finish時關閉ModalWindow,解決方式:
    繼承AjaxSubmitButton,並在onSubmit()中呼叫WizardModel的finsih()。另外,由於AjaxSubmit似乎沒辦法透過setEnabled(false)來關閉,所以也必須override onComponentTag()來對HTML tag添加 disabled屬性

2007年6月21日 星期四

Wicket 心得

1. 建立簡單的Ajax component: 對一般的component加上AjaxEventBehavior來達成,範例:
 DropDownChoice choice=new DropDownChoice(...);
choice.add(new AjaxEventBehavior("onchange") {
protected void onEvent(AjaxRequestTarget target) {
System.out.println("ajax here!");
}
}
或是自行繼承AbstractDefaultAjaxBehavior並進行必要改寫onComponentTag()與respond()來加入所需要的javascript與處理行為
2. 較複雜的Ajax component可以透過組合多個component來達成,可以參考wicket-extension裡的AjaxEditableLabel
3. 部份特效可以借助CSS來達成,例如tooltip
4. Override onComponentTag()可以修改tag本身的資訊,包含name,也可以加上其他attribute
5. 目前wicket中提供的大部分component都已經將onComponentTag()與onComponentTagBody()定義成final,所以要加上額外的attribute必須使用AttributeModifier或是AttributeAppender
6. ListView不只可以使用於table與<div>,亦可使用於<span>。若是需要橫向排列的效果,便需要使用<span>

2007年6月10日 星期日

Hibnerate Annotation心得筆記

1. 原來JPA的Annotation的定義:@OneToOne跟@ManyToOne都是預設是Fetch.Eager
2. Hibernate Annotation可以用@LazyToOne來設定@OneToOne或是@ManyToOne的Lazy fetch的方式(通常都是用LazyToOneOption.PROXY吧)。但是,對於@OneToOne來說,怎麼設定似乎都是無用的,Hibernate的實做,似乎還是會進行non-lazy fetch.
3. 若是@ManyToOne設定為fetch=FetchMode.Lazy的話,則該relation必須在使用Hibernate.initialize()之後,或是在查詢時加上fetch的語法才能在session關閉後取得.
4. 目前用Hibernate annotation extension中的@Index想幫一些relation加上db index,但是似乎不行。

結論,JPA的定義與原本的Hibernate作法似乎是有不少差異,而且就功能上來看,JPA的annotation比起Hibernate也就為不足

2007年2月6日 星期二

Hibernate Secindary Level Cache 與Persistence Entity的繼承關係的問題

使用Hibernate的好處除了可以使用ORM的觀念來開發系統之外,另一個優點就是它提供了可以設定的cache機制 ,尤其是Secondary Level Cache。在對於經常使用到primary key來查詢的情況下,是有蠻大的幫助的。不過,最近在專案內使用卻發現一個問題。就是在有繼承關係的entity中,可能會造成一些問題:
例如,AUser跟BUser都繼承自User,而三者的繼承作法是採用Join的作法,也就是說AUser跟BUser都是參考到User的table。在還沒有使用Secondary level cache時,查詢一個已在AUser,但是不在BUser的entity,若是先以AUser的型態查詢,然後再以BUser的型態查詢則會正確的回傳null。但是當對User啟用Second level cache時,就會出現ClassCastException。原因應該是cache是以primary key來作為cache的key,所以第一次查詢的結果會被存入cache,但是第二次的查詢則因為是直接由cache取得,所以會得到AUser,但是由於查詢的型別是BUser,所以會出會轉型錯誤。

當然,這問題對於一般的系統可能是很難碰到。但是如果對於有應用到繼承觀念的系統的話,也許是需要特別注意的。