/*
* Copyright 2015 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.xd.gpload;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.step.tasklet.x.AbstractProcessBuilderTasklet;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/**
* Tasklet used for running gpload tool.
*
* Note: This this class is not thread-safe.
*
* @since 1.2
* @author Thomas Risberg
* @author Gary Russell
*/
public class GploadTasklet extends AbstractProcessBuilderTasklet implements InitializingBean {
private static final String GPLOAD_COMMAND = "gpload";
private static final String CONTROL_FILE_NODE_GPLOAD = "GPLOAD";
private static final String CONTROL_FILE_NODE_INPUT = "INPUT";
private static final String CONTROL_FILE_NODE_SOURCE = "SOURCE";
private static final String CONTROL_FILE_NODE_FILE = "FILE";
private String gploadHome;
private String controlFile;
private String options;
private String inputSourceFile;
private String database;
private String host;
private Integer port;
private String username;
private String password;
private String passwordFile;
private String controlFileToUse;
public String getGploadHome() {
return gploadHome;
}
public void setGploadHome(String gploadHome) {
this.gploadHome = gploadHome;
}
public String getControlFile() {
return controlFile;
}
public void setControlFile(String controlFile) {
this.controlFile = controlFile;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
public String getInputSourceFile() {
return inputSourceFile;
}
public void setInputSourceFile(String inputSourceFile) {
this.inputSourceFile = inputSourceFile;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordFile() {
return passwordFile;
}
public void setPasswordFile(String passwordFile) {
this.passwordFile = passwordFile;
}
@Override
protected boolean isStoppable() {
return false;
}
@Override
protected List<String> createCommand() throws Exception {
List<String> command = new ArrayList<String>();
command.add(getCommandString());
if (StringUtils.hasText(host)) {
command.add("-h");
command.add(host);
}
if (port != null) {
command.add("-p");
command.add(port.toString());
}
if (StringUtils.hasText(database)) {
command.add("-d");
command.add(database);
}
if (StringUtils.hasText(username)) {
command.add("-U");
command.add(username);
}
if (StringUtils.hasText(controlFile)) {
if (StringUtils.hasText(inputSourceFile)) {
try {
controlFileToUse = createControlFile(inputSourceFile, controlFile);
}
catch (IOException e) {
throw new JobExecutionException("Error while creating control file", e);
}
}
else {
controlFileToUse = controlFile;
}
command.add("-f");
command.add(controlFileToUse);
}
if (StringUtils.hasText(options)) {
command.add(options);
}
return command;
}
@Override
protected String getCommandDisplayString() {
return getCommandString();
}
@Override
protected String getCommandName() {
return "gpload";
}
@Override
protected String getCommandDescription() {
return "gpload job";
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
stepExecution.getExecutionContext().put(getCommandName() + ".controlFile", controlFileToUse);
return super.afterStep(stepExecution);
}
@Override
public void afterPropertiesSet() throws Exception {
if (!StringUtils.hasText(gploadHome)) {
throw new IllegalArgumentException(
"Missing gploadHome property, it is mandatory for running gpload command");
}
if (StringUtils.hasText(password) && StringUtils.hasText(passwordFile)) {
throw new IllegalArgumentException("You can't specify both 'password' and 'passwordFile' options");
}
if (StringUtils.hasText(password)) {
addEnvironmentProvider(new GploadEnvironmentProvider(gploadHome, false, password));
}
else if (StringUtils.hasText(passwordFile)) {
addEnvironmentProvider(new GploadEnvironmentProvider(gploadHome, true, passwordFile));
}
else {
throw new IllegalArgumentException("You must specify either 'password' or 'passwordFile' option");
}
}
private String getCommandString() {
return gploadHome + "/bin/" + GPLOAD_COMMAND;
}
private String createControlFile(String inputSourceFile, String controlFile) throws IOException {
InputStream in = new FileInputStream(new File(controlFile));
Yaml yaml = new Yaml();
Object data = yaml.load(in);
in.close();
replaceInputSourceFile(data, inputSourceFile);
String output = yaml.dump(data);
File out = File.createTempFile("gpload-", ".yml");
String outFile = out.getAbsolutePath();
FileWriter f = new FileWriter(out);
f.write(output);
f.close();
return outFile;
}
@SuppressWarnings("unchecked")
protected static void replaceInputSourceFile(Object data, String inputSourceFile) {
if (data != null && data instanceof Map) {
Object gpload = ((Map) data).get(CONTROL_FILE_NODE_GPLOAD);
if (gpload != null && gpload instanceof Map) {
Object input = ((Map) gpload).get(CONTROL_FILE_NODE_INPUT);
if (input == null) {
input = new ArrayList<Object>();
((Map) gpload).put(CONTROL_FILE_NODE_INPUT, input);
}
if (input instanceof List) {
boolean hasSource = false;
for (Object o : (List) input) {
if (o instanceof Map && ((Map) o).containsKey(CONTROL_FILE_NODE_SOURCE)) {
hasSource = true;
}
}
if (!hasSource) {
Map<String, Object> tmp = new LinkedHashMap<>();
tmp.put(CONTROL_FILE_NODE_SOURCE, new LinkedHashMap<String, Object>());
((List) input).add(tmp);
}
for (Object o : (List) input) {
if (o instanceof Map && ((Map) o).containsKey(CONTROL_FILE_NODE_SOURCE)) {
Object source = ((Map) o).get(CONTROL_FILE_NODE_SOURCE);
if (source != null && source instanceof Map) {
((Map) source).put(CONTROL_FILE_NODE_FILE, Arrays.asList(inputSourceFile));
}
}
}
}
}
}
}
}