Java EE 6: Как добавить веб-модуль поверх клиентского приложения - PullRequest
7 голосов
/ 17 июня 2011

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

У меня есть клиент приложения, который обращается к БД через JPA. Нет EJB участвует. Теперь мне нужно добавить веб-интерфейс для этого приложения-клиента. Поэтому я выберу JSF 2.x. У меня есть некоторые опасения по поводу дизайна здесь, и я надеюсь, что сообщество поможет мне. Таким образом, благодаря BalusC я могу использовать JPA в автономном клиентском приложении , указав transaction-type=RESOURCE_LOCAL в файле persistence.xml. Ниже приведены демонстрации:

РЕДАКТИРОВАТЬ приведенные ниже коды были отредактированы на основе предложения BalusC

Вот мой клиент приложения 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);
    }
}

Вот мой DAO интерфейсный класс в моем 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);

}

Вот клиент приложения 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;
    }
    ...
}

А вот и 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>

Теперь мне нужно добавить веб-модуль поверх этого. Я знаю, что мне нужен JTA тип транзакции, поэтому я создаю вызов проекта EAR foo с foo_ejb и foo_war. Так что мой EJB выглядит так.

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

    ...
}

Обратите внимание, что CoreInWeb-ejbPU - это новое имя модуля persistence.xml с типом транзакции JTA. Я также добавляю файл jar клиента приложения в пакет foo_ejb. При развертывании я получил это сообщение Invalid ejb jar [foo-ejb.jar]: it contains zero ejb. Именно из-за этого @Stateless public class CoreEJB implements EntityDAO. Если я вытащу implements EntityDAO, то он будет развернут, но мне нужно, чтобы EJB реализовывал EntityDAO, чтобы в моем управляемом компоненте я мог сделать это

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

   // ...
}

Как я могу сделать это правильно? Пожалуйста, помогите

Я знаю, что, возможно, здесь слишком много просят, но если вы можете опираться на мою структуру выше, покажите мне, как добавить веб-модуль, я был бы очень признателен. Некоторые коды были бы потрясающими. Я все еще учусь, поэтому, если мой дизайн имеет недостатки, не стесняйтесь копировать его, я перепроектирую все, если буду убежден, что есть лучший способ сделать это. Суть в том, что есть набор бизнес-логики, и я хочу получить доступ к ним через application client и web interface. Например, у glassfishv3 есть веб-интерфейс и консоль администратора

Ответы [ 4 ]

6 голосов
/ 17 июня 2011

Не могли бы вы, ребята, сказать мне, где я должен поместить / создать еще один файл persistence.xml с транзакцией-type = "JTA"? Внутри моего веб-модуля или создать отдельный EJB?

Это не отличается. Это все еще должно пойти в /META-INF/persistence.xml файле. Если ваш проект представляет собой WAR, поместите его в веб-проект. Или, если он представляет EAR, поместите его в проект EJB.


В JSF я использую Managed Bean, как мой управляемый компонент вызывает метод из EntityUtil? Я задаю этот вопрос, потому что обычно у меня где-то есть EJB-компонент, поэтому, если я хочу, чтобы мой управляемый компонент получил к нему доступ, я внедряю EJB с помощью аннотации @EJB. Поскольку EntityUtil не аннотирован как EJB, как я могу получить к нему доступ из Managed Bean?

В теории вы можете просто создать новый EJB, который будет составлять / делегировать EntityUtil и, в свою очередь, внедрять этот EJB в управляемый компонент.

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

    // ...
}

Однако ... Ваш EntityUtil является не безопасным для потоков . Все в вашем EntityUtil есть static. Веб-приложение Java EE - это многопоточная среда. Несколько одновременно работающих пользователей используют одну и ту же кодовую базу одновременно. Все открытые статические переменные являются общими для всех пользователей. Когда пользователь набирает close() на вашем EntityUtil, это повлияет на всех текущих пользователей веб-приложения. Поэтому те, кто занят транзакцией, получат исключение.

Независимо от того, является ли он клиентом или веб-приложением, вы должны создавать EntityManagerFactory только один раз при запуске приложения, повторно использовать один и тот же экземпляр в течение всего срока службы приложения и закрывать его только при завершении работы приложения. EntityManager необходимо создавать только один раз для транзакции или сеанса и закрывать к концу транзакции или сеанса. В Java EE 6 с типом транзакции JTA транзакции полностью управляются контейнером. Но в клиентском приложении с локальным типом транзакции ресурса вам нужно управлять транзакциями самостоятельно.

Я бы предложил переписать ваш EntityUtil на DAO-подобный интерфейс, который должен иметь различные реализации для клиентского приложения и веб-приложения.

public interface SomeDAO {

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

Вот как бы вы реализовали это для клиентского приложения:

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

    // ...
}

и используйте его следующим образом:

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

Вот как бы вы реализовали это для веб-приложения:

@Stateless
public class SomeDAOEJB implements SomeDAO {

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

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

    // ...
}

и используйте его следующим образом

@ManagedBean
@RequestScoped
public class Bean {

    @EJB
    private SomeDAO someDAO;
    private Some some;

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

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

    // ...
}
2 голосов
/ 21 июня 2011

@ Гарри Вы обсуждаете три слоя 1) Презентация 2) Бизнес 3) Постоянство

Случай 1 - Клиент приложения - Вы использовали основной класс для представления, validatePDF, processPDF, createPrintJob - ваш бизнес-уровень. 3) EntityDAOClient и JPA - ваш уровень персистентности.

Случай 2 - Веб-модуль - вы использовали (хотите использовать) jsf для презентации, CoreEJB для настойчивости - мне не ясно, куда вы намереваетесь поместить бизнес-логику, потому что сейчас плохо воспринимать это как другой класс ejb.

Основная проблема в этом подходе - «Использование двух разных подходов для уровня бизнес-логики». Случай 1 - обычный Java. Случай 2 Ejb.

Когда вы используете простой Java, вы теряете роскошь внедрения зависимостей, управления TX, параллелизма, перехватчиков и т. Д.

Когда вы используете EJB - вы теряете гибкость в совместном использовании кода с клиентом приложения.

Решение - использовать CDI. С CDI вы можете интегрировать оба ваших варианта использования. Это работает как мост между тремя слоями. Предоставьте клиенту приложения всю мощь внедрения зависимостей, перехватчиков, декораторов.

По сути, нулевые изменения в вашем бизнес-уровне и коде постоянного уровня в клиенте приложения, а также в JavaEE.

Я упоминал в модели программирования CDI, что вам не нужно использовать @managed. Все pojo - это CDI-бобы.

В CDI вам не нужно писать основной класс. Начните писать бизнес-логику, как показано ниже

<code>
//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 </p>

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

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

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

<p>
<code>
// this class is same for both JavaEE and Client
public class EntityDAOClient implements EntityDAO {</p>

<p>private @Inject EntityManager em;</p>

<p>    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;
    }</p>

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

Чтобы запустить приложение-клиент

<code>
java org.jboss.weld.environment.se.StartMain
Для большего количества мяса проверьте www.seamframework.org
0 голосов
/ 27 июня 2011

Я хочу извиниться за мой поздний ответ.Я отставал от графика другого проекта, поэтому у меня нет времени написать правильный ответ на ответы BalusC и kiran.

kiran спросите меня: 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.

Ответ : Привет, Киран, суть в том, что я хочу, чтобы тот же набор логики был доступен накак на стороне клиента приложения, так и на стороне веб-модуля.Как сервер приложений Glassfish.У вас есть версия для командной строки и версия веб-модуля для доступа к одним и тем же ресурсам.

BalusC предложение использовать RESOURCE_LOCAL прекрасно работает, если я просто использую приложение-клиент.Однако, добавив веб-модуль поверх него, я больше не могу заставить его работать, даже несмотря на то, что BalusC пытался работать со мной.Вы, ребята, можете прочитать ответ BalusC и мою реализацию его идеи выше (код из исходного поста - моя реализация идеи BalusC).Если я добавлю клиент приложения в путь к классу проекта Java EE (EAR), он не получит ошибку компиляции при попытке получить доступ к логике из клиента приложения, но при запуске веб-модуля генерируется исключение, говорящее о том, что он не видиткласс / метод, к которому я пытаюсь получить доступ из App Client.Поэтому я решил перенести всю мою логику на EJB и сделать мой App Client очень тонким.Логики будут доступны для App Client через интерфейс javax.ejb.Remote, а для веб-модуля - через интерфейс javax.ejb.Local.Ниже приведен мой общий план этой идеи.

Это мой thin клиент приложения (основной)

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

Итак, на моей стороне EJB, какой пакет внутри проекта Java EE (EAR), содержащий EJB и веб-модуль, у меня есть

@Stateless
public class CoreMainEJB implements CoreMainEJBRemote, CoreMainEJBLocal {

    @EJB
    private PackageProcessor packageProcessor;

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

Обратите внимание, что поскольку все логики теперь находятся внутри EJB, я могу использовать тип транзакции JTA, который управляется контейнером.Мне это нравится больше, чем самому.

Некоторые люди предлагают разоблачить бизнес-логику через RESTful.Поскольку я не очень хорошо знаю о RESTful, я пока остановлюсь на этой реализации.Спасибо за вашу помощь BalusC и kiran.

0 голосов
/ 21 июня 2011

вы можете сделать несколько небольших примеров проектов с NetBeans и посмотреть, как это работает. NetBeans генерирует все эти «печальные» вещи для вас, так что вы можете изучить их и сделать это самостоятельно.

...