1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 /* 23 * 24 * CmdlineTestlet.java 25 * 26 */ 27 package org.apache.qetest.xsl; 28 29 import java.io.BufferedReader; 30 import java.io.File; 31 import java.io.InputStreamReader; 32 import java.util.Hashtable; 33 34 import org.apache.qetest.Datalet; 35 import org.apache.qetest.Logger; 36 import org.apache.qetest.ThreadedStreamReader; 37 import org.apache.qetest.xslwrapper.TransformWrapper; 38 39 /** 40 * Testlet for conformance testing of xsl stylesheet files using 41 * a command line interface instead of a TransformWrapper. 42 * 43 * This class provides a default algorithim for testing XSLT 44 * processsors from the command line. Subclasses define the 45 * exact command line args, etc. used for different products. 46 * 47 * @author Shane_Curcuru@lotus.com 48 * @version $Id$ 49 */ 50 public class CmdlineTestlet extends StylesheetTestlet 51 { 52 // Initialize our classname for TestletImpl's main() method 53 static { thisClassName = "org.apache.qetest.xsl.CmdlineTestlet"; } 54 55 // Initialize our defaultDatalet 56 { defaultDatalet = (Datalet)new StylesheetDatalet(); } 57 58 /** 59 * Accesor method for a brief description of this test. 60 * 61 * @return String describing what this CmdlineTestlet does. 62 */ getDescription()63 public String getDescription() 64 { 65 return "CmdlineTestlet"; 66 } 67 68 /** 69 * Parameter: Actual name of external program to call. 70 */ 71 public static final String OPT_PROGNAME = "progName"; 72 73 /** 74 * Default Actual name of external program to call. 75 * @return TestXSLT, the Xalan-C command line. 76 */ getDefaultProgName()77 public String getDefaultProgName() 78 { 79 return "TestXSLT"; 80 } 81 82 /** 83 * Path to external program to call; default is none. 84 */ 85 public static final String OPT_PROGPATH = "progPath"; 86 87 88 /** 89 * Worker method to actually perform the transform; 90 * overriden to use command line processing. 91 * 92 * Logs out applicable info; attempts to perform transformation. 93 * 94 * @param datalet to test with 95 * @param transformWrapper to have perform the transform 96 * @throws allows any underlying exception to be thrown 97 */ testDatalet(StylesheetDatalet datalet, TransformWrapper transformWrapper)98 protected void testDatalet(StylesheetDatalet datalet, TransformWrapper transformWrapper) 99 throws Exception 100 { 101 String[] defaultArgs = new String[0]; // Currently unused 102 String[] args = getProgramArguments(datalet, defaultArgs); 103 104 StringBuffer argBuf = new StringBuffer(); 105 for (int i = 0; i < args.length; i++) 106 { 107 argBuf.append(args[i]); 108 argBuf.append(" "); 109 } 110 //@todo Should we log a custom logElement here instead? 111 logger.logMsg(Logger.TRACEMSG, "cmdline executing: " + argBuf.toString()); 112 113 // Declare variables ahead of time to minimize latency 114 long startTime = 0; 115 long overallTime = 0; 116 117 // Use our worker method to execute the process, which 118 // runs the test via the command line 119 startTime = System.currentTimeMillis(); 120 execProcess(args, null); 121 overallTime = System.currentTimeMillis() - startTime; 122 123 } 124 125 126 /** 127 * Worker method to get list of arguments specific to this program. 128 * 129 * <p>Must be overridden for different processors, obviously. 130 * This implementation returns the args for Xalan-C TestXSLT</p> 131 * 132 * @param program path\name of program to Runtime.exec() 133 * @param defaultArgs any additional arguments to pass 134 * @return String array of arguments suitable to pass to 135 * Runtime.exec() 136 */ getProgramArguments(StylesheetDatalet datalet, String[] defaultArgs)137 public String[] getProgramArguments(StylesheetDatalet datalet, String[] defaultArgs) 138 { 139 final int NUMARGS = 7; 140 String[] args = new String[defaultArgs.length + NUMARGS]; 141 String progName = datalet.options.getProperty(OPT_PROGNAME, getDefaultProgName()); 142 String progPath = datalet.options.getProperty(OPT_PROGPATH); 143 if ((null != progPath) && (progPath.length() > 0)) 144 { 145 args[0] = progPath + File.separator + progName; 146 } 147 else 148 { 149 // Pesume the program is on the PATH already... 150 args[0] = progName; 151 } 152 153 // Default args for Xalan-C TestXSLT 154 args[1] = "-in"; 155 args[2] = datalet.xmlName; 156 args[3] = "-xsl"; 157 args[4] = datalet.inputName; 158 args[5] = "-out"; 159 args[6] = datalet.outputName; 160 161 if (defaultArgs.length > 0) 162 System.arraycopy(defaultArgs, 0, args, NUMARGS, defaultArgs.length); 163 164 return args; 165 } 166 167 168 /** 169 * Worker method to shell out an external process. 170 * 171 * <p>Does a simple capturing of the out and err streams from 172 * the process and logs them out. Inherits the same environment 173 * that the current JVM is in.</p> 174 * 175 * @param cmdline actual command line to run, including program name 176 * @param environment passed as-is to Process.run 177 * @return return value from program 178 * @exception Exception may be thrown by Runtime.exec 179 */ execProcess(String[] cmdline, String[] environment)180 public void execProcess(String[] cmdline, String[] environment) 181 throws Exception 182 { 183 if ((cmdline == null) || (cmdline.length < 1)) 184 { 185 logger.checkFail("execProcess called with null/blank arguments!"); 186 return; 187 } 188 189 int bufSize = 2048; // Arbitrary bufSize seems to work well 190 ThreadedStreamReader outReader = new ThreadedStreamReader(); 191 ThreadedStreamReader errReader = new ThreadedStreamReader(); 192 Runtime r = Runtime.getRuntime(); 193 java.lang.Process proc = null; // Fully declare to not conflict with org.apache.xalan.xslt.Process 194 195 // Actually begin executing the program 196 logger.logMsg(Logger.TRACEMSG, "execProcess starting " + cmdline[0]); 197 198 proc = r.exec(cmdline, environment); 199 200 // Immediately begin capturing any output therefrom 201 outReader.setInputStream( 202 new BufferedReader( 203 new InputStreamReader(proc.getInputStream()), bufSize)); 204 errReader.setInputStream( 205 new BufferedReader( 206 new InputStreamReader(proc.getErrorStream()), bufSize)); 207 208 // Start two threads off on reading the System.out and System.err from proc 209 outReader.start(); 210 errReader.start(); 211 int processReturnVal = -2; // HACK the default 212 try 213 { 214 // Wait for the process to exit normally 215 processReturnVal = proc.waitFor(); 216 } 217 catch (InterruptedException ie1) 218 { 219 logger.logThrowable(Logger.ERRORMSG, ie1, 220 "execProcess proc.waitFor() threw"); 221 } 222 223 // Now that we're done, presumably the Readers are also done 224 StringBuffer sysOut = null; 225 StringBuffer sysErr = null; 226 try 227 { 228 outReader.join(); 229 sysOut = outReader.getBuffer(); 230 } 231 catch (InterruptedException ie2) 232 { 233 logger.logThrowable(Logger.ERRORMSG, ie2, "Joining outReader threw"); 234 } 235 236 try 237 { 238 errReader.join(); 239 sysErr = errReader.getBuffer(); 240 } 241 catch (InterruptedException ie3) 242 { 243 logger.logThrowable(Logger.ERRORMSG, ie3, "Joining errReader threw"); 244 } 245 246 checkOutputStreams(cmdline, sysOut, sysErr, processReturnVal); 247 } 248 249 250 /** 251 * Worker method to evaluate the System.out/.err streams of 252 * a particular processor. 253 * 254 * @param cmdline that was used for execProcess 255 * @param outBuf buffer from execProcess' System.out 256 * @param errBuf buffer from execProcess' System.err 257 * @param processReturnVal from execProcess 258 */ checkOutputStreams(String[] cmdline, StringBuffer outBuf, StringBuffer errBuf, int processReturnVal)259 protected void checkOutputStreams(String[] cmdline, StringBuffer outBuf, 260 StringBuffer errBuf, int processReturnVal) 261 { 262 Hashtable attrs = new Hashtable(); 263 attrs.put("program", cmdline[0]); 264 attrs.put("returnVal", String.valueOf(processReturnVal)); 265 266 StringBuffer buf = new StringBuffer(); 267 if ((null != errBuf) && (errBuf.length() > 0)) 268 { 269 buf.append("<system-err>"); 270 buf.append(errBuf); 271 buf.append("</system-err>\n"); 272 } 273 if ((null != outBuf) && (outBuf.length() > 0)) 274 { 275 buf.append("<system-out>"); 276 buf.append(outBuf); 277 buf.append("</system-out>\n"); 278 } 279 logger.logElement(Logger.INFOMSG, "checkOutputStreams", attrs, buf.toString()); 280 attrs = null; 281 buf = null; 282 } 283 284 285 /** 286 * Worker method to get a TransformWrapper; overridden as no-op. 287 * 288 * @param datalet to test with 289 * @return null; CmdlineTestlet does not use this 290 */ getTransformWrapper(StylesheetDatalet datalet)291 protected TransformWrapper getTransformWrapper(StylesheetDatalet datalet) 292 { 293 return null; 294 } 295 } // end of class CmdlineTestlet 296 297