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.Collections;
24  import java.util.List;
25  import java.util.Set;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.concurrent.ConcurrentMap;
28  import java.util.concurrent.CopyOnWriteArrayList;
29  import java.util.concurrent.CopyOnWriteArraySet;
30  import java.util.concurrent.CountDownLatch;
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.common.RuntimeIOException;
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: 555855 $, $Date: 2007-07-13 12:19:00 +0900 (금, 13  7월 2007) $
50   */
51  public class IoServiceListenerSupport {
52      /**
53       * A list of {@link IoServiceListener}s.
54       */
55      private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();
56  
57      /**
58       * Tracks managed <tt>serviceAddress</tt>es.
59       */
60      private final Set<SocketAddress> managedServiceAddresses = new CopyOnWriteArraySet<SocketAddress>();
61  
62      /**
63       * Tracks managed sesssions with <tt>serviceAddress</tt> as a key.
64       */
65      private final ConcurrentMap<SocketAddress, Set<IoSession>> managedSessions = new ConcurrentHashMap<SocketAddress, Set<IoSession>>();
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          listeners.add(listener);
78      }
79  
80      /**
81       * Removes an existing listener.
82       */
83      public void remove(IoServiceListener listener) {
84          listeners.remove(listener);
85      }
86  
87      public Set<SocketAddress> getManagedServiceAddresses() {
88          return Collections.unmodifiableSet(managedServiceAddresses);
89      }
90  
91      public boolean isManaged(SocketAddress serviceAddress) {
92          return managedServiceAddresses.contains(serviceAddress);
93      }
94  
95      public Set<IoSession> getManagedSessions(SocketAddress serviceAddress) {
96          Set<IoSession> sessions = managedSessions.get(serviceAddress);
97  
98          if (null == sessions) {
99              return Collections.emptySet();
100         }
101 
102         synchronized (sessions) {
103             return new IdentityHashSet<IoSession>(sessions);
104         }
105     }
106 
107     /**
108      * Calls {@link IoServiceListener#serviceActivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
109      * for all registered listeners.
110      */
111     public void fireServiceActivated(IoService service,
112             SocketAddress serviceAddress, IoHandler handler,
113             IoServiceConfig config) {
114         if (!managedServiceAddresses.add(serviceAddress)) {
115             return;
116         }
117 
118         for (IoServiceListener listener : listeners) {
119             listener.serviceActivated(service, serviceAddress, handler, config);
120         }
121     }
122 
123     /**
124      * Calls {@link IoServiceListener#serviceDeactivated(IoService, SocketAddress, IoHandler, IoServiceConfig)}
125      * for all registered listeners.
126      */
127     public synchronized void fireServiceDeactivated(IoService service,
128             SocketAddress serviceAddress, IoHandler handler,
129             IoServiceConfig config) {
130         if (!managedServiceAddresses.remove(serviceAddress)) {
131             return;
132         }
133 
134         try {
135             for (IoServiceListener listener : listeners) {
136                 listener.serviceDeactivated(service, serviceAddress, handler,
137                         config);
138             }
139         } finally {
140             disconnectSessions(serviceAddress, config);
141         }
142     }
143 
144     /**
145      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
146      */
147     public void fireSessionCreated(IoSession session) {
148         SocketAddress serviceAddress = session.getServiceAddress();
149 
150         // Get the session set.
151         Set<IoSession> s = new IdentityHashSet<IoSession>();
152         Set<IoSession> sessions = managedSessions.putIfAbsent(serviceAddress,
153                 Collections.synchronizedSet(s));
154         boolean firstSession;
155 
156         if (null == sessions) {
157             sessions = s;
158             firstSession = true;
159         } else {
160             firstSession = false;
161         }
162 
163         // If already registered, ignore.
164         if (!sessions.add(session)) {
165             return;
166         }
167 
168         // If the first connector session, fire a virtual service activation event.
169         if (session.getService() instanceof IoConnector && firstSession) {
170             fireServiceActivated(session.getService(), session
171                     .getServiceAddress(), session.getHandler(), session
172                     .getServiceConfig());
173         }
174 
175         // Fire session events.
176         session.getFilterChain().fireSessionCreated(session);
177         session.getFilterChain().fireSessionOpened(session);
178 
179         // Fire listener events.
180         for (IoServiceListener listener : listeners) {
181             listener.sessionCreated(session);
182         }
183     }
184 
185     /**
186      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
187      */
188     public void fireSessionDestroyed(IoSession session) {
189         SocketAddress serviceAddress = session.getServiceAddress();
190 
191         // Get the session set.
192         Set<IoSession> sessions = managedSessions.get(serviceAddress);
193         // Ignore if unknown.
194         if (sessions == null) {
195             return;
196         }
197 
198         sessions.remove(session);
199 
200         boolean lastSession = false;
201 
202         // Try to remove the remaining empty session set after removal.
203         if (sessions.isEmpty()) {
204             lastSession = managedSessions.remove(serviceAddress, sessions);
205         }
206 
207         // Fire session events.
208         session.getFilterChain().fireSessionClosed(session);
209 
210         // Fire listener events.
211         try {
212             for (IoServiceListener listener : listeners) {
213                 listener.sessionDestroyed(session);
214             }
215         } finally {
216             // Fire a virtual service deactivation event for the last session of the connector.
217             //TODO double-check that this is *STILL* the last session. May not be the case
218             if (session.getService() instanceof IoConnector && lastSession) {
219                 fireServiceDeactivated(session.getService(), session
220                         .getServiceAddress(), session.getHandler(), session
221                         .getServiceConfig());
222             }
223         }
224     }
225 
226     private void disconnectSessions(SocketAddress serviceAddress,
227             IoServiceConfig config) {
228         if (!(config instanceof IoAcceptorConfig)) {
229             return;
230         }
231 
232         if (!((IoAcceptorConfig) config).isDisconnectOnUnbind()) {
233             return;
234         }
235 
236         Set<IoSession> sessions = getManagedSessions(serviceAddress);
237 
238         if (sessions.isEmpty()) {
239             return;
240         }
241 
242         final CountDownLatch latch = new CountDownLatch(sessions.size());
243 
244         for (IoSession session : sessions) {
245             session.close().addListener(new IoFutureListener() {
246                 public void operationComplete(IoFuture future) {
247                     latch.countDown();
248                 }
249             });
250         }
251 
252         try {
253             latch.await();
254         } catch (InterruptedException e) {
255             throw new RuntimeIOException(e);
256         }
257     }
258 }