Wed, 27 Apr 2016 01:27:09 +0800
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 }