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

org.glassfish.internal.embedded.ScatteredArchive Maven / Gradle / Ivy

There is a newer version: 8.0.0-JDK17-M9
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.internal.embedded;

import org.glassfish.api.deployment.archive.ReadableArchiveAdapter;

import java.io.*;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.MalformedURLException;
import java.util.*;
import java.util.jar.Manifest;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.jar.JarEntry;

/**
 * Abstraction for a scattered archive (parts disseminated in various directories)
 *
 * @author Jerome Dochez
 */
public class ScatteredArchive extends ReadableArchiveAdapter {


    public static class Builder {

        final String name;
        File topDir = null;
        File resources = null;
        final List urls = new ArrayList();
        final Map metadata = new HashMap();


        /**
         * Construct a new scattered archive builder with the minimum information
         * By default, a scattered archive is not different from any other
         * archive where all the files are located under a top level
         * directory (topDir).
         * Some files can then be scattered in different locations and be specified
         * through the appropriate setters.
         * Alternatively, topDir can be null to specify a truely scattered archive
         * and all the locations must be specified.
         *
         * @param name   archive name
         * @param topDir top level directory
         */
        public Builder(String name, File topDir) {
            this.name = name;
            this.topDir = topDir;
        }

        /**
         * Construct a new scattered archive builder with a set of URLs as repository
         * for locating archive resources (like .class files).
         * @param name archive name
         * @param urls set of resources repository
         */
        public Builder(String name, Collection urls) {
            this.name = name;
            for (URL u : urls) {
                this.urls.add(u);
            }
        }

        /**
         * Sets the location of resources files
         *
         * @param resources the resources directory
         * @return itself
         */
        public Builder resources(File resources) {
            if (!resources.exists()) {
                throw new IllegalArgumentException(resources.getAbsolutePath() + " not found");
            }
            this.resources = resources;
            return this;
        }

        /**
         * Add a new metadata locator for this scattered archive. A metadata is identified
         * by its name (like WEB-INF/web.xml) and the location of the metadata is used when
         * the embedded server is requesting the metadata file.
         * The name for this metadata will be obtained by doing metadata.getName()
         *
         * @param metadata the metadata file location
         *
         * @return itself
         */
        public Builder addMetadata(File metadata) {
            if (!metadata.exists()) {
                throw new IllegalArgumentException(metadata.getAbsolutePath() + " not found");
            }
            return addMetadata(metadata.getName(), metadata);
        }

        /**
         * Add a new metadata locator for this scattered archive. A metadata is identified
         * by its name (like WEB-INF/web.xml) and the location of the metadata is used when
         * the embedded server is requesting the metadata file.
         *
         * @param name name of the metadata (eg WEB-INF/web.xml or web.xml or META-INF/ejb.xml
         * or ejb.xml).
         *
         * @param metadata the metadata file location
         *
         * @return itself
         */
        public Builder addMetadata(String name, File metadata) {
            if (!metadata.exists()) {
                throw new IllegalArgumentException(metadata.getAbsolutePath() + " not found");
            }
            this.metadata.put(name, metadata);
            return this;
        }

        /**
         * Adds a directory to the classes classpath. Will be used to retrieve requested .class
         * files.
         *
         * @param location must be a directory location
         * @return itself
         */
        public Builder addClassPath(File location) {
            if (!location.isDirectory()) {
                throw new IllegalArgumentException("location is not a directory");
            }
            try {
                this.urls.add(location.toURI().toURL());
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException(e);
            }
            return this;
        }

        /**
         * Adds a URL for the classes classpath. Will be used to retrieve requested .class files
         *
         * @param classpath the new classpath element.
         * @return itself
         */

        public Builder addClassPath(URL classpath) {
            this.urls.add(classpath);
            return this;
        }

        /**
         * Creates a new scattered jar file using this builder instance configuration.
         * The resulting instance will behave like a jar file when introspected by the
         * embedded instance.
         *
         * @return new scattered instance jar file
         */
        public ScatteredArchive buildJar() {
            return new ScatteredArchive(this, Builder.type.jar);
        }

        /**
         * Creates a new scattered war file using this builder instance configuration.
         * The resulting instance will behave like a war file when introspected by the
         * embedded instance.
         *
         * @return the scattered instance war file
         */
        public ScatteredArchive buildWar() {
            return new ScatteredArchive(this, Builder.type.war);
        }

        /**
         * Supported types of scattered archives.
         */
        public enum type {
            jar, war
        }
    }

    final String name;
    final File topDir;
    final File resources;
    final List urls = new ArrayList();
    final Map metadata = new HashMap();
    final Builder.type type;
    final String prefix;

    private ScatteredArchive(Builder builder, Builder.type type) {
        name = builder.name;
        topDir = builder.topDir;
        resources = builder.resources;
        urls.addAll(builder.urls);
        if (topDir!=null && type!=Builder.type.war) {
            try {
                urls.add(topDir.toURI().toURL());
            } catch (MalformedURLException ignore) {

            }
        }
        metadata.putAll(builder.metadata);
        this.type = type;
        prefix = type==Builder.type.war?"WEB-INF/classes":null;
    }

    // todo : look at Open(URI), is it ok ?

    /**
     * Get the classpath URLs
     *
     * @return A read-only copy of the classpath URL Collection
     */
    public Iterable getClassPath() {
        return Collections.unmodifiableCollection(urls);
    }

    /**
     * @return The resources directory
     */
    public File getResourcesDir() {
        return resources;
    }

    /**
     * Returns the InputStream for the given entry name
     * The file name must be relative to the root of the module.
     *
     * @param arg the file name relative to the root of the module.
     * @return the InputStream for the given entry name or null if not found.
     */

    public InputStream getEntry(String arg) throws IOException {
        File f = getFile(arg);
        if (f!=null && f.exists()) return new FileInputStream(f);
        JarFile jar = getJarWithEntry(arg);
        if (jar != null) {
            ZipEntry entry = jar.getEntry(arg);
            if (entry != null) {
                return jar.getInputStream(entry);
            }
        }
        return null;
    }

    @Override
    public long getEntrySize(String arg) {
        File f = getFile(arg);
        if (f!=null && f.exists()) return f.length();
        JarFile jar = getJarWithEntry(arg);
        if (jar != null) {
            ZipEntry entry = jar.getEntry(arg);
            if (entry != null) {
                return jar.getEntry(arg).getSize();
            }
        }
        return 0L;
    }

    /**
     * Returns whether or not a file by that name exists
     * The file name must be relative to the root of the module.
     *
     * @param name the file name relative to the root of the module.
     * @return does the file exist?
     */


    public boolean exists(String name) throws IOException {
        if ("WEB-INF".equals(name) && type == Builder.type.war) {
            return true;
        }
        return getEntry(name) != null;
    }

    /**
     * Returns an enumeration of the module file entries.  All elements
     * in the enumeration are of type String.  Each String represents a
     * file name relative to the root of the module.
     * 

Currently under construction * * @return an enumeration of the archive file entries. */ public Enumeration entries() { // TODO: abstraction breakage. We need file-level abstraction for archive // and then more structured abstraction. Vector entries = new Vector(); File localResources = resources; for (URL url : urls) { try { if (localResources!=null && localResources.toURI().toURL().sameFile(url)) { localResources=null; } File f; try { f = new File(url.toURI()); } catch(URISyntaxException e) { f = new File(url.getPath()); } if (f.isFile()) { JarFile jar = new JarFile(f); Enumeration jarEntries = jar.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); if (jarEntry.isDirectory()) { continue; } entries.add(jarEntry.getName()); } } else { getListOfFiles(f, prefix, entries); } } catch (Exception e) { e.printStackTrace(); } } if (localResources!=null) { getListOfFiles(localResources, null, entries); } return entries.elements(); } private void getListOfFiles(File directory, String prefix, List list) { if (!directory.isDirectory()) return; for (File f : directory.listFiles()) { String name = prefix==null?f.getName():prefix+"/"+f.getName(); if (f.isDirectory()) { getListOfFiles(f, name ,list); } else { list.add(name); } } } /** * Returns the manifest information for this archive * * @return the manifest info */ public Manifest getManifest() throws IOException { InputStream is = getEntry(JarFile.MANIFEST_NAME); if (is != null) { try { return new Manifest(is); } finally { is.close(); } } return new Manifest(); } /** * Returns the path used to create or open the underlying archive *

*

* TODO: abstraction breakage: * Several callers, most notably {@link org.glassfish.api.deployment.DeploymentContext#getSourceDir()} * implementation, assumes that this URI is an URL, and in fact file URL. *

*

* If this needs to be URL, use of {@link URI} is misleading. And furthermore, * if its needs to be a file URL, this should be {@link File}. * * @return the path for this archive. */ public URI getURI() { if (topDir != null) { return topDir.toURI(); } if (resources != null) { return resources.toURI(); } try { //TODO : Fix this if (urls.size() > 0) { for (URL url : urls) { File f = new File(url.toURI()); if (f.isFile()) return url.toURI(); } return urls.get(0).toURI(); } return null; } catch (Exception e) { return null; } } /** * Returns the name of the archive. *

* Implementations should not return null. * * @return the name of the archive */ public String getName() { return name; } /** * Returns the archive type * @return the archive type */ public Builder.type type() { return type; } /** * Returns an enumeration of the module file entries with the * specified prefix. All elements in the enumeration are of * type String. Each String represents a file name relative * to the root of the module. *

Currently Not Supported * * @param s the prefix of entries to be included * @return an enumeration of the archive file entries. */ public Enumeration entries(String s) { Enumeration entries = entries(); Vector prefixedEntries = new Vector(); while (entries.hasMoreElements()) { String entry = (String)entries.nextElement(); if (entry.startsWith(s)) prefixedEntries.add(entry); } return prefixedEntries.elements(); } @Override public Collection getDirectories() throws IOException { return new ArrayList(); } public String toString() { return super.toString() + " located at " + (topDir == null ? resources : topDir); } public File getFile(String name) { if (metadata.containsKey(name)) { return metadata.get(name); } String shortName = (name.indexOf("/") != -1 ? name.substring(name.indexOf("/") + 1) : name); if (metadata.containsKey(shortName)) { return metadata.get(shortName); } if (resources != null) { File f = new File(resources, name); if (f.exists()) { return f; } } if (prefix!=null) { if (name.startsWith(prefix)) { name = name.substring(prefix.length()+1); } } for (URL url : urls) { File f = null; try { f = new File(url.toURI()); } catch(URISyntaxException e) { f = new File(url.getPath()); } f = new File(f, name); if (f.exists()) { return f; } } return null; } private JarFile getJarWithEntry(String name) { for (URL url : urls) { File f = null; try { f = new File(url.toURI()); } catch(URISyntaxException e) { f = new File(url.getPath()); } try { if (f.getName().endsWith(".jar")) { JarFile jar = new JarFile(f); if (jar.getEntry(name) != null) { return jar; } } } catch (Exception ex) { ex.printStackTrace(); return null; } } return null; } @Override public void close() throws IOException { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy