001 package toolbus.tool.execution;
002
003 import java.io.IOException;
004 import java.lang.reflect.Constructor;
005 import java.lang.reflect.InvocationTargetException;
006
007 import toolbus.ToolBus;
008 import toolbus.adapter.java.AbstractJavaTool;
009 import toolbus.exceptions.ToolBusException;
010 import toolbus.logging.ILogger;
011 import toolbus.logging.IToolBusLoggerConstants;
012 import toolbus.logging.LoggerFactory;
013 import toolbus.tool.ToolDefinition;
014 import toolbus.tool.ToolInstance;
015 import aterm.ATerm;
016
017 /**
018 * This tool executor implements the default behavior of an executor.
019 * (Which means it just executes tools and nothing else).
020 *
021 * @author Arnold Lankamp
022 */
023 public class DefaultToolExecutor implements IToolExecutor{
024 private final ToolInstance toolInstance;
025 private final ToolDefinition toolDefinition;
026 private final ToolBus toolbus;
027
028 /**
029 * Constructor.
030 *
031 * @param toolInstance
032 * The tool instance to which the tool is associated.
033 * @param toolDefinition
034 * The definition of the tool.
035 * @param toolbus
036 * The toolbus to which the tool should connect.
037 */
038 public DefaultToolExecutor(ToolInstance toolInstance, ToolDefinition toolDefinition, ToolBus toolbus){
039 super();
040
041 this.toolInstance = toolInstance;
042 this.toolDefinition = toolDefinition;
043 this.toolbus = toolbus;
044 }
045
046 /**
047 * @see toolbus.tool.execution.IToolExecutor#execute()
048 */
049 public void execute() throws ToolBusException{
050 if(toolDefinition.isDirectlyStartableJavaNGTool()){
051 // Create a new classloader for the tool instance.
052 ClassLoader toolClassLoader = toolDefinition.createClassLoader();
053 // Load the tool's main class.
054 String toolClassName = toolDefinition.getClassName();
055 Class<?> toolClass;
056 try{
057 toolClass = toolClassLoader.loadClass(toolClassName);
058 }catch(ClassNotFoundException cnfex){
059 String error = "Unable to load the main class of tool: " + toolClassName;
060 LoggerFactory.log(error, cnfex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
061 throw new ToolBusException(error, cnfex);
062 }
063
064 // Instantiate the tool.
065 AbstractJavaTool tool;
066 try{
067 Constructor<?> toolConstructor = toolClass.getConstructor();
068
069 tool = (AbstractJavaTool) toolConstructor.newInstance();
070 tool.connectDirectly(toolbus, toolDefinition.getName(), toolInstance.getToolID());
071 }catch(InstantiationException iex){
072 String error = "Unable to instantiate the tool. Classname: " + toolClass.getName();
073 LoggerFactory.log(error, iex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
074 throw new ToolBusException(error);
075 }catch(IllegalAccessException iaex){
076 String error = "The constructor of the tool we are trying to instantiate isn't public. Classname: " + toolClass.getName();
077 LoggerFactory.log(error, iaex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
078 throw new ToolBusException(error);
079 }catch(InvocationTargetException itex){
080 String error = "An exception occured during the invokation of the constructor of the tool we are trying to instantiate. Classname: " + toolClass.getName();
081 LoggerFactory.log(error, itex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
082 throw new ToolBusException(error);
083 }catch(NoSuchMethodException nsmex){
084 String error = "The tool we are trying to instantiate doesn't have a proper constructor. Classname: " + toolClass.getName();
085 LoggerFactory.log(error, nsmex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
086 throw new ToolBusException(error);
087 }catch(SecurityException sex){
088 String error = "We don't have permission to invoke the constructor of: " + toolClass.getName();
089 LoggerFactory.log(error, sex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
090 throw new ToolBusException(error);
091 }catch(Exception ex){
092 String error = "Unable to connect tool to the ToolBus directly: " + toolClass.getName();
093 LoggerFactory.log(error, ex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
094 throw new ToolBusException(error);
095 }
096 }else if(toolDefinition.getClassName() != null){
097 String classpath = toolDefinition.getClassPath();
098 // Use the -Xshare=off switch to conserve memory. We don't want to load the entire
099 // standard library in memory every time we start a tool. We're only using a tiny
100 // bit of that library. And do NOT listen to Sun, who says that it will reduce the
101 // memory footprint, because it can share a part of it between all running JVMs; it
102 // can only share about 50% of this memory between JVMs, so this is simply not true.
103 // The last time I checked 1.5MB < (50% of 12.5MB). Thanks Sun for turning this
104 // rubbish feature on by default on the client VM.
105 String[] command = new String[]{"java", "-Xshare:off", "-cp", classpath, toolDefinition.getClassName(), "-TB_TOOL_NAME", toolDefinition.getName(), "-TB_TOOL_ID", Integer.toString(toolInstance.getToolID()), "-TB_HOST", "localhost", "-TB_PORT", "" + toolbus.getPort()};
106
107 ProcessBuilder pb = new ProcessBuilder(command);
108 pb.redirectErrorStream(true);
109
110 ATerm toolKey = toolInstance.getToolKey();
111
112 try{
113 Process process = pb.start();
114 StreamHandler streamHandler = new StreamHandler(process, toolKey.toString());
115 toolInstance.setStreamHandler(streamHandler);
116 Thread streamHandlerThread = new Thread(streamHandler);
117 streamHandlerThread.setName("Stream handler");
118 streamHandlerThread.setDaemon(true);
119 streamHandlerThread.start();
120 }catch(IOException ioex){
121 String error = "Unable to start remote tool: " + toolKey;
122 LoggerFactory.log(error, ioex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
123 throw new ToolBusException(error);
124 }
125 }else if(toolDefinition.getCommand() != null){
126 String[] toolCommand = toolDefinition.getCommand().split(" ");
127 int commandLength = toolCommand.length;
128 String[] command = new String[commandLength + 8];
129 System.arraycopy(toolCommand, 0, command, 0, commandLength);
130 command[commandLength] = "-TB_TOOL_NAME";
131 command[commandLength + 1] = toolDefinition.getName();
132 command[commandLength + 2] = "-TB_TOOL_ID";
133 command[commandLength + 3] = Integer.toString(toolInstance.getToolID());
134 command[commandLength + 4] = "-TB_HOST";
135 command[commandLength + 5] = "localhost";
136 command[commandLength + 6] = "-TB_PORT";
137 command[commandLength + 7] = Integer.toString(toolbus.getPort());
138
139 ProcessBuilder pb = new ProcessBuilder(command);
140 pb.redirectErrorStream(true);
141
142 ATerm toolKey = toolInstance.getToolKey();
143
144 try{
145 Process process = pb.start();
146 StreamHandler streamHandler = new StreamHandler(process, toolKey.toString());
147 toolInstance.setStreamHandler(streamHandler);
148 Thread streamHandlerThread = new Thread(streamHandler);
149 streamHandlerThread.setName("Stream handler");
150 streamHandlerThread.setDaemon(true);
151 streamHandlerThread.start();
152 }catch(IOException ioex){
153 String error = "Unable to start remote tool: " + toolKey;
154 LoggerFactory.log(error, ioex, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
155 throw new ToolBusException(error);
156 }
157 }else{
158 ATerm toolKey = toolInstance.getToolKey();
159
160 String error = "Unable to start tool: " + toolKey + "; command or classname missing in tool definition.";
161 LoggerFactory.log(error, ILogger.ERROR, IToolBusLoggerConstants.TOOLINSTANCE);
162 throw new ToolBusException(error);
163 }
164 }
165 }