Мне нравится изучать 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.
Вы успешно использовали какие-либо из них? Любые другие решения, которые вам нравятся? Я с нетерпением жду вашего совета по этому поводу. Спасибо заранее - матовый