2010-08-31

Jboss JMS Bridge (Jboss to Jboss)

今天開始測試 Jboss JMS Bridge.
看過不少的document後, 看起來 jboss bridge 是用內建JNDI 幫你連接到 Remote JMS Server. 等於省掉了程式開發者自己連接 JNDI等等的 JMS 事項. 另外一個好處就是, Local and Remote 各有一個Queue , 然後 AP server 幫你作保證送達的工作.
下面用到的範例都是從 HornetQ Example 拿出來改得, 因為有些並非原廠文件寫得那樣.

先來運用原廠的 單機兩個Queue作 JMS bridge.
  • /queue/source 是用來接收client 送出來的 message
  • /queue/target 是用來接收 /queue/source 轉送過來的message
  • jms-bridge-jboss-beans.xml 是在jee server 下面 設定的bridge beans

首先 在 hornetq-jms.xml 新增兩個 queue

<queue name="source">
<entry name="/queue/source"/>
</queue>
<queue name="target">
<entry name="/queue/target"/>
</queue>

新增一個 jms-bridge-jboss-beans.xml 到 ${JBOSS_HOME}/server/default/deploy/hornetq
要注意的地方, 在原廠的設定中 target 的 user and password 是沒有設定的. 沒有設定是沒辦法正常使用的, 所以我把 guest/guest 加進去才可以正常運作.

<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns="urn:jboss:bean-deployer:2.0">

<bean name="JMSBridge" class="org.hornetq.jms.bridge.impl.JMSBridgeImpl">
<!-- HornetQ must be started before the bridge -->
<depends>HornetQServer</depends>
<constructor>
<!-- Source ConnectionFactory Factory -->
<parameter>
<inject bean="SourceCFF"/>
</parameter>
<!-- Target ConnectionFactory Factory -->
<parameter>
<inject bean="TargetCFF"/>
</parameter>
<!-- Source DestinationFactory -->
<parameter>
<inject bean="SourceDestinationFactory"/>
</parameter>
<!-- Target DestinationFactory -->
<parameter>
<inject bean="TargetDestinationFactory"/>
</parameter>
<!-- Source username (no username here) -->
<parameter><null /></parameter>
<!-- Source password (no password here)-->
<parameter><null /></parameter>
<!-- Target username (no username here)-->
<parameter>guest</parameter>
<!-- Target password (no password here)-->
<parameter>guest</parameter>
<!-- Selector -->
<parameter><null /></parameter>
<!-- Interval to retry in case of failure (in ms) -->
<parameter>5000</parameter>
<!-- Maximum number of retries to connect to the source and target -->
<parameter>10</parameter>
<!-- Quality of service -->
<parameter>ONCE_AND_ONLY_ONCE</parameter>
<!-- Maximum batch size -->
<parameter>1</parameter>
<!-- Maximum batch time (-1 means infinite) -->
<parameter>-1</parameter>
<!-- Subscription name (no subscription name here)-->
<parameter><null /></parameter>
<!-- client ID (no client ID here)-->
<parameter><null /></parameter>
<!-- concatenate JMS messageID to the target's message header -->
<parameter>true</parameter>
<!-- register the JMS Bridge in the AS MBeanServer -->
<parameter>
<inject bean="TheMBeanServer"/>
</parameter>
<parameter>org.hornetq:service=JMSBridge</parameter>
</constructor>
<property name="transactionManager">
<inject bean="RealTransactionManager"/>
</property>
<!--<property name="transactionManagerLocatorClass">org.hornetq.integration.jboss.tm.JBoss5TransactionManagerLocator</property>
<property name="transactionManagerLocatorMethod">getTm</property>-->
</bean>

<!-- SourceCFF describes the ConnectionFactory used to connect to the source destination -->
<bean name="SourceCFF" class="org.hornetq.jms.bridge.impl.JNDIConnectionFactoryFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/ConnectionFactory</parameter>
</constructor>
</bean>

<!-- TargetCFF describes the ConnectionFactory used to connect to the target destination -->
<bean name="TargetCFF" class="org.hornetq.jms.bridge.impl.JNDIConnectionFactoryFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/ConnectionFactory</parameter>
</constructor>
</bean>

<!-- SourceDestinationFactory describes the Destination used as the source -->
<bean name="SourceDestinationFactory" class="org.hornetq.jms.bridge.impl.JNDIDestinationFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/queue/source</parameter>
</constructor>
</bean>

<!-- TargetDestinationFactory describes the Destination used as the target -->
<bean name="TargetDestinationFactory" class="org.hornetq.jms.bridge.impl.JNDIDestinationFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/queue/target</parameter>
</constructor>
</bean>

<!-- JNDI is a Hashtable containing the JNDI properties required -->
<!-- to connect to the sources and targets JMS resrouces -->
<bean name="JNDI" class="java.util.Hashtable">
<constructor class="java.util.Map">
<map class="java.util.Hashtable" keyClass="java.lang.String"
valueClass="java.lang.String">
<entry>
<key>java.naming.factory.initial</key>
<value>org.jnp.interfaces.NamingContextFactory</value>
</entry>
<entry>
<key>java.naming.provider.url</key>
<value>jnp://localhost:1099</value>
</entry>
<entry>
<key>java.naming.factory.url.pkgs</key>
<value>org.jboss.naming:org.jnp.interfaces"</value>
</entry>
</map>
</constructor>
</bean>

</deployment>


接下測試 兩台不同 AP Server 作為 bridge
  • AP1 新增 /queue/source
  • AP2 新增 /queue/target
  • AP1 新增 jms-bridge-jboss-beans.xml
這一部份直接進入 AP1 jms-bridge-jboss-beans.xml 的設定. 其實跟單機的部份很像. 只不過jndi 的部份需要另外新增. 新增了一個 JNDI-Remote 的 設定檔, 就是告知 AP1 如何連接到 AP2 的JNDI 設定. 當然, target 的 ConnectionFactory and Queue 都要改指向新的 jndi 設定.
在測試的過程中, 發現 第一次設定好以後. 發現message 根本沒有送過去, 就這樣反覆的把AP1 shutdown and start 好幾次以後, 突然message 都傳送到 AP2 了. 搞不清楚怎樣發生的就在繼續測試下去. 最後得到一個結論, 那就是Ap Start 以後, 要接過一小段時間以後, 才會開始傳送Message 而不是 馬上就動工.


<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns="urn:jboss:bean-deployer:2.0">

<bean name="JMSBridge" class="org.hornetq.jms.bridge.impl.JMSBridgeImpl">
<!-- HornetQ must be started before the bridge -->
<depends>HornetQServer</depends>
<constructor>
<!-- Source ConnectionFactory Factory -->
<parameter>
<inject bean="SourceCFF"/>
</parameter>
<!-- Target ConnectionFactory Factory -->
<parameter>
<inject bean="TargetCFF"/>
</parameter>
<!-- Source DestinationFactory -->
<parameter>
<inject bean="SourceDestinationFactory"/>
</parameter>
<!-- Target DestinationFactory -->
<parameter>
<inject bean="TargetDestinationFactory"/>
</parameter>
<!-- Source username (no username here) -->
<parameter><null /></parameter>
<!-- Source password (no password here)-->
<parameter><null /></parameter>
<!-- Target username (no username here)-->
<parameter>guest</parameter>
<!-- Target password (no password here)-->
<parameter>guest</parameter>
<!-- Selector -->
<parameter><null /></parameter>
<!-- Interval to retry in case of failure (in ms) -->
<parameter>5000</parameter>
<!-- Maximum number of retries to connect to the source and target -->
<parameter>10</parameter>
<!-- Quality of service -->
<parameter>ONCE_AND_ONLY_ONCE</parameter>
<!-- Maximum batch size -->
<parameter>1</parameter>
<!-- Maximum batch time (-1 means infinite) -->
<parameter>-1</parameter>
<!-- Subscription name (no subscription name here)-->
<parameter><null /></parameter>
<!-- client ID (no client ID here)-->
<parameter><null /></parameter>
<!-- concatenate JMS messageID to the target's message header -->
<parameter>true</parameter>
<!-- register the JMS Bridge in the AS MBeanServer -->
<parameter>
<inject bean="TheMBeanServer"/>
</parameter>
<parameter>org.hornetq:service=JMSBridge</parameter>
</constructor>
<property name="transactionManager">
<inject bean="RealTransactionManager"/>
</property>
<!--<property name="transactionManagerLocatorClass">org.hornetq.integration.jboss.tm.JBoss5TransactionManagerLocator</property>
<property name="transactionManagerLocatorMethod">getTm</property>-->
</bean>

<!-- SourceCFF describes the ConnectionFactory used to connect to the source destination -->
<bean name="SourceCFF" class="org.hornetq.jms.bridge.impl.JNDIConnectionFactoryFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/ConnectionFactory</parameter>
</constructor>
</bean>

<!-- TargetCFF describes the ConnectionFactory used to connect to the target destination -->
<bean name="TargetCFF" class="org.hornetq.jms.bridge.impl.JNDIConnectionFactoryFactory">
<constructor>
<parameter>
<inject bean="JNDI-Remote" />
</parameter>
<parameter>/ConnectionFactory</parameter>
</constructor>
</bean>

<!-- SourceDestinationFactory describes the Destination used as the source -->
<bean name="SourceDestinationFactory" class="org.hornetq.jms.bridge.impl.JNDIDestinationFactory">
<constructor>
<parameter>
<inject bean="JNDI" />
</parameter>
<parameter>/queue/source</parameter>
</constructor>
</bean>

<!-- TargetDestinationFactory describes the Destination used as the target -->
<bean name="TargetDestinationFactory" class="org.hornetq.jms.bridge.impl.JNDIDestinationFactory">
<constructor>
<parameter>
<inject bean="JNDI-Remote" />
</parameter>
<parameter>/queue/target</parameter>
</constructor>
</bean>

<!-- JNDI is a Hashtable containing the JNDI properties required -->
<!-- to connect to the sources and targets JMS resrouces -->
<bean name="JNDI" class="java.util.Hashtable">
<constructor class="java.util.Map">
<map class="java.util.Hashtable" keyClass="java.lang.String"
valueClass="java.lang.String">
<entry>
<key>java.naming.factory.initial</key>
<value>org.jnp.interfaces.NamingContextFactory</value>
</entry>
<entry>
<key>java.naming.provider.url</key>
<value>jnp://localhost:1099</value>
</entry>
<entry>
<key>java.naming.factory.url.pkgs</key>
<value>org.jboss.naming:org.jnp.interfaces"</value>
</entry>
</map>
</constructor>
</bean>
<bean name="JNDI-Remote" class="java.util.Hashtable">
<constructor class="java.util.Map">
<map class="java.util.Hashtable" keyClass="java.lang.String"
valueClass="java.lang.String">
<entry>
<key>java.naming.factory.initial</key>
<value>org.jnp.interfaces.NamingContextFactory</value>
</entry>
<entry>
<key>java.naming.provider.url</key>
<value>jnp://localhost:2099</value>
</entry>
<entry>
<key>java.naming.factory.url.pkgs</key>
<value>org.jboss.naming:org.jnp.interfaces"</value>
</entry>
</map>
</constructor>
</bean>
</deployment>

2010-08-23

設定 Oracle XA DataSource

直接在 ${Jboss}\server\default\deploy 下面新增 oracel-xa-ds.xml
Jboss 會自動生效 不用重新開機

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<xa-datasource>
<jndi-name>jdbc/TestDataSource</jndi-name>
<!-- uncomment to enable interleaving <interleaving/> -->
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:@localhost:1521:xe</xa-datasource-property>
<xa-datasource-property name="User">root</xa-datasource-property>
<xa-datasource-property name="Password">root</xa-datasource-property>
<!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->
<!--valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name-->
<!-- Checks the Oracle error codes and messages for fatal errors -->
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<!-- Oracles XA datasource cannot reuse a connection outside a transaction once enlisted in a global transaction and vice-versa -->
<no-tx-separate-pools/>

<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>Oracle10i</type-mapping>
</metadata>
</xa-datasource>

<mbean code="org.jboss.resource.adapter.jdbc.vendor.OracleXAExceptionFormatter"
name="jboss.jca:service=OracleXAExceptionFormatter">
<depends optional-attribute-name="TransactionManagerService">jboss:service=TransactionManager</depends>
</mbean>

</datasources>

2010-08-18

Jboss 安裝紀錄

windows 下載與安裝
  1. 下載 jboss-6.0.0.20100721-M4, 直接解開找一個路徑放就可以了.
  2. 設定環境變數 JBOSS_HOME 到 ${JOSS_DIR}, 啟動的時候會用到.
  3. 不建議放在 "c:\Program Files" 下面, 因為會跑不動. 大概目錄有空格就會跑不動.
  4. ${JBOSS_DIR}\bin\run.bat , 跑得起來 沒有任何錯誤. 就是已經開起了.
  5. http://localhost:8080 看得到就是成功了, 選用 admin console.
  6. admin/admin 就可以看到 jboss ap server 的管理介面.

Session Bean 測試
測試過後, 基本上可以 在glassfish and weblogic 上面跑得簡易Session, 都是可以正常運作.

Message Driven Bean測試
  1. 1. 設定 ConnectionFactory 和 Queue 都可以透過 admin console. 只是 我不知道設定檔存在哪邊. 從${JBOSS_DIR}\server\default\deploy\hornetq 裡頭看不出來, 透過admin console 的設定存放在哪邊. 所以後來我統統用自己去修改 ${JBOSS_DIR}\server\default\deploy\hornetq 的所有設定檔.
  2. 2. 佈署 MDB(Message Driver Bean). 把glassfish and weblogic 都會過得 MDB 透過 admin console 放去, 就出現錯誤啦 .
  3. 變裝前
    @MessageDriven(mappedName = "jms/TestQueue", activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
    })

    變裝後

    @MessageDriven( activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "/jms/TestQueue"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
    })

    差距就在他不吃 mappedName 的設定, 要改寫在 ActivationConfigProperty 裡頭. 不過我想如果改寫在 deploy jboss.xml 裡頭會比較好一點.

  4. 事情不是這樣簡單的, 繼續下去吧. 拿去deploy 還是會繼續發生問題的. 錯誤訊息一直跟我說user=null,問題點看了很久,還是看不出來問題出在哪邊. 所以, google 大神請了老半天, 有看沒有懂. 怎麼辦, 老大交待要選一個, 還要能夠用的.
    只好下在hornetq 來看摟, ${hornetq}\example 裡面有很多範例可以慢慢欣賞. 就直接切進去 ${hornetq}\example\javaee\ 來找答案了. 看了老半天, 也沒發現不一樣的. 找兩天, 幾乎沒有看到任何文章或者解釋可以說明的.
    就在要放棄的時候, 想說那看一下 ${hornetq}\example\jms. 發現一個怪現象, 幾乎每一個jms example 的設定檔裡面, 都會多出一個 hornetq-users.xml. 好奇殺死貓, 打開來看, 哇老天壓.....

    <configuration xmlns="urn:hornetq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:hornetq /schema/hornetq-users.xsd">
    <!-- the default user. this is used where username is null-->
    <defaultuser name="guest" password="guest">
    <role name="guest"/>
    </defaultuser>
    </configuration>

    有沒有看到 哪行註解.... the default user. this is used where username is null 就直接把檔案放進 ${JBOSS_DIR}\server\default\deploy\hornetq. 再把 server 重開, 安裝測試檔. 哇...過了.... 還我兩天的青春壓...


  5. 很多事情高興一天就好了, 沒想到我高興不到半小時, 就繼續陷入苦戰了. 可以安裝上AP 不代表可以正常的跑. 安裝上去以後, 總是要測試一下 Queue 到底可不可用, 能不能正常作用. 就寫了一個簡單的 Session 做為 Message Provider, 測試一下 到底資料可不可以透過 Queue 送給 MDB. 安裝後, 呼叫Session送資料到 Queue. 沒想到 console 傳回了一個要命的error.

    [STDERR] Caused by: java.lang.ClassNotFoundException: org.hornetq.integration.transports.netty.NettyConnectorFactory from BaseClassLoader@12e
    bf9a{vfs:///C:/jboss-6.0.0.20100721-M4/server/default/deploy/TestQueue.ear}

    看到這樣的錯誤, 第一個反應是不是少了那一個 jar 沒有放進去 server lib 裡面. 不過沒有到底壓, Jboss 6 已經是內建 hornetq , 不應該會少東西壓. 網路上查了一下, 也看到他們說已經把改補的在五月補上去了壓, 我是抓七月的檔壓. 又花了一天在找資料作測試. 還是一樣徒勞無功. 那就休息吧... 不要在做測試了
    噹噹, 兩天過去了. 不行都已經奮戰了五天了, 不能這樣就放棄 Jboss. 把org.hornetq.integration.transports.netty.NettyConnectorFactory放進 google 找看看好了. google 找到的第一文件 The HornetQ Team Blog: Understanding Connectors & Acceptors 看起來跟解問題沒有關係, 但是列在下面的幾篇又都已經看過了, 反正沒希望了, 就看看 Connectors and Acceptores 有何差別也好學一下人家的設計好了.
    好學不倦的人就是有福了, 文章就直接先解釋Acceptores and Connectors 在的作用. 接下來就講到一個東西 "HornetQ defines 2 types of acceptor/connector : invm and netty". 嘿嘿這解釋就讓我瞭解了, 之前再看設定檔老是看到 netty 不知道怎樣使用的, 沒想到 invm 才是另外一個重點.
    我的測試機上面, AP 和 hornetq 是同一台機器, 而且 hornetq 是設定 embedden 模式, 所以ap and hornetq 會長在同一個vm. (hornetq 還有local and remote mode, 就是一個跟 ap 會同生死但是是分別兩個vm , remote就不會跟AP同生死, 當然也不是在同一個vm. ). 所以按照文章的講法, 我猜想是我的設定檔指定錯connectors 了, 回去翻我當時的設定檔 hornetq-jms.xml.

    <connection-factory name="NettyConnectionFactory">
    <connectors>
    <connector-ref connector-name="netty"/>
    </connectors>
    <entries>
    <entry name="/ConnectionFactory"/>
    <entry name="/XAConnectionFactory"/>
    <entry name="jms/ConnectionFactory"/>
    </entries>
    </connection-factory>
    <connection-factory name="InVMConnectionFactory">
    <connectors>
    <connector-ref connector-name="in-vm"/>
    </connectors>
    <entries>
    <entry name="java:/ConnectionFactory"/>
    <entry name="java:/XAConnectionFactory"/>
    </entries>
    </connection-factory>

    作個實驗修改後

    <connection-factory name="NettyConnectionFactory">
    <connectors>
    <connector-ref connector-name="netty"/>
    </connectors>
    <entries>
    <entry name="/ConnectionFactory"/>
    <entry name="/XAConnectionFactory"/>
    </entries>
    </connection-factory>
    <connection-factory name="InVMConnectionFactory">
    <connectors>
    <connector-ref connector-name="in-vm"/>
    </connectors>
    <entries>
    <entry name="java:/ConnectionFactory"/>
    <entry name="java:/XAConnectionFactory"/>
    <entry name="jms/ConnectionFactory"/>
    </entries>
    </connection-factory>

    嘿嘿, 重開server以後, 測試一下. 哇 恭喜老爺高中狀元, 留下十年寒窗的眼淚, 在console 下看到 我要看到的 Info 了.



後記
搞了一個多禮拜(不包涵六日), 終於最基本的 EJB 都可以在 Jboss上面運做了. 心裡真的爽度很高, 但是也是這樣覺得 Jboss 的設定真的很複雜, 跟weblogic and glassfish 比較起來真的差距很大, 而且現階段 jboss.org 上面的 document 也都是停留在 jboss 5的文件. 所以很多文件讀起來跟實際 jboss 6 有點落差, 光是 JMS部份就 從 messaging 轉換到 hornetq. 所以, 文件中都使以 jboss messaging 為主體, 沒辦法直接套用在 jboss 6.
另外, 在讀 hornetq 的說明也都是如何將 hornetq 掛到 jboss5 上面的說明. 只能從example 跟 hornetq user guide 來推測放在 jboss6 上面應該要如何作設定.
這篇文章就是自己 在做完一整個search and retry 的心得跟作法.

2010-08-03

JAXB : Java object to XML without XML Schema

在 jdk 6.0 上面 已經內建 JAXB. 所以直接使用就可以.
研究了一陣子, 看了很多文件. 都大概需要使用xml schema 來產生 java to xml.
不過今天看到一個文章 Generate an XML Document from an Object Model with JAXB 2裡頭有範例, 可以不用使用 XML schema. 就來試試看摟.

開發兩個 Enity Bean(Person.java, Address.java) , 還有一個測試轉換的 Test Code(PersonTest.java).
1. 在 Person 中, 會包涵 Address. 所以在 Person 定義 @XmlRootElement. 這樣Address 也會自動被轉成xml.
2. Address 作一個設定, 原本應該要產生 zipcode tag, 如果想要換成別的. 必須先定義@XmlAccessorType(XmlAccessType.FIELD), 然後在 zipcode field在定義@XmlElement(name = "zip") 這樣轉換出來的address tag 會包涵zip tag 不會是 zipcode tag.




Person.java

package jaxbtest;

import javax.xml.bind.annotation.XmlRootElement;

/**
*
* @author longtai
*/
@XmlRootElement
public class Person {
private String name;
private String email;
private Address address;

/**
* @return the name
*/
public String getName() {
return name;
}

/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}

/**
* @return the email
*/
public String getEmail() {
return email;
}

/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}

/**
* @return the address
*/
public Address getAddress() {
return address;
}

/**
* @param address the address to set
*/
public void setAddress(Address address) {
this.address = address;
}

}


Address.java

package jaxbtest;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

/**
*
* @author longtai
*/
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
private String address;
@XmlElement(name = "zip")
private String zipcode;

/**
* @return the address
*/
public String getAddress() {
return address;
}

/**
* @param address the address to set
*/
public void setAddress(String address) {
this.address = address;
}

/**
* @return the zipcode
*/
public String getZipcode() {
return zipcode;
}

/**
* @param zipcode the zipcode to set
*/
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}


PersonTest.java

package jaxbtest;

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import junit.framework.JUnit4TestAdapter;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

/**
*
* @author longtai
*/
public class PersonTest {

public PersonTest() {
}

public static junit.framework.Test suite() {
return new JUnit4TestAdapter(PersonTest.class);
}

@BeforeClass
public static void setUpClass() throws Exception {
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@Test
public void testMarshal() throws Exception {
Person p = new Person();
p.setName("longtai");
p.setEmail("longtai.tw@gmail.com");
Address a = new Address();
a.setAddress("台灣新竹");
a.setZipcode("35002");
p.setAddress(a);

StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Person.class);
Marshaller m = context.createMarshaller();
m.marshal(p, writer);

System.out.println(writer);
}

@Test
public void testUnmarshal() throws Exception
{
StringReader reader = new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><person><address><address>%u53F0%u7063%u65B0%u7AF9</address><zip>35002</zip></address><email>longtai.tw@gmail.com</email><name>longtai</name></person>");
JAXBContext context = JAXBContext.newInstance(Person.class);
Unmarshaller um = context.createUnmarshaller();
Person p = (Person)um.unmarshal(reader);

System.out.println(p.getName());
System.out.println(p.getAddress().getAddress());
System.out.println(p.getAddress().getZipcode());


}
}