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    }