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 <a href="http://mina.apache.org">Apache MINA Project</a>
41   */
42  public class IoServiceListenerSupport {
43      /**
44       * The {@link IoService} that this instance manages.
45       */
46      private final IoService service;
47  
48      /**
49       * A list of {@link IoServiceListener}s.
50       */
51      private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();
52  
53      /**
54       * Tracks managed sessions.
55       */
56      private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long, IoSession>();
57  
58      /**
59       * Read only version of {@link #managedSessions}.
60       */
61      private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap(managedSessions);
62  
63      private final AtomicBoolean activated = new AtomicBoolean();
64      private volatile long activationTime;
65      private volatile int largestManagedSessionCount;
66      private volatile long cumulativeManagedSessionCount;
67  
68      /**
69       * Creates a new instance.
70       */
71      public IoServiceListenerSupport(IoService service) {
72          if (service == null) {
73              throw new NullPointerException("service");
74          }
75          this.service = service;
76      }
77  
78      /**
79       * Adds a new listener.
80       */
81      public void add(IoServiceListener listener) {
82          listeners.add(listener);
83      }
84  
85      /**
86       * Removes an existing listener.
87       */
88      public void remove(IoServiceListener listener) {
89          listeners.remove(listener);
90      }
91  
92      public long getActivationTime() {
93          return activationTime;
94      }
95  
96      public Map<Long, IoSession> getManagedSessions() {
97          return readOnlyManagedSessions;
98      }
99  
100     public int getManagedSessionCount() {
101         return managedSessions.size();
102     }
103 
104     public int getLargestManagedSessionCount() {
105         return largestManagedSessionCount;
106     }
107 
108     public long getCumulativeManagedSessionCount() {
109         return cumulativeManagedSessionCount;
110     }
111 
112     public boolean isActive() {
113         return activated.get();
114     }
115 
116     /**
117      * Calls {@link IoServiceListener#serviceActivated(IoService)}
118      * for all registered listeners.
119      */
120     public void fireServiceActivated() {
121         if (!activated.compareAndSet(false, true)) {
122             return;
123         }
124 
125         activationTime = System.currentTimeMillis();
126 
127         for (IoServiceListener l : listeners) {
128             try {
129                 l.serviceActivated(service);
130             } catch (Throwable e) {
131                 ExceptionMonitor.getInstance().exceptionCaught(e);
132             }
133         }
134     }
135 
136     /**
137      * Calls {@link IoServiceListener#serviceDeactivated(IoService)}
138      * for all registered listeners.
139      */
140     public void fireServiceDeactivated() {
141         if (!activated.compareAndSet(true, false)) {
142             return;
143         }
144 
145         try {
146             for (IoServiceListener l : listeners) {
147                 try {
148                     l.serviceDeactivated(service);
149                 } catch (Throwable e) {
150                     ExceptionMonitor.getInstance().exceptionCaught(e);
151                 }
152             }
153         } finally {
154             disconnectSessions();
155         }
156     }
157 
158     /**
159      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
160      */
161     public void fireSessionCreated(IoSession session) {
162         boolean firstSession = false;
163         if (session.getService() instanceof IoConnector) {
164             synchronized (managedSessions) {
165                 firstSession = managedSessions.isEmpty();
166             }
167         }
168 
169         // If already registered, ignore.
170         if (managedSessions.putIfAbsent(Long.valueOf(session.getId()), session) != null) {
171             return;
172         }
173 
174         // If the first connector session, fire a virtual service activation event.
175         if (firstSession) {
176             fireServiceActivated();
177         }
178 
179         // Fire session events.
180         IoFilterChain filterChain = session.getFilterChain(); 
181         filterChain.fireSessionCreated();
182         filterChain.fireSessionOpened();
183 
184         int managedSessionCount = managedSessions.size();
185         if (managedSessionCount > largestManagedSessionCount) {
186             largestManagedSessionCount = managedSessionCount;
187         }
188         cumulativeManagedSessionCount ++;
189 
190         // Fire listener events.
191         for (IoServiceListener l : listeners) {
192             try {
193                 l.sessionCreated(session);
194             } catch (Throwable e) {
195                 ExceptionMonitor.getInstance().exceptionCaught(e);
196             }
197         }
198     }
199 
200     /**
201      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
202      */
203     public void fireSessionDestroyed(IoSession session) {
204         // Try to remove the remaining empty session set after removal.
205         if (managedSessions.remove(Long.valueOf(session.getId())) == null) {
206             return;
207         }
208 
209         // Fire session events.
210         session.getFilterChain().fireSessionClosed();
211 
212         // Fire listener events.
213         try {
214             for (IoServiceListener l : listeners) {
215                 try {
216                     l.sessionDestroyed(session);
217                 } catch (Throwable e) {
218                     ExceptionMonitor.getInstance().exceptionCaught(e);
219                 }
220             }
221         } finally {
222             // Fire a virtual service deactivation event for the last session of the connector.
223             if (session.getService() instanceof IoConnector) {
224                 boolean lastSession = false;
225                 synchronized (managedSessions) {
226                     lastSession = managedSessions.isEmpty();
227                 }
228                 if (lastSession) {
229                     fireServiceDeactivated();
230                 }
231             }
232         }
233     }
234 
235     private void disconnectSessions() {
236         if (!(service instanceof IoAcceptor)) {
237             return;
238         }
239 
240         if (!((IoAcceptor) service).isCloseOnDeactivation()) {
241             return;
242         }
243 
244         Object lock = new Object();
245         IoFutureListener<IoFuture> listener = new LockNotifyingListener(lock);
246 
247         for (IoSession s : managedSessions.values()) {
248             s.close(true).addListener(listener);
249         }
250 
251         try {
252             synchronized (lock) {
253                 while (!managedSessions.isEmpty()) {
254                     lock.wait(500);
255                 }
256             }
257         } catch (InterruptedException ie) {
258             // Ignored
259         }
260     }
261 
262     private static class LockNotifyingListener implements IoFutureListener<IoFuture> {
263         private final Object lock;
264 
265         public LockNotifyingListener(Object lock) {
266             this.lock = lock;
267         }
268 
269         public void operationComplete(IoFuture future) {
270             synchronized (lock) {
271                 lock.notifyAll();
272             }
273         }
274     }
275 }