From 3e213eec47c6147ebc6c413bbaaba90bf4781328 Mon Sep 17 00:00:00 2001 From: lprimak Date: Mon, 2 Dec 2024 02:40:49 -0600 Subject: [PATCH] wip: loading from /lib/warlibs --- .../enterprise/glassfish/web/WarHandler.java | 14 +++++- .../web/loader/WebappClassLoader.java | 22 +++++---- .../annotation/impl/WarScanner.java | 35 +++++++++----- .../deployment/archivist/WebArchivist.java | 45 ++++++++++++++++-- .../descriptor/WebFragmentDescriptor.java | 15 +++++- .../naming/resources/WebDirContext.java | 4 ++ .../org/glassfish/weld/DeploymentImpl.java | 47 +++++++++++-------- .../src/main/resources/lib/warlibs/.gitkeep | 0 .../deployment/common/DeploymentUtils.java | 19 +++++++- .../common/InstalledLibrariesResolver.java | 22 ++++++++- 10 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 nucleus/admin/template/src/main/resources/lib/warlibs/.gitkeep diff --git a/appserver/web/war-util/src/main/java/com/sun/enterprise/glassfish/web/WarHandler.java b/appserver/web/war-util/src/main/java/com/sun/enterprise/glassfish/web/WarHandler.java index 0c6667a5f88..7e4563d018a 100644 --- a/appserver/web/war-util/src/main/java/com/sun/enterprise/glassfish/web/WarHandler.java +++ b/appserver/web/war-util/src/main/java/com/sun/enterprise/glassfish/web/WarHandler.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2016-2023] [Payara Foundation and/or its affiliates] +// Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] package com.sun.enterprise.glassfish.web; @@ -56,6 +56,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; @@ -79,6 +80,9 @@ import org.glassfish.api.deployment.archive.ArchiveDetector; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.deployment.common.DeploymentProperties; +import org.glassfish.deployment.common.DeploymentUtils; +import org.glassfish.deployment.common.InstalledLibrariesResolver; +import org.glassfish.internal.deployment.Deployment; import org.glassfish.loader.util.ASClassLoaderUtil; import org.glassfish.web.loader.LogFacade; import org.glassfish.web.loader.WebappClassLoader; @@ -128,6 +132,8 @@ public class WarHandler extends AbstractArchiveHandler { @Inject private ServerEnvironment serverEnvironment; + @Inject + private Deployment deployment; @Override public String getArchiveType() { @@ -191,6 +197,12 @@ public WebappClassLoader run() { WebXmlParser webXmlParser = getWebXmlParser(context.getSource(), application); configureLoaderAttributes(cloader, webXmlParser, base); configureLoaderProperties(cloader, webXmlParser, base); + if (!cloader.isInternal() && DeploymentUtils.useWarLibraries(deployment.getCurrentDeploymentContext())) { + for (Path warLibrary : InstalledLibrariesResolver.getWarLibraries()) { + cloader.addJar(warLibrary.toString(), new JarFile(warLibrary.toFile()), warLibrary.toFile()); + cloader.closeJARs(true); + } + } configureContextXmlAttribute(cloader, base, context); diff --git a/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java index 72e3e16851c..fc367750fee 100644 --- a/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java +++ b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java @@ -1176,16 +1176,7 @@ public boolean modified() { @Override public String toString() { StringBuilder sb = new StringBuilder(); - Predicate matchesInternal = str -> str.matches(".*generated/.*__.*"); - boolean isInternal = false; - if (repositoryURLs != null) { - isInternal = Arrays.stream(repositoryURLs).map(URL::toString) - .filter(matchesInternal).findAny().isPresent(); - } - if (canonicalLoaderDir != null && matchesInternal.test(canonicalLoaderDir)) { - isInternal = true; - } - if (isInternal) { + if (isInternal()) { sb.append("(internal) "); } sb.append("WebappClassLoader (delegate="); @@ -2109,6 +2100,17 @@ public void stop() throws Exception { } } + public boolean isInternal() { + Predicate matchesInternal = str -> str.matches(".*generated/.*__.*"); + if (repositoryURLs != null) { + return Arrays.stream(repositoryURLs).map(URL::toString) + .filter(matchesInternal).findAny().isPresent(); + } + if (canonicalLoaderDir != null && matchesInternal.test(canonicalLoaderDir)) { + return true; + } + return false; + } /** * Used to periodically signal to the classloader to release 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..64a4199d415 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 @@ -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..8baaf69eb42 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,18 @@ 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.api.deployment.DeployCommandParameters; +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.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; @@ -105,6 +110,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 +316,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 +357,21 @@ 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())); + } + ReadableArchive warArchive = null; + try { + if (wfDesc.isWarLibrary()) { + warArchive = archiveFactory.openArchive(new File(wfDesc.getWarLibraryPath())); + warArchive.setExtraData(Parser.class, archive.getExtraData(Parser.class)); + } + super.readAnnotations(warArchive != null ? warArchive : archive, wfDesc, localExtensions); + } finally { + if (warArchive != null) { + warArchive.close(); + } } - super.readAnnotations(archive, wfDesc, localExtensions); } // scan manifest classpath @@ -400,8 +424,16 @@ 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 (lib.startsWith("/") && lib.contains("/warlibs/")) { + embeddedArchive = archiveFactory.openArchive(new File(lib)); + isWarLibrary = true; + } try { if (embeddedArchive != null && wfArchivist.hasStandardDeploymentDescriptor(embeddedArchive)) { @@ -422,6 +454,9 @@ private List readStandardFragments(WebBundleDescriptorImp } } wfDesc.setJarName(lib.substring(lib.lastIndexOf('/') + 1)); + if (isWarLibrary) { + 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..67272f6ee7f 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; @@ -793,30 +794,16 @@ 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; } } } } + + for(Path warLibrary : InstalledLibrariesResolver.getWarLibraries()) { + addLibBDA(archive, context, warLibrary.toUri(), libBdas); + } } catch (URISyntaxException | IOException e) { //todo: log error } @@ -826,6 +813,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/DeploymentUtils.java b/nucleus/deployment/common/src/main/java/org/glassfish/deployment/common/DeploymentUtils.java index f9bb91ba00d..d7a623fac6a 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; @@ -45,6 +45,7 @@ import com.sun.enterprise.deploy.shared.ArchiveFactory; import com.sun.enterprise.deploy.shared.FileArchive; import com.sun.enterprise.util.io.FileUtils; +import org.glassfish.api.deployment.DeployCommandParameters; import org.glassfish.api.deployment.archive.ArchiveType; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.api.deployment.archive.WritableArchive; @@ -54,6 +55,10 @@ import fish.payara.enterprise.config.serverbeans.DeploymentGroup; import org.glassfish.api.deployment.DeploymentContext; import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.api.UnsatisfiedDependencyException; +import org.glassfish.internal.api.Globals; +import org.glassfish.internal.deployment.Deployment; +import org.glassfish.internal.deployment.ExtendedDeploymentContext; import org.glassfish.loader.util.ASClassLoaderUtil; @@ -62,6 +67,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedInputStream; +import java.nio.file.Path; import java.util.Enumeration; import java.util.Properties; import java.net.URI; @@ -436,9 +442,20 @@ public static List getExternalLibraries(ReadableArchive archive) { } catch (Exception e) { Logger.getAnonymousLogger().log(Level.WARNING, e.getMessage(), e); } + ExtendedDeploymentContext context = null; + try { + context = Globals.getDefaultHabitat().getService(Deployment.class).getCurrentDeploymentContext(); + } catch (Exception e) { } + if(useWarLibraries(context)) { + InstalledLibrariesResolver.getWarLibraries().stream().map(Path::toUri).forEach(externalLibURIs::add); + } return externalLibURIs; } + public static boolean useWarLibraries(DeploymentContext context) { + return context != null && Boolean.parseBoolean(context.getAppProps().getProperty("warlibs")); + } + /** * 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();