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