All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.ee10.webapp.WebInfConfiguration Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee10.webapp;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import jakarta.servlet.ServletContext;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.MountedPathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.resource.Resources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WebInfConfiguration extends AbstractConfiguration
{
    private static final Logger LOG = LoggerFactory.getLogger(WebInfConfiguration.class);

    public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.webapp.tmpResourceBase";
    public static final String ORIGINAL_RESOURCE_BASE = "org.eclipse.jetty.webapp.originalResourceBase";

    public WebInfConfiguration()
    {
        this(new Builder());
    }

    protected WebInfConfiguration(Builder builder)
    {
        super(builder);
    }

    @Override
    public void preConfigure(final WebAppContext context) throws Exception
    {
        // Make a temp directory for the webapp if one is not already set
        resolveTempDirectory(context);

        // Force early configuration (clearing) of the temporary directory so we can unpack into it.
        context.createTempDirectory();

        // Extract webapp if necessary
        unpack(context);
    }

    @Override
    public void configure(WebAppContext context) throws Exception
    {
        Resource webInf = context.getWebInf();

        // Add WEB-INF classes and lib classpaths
        if (webInf != null && webInf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
        {
            // Look for classes directory
            Resource classes = webInf.resolve("classes/");
            if (Resources.isReadableDirectory(classes))
                ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);

            // Look for jars
            Resource lib = webInf.resolve("lib/");
            if (Resources.isReadableDirectory(lib))
                ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
        }
    }

    @Override
    public void deconfigure(WebAppContext context) throws Exception
    {
        //reset the base resource back to what it was before we did any unpacking of resources
        Resource originalBaseResource = (Resource)context.removeAttribute(ORIGINAL_RESOURCE_BASE);
        context.setBaseResource(originalBaseResource);
    }

    /**
     * Get a temporary directory in which to unpack the war etc etc.
     * The algorithm for determining this is to check these alternatives
     * in the order shown:
     * 

* A. Try to use an explicit directory specifically for this webapp: *

    *
  1. * Iff an explicit directory is set for this webapp, use it. Set delete on * exit depends on value of persistTempDirectory. *
  2. *
  3. * If {@value jakarta.servlet.ServletContext#TEMPDIR} context attribute is set for * this webapp && exists && writeable, then use it. Set delete on exit depends on * value of persistTempDirectory. *
  4. *
* *

* B. Create a directory based on global settings. The new directory * will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir" * If the temporary directory is persistent, then the random digits are not added to the name. * The {@link Server#getTempDirectory()} is used for the parent of a created temporary directory. *

* * @param context the context to resolve the temp directory from * @throws Exception if unable to resolve the temp directory */ public void resolveTempDirectory(WebAppContext context) throws Exception { //If a tmp directory is already set we should use it File tempDirectory = context.getTempDirectory(); if (tempDirectory != null) { return; } // No temp directory configured, try to establish one via the jakarta.servlet.context.tempdir. File servletTmpDir = IO.asFile(context.getAttribute(ServletContext.TEMPDIR)); if (servletTmpDir != null) { // Use as tmpDir tempDirectory = servletTmpDir; // Set as TempDir in context. context.setTempDirectory(tempDirectory); return; } context.makeTempDirectory(); } @Deprecated (forRemoval = true, since = "12.0.12") public void makeTempDirectory(File parent, WebAppContext context) throws Exception { context.makeTempDirectory(); } public void unpack(WebAppContext context) throws IOException { Resource webApp = context.getBaseResource(); context.setAttribute(ORIGINAL_RESOURCE_BASE, context.getBaseResource()); if (webApp == null) { String war = context.getWar(); if (war != null && war.length() > 0) webApp = context.newResource(war); else webApp = context.getBaseResource(); if (webApp == null) throw new IllegalStateException("No resourceBase or war set for context"); // Use real location (if different) for WAR file, so that change/modification monitoring can work. if (webApp.isAlias()) { URI realURI = webApp.getRealURI(); if (LOG.isDebugEnabled()) LOG.debug("{} anti-aliased to {}", webApp, realURI); Resource realWebApp = context.newResource(realURI); if (Resources.exists(realWebApp)) webApp = realWebApp; } if (LOG.isDebugEnabled()) LOG.debug("Try webapp={} exists={} directory={} file={}", webApp, webApp.exists(), webApp.isDirectory(), webApp.getPath()); // Track the original web_app Resource, as this could be a PathResource. // Later steps force the Resource to be a JarFileResource, which introduces // URLConnection caches in such a way that it prevents Hot Redeployment // on MS Windows. Resource originalWarResource = webApp; // Is the WAR usable directly? if (Resources.isReadableFile(webApp) && FileID.isArchive(webApp.getURI()) && !webApp.getURI().getScheme().equalsIgnoreCase("jar")) { // Turned this into a jar URL. Resource jarWebApp = context.getResourceFactory().newJarFileResource(webApp.getURI()); if (Resources.isDirectory(jarWebApp)) webApp = jarWebApp; } // If we should extract or the URL is still not usable if ((context.isCopyWebDir() && webApp.getPath() != null && originalWarResource.isDirectory()) || (context.isExtractWAR() && webApp.getPath() != null && !originalWarResource.isDirectory()) || (context.isExtractWAR() && webApp.getPath() == null) || !webApp.isDirectory() ) { // Look for sibling directory. Path extractedWebAppDir = null; if (war != null) { Path warPath = context.getResourceFactory().newResource(war).getPath(); // look for a sibling like "foo/" to a "foo.war" if (warPath != null && FileID.isWebArchive(warPath) && Files.exists(warPath)) { Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) extractedWebAppDir = sibling; } } if (extractedWebAppDir == null) { // Then extract it if necessary to the temporary location extractedWebAppDir = context.getTempDirectory().toPath().resolve("webapp"); context.setAttribute(TEMPORARY_RESOURCE_BASE, extractedWebAppDir); } if (Resources.isReadableDirectory(webApp)) { // Copy directory if (LOG.isDebugEnabled()) LOG.debug("Copy {} to {}", webApp, extractedWebAppDir); webApp.copyTo(extractedWebAppDir); } else { //Use a sentinel file that will exist only whilst the extraction is taking place. //This will help us detect interrupted extractions. File extractionLock = new File(context.getTempDirectory(), ".extract_lock"); if (!Files.exists(extractedWebAppDir)) { //it hasn't been extracted before so extract it extractionLock.createNewFile(); Files.createDirectory(extractedWebAppDir); if (LOG.isDebugEnabled()) LOG.debug("Extract {} to {}", webApp, extractedWebAppDir); try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { Resource jarWebApp = resourceFactory.newJarFileResource(webApp.getURI()); jarWebApp.copyTo(extractedWebAppDir); } extractionLock.delete(); } else { // Only extract if the war file is newer, or a .extract_lock file is left behind meaning a possible partial extraction // Use the original War Resource to obtain lastModified to avoid filesystem locks on MS Windows. if (originalWarResource.lastModified().isAfter(Files.getLastModifiedTime(extractedWebAppDir).toInstant()) || extractionLock.exists()) { extractionLock.createNewFile(); // Best effort delete if (IO.delete(extractedWebAppDir)) { // Recreate the directory if it was deleted. Files.createDirectory(extractedWebAppDir); } else { if (LOG.isInfoEnabled()) LOG.info("Unable to delete path {}, reusing existing path", extractedWebAppDir); } if (LOG.isDebugEnabled()) LOG.debug("Extract {} to {}", webApp, extractedWebAppDir); try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { Resource jarWebApp = resourceFactory.newJarFileResource(webApp.getURI()); jarWebApp.copyTo(extractedWebAppDir); } extractionLock.delete(); } } } Resource extractedWebApp = context.getResourceFactory().newResource(extractedWebAppDir.normalize()); if (extractedWebApp == null) LOG.warn("Unable to use non-existent extracted war location: " + extractedWebApp); else webApp = extractedWebApp; } // Now do we have something usable? if (Resources.missing(webApp)) { LOG.warn("Web application not found {}", war); throw new java.io.FileNotFoundException(war); } context.setBaseResource(webApp); if (LOG.isDebugEnabled()) LOG.debug("webapp={}", webApp); } // Do we need to extract WEB-INF/lib? if (context.isCopyWebInf() && !context.isCopyWebDir()) { Resource webInf = webApp.resolve("WEB-INF/"); if (Resources.isReadableDirectory(webInf)) { File extractedWebInfDir = new File(context.getTempDirectory(), "webinf"); if (extractedWebInfDir.exists()) IO.delete(extractedWebInfDir); extractedWebInfDir.mkdir(); Resource webInfLib = webInf.resolve("lib/"); File webInfDir = new File(extractedWebInfDir, "WEB-INF"); webInfDir.mkdir(); if (Resources.isReadableDirectory(webInfLib)) { File webInfLibDir = new File(webInfDir, "lib"); if (webInfLibDir.exists()) IO.delete(webInfLibDir); webInfLibDir.mkdir(); if (LOG.isDebugEnabled()) LOG.debug("Copying WEB-INF/lib {} to {}", webInfLib, webInfLibDir); webInfLib.copyTo(webInfLibDir.toPath()); } Resource webInfClasses = webInf.resolve("classes/"); if (Resources.isReadableDirectory(webInfClasses)) { File webInfClassesDir = new File(webInfDir, "classes"); if (webInfClassesDir.exists()) IO.delete(webInfClassesDir); webInfClassesDir.mkdir(); if (LOG.isDebugEnabled()) LOG.debug("Copying WEB-INF/classes from {} to {}", webInfClasses, webInfClassesDir.getAbsolutePath()); webInfClasses.copyTo(webInfClassesDir.toPath()); } webInf = context.getResourceFactory().newResource(extractedWebInfDir.getCanonicalPath()); } Resource rc = ResourceFactory.combine(webInf, webApp); if (LOG.isDebugEnabled()) LOG.debug("context.baseResource={}", rc); context.setBaseResource(rc); } } /** * Create a canonical name for a webapp temp directory. *

* The form of the name is: * *

"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"
* * host and port uniquely identify the server * context and virtual host uniquely identify the webapp * randomdigits ensure every tmp directory is unique * * @param context the context to get the canonical name from * @return the canonical name for the webapp temp directory * @deprecated this method is no longer used */ @Deprecated(forRemoval = true, since = "12.0.12") public static String getCanonicalNameForWebAppTmpDir(WebAppContext context) { return context.getCanonicalNameForTmpDir(); } /** * @param resource the Resource for which to extract a short name * @return extract a short name for the resource * @deprecated this method is no longer needed */ @Deprecated(forRemoval = true, since = "12.0.12") protected static String getResourceBaseName(Resource resource) { // Use File System and File interface if present Path resourceFile = resource.getPath(); if ((resourceFile != null) && (resource instanceof MountedPathResource)) { resourceFile = ((MountedPathResource)resource).getContainerPath(); } if (resourceFile != null) { Path fileName = resourceFile.getFileName(); return fileName == null ? "" : fileName.toString(); } // Use URI itself. URI uri = resource.getURI(); if (uri == null) { if (LOG.isDebugEnabled()) { LOG.debug("Resource has no URI reference: {}", resource); } return ""; } return URIUtil.getUriLastPathSegment(uri); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy