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.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 implements ContentProvider
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          if (!Files.isRegularFile(filePath))
59              throw new NoSuchFileException(filePath.toString());
60          if (!Files.isReadable(filePath))
61              throw new AccessDeniedException(filePath.toString());
62          this.filePath = filePath;
63          this.fileSize = Files.size(filePath);
64          this.bufferSize = bufferSize;
65      }
66  
67      @Override
68      public long getLength()
69      {
70          return fileSize;
71      }
72  
73      @Override
74      public Iterator<ByteBuffer> iterator()
75      {
76          return new PathIterator();
77      }
78  
79      private class PathIterator implements Iterator<ByteBuffer>, Closeable
80      {
81          private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
82          private SeekableByteChannel channel;
83          private long position;
84  
85          @Override
86          public boolean hasNext()
87          {
88              return position < getLength();
89          }
90  
91          @Override
92          public ByteBuffer next()
93          {
94              try
95              {
96                  if (channel == null)
97                  {
98                      channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
99                      LOG.debug("Opened file {}", filePath);
100                 }
101 
102                 buffer.clear();
103                 int read = channel.read(buffer);
104                 if (read < 0)
105                     throw new NoSuchElementException();
106 
107                 if (LOG.isDebugEnabled())
108                     LOG.debug("Read {} bytes from {}", read, filePath);
109 
110                 position += read;
111 
112                 if (!hasNext())
113                     close();
114 
115                 buffer.flip();
116                 return buffer;
117             }
118             catch (NoSuchElementException x)
119             {
120                 close();
121                 throw x;
122             }
123             catch (Exception x)
124             {
125                 close();
126                 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
127             }
128         }
129 
130         @Override
131         public void remove()
132         {
133             throw new UnsupportedOperationException();
134         }
135 
136         @Override
137         public void close()
138         {
139             try
140             {
141                 if (channel != null)
142                     channel.close();
143             }
144             catch (Exception x)
145             {
146                 LOG.ignore(x);
147             }
148         }
149     }
150 }