getNamespace() {
+ return this.namespace;
+ }
+
+ @Override
+ public Stability getStability() {
+ return Stability.DEFAULT;
+ }
+
+ @Override
+ public PersistentResourceXMLDescription getXMLDescription() {
+ PersistentResourceXMLDescription.Factory factory = PersistentResourceXMLDescription.factory(this);
+ return factory.builder(BCESubsystemRegistrar.PATH).build();
+ }
+ }
+
+ /**
+ * Registrar for the 'bce' subsystem root resource.
+ */
+ private static final class BCESubsystemRegistrar implements SubsystemResourceDefinitionRegistrar {
+
+ static final String NAME = "bce";
+ static final PathElement PATH = SubsystemResourceDefinitionRegistrar.pathElement(NAME);
+ static final ParentResourceDescriptionResolver RESOLVER = new SubsystemResourceDescriptionResolver(NAME, BCESubsystemRegistrar.class);
+
+ @Override
+ public ManagementResourceRegistration register(SubsystemRegistration parent, ManagementResourceRegistrationContext context) {
+ ManagementResourceRegistration registration = parent.registerSubsystemModel(ResourceDefinition.builder(ResourceRegistration.of(PATH), RESOLVER).build());
+ ResourceDescriptor descriptor = ResourceDescriptor.builder(RESOLVER)
+ .withAddOperationRestartFlag(OperationEntry.Flag.RESTART_ALL_SERVICES)
+ .withRemoveOperationRestartFlag(OperationEntry.Flag.RESTART_ALL_SERVICES)
+ .withRuntimeHandler(new ResourceOperationRuntimeHandler() {
+ @Override
+ public void addRuntime(OperationContext context, ModelNode model) {
+ if (context.isBooting()) {
+ context.addStep(new AbstractDeploymentChainStep() {
+ @Override
+ protected void execute(DeploymentProcessorTarget processorTarget) {
+ processorTarget.addDeploymentProcessor(NAME, Phase.INSTALL, Phase.INSTALL_WELD_DEPLOYMENT, new BCEDeploymentUnitProcessor());
+
+ }
+ }, OperationContext.Stage.RUNTIME);
+ } else {
+ context.reloadRequired();
+ }
+ }
+
+ @Override
+ public void removeRuntime(OperationContext context, ModelNode model) throws OperationFailedException {
+ context.reloadRequired();
+ }
+ })
+ .build();
+ ManagementResourceRegistrar.of(descriptor).register(registration);
+ return registration;
+ }
+ }
+
+ private static final class BCEDeploymentUnitProcessor implements DeploymentUnitProcessor {
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+ try {
+ final WeldCapability weldCapability = deploymentUnit.getAttachment(Attachments.CAPABILITY_SERVICE_SUPPORT)
+ .getCapabilityRuntimeAPI(Capabilities.WELD_CAPABILITY_NAME, WeldCapability.class);
+ weldCapability.registerBuildCompatibleExtension(RegisteredExtension.class, deploymentUnit);
+ } catch (CapabilityServiceSupport.NoSuchCapabilityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+
+}
diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/DummyBean.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/DummyBean.java
new file mode 100644
index 000000000000..623ab11b6040
--- /dev/null
+++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/DummyBean.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.jboss.as.test.integration.weld.extensions.buildcompatible.subsystem;
+
+import jakarta.enterprise.context.Dependent;
+
+@Dependent
+public class DummyBean {
+}
diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredBean.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredBean.java
new file mode 100644
index 000000000000..b9b59db56cda
--- /dev/null
+++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredBean.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.jboss.as.test.integration.weld.extensions.buildcompatible.subsystem;
+
+// no bean defining annotation, processed via RegisteredExtension
+public class RegisteredBean {
+}
diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredExtension.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredExtension.java
new file mode 100644
index 000000000000..17b6e6d6ad33
--- /dev/null
+++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/RegisteredExtension.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.jboss.as.test.integration.weld.extensions.buildcompatible.subsystem;
+
+import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
+import jakarta.enterprise.inject.build.compatible.spi.Discovery;
+import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses;
+
+public class RegisteredExtension implements BuildCompatibleExtension {
+
+ @Discovery
+ public void discovery(ScannedClasses sc) {
+ sc.add(RegisteredBean.class.getName());
+ }
+}
diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/SubsystemBceRegistrationTest.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/SubsystemBceRegistrationTest.java
new file mode 100644
index 000000000000..a711b0d69736
--- /dev/null
+++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/SubsystemBceRegistrationTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.jboss.as.test.integration.weld.extensions.buildcompatible.subsystem;
+
+import jakarta.inject.Inject;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.as.arquillian.api.ServerSetup;
+import org.jboss.as.arquillian.api.ServerSetupTask;
+import org.jboss.as.arquillian.container.ManagementClient;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.as.test.module.util.TestModule;
+import org.jboss.as.test.shared.ServerReload;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.net.URL;
+
+@RunWith(Arquillian.class)
+@ServerSetup(SubsystemBceRegistrationTest.SetupTask.class)
+public class SubsystemBceRegistrationTest {
+
+ @Deployment
+ public static WebArchive getDeployment() throws Exception {
+ return ShrinkWrap.create(WebArchive.class)
+ .addClasses(SubsystemBceRegistrationTest.class, DummyBean.class, TestModule.class, RegisteredBean.class)
+ .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @Inject
+ DummyBean dummyBean;
+
+ @Inject
+ RegisteredBean registeredBean;
+
+ @Test
+ public void testBceRegisteredAndExecuted() {
+ // check plain WAR deployment
+ Assert.assertNotNull(dummyBean);
+
+ // verify BCE was executed; if so, RegisteredBean would now be resolvable
+ Assert.assertNotNull(registeredBean);
+ }
+
+ public static class SetupTask implements ServerSetupTask {
+ private static final String MODULE_NAME = "build-compatible-extension";
+ private static TestModule testModule;
+
+ @Override
+ public void setup(ManagementClient managementClient, String containerId) throws Exception {
+ URL url = BCEExtension.class.getResource(MODULE_NAME + "-module.xml");
+ File moduleXmlFile = new File(url.toURI());
+ testModule = new TestModule("test." + MODULE_NAME, moduleXmlFile);
+ testModule.addResource("bce-extension.jar")
+ .addClasses(BCEExtension.class, RegisteredExtension.class, RegisteredBean.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
+ .addAsServiceProvider(org.jboss.as.controller.Extension.class, BCEExtension.class);
+ testModule.create();
+
+ managementClient.getControllerClient().execute(Util.createAddOperation(PathAddress.pathAddress("extension", "test." + MODULE_NAME)));
+ managementClient.getControllerClient().execute(Util.createAddOperation(PathAddress.pathAddress("subsystem", "bce")));
+
+ ServerReload.executeReloadAndWaitForCompletion(managementClient);
+ }
+
+ @Override
+ public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
+
+ managementClient.getControllerClient().execute(Util.createRemoveOperation(PathAddress.pathAddress("subsystem", "bce")));
+ managementClient.getControllerClient().execute(Util.createRemoveOperation(PathAddress.pathAddress("extension", "test." + MODULE_NAME)));
+
+ testModule.remove();
+
+ ServerReload.executeReloadAndWaitForCompletion(managementClient);
+ }
+ }
+}
diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/build-compatible-extension-module.xml b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/build-compatible-extension-module.xml
new file mode 100644
index 000000000000..ed32ee059842
--- /dev/null
+++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/weld/extensions/buildcompatible/subsystem/build-compatible-extension-module.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/integration/basic/src/test/modules/test/build-compatible-extension/main/module.xml b/testsuite/integration/basic/src/test/modules/test/build-compatible-extension/main/module.xml
new file mode 100644
index 000000000000..3822eb68ecfe
--- /dev/null
+++ b/testsuite/integration/basic/src/test/modules/test/build-compatible-extension/main/module.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/weld/common/src/main/java/org/jboss/as/weld/WeldCapability.java b/weld/common/src/main/java/org/jboss/as/weld/WeldCapability.java
index b40831d7ab35..1464cbe1800c 100644
--- a/weld/common/src/main/java/org/jboss/as/weld/WeldCapability.java
+++ b/weld/common/src/main/java/org/jboss/as/weld/WeldCapability.java
@@ -6,6 +6,8 @@
package org.jboss.as.weld;
import java.util.function.Supplier;
+
+import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.Extension;
@@ -26,15 +28,28 @@ public interface WeldCapability {
* Registers a CDI Portable Extension for the {@link DeploymentUnit} passed as argument to
* this method.
*
- * The extension is registered if only if the DeploymentUnit is part of a Weld Deployment. Specifically,
+ * The extension is registered if and only if the DeploymentUnit is part of a Weld Deployment. Specifically,
* if a call to {@link #isPartOfWeldDeployment(DeploymentUnit)} using the DeploymentUnit argument
- * returns {@code true}. Otherwise this method will return immediately.
+ * returns {@code true}. Otherwise, this method will return immediately.
*
* @param extension An instance of the CDI portable extension to add.
* @param unit The deployment unit where the extension will be registered.
*/
void registerExtensionInstance(final Extension extension, final DeploymentUnit unit);
+ /**
+ * Registers a CDI Build Compatible Extension for the {@link DeploymentUnit} passed as argument to
+ * this method.
+ *
+ * The extension is registered if and only if the DeploymentUnit is part of a Weld Deployment. Specifically,
+ * if a call to {@link #isPartOfWeldDeployment(DeploymentUnit)} using the DeploymentUnit argument
+ * returns {@code true}. Otherwise, this method will return immediately.
+ *
+ * @param extension An instance of the CDI portable extension to add.
+ * @param unit The deployment unit where the extension will be registered.
+ */
+ void registerBuildCompatibleExtension(final Class extends BuildCompatibleExtension> extension, final DeploymentUnit unit);
+
/**
* Adds the Bean Manager service associated to the {@link DeploymentUnit} to ServiceBuilder passed as argument.
*
diff --git a/weld/common/src/main/java/org/jboss/as/weld/deployment/WeldPortableExtensions.java b/weld/common/src/main/java/org/jboss/as/weld/deployment/WeldPortableExtensions.java
index 4174c91c1086..e7be9fffcf38 100644
--- a/weld/common/src/main/java/org/jboss/as/weld/deployment/WeldPortableExtensions.java
+++ b/weld/common/src/main/java/org/jboss/as/weld/deployment/WeldPortableExtensions.java
@@ -5,11 +5,15 @@
package org.jboss.as.weld.deployment;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.function.Function;
+import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
import jakarta.enterprise.inject.spi.Extension;
import org.jboss.as.server.deployment.AttachmentKey;
@@ -18,14 +22,16 @@
import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.bootstrap.spi.helpers.MetadataImpl;
+import org.jboss.weld.exceptions.IllegalArgumentException;
/**
- * Container class that is attached to the top level deployment that holds all portable extension metadata.
+ * Container class that is attached to the top level deployment that holds all portable and build compatible extensions
+ * metadata.
*
- * A portable extension may be available to multiple deployment class loaders, however for each PE we
+ * A CDI extension may be available to multiple deployment class loaders, however for each PE we
* only want to register a single instance.
*
- * This container provides a mechanism for making sure that only a single PE of a given type is registered.
+ * This container provides a mechanism for making sure that only a single PE/BCE of a given type is registered.
*
* @author Stuart Douglas
*
@@ -52,6 +58,7 @@ public static WeldPortableExtensions getPortableExtensions(final DeploymentUnit
}
private final Map, Metadata> extensions = new HashMap<>();
+ private final Collection> buildCompatibleExtensions = new ArrayList<>();
public synchronized void tryRegisterExtension(final Class> extensionClass, final DeploymentUnit deploymentUnit) throws DeploymentUnitProcessingException {
if (!Extension.class.isAssignableFrom(extensionClass)) {
@@ -72,8 +79,36 @@ public synchronized void registerExtensionInstance(final Extension extension, fi
extensions.put(extension.getClass(), new MetadataImpl<>(extension, deploymentUnit.getName()));
}
+ public synchronized void registerBuildCompatibleExtension(final Class extends BuildCompatibleExtension> extension) {
+ buildCompatibleExtensions.add(extension);
+ }
+
+ /**
+ * If there was at least one build compatible extension discovered or registered, this method will add a portable
+ * extension allowing their execution. It is a no-op if there are no build compatible extensions.
+ *
+ * @param extensionCreator a function that can create an instance of the {{@code LiteExtensionTranslator}} from given collection of found BCEs
+ * @throws IllegalArgumentException if the deployment unit parameter equals null
+ */
+ public synchronized void registerLiteExtensionTranslatorIfNeeded(Function>,
+ Extension> extensionCreator, DeploymentUnit deploymentUnit) throws IllegalArgumentException {
+ if (deploymentUnit == null) {
+ throw WeldLogger.ROOT_LOGGER.incorrectBceTranslatorSetup();
+ }
+
+ // only register if we found any BCE be it via discovery or manual registration
+ if (!buildCompatibleExtensions.isEmpty()) {
+ registerExtensionInstance(extensionCreator.apply(getBuildCompatibleExtensions()), deploymentUnit);
+ }
+ }
+
public Collection> getExtensions() {
return new HashSet<>(extensions.values());
}
+ public List> getBuildCompatibleExtensions() {
+ return new ArrayList<>(buildCompatibleExtensions);
+ }
+
+
}
diff --git a/weld/common/src/main/java/org/jboss/as/weld/logging/WeldLogger.java b/weld/common/src/main/java/org/jboss/as/weld/logging/WeldLogger.java
index e5e8a254bbb5..cedff39b688c 100644
--- a/weld/common/src/main/java/org/jboss/as/weld/logging/WeldLogger.java
+++ b/weld/common/src/main/java/org/jboss/as/weld/logging/WeldLogger.java
@@ -267,4 +267,7 @@ public interface WeldLogger extends BasicLogger {
@Message(id = 63, value = "Original %s does not have a module")
IllegalArgumentException originalClassDoesNotHaveAModule(Class> originalClass);
+
+ @Message(id = 64, value = "Incorrect setup for Weld's LiteExtensionTranslator initialization; a deployment unit has to be specified")
+ IllegalArgumentException incorrectBceTranslatorSetup();
}
diff --git a/weld/subsystem/src/main/java/org/jboss/as/weld/WeldCapabilityImpl.java b/weld/subsystem/src/main/java/org/jboss/as/weld/WeldCapabilityImpl.java
index 9e96f6568d81..f598c5d94897 100644
--- a/weld/subsystem/src/main/java/org/jboss/as/weld/WeldCapabilityImpl.java
+++ b/weld/subsystem/src/main/java/org/jboss/as/weld/WeldCapabilityImpl.java
@@ -6,6 +6,7 @@
import java.util.function.Supplier;
+import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.Extension;
@@ -33,6 +34,14 @@ public void registerExtensionInstance(final Extension extension, final Deploymen
}
}
+ @Override
+ public void registerBuildCompatibleExtension(Class extends BuildCompatibleExtension> extension, DeploymentUnit unit) {
+ if (isPartOfWeldDeployment(unit)) {
+ WeldPortableExtensions extensions = WeldPortableExtensions.getPortableExtensions(unit);
+ extensions.registerBuildCompatibleExtension(extension);
+ }
+ }
+
@Override
public Supplier addBeanManagerService(final DeploymentUnit unit, final ServiceBuilder> serviceBuilder) {
if (isPartOfWeldDeployment(unit)) {
diff --git a/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldDeploymentProcessor.java b/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldDeploymentProcessor.java
index 8660dd84d4f1..051b1d176034 100644
--- a/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldDeploymentProcessor.java
+++ b/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldDeploymentProcessor.java
@@ -70,6 +70,7 @@
import org.jboss.weld.config.ConfigurationKey;
import org.jboss.weld.configuration.spi.ExternalConfiguration;
import org.jboss.weld.configuration.spi.helpers.ExternalConfigurationBuilder;
+import org.jboss.weld.lite.extension.translator.LiteExtensionTranslator;
import org.jboss.weld.manager.api.ExecutorServices;
import org.jboss.weld.security.spi.SecurityServices;
import org.jboss.weld.transaction.spi.TransactionServices;
@@ -213,8 +214,11 @@ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitPro
additional.getServices().add(entry.getKey(), Reflections.cast(entry.getValue()));
}
}
-
- final Collection> extensions = WeldPortableExtensions.getPortableExtensions(deploymentUnit).getExtensions();
+ WeldPortableExtensions portableExtensions = WeldPortableExtensions.getPortableExtensions(deploymentUnit);
+ // register LiteExtensionTranslator as the last extension so that we have all info on registered BCEs
+ // NOTE: I chose to register it under the dep. unit of the top level deployment, using its CL, not sure if this is correct
+ portableExtensions.registerLiteExtensionTranslatorIfNeeded(classes -> new LiteExtensionTranslator(classes, module.getClassLoader()), deploymentUnit);
+ final Collection> extensions = portableExtensions.getExtensions();
final WeldDeployment deployment = new WeldDeployment(beanDeploymentArchives, extensions, module, subDeploymentLoaders, deploymentUnit, rootBeanDeploymentModule, eeModuleDescriptors);
diff --git a/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldPortableExtensionProcessor.java b/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldPortableExtensionProcessor.java
index 37f455758e46..ea68bbe25f40 100644
--- a/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldPortableExtensionProcessor.java
+++ b/weld/subsystem/src/main/java/org/jboss/as/weld/deployment/processors/WeldPortableExtensionProcessor.java
@@ -28,7 +28,6 @@
import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.modules.Module;
import org.jboss.vfs.VFSUtils;
-import org.jboss.weld.lite.extension.translator.LiteExtensionTranslator;
import org.wildfly.security.manager.WildFlySecurityManager;
/**
@@ -74,14 +73,9 @@ private void loadAttachments(Module module, DeploymentUnit deploymentUnit, WeldP
// load class and register for portable extensions
Collection> loadedPortableExtensions = loadExtensions(module, portableExtensionServices, Object.class);
registerPortableExtensions(deploymentUnit, extensions, loadedPortableExtensions);
- // load class and register for portable extensions
- // if there is at least one, add a portable extension processing them
- List> loadedBuildCompatExtensions =
- loadExtensions(module, buildCompatibleExtensionServices, BuildCompatibleExtension.class);
- if (!loadedBuildCompatExtensions.isEmpty()) {
- Extension extension = new LiteExtensionTranslator(loadedBuildCompatExtensions, module.getClassLoader());
- // NOTE: I chose to register it under the same dep. unit as other extensions, not sure if this is correct
- extensions.registerExtensionInstance(extension, deploymentUnit);
+ // load class and register for discovered build compatible extensions
+ for (Class extends BuildCompatibleExtension> extensionClass : loadExtensions(module, buildCompatibleExtensionServices, BuildCompatibleExtension.class)) {
+ extensions.registerBuildCompatibleExtension(extensionClass);
}
} catch (IOException e) {
throw new DeploymentUnitProcessingException(e);