1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.impl.nio.codecs;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.channels.FileChannel;
33 import java.nio.channels.ReadableByteChannel;
34
35 import org.apache.http.annotation.NotThreadSafe;
36 import org.apache.http.impl.io.HttpTransportMetricsImpl;
37 import org.apache.http.nio.FileContentDecoder;
38 import org.apache.http.nio.reactor.SessionInputBuffer;
39
40 /**
41 * Content decoder that reads data without any transformation. The end of the
42 * content entity is delineated by closing the underlying connection
43 * (EOF condition). Entities transferred using this input stream can be of
44 * unlimited length.
45 * <p>
46 * This decoder is optimized to transfer data directly from the underlying
47 * I/O session's channel to a {@link FileChannel}, whenever
48 * possible avoiding intermediate buffering in the session buffer.
49 *
50 * @since 4.0
51 */
52 @NotThreadSafe
53 public class IdentityDecoder extends AbstractContentDecoder
54 implements FileContentDecoder {
55
56 public IdentityDecoder(
57 final ReadableByteChannel channel,
58 final SessionInputBuffer buffer,
59 final HttpTransportMetricsImpl metrics) {
60 super(channel, buffer, metrics);
61 }
62
63 /**
64 * Sets the completed status of this decoder. Normally this is not necessary
65 * (the decoder will automatically complete when the underlying channel
66 * returns EOF). It is useful to mark the decoder as completed if you have
67 * some other means to know all the necessary data has been read and want to
68 * reuse the underlying connection for more messages.
69 */
70 public void setCompleted(boolean completed) {
71 this.completed = completed;
72 }
73
74 public int read(final ByteBuffer dst) throws IOException {
75 if (dst == null) {
76 throw new IllegalArgumentException("Byte buffer may not be null");
77 }
78 if (this.completed) {
79 return -1;
80 }
81
82 int bytesRead;
83 if (this.buffer.hasData()) {
84 bytesRead = this.buffer.read(dst);
85 } else {
86 bytesRead = this.channel.read(dst);
87 if (bytesRead > 0) {
88 this.metrics.incrementBytesTransferred(bytesRead);
89 }
90 }
91 if (bytesRead == -1) {
92 this.completed = true;
93 }
94 return bytesRead;
95 }
96
97 public long transfer(
98 final FileChannel dst,
99 long position,
100 long count) throws IOException {
101
102 if (dst == null) {
103 return 0;
104 }
105 if (this.completed) {
106 return 0;
107 }
108
109 long bytesRead;
110 if (this.buffer.hasData()) {
111 dst.position(position);
112 bytesRead = this.buffer.read(dst);
113 } else {
114 if (this.channel.isOpen()) {
115 if (position > dst.size()) {
116 throw new IOException("Position past end of file [" + position +
117 " > " + dst.size() + "]");
118 }
119 bytesRead = dst.transferFrom(this.channel, position, count);
120 if (count > 0 && bytesRead == 0) {
121 bytesRead = this.buffer.fill(this.channel);
122 }
123 } else {
124 bytesRead = -1;
125 }
126 if (bytesRead > 0) {
127 this.metrics.incrementBytesTransferred(bytesRead);
128 }
129 }
130 if (bytesRead == -1) {
131 this.completed = true;
132 }
133 return bytesRead;
134 }
135
136 @Override
137 public String toString() {
138 StringBuilder buffer = new StringBuilder();
139 buffer.append("[identity; completed: ");
140 buffer.append(this.completed);
141 buffer.append("]");
142 return buffer.toString();
143 }
144
145 }