Подтвердить что ты не робот

Подключение к приложениям @EJB или @PersistenceContext в модульных тестах JAX-RS

Мне нравится изучать JAX-RS и Джерси, но я попал в roadblock, пытаясь протестировать простой ресурс, который нужно ввести DAO, что-то вроде этого:

@Stateless
@Path("simple")
public class SimpleResource {

    @PersistenceContext
    private EntityManager em;

// @GET, etc...

}

(я перейду к более абстрактному шаблону DAO, но проблема будет такой же, то есть, как я вставляю DAO @EJB?)

В моих модульных тестах я использую встроенный сервер Jetty, который настраивает Джерси в его web.xml, и я хотел бы подключиться к жизненному циклу ресурса, чтобы я мог вводить макет EntityManager, но я не нашел чистый ответ после многого поиска. Вы можете помочь? Некоторые возможные направления, с которыми я столкнулся:

1) Используйте контекстный поиск JNDI в моем коде для получения DAO bean и зарегистрируйте макет объекта в тестах.

Вместо @EJB или @PersistenceContext используйте в конструкторе ресурса что-то вроде этого:

  theDAO = (DAOImpl) new InitialContext().lookup("java:global/EJB/DAOImpl");

Однако это означает, что моя тестовая среда должна поддерживать JNDI, и это происходит в Jetty, вероятно, будет связано с некоторой болью. Кроме того, он не использует подход чистой аннотации.

2) Используйте инъекцию метода.

Внесите в метод, чтобы я мог установить пост-инстанцирование DAO, например,

@PersistenceContext(name = "persistence/pu00")
public void setPersistenceUnit00(final EntityManager em) {
    em00 = em;
}

ИЛИ

private MyEjbInterface myEjb;
@EJB(mappedName="ejb/MyEjb")
public void setMyEjb(MyEjb myEjb) {
    this.myEjb = myEjb;
}

Однако для этого мне нужен экземпляр, созданный Джерси, например SimpleResource. Как это получить?

3) Используйте отражение.

Вид инъекции DIY, что-то вроде:

public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) {
    Field setId = instanceFieldClass.getDeclaredField(fieldName);
    setId.setAccessible(true);
    setId.set(instance, fieldValue);
}

Опять же, мне нужен экземпляр, созданный Джерси.

4) Используйте поставщика инъекций.

Я все еще отрывочен в отношении того, как это работает, но похоже, что Джерси предоставляет средство для определения индивидуальных инъецируемых аннотаций, например,

    @Provider
    public class EJBProvider implements InjectableProvider<EJB, Type> {

    public ComponentScope getScope() {
    return ComponentScope.Singleton;
    }

    public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
    if (!(t instanceof Class)) {
        return null;
    }
    try {
        Class c = (Class) t;
        Context ic = new InitialContext();
        final Object o = ic.lookup(c.getName());
        return new Injectable<Object>() {
        public Object getValue() {
            return o;
        }
        };
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
    }
}

Вариант с использованием вспомогательного класса:

Server server = new Server(8080);
Context root = new Context(server,"/",Context.SESSIONS);

ResourceConfig rc = new PackagesResourceConfig("edu.mit.senseable.livesingapore.platform.restws.representations");
rc.getSingletons().add(new SingletonTypeInjectableProvider<javax.ws.rs.core.Context, Myobj>(Myobj.class, new Myobj(12,13)){});

root.addServlet(new ServletHolder(new ServletContainer(rc)), "/");
server.start();

При таком использовании:

@Path("/helloworld")
public class HelloWorldResource {
    @Context Myobj myClass;
    ....
}

Является ли это жизнеспособным для @EJB или @PersistenceContext?

5) Расширьте javax.ws.rs.core.Application.

Набросок на это, но:

@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {

  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> classes = new HashSet<Class<?>>();

  public InjectionApplication() {
    // no instance is created, just class is listed
    classes.add(BookResource.class);
  }

  @Override
  public Set<Class<?>> getClasses() {
    return classes;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

6) Расширьте ServletContainer.

Более старый стиль использования InjectableProvider? Выглядит сложнее:

public class ServletAdapter extends ServletContainer {

@Override
protected void configure(ServletConfig servletConfig, ResourceConfig rc, WebApplication wa) {
    super.configure(servletConfig, rc, wa);

    rc.getSingletons().add(new InjectableProvider<Resource, Type>() {

        public ComponentScope getScope() {
            return ComponentScope.Singleton;
        }

        public Injectable<Object> getInjectable(ComponentContext ic, Resource r, Type c) {
            final Holder value = new Holder();

                Context ctx = new InitialContext();
                try {
                    value.value = ctx.lookup(r.name());
                } catch (NamingException ex) {

                    value.value = ctx.lookup("java:comp/env/" + r.name());
                }

            return new Injectable<Object>() {
                public Object getValue() {
                    return value.value;
                }
            };
        }
    });
}
}  

7) Используйте встроенный контейнер EJB.

Например, http://openejb.apache.org. Это довольно тяжело, и я ожидаю, что это будет беспорядочно работать. (Фактически, то, что начало меня по маршруту "Jetty + Jersey", было ошибкой в ​​GlassFish Embedded вокруг входа в систему безопасности. Я также посмотрел на другие контейнеры приложений Java EE 6, такие как JBoss AS, но у каждого были проблемы во встроенном режиме с ограниченным пользователем поддержка сообщества.)

8) Используйте стороннюю IoC-библиотеку, например Spring или Guice.

Spring, по-видимому, обычно используется для решения этих проблем (инъекции макетов при модульном тестировании), но я хотел избежать необходимости изучать другой большой набор API-интерфейсов - чистая Java EE была достаточно сложной задачей! Но я играю, если это лучшее решение. Я еще не внимательно посмотрел на Spring или Guice.

Вы успешно использовали какие-либо из них? Любые другие решения, которые вам нравятся? Я с нетерпением жду вашего совета по этому поводу. Спасибо заранее - матовый

4b9b3361

Ответ 1

Поскольку вы используете Netbeans, попробуйте:

Использование встроенного контейнера EJB для тестирования корпоративных приложений

В учебнике используется встроенный контейнер Glassfish и внедряется EJB, который инкапсулирует EntityManager (аналогично тому, что вы описали в первом варианте).

Ответ 2

Если вам нужен только EntityManager внутри встроенного контейнера Jetty, зачем использовать инъекцию в первую очередь? Вы можете просто поместить одну из реализаций JPA (например, eclipselink или hibernate) на свой класс-путь, настроить локальный блок постоянного ресурса, а затем получить его следующим образом:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("your unit name");
EntityManager em = emf.createEntityManager();

Было бы достаточно иметь что-то (возможно, статическое DAO factory?), которое ведет себя как ваш @EJB для тестирования ваших классов JAX-RS.

Если вы хотите, чтобы ваши модульные тесты были как можно ближе к среде Java EE, запустите их с помощью Arquillian (http://www.jboss.org/arquillian.html). Он запускает тесты непосредственно на контейнере Java EE - это легко, у него отличная документация.