View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.mina.common.support;
21  
22  import java.net.SocketAddress;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.mina.common.CloseFuture;
29  import org.apache.mina.common.IdleStatus;
30  import org.apache.mina.common.IoFuture;
31  import org.apache.mina.common.IoFutureListener;
32  import org.apache.mina.common.IoService;
33  import org.apache.mina.common.IoSession;
34  import org.apache.mina.common.TrafficMask;
35  import org.apache.mina.common.WriteFuture;
36  import org.apache.mina.common.IoFilter.WriteRequest;
37  
38  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
39  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
40  
41  /**
42   * Base implementation of {@link IoSession}.
43   * 
44   * @author The Apache Directory Project (mina-dev@directory.apache.org)
45   * @version $Rev: 592279 $, $Date: 2007-11-06 13:59:46 +0900 (Tue, 06 Nov 2007) $
46   */
47  public abstract class BaseIoSession implements IoSession {
48      
49      private static final IoFutureListener SCHEDULED_COUNTER_RESETTER =
50          new IoFutureListener() {
51              public void operationComplete(IoFuture future) {
52                  BaseIoSession s = (BaseIoSession) future.getSession();
53                  s.scheduledWriteBytes.set(0);
54                  s.scheduledWriteRequests.set(0);
55              }
56          };
57  
58      private final Map attributes = new HashMap(8);
59  
60      private final long creationTime;
61  
62      /** 
63       * A future that will be set 'closed' when the connection is closed.
64       */
65      private final CloseFuture closeFuture = new DefaultCloseFuture(this);
66  
67      private final AtomicBoolean scheduledForFlush = new AtomicBoolean();
68      
69      private final AtomicInteger scheduledWriteBytes = new AtomicInteger();
70  
71      private final AtomicInteger scheduledWriteRequests = new AtomicInteger();
72  
73      private boolean closing;
74  
75      // Configuration variables
76      private int idleTimeForRead;
77  
78      private int idleTimeForWrite;
79  
80      private int idleTimeForBoth;
81  
82      private int writeTimeout;
83  
84      private TrafficMask trafficMask = TrafficMask.ALL;
85  
86      // Status variables
87      private long readBytes;
88  
89      private long writtenBytes;
90  
91      private long readMessages;
92  
93      private long writtenMessages;
94  
95      private long lastReadTime;
96  
97      private long lastWriteTime;
98  
99      private int idleCountForBoth;
100 
101     private int idleCountForRead;
102 
103     private int idleCountForWrite;
104 
105     private long lastIdleTimeForBoth;
106 
107     private long lastIdleTimeForRead;
108 
109     private long lastIdleTimeForWrite;
110 
111     protected BaseIoSession() {
112         creationTime = lastReadTime = lastWriteTime = lastIdleTimeForBoth = lastIdleTimeForRead = lastIdleTimeForWrite = System
113                 .currentTimeMillis();
114         closeFuture.addListener(SCHEDULED_COUNTER_RESETTER);
115     }
116 
117     public boolean isConnected() {
118         return !closeFuture.isClosed();
119     }
120 
121     public synchronized boolean isClosing() {
122         return closing || closeFuture.isClosed();
123     }
124 
125     public CloseFuture getCloseFuture() {
126         return closeFuture;
127     }
128     
129     public boolean isScheduledForFlush() {
130         return scheduledForFlush.get();
131     }
132     
133     public boolean setScheduledForFlush(boolean flag) {
134         if (flag) {
135             return scheduledForFlush.compareAndSet(false, true);
136         } else {
137             scheduledForFlush.set(false);
138             return true;
139         }
140     }
141 
142     public CloseFuture close() {
143         synchronized (this) {
144             if (isClosing()) {
145                 return closeFuture;
146             } else {
147                 closing = true;
148             }
149         }
150 
151         close0();
152         return closeFuture;
153     }
154 
155     /**
156      * Implement this method to perform real close operation.
157      * By default, this method is implemented to set the future to
158      * 'closed' immediately.
159      */
160     protected void close0() {
161         closeFuture.setClosed();
162     }
163 
164     public WriteFuture write(Object message) {
165         return write(message, null);
166     }
167 
168     public WriteFuture write(Object message, SocketAddress remoteAddress) {
169         synchronized (this) {
170             if (isClosing() || !isConnected()) {
171                 return DefaultWriteFuture.newNotWrittenFuture(this);
172             }
173         }
174 
175         WriteFuture future = new DefaultWriteFuture(this);
176         write0(new WriteRequest(message, future, remoteAddress));
177 
178         return future;
179     }
180 
181     /**
182      * Implement this method to perform real write operation with
183      * the specified <code>writeRequest</code>.
184      * 
185      * By default, this method is implemented to set the future to
186      * 'not written' immediately.
187      */
188     protected void write0(WriteRequest writeRequest) {
189         writeRequest.getFuture().setWritten(false);
190     }
191 
192     public Object getAttachment() {
193         synchronized (attributes) {
194             return attributes.get("");
195         }
196     }
197 
198     public Object setAttachment(Object attachment) {
199         synchronized (attributes) {
200             return attributes.put("", attachment);
201         }
202     }
203 
204     public Object getAttribute(String key) {
205         synchronized (attributes) {
206             return attributes.get(key);
207         }
208     }
209 
210     public Object setAttribute(String key, Object value) {
211         synchronized (attributes) {
212             return attributes.put(key, value);
213         }
214     }
215 
216     public Object setAttribute(String key) {
217         return setAttribute(key, Boolean.TRUE);
218     }
219 
220     public Object removeAttribute(String key) {
221         synchronized (attributes) {
222             return attributes.remove(key);
223         }
224     }
225 
226     public boolean containsAttribute(String key) {
227         return getAttribute(key) != null;
228     }
229 
230     public Set getAttributeKeys() {
231         synchronized (attributes) {
232             return new HashSet(attributes.keySet());
233         }
234     }
235 
236     public int getIdleTime(IdleStatus status) {
237         if (status == IdleStatus.BOTH_IDLE)
238             return idleTimeForBoth;
239 
240         if (status == IdleStatus.READER_IDLE)
241             return idleTimeForRead;
242 
243         if (status == IdleStatus.WRITER_IDLE)
244             return idleTimeForWrite;
245 
246         throw new IllegalArgumentException("Unknown idle status: " + status);
247     }
248 
249     public long getIdleTimeInMillis(IdleStatus status) {
250         return getIdleTime(status) * 1000L;
251     }
252 
253     public void setIdleTime(IdleStatus status, int idleTime) {
254         if (idleTime < 0)
255             throw new IllegalArgumentException("Illegal idle time: " + idleTime);
256 
257         if (status == IdleStatus.BOTH_IDLE)
258             idleTimeForBoth = idleTime;
259         else if (status == IdleStatus.READER_IDLE)
260             idleTimeForRead = idleTime;
261         else if (status == IdleStatus.WRITER_IDLE)
262             idleTimeForWrite = idleTime;
263         else
264             throw new IllegalArgumentException("Unknown idle status: " + status);
265     }
266 
267     public int getWriteTimeout() {
268         return writeTimeout;
269     }
270 
271     public long getWriteTimeoutInMillis() {
272         return writeTimeout * 1000L;
273     }
274 
275     public void setWriteTimeout(int writeTimeout) {
276         if (writeTimeout < 0)
277             throw new IllegalArgumentException("Illegal write timeout: "
278                     + writeTimeout);
279         this.writeTimeout = writeTimeout;
280     }
281 
282     public TrafficMask getTrafficMask() {
283         return trafficMask;
284     }
285 
286     public void setTrafficMask(TrafficMask trafficMask) {
287         if (trafficMask == null) {
288             throw new NullPointerException("trafficMask");
289         }
290 
291         if (this.trafficMask == trafficMask) {
292             return;
293         }
294 
295         this.trafficMask = trafficMask;
296         updateTrafficMask();
297     }
298 
299     public void suspendRead() {
300         setTrafficMask(getTrafficMask().and(TrafficMask.READ.not()));
301     }
302 
303     public void suspendWrite() {
304         setTrafficMask(getTrafficMask().and(TrafficMask.WRITE.not()));
305     }
306 
307     public void resumeRead() {
308         setTrafficMask(getTrafficMask().or(TrafficMask.READ));
309     }
310 
311     public void resumeWrite() {
312         setTrafficMask(getTrafficMask().or(TrafficMask.WRITE));
313     }
314 
315     /**
316      * Signals the {@link IoService} that the {@link TrafficMask} of this
317      * session has been changed.
318      */
319     protected abstract void updateTrafficMask();
320 
321     public long getReadBytes() {
322         return readBytes;
323     }
324 
325     public long getWrittenBytes() {
326         return writtenBytes;
327     }
328 
329     public long getWrittenWriteRequests() {
330         return writtenMessages;
331     }
332 
333     public long getReadMessages() {
334         return readMessages;
335     }
336 
337     public long getWrittenMessages() {
338         return writtenMessages;
339     }
340     
341     public int getScheduledWriteBytes() {
342         return scheduledWriteBytes.get();
343     }
344     
345     public int getScheduledWriteRequests() {
346         return scheduledWriteRequests.get();
347     }
348 
349     public void increaseReadBytes(int increment) {
350         if (increment > 0) {
351             readBytes += increment;
352             lastReadTime = System.currentTimeMillis();
353             idleCountForBoth = 0;
354             idleCountForRead = 0;
355         }
356     }
357 
358     public void increaseWrittenBytes(int increment) {
359         if (increment > 0) {
360             writtenBytes += increment;
361             lastWriteTime = System.currentTimeMillis();
362             idleCountForBoth = 0;
363             idleCountForWrite = 0;
364             
365             scheduledWriteBytes.addAndGet(-increment);
366         }
367     }
368     
369     public void increaseReadMessages() {
370         readMessages++;
371         lastReadTime = System.currentTimeMillis();
372     }
373 
374     public void increaseWrittenMessages() {
375         writtenMessages++;
376         lastWriteTime = System.currentTimeMillis();
377         scheduledWriteRequests.decrementAndGet();
378     }
379     
380     public void increaseScheduledWriteBytes(int increment) {
381         scheduledWriteBytes.addAndGet(increment);
382     }
383 
384     public void increaseScheduledWriteRequests() {
385         scheduledWriteRequests.incrementAndGet();
386     }
387 
388     public long getCreationTime() {
389         return creationTime;
390     }
391 
392     public long getLastIoTime() {
393         return Math.max(lastReadTime, lastWriteTime);
394     }
395 
396     public long getLastReadTime() {
397         return lastReadTime;
398     }
399 
400     public long getLastWriteTime() {
401         return lastWriteTime;
402     }
403 
404     public boolean isIdle(IdleStatus status) {
405         if (status == IdleStatus.BOTH_IDLE)
406             return idleCountForBoth > 0;
407 
408         if (status == IdleStatus.READER_IDLE)
409             return idleCountForRead > 0;
410 
411         if (status == IdleStatus.WRITER_IDLE)
412             return idleCountForWrite > 0;
413 
414         throw new IllegalArgumentException("Unknown idle status: " + status);
415     }
416 
417     public int getIdleCount(IdleStatus status) {
418         if (status == IdleStatus.BOTH_IDLE)
419             return idleCountForBoth;
420 
421         if (status == IdleStatus.READER_IDLE)
422             return idleCountForRead;
423 
424         if (status == IdleStatus.WRITER_IDLE)
425             return idleCountForWrite;
426 
427         throw new IllegalArgumentException("Unknown idle status: " + status);
428     }
429 
430     public long getLastIdleTime(IdleStatus status) {
431         if (status == IdleStatus.BOTH_IDLE)
432             return lastIdleTimeForBoth;
433 
434         if (status == IdleStatus.READER_IDLE)
435             return lastIdleTimeForRead;
436 
437         if (status == IdleStatus.WRITER_IDLE)
438             return lastIdleTimeForWrite;
439 
440         throw new IllegalArgumentException("Unknown idle status: " + status);
441     }
442 
443     public void increaseIdleCount(IdleStatus status) {
444         if (status == IdleStatus.BOTH_IDLE) {
445             idleCountForBoth++;
446             lastIdleTimeForBoth = System.currentTimeMillis();
447         } else if (status == IdleStatus.READER_IDLE) {
448             idleCountForRead++;
449             lastIdleTimeForRead = System.currentTimeMillis();
450         } else if (status == IdleStatus.WRITER_IDLE) {
451             idleCountForWrite++;
452             lastIdleTimeForWrite = System.currentTimeMillis();
453         } else
454             throw new IllegalArgumentException("Unknown idle status: " + status);
455     }
456 
457     public String toString() {
458         return "(" + getTransportType() + ", R: " + getRemoteAddress()
459                 + ", L: " + getLocalAddress() + ", S: " + getServiceAddress()
460                 + ')';
461     }
462 }