/**
* Copyright (c) Codice Foundation
* <p/>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p/>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.platform.scheduler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.apache.felix.gogo.runtime.CommandNotFoundException;
import org.apache.karaf.shell.api.console.Session;
import org.apache.karaf.shell.api.console.SessionFactory;
import org.codice.ddf.security.common.Security;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ddf.security.Subject;
/**
* Executes Felix/Karaf commands when called as a Quartz {@link Job}
*
* @author Ashraf Barakat
* @author ddf.isgs@lmco.com
*/
public class CommandJob implements Job {
public static final String COMMAND_KEY = "command";
private static final Logger LOGGER = LoggerFactory.getLogger(CommandJob.class);
private static final Security SECURITY = Security.getInstance();
public Subject getSystemSubject() {
return SECURITY.getSystemSubject();
}
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
SECURITY.runAsAdmin(() -> {
Subject subject = getSystemSubject();
if (subject != null) {
subject.execute(() -> {
doExecute(context);
return null;
});
} else {
LOGGER.debug("Could not execute command. Could not get subject to run command");
}
return null;
});
}
private Bundle getBundle() {
return FrameworkUtil.getBundle(getClass());
}
protected SessionFactory getSessionFactory() {
BundleContext bundleContext = getBundle().getBundleContext();
if (bundleContext == null) {
return null;
}
return bundleContext.getService(bundleContext.getServiceReference(SessionFactory.class));
}
public void doExecute(JobExecutionContext context) throws JobExecutionException {
String commandInput;
try {
commandInput = checkInput(context);
} catch (CommandException e) {
LOGGER.debug("unable to get command from job execution context", e);
return;
}
SessionFactory sessionFactory = getSessionFactory();
if (sessionFactory == null) {
LOGGER.debug("unable to create session factory: command=[{}]", commandInput);
return;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Session session = null;
try (PrintStream output = getPrintStream(byteArrayOutputStream)) {
session = sessionFactory.create(null, output, output);
if (session == null) {
LOGGER.debug("unable to create session: command=[{}]", commandInput);
return;
}
if (commandInput != null) {
try {
LOGGER.trace("Executing command [{}]", commandInput);
session.execute(commandInput);
LOGGER.trace("Execution Output: {}",
byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()));
} catch (CommandNotFoundException e) {
LOGGER.info(
"Command could not be found. Make sure the command's library has been loaded and try again: {}",
e.getLocalizedMessage());
LOGGER.debug("Command not found.", e);
} catch (Exception e) {
LOGGER.info("Error with execution. ", e);
}
}
} catch (UnsupportedEncodingException e) {
LOGGER.info("Unable to produce output", e);
} finally {
if (session != null) {
session.close();
}
try {
byteArrayOutputStream.close();
} catch (IOException e) {
LOGGER.debug("Could not close output stream", e);
}
}
}
private PrintStream getPrintStream(ByteArrayOutputStream byteArrayOutputStream)
throws UnsupportedEncodingException {
return new PrintStream(byteArrayOutputStream, false, StandardCharsets.UTF_8.name());
}
private String checkInput(JobExecutionContext context) throws CommandException {
String command = null;
if (context == null) {
LOGGER.debug("No JobExecutionContext found. Could not fire {}",
CommandJob.class.getSimpleName());
throw new CommandException();
}
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
if (mergedJobDataMap == null) {
LOGGER.debug("No input found. Could not fire {}", CommandJob.class.getSimpleName());
throw new CommandException();
}
if (mergedJobDataMap.getString(COMMAND_KEY) != null) {
command = mergedJobDataMap.getString(COMMAND_KEY);
}
return command;
}
private static class CommandException extends Exception {
}
}