2010-12-29

共用EJB - Remote 篇

先前寫了共用EJB - Local 篇. 後來,要把他運用在同一個container Server , 不同的EJB3 或者 web 上時, 發現他根本不能work . 會一直跑出 Wrong target Eroror message. 研究了一下, 發現應該是不同的 EJB package 不能共用 local ,要用 remote 的方式.

另外就是, 如果你是不同的EJB package 採用 resource inject 方式, 那如果被引用的 EJB shutdown. 引用別人的 EJB or web 在 jboss 6 上面會掛掉, 得重新啟動才有辦法繼續運作. 所以. 建議方式是採用 InitialContext 方式去 引用外部的 EJB resource, 不然一天到晚你要記得去 restart .


在 Session Bean 部份的寫法


package test;

import javax.ejb.Stateless;

@Stateless(name="test/TestSessionBean")
public class TestSessionBean implements TestSessionBeanRLocal,TestSessionBeanRemote {

@Override
public void test() {
System.out.println("Hello Test Bean");
}

}


引用 共用 SessionBean 的寫法(Resource Injection)


package test;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import test.TestSessionBeanLocal;


@Stateless
public class TestBean implements TestBeanLocal {

@EJB(mappedName = "test/TestSessionBean/remote")
TestSessionBeanRemote testBean;

@Override
public void testBean() {
testBean.test();
}
}



上面標注紅色字體部份, 就是在jboss 下面可以正常運做的 code.
主要在 引用共用session bean 那邊的設定, 需要多加一個 /remote , 這是跟local 不一樣的地方.



引用 共用 SessionBean 的寫法(jndi lookup)


package test;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import test.TestSessionBeanLocal;
import javax.naming.InitialContext;

@Stateless
public class TestBean implements TestBeanLocal {

TestSessionBeanRemote testBean;

@Override
public void testBean() {
InitialContext ctx = new InitialContext();
testBean = (TestSessionBeanRemote)ctx.lookup("
test/TestSessionBean/remote");
testBean.test();
}
}

2010-12-03

Query by Page on JPA

在Query 大量的資料時, 如果沒有將資料作 page的動作, 一次抓回來會在吃掉大量的記憶體去產生相關的 class. 如果, 只需要部份資料慢慢取回處理, 可以採用page 的方式去截去資料.
在 JPA 中, Query 這一個物件有
  • setFirstResult(int startPosition)
  • setMaxResults(int maxResult)
運用這兩個 method 就可以達到 Page Query 的方法了.

在 Pro JPA 2 page. 194 提到, 可用 stateful session 來達到用 存放在 http session 中, 讓 jsp or javaServer faces 可以 作到 PageQuery


@Stateful
public class ResultPagerBean implements ResultPager {
@PersistenceContext(unitName="QueryPaging")
private EntityManager em;

private String reportQueryName;
private long currentPage;
private long maxResults;
private long pageSize;

public long getPageSize() {
return pageSize;
}
public long getMaxPages() {
return maxResults / pageSize;
}

public void init(long pageSize, String countQueryName,
String reportQueryName) {
this.pageSize = pageSize;
this.reportQueryName = reportQueryName;
maxResults = em.createNamedQuery(countQueryName, Long.class)
.getSingleResult();
currentPage = 0;
}

public List getCurrentResults() {
return em.createNamedQuery(reportQueryName)
.setFirstResult(currentPage * pageSize)
.setMaxResults(pageSize)
.getResultList();
}

public void next() {
currentPage ;
}

public void previous() {
currentPage--;
if (currentPage < 0) {
currentPage = 0;
}
}

public long getCurrentPage() {
return currentPage;
}

public void setCurrentPage(long currentPage) {
this.currentPage = currentPage;
}

@Remove
public void finished() {}
}

Read-Only entity for JPA

開發系統時會有一些只有Query 不會去 modify 的db query . 在jpa 中 每一個entity 產生後都是會被 managed. 所以, 要怎樣作到 不會被 managed 來造乘Query locked. 在Pro JPA 2 page.191 有提到一個 Optimizing Read-Only Queries 提到兩種方式 :
1. Outside of a Transaction : 將 entity query 的程式段落, 不要包涵在transaction 中. 但是對於 application-managed or extends entity managers 就不是用了.
2. 將Query 封裝在一個 stateless session facade , 在method 宣告 NOT_SUPPORTED. 只要執行到這一個method code, 任何 transaction 都會被 suspended. 已達到read-only 最佳化.

@Stateless
public class QueryServiceBean implements QueryService {
@PersistenceContext(unitName="EmployeeService")
EntityManager em;

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<Department> findAllDepartmentsDetached() {
return em.createQuery("SELECT d FROM Department d",
Department.class)
.getResultList();
}

// ...
}

2010-11-29

共用EJB - Local 篇

之前寫 jee application 都是將所有用到的 SessionBean 全部放在同一個 application ear檔案. 就會出現在 同一個 jee container 上面出現很多相同的 SessionBean 掛在不同的 ear 下面. 這樣整體吃掉的資源其實是很大的, 而且不能把一些共用的Session Bean 拉出來共用.

個人認為 jee 應該不是這樣用的. 所以, 利用一些空閒時間研究一下. 下面是我在 Jboss 下面試出來的, 寫下來自己參考.

在 Session Bean 部份的寫法


package test;

import javax.ejb.Stateless;

@Stateless(name="test/TestSessionBean")
public class TestSessionBean implements TestSessionBeanLocal {

@Override
public void test() {
System.out.println("Hello Test Bean");
}

}


引用 共用 SessionBean 的寫法


package test;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import test.TestSessionBeanLocal;


@Stateless
public class TestBean implements TestBeanLocal {

@EJB(mappedName = "test/TestSessionBean/local")
TestSessionBean testBean;

@Override
public void testBean() {
testBean.test();
}
}



上面標注紅色字體部份, 就是在jboss 下面可以正常運做的 code.
主要在 引用共用session bean 那邊的設定, 需要多加一個 /local , 這是從 jboss jndi tree view 看得. 或許有其他寫法, 但是我還沒有研究出來.

2010-10-28

Eclipse Link JPA on jboss

之前使用 jpa + jta 的時候, 使用到transaction commit 時, 沒有詳細去注意各個 jpa 時做的問題. 但是, 今天就遇到一個問題, 讓目前開發的時候, 如果針對同一個 table 要作資料更新, 才發現不一樣的 jpa 實做會有不一樣的結果.
同一份 EJB 的程式放在 Jboss 6.0 and weblogic 會有不一樣的結果. Jboss 會出現 rollback , 但是 weblogic 不會. 這下又有趣了.
還好, 從Exception 中我看到 jboss 是 org.hibernate 的Exception. 就讓我想到會不會是 jpa 實做的不同導致的. 果然, weblogic 不是採用 hibernate jpa , 是用 eclipse link jpa.
有了這一條線索, 就上網找資料如何讓 jboss 可以採用 eclipse link jpa. EclipseLink/Examples/JPA/JBoss Web Tutorial 這一篇有講到, 但是有一些不一樣.
整個做完以後, eclipselink 做的 有些地方比 hibernate 好.

作法
  1. 下載 eclipse link jpa
  2. 將 eclipselink.jar 放入 ${jboss_home}/server/${server_name}/lib (這邊個上面的文章放的地方不太一樣, 因為我放過 有問題)
  3. 修改
    persistence.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="DefaultDB" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>java:jdbc/DefaultDB</jta-data-source>
    <class>entity.testBean</class>
    <properties>
    <property name="eclipselink.target-server" value="JBoss"/>
    <property name="eclipselink.target-database" value="Oracle"/>
    </properties>
    </persistence-unit>
    </persistence>


provider 之前沒有設定 jboss 會直接抓 container server 的, 現在在這邊指定就可以讓jpa 使用設定的 jpa implements.

eclipselink.target-server 這一個設定值一定要做, 尤其在 jboss server上面, 前沒有做的時候. 一直跑出 No Transaction is currently. 應該是沒有設定 eclipselink 沒有辦法去抓到 jboss xa manager.
eclipselink.target-database 這一個建議要設定, 開始的時候使用Oracle 沒有設定是沒有問題的. 但是轉成 MSSQL 後, 就會有問題. 設定值參考 Sun GlassFish Enterprise Server v3 Prelude Developer's Guide , Chapter 6 Using the Java Persistence API -Specifying the Database 裡頭有各家 DB 的設定值.



詳細問題
在Oracle 設計Table 的時候, 除了會設定primary key 以外, 還可針對欄位作一些 check rule. 現在遇到問題就是, 當我要作一筆 Transaction, 所做的動作為 :
  1. 找尋就有資料是否有存在的組合.
  2. 有符合的資料, 就將資料欄位 isAtvice 從 true 改為 false.
  3. Insert 新的資料進去
在weblogic 中, 這樣一個transaction 是可以完成的. 但是轉換到 jboss 時, 會出現 check rule Exception.
問題後來發現就是出在 hibernate 在處理 transaction 的方法, 跟 eclipselink 處理不同, 所以相同的DB 會出現不同的結果.

參考文獻
  1. Integrating EclipseLink with an Application Server (ELUG)
  2. EclipseLink/Examples/JPA/JBoss Web Tutorial
  3. Sun GlassFish Enterprise Server v3 Prelude Developer's Guide , Chapter 6 Using the Java Persistence API -Specifying the Database

2010-10-25

SQLite on SQuirrel

為了要處理 firefox 資料肥大的問題, 得進去firefox 的place.sqlite看一下.
找了一下, 看有沒有 可以在 squirrel上面可以直接 使用的方法.

最簡單就是找到 sqlite jdbc driver 裝上去.
下載
1. SQLiteJDBC

設定

  1. Click on the Drivers tab on the left, and add a new driver

      Name : SQLite Driver
      Example URL : jdbc:sqlite://
      Website URL: http://www.zentus.com/sqlitejdbc/


  2. Click on the Extra Class Path tab. Click the Add button, then navigate and select the sqlitejdbc jar file.
  3. Click List Drivers and org.sqlite.JDBC will appear in the Class Name field.
  4. Click Button[Ok].

2010-09-29

log4j on Jboss

最近把 project 到了一個段落, 要回來處理一下 log 的問題.
看了很多篇文章, 都發現jboss 上面 要跑含 log4j 的code 像是會有點問題. 看很多網頁, 沒有真的去試試看還不知道會是怎樣. 是了一兩個在 jboss 5.0 jboss 4.0 的解決方案, 好像不是適用於 jboss 6.0 M3.
就硬著把 ${JBOSS_HOME}/server/default/deply 看一輪, 發現到有一個檔案 jboss-logging.xml 裡面有用到jboss 自己的 log , 還有幾個appended 是用 log4j. 想說那就來試試看吧.
想說不用太複雜可以跑就好, 就找 ${JBOSS_HOME}/server/standard/ 來改好了. 要把log4j.jar 放見 ${JBOSS_HOME}/server/stardand/lib 裡面, 發現哇勒 裡面已經有放 一個log4j.jar. 跟在default裡面的lib 不一樣, default/lib 裡面一個東西都沒有, 這邊居然有. 簡單寫一個 web app, 用到log4j 去放.

修改 ${JBOSS_HOME}/server/stardand/deploy/jboss-logging.xml
1. 我是選用 他原有的 FILE 下去修改 , 新增到 檔案裡頭

<size-rotating-file-handler
file-name="${jboss.server.log.dir}/app.log"
name="FILE-APP"
autoflush="true"
append="true"
rotate-size="500k"
max-backup-index="5">

<error-manager>
<only-once/>
</error-manager>

<formatter>
<pattern-formatter pattern="%d %-5p [%c] (%t) %m%n"/>
</formatter>
</size-rotating-file-handler>

2. 新增相對的 logger

<logger category="test.logger">
<level name="DEBUG"/>
<handlers>
<handler-ref name="CONSOLE"/>
<handler-ref name="FILE-APP"/>
</handlers>
</logger>

3. 撰寫測試 log4j 的code

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;

@WebServlet(name = "Test", urlPatterns = {"/Test"})
public class Test extends HttpServlet {

private static Logger log = Logger.getLogger("test.logger");

/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
log.debug("Log Debug");
log.info("Log Info");
} finally {
out.close();
}
}

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Handles the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Short description";
}// </editor-fold>
}



測試一下, 發現很正常, 可以在jboss container 下面, 正常的使用.不用像 jboss docs 裡面寫得, 得用 jboss 包過得 Logger 下去做事情.

ps: 測試的過程中, 我將 standard/lib/log4j.jar copy to default/lib/ 來使用

2010-09-09

JBoss 啟動參數

一開始裝好 jboss 大概都只會用 ${JBOSS_HOME}/bin/run.bat. 這樣的用法大概就是啟動 ${JBOSS_HOME}/server/default 的設定. 不過現在 已經從 Installation and getting started guide 研究出下參數啟動.

run.bat -b 192.168.1.1
這樣的下法, 可以讓server 以外的可以連近來, 最早在做測試的時候都是用localhost , 直到要連接兩台的時候發現怎樣都過不去. 以為是防火牆, 還是那個東西沒有打開. 後來發現, 原來jboss 初始值是讓你在 開發用的. 要等到正式啟用在給他相對應的ip , 這是為了安全性考量.

run.bat -c stardand
這樣的下法, 就可以啟動 ${JBOSS_HOME}/server/stardand 的server .

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());


}
}

2010-07-23

吃紅色火龍果 好像有助 排便

這個禮拜 肚子都漲漲悶悶的.. 精神也不是很好....
就在 昨天晚上 吃了 鄰居送的 紅色火龍果...
今天早上 一到公司 就 一切順暢... 量 還蠻多的..
快到中午又順暢了一次, 但是不是 拉肚子喔..
現在精神就感覺好很多, 比起前幾天的肚子悶悶 跟 精神不繼. 大概 宿便太多了會有影響.

所以, 大概以後有這樣問題. 就來吃紅色火龍果摟..