src/share/jaxws_classes/com/sun/xml/internal/org/jvnet/mimepull/DataHead.java

Wed, 27 Apr 2016 01:27:09 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:27:09 +0800
changeset 0
373ffda63c9a
child 637
9c07ef4934dd
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/jaxws/
changeset: 657:d47a47f961ee
tag: jdk8u25-b17

     1 /*
     2  * Copyright (c) 1997, 2013, 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  */
    26 package com.sun.xml.internal.org.jvnet.mimepull;
    28 import java.io.*;
    29 import java.nio.ByteBuffer;
    31 /**
    32  * Represents an attachment part in a MIME message. MIME message parsing is done
    33  * lazily using a pull parser, so the part may not have all the data. {@link #read}
    34  * and {@link #readOnce} may trigger the actual parsing the message. In fact,
    35  * parsing of an attachment part may be triggered by calling {@link #read} methods
    36  * on some other attachment parts. All this happens behind the scenes so the
    37  * application developer need not worry about these details.
    38  *
    39  * @author Jitendra Kotamraju
    40  */
    41 final class DataHead {
    43     /**
    44      * Linked list to keep the part's content
    45      */
    46     volatile Chunk head, tail;
    48     /**
    49      * If the part is stored in a file, non-null.
    50      */
    51     DataFile dataFile;
    53     private final MIMEPart part;
    55     boolean readOnce;
    56     volatile long inMemory;
    58     /**
    59      * Used only for debugging. This records where readOnce() is called.
    60      */
    61     private Throwable consumedAt;
    63     DataHead(MIMEPart part) {
    64         this.part = part;
    65     }
    67     void addBody(ByteBuffer buf) {
    68         synchronized(this) {
    69             inMemory += buf.limit();
    70         }
    71         if (tail != null) {
    72             tail = tail.createNext(this, buf);
    73         } else {
    74             head = tail = new Chunk(new MemoryData(buf, part.msg.config));
    75         }
    76     }
    78     void doneParsing() {
    79     }
    81     void moveTo(File f) {
    82         if (dataFile != null) {
    83             dataFile.renameTo(f);
    84         } else {
    85             try {
    86                 OutputStream os = new FileOutputStream(f);
    87                 try {
    88                     InputStream in = readOnce();
    89                     byte[] buf = new byte[8192];
    90                     int len;
    91                     while((len=in.read(buf)) != -1) {
    92                         os.write(buf, 0, len);
    93                     }
    94                 } finally {
    95                     if (os != null) {
    96                         os.close();
    97                     }
    98                 }
    99             } catch(IOException ioe) {
   100                 throw new MIMEParsingException(ioe);
   101             }
   102         }
   103     }
   105     void close() {
   106         head = tail = null;
   107         if (dataFile != null) {
   108             dataFile.close();
   109         }
   110     }
   113     /**
   114      * Can get the attachment part's content multiple times. That means
   115      * the full content needs to be there in memory or on the file system.
   116      * Calling this method would trigger parsing for the part's data. So
   117      * do not call this unless it is required(otherwise, just wrap MIMEPart
   118      * into a object that returns InputStream for e.g DataHandler)
   119      *
   120      * @return data for the part's content
   121      */
   122     public InputStream read() {
   123         if (readOnce) {
   124             throw new IllegalStateException("readOnce() is called before, read() cannot be called later.");
   125         }
   127         // Trigger parsing for the part
   128         while(tail == null) {
   129             if (!part.msg.makeProgress()) {
   130                 throw new IllegalStateException("No such MIME Part: "+part);
   131             }
   132         }
   134         if (head == null) {
   135             throw new IllegalStateException("Already read. Probably readOnce() is called before.");
   136         }
   137         return new ReadMultiStream();
   138     }
   140     /**
   141      * Used for an assertion. Returns true when readOnce() is not already called.
   142      * or otherwise throw an exception.
   143      *
   144      * <p>
   145      * Calling this method also marks the stream as 'consumed'
   146      *
   147      * @return true if readOnce() is not called before
   148      */
   149     @SuppressWarnings("ThrowableInitCause")
   150     private boolean unconsumed() {
   151         if (consumedAt != null) {
   152             AssertionError error = new AssertionError("readOnce() is already called before. See the nested exception from where it's called.");
   153             error.initCause(consumedAt);
   154             throw error;
   155         }
   156         consumedAt = new Exception().fillInStackTrace();
   157         return true;
   158     }
   160     /**
   161      * Can get the attachment part's content only once. The content
   162      * will be lost after the method. Content data is not be stored
   163      * on the file system or is not kept in the memory for the
   164      * following case:
   165      *   - Attachement parts contents are accessed sequentially
   166      *
   167      * In general, take advantage of this when the data is used only
   168      * once.
   169      *
   170      * @return data for the part's content
   171      */
   172     public InputStream readOnce() {
   173         assert unconsumed();
   174         if (readOnce) {
   175             throw new IllegalStateException("readOnce() is called before. It can only be called once.");
   176         }
   177         readOnce = true;
   178         // Trigger parsing for the part
   179         while(tail == null) {
   180             if (!part.msg.makeProgress() && tail == null) {
   181                 throw new IllegalStateException("No such Part: "+part);
   182             }
   183         }
   184         InputStream in = new ReadOnceStream();
   185         head = null;
   186         return in;
   187     }
   189     class ReadMultiStream extends InputStream {
   190         Chunk current;
   191         int offset;
   192         int len;
   193         byte[] buf;
   194         boolean closed;
   196         public ReadMultiStream() {
   197             this.current = head;
   198             len = current.data.size();
   199             buf = current.data.read();
   200         }
   202         @Override
   203         public int read(byte b[], int off, int sz) throws IOException {
   204             if (!fetch()) {
   205                 return -1;
   206             }
   208             sz = Math.min(sz, len-offset);
   209             System.arraycopy(buf,offset,b,off,sz);
   210             offset += sz;
   211             return sz;
   212         }
   214         @Override
   215         public int read() throws IOException {
   216             if (!fetch()) {
   217                 return -1;
   218             }
   219             return (buf[offset++] & 0xff);
   220         }
   222         void adjustInMemoryUsage() {
   223             // Nothing to do in this case.
   224         }
   226         /**
   227          * Gets to the next chunk if we are done with the current one.
   228          * @return true if any data available
   229          * @throws IOException when i/o error
   230          */
   231         private boolean fetch() throws IOException {
   232             if (closed) {
   233                 throw new IOException("Stream already closed");
   234             }
   235             if (current == null) {
   236                 return false;
   237             }
   239             while(offset==len) {
   240                 while(!part.parsed && current.next == null) {
   241                     part.msg.makeProgress();
   242                 }
   243                 current = current.next;
   245                 if (current == null) {
   246                     return false;
   247                 }
   248                 adjustInMemoryUsage();
   249                 this.offset = 0;
   250                 this.buf = current.data.read();
   251                 this.len = current.data.size();
   252             }
   253             return true;
   254         }
   256         @Override
   257         public void close() throws IOException {
   258             super.close();
   259             current = null;
   260             closed = true;
   261         }
   262     }
   264     final class ReadOnceStream extends ReadMultiStream {
   266         @Override
   267         void adjustInMemoryUsage() {
   268             synchronized(DataHead.this) {
   269                 inMemory -= current.data.size();    // adjust current memory usage
   270             }
   271         }
   273     }
   276 }

mercurial