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.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.mina.common.ExceptionMonitor;
33  import org.apache.mina.common.IoAcceptorConfig;
34  import org.apache.mina.common.IoConnector;
35  import org.apache.mina.common.IoFuture;
36  import org.apache.mina.common.IoFutureListener;
37  import org.apache.mina.common.IoHandler;
38  import org.apache.mina.common.IoService;
39  import org.apache.mina.common.IoServiceConfig;
40  import org.apache.mina.common.IoServiceListener;
41  import org.apache.mina.common.IoSession;
42  import org.apache.mina.util.IdentityHashSet;
43  
44  /**
45   * A helper which provides addition and removal of {@link IoServiceListener}s and firing
46   * events.
47   * 
48   * @author The Apache Directory Project (mina-dev@directory.apache.org)
49   * @version $Rev: 636193 $, $Date: 2008-03-12 13:17:43 +0900 (Wed, 12 Mar 2008) $
50   */
51  public class IoServiceListenerSupport {
52      /**
53       * A list of {@link IoServiceListener}s.
54       */
55      private final List listeners = new ArrayList();
56  
57      /**
58       * Tracks managed <tt>serviceAddress</tt>es.
59       */
60      private final Set managedServiceAddresses = new HashSet();
61  
62      /**
63       * Tracks managed sesssions with <tt>serviceAddress</tt> as a key.
64       */
65      private final Map managedSessions = new HashMap();
66  
67      /**
68       * Creates a new instance.
69       */
70      public IoServiceListenerSupport() {
71      }
72  
73      /**
74       * Adds a new listener.
75       */
76      public void add(IoServiceListener listener) {
77          synchronized (listeners) {
78              listeners.add(listener);
79          }
80      }
81  
82      /**
83       * Removes an existing listener.
84       */
85      public void remove(IoServiceListener listener) {
86          synchronized (listeners) {
87              listeners.remove(listener);
88          }
89      }
90  
91      public Set getManagedServiceAddresses() {
92          return Collections.unmodifiableSet(managedServiceAddresses);
93      }
94  
95      public boolean isManaged(SocketAddress serviceAddress) {
96          synchronized (managedServiceAddresses) {
97              return managedServiceAddresses.contains(serviceAddress);
98          }
99      }
100 
101     public Set getManagedSessions(SocketAddress serviceAddress) {
102         Set sessions;
103         synchronized (managedSessions) {
104             sessions = (Set) managedSessions.get(serviceAddress);
105             if (sessions == null) {
106                 sessions = new IdentityHashSet();
107             }
108         }
109 
110         synchronized (sessions) {
111             return new IdentityHashSet(sessions);
112         }
113     }
114 
115     /**
116      * Calls {@link IoServiceListener#serviceActivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
117      * for all registered listeners.
118      */
119     public void fireServiceActivated(IoService service,
120             SocketAddress serviceAddress, IoHandler handler,
121             IoServiceConfig config) {
122         synchronized (managedServiceAddresses) {
123             if (!managedServiceAddresses.add(serviceAddress)) {
124                 return;
125             }
126         }
127 
128         synchronized (listeners) {
129             for (Iterator i = listeners.iterator(); i.hasNext();) {
130                 try {
131                     ((IoServiceListener) i.next()).serviceActivated(service,
132                             serviceAddress, handler, config);
133                 } catch (Throwable e) {
134                     ExceptionMonitor.getInstance().exceptionCaught(e);
135                 }
136             }
137         }
138     }
139 
140     /**
141      * Calls {@link IoServiceListener#serviceDeactivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
142      * for all registered listeners.
143      */
144     public synchronized void fireServiceDeactivated(IoService service,
145             SocketAddress serviceAddress, IoHandler handler,
146             IoServiceConfig config) {
147         synchronized (managedServiceAddresses) {
148             if (!managedServiceAddresses.remove(serviceAddress)) {
149                 return;
150             }
151         }
152 
153         try {
154             synchronized (listeners) {
155                 for (Iterator i = listeners.iterator(); i.hasNext();) {
156                     try {
157                         ((IoServiceListener) i.next()).serviceDeactivated(service,
158                                 serviceAddress, handler, config);
159                     } catch (Throwable e) {
160                         ExceptionMonitor.getInstance().exceptionCaught(e);
161                     }
162                 }
163             }
164         } finally {
165             disconnectSessions(serviceAddress, config);
166         }
167     }
168 
169     /**
170      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
171      */
172     public void fireSessionCreated(IoSession session) {
173         SocketAddress serviceAddress = session.getServiceAddress();
174 
175         // Get the session set.
176         boolean firstSession = false;
177         Set sessions;
178         synchronized (managedSessions) {
179             sessions = (Set) managedSessions.get(serviceAddress);
180             if (sessions == null) {
181                 sessions = new IdentityHashSet();
182                 managedSessions.put(serviceAddress, sessions);
183                 firstSession = true;
184             }
185 
186             // If already registered, ignore.
187             synchronized (sessions) {
188                 if (!sessions.add(session)) {
189                     return;
190                 }
191             }
192         }
193 
194         // If the first connector session, fire a virtual service activation event.
195         if (session.getService() instanceof IoConnector && firstSession) {
196             fireServiceActivated(session.getService(), session
197                     .getServiceAddress(), session.getHandler(), session
198                     .getServiceConfig());
199         }
200 
201         // Fire session events.
202         session.getFilterChain().fireSessionCreated(session);
203         session.getFilterChain().fireSessionOpened(session);
204 
205         // Fire listener events.
206         synchronized (listeners) {
207             for (Iterator i = listeners.iterator(); i.hasNext();) {
208                 try {
209                     ((IoServiceListener) i.next()).sessionCreated(session);
210                 } catch (Throwable e) {
211                     ExceptionMonitor.getInstance().exceptionCaught(e);
212                 }
213             }
214         }
215     }
216 
217     /**
218      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
219      */
220     public void fireSessionDestroyed(IoSession session) {
221         SocketAddress serviceAddress = session.getServiceAddress();
222 
223         // Get the session set.
224         Set sessions;
225         boolean lastSession = false;
226         synchronized (managedSessions) {
227             sessions = (Set) managedSessions.get(serviceAddress);
228             // Ignore if unknown.
229             if (sessions == null) {
230                 return;
231             }
232 
233             // Try to remove the remaining empty session set after removal.
234             synchronized (sessions) {
235                 sessions.remove(session);
236                 if (sessions.isEmpty()) {
237                     managedSessions.remove(serviceAddress);
238                     lastSession = true;
239                 }
240             }
241         }
242 
243         // Fire session events.
244         session.getFilterChain().fireSessionClosed(session);
245 
246         // Fire listener events.
247         try {
248             synchronized (listeners) {
249                 for (Iterator i = listeners.iterator(); i.hasNext();) {
250                     try {
251                         ((IoServiceListener) i.next()).sessionDestroyed(session);
252                     } catch (Throwable e) {
253                         ExceptionMonitor.getInstance().exceptionCaught(e);
254                     }
255                 }
256             }
257         } finally {
258             // Fire a virtual service deactivation event for the last session of the connector.
259             //TODO double-check that this is *STILL* the last session. May not be the case
260             if (session.getService() instanceof IoConnector && lastSession) {
261                 fireServiceDeactivated(session.getService(), session
262                         .getServiceAddress(), session.getHandler(), session
263                         .getServiceConfig());
264             }
265         }
266     }
267 
268     private void disconnectSessions(SocketAddress serviceAddress,
269             IoServiceConfig config) {
270         if (!(config instanceof IoAcceptorConfig)) {
271             return;
272         }
273 
274         if (!((IoAcceptorConfig) config).isDisconnectOnUnbind()) {
275             return;
276         }
277 
278         Set sessions;
279         synchronized (managedSessions) {
280             sessions = (Set) managedSessions.get(serviceAddress);
281         }
282 
283         if (sessions == null) {
284             return;
285         }
286 
287         final Object lock = new Object();
288         Set sessionsCopy;
289 
290         // Create a copy to avoid ConcurrentModificationException
291         synchronized (sessions) {
292             sessionsCopy = new IdentityHashSet(sessions);
293         }
294 
295         for (Iterator i = sessionsCopy.iterator(); i.hasNext();) {
296             ((IoSession) i.next()).close().addListener(new IoFutureListener() {
297                 public void operationComplete(IoFuture future) {
298                     synchronized (lock) {
299                         lock.notifyAll();
300                     }
301                 }
302             });
303         }
304 
305         try {
306             synchronized (lock) {
307                 while (!managedSessions.isEmpty()) {
308                     lock.wait(500);
309                 }
310             }
311         } catch (InterruptedException ie) {
312             // Ignored
313         }
314     }
315 }