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