1 22 package org.jboss.system.pm; 23 24 import java.beans.PropertyEditor ; 25 import java.beans.PropertyEditorManager ; 26 import java.io.ByteArrayInputStream ; 27 import java.io.ByteArrayOutputStream ; 28 import java.io.File ; 29 import java.io.FileInputStream ; 30 import java.io.FileOutputStream ; 31 import java.io.FilenameFilter ; 32 import java.io.IOException ; 33 import java.io.InputStream ; 34 import java.io.ObjectInputStream ; 35 import java.io.ObjectOutputStream ; 36 import java.io.OutputStream ; 37 import java.io.Serializable ; 38 import java.net.URL ; 39 import java.text.SimpleDateFormat ; 40 import java.util.Collections ; 41 import java.util.Date ; 42 import java.util.HashMap ; 43 import java.util.Map ; 44 45 import javax.management.Attribute ; 46 import javax.management.AttributeList ; 47 import javax.xml.parsers.DocumentBuilder ; 48 import javax.xml.parsers.DocumentBuilderFactory ; 49 import javax.xml.transform.OutputKeys ; 50 import javax.xml.transform.Result ; 51 import javax.xml.transform.Source ; 52 import javax.xml.transform.Transformer ; 53 import javax.xml.transform.TransformerFactory ; 54 import javax.xml.transform.dom.DOMSource ; 55 import javax.xml.transform.stream.StreamResult ; 56 57 import org.jboss.logging.Logger; 58 import org.jboss.mx.persistence.AttributePersistenceManager; 59 import org.jboss.system.server.ServerConfigLocator; 60 import org.jboss.util.file.Files; 61 import org.w3c.dom.Comment ; 62 import org.w3c.dom.Document ; 63 import org.w3c.dom.Element ; 64 import org.w3c.dom.Node ; 65 import org.w3c.dom.NodeList ; 66 import org.w3c.dom.Text ; 67 68 74 public class XMLAttributePersistenceManager 75 implements AttributePersistenceManager 76 { 77 79 80 public static final String DATA_DIR_ELEMENT = "data-directory"; 81 82 83 public static final String DEFAULT_BASE_DIR = "data/xmbean-attrs"; 84 85 86 public static final String AL_ROOT_ELEMENT = "attribute-list"; 87 public static final String AL_ID_ATTRIBUTE = "id"; 88 public static final String AL_DATE_ATTRIBUTE = "date"; 89 public static final String AL_ATTRIBUTE_ELEMENT = "attribute"; 90 public static final String AL_NAME_ATTRIBUTE = "name"; 91 public static final String AL_TYPE_ATTRIBUTE = "type"; 92 public static final String AL_NULL_ATTRIBUTE = "null"; 93 public static final String AL_SERIALIZED_ATTRIBUTE = "serialized"; 94 public static final String AL_TRUE_VALUE = "true"; 95 public static final String AL_FALSE_VALUE = "false"; 96 97 99 100 private static final Logger log = Logger.getLogger(XMLAttributePersistenceManager.class); 101 102 103 private static final SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyyMMddHHmmss"); 104 105 106 private static final char[] hexDigits = new char[] 107 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 108 109 110 private File dataDir; 111 112 113 private boolean state; 114 115 116 private Map idMap; 117 118 120 123 public XMLAttributePersistenceManager() 124 { 125 log.debug("Constructed"); 126 } 127 128 130 154 public void create(String version, Element config) 155 throws Exception 156 { 157 if (getState()) { 159 return; 160 } 161 162 String baseDir = null; 164 165 if (config == null) { 166 baseDir = DEFAULT_BASE_DIR; 167 } 168 else { 169 if (!config.getTagName().equals(DATA_DIR_ELEMENT)) { 170 throw new Exception ("expected '" + DATA_DIR_ELEMENT + 171 "' XML configuration element, got '" + 172 config.getTagName() + "'"); 173 } 174 else { 175 baseDir = getElementContent(config); 176 } 177 } 178 179 this.dataDir = initDataDir(baseDir, version); 181 182 log.debug("Using data directory: " + this.dataDir.getCanonicalPath()); 183 184 this.idMap = Collections.synchronizedMap(new HashMap ()); 186 187 setState(true); 189 } 190 191 198 public boolean getState() 199 { 200 return this.state; 201 } 202 203 211 public void destroy() 212 { 213 setState(false); 214 215 this.dataDir = null; 217 this.idMap = null; 218 } 219 220 222 231 public void store(String id, AttributeList attrs) 232 throws Exception 233 { 234 log.debug("store(" + id + ") attrs=" + attrs); 235 236 checkActiveState(); 238 239 String origId = id; 241 id = mapId(id); 242 243 if (attrs == null) 244 throw new Exception ("store() called with null AttributeList"); 245 246 File file = checkFileForWrite(id); 248 249 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 251 Document doc = builder.newDocument(); 252 253 Comment comment = doc.createComment(" automatically produced by XMLAttributePersistenceManager "); 255 doc.appendChild(comment); 256 257 Element root = doc.createElement(AL_ROOT_ELEMENT); 259 root.setAttribute(AL_ID_ATTRIBUTE, origId); 260 root.setAttribute(AL_DATE_ATTRIBUTE, dateFormat.format(new Date ())); 261 doc.appendChild(root); 262 263 for (int i = 0; i < attrs.size(); i++) { 265 266 Attribute attr = (Attribute )attrs.get(i); 267 268 String name = attr.getName(); 269 Object value = attr.getValue(); 270 271 Element element = doc.createElement(AL_ATTRIBUTE_ELEMENT); 273 element.setAttribute(AL_NAME_ATTRIBUTE, name); 274 275 if (value == null) { 276 element.setAttribute(AL_NULL_ATTRIBUTE, AL_TRUE_VALUE); 278 279 root.appendChild(element); 281 } 282 else if (value instanceof org.w3c.dom.Element ) { 283 element.setAttribute(AL_TYPE_ATTRIBUTE, "org.w3c.dom.Element"); 285 286 Node copy = doc.importNode((org.w3c.dom.Element )value, true); 287 element.appendChild(copy); 288 289 root.appendChild(element); 291 } 292 else { 293 Class clazz = value.getClass(); 294 String type = clazz.getName(); 295 PropertyEditor peditor = PropertyEditorManager.findEditor(clazz); 296 297 if (peditor != null) { 298 peditor.setValue(value); 300 301 element.setAttribute(AL_TYPE_ATTRIBUTE, type); 302 element.appendChild(doc.createTextNode(peditor.getAsText())); 303 304 root.appendChild(element); 306 } 307 else if (value instanceof Serializable ) { 308 String encoded = encodeAsHexString((Serializable )value); 311 312 if (encoded != null) { 313 element.setAttribute(AL_TYPE_ATTRIBUTE, type); 314 element.setAttribute(AL_SERIALIZED_ATTRIBUTE, AL_TRUE_VALUE); 315 element.appendChild(doc.createTextNode(encoded)); 316 317 root.appendChild(element); 319 } 320 else { 321 root.appendChild(doc.createComment( 323 " WARN <attribute name=\"" + name + "\" type=\"" + type + 324 "\"/> could not be serialized ")); 325 326 log.warn("Could not serialize attribute '" + name + 327 "' of type '" + type + "' and value: " + value); 328 } 329 } 330 else { 331 root.appendChild(doc.createComment( 333 " WARN <attribute name=\"" + name + "\" type=\"" + type + 334 "\"/> could not be persisted ")); 335 336 log.warn("Could not find a way to persist attribute '" + name + 337 "' of type '" + type + "' and value: " + value); 338 } 339 } 340 } 341 342 try { 344 outputXmlFile(doc, file); 345 } 346 catch (Exception e) { 347 log.warn("Cannot persist AttributeList to: \"" + id + "\"", e); 348 throw e; 349 } 350 } 351 352 362 public AttributeList load(String id) 363 throws Exception 364 { 365 log.debug("load(" + id + ")"); 366 367 checkActiveState(); 369 370 id = mapId(id); 372 373 if (!getState()) 374 return null; 375 376 AttributeList attrs = null; 378 379 File file = checkFileForRead(id); 381 382 if (file != null) { 383 Document doc = parseXmlFile(file); 385 386 NodeList docList = doc.getChildNodes(); 388 Element root = null; 389 390 for (int i = 0; i < docList.getLength(); i++) { 391 Node node = docList.item(i); 392 393 if (node.getNodeType() == Node.ELEMENT_NODE && 394 node.getNodeName().equals(AL_ROOT_ELEMENT)) { 395 396 root = (Element )node; 397 break; } 399 } 400 401 if (root == null) { 403 throw new Exception ("Expected XML element: " + AL_ROOT_ELEMENT); 404 } 405 else { 406 attrs = new AttributeList (); 409 410 NodeList rootList = root.getChildNodes(); 411 412 for (int i = 0; i < rootList.getLength(); i++) { 413 Node node = rootList.item(i); 414 415 if (node.getNodeType() == Node.ELEMENT_NODE && 417 node.getNodeName().equals(AL_ATTRIBUTE_ELEMENT)) { 418 419 Element element = (Element )node; 420 421 String name = element.getAttribute(AL_NAME_ATTRIBUTE); 423 424 if (!(name.length() > 0)) { 425 throw new Exception ("Attribute '" + AL_NAME_ATTRIBUTE + 426 "' must be specified for element '" + AL_ATTRIBUTE_ELEMENT + "'"); 427 } 428 429 431 if (element.getAttribute(AL_NULL_ATTRIBUTE).toLowerCase().equals(AL_TRUE_VALUE)) { 432 433 attrs.add(new Attribute (name, null)); 435 } 436 else if (element.getAttribute(AL_SERIALIZED_ATTRIBUTE).toLowerCase().equals(AL_TRUE_VALUE)) { 437 438 String hexStr = getElementContent(element); 440 Serializable obj = decodeFromHexString(hexStr); 441 442 if (obj == null) { 443 throw new Exception ("Failed to deserialize attribute '" + name + "'"); 444 } 445 else { 446 attrs.add(new Attribute (name, obj)); 447 } 448 } 449 else { 450 String type = element.getAttribute(AL_TYPE_ATTRIBUTE); 451 452 if (!(type.length() > 0)) { 454 throw new Exception ("Attribute '" + AL_TYPE_ATTRIBUTE + 455 "' must be specified for name='" + name + "'"); 456 } 457 458 if (type.equals("org.w3c.dom.Element")) { 459 460 462 NodeList nlist = element.getChildNodes(); 463 Element el = null; 464 465 for (int j = 0; j < nlist.getLength(); j++) { 466 467 Node n = nlist.item(j); 468 if (n.getNodeType() == Node.ELEMENT_NODE) 469 { 470 el = (Element )n; 471 break; 472 } 473 } 474 475 if (el != null) { 476 attrs.add(new Attribute (name, el.cloneNode(true))); 477 } 478 else { 479 attrs.add(new Attribute (name, null)); 480 } 481 } 482 else { 483 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 485 Class clazz = null; 486 487 try { 488 clazz = cl.loadClass(type); 489 } 490 catch (ClassNotFoundException e) { 491 throw new Exception ("Class not found for attribute '" + name + 492 "' of type '" + type + "'"); 493 } 494 495 PropertyEditor peditor = PropertyEditorManager.findEditor(clazz); 496 497 if (peditor != null) { 498 499 501 String value = getElementContent(element); 502 peditor.setAsText(value); 503 504 attrs.add(new Attribute (name, peditor.getValue())); 505 } 506 else { 507 throw new Exception ("Cannot find a way to load attribute '" + name + 508 "' of type '" + type + "'"); 509 } 510 } 511 } 512 } 513 } } 515 } 516 log.debug("load() returns with: " + attrs); 517 518 return attrs; 520 } 521 522 524 532 public boolean exists(String id) 533 throws Exception 534 { 535 checkActiveState(); 537 538 return (new File (this.dataDir, mapId(id))).isFile(); 539 } 540 541 547 public void remove(String id) 548 throws Exception 549 { 550 checkActiveState(); 552 553 (new File (this.dataDir, mapId(id))).delete(); 554 } 555 556 562 public void removeAll() 563 throws Exception 564 { 565 checkActiveState(); 567 568 String [] files = this.dataDir.list(new XMLFilter()); 569 570 if (files != null) { 571 for (int i = 0; i < files.length; i++) { 572 (new File (this.dataDir, files[i])).delete(); 573 } 574 } 575 } 576 577 584 public String [] listAll() 585 throws Exception 586 { 587 checkActiveState(); 589 590 String [] files = this.dataDir.list(new XMLFilter()); 591 String [] result = null; 592 593 if (files != null) { 594 result = new String [files.length]; 595 596 for (int i = 0; i < files.length; i++) { 597 result[i] = mapFile(files[i]); 598 } 599 } 600 return result; 601 } 602 603 605 610 private void setState(boolean state) 611 { 612 this.state = state; 613 } 614 615 625 private File initDataDir(String baseDir, String versionTag) 626 throws Exception 627 { 628 File dir = null; 629 630 632 try { 634 URL fileURL = new URL (baseDir); 635 636 File file = new File (fileURL.getFile()); 637 638 if(file.isDirectory() && file.canRead() && file.canWrite()) { 639 dir = file; 640 } 641 } 642 catch(Exception e) { 643 645 File homeDir = ServerConfigLocator.locate().getServerHomeDir(); 646 647 dir = new File (homeDir, baseDir); 648 649 dir.mkdirs(); 650 651 if (!dir.isDirectory()) 652 throw new Exception ("The base directory is not valid: " 653 + dir.getCanonicalPath()); 654 } 655 656 if (versionTag != null && !versionTag.equals("")) { 658 dir = new File (dir, versionTag); 659 660 dir.mkdirs(); 661 662 if (!dir.isDirectory()) 663 throw new Exception ("The data directory is not valid: " 664 + dir.getCanonicalPath()); 665 } 666 return dir; 667 } 668 669 676 private String encodeAsHexString(Serializable obj) 677 { 678 String retn = null; 679 680 if (obj != null) { 681 try { 682 ByteArrayOutputStream baos = new ByteArrayOutputStream (1024); 683 ObjectOutputStream oos = new ObjectOutputStream (baos); 684 685 oos.writeObject(obj); 686 byte[] bytes = baos.toByteArray(); 687 688 StringBuffer sbuf = new StringBuffer (1024); 689 690 for (int i = 0; i < bytes.length; i++) { 691 sbuf.append(hexDigits[ (bytes[i] >> 4) & 0xF ]); sbuf.append(hexDigits[ (bytes[i] ) & 0xF ]); } 694 695 retn = sbuf.toString(); 696 } 697 catch (IOException e) { 698 } 700 } 701 return retn; 702 } 703 704 709 private Serializable decodeFromHexString(String hexStr) 710 { 711 int len = hexStr.length() / 2; 713 byte[] bytes = new byte[len]; 714 715 for (int i = 0; i < len; i++) { 716 717 char h1 = hexStr.charAt(i * 2); char h2 = hexStr.charAt(i * 2 + 1); 720 int d1 = (h1 >= 'a') ? (10 + h1 - 'a') 722 : ((h1 >= 'A') ? (10 + h1 - 'A') 723 : (h1 - '0')); 724 725 int d2 = (h2 >= 'a') ? (10 + h2 - 'a') 726 : ((h2 >= 'A') ? (10 + h2 - 'A') 727 : (h2 - '0')); 728 729 bytes[i] = (byte)(d1 * 16 + d2); } 731 732 Serializable retn = null; 733 734 try { 735 ByteArrayInputStream bais = new ByteArrayInputStream (bytes); 736 ObjectInputStream ois = new ObjectInputStream (bais); 737 738 retn = (Serializable )ois.readObject(); 739 } 740 catch (IOException e) { 741 log.warn("Cannot deserialize object", e); } 742 catch (ClassNotFoundException e) { 743 log.warn("Cannot deserialize object", e); 744 } 745 746 return retn; 747 } 748 749 753 private void outputXmlFile(Document doc, File file) 754 throws Exception 755 { 756 Source source = new DOMSource (doc); 758 759 OutputStream out = new FileOutputStream (file); 761 762 Result result = new StreamResult (out); 764 765 Transformer xformer = TransformerFactory.newInstance().newTransformer(); 767 768 xformer.setOutputProperty(OutputKeys.INDENT, "yes"); 770 771 try 772 { 773 xformer.transform(source, result); 774 } 775 finally 776 { 777 out.close(); 779 } 780 } 781 782 786 private Document parseXmlFile(File file) 787 throws Exception 788 { 789 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 791 792 factory.setValidating(false); 795 796 DocumentBuilder builder = factory.newDocumentBuilder(); 798 Document doc; 799 800 InputStream in = new FileInputStream (file); 804 try 805 { 806 doc = builder.parse(in); 808 } 809 finally 810 { 811 in.close(); 813 } 814 return doc; 816 } 817 818 821 private File checkFileForWrite(String filename) 822 throws Exception 823 { 824 File file = new File (this.dataDir, filename); 825 826 if (file.isFile()) { 827 if (file.canRead() && file.canWrite()) { 828 return file; } 830 else { 831 throw new Exception ("file '" + filename + "' is not r/w"); } 833 } 834 else if (file.isDirectory()) { 835 throw new Exception (filename + " is a directory!"); } 837 else { 838 return file; } 840 } 841 842 847 private File checkFileForRead(String filename) 848 throws Exception 849 { 850 File file = new File (this.dataDir, filename); 851 852 if (file.isFile()) { 853 if (file.canRead() && file.canWrite()) { 854 return file; } 856 else { 857 throw new Exception ("file '" + filename + "' is not r/w"); } 859 } 860 else if (file.isDirectory()) { 861 throw new Exception (filename + " is a directory!"); } 863 else { 864 return null; } 866 } 867 868 872 private String getElementContent(Element element) 873 { 874 NodeList nlist = element.getChildNodes(); 875 876 StringBuffer sbuf = new StringBuffer (1024); 877 878 for (int i = 0; i < nlist.getLength(); i++) { 880 Node node = nlist.item(i); 881 882 if (node.getNodeType() == Node.TEXT_NODE) { 883 sbuf.append(((Text )node).getData()); 884 } 885 } 886 return sbuf.toString(); 887 } 888 889 892 private void checkActiveState() 893 { 894 if (!getState()) { 895 throw new IllegalStateException ("AttributePersistenceManager not active"); 896 } 897 } 898 899 902 private class XMLFilter 903 implements FilenameFilter 904 { 905 public boolean accept(File dir, String name) 906 { 907 return name.endsWith(".xml"); 908 } 909 } 910 911 916 private String mapId(String id) 917 throws Exception 918 { 919 if (id == null) { 920 throw new Exception ("called with null id"); 921 } 922 else { 923 String file = (String )this.idMap.get(id); 925 926 if (file == null) { 928 file = Files.encodeFileName(id) + ".xml"; 929 this.idMap.put(id, file); 930 } 931 return file; 932 } 933 } 934 935 938 private String mapFile(String file) 939 { 940 if (file == null) { 941 return null; 942 } 943 else { 944 file = file.substring(0, file.length() - 4); 946 947 return Files.decodeFileName(file); 948 } 949 } 950 } 951 | Popular Tags |