/*
* 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.apache.brooklyn.entity.software.base;
import java.util.Map;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
import org.apache.brooklyn.api.mgmt.TaskAdaptable;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.StartableMethods;
import org.apache.brooklyn.entity.software.base.SoftwareProcess.ChildStartableMode;
import org.apache.brooklyn.entity.software.base.SoftwareProcess.RestartSoftwareParameters;
import org.apache.brooklyn.entity.software.base.SoftwareProcess.RestartSoftwareParameters.RestartMachineMode;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
import com.google.common.base.Supplier;
/** Thin shim delegating to driver to do start/stop/restart, wrapping as tasks,
* with common code pulled up to {@link MachineLifecycleEffectorTasks} for non-driver usage
* @since 0.6.0 */
@Beta
public class SoftwareProcessDriverLifecycleEffectorTasks extends MachineLifecycleEffectorTasks {
private static final Logger log = LoggerFactory.getLogger(SoftwareProcessDriverLifecycleEffectorTasks.class);
public void restart(ConfigBag parameters) {
RestartMachineMode isRestartMachine = parameters.get(RestartSoftwareParameters.RESTART_MACHINE_TYPED);
if (isRestartMachine==null)
isRestartMachine=RestartMachineMode.AUTO;
if (isRestartMachine==RestartMachineMode.AUTO)
isRestartMachine = getDefaultRestartStopsMachine() ? RestartMachineMode.TRUE : RestartMachineMode.FALSE;
if (isRestartMachine==RestartMachineMode.TRUE) {
log.debug("restart of "+entity()+" requested be applied at machine level");
super.restart(parameters);
return;
}
DynamicTasks.queue("pre-restart", new PreRestartTask());
log.debug("restart of "+entity()+" appears to have driver and hostname - doing driver-level restart");
entity().getDriver().restart();
restartChildren(parameters);
DynamicTasks.queue("post-restart", new PostRestartTask());
}
private class PreRestartTask implements Runnable {
@Override
public void run() {
preRestartCustom();
}
}
private class PostRestartTask implements Runnable {
@Override
public void run() {
postStartCustom();
postRestartCustom();
ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING);
}
}
@Override
protected boolean getDefaultRestartStopsMachine() {
if (entity().getDriver() == null) {
log.debug("restart of "+entity()+" has no driver - doing machine-level restart");
return true;
}
if (Strings.isEmpty(entity().getAttribute(Attributes.HOSTNAME))) {
log.debug("restart of "+entity()+" has no hostname - doing machine-level restart");
return true;
}
return false;
}
@Override
protected SoftwareProcessImpl entity() {
return (SoftwareProcessImpl) super.entity();
}
@Override
protected Map<String, Object> obtainProvisioningFlags(final MachineProvisioningLocation<?> location) {
return entity().obtainProvisioningFlags(location);
}
@Override
protected void preStartCustom(MachineLocation machine) {
entity().initDriver(machine);
// Note: must only apply config-sensors after adding to locations and creating driver;
// otherwise can't do things like acquire free port from location
// or allowing driver to set up ports; but must be careful in init not to block on these!
super.preStartCustom(machine);
entity().preStart();
}
/** returns how children startables should be handled (reporting none for efficiency if there are no children) */
protected ChildStartableMode getChildrenStartableModeEffective() {
if (entity().getChildren().isEmpty()) return ChildStartableMode.NONE;
ChildStartableMode result = entity().getConfig(SoftwareProcess.CHILDREN_STARTABLE_MODE);
if (result!=null) return result;
return ChildStartableMode.NONE;
}
@Override
protected String startProcessesAtMachine(final Supplier<MachineLocation> machineS) {
ChildStartableMode mode = getChildrenStartableModeEffective();
TaskAdaptable<?> children = null;
if (!mode.isDisabled) {
children = StartableMethods.startingChildren(entity(), machineS.get());
// submit rather than queue so it runs in parallel
// (could also wrap as parallel task with driver.start() -
// but only benefit is that child starts show as child task,
// rather than bg task, so not worth the code complexity)
if (!mode.isLate) Entities.submit(entity(), children);
}
entity().getDriver().start();
String result = "Started with driver "+entity().getDriver();
if (!mode.isDisabled) {
if (mode.isLate) {
DynamicTasks.waitForLast();
if (mode.isBackground) {
Entities.submit(entity(), children);
} else {
// when running foreground late, there is no harm here in queueing
DynamicTasks.queue(children);
}
}
if (!mode.isBackground) children.asTask().getUnchecked();
result += "; children started "+mode;
}
return result;
}
@Override
protected void postStartCustom() {
entity().postDriverStart();
if (entity().connectedSensors) {
// many impls aren't idempotent - though they should be!
log.debug("skipping connecting sensors for "+entity()+" in driver-tasks postStartCustom because already connected (e.g. restarting)");
} else {
log.debug("connecting sensors for "+entity()+" in driver-tasks postStartCustom because already connected (e.g. restarting)");
entity().connectSensors();
}
entity().waitForServiceUp();
entity().postStart();
}
@Override
protected void preStopConfirmCustom() {
super.preStopConfirmCustom();
entity().preStopConfirmCustom();
}
@Override
protected void preStopCustom() {
super.preStopCustom();
entity().preStop();
}
@Override
protected void preRestartCustom() {
super.preRestartCustom();
entity().preRestart();
}
@Override
protected void postRestartCustom() {
super.postRestartCustom();
entity().postRestart();
}
@Override
protected String stopProcessesAtMachine() {
String result;
ChildStartableMode mode = getChildrenStartableModeEffective();
TaskAdaptable<?> children = null;
Exception childException = null;
if (!mode.isDisabled) {
children = StartableMethods.stoppingChildren(entity());
if (mode.isBackground || !mode.isLate) Entities.submit(entity(), children);
else {
DynamicTasks.queue(children);
try {
DynamicTasks.waitForLast();
} catch (Exception e) {
childException = e;
}
}
}
if (entity().getDriver() != null) {
entity().getDriver().stop();
result = "Driver stop completed";
} else {
result = "No driver (nothing to do here)";
}
if (!mode.isDisabled && !mode.isBackground) {
try {
children.asTask().get();
} catch (Exception e) {
childException = e;
log.debug("Error stopping children; continuing and will rethrow if no other errors", e);
}
}
if (childException!=null)
throw new IllegalStateException(result+"; but error stopping child: "+childException, childException);
return result;
}
@Override
protected void postStopCustom() {
super.postStopCustom();
entity().postStop();
}
}