View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.client.util;
20  
21  import java.io.Closeable;
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.SeekableByteChannel;
25  import java.nio.file.AccessDeniedException;
26  import java.nio.file.Files;
27  import java.nio.file.NoSuchFileException;
28  import java.nio.file.Path;
29  import java.nio.file.StandardOpenOption;
30  import java.util.Iterator;
31  import java.util.NoSuchElementException;
32  
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  
36  /**
37   * A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.
38   * <p />
39   * It is possible to specify, at the constructor, a buffer size used to read content from the
40   * stream, by default 4096 bytes.
41   */
42  public class PathContentProvider extends AbstractTypedContentProvider
43  {
44      private static final Logger LOG = Log.getLogger(PathContentProvider.class);
45  
46      private final Path filePath;
47      private final long fileSize;
48      private final int bufferSize;
49  
50      public PathContentProvider(Path filePath) throws IOException
51      {
52          this(filePath, 4096);
53      }
54  
55      public PathContentProvider(Path filePath, int bufferSize) throws IOException
56      {
57          this("application/octet-stream", filePath, bufferSize);
58      }
59  
60      public PathContentProvider(String contentType, Path filePath) throws IOException
61      {
62          this(contentType, filePath, 4096);
63      }
64  
65      public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException
66      {
67          super(contentType);
68          if (!Files.isRegularFile(filePath))
69              throw new NoSuchFileException(filePath.toString());
70          if (!Files.isReadable(filePath))
71              throw new AccessDeniedException(filePath.toString());
72          this.filePath = filePath;
73          this.fileSize = Files.size(filePath);
74          this.bufferSize = bufferSize;
75      }
76  
77      @Override
78      public long getLength()
79      {
80          return fileSize;
81      }
82  
83      @Override
84      public Iterator<ByteBuffer> iterator()
85      {
86          return new PathIterator();
87      }
88  
89      private class PathIterator implements Iterator<ByteBuffer>, Closeable
90      {
91          private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
92          private SeekableByteChannel channel;
93          private long position;
94  
95          @Override
96          public boolean hasNext()
97          {
98              return position < getLength();
99          }
100 
101         @Override
102         public ByteBuffer next()
103         {
104             try
105             {
106                 if (channel == null)
107                 {
108                     channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
109                     if (LOG.isDebugEnabled())
110                         LOG.debug("Opened file {}", filePath);
111                 }
112 
113                 buffer.clear();
114                 int read = channel.read(buffer);
115                 if (read < 0)
116                     throw new NoSuchElementException();
117 
118                 if (LOG.isDebugEnabled())
119                     LOG.debug("Read {} bytes from {}", read, filePath);
120 
121                 position += read;
122 
123                 if (!hasNext())
124                     close();
125 
126                 buffer.flip();
127                 return buffer;
128             }
129             catch (NoSuchElementException x)
130             {
131                 close();
132                 throw x;
133             }
134             catch (Exception x)
135             {
136                 close();
137                 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
138             }
139         }
140 
141         @Override
142         public void remove()
143         {
144             throw new UnsupportedOperationException();
145         }
146 
147         @Override
148         public void close()
149         {
150             try
151             {
152                 if (channel != null)
153                     channel.close();
154             }
155             catch (Exception x)
156             {
157                 LOG.ignore(x);
158             }
159         }
160     }
161 }