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