/* * Copyright © 2015 Cask Data, Inc. * * 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 co.cask.cdap.internal.app.runtime.distributed; import co.cask.cdap.api.Resources; import co.cask.cdap.app.program.Program; import co.cask.cdap.proto.ProgramType; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.apache.twill.api.EventHandler; import org.apache.twill.api.ResourceSpecification; import org.apache.twill.api.TwillApplication; import org.apache.twill.api.TwillRunnable; import org.apache.twill.api.TwillSpecification; import org.apache.twill.api.TwillSpecification.Builder; import org.apache.twill.filesystem.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; /** * An abstract base class for all program {@link TwillApplication}. */ public abstract class AbstractProgramTwillApplication implements TwillApplication { private static final Logger LOG = LoggerFactory.getLogger(AbstractProgramTwillApplication.class); private final Program program; private final Map<String, LocalizeResource> localizeResources; private final EventHandler eventHandler; /** * Constructor. * * @param program represents the program to be launched in Twill * @param localizeResources set of resources to be localized to the Twill container * @param eventHandler An {@link EventHandler} for the Twill application which will be set to the * Twill application specification */ protected AbstractProgramTwillApplication(Program program, Map<String, LocalizeResource> localizeResources, EventHandler eventHandler) { this.program = program; this.localizeResources = ImmutableMap.copyOf(localizeResources); this.eventHandler = eventHandler; } /** * Returns type of the program started by this {@link TwillApplication}. */ protected abstract ProgramType getType(); /** * Adds {@link TwillRunnable} to the application. * * @param runnables map for adding {@link TwillRunnable} and resource requirements. */ protected abstract void addRunnables(Map<String, RunnableResource> runnables); /** * Applies start ordering to the runnables added to this application. By default is any order. */ protected Builder.AfterOrder applyOrder(Builder.RunnableSetter runnableSetter) { return runnableSetter.anyOrder(); } /** * Returns a {@link ResourceSpecification} created from the given {@link Resources} and number of instances. */ protected final ResourceSpecification createResourceSpec(Resources resources, int instances) { return ResourceSpecification.Builder.with() .setVirtualCores(resources.getVirtualCores()) .setMemory(resources.getMemoryMB(), ResourceSpecification.SizeUnit.MEGA) .setInstances(instances) .build(); } @Override public final TwillSpecification configure() { Map<String, RunnableResource> runnables = Maps.newHashMap(); addRunnables(runnables); Builder.MoreRunnable moreRunnable = Builder.with() .setName(String.format("%s.%s.%s.%s", getType().name().toLowerCase(), program.getNamespaceId(), program.getApplicationId(), program.getName())) .withRunnable(); Builder.RunnableSetter runnableSetter = null; for (Map.Entry<String, RunnableResource> entry : runnables.entrySet()) { runnableSetter = localizeFiles(moreRunnable.add(entry.getKey(), entry.getValue().getRunnable(), entry.getValue().getResources()).withLocalFiles()); moreRunnable = runnableSetter; } Preconditions.checkState(runnableSetter != null, "No TwillRunnables for distributed program."); return applyOrder(runnableSetter).withEventHandler(eventHandler).build(); } /** * Request localization of the program jar and all other files. */ private Builder.RunnableSetter localizeFiles(Builder.LocalFileAdder fileAdder) { Location programLocation = program.getJarLocation(); LOG.debug("Localizing program jar for {}: {}", program.getName(), programLocation); Builder.MoreFile moreFile = fileAdder.add(programLocation.getName(), programLocation.toURI()); for (Map.Entry<String, LocalizeResource> entry : localizeResources.entrySet()) { LOG.debug("Localizing file for {}: {} {} {}", program.getName(), entry.getKey(), entry.getValue()); moreFile.add(entry.getKey(), entry.getValue().getURI(), entry.getValue().isArchive()); } return moreFile.apply(); } /** * Container class for holding TwillRunnable and ResourceSpecification together. */ protected static final class RunnableResource { private final TwillRunnable runnable; private final ResourceSpecification resources; public RunnableResource(TwillRunnable runnable, ResourceSpecification resources) { this.runnable = runnable; this.resources = resources; } public TwillRunnable getRunnable() { return runnable; } public ResourceSpecification getResources() { return resources; } } }