7

Technology(Java EE 6 with Glassfish 3.1, Netbeans 7.0)

I have an application client that access a db via JPA. No EJB is involved. Now I need to add an web interface for this application client. So I will choose to use JSF 2.x. I have some concern about design here, and I hope the community would help me out. So thanks to BalusC, I am able to use JPA in a stand alone client application by specify transaction-type=RESOURCE_LOCAL in the persistence.xml. Below are demonstration:

EDIT the below codes has been edited base on BalusC suggestion

Here is my App Client main

public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("CoreInPU");
    EntityManager em = emf.createEntityManager();
    EntityDAO entityDAOClient = new EntityDAOClient(em);
    Main pgm = new Main();
    try {
        process(entityDAOClient);
    } catch (Exception e) {
        logger.fatal("", e);
    }finally{
        em.close();
        emf.close();
    }
}

public void process(EntityDAO entityDAO){
    validatePDF(List<pdfFiles>);
    processPDF(List<pdfFiles>, entityDAO);
    createPrintJob(List<pdfFiles>, entityDAO);
}

public void processPDF(List<pdfFiles>, EntityDAO entityDAO){
    for(File file : pdfFiles){
        entityDAO.create(file);
    }
}

Here is my DAO interface class in my App Client

public interface EntityDAO {
    public <T> T create(T t);
    public <T> T find(Class<T> type, Object id);
    public List findWithNamedQuery(String queryName);
    public List findWithNamedQuery(String queryName, int resultLimit);

}

Here is the App Client DAO

public class EntityDAOClient implements EntityDAO {

    private EntityManager em;

    private static Logger logger = Logger.getLogger(EntityDAOClient.class);

    public EntityDAOClient(EntityManager em) {
        this.em = em;
    }

    @Override
    public <T> T create(T t){
        em.getTransaction().begin();
        em.persist(t);
        em.getTransaction().commit();
        return t;
    }

    @Override
    public <T> T find(Class<T> type, Object id){
        em.getTransaction().begin();
        T t = em.find(type, id);
        em.getTransaction().commit();
        return t;
    }
    ...
}

And here is the persistence.xml

<persistence-unit name="CoreInPU" transaction-type="RESOURCE_LOCAL">
   <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <class>com.wf.docsys.core.entity.Acknowledgement</class>
   <class>com.wf.docsys.core.entity.PackageLog</class>
   <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/core"/>
      <property name="javax.persistence.jdbc.password" value="root"/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="xxxx"/>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
   </properties>
</persistence-unit>

Now I need to add a web module on top of this. I know I need JTA transaction type, so I create an EAR project call foo with foo_ejb and foo_war in it. So my EJB look like this.

@Stateless
@LocalBean
public class CoreEJB implements EntityDAO{

    @PersistenceContext(unitName = "CoreInWeb-ejbPU")
    private EntityManager em;

    //@Override
    public <T> T create(T t) {
        em.persist(t);
        return t;
    }

    //@Override
    public <T> T find(Class<T> type, Object id) {
        return em.find(type, id);
    } 

    ...
}

Note that CoreInWeb-ejbPU is the new persistence.xml unit name with JTA transaction type. I also add my app client jar file to the foo_ejb package. When I deploy I got this message Invalid ejb jar [foo-ejb.jar]: it contains zero ejb. It is because of this @Stateless public class CoreEJB implements EntityDAO. If I take the implements EntityDAO out then it is deploy, but I need the EJB to be implements EntityDAO so that in my managed bean I can do this

@ManagedBean
@RequestScoped
public class Bean {

   @EJB
   private CoreEJB coreEJB;


   public Bean() {

   }

   public void runAppClientMainProcess() { 
       //The web interface can also kick off the same process as the app client
       process(coreEJB);
   }

   // ...
}

How can I do this correctly? Please help

I know I might be asking too much here, but if you can base on my structure above, show me how to add a web module in, I would greatly appreciate it. Some codes would be awesome. I am still learning, so if my design is flaw, feel free to rip it, I will redesign everything if I am convince there are better way to accomplish this. Bottom line is, there are a set of business logic, and I want to access those via both application client and web interface. Like how glassfishv3 have web interface and admin console

Community
  • 1
  • 1
Thang Pham
  • 38,125
  • 75
  • 201
  • 285

4 Answers4

6

Can you guys tell me where should I put/create another persistence.xml with transaction-type="JTA"? Inside my web module, or create a separate EJB?

It's not different. It still needs to go in a /META-INF/persistence.xml file. If your project represents a WAR, put it in web project. Or if it represents an EAR, put it in EJB project.


In JSF, I use Managed Bean, how would my managed bean invoke method from EntityUtil? I ask this question because usually I have a EJB somewhere, so if I want my Managed bean to access it, I inject EJB using @EJB annotation. Since EntityUtil is not annotated to be EJB, how I can access it from Managed Bean?

In theory, you could just create a new EJB which composes/delegates EntityUtil and in turn inject this EJB in managed bean.

@Stateless
public class SomeEJB {

    @PersistenceUnit(unitName="someWebPU")
    private EntityManagerFactory emf;

    @PostConstruct
    public void init() {
        EntityUtil.newInstance(emf);
    }

    public void create(Some some) {
        EntityUtil.create(some);
    }

    // ...
}

However... Your EntityUtil is not threadsafe. Everything in your EntityUtil is static. A Java EE web application is a heavily multithreaded environment. Mutiple concurrent users use the same codebase simultaneously. All publicitly exposed static variables are shared among all users. When an user calls close() on your EntityUtil, it will affect all current users of the webapp. So the ones who are busy with a transaction will get an exception.

Regardless of it's a client or a web application, you should create the EntityManagerFactory only once on application's startup, reuse the same instance throughout the application's lifetime and close it only on application's shutdown. The EntityManager needs to be created only once per transaction or session and be closed by end of transaction or session. In Java EE 6 with JTA transaction type the transactions are fully managed by the container. But in a client app with resource local transaction type you need to manage the transactions yourself.

I'd suggest to rewrite your EntityUtil to a DAO-like interface which is intented to have different implementations for the client app and the web app.

public interface SomeDAO {

    public void save(Some some);
    // ...
}

Here's how you would implement it for the client application:

public class SomeDAOClient implements SomeDAO {

    private EntityManager em;

    public SomeDAO(EntityManager em) { 
        this.em = em;
    }

    public void save(Some some) {
        em.getTransaction().begin();
        em.persist(some);
        em.getTransaction().commit();
    }

    // ...
}

and use it as follows:

public static void main(String[] args) throws Exception {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("someClientPU");
    EntityManager em = emf.createEntityManager();
    SomeDAO someDAO = new SomeDAOClient(em);

    try {
        Some some = new Some();
        some.setFoo("foo");
        someDAO.save(some);
    } finally {
        em.close();
        emf.close();
    }
}

Here's how you would implement it for a web application:

@Stateless
public class SomeDAOEJB implements SomeDAO {

    @PersistenceContext(unitName="someWebPU")
    private EntityManager em;

    public void save(Some some) {
        em.persist(some);
    }

    // ...
}

and use it as follows

@ManagedBean
@RequestScoped
public class Bean {

    @EJB
    private SomeDAO someDAO;
    private Some some;

    public Bean() {
        some = new Some();
    }

    public void save() {
        someDAO.save(some);
    }

    // ...
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • cool. before I start redesign this, I want to ask this: Dont you have to `begin() and commit()` transaction for every method in `someDAO`. I know the web app has its contain to managed the transaction. But what about app client? – Thang Pham Jun 17 '11 at 18:28
  • I am sorry if I asked too many question, would it be better if I put all the logics inside `EJB`, then app client just invoke method from `EJB`, since EJB run in EJB container, the container can managed it transaction. (this is my original design, but I have to expose the EJB to app client via Remote interface, and this fear stop me from going this route) – Thang Pham Jun 17 '11 at 18:44
  • Sorry, I overlooked the transactional part. I updated the answer. – BalusC Jun 17 '11 at 19:06
  • So I fix my app client DAO, and it still work. So that good. But still have a bit of trouble with web app. So I create EAR package for web project contain web module (WAR) and EJB module (JAR). So I have trouble in access resources from web app to the App client. Do I add app client jar file as library in both web module and ejb module? – Thang Pham Jun 17 '11 at 20:39
  • 1
    In an EAR project, it needs to go in `/lib` of the main EAR project (I'm not sure how Netbeans does it, but Eclipse creates 3 subprojects for an EAR project: `foo`, `foo_ejb` and `foo_web` where `foo` is the main EAR project which should have a `/lib` folder and `foo_ejb` should contain the EJB/JPA stuff and `foo_web` the web/JSF stuff). – BalusC Jun 17 '11 at 20:45
  • Netbeans is in fact packaging a bit different. If you create a EAR project call `foo` then, then you have `foo` folder. Inside `foo`, you have `foo_ejb` and `foo_web`. However, If I add the app client jar file to the `foo_ejb` library, then i can get rid of the compile error. However, when I deploy on GF. I got this `Invalid ejb jar [CoreInWeb-ejb.jar]: it contains zero ejb.` It is because of this `@Stateless public class SomeDAOEJB implements SomeDAO`. If I take the `implements SomeDAO` out, then I can deploy, but then the structure is wrong. Any idea? – Thang Pham Jun 20 '11 at 14:04
  • I revised my original post with the structure that you suggest me. At the end, where I implements the `Managed Bean`, you can see that I need to invoke the process that app client run. So web interface and the app client interface can invoke the same process. I also add bounty to this question, please help – Thang Pham Jun 20 '11 at 14:36
  • 1
    As to Netbeans project structure trouble, sorry I don't know as I don't use it. After all the app client project must end up in classpath available to EJB. As to invoking the process, you need to invoke a method of `coreEJB` within the bean action method. E.g. `coreEJB.someMethod(someModel)`. – BalusC Jun 20 '11 at 14:43
  • Oh man, I am so frustrated now. If I do `public class SomeDAOEJB implements Serializable`, then it is deploy, but if I change `public class SomeDAOEJB implements SomeDAO`, then it tell me `Invalid ejb jar [CoreInWeb-ejb.jar]: it contains zero ejb.`. So frustrated. I do have the app client jar in the class path available for EJB. I can compile the project file. but ... :( :( so frustrated. – Thang Pham Jun 20 '11 at 15:06
  • This issue was said to be resolved on NB7.0 release. I am using NB 7.0 release. I am think about switch over to Eclipse now. – Thang Pham Jun 20 '11 at 15:25
  • 1
    One topic says that the interface needs to be `@Local`. Try adding another interface in EJB project which has just `@Local public interface CoreEJBBase extends EntityDAO {}` and make your concrete EJB `@Stateless public class CoreEJB implements CoreEJBBase { // methods }`. – BalusC Jun 20 '11 at 15:30
  • Thank you BalusC, it does not work either. Dowloading Eclipse now. in the mean times, what you said in the above comment `you need to invoke a method of coreEJB within the bean action method. E.g. coreEJB.someMethod(someModel)`. So in my app client, I have a `process(SomeDAO someDAO)`. This is the main process. App client `Main` will call this method. passing in client version of `someDAO` without container managed. Now in the web project, I also want to invoke this method to start my process, but I want to use the container managed DAO, so I pass in the EJB. Is this the right design? – Thang Pham Jun 20 '11 at 15:44
  • I'm not sure why you would like to pass the EJB. Is it a multi-step process? Make the interface an abstract class instead and add a non-abstract `process()` method which invokes the desired abstract methods. You should end up like `dao.process(model)`. – BalusC Jun 20 '11 at 15:54
  • Yes it is a multi-step process. I update my post, if you take a look at the first snippet of code, where the `Main` is, you should is demonstration of `process() and processPDF()`. Can you elaborate your above statement? You means to make the `SomeDAO` to be abstract class? – Thang Pham Jun 20 '11 at 17:53
  • Hi BalusC, thanks for all your help and for the late reply, I was busy doing another project. I have post my implementation solution of this problem, and I think I will stay with this for now, since it works for me. tyvm for your help man. – Thang Pham Jun 27 '11 at 13:50
2

@Harry Your are discussing three layers 1) Presentation 2) Business 3) Persistence

Case 1 - Application Client - You have used main class for presentation, validatePDF, processPDF, createPrintJob are your Business layer 3) EntityDAOClient and JPA is your persistence layer

Case 2 - Web Module - You have used(want to use) jsf for presentation , CoreEJB for persistence -- i am not clear where you intend to put business logic, for now ill take is as another ejb class.

The major issue in this approach is "Using two different approaches for business logic layer" Case 1 -- plain java Case 2 Ejb.

When you use plain java you loose the luxury of dependency injection , tx management, concurrency management, interceptors etc..

When you use EJB - you loose the flexibility of sharing the code with your application client.

The solution is to use CDI. With CDI you can integrate both of your use-cases. It works like a bridge between the three layers. Give all the power of dependency injection, interceptors ,decorators to application client.

Effectively there will zero changes in your business layer and persistence layer code in application client as well as JavaEE.

Did i mention in CDI programming model you need not use @managed. All pojo's are CDI beans.

In CDI you need not write a main class. Start writing the business logic as below


//This class works as is in both javaEE and client. 
//There is no need of managedbean in CDI. 
//This class can be accessed in jsf using EL #businessProcessor
@Singleton  @Named
public class BusinessProcessor
{
private @Inject EntityDAO entityDAO; // CDI will inject an implementation of EntityDao. 
//In our case there is only one impl for client as well as JavaEE 

// CDI will invoke below method on startup. Not used in JavaEE. // In JavaEE JSF will execute process() directly using Expression Language public void init(@Observes ContainerInitialized event, @Parameters List parameters){ process(entityDAO); }

public void process(@Inject EntityDAO entityDAO){     validatePDF(List);     processPDF(List, entityDAO);     createPrintJob(List, entityDAO); }

public void processPDF(List, EntityDAO entityDAO){     for(File file : pdfFiles){         entityDAO.create(file);     } } }


// this class is same for both JavaEE and Client
public class EntityDAOClient implements EntityDAO {

private @Inject EntityManager em;

    private static Logger logger = Logger.getLogger(EntityDAOClient.class);         @Override     public T create(T t){         em.getTransaction().begin();         em.persist(t);         em.getTransaction().commit();         return t;     }

    @Override     public T find(Class type, Object id){         em.getTransaction().begin();         T t = em.find(type, id);         em.getTransaction().commit();         return t;     }     ... }

To run the app client


java org.jboss.weld.environment.se.StartMain
For more meat check out www.seamframework.org
kiran.kumar M
  • 811
  • 8
  • 25
  • Hi kiran. tyvm for your help and sorry for the late reply. I was a bit tight up with my project. I have read your reply, however, i decide to go another route. I post my implementation to a separate answer, if you have some times, please take a look at them. tyvm for your help again. – Thang Pham Jun 27 '11 at 13:52
0

you can do some little project examples with netbeans, and see how it works.. NetBeans generate all these "sad" things for you, so, you can study it, and do it by yourself.

caarlos0
  • 20,020
  • 27
  • 85
  • 160
0

I want to apologize for my late reply. I was behind the schedule for another project so I do not have time to write a proper response to BalusC and kiran answers.

kiran ask me this: Case 2 - Web Module - You have used(want to use) jsf for presentation , CoreEJB for persistence -- i am not clear where you intend to put business logic, for now ill take is as another ejb class.

Answer: Hi kiran, the bottom line is that, I want the same set of logic to be access at both app client side and web module side. Like glassfish app server. You have a command line version and web module version accessing the same resources.

BalusC suggestion to use RESOURCE_LOCAL work great if I just use application client. However, once I adding the web module on top of it, I cant not get it to work anymore even though BalusC did try to work with me. You guys can read BalusC response and my implementation of his idea above (The code from the original post is my implementation of BalusC idea). If I add the App Client to the Java EE project (EAR) classpath, it receive no compile error when I try to access the logic from App Client, but when i run the web module, it generate Exception saying that it does not see the class/method that I try to access from App Client. So I decide to port all my logic onto EJB and make my App Client very thin. The logics will be exposed to App Client via javax.ejb.Remote interface, and exposed to the Web module via javax.ejb.Local interface. So below is my general layout of this idea.

This is my thin App Client (Main)

public class Main {

    @EJB
    private static CoreMainEJBRemote coreEJBRemote;

    private static Logger logger = Logger.getLogger(Main.class);

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
         coreEJBRemote.process(args[0]);
    }
}

So on my EJB side which package inside Java EE project (EAR) that contain EJB and web module, I have

@Stateless
public class CoreMainEJB implements CoreMainEJBRemote, CoreMainEJBLocal {

    @EJB
    private PackageProcessor packageProcessor;

    @Override
    public void process(String configFileName) {
         ...
         //Process config File
         packageProcessor.validatePDF();
         packageProcessor.processPDF();
         ...
    }
}

Note that since all the logics now inside EJB, I can use JTA transaction type, which is container managed. I like it this way better than manage it myself.

Some people suggestion to exposed the business logics via RESTful. Since I do not know very well about RESTful, I will stay with this implementation for now. Thank you for all your help BalusC and kiran.

Thang Pham
  • 38,125
  • 75
  • 201
  • 285
  • nice to see you found a solution. You are effectively using the standard three layered architecture where business logic and persistence logic lives on an application server. Presentation optionally on server and remote (client app). One issue u need to consider is connectivity. If your client is out-side your network I recommend REST. – kiran.kumar M Jun 27 '11 at 14:53
  • @kiran: my client will be on the same machine (unix box) as the the glassfish server. Would it consider better or worse or same performance as REST? – Thang Pham Jun 27 '11 at 15:39
  • 1
    Performance will be same in both cases -- Since there is only one client(limited number) at a given point of time. If your client needs scalability similar to stackoverflow.com then REST is the way to go. Its worth looking at JAX-RS. The rest principles can be still applied to solution you propose to use. JAX-RS will allow you to enable REST on your ejbs with little or not effort when you need. Provided you adhere to REST principles. – kiran.kumar M Jun 27 '11 at 15:51
  • Thank you very much. I will look into REST and maybe port this design to REST implementation. – Thang Pham Jun 27 '11 at 16:44