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

com.tangosol.dev.packager.JarFilePackagerSet Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.dev.packager;


import  com.tangosol.io.Base64OutputStream;

import  java.io.FileOutputStream;
import  java.io.IOException;

import  java.util.Collection;
import  java.util.Iterator;
import  java.util.Properties;

import  java.util.jar.Attributes;
import  java.util.jar.JarEntry;
import  java.util.jar.Manifest;
import  java.util.jar.JarOutputStream;

import  java.util.zip.CRC32;
import  java.util.zip.ZipEntry;

import  java.security.MessageDigest;
import  java.security.NoSuchAlgorithmException;


/**
*  This kind of PackagerSet materializes its collection of files in a
*  Jar file with a Manifest.
*/
public class JarFilePackagerSet
        extends PackagerSet
    {
    /**
    *  Construct a JarFilePackagerSet.
    */
    public JarFilePackagerSet()
        {
        super();
        }

    /**
    *  Construct a JarFilePackagerSet to be materialized to the file specified.
    */
    public JarFilePackagerSet(String fileName)
            throws IOException
        {
        super();
        setJarFilePathName(fileName);
        }

    /**
    *  Return whether file compression will be used for the JarFilePackagerSet.
    */
    public boolean isCompressed()
        {
        return(compressed);
        }

    /**
    *  Set whether file compression will be used for the JarFilePackagerSet.
    *  This method must be called before the JarFilePackagerSet is materialized.
    */
    public void setCompressed(boolean compressed)
        {
        this.compressed = compressed;
        }

    /**
    *  Return the manifest entry. This method could be overriden by subclasses
    *  to supply a custom manifest
    */
    public Manifest getManifest()
        {
        return(manifest);
        }

    /**
    *  Sets the the manifest. This method could be used by a client to supply
    *  a custom Manifest.
    *
    *  This method must be called before the JarFilePackagerSet is materialized.
    */
    public void setManifest(Manifest manifest)
        {
        this.manifest = manifest;
        }

    /**
    *  Return the style of the manifest generation.
    *
    *  Note: this value has no relevance if the Manifest is set directly
    */
    public int getManifestStyle()
        {
        return(style);
        }

    /**
    *  Sets the style of the manifest generation. Valid values are:
    *    MANIFEST_NONE   -- no manifest generation is required
    *    MANIFEST_BLANK  -- manifest should be empty
    *    MANIFEST_HEADER -- manifest should be only contain the header
    *    MANIFEST_FULL   -- full manifest should be generated
    *
    *  This method must be called before the JarFilePackagerSet is materialized.
    */
    public void setManifestStyle(int style)
        {
        this.style = style;
        }

    /**
    *  Return the file name to be used for materialization.
    */
    public String getJarFilePathName()
        {
        return(jarFilePathName);
        }

    /**
    *  Set the file name to be used for materialization.
    */
    public void setJarFilePathName(String jarFilePathName)
            throws IOException
        {
        this.jarFilePathName = jarFilePathName;
        }

    /**
    *  Materialize the PackagerSet from its specified entries in the context
    *  of the specified ClassLoader.
    */
    public void materialize(ClassLoader classLoader)
            throws IOException, UnexpectedPackagerException
        {
        Collection entries  = getCollectedEntries();
        Manifest   manifest = getManifest();

        if (manifest == null)
            {
            manifest = createManifest(classLoader);
            }

        FileOutputStream fileOut = new FileOutputStream(jarFilePathName);
        JarOutputStream  jarOut  = manifest == null ?
            new JarOutputStream(fileOut) : new JarOutputStream(fileOut, manifest);

        try
            {
            // store all the entries with their data
            Iterator entriesIterator = entries.iterator();
            while (entriesIterator.hasNext())
                {
                PackagerEntry entry = (PackagerEntry)entriesIterator.next();

                try
                    {
                    storeEntryData(jarOut, entry, classLoader);
                    }
                catch (PackagerEntryNotFoundException e)
                    {
                    throw new IOException("Failed to store " + entry + ": " + e);
                    }
                }
            }
        finally
            {
            jarOut.close();
            }
        }

    /**
    *  Create the Manifest for the Jar file in the context of the specified
    *  ClassLoader.
    *
    *  This method assigns top-level attributes in the manifest, creates
    *  entries for each PackagerEntry, and computes and records SHA and
    *  MD5 message digests for each entry (excluding directory entries
    *  and package entries which have zero length).
    */
    protected Manifest createManifest(ClassLoader classLoader)
        {
        if (style == MANIFEST_NONE)
            {
            return null;
            }

        Manifest manifest = new Manifest();

        if (style > MANIFEST_BLANK)
            {
            Collection entries = getCollectedEntries();

            // initialize main attributes
            Attributes mainAttrs = manifest.getMainAttributes();
            mainAttrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");

            // copy attributes from the PackagerSet to the manifest
            Properties mainProps = getAttributes();
            for (Iterator propKeys = mainProps.keySet().iterator(); propKeys.hasNext(); )
                {
                String key = (String)propKeys.next();
                mainAttrs.put(new Attributes.Name(key), mainProps.getProperty(key));
                }

            if (style > MANIFEST_HEADER)
                {
                // set things up to compute message digest attributes
                String[] digestAlgorithms = {"SHA", "MD5"};
                Attributes.Name digestAlgorithmsAttributeName = new Attributes.Name("Digest-Algorithms");
                Attributes.Name[] digestAttributeNames =
                    {
                    new Attributes.Name("SHA-Digest"),
                    new Attributes.Name("MD5-Digest"),
                    };

                MessageDigest[] msgDigests = new MessageDigest[2];
                String digestNames = null;
                for (int i = 0, n = digestAlgorithms.length; i < n; i++)
                    {
                    try
                        {
                        msgDigests[i] = MessageDigest.getInstance(digestAlgorithms[i]);
                        if (digestNames == null)
                            {
                            digestNames = digestAlgorithms[i];
                            }
                        else
                            {
                            digestNames += " " + digestAlgorithms[i];
                            }
                        }
                    catch (NoSuchAlgorithmException noSuchAlgorithm) {}
                    }

                // compute entry attributes and build the manifest
                for (Iterator entriesIterator = entries.iterator(); entriesIterator.hasNext(); )
                    {
                    PackagerEntry entry = (PackagerEntry)entriesIterator.next();
                    Attributes attributes = new Attributes();

                    // compute message digests for entry unless it's a directory or package
                    if (digestNames != null && entry.isSecured())
                        {
                        try
                            {
                            attributes.put(digestAlgorithmsAttributeName, digestNames);
                            byte[] entryData = entry.getData(classLoader);
                            if (entryData == null)
                                {
                                throw new PackagerEntryNotFoundException("No data for: " + entry);
                                }

                            for (int i = 0, n = digestAlgorithms.length; i < n; i++)
                                {
                                MessageDigest msgDigest = msgDigests[i];
                                if (msgDigest != null)
                                    {
                                    msgDigest.reset();
                                    byte[] digestValue  = msgDigest.digest(entryData);
                                    String digestString = new String(Base64OutputStream.encode(digestValue, false));
                                    attributes.put(digestAttributeNames[i], digestString);
                                    }
                                }
                            }
                        catch (PackagerEntryNotFoundException noSuchEntry)
                            {
                            // out(noSuchEntry.getMessage());
                            continue;
                            }

                        // copy attributes from the PackagerEntry to the ZipEntry
                        Properties entryProps = entry.getAttributes();
                        if (entryProps != null)
                            {
                            for (Iterator propKeys = entryProps.keySet().iterator(); propKeys.hasNext(); )
                                {
                                String key = (String)propKeys.next();
                                attributes.put(new Attributes.Name(key), entryProps.getProperty(key));
                                }
                            }
                        }

                    // add the entry with its attributes to the manifest
                    manifest.getEntries().put(entry.getPath().getPathName(), attributes);
                    }
                }
            }

        return manifest;
        }

    /**
    *  Store the PackagerEntry in the PackagerSet, in the context of the
    *  specified ClassLoader.
    *  This method creates and initialized a ZipEntry for the PackagerEntry.
    */
    protected void storeEntryData(JarOutputStream jarOut, PackagerEntry entry, ClassLoader classLoader)
            throws IOException, PackagerEntryNotFoundException
        {
        PackagerPath path = entry.getPath();
        byte[] data = entry.getData(classLoader);
        if (data == null)
            {
            throw new PackagerEntryNotFoundException("No data for: " + entry);
            }

        ZipEntry zipEntry = new JarEntry(path.getPathName());
        int entrySize  = ((data == null) ? 0 : data.length);

        zipEntry.setTime(entry.getModificationTime());
        String comment = entry.getComment();
        if (comment != null)
            {
            zipEntry.setComment(comment);
            }

        if (entrySize == 0)
            {
            zipEntry.setMethod(JarOutputStream.STORED);
            zipEntry.setSize(0L);
            zipEntry.setCrc(0L);
            }
        else
            {
            zipEntry.setMethod(JarOutputStream.DEFLATED);

            if (!isCompressed())
                {
                crc32.reset();
                crc32.update(data);
                zipEntry.setSize(data.length);
                zipEntry.setCrc(crc32.getValue());
                }
            }

        jarOut.putNextEntry(zipEntry);
        jarOut.write(data, 0, entrySize);
        jarOut.closeEntry();
        }


    // ----- constants ------------------------------------------------------

    public final static int  MANIFEST_NONE    = 0;
    public final static int  MANIFEST_BLANK   = 1;
    public final static int  MANIFEST_HEADER  = 2;
    public final static int  MANIFEST_FULL    = 3;


    // ----- data members ---------------------------------------------------

    private       String     jarFilePathName;
    private       boolean    compressed       = true;
    private       Manifest   manifest         = null;
    private       int        style            = MANIFEST_NONE;
    private       CRC32      crc32            = new CRC32();
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy