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