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 }