Status

Blog::Calendar

« September 2010
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  
       
Today
XML

Blog::Navigation

Blog::Editing

Bookmarks::Blogroll

Blog::Referers

Today''s Page Hits: 413

Other sites

Google Analytics

Powered by Roller Weblogger.
All | JBoss&Seam | JSF | ZK | Music | General | Java
Main | Next page »
20100607 Monday June 07, 2010
zkseam2, integrates ZK with Seam context

zkseasm2 - My parttime project that provides ZK and Seam2 integration.

project site : http://code.google.com/p/zkseam2/

Features of zkseam2

Seam components as ZK variables

You can access seam component as ZK variables in zscript or EL

zscript

You can directly use seam components as a variables in zscript. Following example shows how to persist a new Item entity by entityManager(Seam component, a J2EE EntityManager) in zscript.

data binding

Binding value to todoCtrl.current.name, todoCtrl could be a EJB or a JAVA Bean type Seam component.

i18n messages

Both ZK and Seam provide a i18n messages feature, if you want to access Seam i18n messages in ZK, you could use EL and access messages (a map type Seam component) with a string type message key.

Binding Seam Conversation Scope with ZK desktop

start a conversation

To start a Seam conversation and binding it to ZK desktop, you use DesktopConversaton.begin. After this invocation, I will eanble a long running Seam conversation and lifecyle is binding with ZK Deskotp, in the words, you will get same conversation scope Seam component in same desktop.
DesktopConversation.begin();
You usually begin a conversation in a conversation scope seam controller's creation method. for example :
@Name("winCtrl")
@Scope(ScopeType.CONVERSATION)
public class WinCtrl {
  @Create
  public void create(){
    DesktopConversation.begin();
  }
}

end a conversation

To end a Seam conversation and unbinding with ZK desktop, just call DesktopConversation.end()
DesktopConversation.end();
The conversation will be destroyed automatically when the desktop cleanup by zkau request or conversation timeout.

Inject ZK components into Seam component

inject implicit object

The string 'desktop','page'..etc are keywords to get these instance.
    @In(value="desktop",required=false)
    Desktop desktop;

    @In(value="page",required=false) //the first page in current desktop
    Page page;

    @In(value="execution",required=false)
    Execution exec;

inject named component

    @In(value="mypage1",required=false)
    Page page;

    @In(value="win",required=false)
    Window window;
    
    @In(value="win.btn",required=false)
    Button btn;
    
    @In(value="win.tb",required=false)
    Textbox textbox;
Until now, I have no idea how to bind zk IdSapce in seam, if you want to inject a component, you have to know the component path and replace the path character '/' by '.'.

Seam component as an action listener

action listener annotation in zul

To enable this, you have to add a zk directive as follow

<?init class="com.infinitiessoft.zkseam.zk.AnnotateActionBinderInit"?>

20100506 Thursday May 06, 2010
A solution that fixes accessing Seam components bug in multiple WAR in a EAR
Seam is a great web-bean framework indeed.
However, It has a limitation when running EAR with multiple WAR.
look into the code of Lifecycle and ServletLifecycle, you can see it store applicationContext in 'static' way.
which means , if you have multiple WAR in a EAR, the last initialized WAR become the application context of entire EAR.
It doesn't matter if you only access Seam component in JSF page/event, because FacesLifecycle provide another way to retrieve application context.
but, if you want to access Seam component in a custom servlet or in a thread (for example:Timer, Scheduler), you will always get the last registered application(WAR module) and it might be wrong one if servlet and thread are running for another WAR module.
Following is the example to access Seam component in servlet and thread.
[Access Seam component in Servlet]
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        ServletLifecycle.beginRequest(request);
        try{
            Requests.instance().setRequest(request);
            // do some thing.
        }finally{
            ServletLifecycle.endRequest(request);
        }
    }
[Access Seam component in Thread]
public void run() {
    Lifecycle.beginCall();
    try{
        //do something
    }finally{
        Lifecycle.endCall();
    }
}
Since Lifecycle and ServletLifecycle handle application context in static, so you might get wrong application when use it.
To solve this , we have to know which application context we needed when we want to initial Seam Thanks Open Source, after deep into the code, I has a chance to fix it.
I wrote two Lifecycle Util and one seam component to workaround it.
[LifecycleEx] : helps you to retrieve application by name.
package org.jboss.seam.contexts;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.jboss.seam.ScopeType;
import org.jboss.seam.core.Manager;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
/**
 * A enhanced lifecycle controller for running seam in multiple war
 * 
 * @author dennis
 */
public class LifecycleEx {
    
    private static String NULL_APP_NAME = "LifecycleEx.NULL.APPNAME";
    private static Map<String,Object> defaultApplication = null;
    private static Map<String,Map<String,Object>> applications = new HashMap<String,Map<String,Object>>();
    private static List<Map<String,Object>> applicationList = new LinkedList<Map<String,Object>>();
    private static final Log log = Logging.getLog(LifecycleEx.class);
    
    static public void registerApplication(String name,Map<String,Object> application){
        
        name = name == null?NULL_APP_NAME:name;
        
        if(applications.containsKey(name)){
            log.warn("trying to register application '#0' again",name);
        }
        
        applications.put(name,application);
        applicationList.add(application);
        
        defaultApplication = application;
    }
    
    static public void unregisterApplication(String name){
        name = name == null?NULL_APP_NAME:name;
        
        Map<String,Object> app = applications.remove(name);
        
        if(app!=null){
            applicationList.remove(app);
            if(app==defaultApplication){
                if(applicationList.size()>0){
                    //last one
                    defaultApplication = applicationList.get(applicationList.size()-1);
                }else{
                    defaultApplication = null;
                }
            }
        }
    }
    
    static Map<String,Object> getApplication(String name){
        return getApplication(name,false);
    }
    
    /**
     * get Application context by name, if no application context found, then try to get default application context (the last registered)
     * @param name name of application
     * @param as_possible true: if get application not found, then return default application 
     * @return RuntimeException if no application found.
     */
    static Map<String,Object> getApplication(String name,boolean as_possible){
        name = name == null?NULL_APP_NAME:name;
        
        Map<String,Object> app = applications.get(name);
        if(app==null&&as_possible){
            log.debug("name #0 not found, use default application",name);
            app = defaultApplication;
        }
        if(app==null){
            throw new RuntimeException("application "+name+" not found ");
        }
        return app;
    }
    
    public static void setupApplication(String name)
    {
       Contexts.applicationContext.set( new ApplicationContext(getApplication(name)) );
    }

    public static void cleanupApplication()
    {
       Contexts.applicationContext.set(null);
    }
    
    static void clearThreadlocals() 
    {
       Contexts.eventContext.set(null);
       Contexts.pageContext.set(null);
       Contexts.sessionContext.set(null);
       Contexts.conversationContext.set(null);
       Contexts.businessProcessContext.set(null);
       Contexts.applicationContext.set(null);
    }
    
    public static void beginCall(String appname){
        beginCall(appname,false);
    }
    
    public static void beginCall(String appname,boolean as_possible)
    {
       Contexts.applicationContext.set( new ApplicationContext(getApplication(appname,as_possible)) );
       Contexts.eventContext.set( new BasicContext(ScopeType.EVENT) );
       //TODO should we initial session?? 
       Contexts.sessionContext.set( new BasicContext(ScopeType.SESSION) );
       Contexts.conversationContext.set( new BasicContext(ScopeType.CONVERSATION) );
       Contexts.businessProcessContext.set( new BusinessProcessContext() );
    }

    public static void endCall()
    {
       try
       {
          Contexts.destroy( Contexts.getSessionContext() );
          Contexts.flushAndDestroyContexts();
          if ( Manager.instance().isLongRunningConversation() )
          {
             throw new IllegalStateException("Do not start long-running conversations in direct calls to EJBs");
          }
       }
       finally
       {
          clearThreadlocals();
       }
    }
}
[ServletLifecycleEx] : helps you to retrieve application by name.
package org.jboss.seam.contexts;

import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
import org.jboss.seam.servlet.ServletApplicationMap;
import org.jboss.seam.servlet.ServletRequestMap;
import org.jboss.seam.servlet.ServletRequestSessionMap;
import org.jboss.seam.web.Session;
/**
 * A enhanced servlet lifecycle controller for running seam in multiple war
 * @author dennis
 */
public class ServletLifecycleEx {

    private static final Log log = Logging.getLog(ServletLifecycleEx.class);
    
    static public void registerApplication(String name,ServletContext context){
        log.info("Register application context name #0",name);
        Map<String,Object> application = new ServletApplicationMap(context);
        LifecycleEx.registerApplication(name,application);
        
    }
    
    static public void unregisterApplication(String name){
        log.info("Unregister application context name #0",name);
        LifecycleEx.unregisterApplication(name);
        
    }
    
    public static void beginRequest(String path,HttpServletRequest request){
        log.debug( "begin web request #",path );
        Contexts.eventContext.set( new EventContext( new ServletRequestMap(request) ) );
        Contexts.sessionContext.set( new SessionContext( new ServletRequestSessionMap(request) ) );
        Contexts.applicationContext.set(new ApplicationContext( LifecycleEx.getApplication(path) ) );
        Contexts.conversationContext.set(null); //in case endRequest() was never called
    }
    
    public static void endRequest(HttpServletRequest request) {
        log.debug("After request, destroying contexts");
        try
        {
           Session session = Session.getInstance();
           boolean sessionInvalid = session!=null && session.isInvalid();
           
           Contexts.flushAndDestroyContexts();

           if (sessionInvalid)
           {
              LifecycleEx.clearThreadlocals();
              request.getSession().invalidate();
              //actual session context will be destroyed from the listener
           }
        }
        finally
        {
            LifecycleEx.clearThreadlocals();
        }
    }
}
[SeamLifecycleEx] : this component is for registering application context into LifecycleEx and ServletLifecycleEx.
package foo.bar;

import java.util.Map;
import javax.servlet.ServletContext;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.contexts.LifecycleEx;
import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.contexts.ServletLifecycleEx;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;

@Name("foo.bar.SeamLifecycleEx")
@Scope(ScopeType.APPLICATION)
@Startup @AutoCreate
public class SeamLifecycleEx {

    private static final Log log = Logging.getLog(SeamLifecycleEx.class);
    
    private String _name;
    
    @Create
    public void create(){
        log.debug("Initial SeamLifecycleEx");
        ServletContext context = ServletLifecycle.getServletContext();
        if(context==null){
            log.warn("servlet context not found, did you initial seam out of WAR moudle?");
            Map<String,Object> currentApplication= Lifecycle.getApplication();
            LifecycleEx.registerApplication(null,currentApplication);
            return;
        }
        _name = context.getContextPath();
        //in mock/test , _name is null
        if(_name!=null && _name.startsWith("/")){
            _name = _name.substring(1);
        }
        log.debug("register servlet application context with name #0 ",_name);
        ServletLifecycleEx.registerApplication(_name,context);
    }
    
    @Destroy
    public void destroy(){
        ServletLifecycleEx.unregisterApplication(_name);
    }
}
Ok, Now you have 3 java class, you have to put first 2 java into seam package (the package level protected issue) and put the seam component to where you want.
I don't want to explain how the code work, since you have my code, you can read it by yourself.
now, following is the example when you want to access Seam component in your servlet and thread.
[Access Seam component in Servlet]
    protected String name;

    public void init() throws ServletException {
        super.init();
        if(name.startsWith("/")){
            name = name.substring(1);
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        ServletLifecycleEx.beginRequest(name,request);
        try{
            Requests.instance().setRequest(request);
            // do some thing.
        }finally{
            ServletLifecycleEx.endRequest(request);
        }
    }

[Access Seam component in Thread]
 // in thread, name is a configured string usually.
public void run() {
    LifecycleEx.beginCall(name,true);
    try{
        //do something
    }finally{
        LifecycleEx.endCall();
    }
}
20100326 Friday March 26, 2010
Got "java.sql.SQLException: You cannot commit with autocommit set!"
[Read More]
20091130 Monday November 30, 2009
When will seam create compoent instance.

When will seam cerate component instance?

20090728 Tuesday July 28, 2009
How to install JBPM4 into JBoss AS 4.2.3

Reference :
 http://www.mastertheboss.com/en/jbpm/209-jbpm-4-tutorial-installation.html
 http://www.li-zone.cn/index.php/2009/06/jbpm%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

  1. Install JBPM Schema
    1. create a schema named "jbpm4" on your local MySQL database. Then add an user named "jboss" to the database.
      CREATE DATABASE jbpm4;

      GRANT ALL PRIVILEGES ON *.* TO jboss@localhost
      -> IDENTIFIED BY 'jboss' WITH GRANT OPTION;
    2. Then edit the file JBPM4_HOME\db\jdbc\mysql.properties so that it contains our database properties:
      jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/jbpm4
      jdbc.username=jboss
      jdbc.password=jboss
    3. Now move the the "db" folder where you will find a build.xml. Launch the following command
      ant -Ddatabase=mysql create.jbpm.schema
  2. Install JBPM4 on JBoss 4.2.3
    1. Move into the "jboss" folder and open the ant build.xml file, Configure the following properties, at the top of the file:
      <property name="jboss.version" value="4.2.3.GA" />
      <property name="jboss.home" value="/the_path_of_jboss_ap" /> <!-- ex :/user/home/dennis/jboss-4.2.3.GA -->
    2. Launch the command, it will create a folder (JBoss AS server/default/deploy/jbpm), and copy file into it.
      ant -Ddatabase=mysql install.jbpm.into.jboss
  3. Manually configuration, JBPM4 installation command is only for JBossAS 5, so we need to do some thing manually
    1. Create folder DEPLOY/jbpm/jbpm-service.sar/jbpm.deployer/jbpm.beans/META-INF
    2. Create folder DEPLOY/jbpm/jbpm-service.sar/jbpm.deployer/META-INF
    3. Copy file JBPM4_HOME/jboss/config.jboss4/deploy/jbpm/jbpm-service.sar/jboss-beans.xml to DEPLOY/jbpm/jbpm-service.sar/jbpm.deployer/jbpm.beans/META-INF
    4. Copy file JBPM4_HOME/jboss/config.jboss4/deploy/jbpm/jbpm-service.sar/META-INF/jboss-service.xml to DEPLOY/jbpm/jbpm-service.sar/jbpm.deployer/META-INF
    5. Copy JBPM4_HOME/lib, bpm-spi.jar and jbpm-jboss4.jar to DEPLOY/jbpm/jbpm-service.sar/jbpm.deployer
  4. Start JBoss AS and link to  http://localhost:8080/jbpm-console/ , login with alex(password) .

Make it more automatically

you can modify JBPM4_HOME/jboss/build.xml to make it automatically configure on JBoss 4.2.3.

  1. Move into the "jboss" folder and open the ant build.xml file, configure the following properties, at the top of the file:
    <property name="jboss.version" value="4.2.3.GA" />
    <property name="jboss.home" value="/the_path_of_jboss_ap" /> <!-- ex :/user/home/dennis/jboss-4.2.3.GA -->
  2. Search <antcall target="internal.install.jbpm.into.jboss.500specifics" /> , append this line below it.
    <antcall target="internal.install.jbpm.into.jboss.400specifics" />
    1, Search <condition property="jboss.version.5">, append a condition declaration below the condition
    <condition property="jboss.version.4">
    <or>
    <equals arg1="${jboss.version}" arg2="4.2.3.GA" />
    </or>
    </condition>
  3. Search <target name="internal.install.jbpm.into.jboss.500specifics" if="jboss.version.5">, append a target below the target.
      <!-- ### THE JBOSS 4 SPECIFIC PART ############################### -->
    <target name="internal.install.jbpm.into.jboss.400specifics" if="jboss.version.4">
    <copy todir="${jboss.server.config.dir}/deploy/jbpm/jbpm-service.sar/jbpm.deployer/jbpm.beans/META-INF/" overwrite="true">
    <fileset dir="${jbpm.home}/jboss/config.jboss4/deploy/jbpm/jbpm-service.sar/" >
    <include name="jboss-beans.xml"/>
    </fileset>
    </copy>
    <copy todir="${jboss.server.config.dir}/deploy/jbpm/jbpm-service.sar/jbpm.deployer/META-INF/" overwrite="true">
    <fileset dir="${jbpm.home}/jboss/config.jboss4/deploy/jbpm/jbpm-service.sar/META-INF/" >
    <include name="jboss-service.xml"/>
    </fileset>
    </copy>
    <copy todir="${jboss.server.config.dir}/deploy/jbpm/jbpm-service.sar/jbpm.deployer/" overwrite="true">
    <fileset dir="${jbpm.home}/lib">
    <include name="jbpm-spi.jar"/>
    <include name="jbpm-jboss4.jar"/>
    </fileset>
    </copy>
  4. Launch the command
    ant -Ddatabase=mysql install.jbpm.into.jboss
20090718 Saturday July 18, 2009
Run seam register example in WAR

In past of two days, I was studying JBoss Seam, reading the document and trying to run the tutorial example.
I did a stupid thing that spent me half a day, the example must run in EAR deployment, but I ran it in WAR mode.

I am curious how to make it run in WAR mode.
Cause of the example is made for a EJB, so you need to declare @Stateless/@Stateful for the controller.
In document of Seam, it can control any bean to be a controller, no need to EJB , so we should able to remove EJB annotation.
However cause we still need an entity manager,that annotated by @PersistenceContext, to work, so we don't remove @Stateless/@Stateful directly.
Thanks Seam, it is easy to fix, use @In annotation, you can get a entity manager with out @PersistenceContext

following is the steps to run example in WAR mode.

  1. Remove @Stateless/@Stateful in RegisterAction
  2. Change @PersistenceContext to @In(value="entityManager") , or @In if the EntityManager value name is already "entityManager"
  3. Remove @Local in Register, keep Register interface, I think this interface is still good for a OO Design.
that's all, you only need to care about is the the name of "entityManager" must same as the declartion in components.xml

Copyright (C) 2003, 閣樓貓的五四三 (About Cat)