it-swarm.com.ru

Внедрить EJB в JAX-RS (сервис RESTful)

Я пытаюсь добавить Stateless 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

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

70
Zeck

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

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

    //...
}

Увидеть:

111
Pascal Thivent

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

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

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

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

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

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

Удачного кодирования.

14
Michael Simons

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

Итак, вы настраиваете свое приложение с этим кодом. Это делает BookResource class по запросу 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, и никто в контейнере не хочет ничего внедрять.

10
Martin

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

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

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

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

5
LeChe

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

3
duvo

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

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

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

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

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

1
Lukasz Ronikier

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

0
NIkoas