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