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

2017年2月8日 星期三

jxls

最近試了一下
http://jxls.sourceforge.net/
他應該是算一個產生 xls 的 framework~

覺得是還不錯用。背後可以接 蠻常用的 poi 實作。
http://jxls.sourceforge.net/getting_started.html
Currently Jxls supplies two implementations of this interface in separate modules based on the well-known Apache POI and Java Excel API libraries.
只要引用不同的 maven lib 就可以換實作。

然後他把 xlsx, xls 的 method 合而為一了~~在 poi 上這是分屬兩套不同的系統,所以有解決自己刻時各自寫一套的麻煩。

還有很重要的一點是支援 el 的語法。常用的 取 bean 屬性, if ,each,都有 \(^_^)/...
看起來他還可以換 el 的語法系統。不過因為內建的已經夠用就先降子~
 http://jxls.sourceforge.net/reference/expression_language.html

也有順便把 公式 統計算區間的部份給處理好了。(之前自己刻,在那邊算自己加了幾行,要怎麼下 xls 公式的區間的麻煩問題有幫解了。)

要入門的方式,應該是抓他的 sample code 來跑,是最方便的了。
http://jxls.sourceforge.net/source_code.html
https://bitbucket.org/leonate/jxls-demo
直接跑demo 修改跟看文件說明一起看就會清楚很多!因為這個要 xls template 和 java code 結合一起看才會準(那個文件貼圖是有貼到重點,但是...就還是手動操作一下上手比較快)


個人比較喜歡用 template 方式來做 xls,所以~最後選用全部mark-up 的方式。
http://jxls.sourceforge.net/reference/excel_markup.html
另外因為有多 sheet 的需求,demo 主要都是以單一sheet 的在放,所以我就小修了一下~

xls template 的設定~


最容易忘記的的大概就是一開始,jx:area 一定要設定...不然他不知道要幫你換多大的區塊...
還有就是當表有增刪欄位時,好好檢查一下區間的範圍~囧/
他裡面的 function, Context ,都是整個xls共用的,所以是只要給一次就好了~

另外是因為我做多sheet 測試,習慣就是 template sheet 對 產出 sheet 是一對一的方式,所以會刪掉做為 template 的原本的 sheet。
(當然是jxls的架構, template area 是可以一直重覆利用的。)

Java code:
        try(InputStream is = new FileInputStream(fullpath)) {
            try (OutputStream os = new FileOutputStream(output)) {
                //init 給定最主要的處理 transformer
                Transformer transformer = TransformerFactory.createTransformer(is, os);
               
                //register function 因為有用到method,一定要註冊才能用
                JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer.getTransformationConfig().getExpressionEvaluator();
                Map<String, Object> functionMap = new HashMap<>();
                functionMap.put("fn", new ReportFunction());
                evaluator.getJexlEngine().setFunctions(functionMap);
               
                //set context 給物件資料
                Context context = new Context();
                context.putVar("data", data);
               
                //note comment: NEED define jx:area in the sheet beginning
                AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
                XlsCommentAreaBuilder.addCommandMapping("groupRow", GroupRowCommand.class);
                List<Area> xlsAreaList = areaBuilder.build();
                //取有定義的區塊出來處理
                for(int i = 0 ; i < xlsAreaList.size() ; i++)  {
                    Area xlsArea = xlsAreaList.get(i);
                    String theSheetName = xlsArea.getAreaRef().getSheetName();
                   
                    //apply
                    if(sheetNames.containsKey(theSheetName))  {
                        CellRef newSheetCell = new CellRef(sheetNames.get(theSheetName), 0, 0);
                        //產出後的區塊,要寫到xls 的那邊
                        xlsArea.applyAt(newSheetCell, context);
                        //要跑小計,要記得叫他處理公式區間
                        xlsArea.processFormulas();
                    }
                    //刪除 做為 template 的 sheet
                    transformer.deleteSheet(theSheetName);
                }
                //write
                transformer.write();
               
            }
        }

結果大概就是長降子~
多張 sheet 有出來。

(他會自動把 符合語法的 註解清掉。但是不合的會留著XD...)

看一下 if 的作用,還不錯~



2016年11月13日 星期日

apacheds API client connect with user/pwd

如果 LDAP server 允許匿名登入的話,連線上只要打 port 之類的就好
ex:
        LdapConnection connection = new LdapNetworkConnection( "127.0.0.1", 10389 );
        connection.connect();

        //do something....
      
        connection.close();

不過如果需要用 user/pwd 登入 的話,那就是要利用 config 連線
http://directory.apache.org/api/user-guide/2.1-connection-disconnection.html
然後就要記得 bind,才能再進行其他的操作
http://directory.apache.org/api/user-guide/2.2-binding-unbinding.html

目前的寫法大概是這樣~
Name: 用dn name 比較沒問題
Credentials: 就是 password...
SslProtocol:apacheDS 提供的SSL是 ldaps:// 協定。沒設定就是一般沒加密連線
(簡易寫法連線,最後面打true,就可以走 ssl)
連線上了之後要 bind...

        LdapConnectionConfig config = new LdapConnectionConfig();
        config.setLdapHost( "localhost" );
        config.setLdapPort( 10389 );
        config.setName( "cn=AddTest11,ou=users,dc=example,dc=com" );
        config.setCredentials( "t123456" );
        config.setSslProtocol("ldaps://");
       
        LdapConnection connection = new LdapNetworkConnection(config);
       
        connection.connect();
        System.out.println("connection="+connection);
       
        connection.bind(config.getName(), config.getCredentials());
       
        if(connection.exists("uid=admin,ou=system"))  {
            System.out.println("true");
        }  else  {
            System.out.println("false");
        }
       
        connection.close();

如果 client 沒有進行 bind,server 端又設定 不能匿名訪問的話,在進行search 操作時,那個 server端會有錯誤訊息XD...但client 是無感的(但是查到的 entry 會是空的,但沒看到exception)...只是理論上看 source code,應該是會丟 LdapNoPermissionException 出來的,不知為啥client沒收到O_oa...

另外理論上,看文件~ 應該是要指定 bind dn,不過好像用 .bind() 也會過~
是說,可以用普通user連線,然後 bind 到 admin,權限可以比較大!(好像sudo一樣XD)
 

2016年11月11日 星期五

apacheDS custom setting and connect with user/password

那個,相信在測試的時後,一直看到 console 叫你改 admin 的密碼~
一定會覺得很煩XD...
如果是透過 studio 的話,就是在這邊,自己來改一改...

不過是說...這個server沒動設定的話,是可以匿名(就是不打user)就連上來~
一樣是感覺非常不安全....

只是,它本身就已經是整個包好的一個 jar 檔,所以只好用 java 直接改寫了~

事先要有 那包完整的 source code(嗯,就是幾十個project 的那個)
不過我也不想改他的 source,因為改了要重包,想到maven 在那邊拼命下載 update 就 覺得人生火花(台語)..好啦,其實我也不會包=.=...也不想包,包壞了debug就累死...
所以我採用外掛取代的方式。source code 是拿來抄跟比對用的(因為文件不可靠阿Q_Q)

這方法,也是 google 到的,不知道算不算爛解(那篇是寫1.5版,method 不同,但作法方向是一樣的,就不po了~),不過我覺得那方向是一個不破壞原本的內容不錯的作法。

1. 改寫 起動 bat (還沒測好前,可以先copy來用就好了,linex 就是改.sh)
apacheds-2.0.0-M23\bin\apacheds.bat
最底下一行
java %ADS_CONTROLS% %ADS_EXTENDED_OPERATIONS% -Dlog4j.configuration="file:../instances/%INSTANCE_NAME%/conf/log4j.properties" -Dapacheds.log.dir=../instances/%INSTANCE_NAME%/log -cp %ADS_CLASSPATH% org.apache.directory.server.UberjarMain ../instances/%INSTANCE_NAME% %ACTION%

把啟動的 main method 改成自訂的 ex:

java %ADS_CONTROLS% %ADS_EXTENDED_OPERATIONS% -Dlog4j.configuration="file:../instances/%INSTANCE_NAME%/conf/log4j.properties" -Dapacheds.log.dir=../instances/%INSTANCE_NAME%/log -cp %ADS_CLASSPATH% xxx.yyy.zzz.MyStart ../instances/%INSTANCE_NAME% %ACTION%


2. copy 一隻自訂的啟動 Java... MyStart
那個 Start Server 很難寫,說真的我也不會啦,但是,有提示跟有 source code...
就是copy  org.apache.directory.server.UberjarMain 一模一樣內容的就對了~
只是改 package 跟 class name ,讓 bat 是用自訂來啟動就是了...

然後找到可以插入設定的程式碼區段,我是把他加在 start 起來後~
    public void start( String instanceDirectory )  {
        InstanceLayout layout = new InstanceLayout( instanceDirectory );

        // Creating ApacheDS service
        service = new ApacheDsService();

        // Initializing the service
        try  {
            LOG.info( "Starting the service." );
            service.start( layout );

            startShutdownListener( layout );
           
            //add my custom
            custom();
        }  catch ( Exception e )  {
            LOG.error( "Failed to start the service.", e );
            stop();
            System.exit( 1 );
        }
    }

在 complire 的時後,我是直接掛 ext lib apacheds-service-2.0.0-M23.jar 上來用,萬一有些還是會對不到的,就是用 eclipse include project 進來處理。
(用 maven 去 下相依的 apachds-all 檔並沒有比較好,抓不到的 class 更多)

3. 寫 自訂的部份~
我有印一些數值,和修改一些安全性設定的部份~
取消匿名訪問,和若是admin 密碼跟預設的一樣,就自己隨便改一下~
(改密碼那段,理論上是應該用 ModificationOperation.REPLACE,不過=.=因為之前測試寫錯了,生了好幾個password 的 attr...就先殺了再建,一次解決)

        DirectoryService ds = this.service.getDirectoryService();

        ds.setAllowAnonymousAccess(false);
        //change admin pwd to avoid some issue
        Dn adminDn = ds.getDnFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN );

        Entry adminEntry = ds.getAdminSession().lookup(adminDn);
        Value<?> userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get();
        boolean needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, userPassword.getBytes() );
        System.out.println("needToChangeAdminPassword="+needToChangeAdminPassword);
        if(needToChangeAdminPassword)  {
            Modification modify2 = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, "userPassword");
            Modification modify3 = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "userPassword", "123456" );
            ds.getAdminSession().modify(adminDn, modify2, modify3);
            System.out.println("change the password ok.");
        }
              
4. 寫好的Java 內容包成 jar 檔,放到 apacheds-2.0.0-M23\lib 裡
基本上就是那個 lib 會有兩,原本的 apacheds-service-2.0.0-M23.jar 和 自己包的 xxxx.jar

5. 啟動測試~
連線上就改一下


測試時,可以用 studio 建 connection 來試,理論上這樣一改,原本不打user/pwd 的就不行了~只是,還沒key密碼前,server 上的 log 會長很醜就是了...


apacheDS API search, add, modify and delete ( 1.0.0-RC2 )

再來就是寫 client 的部份,嗯~因為懶惰,所以我也順便用同一套的http://directory.apache.org/api/
在此使用的版本是  API 1.0.0-RC2...

看起來都也是剛 release,不過以官網文件程度來說的話,有比 ds 好T_T...雖然也是有點差異,但相比之下,真的是算好的~只是,這文件妙的是,他連之後的功能都先寫上去了!?(不過實際上我都拿最新版的了,還點不出那個 method,或關鍵字眼XD...)
https://directory.apache.org/api/user-guide/2-basic-ldap-api-usage.html

先測一下最常用的查詢,新增,和修改。基本上沒啥太大的問題。
用法原則上就是 建 client,開 connect, 然後操作(操作時會用到一些 cursor,這個用完記得要關),最後就是 close。
大概有些會很常用到的,檢查是否存在(connection.exists),等於是一個 ldap 的search...然後下filiter指定單一項目,不過可以一行就寫出來是很方便的~

建 Entry 時,要加進去的 attribute 跟內容時,可以多利用 studio 防呆做出結構來看,就比憑空想像來的簡單~dn name的一行內容,也可以從studio 的介面看到,較不容易少key層級~

在新增 Entry 時,透過 request, response。就要看response 是否成功,還是得用官方寫法: response.getLdapResult().getResultCode().equals(ResultCodeEnum.SUCCESS)
那個isDefaultSuccess()...目前還不行XD...
是說,目前碰到有出錯之類(建到重覆,改到不存在的...)的,其實都直接是出 Exception...所以,實做去接 exception 做錯誤處理是比較好的~

另外關於要驗證新增後是否成功的那個method,應該就還沒做好(文件上也有寫是 feature啦,不過目前我看不到在那),所以替代方案就是,先 sleep 一下,或是另起 thread 晚一點去確認那個 entry 是否存在了~

修改密碼的話,目前寫進去後,會被default用 ssha hash,

maven dependency
        <dependency>
            <groupId>org.apache.directory.api</groupId>
            <artifactId>api-all</artifactId>
            <version>1.0.0-RC2</version>
        </dependency>

import 使用到的部份
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.cursor.SearchCursor;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.entry.DefaultModification;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Modification;
import org.apache.directory.api.ldap.model.entry.ModificationOperation;
import org.apache.directory.api.ldap.model.message.AddRequest;
import org.apache.directory.api.ldap.model.message.AddRequestImpl;
import org.apache.directory.api.ldap.model.message.AddResponse;
import org.apache.directory.api.ldap.model.message.Control;
import org.apache.directory.api.ldap.model.message.DeleteRequest;
import org.apache.directory.api.ldap.model.message.DeleteRequestImpl;
import org.apache.directory.api.ldap.model.message.DeleteResponse;
import org.apache.directory.api.ldap.model.message.Response;
import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
import org.apache.directory.api.ldap.model.message.SearchRequest;
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
import org.apache.directory.api.ldap.model.message.SearchResultEntry;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.message.controls.OpaqueControl;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;


code 的部份(標一下建 connection 和 close 的地方)
        LdapConnection connection = new LdapNetworkConnection( "127.0.0.1", 10389 );
        connection.connect();
        System.out.println("connection="+connection);
       
        //Simple search
        System.out.println("====Simple search");
        EntryCursor cursor = connection.search( "ou=system", "(objectclass=*)", SearchScope.ONELEVEL );

        while ( cursor.next() )  {
            Entry entry = cursor.get();
            System.out.println( entry );
        }
        cursor.close();
       
        //Searching using a DN
        System.out.println("====Dn search");
        Dn systemDn = new Dn( "ou=system" );
        EntryCursor cursor2 = connection.search( systemDn, "(objectclass=*)", SearchScope.ONELEVEL );

        while ( cursor2.next() )  {
            Entry entry = cursor2.get();
            System.out.println( entry );
        }

        cursor2.close();
       
        // Create the SearchRequest object
        System.out.println("====SearchRequest object");
        SearchRequest req = new SearchRequestImpl();
        req.setScope( SearchScope.SUBTREE );
        req.addAttributes( "*" );
        req.setTimeLimit( 0 );
        req.setBase( new Dn( "dc=example,dc=com" ) );
        //req.setFilter( "(objectClass=*)" );  //search all
        req.setFilter( "(ou=users)" );  //assign some

        // Process the request
        SearchCursor searchCursor = connection.search( req );

        while ( searchCursor.next() )  {
            Response response = searchCursor.get();

            // process the SearchResultEntry
            if ( response instanceof SearchResultEntry )  {
                Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry();
                System.out.println(resultEntry);
            }
        }
        searchCursor.close();
       
        System.out.println("======Test Search End========");
        System.out.println("======START to UPDATE");
        String uname1="AddTest11";
        Entry entry1 = new DefaultEntry(
                "cn="+uname1+",ou=users,dc=example,dc=com",
                "ObjectClass: top",
                "ObjectClass: inetOrgPerson",
                "ObjectClass: person",
                "ObjectClass: organizationalPerson",
                "cn: "+uname1,
                "sn: "+uname1 );
        if(!connection.exists("cn="+uname1+",ou=users,dc=example,dc=com"))  {
            AddRequest addRequest = new AddRequestImpl();
            addRequest.setEntry( entry1 );
            AddResponse response = connection.add( addRequest );
            System.out.println(response.getLdapResult().isDefaultSuccess());  //why this is false ?_?a...bug?
            if( response.getLdapResult().isDefaultSuccess()
                    || response.getLdapResult().getResultCode().equals(ResultCodeEnum.SUCCESS))  {
                System.out.println("Add OK");
            }  else  {
                System.out.println("not default success["+response.getLdapResult().getResultCode()+"]:"+response.getLdapResult().getDiagnosticMessage());
            }
        }
        //check exist? check it after wait some while
        Thread.currentThread().sleep(1000);
        if(connection.exists("cn="+uname1+",ou=users,dc=example,dc=com"))  {
            System.out.println("re check add OK");
        }
       
        //https://directory.apache.org/api/user-guide/2.6-modifying.html
        //modify
        Modification modify1 = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "uid", "testuser" );
        Modification modify2 = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName", "Well", "Smile" );
        Modification modify3 = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "userPassword", "t123456" );
        connection.modify( "cn="+uname1+",ou=users,dc=example,dc=com", modify1,  modify2, modify3);
       
        System.out.println("======START to Delete");
        //delete easy
        connection.delete( "cn=AddTest2,ou=users,dc=example,dc=com" );
       
        //delete by repsonse
        DeleteRequest deleteRequest = new DeleteRequestImpl();
        deleteRequest.setName( new Dn( "cn=AddTest3,ou=users,dc=example,dc=com" ) );
        Control deleteTreeControl = new OpaqueControl( "1.2.840.113556.1.4.805" );
        deleteRequest.addControl( deleteTreeControl );
        DeleteResponse deleteResponse = connection.delete( deleteRequest );
        if( deleteResponse.getLdapResult().isDefaultSuccess()
                || deleteResponse.getLdapResult().getResultCode().equals(ResultCodeEnum.SUCCESS))  {
            System.out.println("Delete OK");
        }  else  {
            System.out.println("delete fail["+deleteResponse.getLdapResult().getResultCode()+"]:"+deleteResponse.getLdapResult().getDiagnosticMessage());
        }
       
        connection.close();

2016年10月26日 星期三

jboss eap 6.2 + jms local

練習 JBoss 的JMS...Local client呼叫

1. 要記得把 server config 切到 standalone-full.xml
(要用原本的standalone.xml也可以,但是我看就是要加很多東西,有人建議就直接跑full就好,事實上也是,跑 full 幾乎沒啥問題)
在 windows 更換啟動的指令
cd xxxxxx\jboss-eap-6.2\bin\
standalone.bat -c standalone-full.xml
 (一般直接啟用,不帶參數,就是跑 standalone.xml)


2. 如果有設定 datasource,或是什麼要定在 standalone**.xml 裡面東西的,要再下一次。
(總之,就比對一下 兩邊的 xml,大概就可以看出點什麼差別)


3. 查一下 standalone-full.xml 裡面有沒有 <mdb 這個 tag內容
理論上長成這樣
<subsystem xmlns="urn:jboss:domain:ejb3:1.4">
            ....
            <mdb>
                <resource-adapter-ref resource-adapter-name="${ejb.resource-adapter-name:hornetq-ra}"/>
                <bean-instance-pool-ref pool-name="mdb-strict-max-pool"/>
            </mdb>

如果search 沒有  <mdb 就要手動新增,我一樣用 cli 語法加
/subsystem=ejb3:write-attribute(name="default-mdb-instance-pool", value="mdb-strict-max-pool")
/subsystem=ejb3:write-attribute(name="default-resource-adapter-name", value="${ejb.resource-adapter-name:hornetq-ra.rar}")

(ps:jboss 內建是 hornetq,如果要換別家的話,就要多灌lib進來才行吧)


4. 查一下 socket bind
理論上長這樣
<socket-binding-group
.....
        <socket-binding name="messaging" port="5445"/>
        <socket-binding name="messaging-group" port="0" multicast-address="${jboss.messaging.group.address:231.7.7.7}" multicast-port="${jboss.messaging.group.port:9876}"/>
        <socket-binding name="messaging-throughput" port="5455"/>
(不過這個應該 default 就有,只是看一下,知道他會把服務起在那個 port 就是)


5. 寫 jms 設定檔,webapp/META-INF/*-hornetq-jms.xml ,ex: my-hornetq-jms.xml
<?xml version="1.0" encoding="UTF-8"?>
<messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">
    <hornetq-server>
        <jms-destinations>
            <jms-queue name="MyMDBQueue">
                <entry name="/queue/MyQueue"/>
            </jms-queue>
            <jms-topic name="MyQueueMDBTopic">
                <entry name="/topic/MyTopic"/>
            </jms-topic>
        </jms-destinations>
    </hornetq-server>
</messaging-deployment>

6. 寫 Server side listener
import java.util.Date;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(
                propertyName = "destinationType",
                propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(
                propertyName = "destination",
                propertyValue = "queue/MyQueue") })
public class QueueListenerMDB implements MessageListener {
    public QueueListenerMDB() {
    }

    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                System.out.println("Queue: Server listener received ="+System.currentTimeMillis());
                TextMessage msg = (TextMessage) message;
                System.out.println("Message is : " + msg.getText());
            } else if (message instanceof ObjectMessage) {
                System.out.println("Queue: Server received an ObjectMessage at "+System.currentTimeMillis());
                ObjectMessage msg = (ObjectMessage) message;
                Object pojo = msg.getObject();
                System.out.println("pojo Details: "+pojo);
            } else {
                System.out.println("Not valid message for this Queue MDB");
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

7. 寫Client 驗證
(基本的 連接傳送是降子)
    QueueConnection conn;
    QueueSession session;
    Queue que;
    QueueSender send;
   
    private void connect() throws Exception {
        //
        InitialContext iniCtx = new InitialContext();
        Object tmp = iniCtx.lookup("ConnectionFactory");
        QueueConnectionFactory qcf = (QueueConnectionFactory) tmp;
        conn = qcf.createQueueConnection();
        que = (Queue) iniCtx.lookup("queue/MyQueue");
        session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
        conn.start();
    }
   
    private void stop() throws JMSException  {
        conn.stop();
        session.close();
        conn.close();
    }
   
    private void sendText(String text)  throws Exception {
        send = session.createSender(que);
        TextMessage tm = session.createTextMessage(text);
        log.info("sendRecvAsync, sent text=" + tm.getText());
        send.send(tm);
        send.close();
    }
   
    private void sendObject(Serializable obj) throws Exception {
        MessageProducer producer = session.createProducer( que );
        ObjectMessage message = session.createObjectMessage( obj );
        producer.send( message );
        send.close();
    }
   
(呼叫時使用就是照順序 open connect(session) -> session create send -> sendMessage(text/Object) -> close send -> close session)

client.connect();
client.sendText("Hi!");
client.sendObject( MyObject );
client.stop();

8. 包一包丟到JBoss 上
因為 JMS 有跑的時後看起來在 init 的時後,好像會咬住server的資源,如果不是用 shutdown的機制(直接關cmd),我碰到的是會有「存取被拒」的情況,就算是電腦關機後再開,還是會衝,大概還是就是先 正常的 shutdown 後(記得要等個幾秒),再重起 JBoss就會過去
那個 錯誤 在 log 裡會像降子(假警報,可是就是會被嚇到,明明沒動,想說是那裡又改錯了一.一|||,如果沒去看 server log 大概又會以為程式有問題...然後其實那個 war 是在deploy 成功的狀態(fail會有 xxxx.war.fail 的文字檔寫錯誤)...追log看,前面該跑的也都有跑起來,但是就是在後面會卡住)
 [javax.enterprise.resource.webcontainer.jsf.config] (ServerService Thread Pool -- 62) 正在初始化環境「/xxxxxx」的 Mojarra 2.1.19-jbossorg-1 20131024-0833
SEVERE [javax.enterprise.resource.webcontainer.jsf.config] (ServerService Thread Pool -- 62) Critical error during deployment: : com.sun.faces.config.ConfigurationException: java.util.concurrent.ExecutionException: javax.faces.FacesException: java.io.FileNotFoundException: D:\jboss-eap-6.2\standalone\tmp\vfs\temp\tempb1f1831541a362e4\xxxxxx.war-adb47e313f325e6a\xxxxxx.war-8494329521517097592.tmp (存取被拒。)


9. 其它
如果 JMS 有跑起來的話,用  cli 看 jndi-view 是有些東西的喔,local 呼叫的話,就是可以找 ConnectionFactory 裡面,有這東西,如果是遠端,大概要麻就是直接從那個 port 送進來,或者從 remote 那邊找進來了(網路上蠻多是走 jnp,不過 jnp 感覺好像又是另一門作業一.一")
                "ConnectionFactory" => {
                    "class-name" => "org.hornetq.jms.client.HornetQJMSConnectionFactory",
                    "value" => "HornetQConnectionFactory [serverLocator=ServerLocatorImpl [initialCo
nnectors=[TransportConfiguration(name=in-vm, factory=org-hornetq-core-remoting-impl-invm-InVMConnect
orFactory) ?server-id=0], discoveryGroupConfiguration=null], clientID=null, dupsOKBatchSize=1048576,
 transactionBatchSize=1048576, readOnly=true]"

JMS 像 default 起在 5445,也可以用 telnet 127.0.0.1 5445 驗證是否有在聽
            "java:jboss/exported" => {"jms" => {
                "class-name" => "javax.naming.Context",
                "children" => {"RemoteConnectionFactory" => {
                    "class-name" => "org.hornetq.jms.client.HornetQJMSConnectionFactory",
                    "value" => "HornetQConnectionFactory [serverLocator=ServerLocatorImpl [initialCo
nnectors=[TransportConfiguration(name=netty, factory=org-hornetq-core-remoting-impl-netty-NettyConne
ctorFactory) ?port=5445&host=127-0-0-1], discoveryGroupConfiguration=null], clientID=null, dupsOKBat
chSize=1048576, transactionBatchSize=1048576, readOnly=false]"
                }}
            }},


大概這樣,就可以跑一個簡單的 sample了。
話說,JMS 好像還有蠻多種作法(光找練習參考,就...總之每次建環境都覺得很無言啦...)
不過這個應該是最單純的,只靠設定用JBoss 內建的 JMS 來跑。


2016年10月21日 星期五

ejb 3 local

一樣是參照
http://theopentutorials.com/examples/java-ee/ejb3/how-to-create-ejb3-jpa-project-in-eclipse-jboss-as-6-1/

延續上篇~上篇是搞定 JNDI
這邊是建個簡單的 EJB...

差別就是,這邊先使用簡單的 Local ,所以有改寫 client 取的來源,還有一些其他的筆記~

6. 設定 persistence.xml
環境設定一直都是個惡夢...雖然久久一次(通常也是只建那麼一次...),但是每次都會出槌Orz...
classes/META-INF/persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
    <persistence-unit name="mypersistence" transaction-type="JTA">
        <jta-data-source>java:jboss/datasources/PostgresDS</jta-data-source>
        <properties>
            <property name="showSql" value="true"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
        </properties>
    </persistence-unit>
</persistence>

重點就是,EJB 通常是個 jar/ear 檔,所以這個檔的位置是放在 classes/META-INF 裡面的~~如果想放在 war 裡面的話,就是自己手動 copy 到 webapp/WEB-INF/classes/META-INF/persistence.xml 裡面~~放錯位置,就會一直找不到Orz..

至於因為是用 JBoss ,內建 hibernate ,所以沒設定,就是默認為 hibernate 的 jpa..hibernate 設定大概就是要依DB定 hibernate.dialect

7.  Entity
對應 DB 的 Enitity Bean..現在有 annotation 就方便很多~
因為是用 hibernate,也等同 hibernate 的  O/R mapping
(大概就是 pojo,  bo(business object),印象中就是  EJB 概念中有比較活在潮流裡的項目,不過是因為 hibernate 發揚光大的...但真心的說,身為 SQL 派的我討厭hibernate..)

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Column;

@Entity(name = "yyy")
public class AAAA implements Serializable {
    private static final long serialVersionUID = 1L;
   
    public AAAA()  {
        super();
    }
   
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="yyy_id_seq")
    @SequenceGenerator(name="yyy_id_seq", sequenceName="yyy_id_seq", allocationSize=1)
    @Column(name = "id")
    private Long id;
   
    @Column(name = "name")
    private String complete;


Postgresql 如果用 SERIAL 當 type ,其實是個 seq,就設定上要再訂一下。

8. Business Interface
這邊有分 Local 和  Remote,因為 Local 比較簡單,反正之後也是用這個...就先用 Local...
(這層就差不多是 spring 的 service 層(interface))

import javax.ejb.Local;

@Local
public interface  AAAAService {
    /**
     * query all
     * @return all data
     */
    public List<AAAA> findAll();
   
    /**
     * insert
     * @param data
     */
    public void insert(AAAA data);
  
9. Business Logic
實做 business interface...在這邊連結 persistence 的設定,EJB 在這裡可以就下 sql 直接 access db 了
(就也是spring 的 serviceImp,至於底下要不要多一層 dao,看人看架構吧。)

另外因為是套用 hibernate ,所以就要用 hsql 下(個人覺得也是惡夢 Orz)

import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class AAAAServiceBean implements AAAAService {
   
    @PersistenceContext(unitName = "mypersistence")
    private EntityManager entityManager;
    
    public AAAAServiceBean() {  
    }
   
    /**
     * query all
     * @return all data
     */
    public List<AAAA> findAll()  {
        String q = "FROM "+AAAA.class.getName();
        Query query = entityManager.createQuery(q);
        List<AAAA> lst = query.getResultList();
        return lst;
    }

   /**
     * search data according to keyword
     * @param keyword for search
     * @return list of data
     */
    public List<AAAA> search(String keyword)  {
        if(keyword == null || keyword.trim().length() == 0)  {
            return findAll();
        }
        String q = "FROM "+AAAA.class.getName()+" t WHERE t.name LIKE :keyword";
        Query query = entityManager.createQuery(q).setParameter("name", keyword);
        List<AAAA> lst = query.getResultList();
        return lst;
    }
   
    /**
     * insert
     * @param data
     */
    public void insert(AAAA data)  {
        entityManager.persist(data);
    }
   
    /**
     * update data by key (id)
     * @param data
     */
    public void update(AAAA data)  {
        entityManager.merge(data);
    }
   
    /**
     * delete data by key (id)
     * @param data
     */
    public void delete(AAAA data)  {
        entityManager.remove(data);
    }

10. Client
在 EJB 會弄個 Client 來呼叫 Bussiness,基本上也只是去查 Context Name 弄出來
(spring 是把 service 做成 spring 的 bean 來注入,EJB 就得自己去  lookup...所以要知道 service 叫什麼名字是很重要的~那個 cli 指令就還蠻好用的)

client 就沒有什麼特別的 annotation..重點只是在怎麼拿到 Bussiness Instance

Context initContext = new InitialContext();
Context ctx = (Context) initContext.lookup("java:app/MYWEBAPP");
AAAService bean = (AAAService) context.lookup("AAAServiceBean");
List<AAA> list = bean.findAll();
bean.insert(....);

貼一下,如果在 cli 看到的 ServiceBean 大概就會長這樣子
(其實在 context naming 裡,很多 scope 都會有,只是我想就用 app,自己範圍的就好)
"applications" => {"MYWEBAPP.war" => {
    "java:app" => {
    ...
    "MYWEBAPP" => {
    "class-name" => "javax.naming.Context",
    "children" => {
    ...
        "AAAAServiceBean" => {
        "class-name" => "xxx.yyy.businesslogic.AAAAServiceBean",
        "value" => "?"
        },
    ...

在這邊也可以看一下 serviceBean 有沒有被正確的放進來,然後就可以連線測試了~