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 2006-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.tasks;
028
029
030
031import static org.opends.messages.TaskMessages.*;
032import static org.opends.server.config.ConfigConstants.*;
033import static org.opends.server.util.StaticUtils.*;
034
035import java.util.List;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.opends.server.api.ClientConnection;
039import org.opends.server.backends.task.Task;
040import org.opends.server.backends.task.TaskState;
041import org.opends.server.core.DirectoryServer;
042import org.opends.server.types.Attribute;
043import org.opends.server.types.AttributeType;
044import org.opends.server.types.DirectoryException;
045import org.opends.server.types.Entry;
046import org.opends.server.types.Operation;
047import org.opends.server.types.Privilege;
048import org.forgerock.opendj.ldap.ResultCode;
049
050
051
052/**
053 * This class provides an implementation of a Directory Server task that can be
054 * used to stop the server.
055 */
056public class ShutdownTask
057       extends Task
058{
059
060
061
062  /**
063   * Indicates whether to use an exit code that indicates the server should be
064   * restarted.
065   */
066  private boolean restart;
067
068  /** The shutdown message that will be used. */
069  private LocalizableMessage shutdownMessage;
070
071
072  /** {@inheritDoc} */
073  public LocalizableMessage getDisplayName() {
074    return INFO_TASK_SHUTDOWN_NAME.get();
075  }
076
077  /**
078   * Performs any task-specific initialization that may be required before
079   * processing can start.  This default implementation does not do anything,
080   * but subclasses may override it as necessary.  This method will be called at
081   * the time the task is scheduled, and therefore any failure in this method
082   * will be returned to the client.
083   *
084   * @throws  DirectoryException  If a problem occurs during initialization that
085   *                              should be returned to the client.
086   */
087  public void initializeTask()
088         throws DirectoryException
089  {
090    // See if the entry contains a shutdown message.  If so, then use it.
091    // Otherwise, use a default message.
092    Entry taskEntry = getTaskEntry();
093
094    restart         = false;
095    shutdownMessage = INFO_TASK_SHUTDOWN_DEFAULT_MESSAGE.get(taskEntry.getName());
096
097    AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(ATTR_SHUTDOWN_MESSAGE);
098    List<Attribute> attrList = taskEntry.getAttribute(attrType);
099    if (attrList != null && !attrList.isEmpty())
100    {
101      Attribute attr = attrList.get(0);
102      if (!attr.isEmpty())
103      {
104        String valueString = attr.iterator().next().toString();
105        shutdownMessage = INFO_TASK_SHUTDOWN_CUSTOM_MESSAGE.get(taskEntry.getName(), valueString);
106      }
107    }
108
109
110    attrType = DirectoryServer.getAttributeTypeOrDefault(ATTR_RESTART_SERVER);
111    attrList = taskEntry.getAttribute(attrType);
112    if (attrList != null && !attrList.isEmpty())
113    {
114      Attribute attr = attrList.get(0);
115      if (!attr.isEmpty())
116      {
117        String valueString = toLowerCase(attr.iterator().next().toString());
118        restart = valueString.equals("true") || valueString.equals("yes")
119            || valueString.equals("on") || valueString.equals("1");
120      }
121    }
122
123
124    // If the client connection is available, then make sure the associated
125    // client has either the SERVER_SHUTDOWN or SERVER_RESTART privilege, based
126    // on the appropriate action.
127    Operation operation = getOperation();
128    if (operation != null)
129    {
130      ClientConnection clientConnection = operation.getClientConnection();
131      if (restart)
132      {
133        if (! clientConnection.hasPrivilege(Privilege.SERVER_RESTART,
134                                            operation))
135        {
136          LocalizableMessage message =
137              ERR_TASK_SHUTDOWN_INSUFFICIENT_RESTART_PRIVILEGES.get();
138          throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
139                                       message);
140        }
141      }
142      else
143      {
144        if (! clientConnection.hasPrivilege(Privilege.SERVER_SHUTDOWN,
145                                            operation))
146        {
147          LocalizableMessage message =
148              ERR_TASK_SHUTDOWN_INSUFFICIENT_SHUTDOWN_PRIVILEGES.get();
149          throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
150                                       message);
151        }
152      }
153    }
154  }
155
156
157
158  /**
159   * Performs the actual core processing for this task.  This method should not
160   * return until all processing associated with this task has completed.
161   *
162   * @return  The final state to use for the task.
163   */
164  public TaskState runTask()
165  {
166    // This is a unique case in that the shutdown cannot finish until this task
167    // is finished, but this task can't really be finished until the shutdown is
168    // complete.  To work around this catch-22, we'll spawn a separate thread
169    // that will be responsible for really invoking the shutdown and then this
170    // method will return.  We'll have to use different types of threads
171    // depending on whether we're doing a restart or a shutdown.
172    boolean configuredAsService =
173      DirectoryServer.isRunningAsWindowsService();
174    if (configuredAsService && !restart)
175    {
176      ShutdownTaskThread shutdownThread =
177        new ShutdownTaskThread(shutdownMessage)
178      {
179        public void run()
180        {
181          org.opends.server.tools.StopWindowsService.main(new String[]{});
182        }
183      };
184      shutdownThread.start();
185    }
186    else if (restart)
187    {
188      // Since the process will not be killed, we can proceed exactly the same
189      // way with or without windows service configured.
190      RestartTaskThread restartThread = new RestartTaskThread(shutdownMessage);
191      restartThread.start();
192    }
193    else
194    {
195      ShutdownTaskThread shutdownThread =
196           new ShutdownTaskThread(shutdownMessage);
197      shutdownThread.start();
198    }
199
200    return TaskState.COMPLETED_SUCCESSFULLY;
201  }
202}
203