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 有沒有被正確的放進來,然後就可以連線測試了~

jboss eap 6.2 + postgresql 9.4 (jndi)

這說起來有點血淚,然後一直卡在 JBoss 的設定上...

完整的內容是要做 EJB,不過光搞 JNDI+persistence 設定檔 就被搞死了~
大致上的內容參考
http://theopentutorials.com/examples/java-ee/ejb3/how-to-create-ejb3-jpa-project-in-eclipse-jboss-as-6-1/

這篇先寫建立 JNDI / DataSource 的方式

有再用到 JBoss 的 cli 指令
https://docs.jboss.org/author/display/AS71/CLI+Recipes

最後整理出來的步驟就是~
(因為Naming 太多,用顏色分一下=.=,當初就各種 name 對不到)

1. 先找到 postgresql jdbc jar 檔
(你以為 jar 檔抓來就有,還是有陷阱阿阿阿阿阿)
一直有趕上時代的 postgresql...新的 jar 檔都是用 jdk 8 包的...
所以要記得挑 JBoss 有吃的 jdk 版本,我目前環境是用7,要挑 jre7 的版本

<dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4.1207.jre7</version>
        </dependency>

PS: 這個 jar 檔,不用放在 WEB-INF/lib 下,因為 JBoss 不是從這裡拿 driver,只是因為我先裝進去,還是要有這個jar

2. 在 JBoss 上建立 postgresql 的 module/driver
JBoss 的 jdbc driver,規定只能從他的 driver module 設定內取得,不能自己定義在 application 內...
理論上,是更改  jboss-eap-6.2\standalone\configuration\standalone.xml 的內容,不過因為 cli 可以在 server run 時運作,而且感覺比較安全!所以就用 cli 在加內容,基本上 cli cmd 執行成功,這個檔的內容都會有對應的變化~~
(網路上很多的 datasource 範例都是 貼這邊的內容出來,但是跟app層能用的又有微妙不同,所以...唉~算了Orz...就當是個經驗)

這邊用 cli 方式建立 (win版),記得JBoss server 要先起來~
jboss-eap-6.2\jboss-cli.bat
### 進入 cli cmd mode
connect
### 連 jboss server (server沒起,連不到就不能用了)

### 加入 module,注意 name 要取好記好~
### windows 的路徑 folder 是「\」不用反過來(反過來會找不到orz)
module add --name=org.postgres --resources=D:\project\jars\postgresql-9.4.1207.jre7.jar --dependencies=javax.api,javax.transaction.api
### 成功後 在 jboss-eap-6.2\modules\org\postgres\main 這邊就會自動生出來 module.xml, postgresql-9.4.1207.jre7.jar
手動加一個空的純文字檔,與檔名同,再加字尾.index是  XXXX.jar.index
(ex:postgresql-9.4.1207.jre7.jar.index
加一下比較保險, 不然掃不到又會一直吐訊息....

jboss-eap-6.2\modules\org\postgres\main\module.xml
(xmlns 有人說 urn:jboss:module:1.0 有bug..還好這版已修正。 name 就是指 module name,cli 生出來直接就是對的,要是自己手動貼的就要注意來源...)
<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="org.postgres">
    <resources>
        <resource-root path="postgresql-9.4.1207.jre7.jar"/>
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>
### 加入 driver
### 傳統的 jdbc driver, driver name=postgres
/subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver)

### 比較先進的 xa-datasource-class ,driver name=postgresql
/subsystem=datasources/jdbc-driver=postgresql:add(driver-name="postgresql",driver-module-name="org.postgres",driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource)

### 寫成功後,去看 standalone.xml 的 drivers 裡面也應該會生出來
###PS:  xa-datasource-class tag 是用 driver-xa-datasource-class-name 代入

3. 建立 DataSource
建立 DataSource  有兩種,一是做在 JBoss 裡面,就可以直接調用
### cli cmd
data-source add --jndi-name=java:jboss/datasources/PostgresDS1 --name=PostgresDS1Pool --connection-url=jdbc:postgresql://127.0.0.1:5432/XYZ --driver-name=postgres --user-name=xxxx --password=xxxx
### 這個會被寫進 standalone.xml 裡。要注意的是 url 後面不能帶 ?參數,會被截斷有錯

不過我是自己定在 webapp 裡面,就只給自己的 app用,就是看環境的規畫與管理方式吧
放在 webapp/META-INF 下,檔名為 xxx-ds.xml (一定要 -ds.xml結尾,不然JBoss會裝作不認識)。

在 這版的 Jboss 裡, datasource 好像都得放在  java:jboss/datasources/ ,亂擺可能會被亂吐,因為後來覺得煩了,乾脆都照標準放。
在 xxx-ds.xml 內,不可以用 <drivers> 的 tag,那個只能放在 standalone.xml。所以 xxx-ds.xml 就是只給用有匯進去的 driver, datasource 裡的設定 tag 是都也還都可以用
postgres-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <datasource jndi-name="java:jboss/datasources/PostgresDS" pool-name="PostgresDS_Pool"  enabled="true" use-java-context="true">
    <connection-url>jdbc:postgresql://localhost:5432/XYZ</connection-url>
    <driver>postgresql</driver>
    <security>
      <user-name>xxxxx</user-name>
      <password>xxxxxx</password>
    </security>
</datasources>

4. 驗證
反正就包一包,然後 deploy 可以起來的話,可以再用  cli 的指令看一下
/subsystem=naming:jndi-view()

有成功的話,就會看到自訂的 jndi 生出來了
"datasources" => {
    "class-name" => "javax.naming.Context",
    "children" => {
        "ExampleDS" => {
            "class-name" => "org.jboss.jca.adapters.jdbc.WrapperDataSource",
            "value" => "org.jboss.jca.adapters.jdbc.WrapperDataSource@ee07b70"
        },
        "PostgresDS" => {
            "class-name" => "org.jboss.jca.adapters.jdbc.WrapperDataSource",
            "value" => "org.jboss.jca.adapters.jdbc.WrapperDataSource@5a91e85c"
        }
    }
}
這指令蠻好用的,因為會列出所有的 Naming...=.=後面看 EJB 有沒有生出來也可以用這個對名字~~


5. 其它 
### exit cli mode
exit

### remove driver name
/subsystem=datasources/jdbc-driver=postgres:remove

#### remove module
module remove --name=org.postgres
理論上,這指令是要這麼打,但是...有bug不會動...所以就是自己去實體資料夾,把頭砍了就是...


整體上來說就是,設個 JNDI 的過程就會有很多 對應的 name 跟內容,概是降子
jndi-name (datasource) >> driver (driver name) >> module (module name, jdbc jar)

------------------------------------------------------------------------------
xa-datasource-class 和 driver-class 的差別
可以 google 「xa-datasource-class vs driver-class」
http://stackoverflow.com/questions/27456584/why-should-we-define-xa-datasource-class-within-a-driver-configuration-in-jbos

2016年10月20日 星期四

zk 資源

嘛,反正就是讀書...

官網文件
https://www.zkoss.org/documentation

zk source
https://sourceforge.net/projects/zk1/files/ZK/

官方Demo 裡有些簡單說明跟 example 下載
https://www.zkoss.org/zkdemo/getting_started

官方Sandbox ,可以在這邊試改zul再貼回去用,雖然還是有限制,但至少可以看基本layout,不用起server掛一大包...
https://www.zkoss.org/zksandbox

練習可以配合文件,還有sample code包配合章節,還算方便
https://www.zkoss.org/wiki/ZK_Essentials
https://github.com/zkoss/zkessentials

另外就是,如果要看 zk 這個 framework 的架構跟理念的話~
應該要看 他的 ZK xxxx Developer's Reference.pdf,放在
https://www.zkoss.org/documentation#References
內容很多,但是因為他這套 framework 完全自成一格...翻網頁時,這個大概是 2005 年就已經在開發了,但是也許也是很久了,所以會看到許多的時代眼淚吧...
主要code 是在 sourceforge...(不過他的首頁特別標榜在github,大概是現在就兩邊po,也許以後會轉去gitbub了吧。 sourceforge 哭哭)

是說,基本上就是那個時候的熱門 struts...jsf..jstl..icefaces(喵的,現在看到這個會害怕阿阿阿)tag lib的影子是一定會在...不過最厲害的是,...居然還可以看到swing 的影子...|||Orz...
當然人家能活個十幾年,還可以在首頁上貼那麼多成功的客戶,自然是有他厲害的地方...
(第一次跑去看看他們家的 About Me...因為在切入他們家的概念時,覺得真是難以理解,為啥咪要搞成這樣!!這年頭,大家早就不玩這種東西了...主要是前端太難切開,前端工程師無用武之地(算了,是說這樣後端工程師就不會失業,但是就是每天再繼續靠夭前端有夠機車難寫))

話說我總覺得他就是個不Open的Open Source...掛org還這樣限制一堆(如果你掛.com我就認了),那有Open Source 還要登入註冊他們家網站才能下載的Open Source...話說他們家的Eclipse Plug-in 也是要登入註冊帳號...真的是無言...
大概在想,那天他們自己說不open,通通都是產品的時候,把這些網民帳號查一次,大概就可以來討錢之類的~(我是有發現他官網的 zk 包不是隨便可以下載的,普通帳號要下載免費版都被說沒有權限)





2016年10月18日 星期二

maven + zk7

最近要開始寫練習了,基本上就是 zk7...

要順便用maven, 不知道為啥,很久沒用的 maven 壞了,
error:
Could not calculate build plan: Plugin org.apache.maven.plugins:maven-resources-plugin:2.6

Window -> Reference -> Maven -> User Setting
查了一下, 就是User Setting 裡的檔案壞掉了...(我自己看裡面是有些檔好像不見了)
就是去把那個 .m2 的整個檔案目錄 砍了~
之後在用 maven update project...讓他自己整個重新下載,就回來了...
http://stackoverflow.com/questions/31316339/how-to-solve-maven-2-6-resource-plugin-dependency 


接著大概就是~
Eclipse 先開 maven project (package=war)

然後就是 先上網去抓 zk7 ...google 出來第一個,是商業包裝的官網(目前最新是8),從網頁的下載要登入(問題是註冊了還不給下載....OX)
(現在真的不喜歡這些商業化的,他們自己官網給的 link/download 都是有商業目的的,但是google 排序都是在最前面....都得想辦法去找到真正放版本的地方。)

最後是用 sourceforge 的資源, github 上好像只有看到最新版...
https://sourceforge.net/projects/zk1/
https://sourceforge.net/projects/zk1/files/ZK/

抓了 7.0.3 回來用。

然後因為有要用 maven...所以對 lib 加了一下 pom.xml
照標準的 zk 文件加一下
https://www.zkoss.org/wiki/ZK_Installation_Guide/Setting_up_IDE/Maven/Resolving_ZK_Framework_Artifacts_via_Maven

但是發現在
https://mvnrepository.com/
其實好像都可以查得到~

不過就還是意思加一加好了,反正看起來好像也沒事O_oa....

<repositories>
        <repository>
            <id>ZK CE</id>
            <name>ZK CE Repository</name>
            <url>http://mavensync.zkoss.org/maven2</url>
        </repository>
    </repositories>

<!-- zk ce basic -->
        <dependency>
            <groupId>org.zkoss.zk</groupId>
            <artifactId>zkbind</artifactId>
            <version>7.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.zkoss.zk</groupId>
            <artifactId>zul</artifactId>
            <version>7.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.zkoss.zk</groupId>
            <artifactId>zkplus</artifactId>
            <version>7.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.zkoss.zk</groupId>
            <artifactId>zhtml</artifactId>
            <version>7.0.3</version>
        </dependency>
        <!-- zk theme -->
        <dependency>
            <groupId>org.zkoss.theme</groupId>
            <artifactId>silvertail</artifactId>
            <version>7.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.zkoss.theme</groupId>
            <artifactId>sapphire</artifactId>
            <version>7.0.3</version>
        </dependency>
        <!-- zkforge -->
        <dependency>
            <groupId>org.zkoss.addon</groupId>
            <artifactId>flashchart</artifactId>
            <version>1.0</version>
        </dependency>

然後把 zk  那一包的東西,除了 lib (用 pom.xml解決)
對應各個位置 把該貼的檔案貼一貼

然後要記得自己做一個 index (下來的包是沒有 html 文本的)

不過他也很好心的就是有這個地方~
https://www.zkoss.org/zkdemo/miscellaneous/xhtml_component_set
來隨便找個 內文,自己貼一個 index.zul 就可以了~

降子大概就完成了~

-----------------------------------------------------------
在JBoss  上跑 zk 的下載包,tld檔會過不了,是因為
在JBoss 裡不能用 <import 的這個 tag,總之就是把 import 的 tag 全都mark掉,就可以動了。
Tomcat 下倒是沒管那個 tag,直接就能跑。

2016年10月13日 星期四

[RPG] fix chrome restart gif animate

動畫用 GIF 在呈現上好做多了~重點是美觀 Q__Q

在互動 click 時,就讓他跑一下 GIF...
gif restart 的用法,原則上就是
$('#imgXXX').attr('src', 'imgs/XXXX.gif');

不過~試著試著...發現 chrome 有點 bug...Orz...
(講好聽點是設計理念不同...但在我的情況看起來就是bugg...)

這問題曾出在
http://www.realmofprogrammersgamble.website/journal/ch1/jourif2.html
切換語系,重現劇情...
人在走的時後的動畫
http://www.realmofprogrammersgamble.website/imgs/event1/walk.gif

這個問題會發生在,GIF loop=1 的情況(通常gif 不重複 repeat != infinite,就是loop=1)
配合 Chrome 的加持就會出現...

GIF 只會在一開始的時後,乖乖的跑一次動畫...
之後你再重設 src/background 的 gif 時,他會一直定格在最後的 frame 上
(看起來就是不會動了Orz)

查網路出來有蠻多說法~和解法~

有人是用 src=XXXXX.gif?Time=new Date()
這個是有效的~強迫chrome 不要cache...
但是缺點在於 網路會一直去server 要新圖片,吃我的頻寬($)
還有 網路傳輸延遲的問題...

最後找到一篇~其實之前已經試了很多說法,都是死馬當活馬醫...
但是看到的當下也是挺傻眼的XD...(原來降子也行!)
這招真的挺妙的就是~有種騙瀏覽器的感覺XDXD...
http://stackoverflow.com/questions/10730212/proper-way-to-reset-a-gif-animation-with-displaynone-on-chrome

簡單的說就是~ setTimeout 延遲一下下去 load 圖檔...
(話說這篇的點閱打勾還真的不高...)
是說延遲的部份,似乎會看該gif的大小,就gif檔越大,延遲要久一點才會出來~
(就當是圖檔重新下載的時間吧O_oa)

為了讓動畫看起來 smooth 一點,我稍為改進了一下,(直接display block,他開頭還是會有前面最後一格(setTimeout還是有delay,不管設多短 ),感覺會跳,所以再用 opacity 騙一下眼睛)

        // GIF restart
$('#img_g1').css('display', 'block');
        // 透明度0,就不會看到之前的最後 frame 內容
$('#img_g1').css('opacity', '0');
        // setTimeout 解決 chrome 不重跑 gif 的 frame
var ta = setTimeout(function()  {
$('#img_g1').attr('src', '/imgs/event1/walk.gif');
                // 他重跑時才看得到
$('#img_g1').css('opacity', '1');
}, 10);

        // 這段是讓動畫跑完就關掉了
var tb = setTimeout(function()  {
$('#img_g1').css('display', 'none');
$('#img_g1').css('opacity', '0');
}, 2500);

[RPG] 搖搖呼拉圈 gif animate

是說,做動畫還真的比想像中的難很多Q__Q...
最後就想說還是用 gif 就好了~剛好也有找到可以用的 gif 製作工具~
(gif 製作工具是很多沒錯,但不知道為啥,很多都沒有透明背景,超傷腦筋的,而且還花了一些冤枉錢T__T)
別說 photoshop/illustrator 都可以圖層轉 gif 之類的,因為人窮又不想用窮人版麻 |||Orz...
是說以前玩過,其實我並沒有覺得很好用,只是市面上也沒太多選擇~
一是覺得 太肥,很慢...而且改版後,越改越難用,去copy來的窮人版又都有病毒....
所以現在都儘量找平民的替代品中~

總之說一下目前用的 gif 製作工具~
mac 上的 GIF Animator,算便宜的~
https://itunes.apple.com/us/app/gif-animator/id512165265
只要讀入的是有透明背景的png,然後不要手賤去設背景色,產出就會是透明的GIF了~
(之前有買過用過別的,他們都很煩,強迫或多事去設定背景色,然後就都不透明了T__T)

接著試做了
http://www.realmofprogrammersgamble.website/journal/ch1/jourif1.html


準備底圖
http://www.realmofprogrammersgamble.website/imgs/event1/sport0.png
動畫圖 (loop=infinite)
http://www.realmofprogrammersgamble.website/imgs/event1/sport.gif

製做時圖一開始就是都 load進來了,基本上就只是一張顯示,一張不顯示。
html

<img src="/imgs/event1/sport0.png" id="img_t2" class="timg" style=" left: 58%; top: 52%; ">
<img src="/imgs/event1/sport.gif" id="img_t3" class="timg" style=" left: 58%; top: 52%; display: none;">

至於互動 click 時,就是切換 display 而已^^a...
為了加點樂趣~ 所以就讓運動的時間是 random 出來的~
而一般讓 GIF 重load 的寫法就是~ src 重新給值
ex: $('#img_t3').attr('src', '/imgs/event1/sport.gif');
是說,這gif 會一直重複,到是說沒加這段也還好~

click event
 var ts = randIntRange(1, 5);
$('#img_t2').css('display', 'none');
$('#img_t3').css('display', 'block');
$('#img_t3').attr('src', '/imgs/event1/sport.gif');
var tt2 = setTimeout(function()  {
$('#img_t3').css('display', 'none');
$('#img_t2').css('display', 'block');
}, 1000*ts+20);

是說,多搖幾次,會碎碎念的XD...
有一部分也是每年過年後要減肥的心情|||Orz...

apache poi create table

再來就是最機車的 table 問題...

其實一般 table 也是很平易近人的
(下載包附的 table 範例也很ok)...

但是當我拿去在 template table 裡面找字,替代成 內層table時就卡關了Orz...
試了很多排列組合,就是有一好,沒兩好...
然後就....過個幾天(多睡個幾次覺後)...就突然發現,其實有個簡單又方便的寫法 Orz...

說穿了就是...先用  docx.createTable() 保證不會出問題的產table作法,搞好  table 內容跟資料。
再把這 table 的內容像是 剪下貼上 (XmlCursor moveXML)到想要去的地方就好了XD...

XWPFParagraph paragraph = .... //where u want to put the table
XWPFTable table = docx.createTable(); 
 
//add rows/columns, cell and text.. 
//可以參考其範例 SimpleTable.java 太長了不想佔版面 XD...
 
//move new table to p...
XmlCursor c1 = table.getCTTbl().newCursor();
XmlCursor c2 = paragraph.getCTP().newCursor();
c1.moveXml(c2);
c1.dispose();
c2.dispose();

apache poi add image

做文件 docx 麻,難免會用到插圖

其實在原本下載包裡的範例就已經寫得很棒了!
(基本上也只是整個copy過來,加一點變數處理)
PS: 這是使用 3.14 版的,我是知道在 3.10 是跑不出來的

為了自己方便,我加了個 scale,去縮放/限制圖檔的大小

    protected void addImageIntoDocx(XWPFRun r, String imgFile, String scale) throws Exception {

        File img = new File(imgFile);
        if(!img.exists() || img.isDirectory())  {
            throw new Exception("Image not found:"+imgFile);
        }

        int format;

        if(imgFile.endsWith(".emf")) format = XWPFDocument.PICTURE_TYPE_EMF;
        else if(imgFile.endsWith(".wmf")) format = XWPFDocument.PICTURE_TYPE_WMF;
        else if(imgFile.endsWith(".pict")) format = XWPFDocument.PICTURE_TYPE_PICT;
        else if(imgFile.endsWith(".jpeg") || imgFile.endsWith(".jpg")) format = XWPFDocument.PICTURE_TYPE_JPEG;
        else if(imgFile.endsWith(".png")) format = XWPFDocument.PICTURE_TYPE_PNG;
        else if(imgFile.endsWith(".dib")) format = XWPFDocument.PICTURE_TYPE_DIB;
        else if(imgFile.endsWith(".gif")) format = XWPFDocument.PICTURE_TYPE_GIF;
        else if(imgFile.endsWith(".tiff")) format = XWPFDocument.PICTURE_TYPE_TIFF;
        else if(imgFile.endsWith(".eps")) format = XWPFDocument.PICTURE_TYPE_EPS;
        else if(imgFile.endsWith(".bmp")) format = XWPFDocument.PICTURE_TYPE_BMP;
        else if(imgFile.endsWith(".wpg")) format = XWPFDocument.PICTURE_TYPE_WPG;
        else {
            String err = "Unsupported picture: " + imgFile +
                    ". Expected emf|wmf|pict|jpeg|png|dib|gif|tiff|eps|bmp|wpg";
            throw new Exception(err);
        }

        BufferedImage bimg = ImageIO.read(img);
        int w = bimg.getWidth();
        int h = bimg.getHeight();
        double ratio = h*1d/w;

        try  {
            
                int wmax = Integer.parseInt(scale);
                if(w < wmax)  {
                    ;
                } else  {
                    w = wmax;
                    h = (int)Math.round(w*ratio);
                }
            
        }  catch(Exception e)  {
            w = 200;
            h = (int)Math.round(w*ratio);
        }

        //r.addBreak();
        r.addPicture(new FileInputStream(imgFile), format, imgFile, Units.toEMU(w), Units.toEMU(h));
        //r.addBreak(BreakType.PAGE);
    }

apache poi create docx XWPFDocument

最近寫了poi 應用 的docx...
就整理一下
(docx 的 lib 比 doc 的好很多,doc比半殘還可憐orz...後來直接放棄,就只用docx就好)

建立 docx,寫出檔案

FileInputStream fis = new FileInputStream(templateFile);
XWPFDocument docx = new XWPFDocument(fis);

//... do something what u want

FileOutputStream out = new FileOutputStream(outFile);
docx.write(out);
 
如果不用讀 template 的話,就是直接 new XWPFDocument()
 
 
最基本的找內文取代文字
   前者是找 平 的內容
   後者是找 table 內符合的內容 
            for (XWPFParagraph p : docx.getParagraphs()) {
                List<XWPFRun> runs = p.getRuns();
                if (runs != null) {
                    for (XWPFRun r : runs) {
                        String text = r.getText(0);
                        if (text != null && text.indexOf(param.getFind()) >= 0  ) {
                            text = StringUtils.replace(text,  param.getFind(), param.getReplace());
                            r.setText(text, 0);
                        }
                    }
                }
            }
            for (XWPFTable tbl : docx.getTables()) {
                for (XWPFTableRow row : tbl.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (XWPFParagraph p : cell.getParagraphs()) {
                            for (XWPFRun r : p.getRuns()) {
                                String text = r.getText(0);
                                if (text != null && text.indexOf(param.getFind()) >= 0) {
                                    text = StringUtils.replace(text,  param.getFind(), param.getReplace());
                                    r.setText(text, 0);
                                }
                            }
                        }
                    }
                }
            }