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