View Javadoc

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.WritableByteChannel;
33  
34  import org.apache.http.annotation.NotThreadSafe;
35  import org.apache.http.impl.io.HttpTransportMetricsImpl;
36  import org.apache.http.nio.ContentEncoder;
37  import org.apache.http.nio.reactor.SessionOutputBuffer;
38  import org.apache.http.util.Args;
39  import org.apache.http.util.Asserts;
40  
41  /**
42   * Abstract {@link ContentEncoder} that serves as a base for all content
43   * encoder implementations.
44   *
45   * @since 4.0
46   */
47  @NotThreadSafe
48  public abstract class AbstractContentEncoder implements ContentEncoder {
49  
50      protected final WritableByteChannel channel;
51      protected final SessionOutputBuffer buffer;
52      protected final HttpTransportMetricsImpl metrics;
53  
54      /**
55       * TODO: make private
56       */
57      protected boolean completed;
58  
59      /**
60       * Creates an instance of this class.
61       *
62       * @param channel the destination channel.
63       * @param buffer the session output buffer that can be used to store
64       *    session data for intermediate processing.
65       * @param metrics Transport metrics of the underlying HTTP transport.
66       */
67      public AbstractContentEncoder(
68              final WritableByteChannel channel,
69              final SessionOutputBuffer buffer,
70              final HttpTransportMetricsImpl metrics) {
71          super();
72          Args.notNull(channel, "Channel");
73          Args.notNull(buffer, "Session input buffer");
74          Args.notNull(metrics, "Transport metrics");
75          this.buffer = buffer;
76          this.channel = channel;
77          this.metrics = metrics;
78      }
79  
80      public boolean isCompleted() {
81          return this.completed;
82      }
83  
84      public void complete() throws IOException {
85          this.completed = true;
86      }
87  
88      protected void assertNotCompleted() {
89          Asserts.check(!this.completed, "Encoding process already completed");
90      }
91  
92      /**
93       * Flushes content of the session buffer to the channel and updates transport metrics.
94       *
95       * @return number of bytes written to the channel.
96       *
97       * @since 4.3
98       */
99      protected int flushToChannel() throws IOException {
100         if (!this.buffer.hasData()) {
101             return 0;
102         }
103         final int bytesWritten = this.buffer.flush(this.channel);
104         if (bytesWritten > 0) {
105             this.metrics.incrementBytesTransferred(bytesWritten);
106         }
107         return bytesWritten;
108     }
109 
110     /**
111      * Flushes content of the given buffer to the channel and updates transport metrics.
112      *
113      * @return number of bytes written to the channel.
114      *
115      * @since 4.3
116      */
117     protected int writeToChannel(final ByteBuffer src) throws IOException {
118         if (!src.hasRemaining()) {
119             return 0;
120         }
121         final int bytesWritten = this.channel.write(src);
122         if (bytesWritten > 0) {
123             this.metrics.incrementBytesTransferred(bytesWritten);
124         }
125         return bytesWritten;
126     }
127 
128     /**
129      * Transfers content of the source to the channel and updates transport metrics.
130      *
131      * @param src source.
132      * @param limit max number of bytes to transfer.
133      * @return number of bytes transferred.
134      *
135      * @since 4.3
136      */
137     protected int writeToChannel(final ByteBuffer src, final int limit) throws IOException {
138         return doWriteChunk(src, limit, true);
139     }
140 
141     /**
142      * Transfers content of the source to the buffer and updates transport metrics.
143      *
144      * @param src source.
145      * @param limit max number of bytes to transfer.
146      * @return number of bytes transferred.
147      *
148      * @since 4.3
149      */
150     protected int writeToBuffer(final ByteBuffer src, final int limit) throws IOException {
151         return doWriteChunk(src, limit, false);
152     }
153 
154     private int doWriteChunk(
155         final ByteBuffer src, final int chunk, final boolean direct) throws IOException {
156         final int bytesWritten;
157         if (src.remaining() > chunk) {
158             final int oldLimit = src.limit();
159             final int newLimit = oldLimit - (src.remaining() - chunk);
160             src.limit(newLimit);
161             bytesWritten = doWriteChunk(src, direct);
162             src.limit(oldLimit);
163         } else {
164             bytesWritten = doWriteChunk(src, direct);
165         }
166         return bytesWritten;
167     }
168 
169     private int doWriteChunk(final ByteBuffer src, final boolean direct) throws IOException {
170         if (direct) {
171             final int bytesWritten = this.channel.write(src);
172             if (bytesWritten > 0) {
173                 this.metrics.incrementBytesTransferred(bytesWritten);
174             }
175             return bytesWritten;
176         } else {
177             final int chunk = src.remaining();
178             this.buffer.write(src);
179             return chunk;
180         }
181     }
182 
183 }