KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > axis > attachments > DimeBodyPart


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 /**
18  * @author Rick Rineholt
19  */

20
21 package org.apache.axis.attachments;
22
23
24 import org.apache.axis.components.logger.LogFactory;
25 import org.apache.axis.utils.Messages;
26 import org.apache.commons.logging.Log;
27
28 import javax.activation.DataHandler JavaDoc;
29 import javax.activation.DataSource JavaDoc;
30 import java.io.BufferedInputStream JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33
34
35 /**
36  * This class is a single part for DIME mulitpart message.
37 <pre>
38  DIME 1.0 format
39  0 1 2 3
40  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
41  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42  | VERSION |B|E|C| TYPE_T| OPT_T | OPTIONS_LENGTH |
43  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44  | ID_LENGTH | TYPE_LENGTH |
45  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46  | DATA_LENGTH |
47  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48  | /
49  / OPTIONS + PADDING /
50  / |
51  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52  | /
53  / ID + PADDING /
54  / |
55  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56  | /
57  / TYPE + PADDING /
58  / |
59  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60  | /
61  / DATA + PADDING /
62  / |
63  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64  </pre>
65  */

66
67 /**
68  * Holds one attachment DIME part.
69  */

70 public class DimeBodyPart {
71     protected static Log log =
72         LogFactory.getLog(DimeBodyPart.class.getName());
73
74     protected Object JavaDoc data = null;
75     protected DimeTypeNameFormat dtnf = null;
76     protected byte[] type = null;
77     protected byte[] id = null;
78     static final byte POSITION_FIRST = (byte) 0x04;
79     static final byte POSITION_LAST = (byte) 0x02;
80     private static final byte CHUNK = 0x01; //Means set the chunk bit
81
private static final byte CHUNK_NEXT = 0x2; //Means this was part of a CHUNK
82
private static int MAX_TYPE_LENGTH = (1 << 16) - 1;
83     private static int MAX_ID_LENGTH = (1 << 16) - 1;
84
85     static final long MAX_DWORD = 0xffffffffL;
86
87     // fixme: don't use? is this for inheritance only? I can't find any
88
// classes that extend this
89
protected DimeBodyPart() {} //do not use.
90

91     /**
92      * Create a DIME Attachment Part.
93      * @param data a byte array containing the data as the attachment.
94      * @param format the type format for the data.
95      * @param type the type of the data
96      * @param id the ID for the DIME part.
97      *
98      */

99     public DimeBodyPart(byte[] data, DimeTypeNameFormat format,
100       String JavaDoc type, String JavaDoc id) {
101         System.arraycopy(data, 0, this.data = new byte[ data.length], 0, data.length);
102         this.dtnf = format;
103         this.type = type.getBytes();
104         if (this.type.length > MAX_TYPE_LENGTH)
105             throw new IllegalArgumentException JavaDoc(Messages.getMessage
106                     ("attach.dimetypeexceedsmax",
107                     "" + this.type.length, "" + MAX_TYPE_LENGTH));
108         this.id = id.getBytes();
109         if (this.id.length > MAX_ID_LENGTH)
110             throw new IllegalArgumentException JavaDoc(
111                     Messages.getMessage("attach.dimelengthexceedsmax", "" + this.id.length,
112                         "" + MAX_ID_LENGTH));
113     }
114
115     /**
116      * Create a DIME Attachment Part.
117      * @param dh the data for the attachment as a JAF datahadler.
118      * @param format the type format for the data.
119      * @param type the type of the data
120      * @param id the ID for the DIME part.
121      *
122      */

123     public DimeBodyPart(DataHandler JavaDoc dh,
124     DimeTypeNameFormat format, String JavaDoc type, String JavaDoc id) {
125         this.data = dh;
126         this.dtnf = format;
127         if (type == null || type.length() == 0)
128             type = "application/octet-stream";
129         this.type = type.getBytes();
130         if (this.type.length > MAX_TYPE_LENGTH)
131             throw new IllegalArgumentException JavaDoc(Messages.getMessage(
132                         "attach.dimetypeexceedsmax",
133                         "" + this.type.length, "" + MAX_TYPE_LENGTH));
134         this.id = id.getBytes();
135         if (this.id.length > MAX_ID_LENGTH)
136             throw new IllegalArgumentException JavaDoc(Messages.getMessage(
137             "attach.dimelengthexceedsmax",
138             "" + this.id.length, "" + MAX_ID_LENGTH));
139     }
140
141     /**
142      * Create a DIME Attachment Part.
143      * @param dh the data for the attachment as a JAF datahadler.
144      * The type and foramt is derived from the DataHandler.
145      * @param id the ID for the DIME part.
146      *
147      */

148     public DimeBodyPart(DataHandler JavaDoc dh, String JavaDoc id) {
149         this(dh, DimeTypeNameFormat.MIME, dh.getContentType(), id);
150
151         String JavaDoc ct = dh.getContentType();
152
153         if (ct != null) {
154             ct = ct.trim();
155             if (ct.toLowerCase().startsWith("application/uri")) {
156                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(ct, " \t;");
157                 String JavaDoc t = st.nextToken(" \t;");
158
159                 if (t.equalsIgnoreCase("application/uri")) {
160                     for (; st.hasMoreTokens();) {
161                         t = st.nextToken(" \t;");
162                         if (t.equalsIgnoreCase("uri")) {
163                             t = st.nextToken("=");
164                             if (t != null) {
165                                 t = t.trim();
166                                 if (t.startsWith("\"")) t =
167                                   t.substring(1);
168
169                                 if (t.endsWith("\"")) t =
170                                   t.substring(0, t.length() - 1);
171                                 this.type = t.getBytes();
172                                 this.dtnf = DimeTypeNameFormat.URI;
173                             }
174                             return;
175                         } else if (t.equalsIgnoreCase("uri=")) {
176                             t = st.nextToken(" \t;");
177                             if (null != t && t.length() != 0) {
178                                 t = t.trim();
179                                 if (t.startsWith("\"")) t=
180                                   t.substring(1);
181                                 if (t.endsWith("\"")) t=
182                                   t.substring(0, t.length() - 1);
183                                 this.type = t.getBytes();
184                                 this.dtnf = DimeTypeNameFormat.URI;
185                                 return;
186                             }
187                         } else if (t.toLowerCase().startsWith("uri=")) {
188                             if (-1 != t.indexOf('=')) {
189                                 t = t.substring(t.indexOf('=')).trim();
190                                 if (t.length() != 0) {
191                                     t = t.trim();
192                                     if (t.startsWith("\"")) t =
193                                       t.substring(1);
194
195                                     if (t.endsWith("\""))
196                                      t = t.substring(0, t.length() - 1);
197                                     this.type = t.getBytes();
198                                     this.dtnf = DimeTypeNameFormat.URI;
199                                     return;
200
201                                 }
202                             }
203                         }
204                     }
205                 }
206             }
207         }
208     }
209
210     /**
211      * Write to stream the data using maxchunk for the largest junk.
212      *
213      * @param os the <code>OutputStream</code> to write to
214      * @param position the position to write
215      * @param maxchunk the maximum length of any one chunk
216      * @throws IOException if there was a problem writing data to the stream
217      */

218     void write(java.io.OutputStream JavaDoc os, byte position, long maxchunk)
219       throws java.io.IOException JavaDoc {
220         if (maxchunk < 1) throw new IllegalArgumentException JavaDoc(
221                     Messages.getMessage("attach.dimeMaxChunkSize0", "" + maxchunk));
222         if (maxchunk > MAX_DWORD) throw new IllegalArgumentException JavaDoc(
223                     Messages.getMessage("attach.dimeMaxChunkSize1", "" + maxchunk));
224         if (data instanceof byte[]) {
225             send(os, position, (byte[]) data, maxchunk);
226         } else if (data instanceof DynamicContentDataHandler) {
227             send(os, position, (DynamicContentDataHandler) data, maxchunk);
228         } else if (data instanceof DataHandler JavaDoc) {
229             DataSource JavaDoc source = ((DataHandler JavaDoc)data).getDataSource();
230             DynamicContentDataHandler dh2 = new DynamicContentDataHandler(source);
231             send(os, position, dh2, maxchunk);
232         }
233     }
234
235     /**
236      * Write to stream the data using the default largest chunk size.
237      *
238      * @param os the <code>OutputStream</code> to write to
239      * @param position the position to write
240      * @throws IOException if there was a problem writing data to the stream
241      */

242     void write(java.io.OutputStream JavaDoc os, byte position)
243       throws java.io.IOException JavaDoc {
244         write(os, position, MAX_DWORD);
245     }
246
247     private static final byte[] pad = new byte[4];
248
249     void send(java.io.OutputStream JavaDoc os, byte position, byte[] data,
250         final long maxchunk)throws java.io.IOException JavaDoc {
251         send(os, position, data, 0, data.length, maxchunk);
252     }
253
254     void send(java.io.OutputStream JavaDoc os, byte position, byte[] data,
255         int offset, final int length, final long maxchunk)
256         throws java.io.IOException JavaDoc {
257
258         byte chunknext = 0;
259
260         do {
261             int sendlength = (int) Math.min(maxchunk, length - offset);
262
263             sendChunk(os, position, data, offset, sendlength, (byte)
264                 ((sendlength < (length - offset) ? CHUNK : 0)
265                  | chunknext));
266             offset += sendlength;
267             chunknext = CHUNK_NEXT;
268         }
269         while (offset < length);
270     }
271
272     void send(java.io.OutputStream JavaDoc os, byte position, DataHandler JavaDoc dh,
273         final long maxchunk) throws java.io.IOException JavaDoc {
274         java.io.InputStream JavaDoc in = null;
275         try {
276             long dataSize = getDataSize();
277             in = dh.getInputStream();
278             byte[] readbuf = new byte[64 * 1024];
279             int bytesread;
280
281             sendHeader(os, position, dataSize, (byte) 0);
282             long totalsent = 0;
283
284             do {
285                 bytesread = in.read(readbuf);
286                 if (bytesread > 0) {
287                     os.write(readbuf, 0, bytesread);
288                     totalsent += bytesread;
289                 }
290             }
291             while (bytesread > -1);
292             os.write(pad, 0, dimePadding(totalsent));
293         }
294         finally {
295             if (in != null) {
296                 try {
297                   in.close();
298                 }
299                 catch (IOException JavaDoc e) {
300                   // ignore
301
}
302             }
303         }
304     }
305     
306     /**
307      * Special case for dynamically generated content.
308      * maxchunk is currently ignored since the default is 2GB.
309      * The chunk size is retrieved from the DynamicContentDataHandler
310      *
311      * @param os
312      * @param position
313      * @param dh
314      * @param maxchunk
315      * @throws java.io.IOException
316      */

317     void send(java.io.OutputStream JavaDoc os, byte position, DynamicContentDataHandler dh,
318             final long maxchunk)
319             throws java.io.IOException JavaDoc {
320         
321             BufferedInputStream JavaDoc in = new BufferedInputStream JavaDoc(dh.getInputStream());
322             
323             final int myChunkSize = dh.getChunkSize();
324             
325             byte[] buffer1 = new byte[myChunkSize];
326             byte[] buffer2 = new byte[myChunkSize];
327             
328             int bytesRead1 = 0 , bytesRead2 = 0;
329
330             bytesRead1 = in.read(buffer1);
331             
332             if(bytesRead1 < 0) {
333                 sendHeader(os, position, 0, (byte) 0);
334                 os.write(pad, 0, dimePadding(0));
335                 return;
336             }
337             
338             do {
339                 bytesRead2 = in.read(buffer2);
340                 
341                 if(bytesRead2 < 0) {
342                     //last record...do not set the chunk bit.
343
//buffer1 contains the last chunked record!
344
sendChunk(os, position, buffer1, 0, bytesRead1, (byte)0);
345                     break;
346                 }
347                 
348                 sendChunk(os, position, buffer1, 0, bytesRead1, CHUNK);
349
350                 //now that we have written out buffer1, copy buffer2 into to buffer1
351
System.arraycopy(buffer2,0,buffer1,0,myChunkSize);
352                 bytesRead1 = bytesRead2;
353                 
354             }while(bytesRead2 > 0);
355     }
356
357     protected void sendChunk(java.io.OutputStream JavaDoc os,
358     final byte position,
359         byte[] data, byte chunk) throws java.io.IOException JavaDoc {
360
361         sendChunk(os, position, data, 0, data.length, chunk);
362     }
363
364     protected void sendChunk(java.io.OutputStream JavaDoc os,
365         final byte position, byte[] data, int offset, int length,
366         byte chunk) throws java.io.IOException JavaDoc {
367
368         sendHeader(os, position, length, chunk);
369         os.write(data, offset, length);
370         os.write(pad, 0, dimePadding(length));
371     }
372
373     static final byte CURRENT_OPT_T = (byte) 0;
374
375     protected void sendHeader(java.io.OutputStream JavaDoc os,
376     final byte position,
377         long length, byte chunk) throws java.io.IOException JavaDoc {
378         byte[] fixedHeader = new byte[12];
379
380         //VERSION
381
fixedHeader[0] = (byte)((DimeMultiPart.CURRENT_VERSION << 3) & 0xf8);
382
383         // B, E, and C
384
fixedHeader[0] |= (byte) ((position & (byte) 0x6)
385          & ((chunk & CHUNK) != 0 ? ~POSITION_LAST : ~0) &
386                     ((chunk & CHUNK_NEXT) != 0 ? ~POSITION_FIRST : ~0));
387         fixedHeader[0] |= (chunk & CHUNK);
388
389         //TYPE_T
390
if ((chunk & CHUNK_NEXT) == 0) //If this is a follow on chunk dont send id again.
391
fixedHeader[1] = (byte) ((dtnf.toByte() << 4) & 0xf0);
392
393         //OPT_T
394
fixedHeader[1] |= (byte) (CURRENT_OPT_T & 0xf);
395
396         //OPTION_LENGTH
397
fixedHeader[2] = (byte) 0;
398         fixedHeader[3] = (byte) 0;
399
400         //ID_LENGTH
401
if ((chunk & CHUNK_NEXT) == 0) { //If this is a follow on chunk dont send id again.
402
fixedHeader[4] = (byte) ((id.length >>> 8) & 0xff);
403             fixedHeader[5] = (byte) ((id.length) & 0xff);
404         }
405
406         //TYPE_LENGTH
407
if ((chunk & CHUNK_NEXT) == 0) {
408             fixedHeader[6] = (byte) ((type.length >>> 8) & 0xff);
409             fixedHeader[7] = (byte) ((type.length) & 0xff);
410         }
411
412         //DATA_LENGTH
413
fixedHeader[8] = (byte) ((length >>> 24) & 0xff);
414         fixedHeader[9] = (byte) ((length >>> 16) & 0xff);
415         fixedHeader[10] = (byte) ((length >>> 8) & 0xff);
416         fixedHeader[11] = (byte) (length & 0xff);
417
418         os.write(fixedHeader);
419
420         //OPTIONS + PADDING
421
// (NONE)
422

423         //ID + PADDING
424
if ((chunk & CHUNK_NEXT) == 0) {
425             os.write(id);
426             os.write(pad, 0, dimePadding(id.length));
427         }
428
429         //TYPE + PADDING
430
if ((chunk & CHUNK_NEXT) == 0) {
431             os.write(type);
432             os.write(pad, 0, dimePadding(type.length));
433         }
434     }
435
436     static final int dimePadding(long l) {
437         return (int) ((4L - (l & 0x3L)) & 0x03L);
438     }
439
440     long getTransmissionSize(long chunkSize) {
441         long size = 0;
442         size += id.length;
443         size += dimePadding(id.length);
444         size += type.length;
445         size += dimePadding(type.length);
446         //no options.
447
long dataSize = getDataSize();
448
449         if(0 == dataSize){
450             size+=12; //header size.
451
}else{
452
453             long fullChunks = dataSize / chunkSize;
454             long lastChunkSize = dataSize % chunkSize;
455
456             if (0 != lastChunkSize) size += 12; //12 bytes for fixed header
457
size += 12 * fullChunks; //add additional header size for each chunk.
458
size += fullChunks * dimePadding(chunkSize);
459             size += dimePadding(lastChunkSize);
460             size += dataSize;
461         }
462         return size;
463     }
464
465     long getTransmissionSize() {
466         return getTransmissionSize(MAX_DWORD);
467     }
468
469     protected long getDataSize() {
470         if (data instanceof byte[]) return ((byte[]) (data)).length;
471         if (data instanceof DataHandler JavaDoc)
472           return getDataSize((DataHandler JavaDoc) data);
473         return -1;
474     }
475
476     protected long getDataSize(DataHandler JavaDoc dh) {
477         long dataSize = -1L;
478
479         try {
480             DataSource JavaDoc ds = dh.getDataSource();
481
482             //Do files our selfs since this is costly to read in. Ask the file system.
483
// This is 90% of the use of attachments.
484
if (ds instanceof javax.activation.FileDataSource JavaDoc) {
485                 javax.activation.FileDataSource JavaDoc fdh =
486                     (javax.activation.FileDataSource JavaDoc) ds;
487                 java.io.File JavaDoc df = fdh.getFile();
488
489                 if (!df.exists()) {
490                     throw new RuntimeException JavaDoc(
491                             Messages.getMessage("noFile",
492                                 df.getAbsolutePath()));
493                 }
494                 dataSize = df.length();
495             } else {
496                 dataSize = 0;
497                 java.io.InputStream JavaDoc in = ds.getInputStream();
498                 byte[] readbuf = new byte[64 * 1024];
499                 int bytesread;
500
501                 do {
502                     bytesread = in.read(readbuf);
503                     if (bytesread > 0) dataSize += bytesread;
504                 }
505                 while (bytesread > -1);
506
507                 if (in.markSupported()) {
508                     //Leave the stream open for future reading
509
// and reset the stream pointer to the first byte
510
in.reset();
511                 } else {
512                     //FIXME: bug http://nagoya.apache.org/jira/secure/ViewIssue.jspa?key=AXIS-1126
513
//if we close this then how can we read the file? eh?
514
in.close();
515                 }
516             }
517         } catch (Exception JavaDoc e) {
518             //TODO: why are exceptions swallowed here?
519
log.error(Messages.getMessage("exception00"), e);
520         }
521         return dataSize;
522     }
523 }
524
Popular Tags