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