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 class 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      /** The {@link IoService} that this instance manages. */
    44      private final IoService service;
    45
    46      /** A list of {@link IoServiceListener}s. */
    47      private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();
    48
    49      /** Tracks managed sessions. */
    50      private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long, IoSession>();
    51
    52      /**  Read only version of {@link #managedSessions}. */
    53      private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap(managedSessions);
    54
    55      private final AtomicBoolean activated = new AtomicBoolean();
    56
    57      /** Time this listenerSupport has been activated */
    58      private volatile long activationTime;
    59
    60      /** A counter used to store the maximum sessions we managed since the listenerSupport has been activated */
    61      private volatile int largestManagedSessionCount = 0;
    62
    63      /** A global counter to count the number of sessions managed since the start */
    64      private volatile long cumulativeManagedSessionCount = 0;
    65
    66      /**
    67       * Creates a new instance of the listenerSupport.
    68       *
    69       * @param service The associated IoService
    70       */
    71      public IoServiceListenerSupport(IoService service) {
    72          if (service == null) {
    73              throw new IllegalArgumentException("service");
    74          }
    75
    76          this.service = service;
    77      }
    78
    79      /**
    80       * Adds a new listener.
    81       *
    82       * @param listener The added listener
    83       */
    84      public void add(IoServiceListener listener) {
    85          if (listener != null) {
    86              listeners.add(listener);
    87          }
    88      }
    89
    90      /**
    91       * Removes an existing listener.
    92       *
    93       * @param listener The listener to remove
    94       */
    95      public void remove(IoServiceListener listener) {
    96          if (listener != null) {
    97              listeners.remove(listener);
    98          }
    99      }
    100
    101     /**
    102      * @return The time (in ms) this instance has been activated
    103      */
    104     public long getActivationTime() {
    105         return activationTime;
    106     }
    107
    108     public Map<Long, IoSession> getManagedSessions() {
    109         return readOnlyManagedSessions;
    110     }
    111
    112     public int getManagedSessionCount() {
    113         return managedSessions.size();
    114     }
    115
    116     /**
    117      * @return The largest number of managed session since the creation of this
    118      * listenerSupport
    119      */
    120     public int getLargestManagedSessionCount() {
    121         return largestManagedSessionCount;
    122     }
    123
    124     /**
    125      * @return The total number of sessions managed since the initilization of this
    126      * ListenerSupport
    127      */
    128     public long getCumulativeManagedSessionCount() {
    129         return cumulativeManagedSessionCount;
    130     }
    131
    132     /**
    133      * @return true if the instance is active
    134      */
    135     public boolean isActive() {
    136         return activated.get();
    137     }
    138
    139     /**
    140      * Calls {@link IoServiceListener#serviceActivated(IoService)}
    141      * for all registered listeners.
    142      */
    143     public void fireServiceActivated() {
    144         if (!activated.compareAndSet(false, true)) {
    145             // The instance is already active
    146             return;
    147         }
    148
    149         activationTime = System.currentTimeMillis();
    150
    151         // Activate all the listeners now
    152         for (IoServiceListener listener : listeners) {
    153             try {
    154                 listener.serviceActivated(service);
    155             } catch (Throwable e) {
    156                 ExceptionMonitor.getInstance().exceptionCaught(e);
    157             }
    158         }
    159     }
    160
    161     /**
    162      * Calls {@link IoServiceListener#serviceDeactivated(IoService)}
    163      * for all registered listeners.
    164      */
    165     public void fireServiceDeactivated() {
    166         if (!activated.compareAndSet(true, false)) {
    167             // The instance is already desactivated
    168             return;
    169         }
    170
    171         // Desactivate all the listeners
    172         try {
    173             for (IoServiceListener listener : listeners) {
    174                 try {
    175                     listener.serviceDeactivated(service);
    176                 } catch (Throwable e) {
    177                     ExceptionMonitor.getInstance().exceptionCaught(e);
    178                 }
    179             }
    180         } finally {
    181             disconnectSessions();
    182         }
    183     }
    184
    185     /**
    186      * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
    187      *
    188      * @param session The session which has been created
    189      */
    190     public void fireSessionCreated(IoSession session) {
    191         boolean firstSession = false;
    192
    193         if (session.getService() instanceof IoConnector) {
    194             synchronized (managedSessions) {
    195                 firstSession = managedSessions.isEmpty();
    196             }
    197         }
    198
    199         // If already registered, ignore.
    200         if (managedSessions.putIfAbsent(session.getId(), session) != null) {
    201             return;
    202         }
    203
    204         // If the first connector session, fire a virtual service activation event.
    205         if (firstSession) {
    206             fireServiceActivated();
    207         }
    208
    209         // Fire session events.
    210         IoFilterChain filterChain = session.getFilterChain();
    211         filterChain.fireSessionCreated();
    212         filterChain.fireSessionOpened();
    213
    214         int managedSessionCount = managedSessions.size();
    215
    216         if (managedSessionCount > largestManagedSessionCount) {
    217             largestManagedSessionCount = managedSessionCount;
    218         }
    219
    220         cumulativeManagedSessionCount++;
    221
    222         // Fire listener events.
    223         for (IoServiceListener l : listeners) {
    224             try {
    225                 l.sessionCreated(session);
    226             } catch (Throwable e) {
    227                 ExceptionMonitor.getInstance().exceptionCaught(e);
    228             }
    229         }
    230     }
    231
    232     /**
    233      * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners.
    234      *
    235      * @param session The session which has been destroyed
    236      */
    237     public void fireSessionDestroyed(IoSession session) {
    238         // Try to remove the remaining empty session set after removal.
    239         if (managedSessions.remove(session.getId()) == null) {
    240             return;
    241         }
    242
    243         // Fire session events.
    244         session.getFilterChain().fireSessionClosed();
    245
    246         // Fire listener events.
    247         try {
    248             for (IoServiceListener l : listeners) {
    249                 try {
    250                     l.sessionDestroyed(session);
    251                 } catch (Throwable e) {
    252                     ExceptionMonitor.getInstance().exceptionCaught(e);
    253                 }
    254             }
    255         } finally {
    256             // Fire a virtual service deactivation event for the last session of the connector.
    257             if (session.getService() instanceof IoConnector) {
    258                 boolean lastSession = false;
    259
    260                 synchronized (managedSessions) {
    261                     lastSession = managedSessions.isEmpty();
    262                 }
    263
    264                 if (lastSession) {
    265                     fireServiceDeactivated();
    266                 }
    267             }
    268         }
    269     }
    270
    271     /**
    272      * Close all the sessions
    273      * TODO disconnectSessions.
    274      *
    275      */
    276     private void disconnectSessions() {
    277         if (!(service instanceof IoAcceptor)) {
    278             // We don't disconnect sessions for anything but an Acceptor
    279             return;
    280         }
    281
    282         if (!((IoAcceptor) service).isCloseOnDeactivation()) {
    283             return;
    284         }
    285
    286         Object lock = new Object();
    287         IoFutureListener<IoFuture> listener = new LockNotifyingListener(lock);
    288
    289         for (IoSession s : managedSessions.values()) {
    290             s.close(true).addListener(listener);
    291         }
    292
    293         try {
    294             synchronized (lock) {
    295                 while (!managedSessions.isEmpty()) {
    296                     lock.wait(500);
    297                 }
    298             }
    299         } catch (InterruptedException ie) {
    300             // Ignored
    301         }
    302     }
    303
    304     /**
    305      * A listener in charge of releasing the lock when the close has been completed
    306      */
    307     private static class LockNotifyingListener implements IoFutureListener<IoFuture> {
    308         private final Object lock;
    309
    310         public LockNotifyingListener(Object lock) {
    311             this.lock = lock;
    312         }
    313
    314         public void operationComplete(IoFuture future) {
    315             synchronized (lock) {
    316                 lock.notifyAll();
    317             }
    318         }
    319     }
    320 }