001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.mina.common.support;
021
022 import java.net.SocketAddress;
023 import java.util.ArrayList;
024 import java.util.Collections;
025 import java.util.HashMap;
026 import java.util.HashSet;
027 import java.util.Iterator;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.Set;
031 import java.util.concurrent.CountDownLatch;
032
033 import org.apache.mina.common.IoAcceptorConfig;
034 import org.apache.mina.common.IoConnector;
035 import org.apache.mina.common.IoFuture;
036 import org.apache.mina.common.IoFutureListener;
037 import org.apache.mina.common.IoHandler;
038 import org.apache.mina.common.IoService;
039 import org.apache.mina.common.IoServiceConfig;
040 import org.apache.mina.common.IoServiceListener;
041 import org.apache.mina.common.IoSession;
042 import org.apache.mina.util.IdentityHashSet;
043
044 /**
045 * A helper which provides addition and removal of {@link IoServiceListener}s and firing
046 * events.
047 *
048 * @author The Apache Directory Project (mina-dev@directory.apache.org)
049 * @version $Rev: 446526 $, $Date: 2006-09-15 01:44:11 -0400 (Fri, 15 Sep 2006) $
050 */
051 public class IoServiceListenerSupport
052 {
053 /**
054 * A list of {@link IoServiceListener}s.
055 */
056 private final List listeners = new ArrayList();
057
058 /**
059 * Tracks managed <tt>serviceAddress</tt>es.
060 */
061 private final Set managedServiceAddresses = new HashSet();
062
063 /**
064 * Tracks managed sesssions with <tt>serviceAddress</tt> as a key.
065 */
066 private final Map managedSessions = new HashMap();
067
068 /**
069 * Creates a new instance.
070 */
071 public IoServiceListenerSupport()
072 {
073 }
074
075 /**
076 * Adds a new listener.
077 */
078 public void add( IoServiceListener listener )
079 {
080 synchronized( listeners )
081 {
082 listeners.add( listener );
083 }
084 }
085
086 /**
087 * Removes an existing listener.
088 */
089 public void remove( IoServiceListener listener )
090 {
091 synchronized( listeners )
092 {
093 listeners.remove( listener );
094 }
095 }
096
097 public Set getManagedServiceAddresses()
098 {
099 return Collections.unmodifiableSet( managedServiceAddresses );
100 }
101
102 public boolean isManaged( SocketAddress serviceAddress )
103 {
104 synchronized( managedServiceAddresses )
105 {
106 return managedServiceAddresses.contains( serviceAddress );
107 }
108 }
109
110 public Set getManagedSessions( SocketAddress serviceAddress )
111 {
112 Set sessions;
113 synchronized( managedSessions )
114 {
115 sessions = ( Set ) managedSessions.get( serviceAddress );
116 if( sessions == null )
117 {
118 sessions = new IdentityHashSet();
119 }
120 }
121
122 synchronized( sessions )
123 {
124 return new IdentityHashSet( sessions );
125 }
126 }
127
128 /**
129 * Calls {@link IoServiceListener#serviceActivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
130 * for all registered listeners.
131 */
132 public void fireServiceActivated(
133 IoService service, SocketAddress serviceAddress,
134 IoHandler handler, IoServiceConfig config )
135 {
136 synchronized( managedServiceAddresses )
137 {
138 if( !managedServiceAddresses.add( serviceAddress ) )
139 {
140 return;
141 }
142 }
143
144 synchronized( listeners )
145 {
146 for( Iterator i = listeners.iterator(); i.hasNext(); )
147 {
148 ( ( IoServiceListener ) i.next() ).serviceActivated(
149 service, serviceAddress, handler, config );
150 }
151 }
152 }
153
154 /**
155 * Calls {@link IoServiceListener#serviceDeactivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
156 * for all registered listeners.
157 */
158 public synchronized void fireServiceDeactivated(
159 IoService service, SocketAddress serviceAddress,
160 IoHandler handler, IoServiceConfig config )
161 {
162 synchronized( managedServiceAddresses )
163 {
164 if( !managedServiceAddresses.remove( serviceAddress ) )
165 {
166 return;
167 }
168 }
169
170 try
171 {
172 synchronized( listeners )
173 {
174 for( Iterator i = listeners.iterator(); i.hasNext(); )
175 {
176 ( ( IoServiceListener ) i.next() ).serviceDeactivated(
177 service, serviceAddress, handler, config );
178 }
179 }
180 }
181 finally
182 {
183 disconnectSessions( serviceAddress, config );
184 }
185 }
186
187
188 /**
189 * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
190 */
191 public void fireSessionCreated( IoSession session )
192 {
193 SocketAddress serviceAddress = session.getServiceAddress();
194
195 // Get the session set.
196 boolean firstSession = false;
197 Set sessions;
198 synchronized( managedSessions )
199 {
200 sessions = ( Set ) managedSessions.get( serviceAddress );
201 if( sessions == null )
202 {
203 sessions = new IdentityHashSet();
204 managedSessions.put( serviceAddress, sessions );
205 firstSession = true;
206 }
207 }
208
209 // If already registered, ignore.
210 synchronized( sessions )
211 {
212 if ( !sessions.add( session ) )
213 {
214 return;
215 }
216 }
217
218 // If the first connector session, fire a virtual service activation event.
219 if( session.getService() instanceof IoConnector && firstSession )
220 {
221 fireServiceActivated(
222 session.getService(), session.getServiceAddress(),
223 session.getHandler(), session.getServiceConfig() );
224 }
225
226 // Fire session events.
227 session.getFilterChain().fireSessionCreated( session );
228 session.getFilterChain().fireSessionOpened( session);
229
230 // Fire listener events.
231 synchronized( listeners )
232 {
233 for( Iterator i = listeners.iterator(); i.hasNext(); )
234 {
235 ( ( IoServiceListener ) i.next() ).sessionCreated( session );
236 }
237 }
238 }
239
240 /**
241 * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
242 */
243 public void fireSessionDestroyed( IoSession session )
244 {
245 SocketAddress serviceAddress = session.getServiceAddress();
246
247 // Get the session set.
248 Set sessions;
249 boolean lastSession = false;
250 synchronized( managedSessions )
251 {
252 sessions = ( Set ) managedSessions.get( serviceAddress );
253 // Ignore if unknown.
254 if( sessions == null )
255 {
256 return;
257 }
258
259 // Try to remove the remaining empty seession set after removal.
260 synchronized( sessions )
261 {
262 sessions.remove( session );
263 if( sessions.isEmpty() )
264 {
265 managedSessions.remove( serviceAddress );
266 lastSession = true;
267 }
268 }
269 }
270
271 // Fire session events.
272 session.getFilterChain().fireSessionClosed( session );
273
274 // Fire listener events.
275 try
276 {
277 synchronized( listeners )
278 {
279 for( Iterator i = listeners.iterator(); i.hasNext(); )
280 {
281 ( ( IoServiceListener ) i.next() ).sessionDestroyed( session );
282 }
283 }
284 }
285 finally
286 {
287 // Fire a virtual service deactivation event for the last session of the connector.
288 //TODO double-check that this is *STILL* the last session. May not be the case
289 if( session.getService() instanceof IoConnector && lastSession )
290 {
291 fireServiceDeactivated(
292 session.getService(), session.getServiceAddress(),
293 session.getHandler(), session.getServiceConfig() );
294 }
295 }
296 }
297
298 private void disconnectSessions( SocketAddress serviceAddress, IoServiceConfig config )
299 {
300 if( !( config instanceof IoAcceptorConfig ) )
301 {
302 return;
303 }
304
305 if( !( ( IoAcceptorConfig ) config ).isDisconnectOnUnbind() )
306 {
307 return;
308 }
309
310 Set sessions;
311 synchronized( managedSessions )
312 {
313 sessions = ( Set ) managedSessions.get( serviceAddress );
314 }
315
316 if( sessions == null )
317 {
318 return;
319 }
320
321 Set sessionsCopy;
322
323 // Create a copy to avoid ConcurrentModificationException
324 synchronized( sessions )
325 {
326 sessionsCopy = new IdentityHashSet( sessions );
327 }
328
329 final CountDownLatch latch = new CountDownLatch(sessionsCopy.size());
330
331 for( Iterator i = sessionsCopy.iterator(); i.hasNext(); )
332 {
333 ( ( IoSession ) i.next() ).close().addListener( new IoFutureListener()
334 {
335 public void operationComplete( IoFuture future )
336 {
337 latch.countDown();
338 }
339 } );
340 }
341
342 try
343 {
344 latch.await();
345 }
346 catch( InterruptedException ie )
347 {
348 // Ignored
349 }
350 }
351 }
|