Как построить архитектуру плагина с использованием CDI - я использую Wildfly 10

Я хочу построить архитектуру на основе плагинов JEE. Основная идея - сделать что-то похожее на то, что есть в eclipse, но в контексте JEE. Моя цель - иметь минимум модулей в качестве ядра и позволить другим модулям расширять его функциональность. Для этого я реализовал тест, используя 4 модуля:

gauges: Defines and implements a gaugesregistry service, also defines a gauge POJO.
cashgauges: implements a gauge producer using CDI. this is a plugin mock.
othergauges: implements a gauge producer using CDI. this is a second plugin mock.
gauges-web: Contains a basic JSF view to query the gauges registry.

зависимости следующие:

cashgauges --> gauges
othergauges --> gauges
gauges-web --> gauges

Это делается с помощью jboss-deployment-structure.xml на каждый развернутый файл.

Развертывание выполняется в виде отдельных файлов:

gauges.jar 
cashgauges.jar 
othergauges.jar 
gauges-web.war

Все службы запускаются, но я вижу, что мой gaugesregistry создается несколько раз. Я запустил wildfly в режиме отладки, и я вижу, что у каждого модуля есть свой экземпляр класса калибровки: cashgauges и othergauges вызывают один и тот же метод (addGauge) в реестре, но экземпляры этого реестра не совпадают.

Это происходит в обоих случаях, используя @ApplicationScoped а также @Singleton аннотаций. Что я делаю неправильно?

Исходный код доступен на https://github.com/hatit/research

Через пару дней я рассматриваю возможность использования шаблона ServiceLocator и удаленных ссылок вместо CDI. Какие-либо предложения?

1 ответ

Отлично, я получил два -2 голоса (-4 репутации), потому что я задал сложную тему для разработчиков программного обеспечения?

Я искал о Stackru и нашел это

Основанное в 2008 году, Stack Overflow является крупнейшим и наиболее надежным онлайн-сообществом, в котором разработчики могут учиться, делиться своими знаниями и строить свою карьеру...

Если кто-то заинтересован в этой теме, то:

После нескольких часов понимания различий между CDI Beans и жизненным циклом EJB при использовании в качестве независимых модулей (JBoss Modules) я обнаружил:

Компоненты Singleton CDI Bean создаются один раз для каждого модуля, а не для всех модулей.

Чтобы избежать этого, я должен был создать Registry как сессионный компонент Singleton Enterprise. Это связано с новыми проблемами, инъекция CDI не работает среди модулей, поэтому мне пришлось упаковать производителя CDI (мне все равно, синглтон он или нет, только его производитель), который может быть создан любым модулем. Основная ответственность этого производителя - поиск в реестре EJB, чтобы избежать жесткого кодирования пути jndi каждый раз, когда мне нужен доступ к реестру.

Я изменил свой тривиальный пример для поддержки плагинов JSF, это пример того, что я использую в настоящее время.

Лицевые стороны модуля:

Интерфейс реестра:

public interface FaceletsModuleRegistry {
    void registerModule(String module);

    List<String> getRegisteredModules();
}

Реализация реестра:

@Local(FaceletsModuleRegistry.class)
@Singleton(name="FaceletsModuleRegistry")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Vetoed
public class FaceletsModuleRegistryImpl implements FaceletsModuleRegistry {

    private Set<String> registeredModuleNames = new TreeSet<>();

    @Override
    public void registerModule(String module) {
        registeredModuleNames.add(module);
    }

    @Override
    public List<String> getRegisteredModules() {
        return Collections.unmodifiableList(new ArrayList<>(registeredModuleNames));
    }

}

Реестр производителей:

@ApplicationScoped
public class FaceletsModuleRegistryBuilder {

    @EJB(lookup="java:global/facelets/FaceletsModuleRegistry!co.hatit.enterprise.facelets.services.FaceletsModuleRegistry")
    protected FaceletsModuleRegistry faceletsModuleRegistry;

    @Produces
    public FaceletsModuleRegistry getFaceletsModuleRegistry(){
        return faceletsModuleRegistry;
    }
}

Любой другой модуль, который я хочу подключить, реализует этот код (см. @Inject может использоваться на любом модуле, требующем доступа к экземпляру реестра Singleton):

@ApplicationScoped
public class InmueblesActivator {

    @Inject
    private FaceletsModuleRegistry faceletsModuleRegistry;

    public void init(@Observes @Initialized(ApplicationScoped.class) Object init){
        String moduleName = Module.getCallerModule().getIdentifier().getName();
        String name = StringUtils.substringBetween(moduleName, "deployment.", ".jar");
        faceletsModuleRegistry.registerModule(name);
    }

}

Затем я могу ссылаться на Registry из любого модуля как на действительно одноэлементный экземпляр (решил мою проблему с несколькими экземплярами одного и того же класса, когда использовались одноэлементные компоненты CDI среди нескольких модулей).

Теперь я могу подключить JEE-модули, не только Java-код, но и ресурсы Facelets:

public class FaceletsResourceHandler extends ResourceHandlerWrapper {

    Logger logger = LoggerFactory.getLogger(FaceletsResourceHandler.class);

    @Inject
    FaceletsModuleRegistry faceletsModuleRegistry;

    private ResourceHandler wrapped;

    public FaceletsResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewResource createViewResource(FacesContext context, final String name) {
        ViewResource resource = super.createViewResource(context, name);

        if (resource == null) {
            resource = new ViewResource() {
                @Override
                public URL getURL() {
                    try {
                        //iterates over plugins to find the required resource.
                        for(String module : faceletsModuleRegistry.getRegisteredModules()){
                            URL resource = Module.getCallerModule().getModuleLoader()
                                    .loadModule(ModuleIdentifier.create("deployment." + module + ".jar"))
                                    .getExportedResource("META-INF/resources" + name);
                            if (resource != null) return resource;
                        }
                    } catch (ModuleLoadException e) {
                        throw new FacesException(e);
                    }

                    return null;
                }
            };
        }

        return resource;
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}
Другие вопросы по тегам