From 3a59025e7e9872efcf7db6c1eaa23f515bf17e2e Mon Sep 17 00:00:00 2001 From: lprimak Date: Thu, 5 Dec 2024 20:31:07 -0600 Subject: [PATCH] enh: loading JARs from /lib/warlibs if --properies warlibs=true is specified --- .../api/ConnectorClassLoaderServiceImpl.java | 78 +++++++++--------- .../api/ConnectorsClassLoaderUtil.java | 36 ++++++++- .../glassfish/weld/connector/WeldUtils.java | 80 +++++++++++-------- .../annotation/impl/WarScanner.java | 37 ++++++--- .../deployment/archivist/WebArchivist.java | 73 +++++++++++++++-- .../descriptor/WebFragmentDescriptor.java | 15 +++- .../naming/resources/WebDirContext.java | 4 + .../org/glassfish/weld/DeploymentImpl.java | 50 +++++++----- .../src/main/resources/lib/warlibs/.gitkeep | 0 .../common/DeploymentContextImpl.java | 2 - .../deployment/common/DeploymentUtils.java | 61 +++++++++++++- .../common/InstalledLibrariesResolver.java | 22 ++++- 12 files changed, 341 insertions(+), 117 deletions(-) create mode 100644 nucleus/admin/template/src/main/resources/lib/warlibs/.gitkeep diff --git a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorClassLoaderServiceImpl.java b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorClassLoaderServiceImpl.java index 9839e947c26..843503e041d 100644 --- a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorClassLoaderServiceImpl.java +++ b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorClassLoaderServiceImpl.java @@ -37,22 +37,27 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2024] [Payara Foundation and/or its affiliates] package com.sun.appserv.connectors.internal.api; +import jakarta.inject.Provider; +import org.glassfish.deployment.common.DeploymentUtils; import org.glassfish.internal.api.*; +import org.glassfish.internal.deployment.Deployment; import org.jvnet.hk2.annotations.Service; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.UnaryOperator; import java.util.logging.Logger; import java.util.logging.Level; import com.sun.logging.LogDomains; import jakarta.inject.Inject; -import jakarta.inject.Provider; /** * We support two policies: @@ -72,13 +77,13 @@ public class ConnectorClassLoaderServiceImpl implements ConnectorClassLoaderServ * class loader for all applications. In other words, we make every * standalone RARs available to all applications. */ - private volatile DelegatingClassLoader globalConnectorCL; + private final AtomicReference globalConnectorCL = new AtomicReference<>(); + private final AtomicReference globalConnectorWithWarLibCL = new AtomicReference<>(); @Inject private AppSpecificConnectorClassLoaderUtil appsSpecificCCLUtil; - @Inject - private Provider classLoaderHierarchyProvider; + private Provider connectorsClassLoaderUtil; private Logger logger = LogDomains.getLogger(ConnectorClassLoaderServiceImpl.class, LogDomains.RSR_LOGGER); @@ -92,34 +97,8 @@ public class ConnectorClassLoaderServiceImpl implements ConnectorClassLoaderServ public DelegatingClassLoader getConnectorClassLoader(String appName) { DelegatingClassLoader loader = null; - // We do not have dependency on common-class-loader explicitly - // and also cannot initialize globalConnectorCL during postConstruct via ClassLoaderHierarchy - // which will result in circular dependency injection between kernel and connector module - // Hence initializing globalConnectorCL lazily - if (globalConnectorCL == null) { - synchronized (ConnectorClassLoaderServiceImpl.class) { - if (globalConnectorCL == null) { - //[parent is assumed to be common-class-loader in ConnectorClassLoaderUtil.createRARClassLoader() also] - final ClassLoader parent = getCommonClassLoader(); - globalConnectorCL = AccessController.doPrivileged(new PrivilegedAction() { - public DelegatingClassLoader run() { - DelegatingClassLoader dcl = new DelegatingClassLoader(parent); - for (DelegatingClassLoader.ClassFinder cf : appsSpecificCCLUtil.getSystemRARClassLoaders()) { - dcl.addDelegate(cf); - } - return dcl; - } - }); - - for (DelegatingClassLoader.ClassFinder cf : appsSpecificCCLUtil.getSystemRARClassLoaders()) { - globalConnectorCL.addDelegate(cf); - } - } - } - } if (hasGlobalAccessForRARs(appName)) { - assert (globalConnectorCL != null); - loader = globalConnectorCL; + loader = getGlobalConnectorClassLoader(); } else { appsSpecificCCLUtil.detectReferredRARs(appName); loader = createConnectorClassLoaderForApplication(appName); @@ -133,14 +112,42 @@ private boolean hasGlobalAccessForRARs(String appName) { (ConnectorConstants.RAR_VISIBILITY_GLOBAL_ACCESS); } - private ClassLoader getCommonClassLoader(){ - return classLoaderHierarchyProvider.get().getCommonClassLoader(); + private DelegatingClassLoader getGlobalConnectorClassLoader() { + // We do not have dependency on common-class-loader explicitly + // and also cannot initialize globalConnectorCL during postConstruct via ClassLoaderHierarchy + // which will result in circular dependency injection between kernel and connector module + // Hence initializing globalConnectorCL lazily + UnaryOperator updateOperator = currentValue -> { + if (currentValue == null) { + //[parent is assumed to be common-class-loader in ConnectorClassLoaderUtil.createRARClassLoader() also] + var newValue = AccessController.doPrivileged(new PrivilegedAction() { + public DelegatingClassLoader run() { + DelegatingClassLoader dcl = new DelegatingClassLoader(connectorsClassLoaderUtil.get().getCommonClassLoader()); + for (DelegatingClassLoader.ClassFinder cf : appsSpecificCCLUtil.getSystemRARClassLoaders()) { + dcl.addDelegate(cf); + } + return dcl; + } + }); + + for (DelegatingClassLoader.ClassFinder cf : appsSpecificCCLUtil.getSystemRARClassLoaders()) { + newValue.addDelegate(cf); + } + return newValue; + } + return currentValue; + }; + if (DeploymentUtils.useWarLibraries(DeploymentUtils.getCurrentDeploymentContext())) { + return globalConnectorWithWarLibCL.updateAndGet(updateOperator); + } else { + return globalConnectorCL.updateAndGet(updateOperator); + } } private DelegatingClassLoader createConnectorClassLoaderForApplication(String appName){ DelegatingClassLoader appSpecificConnectorClassLoader = - new DelegatingClassLoader(getCommonClassLoader()); + new DelegatingClassLoader(connectorsClassLoaderUtil.get().getCommonClassLoader()); //add system ra classloaders for(DelegatingClassLoader.ClassFinder cf : appsSpecificCCLUtil.getSystemRARClassLoaders()){ @@ -178,7 +185,7 @@ private void addRarClassLoader(String appName, DelegatingClassLoader appSpecific } private DelegatingClassLoader.ClassFinder getClassFinder(String raName) { - List delegates = globalConnectorCL.getDelegates(); + List delegates = getGlobalConnectorClassLoader().getDelegates(); DelegatingClassLoader.ClassFinder classFinder = null; for(DelegatingClassLoader.ClassFinder cf : delegates){ if(raName.equals(((ConnectorClassFinder)cf).getResourceAdapterName())){ @@ -188,5 +195,4 @@ private DelegatingClassLoader.ClassFinder getClassFinder(String raName) { } return classFinder; } - } diff --git a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorsClassLoaderUtil.java b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorsClassLoaderUtil.java index 015067aa5aa..16368f267cf 100644 --- a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorsClassLoaderUtil.java +++ b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ConnectorsClassLoaderUtil.java @@ -37,15 +37,21 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2024] [Payara Foundation and/or its affiliates] package com.sun.appserv.connectors.internal.api; +import com.sun.enterprise.loader.CurrentBeforeParentClassLoader; +import org.glassfish.deployment.common.DeploymentUtils; +import org.glassfish.deployment.common.InstalledLibrariesResolver; import org.glassfish.internal.api.ClassLoaderHierarchy; import org.glassfish.internal.api.DelegatingClassLoader; import org.glassfish.api.admin.*; import org.glassfish.api.event.Events; import org.glassfish.api.event.EventListener; import org.glassfish.api.event.EventTypes; +import org.glassfish.internal.api.Globals; +import org.glassfish.internal.deployment.Deployment; import org.jvnet.hk2.annotations.Service; import jakarta.inject.Singleton; @@ -53,9 +59,11 @@ import java.io.*; import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import java.util.Collection; @@ -81,7 +89,8 @@ public class ConnectorsClassLoaderUtil { @Inject private ClassLoaderHierarchy clh; - //private static List systemRARClassLoaders; + // warLibClassLoader is used to load libraries from the war file + private final AtomicReference warLibClassLoader = new AtomicReference<>(); private Logger _logger = LogDomains.getLogger(ConnectorRuntime.class, LogDomains.RSR_LOGGER); @@ -209,7 +218,7 @@ public Collection getSystemRARClassLoaders() throws Connec libraries = ConnectorsUtil.getInstalledLibrariesFromManifest(location, env); } - ConnectorClassFinder ccf = createRARClassLoader(location, null, rarName, libraries); + ConnectorClassFinder ccf = createRARClassLoader(location, getCommonClassLoader(), rarName, libraries); classLoaders.add(ccf); } // systemRARClassLoaders = classLoaders; @@ -218,6 +227,29 @@ public Collection getSystemRARClassLoaders() throws Connec return classLoaders; } + public ClassLoader getCommonClassLoader() { + if (DeploymentUtils.useWarLibraries(DeploymentUtils.getCurrentDeploymentContext())) { + return warLibClassLoader.updateAndGet(currentValue -> { + if (currentValue == null) { + var newValue = new CurrentBeforeParentClassLoader(InstalledLibrariesResolver.getWarLibraries() + .stream().map(uri -> { + try { + return uri.toUri().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }) + .toArray(URL[]::new), clh.getCommonClassLoader()); + newValue.enableCurrentBeforeParentUnconditional(); + return newValue; + } + return currentValue; + }); + } else { + return clh.getCommonClassLoader(); + } + } + public ConnectorClassFinder getSystemRARClassLoader(String rarName) throws ConnectorRuntimeException { if (ConnectorsUtil.belongsToSystemRA(rarName)) { diff --git a/appserver/web/gf-weld-connector/src/main/java/org/glassfish/weld/connector/WeldUtils.java b/appserver/web/gf-weld-connector/src/main/java/org/glassfish/weld/connector/WeldUtils.java index 2c8eb51d0fa..f5f8050012c 100644 --- a/appserver/web/gf-weld-connector/src/main/java/org/glassfish/weld/connector/WeldUtils.java +++ b/appserver/web/gf-weld-connector/src/main/java/org/glassfish/weld/connector/WeldUtils.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2016-2022] [Payara Foundation and/or its affiliates] +// Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] package org.glassfish.weld.connector; @@ -48,9 +48,13 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import jakarta.decorator.Decorator; @@ -60,6 +64,7 @@ import jakarta.enterprise.context.*; import jakarta.enterprise.inject.Model; import jakarta.enterprise.inject.Stereotype; +import jakarta.faces.flow.FlowScoped; import jakarta.inject.Inject; import jakarta.inject.Scope; import jakarta.inject.Singleton; @@ -68,6 +73,7 @@ import org.glassfish.api.admin.ServerEnvironment; import org.glassfish.api.deployment.DeploymentContext; import org.glassfish.api.deployment.archive.ReadableArchive; +import org.glassfish.deployment.common.DeploymentUtils; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.classmodel.reflect.AnnotationModel; import org.glassfish.hk2.classmodel.reflect.AnnotationType; @@ -83,6 +89,7 @@ import org.xml.sax.helpers.DefaultHandler; import java.util.logging.Logger; import java.util.logging.Level; +import java.util.stream.Collectors; public class WeldUtils { @@ -135,6 +142,7 @@ public enum BDAType { WAR, JAR, RAR, UNKNOWN }; cdi.add("jakarta.faces.view.ViewScoped"); cdi.add("jakarta.faces.flow.FlowScoped"); cdi.add(ConversationScoped.class.getName()); + cdi.add(FlowScoped.class.getName()); cdi.add(ApplicationScoped.class.getName()); cdi.add(SessionScoped.class.getName()); cdi.add(RequestScoped.class.getName()); @@ -218,16 +226,13 @@ public static boolean hasCDIEnablingAnnotations(DeploymentContext context, URI p * @return true, if there is at least one bean annotated with a qualified annotation in the specified paths */ public static boolean hasCDIEnablingAnnotations(DeploymentContext context, Collection paths) { - final Types types = getTypes(context); - if (types != null) { - final Set exclusions = new HashSet<>(); - for (final Type type : types.getAllTypes()) { - if (!(type instanceof AnnotationType) && type.wasDefinedIn(paths)) { - for (final AnnotationModel am : type.getAnnotations()) { - final AnnotationType at = am.getType(); - if (isCDIEnablingAnnotation(at, exclusions)) { - return true; - } + final Set exclusions = new HashSet<>(); + for (final Type type : getAllTypes(context, paths)) { + if (!(type instanceof AnnotationType) && type.wasDefinedIn(paths)) { + for (final AnnotationModel am : type.getAnnotations()) { + final AnnotationType at = am.getType(); + if (isCDIEnablingAnnotation(at, exclusions)) { + return true; } } } @@ -236,7 +241,6 @@ public static boolean hasCDIEnablingAnnotations(DeploymentContext context, Colle return false; } - /** * Get the names of any annotation types that are applied to beans, which should enable CDI * processing even in the absence of a beans.xml descriptor. @@ -248,16 +252,13 @@ public static boolean hasCDIEnablingAnnotations(DeploymentContext context, Colle public static String[] getCDIEnablingAnnotations(DeploymentContext context) { final Set result = new HashSet<>(); - final Types types = getTypes(context); - if (types != null) { - final Set exclusions = new HashSet<>(); - for (final Type type : types.getAllTypes()) { - if (!(type instanceof AnnotationType)) { - for (final AnnotationModel am : type.getAnnotations()) { - final AnnotationType at = am.getType(); - if (isCDIEnablingAnnotation(at, exclusions)) { - result.add(at.getName()); - } + final Set exclusions = new HashSet<>(); + for (final Type type : getAllTypes(context, List.of())) { + if (!(type instanceof AnnotationType)) { + for (final AnnotationModel am : type.getAnnotations()) { + final AnnotationType at = am.getType(); + if (isCDIEnablingAnnotation(at, exclusions)) { + result.add(at.getName()); } } } @@ -280,16 +281,13 @@ public static Collection getCDIAnnotatedClassNames(DeploymentContext con final Set cdiEnablingAnnotations = new HashSet<>(); Collections.addAll(cdiEnablingAnnotations, getCDIEnablingAnnotations(context)); - final Types types = getTypes(context); - if (types != null) { - for (final Type type : types.getAllTypes()) { - if (!(type instanceof AnnotationType)) { - for (final AnnotationModel am : type.getAnnotations()) { - final AnnotationType at = am.getType(); - if (cdiEnablingAnnotations.contains(at.getName())) { - result.add(type.getName()); - break; - } + for (final Type type : getAllTypes(context, List.of())) { + if (!(type instanceof AnnotationType)) { + for (final AnnotationModel am : type.getAnnotations()) { + final AnnotationType at = am.getType(); + if (cdiEnablingAnnotations.contains(at.getName())) { + result.add(type.getName()); + break; } } } @@ -704,6 +702,24 @@ public static String getBeanDiscoveryMode(InputStream beansXmlInputStream) { return beanDiscoveryMode; } + private static List getAllTypes(DeploymentContext context, Collection paths) { + final Types types = getTypes(context); + if (types == null) { + return List.of(); + } + + List allTypes = new ArrayList<>(types.getAllTypes()); + Map cache = DeploymentUtils.getWarLibraryCache(); + for (URI path : paths.isEmpty() ? cache.keySet().stream().map(Path::of).map(Path::toUri) + .collect(Collectors.toList()) : paths) { + var descriptor = cache.get(path.getRawPath()); + if (descriptor != null) { + allTypes.addAll(descriptor.getTypes()); + } + } + return allTypes; + } + private static class LocalDefaultHandler extends DefaultHandler { String beanDiscoveryMode = null; diff --git a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/annotation/impl/WarScanner.java b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/annotation/impl/WarScanner.java index bccd46a1e53..fb8e0536f3b 100644 --- a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/annotation/impl/WarScanner.java +++ b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/annotation/impl/WarScanner.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates] +// Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] package org.glassfish.web.deployment.annotation.impl; @@ -98,7 +98,15 @@ public void process(File archiveFile, WebBundleDescriptor webBundleDesc, public void process(ReadableArchive readableArchive, WebBundleDescriptor webBundleDesc, ClassLoader classLoader, Parser parser) throws IOException { - this.archiveFile = new File(readableArchive.getURI()); + WebFragmentDescriptor webFragmentDesc = new WebFragmentDescriptor(); + if (webBundleDesc instanceof WebFragmentDescriptor) { + webFragmentDesc = (WebFragmentDescriptor) webBundleDesc; + } + if (webFragmentDesc.isWarLibrary()) { + this.archiveFile = new File(webFragmentDesc.getWarLibraryPath()); + } else { + this.archiveFile = new File(readableArchive.getURI()); + } this.classLoader = classLoader; setParser(parser); @@ -108,7 +116,7 @@ public void process(ReadableArchive readableArchive, WebBundleDescriptor webBund AnnotationUtils.getLogger().log(Level.FINE, "classLoader is {0}", classLoader); } - if (!archiveFile.isDirectory()) { + if (!webFragmentDesc.isWarLibrary() && !archiveFile.isDirectory()) { // on client side return; } @@ -122,16 +130,19 @@ public void process(ReadableArchive readableArchive, WebBundleDescriptor webBund File webinf = new File(archiveFile, "WEB-INF"); if (webBundleDesc instanceof WebFragmentDescriptor) { - WebFragmentDescriptor webFragmentDesc = (WebFragmentDescriptor)webBundleDesc; - File lib = new File(webinf, "lib"); - if (lib.exists()) { - File jarFile = new File(lib, webFragmentDesc.getJarName()); - if (jarFile.exists()) { - // support exploded jar file - if (jarFile.isDirectory()) { - addScanDirectory(jarFile); - } else { - addScanJar(jarFile); + if (webFragmentDesc.isWarLibrary()) { + addScanJar(archiveFile); + } else { + File lib = new File(webinf, "lib"); + if (lib.exists()) { + File jarFile = new File(lib, webFragmentDesc.getJarName()); + if (jarFile.exists()) { + // support exploded jar file + if (jarFile.isDirectory()) { + addScanDirectory(jarFile); + } else { + addScanJar(jarFile); + } } } } diff --git a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/archivist/WebArchivist.java b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/archivist/WebArchivist.java index 3ea9a8e0965..eca18f0b6f6 100644 --- a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/archivist/WebArchivist.java +++ b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/archivist/WebArchivist.java @@ -41,13 +41,19 @@ package org.glassfish.web.deployment.archivist; +import com.sun.enterprise.deploy.shared.ArchiveFactory; import com.sun.enterprise.deployment.Application; import com.sun.enterprise.deployment.EarType; +import org.glassfish.deployment.common.InstalledLibrariesResolver; import org.glassfish.deployment.common.RootDeploymentDescriptor; import com.sun.enterprise.deployment.EjbBundleDescriptor; import com.sun.enterprise.deployment.EjbDescriptor; import com.sun.enterprise.deployment.WebComponentDescriptor; import com.sun.enterprise.deployment.annotation.impl.ModuleScanner; +import org.glassfish.hk2.classmodel.reflect.Parser; +import org.glassfish.hk2.classmodel.reflect.Type; +import org.glassfish.hk2.classmodel.reflect.Types; +import org.glassfish.internal.deployment.Deployment; import org.glassfish.web.deployment.annotation.impl.WarScanner; import com.sun.enterprise.deployment.archivist.Archivist; import com.sun.enterprise.deployment.archivist.ArchivistFor; @@ -73,6 +79,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; @@ -83,6 +90,7 @@ import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; /** @@ -105,6 +113,10 @@ public class WebArchivist extends Archivist { private ServerEnvironment env; private WebBundleDescriptorImpl defaultWebXmlBundleDescriptor = null; + @Inject + private ArchiveFactory archiveFactory; + @Inject + private Deployment deployment; /** * @return the module type handled by this archivist @@ -307,6 +319,10 @@ public Set getLibraries(ReadableArchive archive) throws IOException { // EAR shared libraries extractLibraries(parentArchive.getSubArchive("lib"), false, libraries); } + // Webapp shared libraries + if(DeploymentUtils.useWarLibraries(deployment.getCurrentDeploymentContext())) { + InstalledLibrariesResolver.getWarLibraries().forEach(warLibrary -> libraries.add(warLibrary.toString())); + } return libraries; } @@ -344,10 +360,27 @@ protected void postAnnotationProcess(WebBundleDescriptorImpl descriptor, // all web fragment metadata-complete // should be overridden and be true also if (descriptor.isFullAttribute()) { - wfDesc.setFullAttribute( - String.valueOf(descriptor.isFullAttribute())); + wfDesc.setFullAttribute( + String.valueOf(descriptor.isFullAttribute())); + } + if (wfDesc.isWarLibrary()) { + if (!DeploymentUtils.getWarLibraryCache().containsKey(wfDesc.getWarLibraryPath())) { + ReadableArchive warArchive = null; + try { + warArchive = archiveFactory.openArchive(new File(wfDesc.getWarLibraryPath())); + warArchive.setExtraData(Parser.class, archive.getExtraData(Parser.class)); + super.readAnnotations(warArchive, wfDesc, localExtensions); + } finally { + if (warArchive != null) { + warArchive.close(); + } + } + DeploymentUtils.getWarLibraryCache().putIfAbsent(wfDesc.getWarLibraryPath(), + new DeploymentUtils.WarLibraryDescriptor(wfDesc, filterTypesByWarLibrary(wfDesc))); + } + } else { + super.readAnnotations(archive, wfDesc, localExtensions); } - super.readAnnotations(archive, wfDesc, localExtensions); } // scan manifest classpath @@ -385,6 +418,15 @@ protected void postAnnotationProcess(WebBundleDescriptorImpl descriptor, descriptor.addDefaultWebBundleDescriptor(defaultWebBundleDescriptor); } + private List filterTypesByWarLibrary(WebFragmentDescriptor wfDesc) { + Types types = deployment.getCurrentDeploymentContext().getTransientAppMetaData(Types.class.getName(), Types.class); + if (types == null) { + return List.of(); + } + return types.getAllTypes().stream().filter(key -> key.wasDefinedIn( + List.of(Path.of(wfDesc.getWarLibraryPath()).toUri()))).collect(Collectors.toList()); + } + /** * This method will return the list of web fragment in the desired order. */ @@ -400,18 +442,29 @@ private List readStandardFragments(WebBundleDescriptorImp wfArchivist.setAnnotationProcessingRequested(false); WebFragmentDescriptor wfDesc = null; - ReadableArchive embeddedArchive = lib.startsWith("WEB-INF") - ? archive.getSubArchive(lib) : archive.getParentArchive().getSubArchive("lib").getSubArchive(lib); + ReadableArchive embeddedArchive = null; + boolean isWarLibrary = false; + if (lib.startsWith("WEB-INF")) { + embeddedArchive = archive.getSubArchive(lib); + } else if (archive.getParentArchive() != null) { + embeddedArchive = archive.getParentArchive().getSubArchive("lib").getSubArchive(lib); + } else if (!DeploymentUtils.getWarLibraryCache().containsKey(lib) && lib.startsWith("/") + && lib.contains(DeploymentUtils.WAR_LIBRARIES)) { + embeddedArchive = archiveFactory.openArchive(new File(lib)); + isWarLibrary = true; + } try { if (embeddedArchive != null && wfArchivist.hasStandardDeploymentDescriptor(embeddedArchive)) { try { - wfDesc = (WebFragmentDescriptor)wfArchivist.open(embeddedArchive); - } catch(SAXParseException ex) { + wfDesc = (WebFragmentDescriptor) wfArchivist.open(embeddedArchive); + } catch (SAXParseException ex) { IOException ioex = new IOException(); ioex.initCause(ex); throw ioex; } + } else if (DeploymentUtils.getWarLibraryCache().containsKey(lib)) { + wfDesc = (WebFragmentDescriptor) DeploymentUtils.getWarLibraryCache().get(lib).getDescriptor(); } else { wfDesc = new WebFragmentDescriptor(); wfDesc.setExists(false); @@ -422,6 +475,12 @@ private List readStandardFragments(WebBundleDescriptorImp } } wfDesc.setJarName(lib.substring(lib.lastIndexOf('/') + 1)); + if (isWarLibrary) { + if (wfDesc.getClassLoader() != null) { + wfDesc.setClassLoader(wfDesc.getClassLoader().getParent()); + } + wfDesc.setWarLibraryPath(lib); + } wfList.add(wfDesc); descriptor.putJarNameWebFragmentNamePair(wfDesc.getJarName(), wfDesc.getName()); diff --git a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/descriptor/WebFragmentDescriptor.java b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/descriptor/WebFragmentDescriptor.java index 2cf2e0ac60c..0aee2faf08d 100644 --- a/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/descriptor/WebFragmentDescriptor.java +++ b/appserver/web/web-glue/src/main/java/org/glassfish/web/deployment/descriptor/WebFragmentDescriptor.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2014-2019] [Payara Foundation and/or its affiliates] +// Portions Copyright [2014-2024] [Payara Foundation and/or its affiliates] package org.glassfish.web.deployment.descriptor; @@ -60,6 +60,7 @@ public class WebFragmentDescriptor extends WebBundleDescriptorImpl private String jarName = null; private OrderingDescriptor ordering = null; private boolean exists = true; + private String warLibraryPath; /** * Constrct an empty web app [{0}]. @@ -93,6 +94,18 @@ public void setExists(boolean exists) { this.exists = exists; } + public boolean isWarLibrary() { + return warLibraryPath != null; + } + + public String getWarLibraryPath() { + return warLibraryPath; + } + + public void setWarLibraryPath(String warLibraryPath) { + this.warLibraryPath = warLibraryPath; + } + @Override protected WebComponentDescriptor combineWebComponentDescriptor( WebComponentDescriptor webComponentDescriptor) { diff --git a/appserver/web/web-naming/src/main/java/org/apache/naming/resources/WebDirContext.java b/appserver/web/web-naming/src/main/java/org/apache/naming/resources/WebDirContext.java index 87e09149cdb..cab75ea75cf 100644 --- a/appserver/web/web-naming/src/main/java/org/apache/naming/resources/WebDirContext.java +++ b/appserver/web/web-naming/src/main/java/org/apache/naming/resources/WebDirContext.java @@ -55,6 +55,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +// Portions Copyright [2024] Payara Foundation and/or affiliates package org.apache.naming.resources; @@ -322,6 +323,9 @@ protected JarFileEntry lookupFromJars(String name) { String jeName = getAbsoluteJarResourceName(name); for (JarFile jarFile : jarFiles) { JarEntry jarEntry = null; + if (jarFile == null) { + return null; + } if (jeName.charAt(jeName.length() - 1) != '/') { jarEntry = jarFile.getJarEntry(jeName + '/'); if (jarEntry != null) { 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 7012319103c..0758f03350f 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 @@ -57,6 +57,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; @@ -70,6 +71,7 @@ import org.glassfish.cdi.CDILoggerInfo; import org.glassfish.common.util.ObjectInputStreamWithLoader; import org.glassfish.deployment.common.DeploymentContextImpl; +import org.glassfish.deployment.common.DeploymentUtils; import org.glassfish.deployment.common.InstalledLibrariesResolver; import org.glassfish.hk2.classmodel.reflect.Types; import org.glassfish.internal.data.ApplicationInfo; @@ -793,30 +795,18 @@ private void processBdasForAppLibs( ReadableArchive archive, DeploymentContext c for ( URI oneAppLib : appLibs ) { for ( String oneInstalledLibrary : installedLibraries ) { if ( oneAppLib.getPath().endsWith( oneInstalledLibrary ) ) { - ReadableArchive libArchive = null; - try { - libArchive = archiveFactory.openArchive(oneAppLib); - if ( libArchive.exists( WeldUtils.META_INF_BEANS_XML ) ) { - String bdaId = archive.getName() + "_" + libArchive.getName(); - RootBeanDeploymentArchive rootBda = - new RootBeanDeploymentArchive(libArchive, - Collections.emptyList(), - context, - bdaId ); - libBdas.add(rootBda); - } - } finally { - if ( libArchive != null ) { - try { - libArchive.close(); - } catch ( Exception ignore ) {} - } - } + addLibBDA(archive, context, oneAppLib, libBdas); break; } } } } + + if (DeploymentUtils.useWarLibraries(context)) { + for (Path warLibrary : InstalledLibrariesResolver.getWarLibraries()) { + addLibBDA(archive, context, warLibrary.toUri(), libBdas); + } + } } catch (URISyntaxException | IOException e) { //todo: log error } @@ -826,6 +816,28 @@ private void processBdasForAppLibs( ReadableArchive archive, DeploymentContext c } } + private void addLibBDA(ReadableArchive archive, DeploymentContext context, URI oneAppLib, List libBdas) throws IOException { + ReadableArchive libArchive = null; + try { + libArchive = archiveFactory.openArchive(oneAppLib); + if ( libArchive.exists( WeldUtils.META_INF_BEANS_XML ) || WeldUtils.isImplicitBeanArchive(context, libArchive) ) { + String bdaId = archive.getName() + "_" + libArchive.getName(); + RootBeanDeploymentArchive rootBda = + new RootBeanDeploymentArchive(libArchive, + Collections.emptyList(), + context, + bdaId ); + libBdas.add(rootBda); + } + } finally { + if ( libArchive != null ) { + try { + libArchive.close(); + } catch ( Exception ignore ) {} + } + } + } + protected void addDeployedEjbs( Collection ejbs ) { if ( ejbs != null ) { deployedEjbs.addAll( ejbs ); diff --git a/nucleus/admin/template/src/main/resources/lib/warlibs/.gitkeep b/nucleus/admin/template/src/main/resources/lib/warlibs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentContextImpl.java b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentContextImpl.java index 0942d72ec63..235ecdcc119 100644 --- a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentContextImpl.java +++ b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentContextImpl.java @@ -72,8 +72,6 @@ import com.sun.enterprise.util.io.FileUtils; import fish.payara.nucleus.hotdeploy.ApplicationState; import fish.payara.nucleus.hotdeploy.HotDeployService; -import java.io.Closeable; -import java.util.logging.Level; import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINEST; import org.glassfish.api.deployment.DeployCommandParameters; diff --git a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentUtils.java b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentUtils.java index f9bb91ba00d..9ea44d5fefb 100644 --- a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentUtils.java +++ b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentUtils.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2018] Payara Foundation and/or affiliates +// Portions Copyright [2018-2024] Payara Foundation and/or affiliates package org.glassfish.deployment.common; @@ -54,6 +54,9 @@ import fish.payara.enterprise.config.serverbeans.DeploymentGroup; import org.glassfish.api.deployment.DeploymentContext; import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.classmodel.reflect.Type; +import org.glassfish.internal.api.Globals; +import org.glassfish.internal.deployment.Deployment; import org.glassfish.loader.util.ASClassLoaderUtil; @@ -62,7 +65,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedInputStream; +import java.nio.file.Path; import java.util.Enumeration; +import java.util.Map; import java.util.Properties; import java.net.URI; import java.net.URL; @@ -70,6 +75,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import java.util.zip.Adler32; import java.util.jar.Manifest; import java.util.jar.JarFile; @@ -79,8 +86,6 @@ import org.glassfish.logging.annotation.LogMessageInfo; -import org.glassfish.logging.annotation.LoggerInfo; -import org.glassfish.logging.annotation.LogMessagesResourceBundle; /** * Utility methods for deployment. @@ -94,7 +99,8 @@ public class DeploymentUtils { private static final String EXCEPTION_CAUGHT = "NCLS-DEPLOYMENT-00010"; public static final String DEPLOYMENT_PROPERTY_JAVA_WEB_START_ENABLED = "java-web-start-enabled"; - + public static final String WAR_LIBRARIES = "warlibs"; + final private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(DeploymentUtils.class); private static final String V2_COMPATIBILITY = "v2"; @@ -107,6 +113,26 @@ public class DeploymentUtils { private final static String DOWNLOADABLE_ARTIFACTS_KEY_PREFIX = "downloadable"; private final static String GENERATED_ARTIFACTS_KEY_PREFIX = "generated"; + private static final Map warLibraryCache = new ConcurrentHashMap<>(); + + public static class WarLibraryDescriptor { + private final Descriptor descriptor; + private final List types; + + public WarLibraryDescriptor(Descriptor descriptor, List types) { + this.descriptor = descriptor; + this.types = types; + } + + public Descriptor getDescriptor() { + return descriptor; + } + + public List getTypes() { + return types; + } + } + public static boolean isDASTarget(final String targetName) { return DAS_TARGET_NAME.equals(targetName); } @@ -436,9 +462,36 @@ public static List getExternalLibraries(ReadableArchive archive) { } catch (Exception e) { Logger.getAnonymousLogger().log(Level.WARNING, e.getMessage(), e); } + externalLibURIs.addAll(getWarLibraryURIs(getCurrentDeploymentContext())); return externalLibURIs; } + public static DeploymentContext getCurrentDeploymentContext() { + try { + return Globals.getDefaultHabitat().getService(Deployment.class).getCurrentDeploymentContext(); + } catch (Exception e) { + return null; + } + } + + public static List getWarLibraryURIs(DeploymentContext context) { + if(useWarLibraries(context)) { + return InstalledLibrariesResolver.getWarLibraries().stream() + .filter(key -> !warLibraryCache.containsKey(key.toString())) + .map(Path::toUri).collect(Collectors.toList()); + } else { + return List.of(); + } + } + + public static boolean useWarLibraries(DeploymentContext context) { + return context != null && Boolean.parseBoolean(context.getAppProps().getProperty(WAR_LIBRARIES)); + } + + public static Map getWarLibraryCache() { + return warLibraryCache; + } + /** * Opens the specified file as an archive, using the provided archive factory. * @param dir directory to be opened as an archive diff --git a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/InstalledLibrariesResolver.java b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/InstalledLibrariesResolver.java index 192e5ffab46..6df72755c4a 100644 --- a/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/InstalledLibrariesResolver.java +++ b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/InstalledLibrariesResolver.java @@ -37,11 +37,13 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright 2018-2022 Payara Foundation and/or affiliates +// Portions Copyright 2018-2024 Payara Foundation and/or affiliates package org.glassfish.deployment.common; import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.MessageFormat; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -49,6 +51,7 @@ import java.util.jar.JarInputStream; import java.util.logging.*; import java.util.*; +import java.util.stream.Collectors; import org.glassfish.api.deployment.archive.ReadableArchive; @@ -65,6 +68,7 @@ public class InstalledLibrariesResolver { // installed libraries list (accounts only for "domainRoot/lib/applibs" and not any // of "java.ext.dirs" entries) private static Map appLibsDirLibsStore = new HashMap(); + private static List warLibraries = new ArrayList<>(); public static final Logger deplLogger = org.glassfish.deployment.common.DeploymentContextImpl.deplLogger; @@ -111,6 +115,13 @@ public static boolean resolveDependencies(Manifest manifest, String archiveUri) * @param libDir libraryDirectory */ public static void initializeInstalledLibRegistry(String libDir ) { + try (var jarStream = Files.list(Path.of(String.format("%s/warlibs", libDir))) + .filter(path -> (Files.isRegularFile(path) || Files.isSymbolicLink(path)) + && path.toString().endsWith(".jar"))){ + warLibraries = jarStream.collect(Collectors.toList()); + } catch (IOException e) { + deplLogger.fine("Error in opening package directory " + libDir + " due to exception: " + e.getMessage()); + } initializeInstalledLibRegistryForApplibs(libDir); } @@ -147,6 +158,15 @@ public static Set getInstalledLibraries(ReadableArchive archive) throws return libraries; } + /** + * These libraries will be included in non-internal WAR applications, + * to improve developer experience and not require all dependencies to be included in the WAR. + * @return list of libraries in /lib/warlibs + */ + public static List getWarLibraries() { + return warLibraries; + } + private static Set getInstalledLibraries(String archiveURI, Manifest manifest, boolean strict, Map libraryStore) { Set libraries = new HashSet();