/*
* Copyright 2013 the original author or authors.
*
* Licensed 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.springframework.yarn.am;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.ContainerStatus;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.springframework.yarn.am.allocate.AbstractAllocator;
import org.springframework.yarn.am.monitor.ContainerAware;
import org.springframework.yarn.am.monitor.ContainerMonitor;
/**
* A simple application master implementation which will allocate
* and launch a number of containers, monitor container statuses
* and finally exit the application by sending corresponding
* message back to resource manager. This implementation also
* is able to handle failed containers.
*
* @author Janne Valkealahti
*
*/
public class StaticEventingAppmaster extends AbstractEventingAppmaster implements YarnAppmaster {
private static final Log log = LogFactory.getLog(StaticEventingAppmaster.class);
/** Static count of containers to run */
private int containerCount;
@Override
public void submitApplication() {
log.info("Submitting application");
registerAppmaster();
start();
if(getAllocator() instanceof AbstractAllocator) {
((AbstractAllocator)getAllocator()).setApplicationAttemptId(getApplicationAttemptId());
}
// TODO: do exception safe parse
containerCount = Integer.parseInt(getParameters().getProperty(AppmasterConstants.CONTAINER_COUNT, "1"));
log.info("count: " + containerCount);
getAllocator().allocateContainers(containerCount);
}
@Override
protected void onContainerAllocated(Container container) {
if (getMonitor() instanceof ContainerAware) {
((ContainerAware)getMonitor()).onContainer(Arrays.asList(container));
}
getLauncher().launchContainer(container, getCommands());
}
@Override
protected void onContainerLaunched(Container container) {
if (getMonitor() instanceof ContainerAware) {
((ContainerAware)getMonitor()).onContainer(Arrays.asList(container));
}
}
@Override
protected void onContainerCompleted(ContainerStatus status) {
super.onContainerCompleted(status);
if (getMonitor() instanceof ContainerAware) {
((ContainerAware)getMonitor()).onContainerStatus(Arrays.asList(status));
}
int exitStatus = status.getExitStatus();
// 0 - ok, -100 - container released by app
if (exitStatus == 0 || exitStatus == -100) {
if (isComplete()) {
notifyCompleted();
}
} else {
log.warn("Got ContainerStatus=[" + status + "]");
if (!onContainerFailed(status)) {
setFinalApplicationStatus(FinalApplicationStatus.FAILED);
notifyCompleted();
}
}
}
/**
* Called if completed container has failed. User
* may override this method to process failed container,
* i.e. making a request to re-allocate new container instead
* of failing the application.
* <p>
* Default implementation doesn't do anything and just
* returns that failed container wasn't handled.
*
* @param containerStatus the container status
* @return true, if container was handled.
*/
protected boolean onContainerFailed(ContainerStatus containerStatus) {
return false;
}
/**
* Returns state telling if application is considered
* as complete. Default implementation is comparing if
* current target container count has been satisfied
* by a count from a method {@link ContainerMonitor#completedCount()}.
*
* @return true if application is complete
*/
protected boolean isComplete() {
return getMonitor().completedCount() >= containerCount;
}
}