1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 28 package com.sun.javatest.exec; 29 30 import com.sun.javatest.InterviewParameters; 31 import com.sun.javatest.Parameters; 32 import com.sun.javatest.TestFilter; 33 import com.sun.javatest.TestSuite; 34 import com.sun.javatest.WorkDirectory; 35 import com.sun.javatest.util.Debug; 36 import com.sun.javatest.util.I18NResourceBundle; 37 import java.io.File; 38 import java.io.FileNotFoundException; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.Comparator; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.logging.Logger; 45 46 /** 47 * The very classic implementation of Session that encapsulates the WorkDirectory 48 * instance and InterviewParameters instance. 49 * 50 * @author Dmitry Fazunenko 51 */ 52 public class BasicSession implements SessionExt { 53 54 /** 55 * Instance of configuration 56 */ 57 private final InterviewParameters config; 58 59 /** 60 * Instance of workdir 61 */ 62 private WorkDirectory wd; 63 64 /** 65 * List of registered observers 66 */ 67 protected final List<Observer> observers = new ArrayList<Observer>(); 68 69 /** 70 * List of available filters 71 */ 72 protected final List<String> filterNames = new ArrayList<String>(); 73 74 /** 75 * List of observable properties 76 */ 77 protected final List<String> props = new ArrayList<String>(); 78 79 static final String EL_FILTER = "ExcludeList"; 80 static final String PRIOR_FILTER = "PriorStatus"; 81 static final String KWD_FILTER = "Keywords"; 82 static final String RELEVANT_FILTER = "Relevant"; 83 84 public static final String CONFIG_NAME_PROP = "Configuration"; 85 public static final String WD_PROP = "WorkDir"; 86 87 private boolean isSorted = false; 88 89 private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(BasicSession.class); 90 91 /** 92 * Class of update to configuration 93 */ 94 public static class U_NewConfig implements Update { 95 public final InterviewParameters ip; 96 public U_NewConfig(InterviewParameters ip) { 97 this.ip = ip; 98 } 99 } 100 101 /** 102 * Class of update to WorkDirectory 103 */ 104 static class U_NewWD implements Update { 105 public final WorkDirectory wd; 106 public U_NewWD(WorkDirectory wd) { 107 this.wd = wd; 108 } 109 } 110 111 112 /** 113 * Event which is delivered when WorkDircotry has been set. 114 */ 115 public static class E_NewWD implements Event { 116 public final WorkDirectory wd; 117 public final boolean doRestoreConfig; // for optimization 118 E_NewWD(WorkDirectory wd, boolean doRestoreConfig) { 119 this.wd = wd; 120 this.doRestoreConfig = doRestoreConfig; 121 } 122 } 123 124 /** 125 * Event which is delivered when current configuration has been modified. 126 */ 127 public static class E_NewConfig implements Event { 128 public final InterviewParameters ip; 129 public E_NewConfig(InterviewParameters ip) { 130 this.ip = ip; 131 } 132 } 133 134 /** 135 * Extension to the Observer interface for those classes which 136 * are sensitive to the order of notifying. If an observer wants 137 * to be notified in the very first turn, it should implements OrderedObserver 138 * interface, not just Observer and implement the order() method to return 139 * Integer.MIN_VALUE. To be notified last, the order() method should return 140 * Integer.MAX_VALUE. The order of regular observers is zero. 141 */ 142 public static interface OrderedObserver extends Observer { 143 /** 144 * Returns number from Integer.MIN_VALUE to Integer.MAX_VALUE 145 * to be sorted by when notifying. 146 */ 147 public int order(); 148 } 149 150 151 /** 152 * Creates empty session for the passed test suite. 153 * @param ts 154 * @throws com.sun.javatest.exec.Session.Fault 155 */ 156 public BasicSession(TestSuite ts) throws Fault { 157 initFilterList(); 158 initPropertyList(); 159 try { 160 config = ts.createInterview(); 161 } catch (Exception e) { 162 throw new Fault(e); 163 } 164 } 165 166 /** 167 * Applies the update. Ignores updates of unknown type. Subclasses need 168 * override this method to support more update types. 169 * @param u 170 * @throws com.sun.javatest.exec.Session.Fault 171 */ 172 public void update(Update u) throws Fault { 173 // here to preserve 4.4.0 behavior (true) 174 update(u, true); 175 } 176 177 178 /** 179 * Applies the update. Ignores updates of unknown type. Subclasses need 180 * override this method to support more update types. 181 * @param u 182 * @throws com.sun.javatest.exec.Session.Fault 183 * @since 4.4.1 184 */ 185 public void update(Update u, boolean updateConfig) throws Fault { 186 if (u instanceof U_NewWD) { 187 updateWorkDir(((U_NewWD)u).wd, updateConfig); 188 } else if (u instanceof U_NewConfig) { 189 updateNewConfig(((U_NewConfig)u).ip); 190 } 191 } 192 193 public void addObserver(Observer obs) { 194 if (obs != null && !observers.contains(obs)) { 195 observers.add(obs); 196 isSorted = false; 197 } 198 } 199 200 public void removeObserver(Observer obs) { 201 if (obs != null && observers.contains(obs)) { 202 observers.remove(obs); 203 } 204 } 205 206 /** 207 * Delivers events to the all registered observers 208 * @param evn - event to be sent out. 209 */ 210 public void notifyObservers(Event evn) { 211 if (!isSorted) { 212 sortObservers(); 213 } 214 for (Observer obs: observers) { 215 queue.add(new Pair(obs, evn)); 216 } 217 notifyQueue(); 218 } 219 220 private final ArrayList<Pair> queue = new ArrayList<Pair>(); 221 private boolean isNotifying = false; 222 private static class Pair { 223 final Observer obs; 224 final Event evn; 225 Pair(Observer obs, Event evn) { 226 this.obs = obs; 227 this.evn = evn; 228 } 229 } 230 private void notifyQueue() { 231 if (isNotifying) { 232 return; // already working... 233 } 234 isNotifying = true; 235 boolean cont = queue.size() > 0; 236 while (cont) { 237 Pair pair = queue.remove(0); 238 pair.obs.updated(pair.evn); // this call may cause a new 239 // pair to be add to the queue 240 cont = queue.size() > 0; 241 } 242 isNotifying = false; 243 } 244 245 /** 246 * Sorts observers by their order. 247 */ 248 private void sortObservers() { 249 Collections.sort(observers, new Comparator<Observer>() { 250 public int compare(Observer o1, Observer o2) { 251 long order1 = 0; 252 if (o1 instanceof OrderedObserver) { 253 order1 = ((OrderedObserver)o1).order(); 254 } 255 long order2 = 0; 256 if (o2 instanceof OrderedObserver) { 257 order2 = ((OrderedObserver)o2).order(); 258 } 259 return (int)(order1 - order2); // long is used to avoid overflow 260 } 261 262 }); 263 isSorted = true; 264 } 265 266 public TestFilter getTestFilter(String name) { 267 if (config == null) { 268 throw new IllegalStateException(i18n.getString("bc.configNotReady.err")); 269 } 270 TestFilter tf; 271 if (filterNames.contains(name)) { 272 tf = findFilter(name); 273 if (tf != null) { 274 return tf; 275 } 276 } 277 throw new IllegalArgumentException(i18n.getString("bc.unknownFilter.err", name)); 278 } 279 280 /** 281 * Supposed to be overridden when extra filters added 282 * @param name 283 * @return found filter or null, if not found. 284 */ 285 protected TestFilter findFilter(String name) { 286 if (EL_FILTER.equals(name)) { 287 return config.getExcludeListFilter(); 288 } else if (KWD_FILTER.equals(name)) { 289 return config.getKeywordsFilter(); 290 } else if (PRIOR_FILTER.equals(name)) { 291 return config.getPriorStatusFilter(); 292 } else if (RELEVANT_FILTER.equals(name)) { 293 return config.getRelevantTestFilter(); 294 } 295 return null; 296 } 297 298 public List<String> getTestFilterNames() { 299 return filterNames; 300 } 301 302 public void save(Map<String, String> map) { 303 if (wd != null) 304 map.put("workDir", wd.getPath()); 305 // save name of interview file 306 if (config != null && config.getFile() != null) 307 map.put("config", config.getFile().getPath()); 308 } 309 310 public void restore(Map map) throws Fault { 311 if (map == null) 312 return; 313 314 String wdPath = (String)map.get("workDir"); 315 if (wdPath == null) { 316 return; 317 } 318 try { 319 WorkDirectory workDir = WorkDirectory.open(new File(wdPath), config.getTestSuite()); 320 updateWorkDir(workDir, false); 321 //this.wd = workDir; 322 //applyWorkDir(wd); 323 } catch (FileNotFoundException e) { 324 // It's ok - saved WD could be removed or moved 325 return; 326 } catch (Exception e) { 327 throw new Fault(e); 328 } 329 330 String ipPath = (String)map.get("config"); 331 if (ipPath == null) { 332 return; 333 } 334 try { 335 loadInterviewFromFile(wd, new File(ipPath)); 336 } catch (Exception e) { 337 throw new Fault(e); 338 } 339 } 340 341 /** 342 * Loads interview from file. 343 * @param wd 344 * @param cfgFile 345 * @throws com.sun.javatest.exec.Session.Fault 346 */ 347 public void loadInterviewFromFile(WorkDirectory wd, File cfgFile) throws Fault { 348 try { 349 final long start = System.currentTimeMillis(); 350 351 config.load(cfgFile); 352 logLoadTime("exec.log.iload", System.currentTimeMillis()-start, 353 wd, cfgFile.getAbsolutePath()); 354 config.setWorkDirectory(wd); 355 notifyObservers(new E_NewConfig(config)); 356 } catch (Exception e) { 357 throw new Fault(e); 358 } 359 } 360 361 public void dispose() { 362 config.dispose(); 363 } 364 public List<String> getPropertyNames() { 365 return props; 366 } 367 368 public String getValue(String name) { 369 if (props.contains(name)) { 370 if (WD_PROP.equals(name)) { 371 return wd == null ? null : wd.getPath(); 372 } else if (CONFIG_NAME_PROP.equals(name)) { 373 if (config == null) { 374 return null; 375 } 376 File f = config.getFile(); 377 return f == null ? null : f.getPath(); 378 } 379 } 380 throw new IllegalArgumentException(i18n.getString("bc.unknownProperty.err", name)); 381 } 382 383 /** 384 * Work directory assigned to the session. 385 * @return The current wd set. 386 */ 387 public WorkDirectory getWorkDirectory() { 388 return wd; 389 } 390 391 public InterviewParameters getInterviewParameters() { 392 return config; 393 } 394 395 public Parameters getParameters() { 396 return getInterviewParameters(); 397 } 398 399 public boolean isReady() { 400 return config != null && config.isFinishable() && config.getFile() != null; 401 } 402 403 /** 404 * Creates list of supported filters: ExcludeList, PriorStatus, Keyword, 405 * Relevant. 406 */ 407 protected void initFilterList() { 408 filterNames.add(EL_FILTER); 409 filterNames.add(PRIOR_FILTER); 410 filterNames.add(KWD_FILTER); 411 filterNames.add(RELEVANT_FILTER); 412 } 413 414 /** 415 * Creates list of two session properties: WorkDirectory and Configuration. 416 */ 417 protected void initPropertyList() { 418 props.add(WD_PROP); 419 props.add(CONFIG_NAME_PROP); 420 } 421 422 /** 423 * Sets the work dir to the new specified value, inovkes applyWorkDir() 424 * method, notifies observers of the work dir change. 425 * <p> 426 * It's not recommended to override this method. 427 * @param wd - instance of WorkDirectory 428 * @param doRestoreConfig - flag to be passed via Event 429 * signaling whether restoring configuration from wd is required 430 */ 431 protected void updateWorkDir(WorkDirectory wd, boolean doRestoreConfig) { 432 if (this.wd == wd) { 433 return; // nothing to change 434 } 435 if (this.wd != null) { 436 throw new IllegalStateException(i18n.getString("bc.resetWorkDir.err")); 437 } 438 this.wd = wd; 439 applyWorkDir(wd); 440 notifyObservers(new E_NewWD(wd, doRestoreConfig)); 441 } 442 443 /** 444 * Associates session with the work dir. 445 * To be overridden when wd should be applied not only to session, but template 446 * or other properties. 447 * 448 * @param wd 449 */ 450 protected void applyWorkDir(WorkDirectory wd) { 451 if (config != null) { 452 config.setWorkDirectory(wd); 453 } 454 } 455 456 /** 457 * Method invoked as a reaction on U_NewConfig update. 458 * Checks if there are any changes in the update, if none - does nothing, 459 * Otherwise, copies new values into the main configuration instance, 460 * notifies observers with E_NewConfig event. 461 * 462 * @param ip 463 * @throws com.sun.javatest.exec.Session.Fault 464 */ 465 protected void updateNewConfig(InterviewParameters ip) throws Fault { 466 if (InterviewEditor.equal(ip, this.config) && 467 ip.getFile() != null && ip.getFile().equals(config.getFile())) { 468 return; // nothing to update 469 } 470 try { 471 InterviewEditor.copy(ip, this.config); 472 } catch (Exception e) { 473 throw new Fault(e); 474 } 475 notifyObservers(new E_NewConfig(this.config)); 476 } 477 478 /** 479 * Reloads interview if out of date. 480 */ 481 public void reloadInterview() throws Fault { 482 ensureInterviewUpToDate(); 483 } 484 485 void ensureInterviewUpToDate() throws Fault { 486 try { 487 if (config.isFileNewer()) { 488 config.load(); 489 } 490 } catch (Exception e) { 491 throw new Fault(e); 492 } 493 } 494 /** 495 * @param time Time used in loading, in ms. 496 * @param wd Work directory associated, may not be null. 497 * @param msg The message to include with the time, may be null, but usually 498 * is the path to the session file that was loaded. 499 */ 500 private static void logLoadTime(String res,final long time, WorkDirectory wd, String msg) { 501 if (wd == null) 502 return; 503 504 Logger log = null; 505 try { 506 log = wd.getTestSuite().createLog(wd, null, i18n.getString("exec.log.name")); 507 } 508 catch (TestSuite.DuplicateLogNameFault f) { 509 try { 510 log = wd.getTestSuite().getLog(wd, i18n.getString("exec.log.name")); 511 } 512 catch (TestSuite.NoSuchLogFault f2) { return; } 513 } 514 515 if (log != null) { 516 Integer loadTime = new Integer((int) (time / 1000)); 517 Object[] params = new Object[]{loadTime, msg}; 518 String output = i18n.getString(res, params); 519 log.info(output); 520 521 if (debug > 0) 522 Debug.println(output); 523 } 524 525 } 526 private static int debug = Debug.getInt(ExecTool.class); 527 528 }