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 * TestletImpl.java 25 * 26 */ 27 package org.apache.qetest; 28 29 30 31 /** 32 * Simple implementation of a testlet, a sort of mini-test. 33 * <p>A TestletImpl defines some common implementations that 34 * may be useful, including sample implementations that 35 * can be copied if you don't want to exend this class.</p> 36 * 37 * <p>The most useful implementation is of main(String[]), which 38 * allows a Testlet to be executed independently from the command 39 * line. See the code comments for a way to get this behavior in 40 * your testlet without having to re-implement the whole main 41 * method - by just including a static{} initializer with the 42 * fully qualified classname of your class.</p> 43 * 44 * <b>Note:</b> Testlets based on this class are probably 45 * not threadsafe, and must be executed singly! 46 * See comments for thisClassName. 47 * 48 * @author Shane_Curcuru@lotus.com 49 * @version $Id$ 50 */ 51 public class TestletImpl implements Testlet 52 { 53 54 //----------------------------------------------------- 55 //---- Implement Testlet interface methods 56 //----------------------------------------------------- 57 58 /** 59 * Accesor method for a brief description of this test. 60 * 61 * @return String "TestletImpl: default implementation, does nothing" 62 */ getDescription()63 public String getDescription() 64 { 65 return "TestletImpl: default implementation, does nothing"; 66 } 67 68 69 /** 70 * Accesor methods for our Logger. 71 * 72 * @param l the Logger to have this test use for logging 73 * results; or null to use a default logger 74 */ setLogger(Logger l)75 public void setLogger(Logger l) 76 { 77 // if null, set a default one 78 if (null == l) 79 logger = getDefaultLogger(); 80 else 81 logger = l; 82 } 83 84 85 /** 86 * Accesor methods for our Logger. 87 * 88 * @return Logger we tell all our secrets to. 89 */ getLogger()90 public Logger getLogger() 91 { 92 return logger; 93 } 94 95 96 /** 97 * Get a default Logger for use with this Testlet. 98 * Gets a default ConsoleLogger (only if a Logger isn't 99 * currently set!). 100 * 101 * @return current logger; if null, then creates a 102 * Logger.DEFAULT_LOGGER and returns that; if it cannot 103 * create one, throws a RuntimeException 104 */ getDefaultLogger()105 public Logger getDefaultLogger() 106 { 107 if (logger != null) 108 return logger; 109 110 try 111 { 112 Class rClass = Class.forName(Logger.DEFAULT_LOGGER); 113 return (Logger)rClass.newInstance(); 114 } 115 catch (Exception e) 116 { 117 // Must re-throw the exception, since returning 118 // null or the like could lead to recursion 119 e.printStackTrace(); 120 throw new RuntimeException(e.toString()); 121 } 122 } 123 124 125 /** 126 * Return this TestletImpl's default Datalet. 127 * 128 * @return Datalet <code>defaultDatalet</code>. 129 */ getDefaultDatalet()130 public Datalet getDefaultDatalet() 131 { 132 return defaultDatalet; 133 } 134 135 136 /** 137 * Run this TestletImpl: execute it's test and return. 138 * This must (obviously) be overriden by subclasses. Here, 139 * we simply log a message for debugging purposes. 140 * 141 * @param Datalet to use as data points for the test. 142 */ execute(Datalet datalet)143 public void execute(Datalet datalet) 144 { 145 logger.logMsg(Logger.STATUSMSG, "TestletImpl.execute(" + datalet + ")"); 146 } 147 148 149 //----------------------------------------------------- 150 //---- Implement useful worker methods and main() 151 //----------------------------------------------------- 152 153 /** 154 * Process default command line args. 155 * Provides simple usage functionality: given a first arg of 156 * -h, -H, -?, prints a usage statement based on getDescription() 157 * and on getDefaultDatalet().getDescription() 158 * 159 * @param args command line args from the JVM 160 * @return true if we got and handled any default command line 161 * args (i.e. you can quit now); false otherwise (i.e. you 162 * should go ahead and execute) 163 */ handledDefaultArgs(String[] args)164 protected boolean handledDefaultArgs(String[] args) 165 { 166 // We don't handle null or blank args 167 if ((null == args) || (0 == args.length)) 168 return false; 169 170 // Provide basic processing for help, usage cases 171 if ("-h".equals(args[0]) 172 || "-H".equals(args[0]) 173 || "-?".equals(args[0]) 174 ) 175 { 176 logger.logMsg(Logger.STATUSMSG, thisClassName + " usage:"); 177 logger.logMsg(Logger.STATUSMSG, " Testlet: " + getDescription()); 178 logger.logMsg(Logger.STATUSMSG, " Datalet: " + getDefaultDatalet().getDescription()); 179 return true; 180 } 181 182 // Otherwise, don't handle any other args 183 return false; 184 } 185 186 187 /** 188 * Default implementation for command line use. 189 * Note subclasses can easily get the functionality we provide 190 * here without having to copy this method merely by copying the 191 * static initalization block shown above, replacing the 192 * "thisClassName" with their own FQCN. 193 * 194 * This default implementation installs a default Logger, then 195 * checks for and handles a few default command line args, and 196 * then executes the Testlet. 197 * 198 * //@todo How do we easily specify alternate Datalets or 199 * data to load a Datalet from? What about the Datalet that 200 * actually wants the first arg to be -h? 201 * 202 * @param args command line args from the JVM 203 */ main(String[] args)204 public static void main(String[] args) 205 { 206 if (true) 207 System.out.println("TestletImpl.main"); 208 TestletImpl t = null; 209 try 210 { 211 // Create an instance of the specific class at runtime 212 // This relies on subclasses to reset 'thisClassName' 213 // in their own static[] initialization block! 214 t = (TestletImpl)Class.forName(thisClassName).newInstance(); 215 216 // Set a default logger automatically 217 t.setLogger(t.getDefaultLogger()); 218 219 // Process default -h, etc. args 220 if (!t.handledDefaultArgs(args)) 221 { 222 if (args.length > 0) 223 { 224 // If we do have any args, then attempt to 225 // load the correct-typed Datalet from the args 226 Class dataletClass = t.getDefaultDatalet().getClass(); 227 t.logger.logMsg(Logger.TRACEMSG, "Loading Datalet " 228 + dataletClass.getName() + " from args"); 229 Datalet d = (Datalet)dataletClass.newInstance(); 230 d.load(args); 231 t.execute(d); 232 } 233 else 234 { 235 // Otherwise, use the defaultDatalet for that Testlet 236 t.logger.logMsg(Logger.TRACEMSG, "Using defaultDatalet"); 237 t.execute(t.getDefaultDatalet()); 238 } 239 } 240 } 241 catch (Exception e) 242 { 243 if ((null != t) && (null != t.logger)) 244 { 245 // Use the logger which is (hopefully) OK 246 t.logger.checkErr("TestletImpl threw: " + e.toString()); 247 java.io.StringWriter sw = new java.io.StringWriter(); 248 java.io.PrintWriter pw = new java.io.PrintWriter(sw); 249 e.printStackTrace(pw); 250 t.logger.logArbitrary(Logger.ERRORMSG, sw.toString()); 251 } 252 else 253 { 254 // Otherwise, just dump to System.err 255 System.err.println("TestletImpl threw: " + e.toString()); 256 e.printStackTrace(); 257 } 258 } 259 } 260 261 262 /** 263 * The FQCN of the current class. 264 * See comments in main() and in the static initializer. 265 * <b>Note:</b> Testlets based on this class are probably 266 * not threadsafe, and must be executed singly! 267 */ 268 protected static String thisClassName = null; 269 270 271 /** 272 * A static initializer setting the value of thisClassName. 273 * Subclasses must copy this static block, and replace the 274 * name of "...TestletImpl" with their own name. Then, when 275 * a user executes the subclassed Testlet on the command line 276 * or calls it's main() method, the correct thing will happen. 277 */ 278 static { thisClassName = "org.apache.qetest.TestletImpl"; } 279 280 281 /** 282 * Our Logger, who we tell all our secrets to. 283 */ 284 protected Logger logger = null; 285 286 287 /** 288 * Our deafult Datalet: in this case, not very interesting. 289 * Provide a default 'null' datalet so unsuspecting callers 290 * don't get NullPointerExceptions. 291 */ 292 protected Datalet defaultDatalet = new NullDatalet(); 293 294 } // end of class TestletImpl 295