alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (InputJarArchive.java)

This example Glassfish source code file (InputJarArchive.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

entryenumeration, enumeration, file, inputjararchive, io, ioexception, ioexception, jar, jarentry, jarentry, jarinputstream, log, logging, net, network, override, override, string, uri, uri

The Glassfish InputJarArchive.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2006-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.deployment.deploy.shared;

import com.sun.logging.LogDomains;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.util.io.FileUtils;
import java.net.MalformedURLException;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.deployment.common.DeploymentUtils;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.PerLookup;

import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * This implementation of the Archive deal with reading
 * jar files either from a JarFile or from a JarInputStream
 *
 * @author Jerome Dochez
 */
@Service(name="jar")
@Scoped(PerLookup.class)
public class InputJarArchive extends JarArchive implements ReadableArchive {
    
    final static Logger logger = LogDomains.getLogger(DeploymentUtils.class, LogDomains.DPL_LOGGER);

    // the file we are currently mapped to 
    volatile protected JarFile jarFile=null;
    
    // in case this abstraction is dealing with a jar file
    // within a jar file, the jarFile will be null and this
    // JarInputStream will contain the 
    volatile protected JarInputStream jarIS=null;
    
    // the archive Uri
    volatile private URI uri;

    // parent jar file for embedded jar
    private InputJarArchive parentArchive=null;

    private StringManager localStrings = StringManager.getManager(getClass());
    
    /**
     * Get the size of the archive
     * @return tje the size of this archive or -1 on error
     */
    public long getArchiveSize() throws NullPointerException, SecurityException {
        if(uri == null) {
            return -1;
        }
        File tmpFile = new File(uri);
        return(tmpFile.length());
    }
    
    /** @return an @see java.io.OutputStream for a new entry in this
     * current abstract archive.
     * @param name the entry name
     */
    public OutputStream addEntry(String name) throws IOException {
        throw new UnsupportedOperationException("Cannot write to an JAR archive open for reading");        
    }
    
    /** 
     * close the abstract archive
     */
    public void close() throws IOException {
        if (jarFile!=null) {
            jarFile.close();
            jarFile=null;
        }
        if (jarIS!=null) {
            jarIS.close();
            jarIS=null;
        }
    }

    /**
     * Returns the collection of first level directories in this
     * archive.
     * <p>
     * Avoid having to fetch all the entries if we can avoid it.  The only time
     * we must do that is if size() is invoked on the collection.  Use
     * the CollectionWrappedEnumeration for this optimization which will
     * create a full in-memory list of the entries only if and when needed
     * to satisfy the size() method.
     *
     * @return collection of directories under the root of this archive
     */
    @Override
    public Collection<String> getDirectories() throws IOException {
        return new CollectionWrappedEnumeration<String>(
                new CollectionWrappedEnumeration.EnumerationFactory<String>() {

            @Override
            public Enumeration<String> enumeration() {
                return entries(true);
            }
        });
    }

    /** 
     * creates a new abstract archive with the given path
     *
     * @param uri the path to create the archive
     */
    public void create(URI uri) throws IOException {
        throw new UnsupportedOperationException("Cannot write to an JAR archive open for reading");        
    }

    @Override
    public Enumeration<String> entries() {
        return entries(false);
    }

    /**
     * Returns an enumeration of the entry names in the archive.
     *
     * @param topLevelDirectoriesOnly whether to report directories only or non-directories only
     * @return enumeration of the matching entry names, excluding the manifest
     */
    private Enumeration<String> entries(final boolean topLevelDirectoriesOnly) {
        /*
         * We have two decisions to make: 
         * 
         * 1. whether the caller wants top-level directory entries or all 
         * non-directory entries enumerated, and 
         * 
         * 2. what URI to use to open a stream to this archive (determined by
         * whether the archive is a subarchive of another archive or is a 
         * stand-alone archive).
         *
         * The URI is a top-level URI (file:path-to-file.jar) if
         * this archive has no parent archive, and it's an entry URI
         * (jar:path-to-file.jar!path-to-inner-archive.jar) format
         * URI if this archive does in fact have a parent archive - which means 
         * that this is a subarchive.
         *
         * Whichever enumerator we choose to create will open and, eventually,
         * close the URI once all the entries have been reported.  That's why
         * we create the URI rather than opening the stream now.
         */
        URI uriToReadForEntries = null;
        try {
            if (parentArchive != null) {
                uriToReadForEntries = new URI(
                    "jar",
                    "file:" + parentArchive.getURI().getSchemeSpecificPart() +
                        "!/" +
                        getURI().getSchemeSpecificPart(),
                    null);
            } else {
                try {
                    if (jarFile == null) {
                        getJarFile(uri);
                    }
                } catch (IOException ioe) {
                    return Collections.enumeration(Collections.EMPTY_LIST);
                }
                if (jarFile == null) {
                    return Collections.enumeration(Collections.EMPTY_LIST);
                }

                uriToReadForEntries = new URI("file", getURI().getSchemeSpecificPart(), null);
            }

            return createEntryEnumeration(uriToReadForEntries, topLevelDirectoriesOnly);
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        } catch (URISyntaxException use) {
            throw new RuntimeException(use);
        }
    }

    /**
     *  @return an @see java.util.Enumeration of entries in this abstract
     * archive, providing the list of embedded archive to not count their 
     * entries as part of this archive
     */
     public Enumeration entries(Enumeration embeddedArchives) {
	// jar file are not recursive    
  	return entries();
    }

    public JarEntry getJarEntry(String name) {
        if (jarFile!=null) {
            return jarFile.getJarEntry(name);
        }
        return null;
    }
    
    /**
     * Returns the existence of the given entry name
     * 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 the existence the given entry name.
     */
    public boolean exists(String name) throws IOException {
        if (jarFile!=null) {
            ZipEntry ze = jarFile.getEntry(name);
            if (ze!=null) {
                return true;
            }
        }
        return false;
    }    

    /**
     * @return a @see java.io.InputStream for an existing entry in
     * the current abstract archive
     * @param entryName entry name
     */
    public InputStream getEntry(String entryName) throws IOException {
        if (jarFile!=null) {
            ZipEntry ze = jarFile.getEntry(entryName);
            if (ze!=null) {
                return new BufferedInputStream(jarFile.getInputStream(ze));
            } else {
                return null;
            }            
        } else
	if ((parentArchive != null) && (parentArchive.jarFile != null)) {
            JarEntry je;
            // close the current input stream
            if (jarIS!=null) {
                jarIS.close();
            }
            
            // reopen the embedded archive and position the input stream
            // at the beginning of the desired element
	    JarEntry archiveJarEntry = (uri != null)? parentArchive.jarFile.getJarEntry(uri.getSchemeSpecificPart()) : null;
	    if (archiveJarEntry == null) {
		return null;
	    }
            jarIS = new JarInputStream(parentArchive.jarFile.getInputStream(archiveJarEntry));
            do {
                je = jarIS.getNextJarEntry();
            } while (je!=null && !je.getName().equals(entryName));
            if (je!=null) {
                return new BufferedInputStream(jarIS);
            } else {
                return null;
            }
        } else {
	    return null;
	}
    }

    /**
     * Returns the entry size for a given entry name or 0 if not known
     *
     * @param name the entry name
     * @return the entry size
     */
    public long getEntrySize(String name) {
        if (jarFile!=null) {
            ZipEntry ze = jarFile.getEntry(name);
            if (ze!=null) {
                return ze.getSize();
            }
        }
        return 0;
    }

    /** Open an abstract archive
     * @param uri the path to the archive
     */
    public void open(URI uri) throws IOException {
       this.uri = uri;
       jarFile = getJarFile(uri);
    }
    
    /**
     * @return a JarFile instance for a file path
     */
    protected JarFile getJarFile(URI uri) throws IOException {
        jarFile = null;
        try {
            File file = new File(uri);
            if (file.exists()) {
                jarFile = new JarFile(file);
            }
        } catch(IOException e) {
            logger.log(Level.WARNING,
                "enterprise.deployment.backend.fileOpenFailure", 
                new Object[]{uri});
            // add the additional information about the path
            // since the IOException from jdk doesn't include that info
            String additionalInfo = localStrings.getString(
                "enterprise.deployment.invalid_zip_file", uri);
            logger.log(Level.WARNING,
                e.getLocalizedMessage() + " --  " + additionalInfo);
        }
        return jarFile;
    }       
    
    
    /** 
     * @return the manifest information for this abstract archive
     */
    public Manifest getManifest() throws IOException {
        if (jarFile!=null) {
            return jarFile.getManifest();
        } 
        if (parentArchive!=null) {    
            // close the current input stream
            if (jarIS!=null) {
                jarIS.close();
            }
            // reopen the embedded archive and position the input stream
            // at the beginning of the desired element
            if (jarIS==null) {
                jarIS = new JarInputStream(parentArchive.jarFile.getInputStream(parentArchive.jarFile.getJarEntry(uri.getSchemeSpecificPart())));
            }
            Manifest m = jarIS.getManifest();
            if (m==null) {
               java.io.InputStream is = getEntry(java.util.jar.JarFile.MANIFEST_NAME);
               if (is!=null) {
                    m = new Manifest();
                    m.read(is);
                    is.close();
               }
            }
            return m;
        }                        
        return null;
    }

    /**
     * Returns the path used to create or open the underlying archive
     *
     * @return the path for this archive.
     */
    public URI getURI() {
        return uri;
    }

    /**
     * @return true if this abstract archive maps to an existing 
     * jar file
     */
    public boolean exists() {
        return jarFile!=null;
    }
    
    /**
     * deletes the underlying jar file
     */
    public boolean delete() {
        if (jarFile==null) {
            return false;
        }
        try {
            jarFile.close();
            jarFile = null;
        } catch (IOException ioe) {
            return false;
        }
        return FileUtils.deleteFile(new File(uri));
    }
    
    /**
     * rename the underlying jar file
     */
    public boolean renameTo(String name) {
        if (jarFile==null) {
            return false;
        }
        try {
            jarFile.close();
            jarFile = null;
        } catch (IOException ioe) {
            return false;
        }        
        return FileUtils.renameFile(new File(uri), new File(name));
    }
    
    /**
     * @return an Archive for an embedded archive indentified with
     * the name parameter
     */
    public ReadableArchive getSubArchive(String name) throws IOException {
        if (jarFile!=null) {
            // for now, I only support one level down embedded archives
            InputJarArchive ija = new InputJarArchive();
            JarEntry je = jarFile.getJarEntry(name);
            if (je!=null) {
                JarInputStream jis = new JarInputStream(new BufferedInputStream(jarFile.getInputStream(je)));
                try {
                    ija.uri = new URI("jar",name, null);
                } catch(URISyntaxException e) {
                    // do nothing
                }
                ija.jarIS = jis;
                ija.parentArchive = this;
                return ija;
            }
        }
        return null;
    }

    /**
     * Creates the correct type of entry enumeration, depending on whether the
     * current archive is nested or not and depending on whether the caller
     * requested top-level directory entries or all non-directory entries be returned
     * in the enumeration.
     *
     * @param uriToReadForEntries
     * @param topLevelDirectoriesOnly
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     */
    private EntryEnumeration createEntryEnumeration(
            final URI uriToReadForEntries,
            final boolean topLevelDirectoriesOnly) throws FileNotFoundException, IOException {
        if (topLevelDirectoriesOnly) {
            return new TopLevelDirectoryEntryEnumeration(uriToReadForEntries);
        } else {
            return new NonDirectoryEntryEnumeration(uriToReadForEntries);
        }
    }

    /**
     * Logic for enumerations of the entry names that is common between the
     * top-level directory entry enumeration and the full non-directory
     * enumeration.
     * <p>
     * The goal is to wrap an Enumeration around the underlying entries 
     * available in the archive.  This avoids collecting all
     * the entry names first and then returning an enumeration of the collection;
     * that can be very costly for large JARs.
     */
    private abstract class EntryEnumeration implements Enumeration<String> {

        /** look-ahead of one entry */
        private JarEntry nextMatchingEntry;

        private final JarInputStream jis;
        private boolean reachedEndOfStream = false;

    private EntryEnumeration(final URI archiveURI) throws MalformedURLException, IOException {
            final InputStream is = archiveURI.toURL().openStream();
            this.jis = new JarInputStream(is);
        }

        /**
         * Finishes the initialization for the enumeration; MUST be invoked
         * from the subclass constructor after super(...).
         */
        protected void completeInit() {
            nextMatchingEntry = skipToNextMatchingEntry();
        }

        @Override
        public boolean hasMoreElements() {
            return nextMatchingEntry != null;
        }

        @Override
        public String nextElement() {
            if (nextMatchingEntry == null) {
                throw new NoSuchElementException();
            }
            final String answer = nextMatchingEntry.getName();
            nextMatchingEntry = skipToNextMatchingEntry();
            return answer;
        }

        /**
         * Returns the next JarEntry available from the archive.
         * <p>
         * Different concrete subclasses implement this differently so as to
         * enumerate the correct sequence of entry names.
         *
         * @return the next available JarEntry; null if no more are available
         */
        protected abstract JarEntry skipToNextMatchingEntry();

        protected JarEntry getNextJarEntry() {
            if (reachedEndOfStream) {
                return null;
            }
            try {
                final JarEntry result = jis.getNextJarEntry();
                if (result == null) {
                    jis.close();
                    reachedEndOfStream = true;
                }
                return result;
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            if ( ! reachedEndOfStream) {
                jis.close();
            }
        }
    }

    /**
     * Enumerates the top-level directory entries.
     * <p>
     * This implementation uses the enumeration of JarEntry objects from the
     * JarFile itself.
     */
    private class TopLevelDirectoryEntryEnumeration extends EntryEnumeration {

        private TopLevelDirectoryEntryEnumeration(final URI archiveURI) throws FileNotFoundException, IOException {
            super(archiveURI);
            completeInit();
        }

        @Override
        protected JarEntry skipToNextMatchingEntry() {
            JarEntry candidateNextEntry;

            /*
             * The next entry should be returned (and not skipped) if the entry is a
             * directory entry and it contains only a single slash at the
             * end of the entry name.
             */
            while ((candidateNextEntry = getNextJarEntry()) != null) {
                final String candidateNextEntryName = candidateNextEntry.getName();
                if ( candidateNextEntry.isDirectory() &&
                       (candidateNextEntryName.indexOf('/') ==
                            candidateNextEntryName.lastIndexOf('/')) &&
                       (candidateNextEntryName.indexOf('/') ==
                            candidateNextEntryName.length() - 1)
                   ) {
                    break;
                }
            }
            return candidateNextEntry;
        }
    }

    /**
     * Enumerates entries in the archive that are not directories.
     */
    private class NonDirectoryEntryEnumeration extends EntryEnumeration {

        private NonDirectoryEntryEnumeration(final URI archiveURI) throws IOException {
            super(archiveURI);
            completeInit();
        }

        @Override
        protected JarEntry skipToNextMatchingEntry() {
            JarEntry candidateNextEntry;

            /*
             * The next entry should be returned (and not skipped) if the entry is
             * not a directory entry and if it also not the manifest.
             */
            while ((candidateNextEntry = getNextJarEntry()) != null) {
                final String candidateNextEntryName = candidateNextEntry.getName();
                if ( ! candidateNextEntry.isDirectory() &&
                          ! candidateNextEntryName.equals(JarFile.MANIFEST_NAME)
                   ) {
                    break;
                }
            }
            return candidateNextEntry;
        }
    }
    
    /**
     * A Collection which wraps an Enumeration.
     * <p>
     * Note that the nextSlot field is always updated, even if we are using
     * the original enumeration to return the next value from the iterator.  This
     * is so that, if the caller invokes size() which causes us to build the
     * ArrayList containing all the elements -- even if that invocation comes while
     * the iterator is being used to return values -- the subsequent invocations
     * of hasNext and next will use the correct place in the newly-constructed
     * ArrayList of values.
     *
     * @param <T>
     */
    static class CollectionWrappedEnumeration<T> extends AbstractCollection {

        /** Used only if size is invoked */
        private ArrayList<T> entries = null;

        /** always updated, even if we use the enumeration */
        private int nextSlot = 0;

        private final EnumerationFactory<T> factory;

        private Enumeration<T> e;

        static interface EnumerationFactory<T> {
            public Enumeration<T> enumeration();
        }

        CollectionWrappedEnumeration(final EnumerationFactory<T> factory) {
            this.factory = factory;
            e = factory.enumeration();
        }

        @Override
        public Iterator<T> iterator() {
            return new Iterator<T>() {

                @Override
                public boolean hasNext() {
                    return (entries != null) ?
                        nextSlot < entries.size() :
                        e.hasMoreElements();
                }

                @Override
                public T next() {
                    T result = null;
                    if (entries != null) {
                        if (nextSlot >= entries.size()) {
                            throw new NoSuchElementException();
                        }
                        result = entries.get(nextSlot++);
                    } else {
                        result = e.nextElement();
                        nextSlot++;
                    }
                    return result;
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }

        @Override
        public int size() {
            if (entries == null) {
                populateEntries();
            };
            return entries.size();
        }

        private void populateEntries() {
            entries = new ArrayList<T>();
            /*
             * Fill up the with data from
             * a new enumeration.
             */
            for (Enumeration<T> newE = factory.enumeration(); newE.hasMoreElements(); ) {
                entries.add(newE.nextElement());
            }
            e = null;
        }
    }
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish InputJarArchive.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.