diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/BeanDeploymentArchiveImpl.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/BeanDeploymentArchiveImpl.java index b565f615ba2..f90567813cb 100644 --- a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/BeanDeploymentArchiveImpl.java +++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/BeanDeploymentArchiveImpl.java @@ -62,6 +62,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -78,36 +82,37 @@ import static org.glassfish.weld.WeldDeployer.WELD_BOOTSTRAP; import static org.glassfish.weld.connector.WeldUtils.*; - - /* * The means by which Weld Beans are discovered on the classpath. */ -public class BeanDeploymentArchiveImpl implements BeanDeploymentArchive { +public class BeanDeploymentArchiveImpl implements BeanDeploymentArchive, Serializable { + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(BeanDeploymentArchiveImpl.class.getName()); - private ReadableArchive archive; - private String id; - private List moduleClassNames = null; // Names of classes in the module - private List beanClassNames = null; // Names of bean classes in the module - private List> moduleClasses = null; // Classes in the module - private List> beanClasses = null; // Classes identified as Beans through Weld SPI - private List beansXmlURLs = null; - private final Collection> ejbDescImpls; - private final Set beanDeploymentArchives; + private transient ReadableArchive archive; + private final String id; + private final List moduleClassNames; // Names of classes in the module + private final List beanClassNames; // Names of bean classes in the module + private transient List> moduleClasses; // Classes in the module + private transient List> beanClasses; // Classes identified as Beans through Weld SPI + private final List beansXmlURLs; + private transient Collection> ejbDescImpls; + private transient Set beanDeploymentArchives; + private transient List deserializedBDAs; - private SimpleServiceRegistry simpleServiceRegistry = null; + private transient SimpleServiceRegistry simpleServiceRegistry = null; + private int originalIdentity; private BDAType bdaType = BDAType.UNKNOWN; - DeploymentContext context; - WeldBootstrap weldBootstrap; + transient DeploymentContext context; + transient WeldBootstrap weldBootstrap; - private final Map, InjectionTarget> itMap = new HashMap<>(); + private transient Map, InjectionTarget> itMap = new HashMap<>(); //workaround: WELD-781 - private ClassLoader moduleClassLoaderForBDA = null; + private transient ClassLoader moduleClassLoaderForBDA; private String friendlyId = ""; @@ -192,59 +197,38 @@ public BeanDeploymentArchiveImpl(String getClassLoader(); } - BeanDeploymentArchiveImpl(BeanDeploymentArchiveImpl self) { - this.id = self.id; - this.moduleClasses = new ArrayList<>(self.moduleClasses); - this.beanClasses = new ArrayList<>(self.beanClasses); - this.moduleClassNames = new ArrayList<>(self.moduleClassNames); - this.beanClassNames = new ArrayList<>(self.beanClassNames); - this.beansXmlURLs = new ArrayList<>(self.beansXmlURLs); - this.ejbDescImpls = new LinkedHashSet<>(self.ejbDescImpls); - this.beanDeploymentArchives = new LinkedHashSet<>(); - this.context = self.context; - this.weldBootstrap = self.weldBootstrap; - this.moduleClassLoaderForBDA = self.moduleClassLoaderForBDA; - } - - BeanDeploymentArchiveImpl deepCopy() { - return copySubBDAs(copyArchive()); - } - - BeanDeploymentArchiveImpl copyArchive() { - BeanDeploymentArchiveImpl copy = new BeanDeploymentArchiveImpl(this); - copy.ejbDescImpls.addAll(this.ejbDescImpls); - copy.simpleServiceRegistry = this.simpleServiceRegistry; - return copy; - } - - private BeanDeploymentArchiveImpl copySubBDAs(BeanDeploymentArchiveImpl copy) { - copy.beanDeploymentArchives.addAll(deepCopy(new HashSet<>())); - return copy; - } - - private Set deepCopy(Set seen) { - Set copySet = new HashSet<>(); - for (BeanDeploymentArchive bda : this.beanDeploymentArchives) { - BeanDeploymentArchiveImpl bdaImpl = (BeanDeploymentArchiveImpl) bda; - BeanDeploymentArchiveImpl copy = bdaImpl.copyArchive(); - copySet.add(copy); - if (!seen.contains(bda)) { - seen.add(copy); - copy.beanDeploymentArchives.addAll(bdaImpl.deepCopy(seen)); - } else { - for (var subBda : bdaImpl.beanDeploymentArchives) { - if (seen.contains(subBda)) { - seen.stream().filter(subBda::equals).findFirst().ifPresent(copy.beanDeploymentArchives::add); - } else { - BeanDeploymentArchiveImpl subBdaImpl = (BeanDeploymentArchiveImpl) subBda; - BeanDeploymentArchiveImpl subBdaCopy = subBdaImpl.copyArchive(); - seen.add(subBdaCopy); - subBdaCopy.beanDeploymentArchives.addAll(subBdaImpl.deepCopy(seen)); - } - } + public BeanDeploymentArchiveImpl(BeanDeploymentArchiveImpl beanDeploymentArchive) { + this.id = beanDeploymentArchive.id; + this.originalIdentity = beanDeploymentArchive.originalIdentity; + this.moduleClassNames = new ArrayList<>(beanDeploymentArchive.moduleClassNames); + this.beanClassNames = new ArrayList<>(beanDeploymentArchive.beanClassNames); + this.beansXmlURLs = new CopyOnWriteArrayList<>(beanDeploymentArchive.beansXmlURLs); + this.deserializedBDAs = beanDeploymentArchive.deserializedBDAs; + this.friendlyId = beanDeploymentArchive.friendlyId; + this.bdaType = beanDeploymentArchive.bdaType; + this.deploymentComplete = beanDeploymentArchive.deploymentComplete; + + initializeFromOriginal(); + } + + void initializeFromOriginal() { + if (context == null) { + this.context = DeploymentImpl.currentDeploymentContext.get(); + this.weldBootstrap = context.getTransientAppMetaData(WELD_BOOTSTRAP, WeldBootstrap.class); + this.moduleClasses = getOriginal().moduleClasses; + this.beanClasses = getOriginal().beanClasses; + this.simpleServiceRegistry = getOriginal().simpleServiceRegistry; + this.moduleClassLoaderForBDA = getOriginal().moduleClassLoaderForBDA; + this.ejbDescImpls = new LinkedHashSet<>(getOriginal().ejbDescImpls); + if (this.itMap == null) { + this.itMap = new HashMap<>(); } + this.itMap.putAll(getOriginal().itMap); } - return copySet; + } + + BeanDeploymentArchiveImpl getOriginal() { + return DeploymentImpl.currentBDAs.get().get(originalIdentity); } private void populateEJBsForThisBDA(Collection ejbs) { @@ -260,11 +244,15 @@ private void populateEJBsForThisBDA(Collection getBeanDeploymentArchives() { + if (beanDeploymentArchives == null && deserializedBDAs != null) { + beanDeploymentArchives = new LinkedHashSet<>(deserializedBDAs); + } return beanDeploymentArchives; } @Override public Collection getBeanClasses() { + initializeFromOriginal(); //This method is called during BeanDeployment.deployBeans, so this would //be the right time to place the module classloader for the BDA as the TCL if (logger.isLoggable(FINER)) { @@ -282,6 +270,7 @@ public Collection getBeanClasses() { } public Collection> getBeanClassObjects() { + initializeFromOriginal(); return beanClasses; } @@ -290,6 +279,7 @@ public Collection getModuleBeanClasses() { } public Collection> getModuleBeanClassObjects() { + initializeFromOriginal(); return moduleClasses; } @@ -339,11 +329,12 @@ public BeansXml getBeansXml() { */ @Override public Collection> getEjbs() { - + initializeFromOriginal(); return ejbDescImpls; } public EjbDescriptor getEjbDescriptor(String ejbName) { + initializeFromOriginal(); EjbDescriptor match = null; for (EjbDescriptor next : ejbDescImpls) { @@ -358,6 +349,7 @@ public EjbDescriptor getEjbDescriptor(String ejbName) { @Override public ServiceRegistry getServices() { + initializeFromOriginal(); if (simpleServiceRegistry == null) { simpleServiceRegistry = new SimpleServiceRegistry(); } @@ -380,10 +372,36 @@ public Collection getKnownClasses() { @Override public Collection> getLoadedBeanClasses() { + initializeFromOriginal(); return beanClasses; } + Object readResolve() throws ObjectStreamException { + return new BeanDeploymentArchiveImpl(this); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + originalIdentity = System.identityHashCode(this); + if (DeploymentImpl.currentBDAs.get().put(originalIdentity, this) != null) { + throw new IllegalStateException("Duplicate BDA detected: " + this); + } + out.writeInt(originalIdentity); + out.writeInt(beanDeploymentArchives.size()); + for (BeanDeploymentArchive bda : beanDeploymentArchives) { + out.writeObject(bda); + } + } + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + originalIdentity = in.readInt(); + int size = in.readInt(); + deserializedBDAs = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + deserializedBDAs.add((BeanDeploymentArchive) in.readObject()); + } + } //A graphical representation of the BDA hierarchy to aid in debugging //and to provide a better representation of how Weld treats the deployed @@ -791,14 +809,17 @@ private ClassLoader getClassLoader() { } public InjectionTarget getInjectionTarget(AnnotatedType annotatedType) { + initializeFromOriginal(); return itMap.get(annotatedType); } void putInjectionTarget(AnnotatedType annotatedType, InjectionTarget it) { + initializeFromOriginal(); itMap.put(annotatedType, it); } public ClassLoader getModuleClassLoaderForBDA() { + initializeFromOriginal(); return moduleClassLoaderForBDA; } diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/DeploymentImpl.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/DeploymentImpl.java index f07ea2b3401..5d453ed8957 100644 --- a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/DeploymentImpl.java +++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/DeploymentImpl.java @@ -45,9 +45,15 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.WARNING; import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; import static org.glassfish.weld.connector.WeldUtils.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -62,9 +68,11 @@ import org.glassfish.api.deployment.DeploymentContext; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.cdi.CDILoggerInfo; +import org.glassfish.common.util.ObjectInputStreamWithLoader; import org.glassfish.deployment.common.DeploymentContextImpl; import org.glassfish.deployment.common.InstalledLibrariesResolver; import org.glassfish.hk2.classmodel.reflect.Types; +import org.glassfish.internal.data.ApplicationInfo; import org.glassfish.javaee.core.deployment.ApplicationHolder; import org.glassfish.weld.connector.WeldUtils; import org.glassfish.weld.connector.WeldUtils.BDAType; @@ -85,17 +93,19 @@ import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; import org.jboss.weld.lite.extension.translator.LiteExtensionTranslator; import java.security.PrivilegedAction; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.lang.System.getSecurityManager; import static java.security.AccessController.doPrivileged; import jakarta.enterprise.inject.build.compatible.spi.SkipIfPortableExtensionPresent; - /* * Represents a deployment of a CDI (Weld) application. */ -public class DeploymentImpl implements CDI11Deployment { +public class DeploymentImpl implements CDI11Deployment, Serializable { + private static final long serialVersionUID = 1L; + static final ThreadLocal currentDeployment = new ThreadLocal<>(); + static final ThreadLocal> currentBDAs = new ThreadLocal<>(); + static final ThreadLocal currentDeploymentContext = new ThreadLocal<>(); // Keep track of our BDAs for this deployment private final Set rarRootBdas = new LinkedHashSet<>(); @@ -104,13 +114,13 @@ public class DeploymentImpl implements CDI11Deployment { private final Set libJarRootBdas = new LinkedHashSet<>(); private final Set beanDeploymentArchives = new LinkedHashSet<>(); - final DeploymentContext context; + final transient DeploymentContext context; // A convenience Map to get BDA for a given BDA ID - private final Map idToBeanDeploymentArchive = new HashMap<>(); - private SimpleServiceRegistry simpleServiceRegistry = null; + final Map idToBeanDeploymentArchive = new HashMap<>(); + private transient SimpleServiceRegistry simpleServiceRegistry = null; - private final Logger logger = CDILoggerInfo.getLogger(); + private final transient Logger logger; // holds BDA's created for extensions private final Map extensionBDAMap = new HashMap<>(); @@ -119,14 +129,14 @@ public class DeploymentImpl implements CDI11Deployment { private final List> dynamicExtensions = new ArrayList<>(); - private final Collection deployedEjbs = new LinkedList<>(); - private ArchiveFactory archiveFactory; + private final transient Collection deployedEjbs; + private final transient ArchiveFactory archiveFactory; private boolean earContextAppLibBdasProcessed = false; private String appName; private String contextId; - final InjectionManager injectionManager; + final transient InjectionManager injectionManager; /** * Produce BeanDeploymentArchives for this Deployment @@ -138,6 +148,8 @@ public DeploymentImpl(ReadableArchive archive, ArchiveFactory archiveFactory, String moduleName, InjectionManager injectionManager) { + logger = CDILoggerInfo.getLogger(); + deployedEjbs = new LinkedList<>(); if ( logger.isLoggable( FINE ) ) { logger.log(FINE, CDILoggerInfo.CREATING_DEPLOYMENT_ARCHIVE, new Object[]{ archive.getName()}); } @@ -166,33 +178,46 @@ public DeploymentImpl(ReadableArchive archive, createModuleBda(archive, ejbs, context, contextId); } - private DeploymentImpl(InjectionManager injectionManager, DeploymentContext context) { - this.injectionManager = injectionManager; - this.context = context; + DeploymentImpl(DeploymentImpl deployment) { + this.rarRootBdas.addAll(deployment.rarRootBdas); + this.ejbRootBdas.addAll(deployment.ejbRootBdas); + this.warRootBdas.addAll(deployment.warRootBdas); + this.libJarRootBdas.addAll(deployment.libJarRootBdas); + this.beanDeploymentArchives.addAll(deployment.beanDeploymentArchives); + this.appName = deployment.appName; + this.contextId = deployment.contextId; + this.earContextAppLibBdasProcessed = deployment.earContextAppLibBdasProcessed; + + this.context = currentDeploymentContext.get(); + this.archiveFactory = currentDeployment.get().archiveFactory; + this.simpleServiceRegistry = currentDeployment.get().simpleServiceRegistry; + this.injectionManager = currentDeployment.get().injectionManager; + this.logger = currentDeployment.get().logger; + this.deployedEjbs = currentDeployment.get().deployedEjbs; } - DeploymentImpl filter(RootBeanDeploymentArchive rootBDA) { - DeploymentImpl filteredDeployment = new DeploymentImpl(injectionManager, rootBDA.context); + DeploymentImpl filter(RootBeanDeploymentArchive rootBDA, ApplicationInfo applicationInfo) { + DeploymentImpl filteredDeployment; + try { + filteredDeployment = serializeAndDeserialize(this, applicationInfo.getAppClassLoader()); + } catch (IOException | ClassNotFoundException e) { + throw new IllegalStateException(e); + } List nonRooIDs = List.of(rootBDA.getId(), rootBDA.getModuleBda().getId()); - - filteredDeployment.warRootBdas.addAll(filterBDAs(warRootBdas, nonRooIDs)); - filteredDeployment.rarRootBdas.addAll(rarRootBdas.stream().map(RootBeanDeploymentArchive::deepCopy).collect(Collectors.toSet())); - filteredDeployment.ejbRootBdas.addAll(ejbRootBdas.stream().map(RootBeanDeploymentArchive::deepCopy).collect(Collectors.toSet())); - filteredDeployment.libJarRootBdas.addAll(libJarRootBdas.stream().map(RootBeanDeploymentArchive::deepCopy).collect(Collectors.toSet())); - filteredDeployment.beanDeploymentArchives.addAll(filterBDAs(beanDeploymentArchives, nonRooIDs, - filteredDeployment.rarRootBdas, filteredDeployment.ejbRootBdas, filteredDeployment.libJarRootBdas).stream() - .map(BeanDeploymentArchiveImpl.class::cast).map(BeanDeploymentArchiveImpl::deepCopy) - .collect(Collectors.toSet())); - filteredDeployment.archiveFactory = archiveFactory; - filteredDeployment.appName = appName; + filteredDeployment.clearAndAddAll(filteredDeployment.warRootBdas, filterBDAs(filteredDeployment.warRootBdas, nonRooIDs)); + filteredDeployment.clearAndAddAll(filteredDeployment.beanDeploymentArchives, + filterBDAs(filteredDeployment.beanDeploymentArchives, nonRooIDs, filteredDeployment.rarRootBdas, + filteredDeployment.ejbRootBdas, filteredDeployment.libJarRootBdas)); filteredDeployment.contextId = rootBDA.getId() + ".bda"; - filteredDeployment.earContextAppLibBdasProcessed = earContextAppLibBdasProcessed; - filteredDeployment.simpleServiceRegistry = simpleServiceRegistry; - return filteredDeployment; } + private void clearAndAddAll(Set originalBdas, Set bdas) { + originalBdas.clear(); + originalBdas.addAll(bdas); + } + Set getRootBDAs() { if (!warRootBdas.isEmpty()) { return warRootBdas; @@ -207,8 +232,28 @@ Set getRootBDAs() { } } + @SuppressWarnings("unchecked") + private TT serializeAndDeserialize(TT original, ClassLoader classLoader) + throws IOException, ClassNotFoundException { + // serialize + var byteArrayOutputStream = new ByteArrayOutputStream(); + try (var outputStream = new ObjectOutputStream(byteArrayOutputStream)) { + outputStream.writeObject(original); + } + + // deserialize + try (var inputStream = new ObjectInputStreamWithLoader( + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), classLoader)) { + return (TT) inputStream.readObject(); + } + } + + Object readResolve() throws ObjectStreamException { + return new DeploymentImpl(this); + } + @SafeVarargs - private List filterBDAs(Set bdas, List bda, + private Set filterBDAs(Set bdas, List bda, Set... include) { if (bdas == null) { return null; @@ -222,7 +267,7 @@ private List filterBDAs(Set bdas, Lis .collect(toList()); return bdas.stream() .filter(b -> bda.stream().anyMatch(b.getId()::startsWith) || includeList.contains(b)) - .collect(toList()); + .collect(toSet()); } private void addBeanDeploymentArchives(RootBeanDeploymentArchive bda) { @@ -411,7 +456,7 @@ private void addDependentBdas() { } private void recursivelyAdd(Collection bdas, BeanDeploymentArchive bda, Set seen) { - for (BeanDeploymentArchive subBda : bdas) { + for (BeanDeploymentArchive subBda : new LinkedHashSet<>(bdas)) { if (seen.add(subBda)) { subBda.getBeanDeploymentArchives().add(bda); recursivelyAdd(subBda.getBeanDeploymentArchives(), bda, seen); diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/RootBeanDeploymentArchive.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/RootBeanDeploymentArchive.java index 17b72f7a377..9d5c526eb9c 100644 --- a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/RootBeanDeploymentArchive.java +++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/RootBeanDeploymentArchive.java @@ -49,6 +49,7 @@ import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive; import org.jboss.weld.bootstrap.spi.BeansXml; +import java.io.ObjectStreamException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -91,9 +92,9 @@ public RootBeanDeploymentArchive(ReadableArchive archive, createModuleBda(archive, ejbs, deploymentContext, moduleBdaID); } - private RootBeanDeploymentArchive(BeanDeploymentArchiveImpl self, BeanDeploymentArchiveImpl moduleBda) { - super(self); - this.moduleBda = moduleBda; + public RootBeanDeploymentArchive(RootBeanDeploymentArchive rootBeanDeploymentArchive) { + super(rootBeanDeploymentArchive); + moduleBda = rootBeanDeploymentArchive.moduleBda; } private void createModuleBda(ReadableArchive archive, @@ -157,14 +158,8 @@ public WeldUtils.BDAType getModuleBDAType() { return moduleBda.getBDAType(); } - @Override - RootBeanDeploymentArchive deepCopy() { - return new RootBeanDeploymentArchive(super.deepCopy(), moduleBda.deepCopy()); - } - - @Override - RootBeanDeploymentArchive copyArchive() { - return new RootBeanDeploymentArchive(this, moduleBda); + Object readResolve() throws ObjectStreamException { + return new RootBeanDeploymentArchive(this); } @Override diff --git a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/WeldDeployer.java b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/WeldDeployer.java index c9c233b59ef..1c60b68f10d 100644 --- a/appserver/web/weld-integration/src/main/java/org/glassfish/weld/WeldDeployer.java +++ b/appserver/web/weld-integration/src/main/java/org/glassfish/weld/WeldDeployer.java @@ -511,12 +511,21 @@ private void processApplicationLoaded(ApplicationInfo applicationInfo) { try { invocationManager.preInvoke(componentInvocation); // Modern, multiple WARs in an EAR scenario - if (deploymentImpl.ejbRootBdas == null || deploymentImpl.ejbRootBdas.isEmpty()) { + if (deploymentImpl.ejbRootBdas.isEmpty()) { for (RootBeanDeploymentArchive rootBDA : deploymentImpl.getRootBDAs()) { - DeploymentImpl filtered = deploymentImpl.filter(rootBDA); - completeDeployment(filtered); - WeldBootstrap bootstrap = filtered.context.getTransientAppMetaData(WELD_BOOTSTRAP, WeldBootstrap.class); - startWeldBootstrap(applicationInfo, rootBDA, bootstrap, filtered, componentInvocation); + DeploymentImpl.currentDeployment.set(deploymentImpl); + DeploymentImpl.currentBDAs.set(new HashMap<>()); + DeploymentImpl.currentDeploymentContext.set(rootBDA.context); + try { + DeploymentImpl filtered = deploymentImpl.filter(rootBDA, applicationInfo); + completeDeployment(filtered); + WeldBootstrap bootstrap = filtered.context.getTransientAppMetaData(WELD_BOOTSTRAP, WeldBootstrap.class); + startWeldBootstrap(applicationInfo, rootBDA, bootstrap, filtered, componentInvocation); + } finally { + DeploymentImpl.currentDeployment.remove(); + DeploymentImpl.currentBDAs.remove(); + DeploymentImpl.currentDeploymentContext.remove(); + } } } else if (!deploymentImpl.getRootBDAs().isEmpty()) { completeDeployment(deploymentImpl);