View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.client.api.ContentProvider;
34  import org.eclipse.jetty.util.log.Log;
35  import org.eclipse.jetty.util.log.Logger;
36  
37  /**
38   * A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.
39   * <p>
40   * It is possible to specify, at the constructor, a buffer size used to read content from the
41   * stream, by default 4096 bytes.
42   */
43  public class PathContentProvider extends AbstractTypedContentProvider
44  {
45      private static final Logger LOG = Log.getLogger(PathContentProvider.class);
46  
47      private final Path filePath;
48      private final long fileSize;
49      private final int bufferSize;
50  
51      public PathContentProvider(Path filePath) throws IOException
52      {
53          this(filePath, 4096);
54      }
55  
56      public PathContentProvider(Path filePath, int bufferSize) throws IOException
57      {
58          this("application/octet-stream", filePath, bufferSize);
59      }
60  
61      public PathContentProvider(String contentType, Path filePath) throws IOException
62      {
63          this(contentType, filePath, 4096);
64      }
65  
66      public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException
67      {
68          super(contentType);
69          if (!Files.isRegularFile(filePath))
70              throw new NoSuchFileException(filePath.toString());
71          if (!Files.isReadable(filePath))
72              throw new AccessDeniedException(filePath.toString());
73          this.filePath = filePath;
74          this.fileSize = Files.size(filePath);
75          this.bufferSize = bufferSize;
76      }
77  
78      @Override
79      public long getLength()
80      {
81          return fileSize;
82      }
83  
84      @Override
85      public Iterator<ByteBuffer> iterator()
86      {
87          return new PathIterator();
88      }
89  
90      private class PathIterator implements Iterator<ByteBuffer>, Closeable
91      {
92          private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
93          private SeekableByteChannel channel;
94          private long position;
95  
96          @Override
97          public boolean hasNext()
98          {
99              return position < getLength();
100         }
101 
102         @Override
103         public ByteBuffer next()
104         {
105             try
106             {
107                 if (channel == null)
108                 {
109                     channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
110                     if (LOG.isDebugEnabled())
111                         LOG.debug("Opened file {}", filePath);
112                 }
113 
114                 buffer.clear();
115                 int read = channel.read(buffer);
116                 if (read < 0)
117                     throw new NoSuchElementException();
118 
119                 if (LOG.isDebugEnabled())
120                     LOG.debug("Read {} bytes from {}", read, filePath);
121 
122                 position += read;
123 
124                 if (!hasNext())
125                     close();
126 
127                 buffer.flip();
128                 return buffer;
129             }
130             catch (NoSuchElementException x)
131             {
132                 close();
133                 throw x;
134             }
135             catch (Throwable x)
136             {
137                 close();
138                 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
139             }
140         }
141 
142         @Override
143         public void remove()
144         {
145             throw new UnsupportedOperationException();
146         }
147 
148         @Override
149         public void close()
150         {
151             try
152             {
153                 if (channel != null)
154                     channel.close();
155             }
156             catch (Throwable x)
157             {
158                 LOG.ignore(x);
159             }
160         }
161     }
162 }