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.nio.testserver;
29  
30  import java.io.IOException;
31  import java.net.SocketAddress;
32  import java.nio.ByteBuffer;
33  import java.nio.channels.ByteChannel;
34  import java.nio.channels.SelectionKey;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.http.nio.reactor.IOSession;
38  import org.apache.http.nio.reactor.SessionBufferStatus;
39  
40  /**
41   * Decorator class intended to transparently extend an {@link IOSession}
42   * with basic event logging capabilities using Commons Logging.
43   *
44   */
45  public class LoggingIOSession implements IOSession {
46  
47      private final Log log;
48      private final Wire wireLog;
49      private final String id;
50      private final IOSession session;
51      private final ByteChannel channel;
52  
53      public LoggingIOSession(final IOSession session, final String id, final Log log, final Log wireLog) {
54          super();
55          this.session = session;
56          this.channel = new LoggingByteChannel();
57          this.id = id;
58          this.log = log;
59          this.wireLog = new Wire(wireLog, this.id);
60      }
61  
62      @Override
63      public ByteChannel channel() {
64          return this.channel;
65      }
66  
67      @Override
68      public SocketAddress getLocalAddress() {
69          return this.session.getLocalAddress();
70      }
71  
72      @Override
73      public SocketAddress getRemoteAddress() {
74          return this.session.getRemoteAddress();
75      }
76  
77      @Override
78      public int getEventMask() {
79          return this.session.getEventMask();
80      }
81  
82      private static String formatOps(final int ops) {
83          final StringBuilder buffer = new StringBuilder(6);
84          buffer.append('[');
85          if ((ops & SelectionKey.OP_READ) > 0) {
86              buffer.append('r');
87          }
88          if ((ops & SelectionKey.OP_WRITE) > 0) {
89              buffer.append('w');
90          }
91          if ((ops & SelectionKey.OP_ACCEPT) > 0) {
92              buffer.append('a');
93          }
94          if ((ops & SelectionKey.OP_CONNECT) > 0) {
95              buffer.append('c');
96          }
97          buffer.append(']');
98          return buffer.toString();
99      }
100 
101     @Override
102     public void setEventMask(final int ops) {
103         this.session.setEventMask(ops);
104         if (this.log.isDebugEnabled()) {
105             this.log.debug(this.id + " " + this.session + ": Event mask set " + formatOps(ops));
106         }
107     }
108 
109     @Override
110     public void setEvent(final int op) {
111         this.session.setEvent(op);
112         if (this.log.isDebugEnabled()) {
113             this.log.debug(this.id + " " + this.session + ": Event set " + formatOps(op));
114         }
115     }
116 
117     @Override
118     public void clearEvent(final int op) {
119         this.session.clearEvent(op);
120         if (this.log.isDebugEnabled()) {
121             this.log.debug(this.id + " " + this.session + ": Event cleared " + formatOps(op));
122         }
123     }
124 
125     @Override
126     public void close() {
127         if (this.log.isDebugEnabled()) {
128             this.log.debug(this.id + " " + this.session + ": Close");
129         }
130         this.session.close();
131     }
132 
133     @Override
134     public int getStatus() {
135         return this.session.getStatus();
136     }
137 
138     @Override
139     public boolean isClosed() {
140         return this.session.isClosed();
141     }
142 
143     @Override
144     public void shutdown() {
145         if (this.log.isDebugEnabled()) {
146             this.log.debug(this.id + " " + this.session + ": Shutdown");
147         }
148         this.session.shutdown();
149     }
150 
151     @Override
152     public int getSocketTimeout() {
153         return this.session.getSocketTimeout();
154     }
155 
156     @Override
157     public void setSocketTimeout(final int timeout) {
158         if (this.log.isDebugEnabled()) {
159             this.log.debug(this.id + " " + this.session + ": Set timeout " + timeout);
160         }
161         this.session.setSocketTimeout(timeout);
162     }
163 
164     @Override
165     public void setBufferStatus(final SessionBufferStatus status) {
166         this.session.setBufferStatus(status);
167     }
168 
169     @Override
170     public boolean hasBufferedInput() {
171         return this.session.hasBufferedInput();
172     }
173 
174     @Override
175     public boolean hasBufferedOutput() {
176         return this.session.hasBufferedOutput();
177     }
178 
179     @Override
180     public Object getAttribute(final String name) {
181         return this.session.getAttribute(name);
182     }
183 
184     @Override
185     public void setAttribute(final String name, final Object obj) {
186         if (this.log.isDebugEnabled()) {
187             this.log.debug(this.id + " " + this.session + ": Set attribute " + name);
188         }
189         this.session.setAttribute(name, obj);
190     }
191 
192     @Override
193     public Object removeAttribute(final String name) {
194         if (this.log.isDebugEnabled()) {
195             this.log.debug(this.id + " " + this.session + ": Remove attribute " + name);
196         }
197         return this.session.removeAttribute(name);
198     }
199 
200     @Override
201     public String toString() {
202         return this.id + " " + this.session.toString();
203     }
204 
205     class LoggingByteChannel implements ByteChannel {
206 
207         @Override
208         public int read(final ByteBuffer dst) throws IOException {
209             final int bytesRead = session.channel().read(dst);
210             if (log.isDebugEnabled()) {
211                 log.debug(id + " " + session + ": " + bytesRead + " bytes read");
212             }
213             if (bytesRead > 0 && wireLog.isEnabled()) {
214                 final ByteBuffer b = dst.duplicate();
215                 final int p = b.position();
216                 b.limit(p);
217                 b.position(p - bytesRead);
218                 wireLog.input(b);
219             }
220             return bytesRead;
221         }
222 
223         @Override
224         public int write(final ByteBuffer src) throws IOException {
225             final int byteWritten = session.channel().write(src);
226             if (log.isDebugEnabled()) {
227                 log.debug(id + " " + session + ": " + byteWritten + " bytes written");
228             }
229             if (byteWritten > 0 && wireLog.isEnabled()) {
230                 final ByteBuffer b = src.duplicate();
231                 final int p = b.position();
232                 b.limit(p);
233                 b.position(p - byteWritten);
234                 wireLog.output(b);
235             }
236             return byteWritten;
237         }
238 
239         @Override
240         public void close() throws IOException {
241             if (log.isDebugEnabled()) {
242                 log.debug(id + " " + session + ": Channel close");
243             }
244             session.channel().close();
245         }
246 
247         @Override
248         public boolean isOpen() {
249             return session.channel().isOpen();
250         }
251 
252     }
253 
254 }