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.core.service;
21  
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentMap;
27  import java.util.concurrent.CopyOnWriteArrayList;
28  import java.util.concurrent.atomic.AtomicBoolean;
29  
30  import org.apache.mina.core.filterchain.IoFilterChain;
31  import org.apache.mina.core.future.IoFuture;
32  import org.apache.mina.core.future.IoFutureListener;
33  import org.apache.mina.core.session.IoSession;
34  import org.apache.mina.util.ExceptionMonitor;
35  
36  /**
37   * A helper class which provides addition and removal of {@link IoServiceListener}s and firing
38   * events.
39   *
40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
41   */
42  public class IoServiceListenerSupport {
43      /** The {@link IoService} that this instance manages. */
44      private final IoService service;
45  
46      /** A list of {@link IoServiceListener}s. */
47      private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();
48  
49      /** Tracks managed sessions. */
50      private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long, IoSession>();
51  
52      /**  Read only version of {@link #managedSessions}. */
53      private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap(managedSessions);
54  
55      private final AtomicBoolean activated = new AtomicBoolean();
56  
57      /** Time this listenerSupport has been activated */
58      private volatile long activationTime;
59  
60      /** A counter used to store the maximum sessions we managed since the listenerSupport has been activated */
61      private volatile int largestManagedSessionCount = 0;
62  
63      /** A global counter to count the number of sessions managed since the start */
64      private volatile long cumulativeManagedSessionCount = 0;
65  
66      /**
67       * Creates a new instance of the listenerSupport.
68       * 
69       * @param service The associated IoService
70       */
71      public IoServiceListenerSupport(IoService service) {
72          if (service == null) {
73              throw new IllegalArgumentException("service");
74          }
75  
76          this.service = service;
77      }
78  
79      /**
80       * Adds a new listener.
81       * 
82       * @param listener The added listener
83       */
84      public void add(IoServiceListener listener) {
85          if (listener != null) {
86              listeners.add(listener);
87          }
88      }
89  
90      /**
91       * Removes an existing listener.
92       * 
93       * @param listener The listener to remove
94       */
95      public void remove(IoServiceListener listener) {
96          if (listener != null) {
97              listeners.remove(listener);
98          }
99      }
100 
101     /**
102      * @return The time (in ms) this instance has been activated
103      */
104     public long getActivationTime() {
105         return activationTime;
106     }
107 
108     public Map<Long, IoSession> getManagedSessions() {
109         return readOnlyManagedSessions;
110     }
111 
112     public int getManagedSessionCount() {
113         return managedSessions.size();
114     }
115 
116     /**
117      * @return The largest number of managed session since the creation of this
118      * listenerSupport
119      */
120     public int getLargestManagedSessionCount() {
121         return largestManagedSessionCount;
122     }
123 
124     /**
125      * @return The total number of sessions managed since the initilization of this
126      * ListenerSupport
127      */
128     public long getCumulativeManagedSessionCount() {
129         return cumulativeManagedSessionCount;
130     }
131 
132     /**
133      * @return true if the instance is active
134      */
135     public boolean isActive() {
136         return activated.get();
137     }
138 
139     /**
140      * Calls {@link IoServiceListener#serviceActivated(IoService)}
141      * for all registered listeners.
142      */
143     public void fireServiceActivated() {
144         if (!activated.compareAndSet(false, true)) {
145             // The instance is already active
146             return;
147         }
148 
149         activationTime = System.currentTimeMillis();
150 
151         // Activate all the listeners now
152         for (IoServiceListener listener : listeners) {
153             try {
154                 listener.serviceActivated(service);
155             } catch (Exception e) {
156                 ExceptionMonitor.getInstance().exceptionCaught(e);
157             }
158         }
159     }
160 
161     /**
162      * Calls {@link IoServiceListener#serviceDeactivated(IoService)}
163      * for all registered listeners.
164      */
165     public void fireServiceDeactivated() {
166         if (!activated.compareAndSet(true, false)) {
167             // The instance is already desactivated
168             return;
169         }
170 
171         // Desactivate all the listeners
172         try {
173             for (IoServiceListener listener : listeners) {
174                 try {
175                     listener.serviceDeactivated(service);
176                 } catch (Exception e) {
177                     ExceptionMonitor.getInstance().exceptionCaught(e);
178                 }
179             }
180         } finally {
181             disconnectSessions();
182         }
183     }
184 
185     /**
186      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
187      * 
188      * @param session The session which has been created
189      */
190     public void fireSessionCreated(IoSession session) {
191         boolean firstSession = false;
192 
193         if (session.getService() instanceof IoConnector) {
194             synchronized (managedSessions) {
195                 firstSession = managedSessions.isEmpty();
196             }
197         }
198 
199         // If already registered, ignore.
200         if (managedSessions.putIfAbsent(session.getId(), session) != null) {
201             return;
202         }
203 
204         // If the first connector session, fire a virtual service activation event.
205         if (firstSession) {
206             fireServiceActivated();
207         }
208 
209         // Fire session events.
210         IoFilterChain filterChain = session.getFilterChain();
211         filterChain.fireSessionCreated();
212         filterChain.fireSessionOpened();
213 
214         int managedSessionCount = managedSessions.size();
215 
216         if (managedSessionCount > largestManagedSessionCount) {
217             largestManagedSessionCount = managedSessionCount;
218         }
219 
220         cumulativeManagedSessionCount++;
221 
222         // Fire listener events.
223         for (IoServiceListener l : listeners) {
224             try {
225                 l.sessionCreated(session);
226             } catch (Exception e) {
227                 ExceptionMonitor.getInstance().exceptionCaught(e);
228             }
229         }
230     }
231 
232     /**
233      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
234      * 
235      * @param session The session which has been destroyed
236      */
237     public void fireSessionDestroyed(IoSession session) {
238         // Try to remove the remaining empty session set after removal.
239         if (managedSessions.remove(session.getId()) == null) {
240             return;
241         }
242 
243         // Fire session events.
244         session.getFilterChain().fireSessionClosed();
245 
246         // Fire listener events.
247         try {
248             for (IoServiceListener l : listeners) {
249                 try {
250                     l.sessionDestroyed(session);
251                 } catch (Exception e) {
252                     ExceptionMonitor.getInstance().exceptionCaught(e);
253                 }
254             }
255         } finally {
256             // Fire a virtual service deactivation event for the last session of the connector.
257             if (session.getService() instanceof IoConnector) {
258                 boolean lastSession = false;
259 
260                 synchronized (managedSessions) {
261                     lastSession = managedSessions.isEmpty();
262                 }
263 
264                 if (lastSession) {
265                     fireServiceDeactivated();
266                 }
267             }
268         }
269     }
270 
271     /**
272      * Close all the sessions
273      * TODO disconnectSessions.
274      *
275      */
276     private void disconnectSessions() {
277         if (!(service instanceof IoAcceptor)) {
278             // We don't disconnect sessions for anything but an Acceptor
279             return;
280         }
281 
282         if (!((IoAcceptor) service).isCloseOnDeactivation()) {
283             return;
284         }
285 
286         Object lock = new Object();
287         IoFutureListener<IoFuture> listener = new LockNotifyingListener(lock);
288 
289         for (IoSession s : managedSessions.values()) {
290             s.close(true).addListener(listener);
291         }
292 
293         try {
294             synchronized (lock) {
295                 while (!managedSessions.isEmpty()) {
296                     lock.wait(500);
297                 }
298             }
299         } catch (InterruptedException ie) {
300             // Ignored
301         }
302     }
303 
304     /**
305      * A listener in charge of releasing the lock when the close has been completed
306      */
307     private static class LockNotifyingListener implements IoFutureListener<IoFuture> {
308         private final Object lock;
309 
310         public LockNotifyingListener(Object lock) {
311             this.lock = lock;
312         }
313 
314         public void operationComplete(IoFuture future) {
315             synchronized (lock) {
316                 lock.notifyAll();
317             }
318         }
319     }
320 }