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.filterchain.IoFilterChain;
31  import org.apache.mina.core.future.IoFuture;
32  import org.apache.mina.core.future.IoFutureListener;
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: 713957 $, $Date: 2008-11-14 10:27:16 +0100 (Fri, 14 Nov 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#serviceDeactivated(IoService)}
139      * for all registered listeners.
140      */
141     public void fireServiceDeactivated() {
142         if (!activated.compareAndSet(true, false)) {
143             return;
144         }
145 
146         try {
147             for (IoServiceListener l : listeners) {
148                 try {
149                     l.serviceDeactivated(service);
150                 } catch (Throwable e) {
151                     ExceptionMonitor.getInstance().exceptionCaught(e);
152                 }
153             }
154         } finally {
155             disconnectSessions();
156         }
157     }
158 
159     /**
160      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
161      */
162     public void fireSessionCreated(IoSession session) {
163         boolean firstSession = false;
164         if (session.getService() instanceof IoConnector) {
165             synchronized (managedSessions) {
166                 firstSession = managedSessions.isEmpty();
167             }
168         }
169 
170         // If already registered, ignore.
171         if (managedSessions.putIfAbsent(Long.valueOf(session.getId()), session) != null) {
172             return;
173         }
174 
175         // If the first connector session, fire a virtual service activation event.
176         if (firstSession) {
177             fireServiceActivated();
178         }
179 
180         // Fire session events.
181         IoFilterChain filterChain = session.getFilterChain(); 
182         filterChain.fireSessionCreated();
183         filterChain.fireSessionOpened();
184 
185         int managedSessionCount = managedSessions.size();
186         if (managedSessionCount > largestManagedSessionCount) {
187             largestManagedSessionCount = managedSessionCount;
188         }
189         cumulativeManagedSessionCount ++;
190 
191         // Fire listener events.
192         for (IoServiceListener l : listeners) {
193             try {
194                 l.sessionCreated(session);
195             } catch (Throwable e) {
196                 ExceptionMonitor.getInstance().exceptionCaught(e);
197             }
198         }
199     }
200 
201     /**
202      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
203      */
204     public void fireSessionDestroyed(IoSession session) {
205         // Try to remove the remaining empty session set after removal.
206         if (managedSessions.remove(Long.valueOf(session.getId())) == null) {
207             return;
208         }
209 
210         // Fire session events.
211         session.getFilterChain().fireSessionClosed();
212 
213         // Fire listener events.
214         try {
215             for (IoServiceListener l : listeners) {
216                 try {
217                     l.sessionDestroyed(session);
218                 } catch (Throwable e) {
219                     ExceptionMonitor.getInstance().exceptionCaught(e);
220                 }
221             }
222         } finally {
223             // Fire a virtual service deactivation event for the last session of the connector.
224             if (session.getService() instanceof IoConnector) {
225                 boolean lastSession = false;
226                 synchronized (managedSessions) {
227                     lastSession = managedSessions.isEmpty();
228                 }
229                 if (lastSession) {
230                     fireServiceDeactivated();
231                 }
232             }
233         }
234     }
235 
236     private void disconnectSessions() {
237         if (!(service instanceof IoAcceptor)) {
238             return;
239         }
240 
241         if (!((IoAcceptor) service).isCloseOnDeactivation()) {
242             return;
243         }
244 
245         Object lock = new Object();
246         IoFutureListener<IoFuture> listener = new LockNotifyingListener(lock);
247 
248         for (IoSession s : managedSessions.values()) {
249             s.close(true).addListener(listener);
250         }
251 
252         try {
253             synchronized (lock) {
254                 while (!managedSessions.isEmpty()) {
255                     lock.wait(500);
256                 }
257             }
258         } catch (InterruptedException ie) {
259             // Ignored
260         }
261     }
262 
263     private static class LockNotifyingListener implements IoFutureListener<IoFuture> {
264         private final Object lock;
265 
266         public LockNotifyingListener(Object lock) {
267             this.lock = lock;
268         }
269 
270         public void operationComplete(IoFuture future) {
271             synchronized (lock) {
272                 lock.notifyAll();
273             }
274         }
275     }
276 }