/*
* Copyright © 2014-2016 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.batch;
import co.cask.cdap.api.data.batch.Split;
import co.cask.cdap.api.plugin.Plugin;
import co.cask.cdap.app.runtime.Arguments;
import co.cask.cdap.common.app.RunIds;
import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.internal.app.runtime.BasicArguments;
import co.cask.cdap.internal.app.runtime.workflow.WorkflowProgramInfo;
import co.cask.cdap.proto.id.ProgramId;
import co.cask.tephra.Transaction;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.twill.api.RunId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Helper class for getting and setting specific config settings for a job context.
*/
public final class MapReduceContextConfig {
private static final Logger LOG = LoggerFactory.getLogger(MapReduceContextConfig.class);
private static final Gson GSON = new Gson();
private static final String HCONF_ATTR_RUN_ID = "hconf.program.run.id";
private static final String HCONF_ATTR_PROGRAM_ID = "hconf.program.id";
private static final String HCONF_ATTR_WORKFLOW_INFO = "hconf.program.workflow.info";
private static final String HCONF_ATTR_PLUGINS = "hconf.program.plugins.map";
private static final String HCONF_ATTR_ARGS = "hconf.program.args";
private static final String HCONF_ATTR_PROGRAM_JAR_URI = "hconf.program.jar.uri";
private static final String HCONF_ATTR_CCONF = "hconf.cconf";
private static final String HCONF_ATTR_NEW_TX = "hconf.program.newtx.tx";
private static final String HCONF_ATTR_LOCAL_FILES = "hconf.program.local.files";
private final Configuration hConf;
public MapReduceContextConfig(Configuration hConf) {
this.hConf = hConf;
}
public Configuration getHConf() {
return hConf;
}
/**
* Updates the {@link Configuration} of this class with the given paramters.
*
* @param context the context for the MapReduce program
* @param conf the CDAP configuration
* @param tx the long transaction created for the MapReduce program
* @param programJarURI The URI of the program JAR
* @param localizedUserResources the localized resources for the MapReduce program
*/
public void set(BasicMapReduceContext context, CConfiguration conf, Transaction tx, URI programJarURI,
Map<String, String> localizedUserResources) {
setRunId(context.getRunId().getId());
setProgramId(context.getProgram().getId().toEntityId());
setWorkflowProgramInfo(context.getWorkflowInfo());
setPlugins(context.getPlugins());
setArguments(context.getRuntimeArguments());
setProgramJarURI(programJarURI);
setConf(conf);
setTx(tx);
setLocalizedResources(localizedUserResources);
}
private void setArguments(Map<String, String> arguments) {
hConf.set(HCONF_ATTR_ARGS, GSON.toJson(arguments));
}
/**
* Returns the runtime arguments for the MapReduce program
*/
public Arguments getArguments() {
Map<String, String> arguments = GSON.fromJson(hConf.get(HCONF_ATTR_ARGS),
new TypeToken<Map<String, String>>() { }.getType());
return new BasicArguments(arguments);
}
private void setRunId(String runId) {
hConf.set(HCONF_ATTR_RUN_ID, runId);
}
private void setProgramId(ProgramId programId) {
hConf.set(HCONF_ATTR_PROGRAM_ID, GSON.toJson(programId));
}
/**
* Returns the {@link RunId} for the MapReduce program.
*/
public RunId getRunId() {
return RunIds.fromString(hConf.get(HCONF_ATTR_RUN_ID));
}
/**
* Returns the {@link ProgramId} for the MapReduce program.
*/
public ProgramId getProgramId() {
return GSON.fromJson(hConf.get(HCONF_ATTR_PROGRAM_ID), ProgramId.class);
}
private void setWorkflowProgramInfo(@Nullable WorkflowProgramInfo info) {
if (info != null) {
hConf.set(HCONF_ATTR_WORKFLOW_INFO, GSON.toJson(info));
}
}
/**
* Returns the {@link WorkflowProgramInfo} if it is running inside Workflow or {@code null} if not.
*/
@Nullable
public WorkflowProgramInfo getWorkflowProgramInfo() {
String info = hConf.get(HCONF_ATTR_WORKFLOW_INFO);
if (info == null) {
return null;
}
WorkflowProgramInfo workflowProgramInfo = GSON.fromJson(info, WorkflowProgramInfo.class);
workflowProgramInfo.getWorkflowToken().disablePut();
return workflowProgramInfo;
}
private void setPlugins(Map<String, Plugin> plugins) {
hConf.set(HCONF_ATTR_PLUGINS, GSON.toJson(plugins));
}
/**
* Returns the plugins being used in the MapReduce program.
*/
public Map<String, Plugin> getPlugins() {
String spec = hConf.get(HCONF_ATTR_PLUGINS);
if (spec == null) {
return ImmutableMap.of();
}
return GSON.fromJson(spec, new TypeToken<Map<String, Plugin>>() {
}.getType());
}
private void setProgramJarURI(URI programJarURI) {
hConf.set(HCONF_ATTR_PROGRAM_JAR_URI, programJarURI.toASCIIString());
}
/**
* Returns the URI of where the program JAR is.
*/
public URI getProgramJarURI() {
return URI.create(hConf.get(HCONF_ATTR_PROGRAM_JAR_URI));
}
/**
* Returns the file name of the program JAR.
*/
public String getProgramJarName() {
return new Path(getProgramJarURI()).getName();
}
private void setLocalizedResources(Map<String, String> localizedUserResources) {
hConf.set(HCONF_ATTR_LOCAL_FILES, GSON.toJson(localizedUserResources));
}
public Map<String, File> getLocalizedResources() {
Map<String, String> nameToPath = GSON.fromJson(hConf.get(HCONF_ATTR_LOCAL_FILES),
new TypeToken<Map<String, String>>() { }.getType());
Map<String, File> nameToFile = new HashMap<>();
for (Map.Entry<String, String> entry : nameToPath.entrySet()) {
nameToFile.put(entry.getKey(), new File(entry.getValue()));
}
return nameToFile;
}
// This is needed to deserialize JSON into generified List
private static final class ListSplitType implements ParameterizedType {
private final Class<? extends Split> implementationClass;
private ListSplitType(Class<? extends Split> implementationClass) {
this.implementationClass = implementationClass;
}
@Override
public Type[] getActualTypeArguments() {
return new Type[]{implementationClass};
}
@Override
public Type getRawType() {
return List.class;
}
@Override
public Type getOwnerType() {
// it is fine, as it is not inner class
return null;
}
}
private void setConf(CConfiguration conf) {
StringWriter stringWriter = new StringWriter();
try {
conf.writeXml(stringWriter);
} catch (IOException e) {
LOG.error("Unable to serialize CConfiguration into xml");
throw Throwables.propagate(e);
}
hConf.set(HCONF_ATTR_CCONF, stringWriter.toString());
}
/**
* Returns the {@link CConfiguration} stored inside the job {@link Configuration}.
*/
public CConfiguration getCConf() {
String conf = hConf.get(HCONF_ATTR_CCONF);
Preconditions.checkArgument(conf != null, "No CConfiguration available");
return CConfiguration.create(new ByteArrayInputStream(conf.getBytes(Charsets.UTF_8)));
}
private void setTx(Transaction tx) {
hConf.set(HCONF_ATTR_NEW_TX, GSON.toJson(tx));
}
/**
* Returns the {@link Transaction} information stored inside the job {@link Configuration}.
*/
public Transaction getTx() {
return GSON.fromJson(hConf.get(HCONF_ATTR_NEW_TX), Transaction.class);
}
}