Внедрить EJB в JAX-RS (сервис RESTful) - PullRequest
70 голосов
/ 12 июня 2010

Я пытаюсь добавить EJB без сохранения состояния в мой веб-сервис 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

Ребята, можете показать какой-нибудь рабочий пример?

Ответы [ 7 ]

111 голосов
/ 12 июня 2010

Я не уверен, что это должно работать. Так что либо:

Вариант 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;

    //...
}

См:

14 голосов
/ 12 октября 2012

Эта тема довольно старая, тем не менее я боролся с той же проблемой только вчера.Вот мое решение:

Просто сделайте BookResource управляемым бином через @javax.annotation.ManagedBean на уровне класса.

Чтобы это работало, вам нужно включить CDIwith 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 является частью файла war.Если BookResource упакован с ejbs, поместите его в META-INF.

Если вы хотите использовать @EJB, все готово.Если вы хотите внедрить EJB через @Inject, то необходимо добавить bean.xml в файл ejbs jar и в META-INF.

Что вы делаете: вы просто сообщаете контейнеру, чторесурс должен управляться контейнером.Для этого он поддерживает инъекцию, а также события жизненного цикла.Таким образом, у вас есть бизнес-фасад, не превращающий его в EJB.

Вам не нужно расширять javax.ws.rs.core.Application, чтобы это работало.BookResource в качестве корневого ресурса автоматически ограничивает запрос.

Протестировано с Glassfish 3.1.2 и проектом maven.

Удачное кодирование.

10 голосов
/ 07 марта 2012

Вы сможете делать инъекцию в ресурс JAX-RS, не делая его компонентом EJB или CDI.Но вы должны помнить, что ваш ресурс JAX-RS не должен быть одноэлементным.

Итак, вы настраиваете свое приложение с помощью этого кода.Это делает BookResource class per-request 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 class singleton JAX-RS ресурс, то вы добавите getSingletons

public Set<Object> getSingletons() {
  singletons.add(new BookResource());
  return singletons;
}

, тогда вы создали экземпляркоторый не управляется средой исполнения JAX-RS, и никто в контейнере не заботится о том, чтобы что-то внедрить.

5 голосов
/ 01 сентября 2011

К сожалению, мой ответ слишком длинный для комментария, так что здесь идет.:)

Зек, я надеюсь, что ты осознаешь, что именно ты делаешь, продвигая свой боб в EJB, как это предложил Паскаль.К сожалению, так же просто, как в настоящее время в Java EE «сделать класс EJB», вы должны знать о последствиях этого.Каждый EJB создает накладные расходы наряду с дополнительными функциональными возможностями, которые он предоставляет: они осведомлены о транзакциях, имеют свои собственные контексты, они принимают участие в полном жизненном цикле EJB и т. Д.

Что я думаю, вы должны делать для чистогои многоразовый подход заключается в следующем: извлекать доступ к службам ваших серверов (которые, мы надеемся, доступны через SessionFacade :) в BusinessDelegate .Этот делегат должен использовать какой-то поиск JNDI (вероятно, ServiceLocator - да, они все еще действительны в Java EE!) Для доступа к вашему бэкэнду.

Ладно, не для записи: если вам действительно, действительно, действительно нужна инъекция, потому что вы не хотите писать JNDI-доступ вручную, вы все равно можете сделать свой делегат EJB, хотя это... ну, это просто неправильно.:) Таким образом, по крайней мере позже будет легко заменить его чем-то другим, если вы решите переключиться на подход поиска JNDI ...

3 голосов
/ 28 мая 2015

Я пытался сделать то же самое. Я использую 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 посмотреть советы

1 голос
/ 21 августа 2015

Арджан прав. Я создал другой класс для инициализации EJB вместо создания bean-компонента для RS

@Singleton
@LocalBean
public class Mediator {
    @EJB
    DatabaseInterface databaseFacade;

чтобы избежать нулевого указателя с:

@Path("stock")
public class StockResource {
    @EJB
    DatabaseInterface databaseFacade;
...

на самом деле работает на GF

0 голосов
/ 14 июля 2012

У меня та же проблема, и я решил ее, вызвав te EJB путем поиска по контексту (внедрение было невозможно, у меня была та же ошибка NullPointerException).

...