blob: c5a943f6067effa804f129be293670fc04134f05 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.glassfish.common;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.modules.glassfish.tooling.GlassFishIdeException;
import org.netbeans.modules.glassfish.tooling.GlassFishStatus;
import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.OFFLINE;
import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.ONLINE;
import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.SHUTDOWN;
import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.STARTUP;
import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.UNKNOWN;
import org.netbeans.modules.glassfish.tooling.TaskEvent;
import org.netbeans.modules.glassfish.tooling.TaskState;
import org.netbeans.modules.glassfish.tooling.TaskStateListener;
import org.netbeans.modules.glassfish.tooling.admin.CommandGetProperty;
import org.netbeans.modules.glassfish.tooling.admin.CommandRestartDAS;
import org.netbeans.modules.glassfish.tooling.admin.CommandSetProperty;
import org.netbeans.modules.glassfish.tooling.admin.CommandStopDAS;
import org.netbeans.modules.glassfish.tooling.admin.ResultMap;
import org.netbeans.modules.glassfish.tooling.admin.ResultString;
import org.netbeans.modules.glassfish.tooling.data.GlassFishServerStatus;
import org.netbeans.modules.glassfish.tooling.utils.NetUtils;
import org.netbeans.modules.glassfish.tooling.utils.ServerUtils;
import static org.netbeans.modules.glassfish.common.BasicTask.START_TIMEOUT;
import static org.netbeans.modules.glassfish.common.BasicTask.STOP_TIMEOUT;
import static org.netbeans.modules.glassfish.common.BasicTask.TIMEUNIT;
import static org.netbeans.modules.glassfish.common.GlassFishState.getStatus;
import org.netbeans.modules.glassfish.spi.CommandFactory;
import org.netbeans.modules.glassfish.spi.GlassfishModule;
import org.netbeans.modules.glassfish.spi.GlassfishModule.ServerState;
import org.openide.util.NbBundle;
/**
*
* @author Peter Williams
* @author Vince Kraemer
*/
public class RestartTask extends BasicTask<TaskState> {
////////////////////////////////////////////////////////////////////////////
// Class attributes //
////////////////////////////////////////////////////////////////////////////
/** Local logger. */
private static final Logger LOGGER = GlassFishLogger.get(RestartTask.class);
////////////////////////////////////////////////////////////////////////////
// Instance attributes //
////////////////////////////////////////////////////////////////////////////
/** How long to wait after stopping server to let OS clean up resources. */
@SuppressWarnings("FieldNameHidesFieldInSuperclass")
private static final int RESTART_DELAY = 5000;
/** Common support object for the server instance being restarted. */
private final CommonServerSupport support;
////////////////////////////////////////////////////////////////////////////
// Constructors //
////////////////////////////////////////////////////////////////////////////
/**
* Constructs an instance of asynchronous GlassFish server restart command
* execution support object.
* <p/>
* @param support Common support object for the server instance being
* restarted
* @param stateListener State monitor to track restart progress.
*/
public RestartTask(CommonServerSupport support, TaskStateListener... stateListener) {
super(support.getInstance(), stateListener);
this.support = support;
}
////////////////////////////////////////////////////////////////////////////
// Methods //
////////////////////////////////////////////////////////////////////////////
/**
* Start local server that is offline.
* <p/>
* @return State change request about offline remote server start request.
*/
private StateChange localOfflineStart() {
Future<TaskState> startTask
= support.startServer(null, ServerState.RUNNING);
TaskState startResult = TaskState.FAILED;
try {
startResult = startTask.get(START_TIMEOUT, TIMEUNIT);
} catch (Exception ex) {
LOGGER.log(Level.FINER,
ex.getLocalizedMessage(), ex);
}
if (startResult == TaskState.FAILED) {
return new StateChange(this,
TaskState.FAILED, TaskEvent.CMD_FAILED,
"RestartTask.localOfflineStart.failed", instanceName);
}
return new StateChange(this,
TaskState.COMPLETED, TaskEvent.CMD_COMPLETED,
"RestartTask.localOfflineStart.completed", instanceName);
}
/**
* Start remote server that is offline.
* <p/>
* This operation is not possible and will always fail.
* <p/>
* @return State change request about offline remote server start request.
*/
private StateChange remoteOfflineStart() {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.remoteOfflineStart.failed", instanceName);
}
/**
* Wait for local server currently shutting down and start it up.
* <p/>
* @return State change request about local server (that is shutting down)
* start request.
*/
private StateChange localShutdownStart() {
StateChange stateChange = waitShutDown();
if (stateChange != null) {
return stateChange;
}
GlassFishServerStatus status = getStatus(instance);
switch(status.getStatus()) {
case UNKNOWN: case ONLINE: case SHUTDOWN: case STARTUP:
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.localShutdownStart.notOffline",
instanceName);
default:
if (!ServerUtils.isAdminPortListening(
instance, NetUtils.PORT_CHECK_TIMEOUT)) {
return localOfflineStart();
} else {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.localShutdownStart.portOccupied",
instanceName);
}
}
}
/**
* Wait for remote server currently shutting down and start it up.
* <p/>
* This operation is not possible and will always fail.
* <p/>
* @return State change request about remote server (that is shutting down)
* start request.
*/
private StateChange remoteShutdownStart() {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.remoteShutdownStart.failed", instanceName);
}
/**
* Wait for server to start up.
* <p/>
* @return State change request.
*/
private StateChange startupWait() {
StartStateListener listener = prepareStartMonitoring(true);
if (listener == null) {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.startupWait.listenerError",
instanceName);
}
long start = System.currentTimeMillis();
try {
synchronized(listener) {
while (!listener.isWakeUp()
&& (System.currentTimeMillis()
- start < START_TIMEOUT)) {
listener.wait(System.currentTimeMillis() - start);
}
}
} catch (InterruptedException ie) {
LOGGER.log(Level.INFO, NbBundle.getMessage(RestartTask.class,
"RestartTask.startupWait.interruptedException",
new String[] {
instance.getName(), ie.getLocalizedMessage()}));
} finally {
GlassFishStatus.removeListener(instance, listener);
}
if (GlassFishState.isOnline(instance)) {
return new StateChange(this,
TaskState.COMPLETED, TaskEvent.CMD_COMPLETED,
"RestartTask.startupWait.completed", instanceName);
} else {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.startupWait.failed", instanceName);
}
}
/**
* Full restart of local online server.
* <p/>
* @return State change request.
*/
private StateChange localRestart() {
if (GlassFishStatus.shutdown(instance)) {
ResultString result = CommandStopDAS.stopDAS(instance);
if (result.getState() == TaskState.COMPLETED) {
return localShutdownStart();
} else {
// TODO: Reset server status monitoring
return new StateChange(this,
TaskState.FAILED, TaskEvent.CMD_FAILED,
"RestartTask.localRestart.cmdFailed", instanceName);
}
} else {
return new StateChange(this,
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.localRestart.failed", instanceName);
}
}
/**
* Update server debug options before restart.
*/
private boolean updateDebugOptions(final int debugPort) {
boolean updateResult = false;
try {
ResultMap<String, String> result
= CommandGetProperty.getProperties(instance,
"configs.config.server-config.java-config.debug-options");
if (result.getState() == TaskState.COMPLETED) {
Map<String, String> values = result.getValue();
if (values != null && !values.isEmpty()) {
CommandFactory commandFactory =
instance.getInstanceProvider().getCommandFactory();
String oldValue = values.get(
"configs.config.server-config.java-config.debug-options");
CommandSetProperty setCmd =
commandFactory.getSetPropertyCommand(
"configs.config.server-config.java-config.debug-options",
oldValue.replace("transport=dt_shmem", "transport=dt_socket").
replace("address=[^,]+", "address=" + debugPort));
try {
CommandSetProperty.setProperty(instance, setCmd);
updateResult = true;
} catch (GlassFishIdeException gfie) {
LOGGER.log(Level.INFO, debugPort + "", gfie);
}
}
}
} catch (GlassFishIdeException gfie) {
LOGGER.log(Level.INFO,
"Could not retrieve property from server.", gfie);
}
return updateResult;
}
/**
* Wait for debug port to become active.
* <p/>
* @return Value of <code>true</code> if port become active before timeout
* or <code>false</code> otherwise.
*/
@SuppressWarnings("SleepWhileInLoop")
private boolean vaitForDebugPort(final String host, final int port) {
boolean result = NetUtils.isPortListeningRemote(
host, port, NetUtils.PORT_CHECK_TIMEOUT);
if (!result) {
long tmStart = System.currentTimeMillis();
while (!result
&& System.currentTimeMillis() - tmStart
< START_ADMIN_PORT_TIMEOUT) {
try {
Thread.sleep(PORT_CHECK_IDLE);
} catch (InterruptedException ex) {}
result = NetUtils.isPortListeningRemote(
host, port, NetUtils.PORT_CHECK_TIMEOUT);
}
}
return result;
}
/**
* Full restart of remote online server.
* <p/>
* @return State change request.
*/
private StateChange remoteRestart() {
boolean debugMode = instance.getJvmMode() == GlassFishJvmMode.DEBUG;
// Wrong scenario as default.
boolean debugPortActive = true;
int debugPort = -1;
if (debugMode) {
debugPort = instance.getDebugPort();
debugMode = updateDebugOptions(debugPort);
debugPortActive = NetUtils.isPortListeningRemote(
instance.getHost(), debugPort, NetUtils.PORT_CHECK_TIMEOUT);
}
ResultString result
= CommandRestartDAS.restartDAS(instance, debugMode);
LogViewMgr.removeLog(instance);
LogViewMgr logger = LogViewMgr.getInstance(
instance.getProperty(GlassfishModule.URL_ATTR));
logger.stopReaders();
switch (result.getState()) {
case COMPLETED:
if (debugMode && !debugPortActive) {
vaitForDebugPort(instance.getHost(), debugPort);
waitStartUp(true, false);
// This probably won't be needed.
// } else {
// try {
// Thread.sleep(RESTART_DELAY);
// } catch (InterruptedException ex) {}
}
return new StateChange(this,
result.getState(), TaskEvent.CMD_COMPLETED,
"RestartTask.remoteRestart.completed", instanceName);
default:
return new StateChange(this,
result.getState(), TaskEvent.CMD_COMPLETED,
"RestartTask.remoteRestart.failed", new String[] {
instanceName, result.getValue()});
}
}
////////////////////////////////////////////////////////////////////////////
// ExecutorService call() Method //
////////////////////////////////////////////////////////////////////////////
/**
* Restart GlassFish server.
* <p/>
* Possible states are <code>UNKNOWN</code>, <code>OFFLINE</code>,
* <code>STARTUP</code>, <code>ONLINE</code> and <code>SHUTDOWN</code>:
* <p/>
* <code>UNKNOWN</code>: Do nothing. UI shall not allow restarting while
* server status is unknown.
* <code>OFFLINE</code>: Server is already offline, let's start it
* if administrator port is not occupied.
* <code>STARTUP</code>: We are already in the middle of startup process.
* Let's just wait for sever to start.
* <code>ONLINE</code>: Full restart is needed.
* <code>SHUTDOWN</code>: Shutdown process has already started, let's wait
* for it to finish. Server will be started after
* that.
*/
@Override
public TaskState call() {
GlassFishStatus state = GlassFishState.getStatus(instance).getStatus();
StateChange change;
switch (state) {
case UNKNOWN:
return fireOperationStateChanged(
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.call.unknownState", instanceName);
case OFFLINE:
change = instance.isRemote()
? remoteOfflineStart() : localOfflineStart();
return change.fireOperationStateChanged();
case STARTUP:
change = startupWait();
return change.fireOperationStateChanged();
case ONLINE:
change = instance.isRemote()
? remoteRestart() : localRestart();
return change.fireOperationStateChanged();
case SHUTDOWN:
change = instance.isRemote()
? remoteShutdownStart() : localShutdownStart();
return change.fireOperationStateChanged();
// This shall be unrechable, all states should have
// own case handlers.
default:
return fireOperationStateChanged(
TaskState.FAILED, TaskEvent.ILLEGAL_STATE,
"RestartTask.call.unknownState", instanceName);
}
}
/**
* Restart operation:
*
* RUNNING -> stop server
* start server
*
* STARTING -> wait for state == STOPPED or RUNNING.
*
* STOPPED -> start server
*
* STOPPING -> wait for state == STOPPED
* start server
*
* For all of the above, command succeeds if state == RUNNING at the end.
*
*/
@SuppressWarnings("SleepWhileInLoop")
// @Override
public TaskState call2() {
Logger.getLogger("glassfish").log(Level.FINEST,
"RestartTask.call() called on thread \"{0}\"",
Thread.currentThread().getName());
fireOperationStateChanged(TaskState.RUNNING, TaskEvent.CMD_RUNNING,
"MSG_RESTART_SERVER_IN_PROGRESS", instanceName);
//ServerState state = support.getServerState();
GlassFishStatus state = GlassFishState.getStatus(instance).getStatus();
if (state == GlassFishStatus.STARTUP) {
// wait for start to finish, we are done.
GlassFishStatus currentState = state;
int steps = (START_TIMEOUT / DELAY);
int count = 0;
while (currentState == GlassFishStatus.STARTUP && count++ < steps) {
try {
Thread.sleep(DELAY);
} catch (InterruptedException ex) {
Logger.getLogger("glassfish").log(Level.FINER,
ex.getLocalizedMessage(), ex);
}
currentState = GlassFishState.getStatus(instance).getStatus();
}
if (!GlassFishState.isOnline(instance)) {
return fireOperationStateChanged(TaskState.FAILED,
TaskEvent.CMD_FAILED,
"MSG_RESTART_SERVER_FAILED_WONT_START", instanceName);
}
} else {
boolean postStopDelay = true;
if (state == GlassFishStatus.ONLINE) {
Future<TaskState> stopTask = support.stopServer(null);
TaskState stopResult = TaskState.FAILED;
try {
stopResult = stopTask.get(STOP_TIMEOUT, TIMEUNIT);
} catch (Exception ex) {
Logger.getLogger("glassfish").log(Level.FINER,
ex.getLocalizedMessage(), ex);
}
if (stopResult == TaskState.FAILED) {
return fireOperationStateChanged(TaskState.FAILED,
TaskEvent.CMD_FAILED,
"MSG_RESTART_SERVER_FAILED_WONT_STOP",
instanceName);
}
} else if (state == GlassFishStatus.SHUTDOWN) {
// wait for server to stop.
GlassFishStatus currentState = state;
int steps = (STOP_TIMEOUT / DELAY);
int count = 0;
while (currentState == GlassFishStatus.SHUTDOWN && count++ < steps) {
try {
Thread.sleep(DELAY);
} catch (InterruptedException ex) {
Logger.getLogger("glassfish").log(Level.FINER,
ex.getLocalizedMessage(), ex);
}
currentState = GlassFishState.getStatus(instance).getStatus();
}
if (!GlassFishState.isOffline(instance)) {
return fireOperationStateChanged(TaskState.FAILED,
TaskEvent.CMD_FAILED,
"MSG_RESTART_SERVER_FAILED_WONT_STOP",
instanceName);
}
} else {
postStopDelay = false;
}
if (postStopDelay) {
// If we stopped the server (or it was already stopping), delay
// start for a few seconds to let system clean up ports.
support.setServerState(ServerState.STARTING);
try {
Thread.sleep(RESTART_DELAY);
} catch (InterruptedException ex) {
// ignore
}
}
// Server should be stopped. Start it.
Object o = support.setEnvironmentProperty(
GlassfishModule.JVM_MODE,
GlassfishModule.NORMAL_MODE, false);
if (GlassfishModule.PROFILE_MODE.equals(o)) {
support.setEnvironmentProperty(GlassfishModule.JVM_MODE,
GlassfishModule.NORMAL_MODE, false);
}
Future<TaskState> startTask = support.startServer(null, ServerState.RUNNING);
TaskState startResult = TaskState.FAILED;
try {
startResult = startTask.get(START_TIMEOUT, TIMEUNIT);
} catch (Exception ex) {
Logger.getLogger("glassfish").log(Level.FINER,
ex.getLocalizedMessage(), ex); // NOI18N
}
if (startResult == TaskState.FAILED) {
return fireOperationStateChanged(TaskState.FAILED,
TaskEvent.CMD_FAILED,
"MSG_RESTART_SERVER_FAILED_WONT_START",
instanceName);
}
if (!support.isRemote()
&& support.getServerState() != ServerState.RUNNING) {
return fireOperationStateChanged(TaskState.FAILED,
TaskEvent.CMD_FAILED,
"MSG_RESTART_SERVER_FAILED_REASON_UNKNOWN",
instanceName);
}
}
return fireOperationStateChanged(TaskState.COMPLETED,
TaskEvent.CMD_COMPLETED,
"MSG_SERVER_RESTARTED", instanceName);
}
}