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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tasks;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.LinkedHashSet;
033    import java.util.List;
034    
035    import org.opends.server.api.ClientConnection;
036    import org.opends.server.backends.task.Task;
037    import org.opends.server.backends.task.TaskState;
038    import org.opends.server.core.DirectoryServer;
039    import org.opends.server.types.Attribute;
040    import org.opends.server.types.AttributeType;
041    import org.opends.server.types.AttributeValue;
042    import org.opends.server.types.DirectoryException;
043    import org.opends.server.types.Entry;
044    import org.opends.server.types.Operation;
045    import org.opends.server.types.Privilege;
046    import org.opends.server.types.ResultCode;
047    
048    import static org.opends.server.config.ConfigConstants.*;
049    import static org.opends.messages.TaskMessages.*;
050    import static org.opends.server.util.StaticUtils.*;
051    
052    
053    
054    /**
055     * This class provides an implementation of a Directory Server task that can be
056     * used to stop the server.
057     */
058    public class ShutdownTask
059           extends Task
060    {
061    
062    
063    
064      // Indicates whether to use an exit code that indicates the server should be
065      // restarted.
066      private boolean restart;
067    
068      // The shutdown message that will be used.
069      private Message shutdownMessage;
070    
071    
072      /**
073       * {@inheritDoc}
074       */
075      public Message getDisplayName() {
076        return INFO_TASK_SHUTDOWN_NAME.get();
077      }
078    
079      /**
080       * Performs any task-specific initialization that may be required before
081       * processing can start.  This default implementation does not do anything,
082       * but subclasses may override it as necessary.  This method will be called at
083       * the time the task is scheduled, and therefore any failure in this method
084       * will be returned to the client.
085       *
086       * @throws  DirectoryException  If a problem occurs during initialization that
087       *                              should be returned to the client.
088       */
089      public void initializeTask()
090             throws DirectoryException
091      {
092        // See if the entry contains a shutdown message.  If so, then use it.
093        // Otherwise, use a default message.
094        Entry taskEntry = getTaskEntry();
095    
096        restart         = false;
097        shutdownMessage = INFO_TASK_SHUTDOWN_DEFAULT_MESSAGE.get(
098            String.valueOf(taskEntry.getDN()));
099    
100        AttributeType attrType =
101             DirectoryServer.getAttributeType(ATTR_SHUTDOWN_MESSAGE, true);
102        List<Attribute> attrList = taskEntry.getAttribute(attrType);
103        if ((attrList != null) && (attrList.size() > 0))
104        {
105          Attribute attr = attrList.get(0);
106          LinkedHashSet<AttributeValue> values = attr.getValues();
107          if ((values != null) && (! values.isEmpty()))
108          {
109            String valueString = values.iterator().next().getStringValue();
110    
111            shutdownMessage = INFO_TASK_SHUTDOWN_CUSTOM_MESSAGE.get(
112                String.valueOf(taskEntry.getDN()), String.valueOf(valueString));
113          }
114        }
115    
116    
117        attrType = DirectoryServer.getAttributeType(ATTR_RESTART_SERVER, true);
118        attrList = taskEntry.getAttribute(attrType);
119        if ((attrList != null) && (attrList.size() > 0))
120        {
121          Attribute attr = attrList.get(0);
122          LinkedHashSet<AttributeValue> values = attr.getValues();
123          if ((values != null) && (! values.isEmpty()))
124          {
125            String valueString =
126                 toLowerCase(values.iterator().next().getStringValue());
127    
128            restart = (valueString.equals("true") || valueString.equals("yes") ||
129                       valueString.equals("on") || valueString.equals("1"));
130          }
131        }
132    
133    
134        // If the client connection is available, then make sure the associated
135        // client has either the SERVER_SHUTDOWN or SERVER_RESTART privilege, based
136        // on the appropriate action.
137        Operation operation = getOperation();
138        if (operation != null)
139        {
140          ClientConnection clientConnection = operation.getClientConnection();
141          if (restart)
142          {
143            if (! clientConnection.hasPrivilege(Privilege.SERVER_RESTART,
144                                                operation))
145            {
146              Message message =
147                  ERR_TASK_SHUTDOWN_INSUFFICIENT_RESTART_PRIVILEGES.get();
148              throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
149                                           message);
150            }
151          }
152          else
153          {
154            if (! clientConnection.hasPrivilege(Privilege.SERVER_SHUTDOWN,
155                                                operation))
156            {
157              Message message =
158                  ERR_TASK_SHUTDOWN_INSUFFICIENT_SHUTDOWN_PRIVILEGES.get();
159              throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
160                                           message);
161            }
162          }
163        }
164      }
165    
166    
167    
168      /**
169       * Performs the actual core processing for this task.  This method should not
170       * return until all processing associated with this task has completed.
171       *
172       * @return  The final state to use for the task.
173       */
174      public TaskState runTask()
175      {
176        // This is a unique case in that the shutdown cannot finish until this task
177        // is finished, but this task can't really be finished until the shutdown is
178        // complete.  To work around this catch-22, we'll spawn a separate thread
179        // that will be responsible for really invoking the shutdown and then this
180        // method will return.  We'll have to use different types of threads
181        // depending on whether we're doing a restart or a shutdown.
182        if (restart)
183        {
184          RestartTaskThread restartThread = new RestartTaskThread(shutdownMessage);
185          restartThread.start();
186        }
187        else
188        {
189          ShutdownTaskThread shutdownThread =
190               new ShutdownTaskThread(shutdownMessage);
191          shutdownThread.start();
192        }
193    
194        return TaskState.COMPLETED_SUCCESSFULLY;
195      }
196    }
197