Используйте @EJB как аннотацию впрыска в сварке - PullRequest
1 голос
/ 29 ноября 2011

У меня есть приложение, которое является частью JavaEE (на стороне сервера), частью JavaSE (на стороне клиента).Поскольку я хочу, чтобы этот клиент был хорошо структурирован, я использую в нем Weld для внедрения различных компонентов.Некоторые из этих компонентов должны быть на стороне сервера @ EJB.

Я планирую расширить архитектуру Weld, чтобы обеспечить «компонент», позволяющий Weld выполнять поиск JNDI для загрузки экземпляров EJB при попытке ссылки клиентаих.Но как мне это сделать?

В других версиях я хочу иметь

на стороне клиента

public class ClientCode {
    public @Inject @EJB MyEJBInterface;
}

на стороне сервера

@Stateless
public class MyEJB implements MyEJBInterface {
}

С Weld "неявно", выполняющим поиск JNDI при создании объектов ClientCode.Как я могу это сделать?

Ответы [ 2 ]

3 голосов
/ 16 декабря 2011

По сути, для этого необходимо написать так называемое портативное расширение CDI .

Но, поскольку оно довольно длинное и требует нескольких настроек, позвольте мне объяснить это подробнее.

Переносимое расширение

Как объясняет документ сварки, первым шагом является создание класса, реализующего интерфейс тегирования Extension, в котором будет написан код, соответствующий интересным событиям CDI.,В этом конкретном случае наиболее интересное событие, на мой взгляд, AfterBeanDiscovery .Действительно, это событие происходит после того, как все «локальные» bean-компоненты были найдены с помощью CDI.

Таким образом, расширение записи, более того, пишет обработчик для этого события:

public void loadJndiBeansFromServer(
        @Observes AfterBeanDiscovery beanDiscovery, BeanManager beanManager)
        throws NamingException, ClassNotFoundException, IOException {
    // Due to my inability to navigate in server JNDI naming (a weird issue in Glassfish naming)
    // This props maps interface class to JNDI name for its server-side
    Properties interfacesToNames = extractInterfacesToNames();

    // JNDI properties
    Properties jndiProperties = new Properties();
    Context context = new InitialContext();
    for (Entry<?, ?> entry : interfacesToNames.entrySet()) {
        String interfaceName = entry.getKey().toString();
        Class<?> interfaceClass = Class.forName(interfaceName);
        String jndiName = entry.getValue().toString();
        Bean<?> jndiBean = createJndIBeanFor(beanManager, interfaceClass, jndiName, jndiProperties);
        beanDiscovery.addBean(jndiBean);
    }
}

Создание bean-компонента не является тривиальной операцией: оно требует преобразования «базовых» объектов Java-отражения в более сложные объекты сварки (ну, в моем случае)

private <Type> Bean<Type> createJndIBeanFor(BeanManager beanManager, Class<Type> interfaceClass,
        String jndiName, Properties p) {
    AnnotatedType<Type> annotatedType = beanManager
            .createAnnotatedType(interfaceClass);
    // Creating injection target in a classical way will fail, as interfaceClass is the interface of an EJB
    JndiBean<Type> beanToAdd = new JndiBean<Type>(interfaceClass, jndiName, p);
    return beanToAdd;
}

Наконец, нужно написать класс JndiBean.Но раньше требуется небольшое перемещение в области аннотаций.

Определение используемой аннотации

Сначала я использовал @EJB.Идея bad : Weld использует вызов метода-аннотации для вызова метода result для создания хеш-кода bean-компонента!Итак, я создал свою собственную аннотацию @JndiClient, которая не содержит ни методов, ни констант, чтобы она была максимально простой.

Создание клиентского компонента JNDI

Два понятияслиться здесь.

  • С одной стороны, интерфейс Bean кажется (мне) определять, что такое бин.
  • С другой стороны, InjectionTarget определяет, в определенной степени, жизненный цикл этого самого компонента.

Из литературы, которую я смог найти, эти две реализации интерфейсов часто разделяют, по крайней мере, некоторые из ихгосударство.Поэтому я решил использовать их с помощью уникального класса: JndiBean!

В этом бине большинство методов оставлены пустыми (или имеют значение по умолчанию), за исключением

  • Bean#getTypes, который должен возвращать удаленный интерфейс EJB и все расширенные интерфейсы @Remote (так как через этот интерфейс могут вызываться методы из этих интерфейсов)
  • Bean#getQualifiers, который возвращает набор, содержащий только один элемент: AnnotationLiteral, соответствующий @JndiClient интерфейсу.
  • Contextual#create (вызабыл расширенный Бин Contextual, не так ли?) который выполняет поиск:

    @Override
    public T create(CreationalContext<T> arg0) {
        // Some classloading confusion occurs here in my case, but I guess they're of no interest to you
    
        try {
            Hashtable contextProps = new Hashtable();
            contextProps.putAll(jndiProperties);
            Context context = new InitialContext(contextProps);
            Object serverSide = context.lookup(jndiName);
            return interfaceClass.cast(serverSide);
        } catch (NamingException e) {
            // An unchecked exception to go through weld and break the world appart
            throw new LookupFailed(e);
        }
    }
    

И это все

Использование?

Ну,теперь, в своем коде Java-клиента Glassfish, я могу написать такие вещи, как

private @Inject @JndiClient MyRemoteEJB instance;

И это работает без проблем

Будущее?

Ну, пока,учетные данные пользователя не управляются, но я полагаю, что это вполне возможно при использовании C CDI: Контексты ... о нет!Не контексты: Области применения !

1 голос
/ 30 ноября 2011

Раздел 3.5 спецификации CDI должен помочь. Вы также можете использовать некоторые свойства в аннотации EJB. Также (вероятно, вам не нужно об этом говорить) убедитесь, что на клиенте правильно настроен JNDI для ссылки на сервер, и запакуйте все необходимые интерфейсы в ваш клиентский jar.

...