001package jmri.configurexml; 002 003import java.io.File; 004import java.net.URL; 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.LinkedHashMap; 008import java.util.List; 009import java.util.Map; 010import jmri.InstanceManager; 011import jmri.jmrit.XmlFile; 012import jmri.jmrit.revhistory.FileHistory; 013import jmri.util.FileUtil; 014import org.jdom2.Attribute; 015import org.jdom2.Document; 016import org.jdom2.Element; 017import org.jdom2.ProcessingInstruction; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020 021/** 022 * Provides the mechanisms for storing an entire layout configuration to XML. 023 * "Layout" refers to the hardware: Specific communication systems, etc. 024 * 025 * @see <a href="package-summary.html">Package summary for details of the 026 * overall structure</a> 027 * @author Bob Jacobsen Copyright (c) 2002, 2008 028 */ 029public class ConfigXmlManager extends jmri.jmrit.XmlFile 030 implements jmri.ConfigureManager { 031 032 /** 033 * Define the current schema version string for the layout-config schema. 034 * See the <a href="package-summary.html#schema">Schema versioning 035 * discussion</a>. Also controls the stylesheet file version. 036 */ 037 static final public String schemaVersion = "-5-5-5"; 038 039 public ConfigXmlManager() { 040 } 041 042 /** {@inheritDoc} */ 043 @Override 044 public void registerConfig(Object o) { 045 registerConfig(o, 50); 046 } 047 048 /** {@inheritDoc} */ 049 @Override 050 public void registerPref(Object o) { 051 // skip if already present, leaving in original order 052 if (plist.contains(o)) { 053 return; 054 } 055 confirmAdapterAvailable(o); 056 // and add to list 057 plist.add(o); 058 } 059 060 /** 061 * Common check routine to confirm an adapter is available as part of 062 * registration process. 063 * <p> 064 * Note: Should only be called for debugging purposes, for example, when 065 * Log4J DEBUG level is selected, to load fewer classes at startup. 066 * 067 * @param o object to confirm XML adapter exists for 068 */ 069 void confirmAdapterAvailable(Object o) { 070 if (log.isDebugEnabled()) { 071 String adapter = adapterName(o); 072 log.debug("register {} adapter {}", o, adapter); 073 if (adapter != null) { 074 try { 075 Class.forName(adapter); 076 } catch (ClassNotFoundException | NoClassDefFoundError ex) { 077 locateClassFailed(ex, adapter, o); 078 } 079 } 080 } 081 } 082 083 /** 084 * Handles ConfigureXml classes that have moved to a new package or been 085 * superseded. 086 * 087 * @param name name of the moved or superceded ConfigureXml class 088 * @return name of the ConfigureXml class in newer package or of superseding 089 * class 090 */ 091 static public String currentClassName(String name) { 092 return InstanceManager.getDefault(ClassMigrationManager.class).getClassName(name); 093 } 094 095 /** {@inheritDoc} */ 096 @Override 097 public void removePrefItems() { 098 log.debug("removePrefItems dropped {}", plist.size()); 099 plist.clear(); 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public Object findInstance(Class<?> c, int index) { 105 List<Object> temp = new ArrayList<>(plist); 106 temp.addAll(clist.keySet()); 107 temp.addAll(tlist); 108 temp.addAll(ulist); 109 temp.addAll(uplist); 110 for (Object o : temp) { 111 if (c.isInstance(o)) { 112 if (index-- == 0) { 113 return o; 114 } 115 } 116 } 117 return null; 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public List<Object> getInstanceList(Class<?> c) { 123 List<Object> result = new ArrayList<>(); 124 125 List<Object> temp = new ArrayList<>(plist); 126 temp.addAll(clist.keySet()); 127 temp.addAll(tlist); 128 temp.addAll(ulist); 129 temp.addAll(uplist); 130 for (Object o : temp) { 131 if (c.isInstance(o)) { 132 result.add(o); 133 } 134 } 135 return result; 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public void registerConfig(Object o, int x) { 141 // skip if already present, leaving in original order 142 if (clist.containsKey(o)) { 143 return; 144 } 145 confirmAdapterAvailable(o); 146 // and add to list 147 clist.put(o, x); 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public void registerTool(Object o) { 153 // skip if already present, leaving in original order 154 if (tlist.contains(o)) { 155 return; 156 } 157 confirmAdapterAvailable(o); 158 // and add to list 159 tlist.add(o); 160 } 161 162 /** 163 * Register an object whose state is to be tracked. It is not an error if 164 * the original object was already registered. 165 * 166 * @param o The object, which must have an associated adapter class. 167 */ 168 @Override 169 public void registerUser(Object o) { 170 // skip if already present, leaving in original order 171 if (ulist.contains(o)) { 172 return; 173 } 174 confirmAdapterAvailable(o); 175 // and add to list 176 ulist.add(o); 177 } 178 179 /** {@inheritDoc} */ 180 @Override 181 public void registerUserPrefs(Object o) { 182 // skip if already present, leaving in original order 183 if (uplist.contains(o)) { 184 return; 185 } 186 confirmAdapterAvailable(o); 187 // and add to list 188 uplist.add(o); 189 } 190 191 /** {@inheritDoc} */ 192 @Override 193 public void deregister(Object o) { 194 plist.remove(o); 195 if (o != null) { 196 clist.remove(o); 197 } 198 tlist.remove(o); 199 ulist.remove(o); 200 uplist.remove(o); 201 } 202 203 private List<Object> plist = new ArrayList<>(); 204 Map<Object, Integer> clist = Collections.synchronizedMap(new LinkedHashMap<>()); 205 private List<Object> tlist = new ArrayList<>(); 206 private List<Object> ulist = new ArrayList<>(); 207 private List<Object> uplist = new ArrayList<>(); 208 private final List<Element> loadDeferredList = new ArrayList<>(); 209 210 /** 211 * Find the name of the adapter class for an object. 212 * 213 * @param o object of a configurable type 214 * @return class name of adapter 215 */ 216 public static String adapterName(Object o) { 217 String className = o.getClass().getName(); 218 log.trace("handle object of class {}", className); 219 int lastDot = className.lastIndexOf("."); 220 if (lastDot > 0) { 221 // found package-class boundary OK 222 String result = className.substring(0, lastDot) 223 + ".configurexml." 224 + className.substring(lastDot + 1, className.length()) 225 + "Xml"; 226 log.trace("adapter class name is {}", result); 227 return result; 228 } else { 229 // no last dot found! 230 log.error("No package name found, which is not yet handled!"); 231 return null; 232 } 233 } 234 235 /** 236 * Handle failure to load adapter class. Although only a one-liner in this 237 * class, it is a separate member to facilitate testing. 238 * 239 * @param ex the exception throw failing to load adapterName as o 240 * @param adapterName name of the adapter class 241 * @param o adapter object 242 */ 243 void locateClassFailed(Throwable ex, String adapterName, Object o) { 244 log.error("{} could not load adapter class {}", ex, adapterName); 245 log.debug("Stack trace is", ex); 246 } 247 248 protected Element initStore() { 249 Element root = new Element("layout-config"); 250 root.setAttribute("noNamespaceSchemaLocation", 251 "http://jmri.org/xml/schema/layout" + schemaVersion + ".xsd", 252 org.jdom2.Namespace.getNamespace("xsi", 253 "http://www.w3.org/2001/XMLSchema-instance")); 254 return root; 255 } 256 257 protected void addPrefsStore(Element root) { 258 for (int i = 0; i < plist.size(); i++) { 259 Object o = plist.get(i); 260 Element e = elementFromObject(o); 261 if (e != null) { 262 root.addContent(e); 263 } 264 } 265 } 266 267 protected boolean addConfigStore(Element root) { 268 boolean result = true; 269 List<Map.Entry<Object, Integer>> l = new ArrayList<>(clist.entrySet()); 270 Collections.sort(l, (Map.Entry<Object, Integer> o1, Map.Entry<Object, Integer> o2) -> o1.getValue().compareTo(o2.getValue())); 271 for (int i = 0; i < l.size(); i++) { 272 try { 273 Object o = l.get(i).getKey(); 274 Element e = elementFromObject(o); 275 if (e != null) { 276 root.addContent(e); 277 } 278 } catch (Exception e) { 279 storingErrorEncountered(null, "storing to file in addConfigStore", 280 "Exception thrown", null, null, e); 281 result = false; 282 } 283 } 284 return result; 285 } 286 287 protected boolean addToolsStore(Element root) { 288 boolean result = true; 289 for (Object o : tlist) { 290 try { 291 Element e = elementFromObject(o); 292 if (e != null) { 293 root.addContent(e); 294 } 295 } catch (Exception e) { 296 result = false; 297 storingErrorEncountered(null, "storing to file in addToolsStore", 298 "Exception thrown", null, null, e); 299 } 300 } 301 return result; 302 } 303 304 protected boolean addUserStore(Element root) { 305 boolean result = true; 306 for (Object o : ulist) { 307 try { 308 Element e = elementFromObject(o); 309 if (e != null) { 310 root.addContent(e); 311 } 312 } catch (Exception e) { 313 result = false; 314 storingErrorEncountered(null, "storing to file in addUserStore", 315 "Exception thrown", null, null, e); 316 } 317 } 318 return result; 319 } 320 321 protected void addUserPrefsStore(Element root) { 322 for (Object o : uplist) { 323 Element e = elementFromObject(o); 324 if (e != null) { 325 root.addContent(e); 326 } 327 } 328 } 329 330 protected void includeHistory(Element root, File file) { 331 // add history to end of document 332 if (InstanceManager.getNullableDefault(FileHistory.class) != null) { 333 root.addContent(jmri.jmrit.revhistory.configurexml.FileHistoryXml.storeDirectly( 334 InstanceManager.getDefault(FileHistory.class), file.getPath())); 335 } 336 } 337 338 protected boolean finalStore(Element root, File file) { 339 try { 340 // Document doc = newDocument(root, dtdLocation+"layout-config-"+dtdVersion+".dtd"); 341 Document doc = newDocument(root); 342 343 // add XSLT processing instruction 344 // <?xml-stylesheet type="text/xsl" href="XSLT/panelfile"+schemaVersion+".xsl"?> 345 java.util.Map<String, String> m = new java.util.HashMap<>(); 346 m.put("type", "text/xsl"); 347 m.put("href", xsltLocation + "panelfile" + schemaVersion + ".xsl"); 348 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 349 doc.addContent(0, p); 350 351 // add version at front 352 storeVersion(root); 353 354 writeXML(file, doc); 355 } catch (java.io.FileNotFoundException ex3) { 356 storingErrorEncountered(null, "storing to file " + file.getName(), 357 "File not found " + file.getName(), null, null, ex3); 358 log.error("FileNotFound error writing file: {}", ex3.getLocalizedMessage()); 359 return false; 360 } catch (java.io.IOException ex2) { 361 storingErrorEncountered(null, "storing to file " + file.getName(), 362 "IO error writing file " + file.getName(), null, null, ex2); 363 log.error("IO error writing file: {}", ex2.getLocalizedMessage()); 364 return false; 365 } 366 return true; 367 } 368 369 /** {@inheritDoc} */ 370 @Override 371 public void storePrefs() { 372 storePrefs(prefsFile); 373 } 374 375 /** {@inheritDoc} */ 376 @Override 377 public void storePrefs(File file) { 378 synchronized (this) { 379 Element root = initStore(); 380 addPrefsStore(root); 381 finalStore(root, file); 382 } 383 } 384 385 /** {@inheritDoc} */ 386 @Override 387 public void storeUserPrefs(File file) { 388 synchronized (this) { 389 Element root = initStore(); 390 addUserPrefsStore(root); 391 finalStore(root, file); 392 } 393 } 394 395 /** 396 * Set location for preferences file. 397 * <p> 398 * File need not exist, but location must be writable when storePrefs() 399 * called. 400 * 401 * @param prefsFile new location for preferences file 402 */ 403 public void setPrefsLocation(File prefsFile) { 404 this.prefsFile = prefsFile; 405 } 406 File prefsFile; 407 408 /** {@inheritDoc} */ 409 @Override 410 public boolean storeConfig(File file) { 411 boolean result = true; 412 Element root = initStore(); 413 if (!addConfigStore(root)) { 414 result = false; 415 } 416 includeHistory(root, file); 417 if (!finalStore(root, file)) { 418 result = false; 419 } 420 return result; 421 } 422 423 /** {@inheritDoc} */ 424 @Override 425 public boolean storeUser(File file) { 426 boolean result = true; 427 Element root = initStore(); 428 if (!addConfigStore(root)) { 429 result = false; 430 } 431 if (!addUserStore(root)) { 432 result = false; 433 } 434 includeHistory(root, file); 435 if (!finalStore(root, file)) { 436 result = false; 437 } 438 return result; 439 } 440 441 /** {@inheritDoc} */ 442 @Override 443 public boolean makeBackup(File file) { 444 return makeBackupFile(FileUtil.getUserFilesPath() + "backupPanels", file); 445 } 446 447 /** 448 * 449 * @param o The object to get an XML representation of 450 * @return An XML element representing o 451 */ 452 static public Element elementFromObject(Object o) { 453 return ConfigXmlManager.elementFromObject(o, true); 454 } 455 456 /** 457 * 458 * @param object The object to get an XML representation of 459 * @param shared true if the XML should be shared, false if the XML should 460 * be per-node 461 * @return An XML element representing object 462 */ 463 static public Element elementFromObject(Object object, boolean shared) { 464 String aName = adapterName(object); 465 log.debug("store using {}", aName); 466 XmlAdapter adapter = null; 467 try { 468 adapter = (XmlAdapter) Class.forName(adapterName(object)).getDeclaredConstructor().newInstance(); 469 } catch (ClassNotFoundException | IllegalAccessException | InstantiationException 470 | NoSuchMethodException | java.lang.reflect.InvocationTargetException ex) { 471 log.error("Cannot load configuration adapter for {}", object.getClass().getName(), ex); 472 } 473 if (adapter != null) { 474 return adapter.store(object, shared); 475 } else { 476 log.error("Cannot store configuration for {}", object.getClass().getName()); 477 return null; 478 } 479 } 480 481 private void storeVersion(Element root) { 482 // add version at front 483 root.addContent(0, 484 new Element("jmriversion") 485 .addContent(new Element("major").addContent("" + jmri.Version.major)) 486 .addContent(new Element("minor").addContent("" + jmri.Version.minor)) 487 .addContent(new Element("test").addContent("" + jmri.Version.test)) 488 .addContent(new Element("modifier").addContent(jmri.Version.getModifier())) 489 ); 490 } 491 492 /** 493 * Load a file. 494 * <p> 495 * Handles problems locally to the extent that it can, by routing them to 496 * the creationErrorEncountered method. 497 * 498 * @param fi file to load 499 * @return true if no problems during the load 500 * @throws jmri.configurexml.JmriConfigureXmlException if unable to load 501 * file 502 */ 503 @Override 504 public boolean load(File fi) throws JmriConfigureXmlException { 505 return load(fi, false); 506 } 507 508 /** {@inheritDoc} */ 509 @Override 510 public boolean load(URL url) throws JmriConfigureXmlException { 511 return load(url, false); 512 } 513 514 /** 515 * Load a file. 516 * <p> 517 * Handles problems locally to the extent that it can, by routing them to 518 * the creationErrorEncountered method. 519 * 520 * @param fi file to load 521 * @param registerDeferred true to register objects to defer 522 * @return true if no problems during the load 523 * @throws JmriConfigureXmlException if problem during load 524 * @see jmri.configurexml.XmlAdapter#loadDeferred() 525 * @since 2.11.2 526 */ 527 @Override 528 public boolean load(File fi, boolean registerDeferred) throws JmriConfigureXmlException { 529 return this.load(FileUtil.fileToURL(fi), registerDeferred); 530 } 531 532 /** 533 * Load a file. 534 * <p> 535 * Handles problems locally to the extent that it can, by routing them to 536 * the creationErrorEncountered method. 537 * <p> 538 * Always processes on Swing thread 539 * 540 * @param url URL of file to load 541 * @param registerDeferred true to register objects to defer 542 * @return true if no problems during the load 543 * @throws JmriConfigureXmlException if problem during load 544 * @see jmri.configurexml.XmlAdapter#loadDeferred() 545 * @since 3.3.2 546 */ 547 @Override 548 public boolean load(URL url, boolean registerDeferred) throws JmriConfigureXmlException { 549 log.trace("starting load({}, {})", url, registerDeferred); 550 551 // we do the actual load on the Swing thread in case it changes visible windows 552 Boolean retval = jmri.util.ThreadingUtil.runOnGUIwithReturn(() -> { 553 try { 554 Boolean ret = loadOnSwingThread(url, registerDeferred); 555 return ret; 556 } catch (Exception e) { 557 log.trace(" ending load() via JmriConfigureXmlException"); 558 throw new RuntimeException(e); 559 } 560 }); 561 562 log.trace(" ending load({}, {} with {})", url, registerDeferred, retval); 563 return retval; 564 } 565 566 private XmlFile.Validate validate = XmlFile.Validate.CheckDtdThenSchema; 567 568 /** {@inheritDoc} */ 569 @Override 570 public void setValidate(XmlFile.Validate v) { 571 validate = v; 572 } 573 574 /** {@inheritDoc} */ 575 @Override 576 public XmlFile.Validate getValidate() { 577 return validate; 578 } 579 580 // must run on GUI thread only; that's ensured at the using level. 581 private Boolean loadOnSwingThread(URL url, boolean registerDeferred) throws JmriConfigureXmlException { 582 boolean result = true; 583 Element root = null; 584 /* We will put all the elements into a load list, along with the load order 585 As XML files prior to 2.13.1 had no order to the store, beans would be stored/loaded 586 before beans that they were dependant upon had been stored/loaded 587 */ 588 Map<Element, Integer> loadlist = Collections.synchronizedMap(new LinkedHashMap<>()); 589 590 try { 591 setValidate(validate); 592 root = super.rootFromURL(url); 593 // get the objects to load 594 List<Element> items = root.getChildren(); 595 for (Element item : items) { 596 //Put things into an ordered list 597 Attribute a = item.getAttribute("class"); 598 if (a == null) { 599 // this is an element that we're not meant to read 600 log.debug("skipping {}", item); 601 continue; 602 } 603 String adapterName = a.getValue(); 604 log.debug("attempt to get adapter {} for {}", adapterName, item); 605 adapterName = currentClassName(adapterName); 606 XmlAdapter adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 607 int order = adapter.loadOrder(); 608 log.debug("add {} to load list with order id of {}", item, order); 609 loadlist.put(item, order); 610 } 611 612 List<Map.Entry<Element, Integer>> l = new ArrayList<>(loadlist.entrySet()); 613 Collections.sort(l, (Map.Entry<Element, Integer> o1, Map.Entry<Element, Integer> o2) -> o1.getValue().compareTo(o2.getValue())); 614 615 for (Map.Entry<Element, Integer> elementIntegerEntry : l) { 616 Element item = elementIntegerEntry.getKey(); 617 String adapterName = item.getAttribute("class").getValue(); 618 adapterName = currentClassName(adapterName); 619 log.debug("load {} via {}", item, adapterName); 620 XmlAdapter adapter = null; 621 try { 622 adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 623 624 // get version info 625 // loadVersion(root, adapter); 626 // and do it 627 if (adapter.loadDeferred() && registerDeferred) { 628 // register in the list for deferred load 629 loadDeferredList.add(item); 630 log.debug("deferred load registered for {} {}", item, adapterName); 631 } else { 632 boolean loadStatus = adapter.load(item, item); 633 log.debug("load status for {} {} is {}", item, adapterName, loadStatus); 634 635 // if any adaptor load fails, then the entire load has failed 636 if (!loadStatus) { 637 result = false; 638 } 639 } 640 } catch (Exception e) { 641 creationErrorEncountered(adapter, "load(" + url.getFile() + ")", "Unexpected error (Exception)", null, null, e); 642 643 result = false; // keep going, but return false to signal problem 644 } catch (Throwable et) { 645 creationErrorEncountered(adapter, "in load(" + url.getFile() + ")", "Unexpected error (Throwable)", null, null, et); 646 647 result = false; // keep going, but return false to signal problem 648 } 649 } 650 651 } catch (java.io.FileNotFoundException e1) { 652 // this returns false to indicate un-success, but not enough 653 // of an error to require a message 654 creationErrorEncountered(null, "opening file " + url.getFile(), 655 "File not found", null, null, e1); 656 result = false; 657 } catch (org.jdom2.JDOMException e) { 658 creationErrorEncountered(null, "parsing file " + url.getFile(), 659 "Parse error", null, null, e); 660 result = false; 661 } catch (java.io.IOException e) { 662 creationErrorEncountered(null, "loading from file " + url.getFile(), 663 "IOException", null, null, e); 664 result = false; 665 } catch (ClassNotFoundException e) { 666 creationErrorEncountered(null, "loading from file " + url.getFile(), 667 "ClassNotFoundException", null, null, e); 668 result = false; 669 } catch (InstantiationException e) { 670 creationErrorEncountered(null, "loading from file " + url.getFile(), 671 "InstantiationException", null, null, e); 672 result = false; 673 } catch (IllegalAccessException e) { 674 creationErrorEncountered(null, "loading from file " + url.getFile(), 675 "IllegalAccessException", null, null, e); 676 result = false; 677 } catch (NoSuchMethodException e) { 678 creationErrorEncountered(null, "loading from file " + url.getFile(), 679 "NoSuchMethodException", null, null, e); 680 result = false; 681 } catch (java.lang.reflect.InvocationTargetException e) { 682 creationErrorEncountered(null, "loading from file " + url.getFile(), 683 "InvocationTargetException", null, null, e); 684 result = false; 685 } finally { 686 // no matter what, close error reporting 687 handler.done(); 688 } 689 690 // loading complete, as far as it got, make history entry 691 FileHistory r = InstanceManager.getNullableDefault(FileHistory.class); 692 if (r != null) { 693 FileHistory included = null; 694 if (root != null) { 695 Element filehistory = root.getChild("filehistory"); 696 if (filehistory != null) { 697 included = jmri.jmrit.revhistory.configurexml.FileHistoryXml.loadFileHistory(filehistory); 698 } 699 } 700 String friendlyName = url.getFile().replaceAll("%20", " "); 701 r.addOperation((result ? "Load OK" : "Load with errors"), friendlyName, included); 702 } else { 703 log.info("Not recording file history"); 704 } 705 return result; 706 } 707 708 /** {@inheritDoc} */ 709 @Override 710 public boolean loadDeferred(File fi) { 711 return this.loadDeferred(FileUtil.fileToURL(fi)); 712 } 713 714 /** {@inheritDoc} */ 715 @Override 716 public boolean loadDeferred(URL url) { 717 boolean result = true; 718 // Now process the load-later list 719 log.debug("Start processing deferred load list (size): {}", loadDeferredList.size()); 720 if (!loadDeferredList.isEmpty()) { 721 for (Element item : loadDeferredList) { 722 String adapterName = item.getAttribute("class").getValue(); 723 log.debug("deferred load via {}", adapterName); 724 XmlAdapter adapter = null; 725 try { 726 adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 727 boolean loadStatus = adapter.load(item, item); 728 log.debug("deferred load status for {} is {}", adapterName, loadStatus); 729 730 // if any adaptor load fails, then the entire load has failed 731 if (!loadStatus) { 732 result = false; 733 } 734 } catch (Exception e) { 735 creationErrorEncountered(adapter, "deferred load(" + url.getFile() + ")", 736 "Unexpected error (Exception)", null, null, e); 737 result = false; // keep going, but return false to signal problem 738 } catch (Throwable et) { 739 creationErrorEncountered(adapter, "in deferred load(" + url.getFile() + ")", 740 "Unexpected error (Throwable)", null, null, et); 741 result = false; // keep going, but return false to signal problem 742 } 743 } 744 } 745 log.debug("Done processing deferred load list with result: {}", result); 746 return result; 747 } 748 749 /** 750 * Find a file by looking 751 * <ul> 752 * <li> in xml/layout/ in the preferences directory, if that exists 753 * <li> in xml/layout/ in the application directory, if that exists 754 * <li> in xml/ in the preferences directory, if that exists 755 * <li> in xml/ in the application directory, if that exists 756 * <li> at top level in the application directory 757 * </ul> 758 * 759 * @param f Local filename, perhaps without path information 760 * @return Corresponding File object 761 */ 762 @Override 763 public URL find(String f) { 764 URL u = FileUtil.findURL(f, "xml/layout", "xml"); // NOI18N 765 if (u == null) { 766 this.locateFileFailed(f); 767 } 768 return u; 769 } 770 771 /** 772 * Report a failure to find a file. This is a separate member to ease 773 * testing. 774 * 775 * @param f Name of file not located. 776 */ 777 void locateFileFailed(String f) { 778 log.warn("Could not locate file {}", f); 779 } 780 781 /** 782 * Invoke common handling of errors that happen during the "load" process. 783 * <p> 784 * Exceptions passed into this are absorbed. 785 * 786 * @param adapter Object that encountered the error (for reporting), may 787 * be null 788 * @param operation description of the operation being attempted, may be 789 * null 790 * @param description description of error encountered 791 * @param systemName System name of bean being handled, may be null 792 * @param userName used name of the bean being handled, may be null 793 * @param exception Any exception being handled in the processing, may be 794 * null 795 */ 796 static public void creationErrorEncountered( 797 XmlAdapter adapter, 798 String operation, 799 String description, 800 String systemName, 801 String userName, 802 Throwable exception) { 803 // format and log a message (note reordered from arguments) 804// System.out.format("creationErrorEncountered: %s%n", exception.getMessage()); 805// System.out.format("creationErrorEncountered: %s, %s, %s, %s, %s, %s%n", adapter, operation, description, systemName, userName, exception == null ? null : exception.getMessage()); 806 ErrorMemo e = new ErrorMemo( 807 adapter, operation, description, 808 systemName, userName, exception, "loading"); 809 if (adapter != null) { 810 ErrorHandler aeh = adapter.getExceptionHandler(); 811 if (aeh != null) { 812 aeh.handle(e); 813 } 814 } else { 815 handler.handle(e); 816 } 817 } 818 819 /** 820 * Invoke common handling of errors that happen during the "store" process. 821 * <p> 822 * Exceptions passed into this are absorbed. 823 * 824 * @param adapter Object that encountered the error (for reporting), may 825 * be null 826 * @param operation description of the operation being attempted, may be 827 * null 828 * @param description description of error encountered 829 * @param systemName System name of bean being handled, may be null 830 * @param userName used name of the bean being handled, may be null 831 * @param exception Any exception being handled in the processing, may be 832 * null 833 */ 834 static public void storingErrorEncountered( 835 XmlAdapter adapter, 836 String operation, 837 String description, 838 String systemName, 839 String userName, 840 Throwable exception) { 841 // format and log a message (note reordered from arguments) 842 ErrorMemo e = new ErrorMemo( 843 adapter, operation, description, 844 systemName, userName, exception, "storing"); 845 if (adapter != null) { 846 ErrorHandler aeh = adapter.getExceptionHandler(); 847 if (aeh != null) { 848 aeh.handle(e); 849 } 850 } else { 851 handler.handle(e); 852 } 853 } 854 855 private static ErrorHandler handler = new ErrorHandler(); 856 857 static public void setErrorHandler(ErrorHandler handler) { 858 ConfigXmlManager.handler = handler; 859 } 860 861 /** 862 * @return the loadDeferredList 863 */ 864 protected List<Element> getLoadDeferredList() { 865 return loadDeferredList; 866 } 867 868 // initialize logging 869 private final static Logger log = LoggerFactory.getLogger(ConfigXmlManager.class); 870 871}