Внедрить EJB в JAX-RS (служба RESTful)
Я пытаюсь внедрить EJB Stateless в свой веб-сервис JAX-RS через аннотации. К сожалению, EJB просто null
, и я пытаюсь использовать его NullPointerException
.
@Path("book")
public class BookResource {
@EJB
private BookEJB bookEJB;
public BookResource() {
}
@GET
@Produces("application/xml")
@Path("/{bookId}")
public Book getBookById(@PathParam("bookId") Integer id)
{
return bookEJB.findById(id);
}
}
Что я делаю неправильно?
Ниже приведена информация о моей машине:
- Glassfish 3.1
- Netbeans 6.9 RC 2
- Java EE 6
Можете ли вы, ребята, показать какой-нибудь рабочий пример?
Ответы
Ответ 1
Я не уверен, что это должно работать. Итак, либо:
Вариант 1: используйте SPI поставщика инъекций
Внедрите поставщика, который будет выполнять поиск и вводит EJB. См:
Пример для com.sun.jersey: jersey-server: 1.17:
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Type;
/**
* JAX-RS EJB Injection provider.
*/
@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;
}
}
}
Вариант 2: Сделайте BookResource EJB
@Stateless
@Path("book")
public class BookResource {
@EJB
private BookEJB bookEJB;
//...
}
См:
Вариант 3. Использование CDI
@Path("book")
@RequestScoped
public class BookResource {
@Inject
private BookEJB bookEJB;
//...
}
См:
Ответ 2
Эта ветка довольно старая, тем не менее я боролся с той же проблемой только вчера. Вот мое решение:
Просто сделайте BookResource управляемым bean через @javax.annotation.ManagedBean на уровне класса.
Для этого вам нужно включить CDI с beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Этот файл должен быть в WEB-INF, если BookResource является частью военного файла. Если BookResource упакован с ejbs, поместите его в META-INF.
Если вы хотите использовать @EJB, все готово. Если вы хотите вставить EJB через @Inject, а beans.xml должен быть помещен в файл jar файла ejbs в META-INF.
Что вы делаете: вы просто указываете контейнеру, что ресурс должен управляться контейнером. Поэтому он поддерживает инъекции, а также события жизненного цикла. Таким образом, у вас есть свой бизнес-фасад, не продвигая его в EJB.
Вам не нужно расширять javax.ws.rs.core.Application, чтобы это работало. BookResource как корневой ресурс автоматически запрашивает область действия.
Протестировано с Glassfish 3.1.2 и проектом maven.
Счастливое кодирование.
Ответ 3
Вы сможете делать инъекцию в ресурс JAX-RS, не создавая его компонент EJB или CDI. Но вы должны помнить, что ваш ресурс JAX-RS не должен быть одиночным.
Итак, вы настраиваете приложение с помощью этого кода. Это делает ресурс BookResource класса для каждого запроса JAX-RS.
@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;
}
}
С помощью этой настройки вы позволяете JAX-RS создавать экземпляры BookResource для вас по запросу и также вводить все необходимые зависимости. Если вы создаете ресурс BookResource класса singleton JAX-RS, это означает, что вы помещаете getSingletons
public Set<Object> getSingletons() {
singletons.add(new BookResource());
return singletons;
}
то вы создали экземпляр, который не управляется приложением JAX-RS, и никто в контейнере не хочет ничего вводить.
Ответ 4
К сожалению, мой ответ слишком длинный для комментария, так что здесь идет.:)
Zeck, я надеюсь, что вы знаете, что именно вы делаете, продвигая ваш bean в EJB, как это предлагает Паскаль. К сожалению, так же легко, как сейчас, когда Java EE "делает класс EJB", вы должны знать о последствиях этого. Каждый EJB создает накладные расходы вместе с дополнительными функциями, которые он предоставляет: они осведомлены о транзакциях, имеют свои собственные контексты, участвуют в полном жизненном цикле EJB и т.д.
То, что я думаю, что вы должны делать для чистого и многоразового подхода, заключается в следующем: извлеките доступ к вашим серверам (к которым, надеюсь, обращаются через SessionFacade:) в BusinessDelegate. Этот делегат должен использовать какой-то поиск JNDI (возможно, ServiceLocator - да, они все еще действительны в Java EE!) Для доступа к вашему бэкенд.
Хорошо, вне записи: если вам действительно нужна действительно инъекция, потому что вы не хотите писать JNDI-доступ вручную, вы все равно можете сделать свой делегат EJB, хотя он... ну, он просто чувствует себя не так,:)
Таким образом, по крайней мере, будет легко заменить его потом чем-то другим, если вы решите переключиться на подход поиска JNDI...
Ответ 5
Я пытался сделать то же самое. Я использую EJB 3.1 и имею развернутое мое приложение как EAR с отдельным проектом EJB. Как указывал Jav_Rock, я использую контекстный поиск.
@Path("book")
public class BookResource {
@EJB
BookEJB bookEJB;
public BookResource() {
try {
String lookupName = "java:global/my_app/my_ejb_module/BookEJB";
bookEJB = (BookEJB) InitialContext.doLookup(lookupName);
} catch (NamingException e) {
e.printStackTrace();
}
}
@GET
@Produces("application/xml")
@Path("/{bookId}")
public Book getBookById(@PathParam("bookId") Integer id) {
return bookEJB.findById(id);
}
}
См. приведенную ниже ссылку для очень полезных подсказок JNDI.
Советы JNDI по поиску
Ответ 6
Арьян прав. Я создал еще один класс для инициализации EJB вместо создания bean для RS
@Singleton
@LocalBean
public class Mediator {
@EJB
DatabaseInterface databaseFacade;
чтобы избежать пустого указателя с помощью:
@Path("stock")
public class StockResource {
@EJB
DatabaseInterface databaseFacade;
...
он действительно работает на GF
Ответ 7
У меня та же проблема, и я решил ее вызвать te EJB с помощью контекстного поиска (инъекция была невозможна, у меня была такая же ошибка NullPointerException).