CDI: использование перехватчиков в разных модулях /bean архивах

Приложение My Java EE 6 включает в себя войну и модуль ejb, упакованные в файл уха. Я использую CDI для DI (т.е. У меня есть файл beans.xml в обоих модулях). Я хочу использовать перехватчик регистрации, который также определен в модуле ejb в военном модуле. Я включил перехватчик в ejb beans.xml:

<beans>
    <interceptors>
        <class>com.test.interceptor.LoggingInterceptor</class>
    </interceptors>
</beans>

Это работает только для классов, которые аннотируются с помощью перехватчика в модуле ejb. Классы в военном модуле не перехватываются (хотя они тоже аннотируются с перехватчиком). Я думал, что решение будет состоять в том, чтобы включить перехватчик в военном перехватчике (как и выше). Но приложение не может быть развернуто со следующим сообщением:

SEVERE: исключение при загрузке приложения: WELD-001417 Включен класс класса перехватчика com.test.interceptor.LoggingInterceptor не является аннотированным @Interceptor и не зарегистрирован через переносимое расширение

My LoggingInterceptor выглядит следующим образом:

@Log
@Interceptor
public class LoggingInterceptor {
    private static final Logger logger =  Logger.getLogger(LoggingInterceptor.class.getName());

    static {
        logger.setLevel(Level.ALL);
    }

    @AroundInvoke
    public Object logMethod(InvocationContext ctx) throws Exception {
        logger.log(Level.FINE, "ENTRY {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
        long startTime = System.nanoTime();
        try {
            return ctx.proceed();
        } finally {
            long diffTime = System.nanoTime() - startTime;
            logger.log(Level.FINE, "RETURN {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
            logger.log(Level.FINE, "{0} took {1} ms", new Object[]{ ctx.getMethod(),
                    TimeUnit.MILLISECONDS.convert(diffTime, TimeUnit.NANOSECONDS)});
        }
    }

}

И привязка перехватчика:

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Log {}

Как я могу использовать перехватчик для обоих модулей?

Ответы

Ответ 1

Спецификация J2EE 7 говорит (ссылка):

Перехватчики, указанные вами в файле beans.xml, применяются только к классы в том же архиве. Используйте аннотацию @Priority, чтобы указать перехватчиков по всему миру для приложения, которое состоит из нескольких Модули

Это решение имеет то преимущество, что оно не зависит от поставщика.

Пример:

@Logged
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class LoggedInterceptor implements Serializable { ... }

Ответ 2

Слишком поздно, но если у кого-то все еще есть эта проблема. Оба модуля должны загружаться одним и тем же загрузчиком классов, чтобы можно было использовать перехватчик по разным модулям, по крайней мере, в WebSphere 8b2. В WebSphere этот параметр можно переключить в консоли администрирования: Приложения > Типы приложений > Корпоративные приложения WebSphere > [имя вашего приложения] > Обнаружение загрузки и обнаружение классa > Политика загрузчика WAR-класса = Одиночный загрузчик классов для приложения.
Перехватчик должен быть включен только ONCE в beans.xml.

Ответ 3

Интересно, не испытывает ли ваша WAR недостаток classloader в вашей ejb-jar? Я думаю, что в идеале 299-перехватчики будут находиться в их собственной банке, видимой как для EJB, так и для веб-модулей и включаться в оба их beans.xml.

Ответ 4

У меня такая же проблема в JBoss AS 6.0/6.1 (ночная сборка) и исправлена ​​с помощью отключение отдельных загрузчиков классов (вариант 1), но будьте предельно осторожны с этим. Разделение загрузчиков классов не было введено ни по какой причине, поэтому, по-видимому, на дороге есть новые проблемы...

Это - отчет о джире, пожалуйста, проголосуйте за него: -)

Ответ 5

У меня была точно такая же проблема с моим протоколированием перехватчика на JBoss 7 и исправлена ​​его, наложив полный баннер перехватчика на приложение.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <overlays>
                    <overlay>
                        <groupId>com.github.t1</groupId>
                        <artifactId>logging-interceptor</artifactId>
                        <type>jar</type>
                        <targetPath>WEB-INF/classes</targetPath>
                    </overlay>
                </overlays>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.github.t1</groupId>
        <artifactId>logging-interceptor</artifactId>
        <version>1.1</version>
        <optional>true</optional>
    </dependency>
</dependencies>

Вам все равно придется активировать перехватчик в приложении breans.xml.

Не нравится, но он работает. В Java EE 7 он работает без активации, аннотируя перехватчик как @Priority.

Ответ 6

Если у вас нет контроля над внешней зависимостью и вы все еще хотите включить перехватчики без beans.xml, вы можете написать расширение CDI:

package my.package;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterTypeDiscovery;
import javax.enterprise.inject.spi.Extension;

public class MyCdiExtension implements Extension {

    public void observeAfterTypeDiscovery(@Observes AfterTypeDiscovery afterTypeDiscovery) {
        afterTypeDiscovery.getInterceptors().add(SomeExternalInterceptor.class);
    }
}

Добавить файл resources/META-INF/services/javax.enterprise.inject.spi.Extension с содержанием:

my.package.MyCdiExtension