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

Tue, 06 Mar 2012 16:09:35 -0800

author
ohair
date
Tue, 06 Mar 2012 16:09:35 -0800
changeset 286
f50545b5e2f1
child 368
0989ad8c0860
permissions
-rw-r--r--

7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom

     1 /*
     2  * Copyright (c) 1997, 2011, 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 attachemnt 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                 InputStream in = readOnce();
    88                 byte[] buf = new byte[8192];
    89                 int len;
    90                 while((len=in.read(buf)) != -1) {
    91                     os.write(buf, 0, len);
    92                 }
    93                 os.close();
    94             } catch(IOException ioe) {
    95                 throw new MIMEParsingException(ioe);
    96             }
    97         }
    98     }
   100     void close() {
   101         if (dataFile != null) {
   102             head = tail = null;
   103             dataFile.close();
   104         }
   105     }
   108     /**
   109      * Can get the attachment part's content multiple times. That means
   110      * the full content needs to be there in memory or on the file system.
   111      * Calling this method would trigger parsing for the part's data. So
   112      * do not call this unless it is required(otherwise, just wrap MIMEPart
   113      * into a object that returns InputStream for e.g DataHandler)
   114      *
   115      * @return data for the part's content
   116      */
   117     public InputStream read() {
   118         if (readOnce) {
   119             throw new IllegalStateException("readOnce() is called before, read() cannot be called later.");
   120         }
   122         // Trigger parsing for the part
   123         while(tail == null) {
   124             if (!part.msg.makeProgress()) {
   125                 throw new IllegalStateException("No such MIME Part: "+part);
   126             }
   127         }
   129         if (head == null) {
   130             throw new IllegalStateException("Already read. Probably readOnce() is called before.");
   131         }
   132         return new ReadMultiStream();
   133     }
   135     /**
   136      * Used for an assertion. Returns true when readOnce() is not already called.
   137      * or otherwise throw an exception.
   138      *
   139      * <p>
   140      * Calling this method also marks the stream as 'consumed'
   141      *
   142      * @return true if readOnce() is not called before
   143      */
   144     private boolean unconsumed() {
   145         if (consumedAt != null) {
   146             AssertionError error = new AssertionError("readOnce() is already called before. See the nested exception from where it's called.");
   147             error.initCause(consumedAt);
   148             throw error;
   149         }
   150         consumedAt = new Exception().fillInStackTrace();
   151         return true;
   152     }
   154     /**
   155      * Can get the attachment part's content only once. The content
   156      * will be lost after the method. Content data is not be stored
   157      * on the file system or is not kept in the memory for the
   158      * following case:
   159      *   - Attachement parts contents are accessed sequentially
   160      *
   161      * In general, take advantage of this when the data is used only
   162      * once.
   163      *
   164      * @return data for the part's content
   165      */
   166     public InputStream readOnce() {
   167         assert unconsumed();
   168         if (readOnce) {
   169             throw new IllegalStateException("readOnce() is called before. It can only be called once.");
   170         }
   171         readOnce = true;
   172         // Trigger parsing for the part
   173         while(tail == null) {
   174             if (!part.msg.makeProgress() && tail == null) {
   175                 throw new IllegalStateException("No such Part: "+part);
   176             }
   177         }
   178         InputStream in = new ReadOnceStream();
   179         head = null;
   180         return in;
   181     }
   183     class ReadMultiStream extends InputStream {
   184         Chunk current;
   185         int offset;
   186         int len;
   187         byte[] buf;
   188         boolean closed;
   190         public ReadMultiStream() {
   191             this.current = head;
   192             len = current.data.size();
   193             buf = current.data.read();
   194         }
   196         @Override
   197         public int read(byte b[], int off, int sz) throws IOException {
   198             if(!fetch())    return -1;
   200             sz = Math.min(sz, len-offset);
   201             System.arraycopy(buf,offset,b,off,sz);
   202             offset += sz;
   203             return sz;
   204         }
   206         public int read() throws IOException {
   207             if (!fetch()) {
   208                 return -1;
   209             }
   210             return (buf[offset++] & 0xff);
   211         }
   213         void adjustInMemoryUsage() {
   214             // Nothing to do in this case.
   215         }
   217         /**
   218          * Gets to the next chunk if we are done with the current one.
   219          * @return true if any data available
   220          * @throws IOException when i/o error
   221          */
   222         private boolean fetch() throws IOException {
   223             if (closed) {
   224                 throw new IOException("Stream already closed");
   225             }
   226             if (current == null) {
   227                 return false;
   228             }
   230             while(offset==len) {
   231                 while(!part.parsed && current.next == null) {
   232                     part.msg.makeProgress();
   233                 }
   234                 current = current.next;
   236                 if (current == null) {
   237                     return false;
   238                 }
   239                 adjustInMemoryUsage();
   240                 this.offset = 0;
   241                 this.buf = current.data.read();
   242                 this.len = current.data.size();
   243             }
   244             return true;
   245         }
   247         public void close() throws IOException {
   248             super.close();
   249             current = null;
   250             closed = true;
   251         }
   252     }
   254     final class ReadOnceStream extends ReadMultiStream {
   256         @Override
   257         void adjustInMemoryUsage() {
   258             synchronized(DataHead.this) {
   259                 inMemory -= current.data.size();    // adjust current memory usage
   260             }
   261         }
   263     }
   266 }

mercurial