Tue, 06 Mar 2012 16:09:35 -0800
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 }