001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2015 ForgeRock AS.
016 */
017package org.opends.server.core;
018
019import org.forgerock.i18n.LocalizableMessage;
020
021import org.opends.server.api.ClientConnection;
022import org.opends.server.api.ConnectionHandler;
023import org.opends.server.api.DirectoryThread;
024import org.opends.server.api.ServerShutdownListener;
025import org.forgerock.i18n.slf4j.LocalizedLogger;
026import org.opends.server.types.DisconnectReason;
027
028import static org.opends.messages.CoreMessages.*;
029import static org.opends.server.util.StaticUtils.*;
030
031/**
032 * This class defines a thread that will be used to terminate client
033 * connections if they have been idle for too long.
034 */
035public class IdleTimeLimitThread
036       extends DirectoryThread
037       implements ServerShutdownListener
038{
039  /** The debug log tracer for this object. */
040  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
041
042
043
044  /** Shutdown monitor state. */
045  private volatile boolean shutdownRequested;
046  private final Object shutdownLock = new Object();
047
048
049
050  /**
051   * Creates a new instance of this idle time limit thread.
052   */
053  public IdleTimeLimitThread()
054  {
055    super("Idle Time Limit Thread");
056    setDaemon(true);
057
058    shutdownRequested = false;
059    DirectoryServer.registerShutdownListener(this);
060  }
061
062
063
064  /**
065   * Operates in a loop, teriminating any client connections that have been idle
066   * for too long.
067   */
068  public void run()
069  {
070    LocalizableMessage disconnectMessage = INFO_IDLETIME_LIMIT_EXCEEDED.get();
071
072    long sleepTime = 5000L;
073
074    while (! shutdownRequested)
075    {
076      try
077      {
078        synchronized (shutdownLock)
079        {
080          if (!shutdownRequested)
081          {
082            try
083            {
084              shutdownLock.wait(sleepTime);
085            }
086            catch (InterruptedException e)
087            {
088              // Server shutdown monitor may interrupt slow threads.
089              logger.traceException(e);
090              shutdownRequested = true;
091              break;
092            }
093          }
094        }
095
096        sleepTime = 5000L;
097        for (ConnectionHandler<?> ch : DirectoryServer.getConnectionHandlers())
098        {
099          for (ClientConnection c : ch.getClientConnections())
100          {
101            if (c==null) {
102              logger.trace("Null client connection found in \"" + ch.getConnectionHandlerName() + "\"");
103              continue;
104            }
105
106            long idleTime = c.getIdleTime();
107            if (idleTime > 0)
108            {
109              long idleTimeLimit = c.getIdleTimeLimit();
110              if (idleTimeLimit > 0)
111              {
112                if (idleTime >= idleTimeLimit)
113                {
114                  if (logger.isTraceEnabled())
115                  {
116                    logger.trace("Terminating client connection " +
117                                     c.getConnectionID() +
118                                     " due to the idle time limit");
119                  }
120
121                  try
122                  {
123                    c.disconnect(DisconnectReason.IDLE_TIME_LIMIT_EXCEEDED,
124                                 true, disconnectMessage);
125                  }
126                  catch (Exception e)
127                  {
128                    logger.traceException(e);
129
130                    logger.error(ERR_IDLETIME_DISCONNECT_ERROR, c.getConnectionID(),
131                            stackTraceToSingleLineString(e)
132                    );
133                  }
134                }
135                else
136                {
137                  long shouldSleepTime = idleTimeLimit - idleTime;
138                  if (shouldSleepTime < sleepTime)
139                  {
140                    sleepTime = shouldSleepTime;
141                  }
142                }
143              }
144            }
145          }
146        }
147      }
148      catch (Exception e)
149      {
150        logger.traceException(e);
151
152        logger.error(ERR_IDLETIME_UNEXPECTED_ERROR, stackTraceToSingleLineString(e));
153      }
154    }
155  }
156
157
158
159  /** {@inheritDoc} */
160  public String getShutdownListenerName()
161  {
162    return "Idle Time Limit Thread";
163  }
164
165
166
167  /** {@inheritDoc} */
168  public void processServerShutdown(LocalizableMessage reason)
169  {
170    synchronized (shutdownLock)
171    {
172      shutdownRequested = true;
173      shutdownLock.notifyAll();
174    }
175  }
176}
177