001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.nio.codecs; 029 030import java.io.IOException; 031import java.nio.ByteBuffer; 032import java.nio.channels.FileChannel; 033import java.nio.channels.WritableByteChannel; 034 035import org.apache.http.impl.io.HttpTransportMetricsImpl; 036import org.apache.http.nio.FileContentEncoder; 037import org.apache.http.nio.reactor.SessionOutputBuffer; 038 039/** 040 * Content encoder that writes data without any transformation. The end of 041 * the content entity is demarcated by closing the underlying connection 042 * (EOF condition). Entities transferred using this input stream can be of 043 * unlimited length. 044 * <p> 045 * This decoder is optimized to transfer data directly from 046 * a {@link FileChannel} to the underlying I/O session's channel whenever 047 * possible avoiding intermediate buffering in the session buffer. 048 * 049 * @since 4.0 050 */ 051public class IdentityEncoder extends AbstractContentEncoder 052 implements FileContentEncoder { 053 054 private final int fragHint; 055 056 /** 057 * @since 4.3 058 * 059 * @param channel underlying channel. 060 * @param buffer session buffer. 061 * @param metrics transport metrics. 062 * @param fragementSizeHint fragment size hint defining an minimal size of a fragment 063 * that should be written out directly to the channel bypassing the session buffer. 064 * Value {@code 0} disables fragment buffering. 065 */ 066 public IdentityEncoder( 067 final WritableByteChannel channel, 068 final SessionOutputBuffer buffer, 069 final HttpTransportMetricsImpl metrics, 070 final int fragementSizeHint) { 071 super(channel, buffer, metrics); 072 this.fragHint = fragementSizeHint > 0 ? fragementSizeHint : 0; 073 } 074 075 public IdentityEncoder( 076 final WritableByteChannel channel, 077 final SessionOutputBuffer buffer, 078 final HttpTransportMetricsImpl metrics) { 079 this(channel, buffer, metrics, 0); 080 } 081 082 @Override 083 public int write(final ByteBuffer src) throws IOException { 084 if (src == null) { 085 return 0; 086 } 087 assertNotCompleted(); 088 089 int total = 0; 090 while (src.hasRemaining()) { 091 if (this.buffer.hasData() || this.fragHint > 0) { 092 if (src.remaining() <= this.fragHint) { 093 final int capacity = this.fragHint - this.buffer.length(); 094 if (capacity > 0) { 095 final int limit = Math.min(capacity, src.remaining()); 096 final int bytesWritten = writeToBuffer(src, limit); 097 total += bytesWritten; 098 } 099 } 100 } 101 if (this.buffer.hasData()) { 102 if (this.buffer.length() >= this.fragHint || src.hasRemaining()) { 103 final int bytesWritten = flushToChannel(); 104 if (bytesWritten == 0) { 105 break; 106 } 107 } 108 } 109 if (!this.buffer.hasData() && src.remaining() > this.fragHint) { 110 final int bytesWritten = writeToChannel(src); 111 total += bytesWritten; 112 if (bytesWritten == 0) { 113 break; 114 } 115 } 116 } 117 return total; 118 } 119 120 @Override 121 public long transfer( 122 final FileChannel src, 123 final long position, 124 final long count) throws IOException { 125 126 if (src == null) { 127 return 0; 128 } 129 assertNotCompleted(); 130 131 flushToChannel(); 132 if (this.buffer.hasData()) { 133 return 0; 134 } 135 136 final long bytesWritten = src.transferTo(position, count, this.channel); 137 if (bytesWritten > 0) { 138 this.metrics.incrementBytesTransferred(bytesWritten); 139 } 140 return bytesWritten; 141 } 142 143 @Override 144 public String toString() { 145 final StringBuilder sb = new StringBuilder(); 146 sb.append("[identity; completed: "); 147 sb.append(isCompleted()); 148 sb.append("]"); 149 return sb.toString(); 150 } 151 152}