src/java.base/share/classes/java/util/jar/JarOutputStream.java
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
child 58242 94bb65cb37d3
child 58678 9cf78a70fa4f
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.util.jar;
       
    27 
       
    28 import java.util.zip.*;
       
    29 import java.io.*;
       
    30 
       
    31 /**
       
    32  * The <code>JarOutputStream</code> class is used to write the contents
       
    33  * of a JAR file to any output stream. It extends the class
       
    34  * <code>java.util.zip.ZipOutputStream</code> with support
       
    35  * for writing an optional <code>Manifest</code> entry. The
       
    36  * <code>Manifest</code> can be used to specify meta-information about
       
    37  * the JAR file and its entries.
       
    38  *
       
    39  * @author  David Connelly
       
    40  * @see     Manifest
       
    41  * @see     java.util.zip.ZipOutputStream
       
    42  * @since   1.2
       
    43  */
       
    44 public
       
    45 class JarOutputStream extends ZipOutputStream {
       
    46     private static final int JAR_MAGIC = 0xCAFE;
       
    47 
       
    48     /**
       
    49      * Creates a new <code>JarOutputStream</code> with the specified
       
    50      * <code>Manifest</code>. The manifest is written as the first
       
    51      * entry to the output stream.
       
    52      *
       
    53      * @param out the actual output stream
       
    54      * @param man the optional <code>Manifest</code>
       
    55      * @exception IOException if an I/O error has occurred
       
    56      */
       
    57     public JarOutputStream(OutputStream out, Manifest man) throws IOException {
       
    58         super(out);
       
    59         if (man == null) {
       
    60             throw new NullPointerException("man");
       
    61         }
       
    62         ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
       
    63         putNextEntry(e);
       
    64         man.write(new BufferedOutputStream(this));
       
    65         closeEntry();
       
    66     }
       
    67 
       
    68     /**
       
    69      * Creates a new <code>JarOutputStream</code> with no manifest.
       
    70      * @param out the actual output stream
       
    71      * @exception IOException if an I/O error has occurred
       
    72      */
       
    73     public JarOutputStream(OutputStream out) throws IOException {
       
    74         super(out);
       
    75     }
       
    76 
       
    77     /**
       
    78      * Begins writing a new JAR file entry and positions the stream
       
    79      * to the start of the entry data. This method will also close
       
    80      * any previous entry. The default compression method will be
       
    81      * used if no compression method was specified for the entry.
       
    82      * The current time will be used if the entry has no set modification
       
    83      * time.
       
    84      *
       
    85      * @param ze the ZIP/JAR entry to be written
       
    86      * @exception ZipException if a ZIP error has occurred
       
    87      * @exception IOException if an I/O error has occurred
       
    88      */
       
    89     public void putNextEntry(ZipEntry ze) throws IOException {
       
    90         if (firstEntry) {
       
    91             // Make sure that extra field data for first JAR
       
    92             // entry includes JAR magic number id.
       
    93             byte[] edata = ze.getExtra();
       
    94             if (edata == null || !hasMagic(edata)) {
       
    95                 if (edata == null) {
       
    96                     edata = new byte[4];
       
    97                 } else {
       
    98                     // Prepend magic to existing extra data
       
    99                     byte[] tmp = new byte[edata.length + 4];
       
   100                     System.arraycopy(edata, 0, tmp, 4, edata.length);
       
   101                     edata = tmp;
       
   102                 }
       
   103                 set16(edata, 0, JAR_MAGIC); // extra field id
       
   104                 set16(edata, 2, 0);         // extra field size
       
   105                 ze.setExtra(edata);
       
   106             }
       
   107             firstEntry = false;
       
   108         }
       
   109         super.putNextEntry(ze);
       
   110     }
       
   111 
       
   112     private boolean firstEntry = true;
       
   113 
       
   114     /*
       
   115      * Returns true if specified byte array contains the
       
   116      * jar magic extra field id.
       
   117      */
       
   118     private static boolean hasMagic(byte[] edata) {
       
   119         try {
       
   120             int i = 0;
       
   121             while (i < edata.length) {
       
   122                 if (get16(edata, i) == JAR_MAGIC) {
       
   123                     return true;
       
   124                 }
       
   125                 i += get16(edata, i + 2) + 4;
       
   126             }
       
   127         } catch (ArrayIndexOutOfBoundsException e) {
       
   128             // Invalid extra field data
       
   129         }
       
   130         return false;
       
   131     }
       
   132 
       
   133     /*
       
   134      * Fetches unsigned 16-bit value from byte array at specified offset.
       
   135      * The bytes are assumed to be in Intel (little-endian) byte order.
       
   136      */
       
   137     private static int get16(byte[] b, int off) {
       
   138         return Byte.toUnsignedInt(b[off]) | ( Byte.toUnsignedInt(b[off+1]) << 8);
       
   139     }
       
   140 
       
   141     /*
       
   142      * Sets 16-bit value at specified offset. The bytes are assumed to
       
   143      * be in Intel (little-endian) byte order.
       
   144      */
       
   145     private static void set16(byte[] b, int off, int value) {
       
   146         b[off+0] = (byte)value;
       
   147         b[off+1] = (byte)(value >> 8);
       
   148     }
       
   149 }