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