Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java EE 6: How to add web module on top of application client

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

like image 678
Thang Pham Avatar asked Jun 17 '11 14:06

Thang Pham


1 Answers

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

    // ...
}
like image 72
BalusC Avatar answered Sep 27 '22 18:09

BalusC