View Javadoc

1   /*
2    * Copyright 2009 Red Hat, Inc.
3    *
4    * Red Hat licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the
6    * License.  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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.handler.codec.http2;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.FileChannel;
25  import java.nio.charset.Charset;
26  
27  import org.jboss.netty.buffer.ChannelBuffer;
28  import org.jboss.netty.buffer.ChannelBuffers;
29  
30  /**
31   * Abstract Disk HttpData implementation
32   *
33   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
34   * @author Andy Taylor (andy.taylor@jboss.org)
35   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
36   * @author <a href="http://openr66.free.fr/">Frederic Bregier</a>
37   *
38   */
39  public abstract class AbstractDiskHttpData extends AbstractHttpData implements HttpData {
40  
41      protected File file = null;
42  
43      private boolean isRenamed = false;
44  
45      private FileChannel fileChannel = null;
46  
47      public AbstractDiskHttpData(String name, Charset charset, long size)
48              throws NullPointerException, IllegalArgumentException {
49          super(name, charset, size);
50      }
51  
52      /**
53       *
54       * @return the real DiskFilename (basename)
55       */
56      protected abstract String getDiskFilename();
57      /**
58       *
59       * @return the default prefix
60       */
61      protected abstract String getPrefix();
62      /**
63       *
64       * @return the default base Directory
65       */
66      protected abstract String getBaseDirectory();
67      /**
68       *
69       * @return the default postfix
70       */
71      protected abstract String getPostfix();
72      /**
73       *
74       * @return True if the file should be deleted on Exit by default
75       */
76      protected abstract boolean deleteOnExit();
77  
78      /**
79       *
80       * @return a new Temp File from getDiskFilename(), default prefix, postfix and baseDirectory
81       * @throws IOException
82       */
83      private File tempFile() throws IOException {
84          String newpostfix = null;
85          String diskFilename = getDiskFilename();
86          if (diskFilename != null) {
87              newpostfix = "_" + diskFilename;
88          } else {
89              newpostfix = getPostfix();
90          }
91          File tmpFile;
92          if (getBaseDirectory() == null) {
93              // create a temporary file
94              tmpFile = File.createTempFile(getPrefix(), newpostfix);
95          } else {
96              tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
97                      getBaseDirectory()));
98          }
99          if (deleteOnExit()) {
100             tmpFile.deleteOnExit();
101         }
102         return tmpFile;
103     }
104 
105     public void setContent(ChannelBuffer buffer) throws IOException {
106         if (buffer == null) {
107             throw new NullPointerException("buffer");
108         }
109         size = buffer.readableBytes();
110         if (definedSize > 0 && definedSize < size) {
111             throw new IOException("Out of size: " + size + " > " + definedSize);
112         }
113         if (file == null) {
114             file = tempFile();
115         }
116         if (buffer.readableBytes() == 0) {
117             // empty file
118             file.createNewFile();
119             return;
120         }
121         FileOutputStream outputStream = new FileOutputStream(file);
122         FileChannel localfileChannel = outputStream.getChannel();
123         ByteBuffer byteBuffer = buffer.toByteBuffer();
124         int written = 0;
125         while (written < size) {
126             written += localfileChannel.write(byteBuffer);
127         }
128         buffer.readerIndex(buffer.readerIndex() + written);
129         localfileChannel.force(false);
130         localfileChannel.close();
131         completed = true;
132     }
133 
134     public void addContent(ChannelBuffer buffer, boolean last)
135             throws IOException {
136         if (buffer != null) {
137             int localsize = buffer.readableBytes();
138             if (definedSize > 0 && definedSize < size + localsize) {
139                 throw new IOException("Out of size: " + (size + localsize) +
140                         " > " + definedSize);
141             }
142             ByteBuffer byteBuffer = buffer.toByteBuffer();
143             int written = 0;
144             if (file == null) {
145                 file = tempFile();
146             }
147             if (fileChannel == null) {
148                 FileOutputStream outputStream = new FileOutputStream(file);
149                 fileChannel = outputStream.getChannel();
150             }
151             while (written < localsize) {
152                 written += fileChannel.write(byteBuffer);
153             }
154             size += localsize;
155             buffer.readerIndex(buffer.readerIndex() + written);
156         }
157         if (last) {
158             if (file == null) {
159                 file = tempFile();
160             }
161             if (fileChannel == null) {
162                 FileOutputStream outputStream = new FileOutputStream(file);
163                 fileChannel = outputStream.getChannel();
164             }
165             fileChannel.force(false);
166             fileChannel.close();
167             fileChannel = null;
168             completed = true;
169         } else {
170             if (buffer == null) {
171                 throw new NullPointerException("buffer");
172             }
173         }
174     }
175 
176     public void setContent(File file) throws IOException {
177         if (this.file != null) {
178             delete();
179         }
180         this.file = file;
181         size = file.length();
182         isRenamed = true;
183         completed = true;
184     }
185 
186     public void setContent(InputStream inputStream) throws IOException {
187         if (inputStream == null) {
188             throw new NullPointerException("inputStream");
189         }
190         if (this.file != null) {
191             delete();
192         }
193         file = tempFile();
194         FileOutputStream outputStream = new FileOutputStream(file);
195         FileChannel localfileChannel = outputStream.getChannel();
196         byte[] bytes = new byte[4096*4];
197         ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
198         int read = inputStream.read(bytes);
199         int written = 0;
200         while (read > 0) {
201             byteBuffer.position(read).flip();
202             written += localfileChannel.write(byteBuffer);
203             read = inputStream.read(bytes);
204         }
205         localfileChannel.force(false);
206         localfileChannel.close();
207         size = written;
208         if (definedSize > 0 && definedSize < size) {
209             file.delete();
210             file = null;
211             throw new IOException("Out of size: " + size + " > " + definedSize);
212         }
213         isRenamed = true;
214         completed = true;
215     }
216 
217     public void delete() {
218         if (! isRenamed) {
219             if (file != null) {
220                 file.delete();
221             }
222         }
223     }
224 
225     public byte[] get() throws IOException {
226         if (file == null) {
227             return new byte[0];
228         }
229         return readFrom(file);
230     }
231 
232     public ChannelBuffer getChannelBuffer() throws IOException {
233         if (file == null) {
234             return ChannelBuffers.EMPTY_BUFFER;
235         }
236         byte[] array = readFrom(file);
237         return ChannelBuffers.wrappedBuffer(array);
238     }
239 
240     public ChannelBuffer getChunk(int length) throws IOException {
241         if (file == null || length == 0) {
242             return ChannelBuffers.EMPTY_BUFFER;
243         }
244         if (fileChannel == null) {
245             FileInputStream  inputStream = new FileInputStream(file);
246             fileChannel = inputStream.getChannel();
247         }
248         int read = 0;
249         ByteBuffer byteBuffer = ByteBuffer.allocate(length);
250         while (read < length) {
251             int readnow = fileChannel.read(byteBuffer);
252             if (readnow == -1) {
253                 fileChannel.close();
254                 fileChannel = null;
255                 break;
256             } else {
257                 read += readnow;
258             }
259         }
260         if (read == 0) {
261             return ChannelBuffers.EMPTY_BUFFER;
262         }
263         byteBuffer.flip();
264         ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(byteBuffer);
265         buffer.readerIndex(0);
266         buffer.writerIndex(read);
267         return buffer;
268     }
269 
270     public String getString() throws IOException {
271         return getString(HttpCodecUtil.DEFAULT_CHARSET);
272     }
273 
274     public String getString(Charset encoding) throws IOException {
275         if (file == null) {
276             return "";
277         }
278         if (encoding == null) {
279             byte[] array = readFrom(file);
280             return new String(array, HttpCodecUtil.DEFAULT_CHARSET);
281         }
282         byte[] array = readFrom(file);
283         return new String(array, encoding);
284     }
285 
286     public boolean isInMemory() {
287         return false;
288     }
289 
290     public boolean renameTo(File dest) throws IOException {
291         if (dest == null) {
292             throw new NullPointerException("dest");
293         }
294         if (!file.renameTo(dest)) {
295             // must copy
296             FileInputStream inputStream = new FileInputStream(file);
297             FileOutputStream outputStream = new FileOutputStream(dest);
298             FileChannel in = inputStream.getChannel();
299             FileChannel out = outputStream.getChannel();
300             long destsize = in.transferTo(0, size, out);
301             in.close();
302             out.close();
303             if (destsize == size) {
304                 file.delete();
305                 file = dest;
306                 isRenamed = true;
307                 return true;
308             } else {
309                 dest.delete();
310                 return false;
311             }
312         }
313         file = dest;
314         isRenamed = true;
315         return true;
316     }
317 
318     /**
319      * Utility function
320      * @param src
321      * @return the array of bytes
322      * @throws IOException
323      */
324     private byte[] readFrom(File src) throws IOException {
325         long srcsize = src.length();
326         if (srcsize > Integer.MAX_VALUE) {
327             throw new IllegalArgumentException(
328                     "File too big to be loaded in memory");
329         }
330         FileInputStream inputStream = new FileInputStream(src);
331         FileChannel fileChannel = inputStream.getChannel();
332         byte[] array = new byte[(int) srcsize];
333         ByteBuffer byteBuffer = ByteBuffer.wrap(array);
334         int read = 0;
335         while (read < srcsize) {
336             read += fileChannel.read(byteBuffer);
337         }
338         fileChannel.close();
339         return array;
340     }
341 
342     public File getFile() throws IOException {
343         return file;
344     }
345 
346 }