All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sshtools.forker.common.Command Maven / Gradle / Ivy

There is a newer version: 1.6.4
Show newest version
/**
 * Copyright © 2015 - ${year} SSHTOOLS Limited ([email protected])
 *
 * 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 com.sshtools.forker.common;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.SystemUtils;

/**
 * This class carries all of the detail for the command to launch and may be
 * serialised and sent to the Forker Daemon, which will deserialise and
 * actually run it.
 *
 */
public class Command {
	@SuppressWarnings("serial")
	private List arguments = new ArrayList() {

		@Override
		public boolean add(String e) {
			if(e == null)
				throw new NullPointerException();
			return super.add(e);
		}
		
	};
	private boolean redirectError;
	private File directory;
	private Map environment;
	private String runAs = "";
	private IO io = IO.IO;
	private Priority priority = null;
	private List affinity = new ArrayList();

	/**
	 * Constructor
	 */
	public Command() {
		environment = new ProcessBuilder("dummy").environment();
	}

	/**
	 * Construct a new command given the serialisation stream. The order of
	 * attributes must be as per {@link Command#write(DataOutputStream)}.
	 * 
	 * @param din
	 *            input stream
	 * @throws IOException
	 *             on any error
	 */
	public Command(DataInputStream din) throws IOException {
		int argc = din.readInt();
		for (int i = 0; i < argc; i++) {
			arguments.add(din.readUTF());
		}
		directory = new File(din.readUTF());
		redirectError = din.readBoolean();
		boolean hasEnv = din.readBoolean();
		if (hasEnv) {
			environment = new HashMap();
			int envc = din.readInt();
			for (int i = 0; i < envc; i++) {
				environment.put(din.readUTF(), din.readUTF());
			}
		} else {
			// Otherwise pass on our own environment.
			// TODO i think better handling of environment in general is needed.
			// Something for 1.3
			environment = new ProcessBuilder("dummy").environment();
		}
		String r = din.readUTF();
		runAs = r.equals("") ? null : r;
		io = IO.DefaultIO.valueOf(din.readUTF());
		String priStr = din.readUTF();
		priority = priStr.equals("") ? null : Priority.valueOf(priStr);
	}

	/**
	 * Get the list of processes this command should be bound to. An empty list
	 * means the default OS behavior should be used.
	 * 
	 * @return processor affinity
	 */
	public List getAffinity() {
		return affinity;
	}

	/**
	 * Get whether stderr should be redirected to stdout.
	 * 
	 * @return redirect error stream
	 */
	public boolean isRedirectError() {
		return redirectError;
	}

	/**
	 * Set whether stderr should be redirected to stdout.
	 * 
	 * @param redirectError
	 *            redirect error stream
	 */
	public void setRedirectError(boolean redirectError) {
		this.redirectError = redirectError;
	}

	/**
	 * Get the working directory this command should run in.
	 * 
	 * @return directory
	 */
	public File getDirectory() {
		return directory;
	}

	/**
	 * Set the working directory this command should run in.
	 * 
	 * @param directory
	 *            directory
	 */
	public void setDirectory(File directory) {
		this.directory = directory;
	}

	/**
	 * Get the environment variables that will be passed to the command.
	 * 
	 * @return environment variables
	 */
	public Map getEnvironment() {
		return environment;
	}

	/**
	 * Set the environment variables that will be passed to the command.
	 * 
	 * @param environment
	 *            environment variables
	 */
	public void setEnvironment(Map environment) {
		this.environment = environment;
	}

	/**
	 * Get the priority the command should run under.
	 * 
	 * @return priority
	 */
	public Priority getPriority() {
		return priority;
	}

	/**
	 * Set the priority the command should run under.
	 * 
	 * @param priority
	 *            priority
	 */
	public void setPriority(Priority priority) {
		this.priority = priority;
	}

	/**
	 * Get the I/O mode that should be used.
	 * 
	 * @return I/O mode
	 */
	public IO getIO() {
		return io;
	}

	/**
	 * Set the I/O mode that should be used.
	 * 
	 * @param io
	 *            I/O mode
	 */
	public void setIO(IO io) {
		this.io = io;
	}

	/**
	 * Get the user the command should run as. If this is null, the
	 * user will be run under the same user as the either the current runtime or
	 * the daemon.
	 * 
	 * @return user to run as
	 */
	public String getRunAs() {
		return runAs;
	}

	/**
	 * Set the user the command should run as. If this is null, the
	 * user will be run under the same user as the either the current runtime or
	 * the daemon.
	 * 
	 * @param runAs
	 *            user to run as
	 */
	public void setRunAs(String runAs) {
		this.runAs = runAs;
	}

	/**
	 * Get the list of command arguments. The list may be modified directly.
	 * 
	 * @return arguments
	 */
	public List getArguments() {
		return arguments;
	}

	/**
	 * Serialise this command to a stream. This data may be used to construct another
	 * {@link Command} (see the constructors).
	 * 
	 * @param dout output stream to write command data to
	 * @throws IOException on any error
	 */
	public void write(DataOutputStream dout) throws IOException {
		dout.writeInt(arguments.size());
		for (String s : arguments) {
			dout.writeUTF(s);
		}
		dout.writeUTF(directory == null ? System.getProperty("user.dir") : directory.getAbsolutePath());
		dout.writeBoolean(redirectError);
		dout.writeBoolean(environment != null);
		if (environment != null) {
			dout.writeInt(environment.size());
			for (Map.Entry en : environment.entrySet()) {
				dout.writeUTF(en.getKey());
				dout.writeUTF(en.getValue());
			}
		}
		dout.writeUTF(runAs == null ? "" : runAs);
		dout.writeUTF(io.name());
		dout.writeUTF(priority == null ? "" : priority.name());
	}

	@Override
	public String toString() {
		return "Command [arguments=" + arguments + ", redirectError=" + redirectError + ", directory=" + directory
				+ ", runAs=" + runAs + ", io=" + io + ", priority=" + priority + ", affinity=" + affinity + "]";
	}

	/**
	 * Get all of the arguments that will actually be run. This will be the contents of {@link #getArguments()},
	 * but adjusted to include wrapper commands that may do things such as change the priority.
	 * 
	 * @return all arguments
	 */
	public List getAllArguments() {
		List a = new ArrayList(arguments);
		if (priority != null) {
			if (SystemUtils.IS_OS_UNIX) {
				a.add(0, "nice");
				a.add(1, "-n");
				switch (priority) {
				case REALTIME:
					a.add(2, "-20");
					break;
				case HIGH:
					a.add(2, "-10");
					break;
				case NORMAL:
					a.add(2, "10");
					break;
				case LOW:
					a.add(2, "19");
					break;
				default:
					break;
				}
			} else if (SystemUtils.IS_OS_WINDOWS) {
				if (a.size() < 3 || !a.get(0).equals("CMD.EXE") || !a.get(1).equals("/C")
						|| !a.get(2).equals("START")) {
					a.add(0, "CMD.EXE");
					a.add(1, "/C");
					a.add(2, "START");
				}
				a.add(3, "/WAIT");
				a.add(4, "/B");
				switch (priority) {
				case REALTIME:
					a.add(5, "/REALTIME");
					break;
				case HIGH:
					a.add(5, "/HIGH");
					break;
				case NORMAL:
					a.add(5, "/NORMAL");
					break;
				case LOW:
					a.add(5, "/LOW");
					break;
				default:
					break;
				}
				// http://stackoverflow.com/questions/154075/using-the-dos-start-command-with-parameters-passed-to-the-started-program
				a.add(6, "\"Forker\"");
			} else
				throw new UnsupportedOperationException();
		}

		if (!affinity.isEmpty()) {
			long mask = 0;
			for (Integer cpu : affinity) {
				mask = mask | 1 << (cpu - 1);
			}
			if (SystemUtils.IS_OS_UNIX) {
				a.add(0, "taskset");
				a.add(1, String.format("0x%x", mask));
			} else if (SystemUtils.IS_OS_WINDOWS && !(SystemUtils.IS_OS_WINDOWS_XP) && !SystemUtils.IS_OS_WINDOWS_95
					&& !SystemUtils.IS_OS_WINDOWS_98 && !SystemUtils.IS_OS_WINDOWS_ME && !SystemUtils.IS_OS_WINDOWS_NT
					&& !SystemUtils.IS_OS_WINDOWS_VISTA) {
				// Windows 7 and above
				if (a.size() < 3 || !a.get(0).equals("CMD.EXE") || !a.get(1).equals("/C")
						|| !a.get(2).equals("START")) {
					a.add(0, "CMD.EXE");
					a.add(1, "/C");
					a.add(2, "START");
				}
				a.add(3, "/AFFINITY");
				a.add(4, String.format("0x%x", mask));
			} else
				throw new UnsupportedOperationException();
		}
		return a;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy