/* * 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.worker; import co.cask.cdap.api.Resources; import co.cask.cdap.api.app.ApplicationSpecification; import co.cask.cdap.api.metrics.MetricsCollectionService; import co.cask.cdap.api.worker.Worker; import co.cask.cdap.api.worker.WorkerSpecification; import co.cask.cdap.app.program.Program; import co.cask.cdap.app.runtime.ProgramController; import co.cask.cdap.app.runtime.ProgramOptions; import co.cask.cdap.app.runtime.ProgramRunner; import co.cask.cdap.app.stream.StreamWriterFactory; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.data2.dataset2.DatasetFramework; import co.cask.cdap.data2.metadata.writer.ProgramContextAware; import co.cask.cdap.internal.app.runtime.AbstractProgramRunnerWithPlugin; import co.cask.cdap.internal.app.runtime.ProgramControllerServiceAdapter; import co.cask.cdap.internal.app.runtime.ProgramOptionConstants; import co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramType; import co.cask.tephra.TransactionSystemClient; import com.google.common.base.Preconditions; import com.google.common.io.Closeables; import com.google.common.util.concurrent.Service; import com.google.gson.Gson; import com.google.inject.Inject; import org.apache.twill.api.RunId; import org.apache.twill.common.Threads; import org.apache.twill.discovery.DiscoveryServiceClient; import org.apache.twill.internal.RunIds; import org.apache.twill.internal.ServiceListenerAdapter; /** * A {@link ProgramRunner} that runs a {@link Worker}. */ public class WorkerProgramRunner extends AbstractProgramRunnerWithPlugin { private static final Gson GSON = new Gson(); private final MetricsCollectionService metricsCollectionService; private final DatasetFramework datasetFramework; private final DiscoveryServiceClient discoveryServiceClient; private final TransactionSystemClient txClient; private final StreamWriterFactory streamWriterFactory; @Inject public WorkerProgramRunner(CConfiguration cConf, MetricsCollectionService metricsCollectionService, DatasetFramework datasetFramework, DiscoveryServiceClient discoveryServiceClient, TransactionSystemClient txClient, StreamWriterFactory streamWriterFactory) { super(cConf); this.metricsCollectionService = metricsCollectionService; this.datasetFramework = datasetFramework; this.discoveryServiceClient = discoveryServiceClient; this.txClient = txClient; this.streamWriterFactory = streamWriterFactory; } @Override public ProgramController run(Program program, ProgramOptions options) { ApplicationSpecification appSpec = program.getApplicationSpecification(); Preconditions.checkNotNull(appSpec, "Missing application specification."); int instanceId = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCE_ID, "-1")); Preconditions.checkArgument(instanceId >= 0, "Missing instance Id"); int instanceCount = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCES, "0")); Preconditions.checkArgument(instanceCount > 0, "Invalid or missing instance count"); String runIdOption = options.getArguments().getOption(ProgramOptionConstants.RUN_ID); Preconditions.checkNotNull(runIdOption, "Missing runId"); RunId runId = RunIds.fromString(runIdOption); ProgramType programType = program.getType(); Preconditions.checkNotNull(programType, "Missing processor type."); Preconditions.checkArgument(programType == ProgramType.WORKER, "Only Worker process type is supported."); WorkerSpecification workerSpec = appSpec.getWorkers().get(program.getName()); Preconditions.checkArgument(workerSpec != null, "Missing Worker specification for {}", program.getId()); String instances = options.getArguments().getOption(ProgramOptionConstants.INSTANCES, String.valueOf(workerSpec.getInstances())); String resourceString = options.getArguments().getOption(ProgramOptionConstants.RESOURCES, null); Resources newResources = (resourceString != null) ? GSON.fromJson(resourceString, Resources.class) : workerSpec.getResources(); WorkerSpecification newWorkerSpec = new WorkerSpecification(workerSpec.getClassName(), workerSpec.getName(), workerSpec.getDescription(), workerSpec.getProperties(), workerSpec.getDatasets(), newResources, Integer.valueOf(instances)); // Setup dataset framework context, if required if (datasetFramework instanceof ProgramContextAware) { Id.Program programId = program.getId(); ((ProgramContextAware) datasetFramework).initContext(new Id.Run(programId, runId.getId())); } final PluginInstantiator pluginInstantiator = createPluginInstantiator(options, program.getClassLoader()); try { BasicWorkerContext context = new BasicWorkerContext( newWorkerSpec, program, runId, instanceId, instanceCount, options.getUserArguments(), metricsCollectionService, datasetFramework, txClient, discoveryServiceClient, streamWriterFactory, pluginInstantiator); WorkerDriver worker = new WorkerDriver(program, newWorkerSpec, context); // Add a service listener to make sure the plugin instantiator is closed when the worker driver finished. worker.addListener(new ServiceListenerAdapter() { @Override public void terminated(Service.State from) { Closeables.closeQuietly(pluginInstantiator); } @Override public void failed(Service.State from, Throwable failure) { Closeables.closeQuietly(pluginInstantiator); } }, Threads.SAME_THREAD_EXECUTOR); ProgramController controller = new WorkerControllerServiceAdapter(worker, program.getId(), runId, workerSpec.getName() + "-" + instanceId); worker.start(); return controller; } catch (Throwable t) { Closeables.closeQuietly(pluginInstantiator); throw t; } } private static final class WorkerControllerServiceAdapter extends ProgramControllerServiceAdapter { private final WorkerDriver workerDriver; WorkerControllerServiceAdapter(WorkerDriver workerDriver, Id.Program programId, RunId runId, String componentName) { super(workerDriver, programId, runId, componentName); this.workerDriver = workerDriver; } @Override protected void doCommand(String name, Object value) throws Exception { super.doCommand(name, value); if (!ProgramOptionConstants.INSTANCES.equals(name) || !(value instanceof Integer)) { return; } workerDriver.setInstanceCount((Integer) value); } } }