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-2016 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  /** Shutdown monitor state. */
043  private volatile boolean shutdownRequested;
044  private final Object shutdownLock = new Object();
045
046  /** Creates a new instance of this idle time limit thread. */
047  public IdleTimeLimitThread()
048  {
049    super("Idle Time Limit Thread");
050    setDaemon(true);
051
052    shutdownRequested = false;
053    DirectoryServer.registerShutdownListener(this);
054  }
055
056  /** Operates in a loop, teriminating any client connections that have been idle for too long. */
057  @Override
058  public void run()
059  {
060    LocalizableMessage disconnectMessage = INFO_IDLETIME_LIMIT_EXCEEDED.get();
061
062    long sleepTime = 5000L;
063
064    while (! shutdownRequested)
065    {
066      try
067      {
068        synchronized (shutdownLock)
069        {
070          if (!shutdownRequested)
071          {
072            try
073            {
074              shutdownLock.wait(sleepTime);
075            }
076            catch (InterruptedException e)
077            {
078              // Server shutdown monitor may interrupt slow threads.
079              logger.traceException(e);
080              shutdownRequested = true;
081              break;
082            }
083          }
084        }
085
086        sleepTime = 5000L;
087        for (ConnectionHandler<?> ch : DirectoryServer.getConnectionHandlers())
088        {
089          for (ClientConnection c : ch.getClientConnections())
090          {
091            if (c==null) {
092              logger.trace("Null client connection found in \"" + ch.getConnectionHandlerName() + "\"");
093              continue;
094            }
095
096            long idleTime = c.getIdleTime();
097            if (idleTime > 0)
098            {
099              long idleTimeLimit = c.getIdleTimeLimit();
100              if (idleTimeLimit > 0)
101              {
102                if (idleTime >= idleTimeLimit)
103                {
104                  if (logger.isTraceEnabled())
105                  {
106                    logger.trace("Terminating client connection " +
107                                     c.getConnectionID() +
108                                     " due to the idle time limit");
109                  }
110
111                  try
112                  {
113                    c.disconnect(DisconnectReason.IDLE_TIME_LIMIT_EXCEEDED,
114                                 true, disconnectMessage);
115                  }
116                  catch (Exception e)
117                  {
118                    logger.traceException(e);
119
120                    logger.error(ERR_IDLETIME_DISCONNECT_ERROR, c.getConnectionID(),
121                            stackTraceToSingleLineString(e)
122                    );
123                  }
124                }
125                else
126                {
127                  long shouldSleepTime = idleTimeLimit - idleTime;
128                  if (shouldSleepTime < sleepTime)
129                  {
130                    sleepTime = shouldSleepTime;
131                  }
132                }
133              }
134            }
135          }
136        }
137      }
138      catch (Exception e)
139      {
140        logger.traceException(e);
141
142        logger.error(ERR_IDLETIME_UNEXPECTED_ERROR, stackTraceToSingleLineString(e));
143      }
144    }
145  }
146
147  @Override
148  public String getShutdownListenerName()
149  {
150    return "Idle Time Limit Thread";
151  }
152
153  @Override
154  public void processServerShutdown(LocalizableMessage reason)
155  {
156    synchronized (shutdownLock)
157    {
158      shutdownRequested = true;
159      shutdownLock.notifyAll();
160    }
161  }
162}