1 19 20 package org.openide.util; 21 22 import java.beans.PropertyChangeListener ; 23 import java.beans.PropertyChangeSupport ; 24 import java.io.Externalizable ; 25 import java.io.IOException ; 26 import java.io.ObjectInput ; 27 import java.io.ObjectInputStream ; 28 import java.io.ObjectOutput ; 29 import java.io.ObjectOutputStream ; 30 import java.io.ObjectStreamException ; 31 import java.io.Serializable ; 32 import java.lang.ref.WeakReference ; 33 import java.lang.reflect.Method ; 34 import java.security.AccessController ; 35 import java.security.PrivilegedActionException ; 36 import java.security.PrivilegedExceptionAction ; 37 import java.util.HashMap ; 38 import java.util.HashSet ; 39 import java.util.Map ; 40 import java.util.Set ; 41 import java.util.WeakHashMap ; 42 import java.util.logging.Level ; 43 import java.util.logging.Logger ; 44 45 52 public abstract class SharedClassObject extends Object implements Externalizable { 53 54 private static final long serialVersionUID = 4527891234589143259L; 55 56 57 private static final Object PROP_SUPPORT = new Object (); 58 59 60 private static final Map <Class ,DataEntry> values = new WeakHashMap <Class ,DataEntry>(37); 61 62 70 private static final Map <String ,Integer > instancesBeingCreated = new HashMap <String ,Integer >(7); 71 72 75 private static final Set <String > alreadyWarnedAboutDupes = new HashSet <String >(); 76 private static final Logger err = Logger.getLogger("org.openide.util.SharedClassObject"); 78 79 private final DataEntry dataEntry; 80 81 82 private Object lock; 83 84 88 private final SharedClassObject first; 89 90 93 private Throwable firstTrace = null; 94 95 100 private boolean systemOption = false; 101 102 106 private boolean waitingOnSystemOption = false; 107 private IllegalStateException prematureSystemOptionMutation = null; 108 private boolean inReadExternal = false; 109 110 111 private boolean addNotifySuper; 112 113 114 private boolean removeNotifySuper; 115 116 117 private boolean initializeSuper; 118 119 124 protected SharedClassObject() { 125 synchronized (getLock()) { 126 DataEntry de = values.get(getClass()); 127 128 if (de == null) { 130 de = new DataEntry(); 131 values.put(getClass(), de); 132 } 133 134 dataEntry = de; 135 de.increase(); 136 137 first = de.first(this); 139 } 140 141 if (first != null) { 142 if (first == this) { 143 if (err.isLoggable(Level.FINE)) { 145 Throwable t = new Throwable ("First instance created here"); t.fillInStackTrace(); 147 first.firstTrace = t; 148 } 149 } else { 150 String clazz = getClass().getName(); 151 boolean creating; 152 153 synchronized (instancesBeingCreated) { 154 creating = instancesBeingCreated.containsKey(clazz); 155 } 156 157 if (creating) { 158 } else { 160 if (!alreadyWarnedAboutDupes.contains(clazz)) { 161 alreadyWarnedAboutDupes.add(clazz); 162 163 Exception e = new IllegalStateException ( 164 "Warning: multiple instances of shared class " + clazz + " created." 165 ); 167 if (first.firstTrace != null) { 168 err.log(Level.WARNING, "First stack trace", first.firstTrace); 169 } else { 170 err.warning("(Run with -J-Dorg.openide.util.SharedClassObject.level=0 for more details.)"); } 172 173 err.log(Level.WARNING, null, e); 174 } 175 } 176 } 177 } 178 } 179 180 185 protected final void finalize() throws Throwable { 186 referenceLost(); 187 } 188 189 203 protected boolean clearSharedData() { 204 return true; 205 } 206 207 211 public final boolean equals(Object obj) { 212 return ((obj instanceof SharedClassObject) && (getClass().equals(obj.getClass()))); 213 } 214 215 218 public final int hashCode() { 219 return getClass().hashCode(); 220 } 221 222 227 protected final Object getLock() { 228 if (lock == null) { 229 lock = getClass().getName().intern(); 230 } 231 232 return lock; 233 } 234 235 239 private void referenceLost() { 240 synchronized (getLock()) { 245 if ((dataEntry == null) || (dataEntry.decrease() == 0)) { 246 if (clearSharedData()) { 247 values.remove(getClass()); 249 } 250 } 251 } 252 253 } 255 256 262 protected final Object putProperty(Object key, Object value) { 263 if (key == null) { 264 throw new NullPointerException ("Tried to pass null key (value=" + value + ") to putProperty"); } 266 267 synchronized (getLock()) { 268 if ( 269 waitingOnSystemOption && (key != PROP_SUPPORT) && (prematureSystemOptionMutation == null) && 270 !dataEntry.isInInitialize() && !inReadExternal 271 ) { 272 prematureSystemOptionMutation = new IllegalStateException ("...setting property here..."); } 277 278 return dataEntry.getMap(this).put(key, value); 279 280 } 282 } 283 284 303 protected final Object putProperty(String key, Object value, boolean notify) { 304 Object previous = putProperty(key, value); 305 306 if (notify) { 307 firePropertyChange(key, previous, value); 308 } 309 310 return previous; 311 } 312 313 318 protected final Object getProperty(Object key) { 319 synchronized (getLock()) { 320 if ("org.openide.util.SharedClassObject.initialize".equals(key)) { 323 return dataEntry.isInInitialize() ? Boolean.TRUE : null; 324 } 325 326 return dataEntry.get(this, key); 327 } 328 } 329 330 336 protected void initialize() { 337 initializeSuper = true; 338 } 339 340 344 public final void addPropertyChangeListener(PropertyChangeListener l) { 345 boolean noListener; 346 347 synchronized (getLock()) { 348 PropertyChangeSupport supp = (PropertyChangeSupport ) getProperty(PROP_SUPPORT); 350 351 if (supp == null) { 352 putProperty(PROP_SUPPORT, supp = new PropertyChangeSupport (this)); 354 } 355 356 noListener = !supp.hasListeners(null); 357 supp.addPropertyChangeListener(l); 358 } 359 360 if (noListener) { 361 addNotifySuper = false; 362 addNotify(); 363 364 if (!addNotifySuper) { 365 String msg = "You must call super.addNotify() from " + getClass().getName() + ".addNotify()"; err.warning(msg); 369 } 370 } 371 } 372 373 378 public final void removePropertyChangeListener(PropertyChangeListener l) { 379 boolean callRemoved; 380 381 synchronized (getLock()) { 382 PropertyChangeSupport supp = (PropertyChangeSupport ) getProperty(PROP_SUPPORT); 383 384 if (supp == null) { 385 return; 386 } 387 388 boolean hasListener = supp.hasListeners(null); 389 supp.removePropertyChangeListener(l); 390 callRemoved = hasListener && !supp.hasListeners(null); 391 } 392 393 if (callRemoved) { 394 putProperty(PROP_SUPPORT, null); removeNotifySuper = false; 396 removeNotify(); 397 398 if (!removeNotifySuper) { 399 String msg = "You must call super.removeNotify() from " + getClass().getName() + ".removeNotify()"; err.warning(msg); 401 } 402 } 403 } 404 405 409 protected void addNotify() { 410 addNotifySuper = true; 411 } 412 413 417 protected void removeNotify() { 418 removeNotifySuper = true; 419 } 420 421 426 427 protected void firePropertyChange(String name, Object oldValue, Object newValue) { 429 PropertyChangeSupport supp = (PropertyChangeSupport ) getProperty(PROP_SUPPORT); 430 431 if (supp != null) { 432 supp.firePropertyChange(name, oldValue, newValue); 433 } 434 } 435 436 439 public void writeExternal(ObjectOutput oo) throws IOException { 440 } 441 442 445 public void readExternal(ObjectInput oi) throws IOException , ClassNotFoundException { 446 } 447 448 458 protected Object writeReplace() { 459 return new WriteReplace(this); 460 } 461 462 466 public static <T extends SharedClassObject> T findObject(Class <T> clazz) { 467 return findObject(clazz, false); 468 } 469 470 479 public static <T extends SharedClassObject> T findObject(Class <T> clazz, boolean create) { 480 synchronized (clazz.getName().intern()) { 482 DataEntry de = values.get(clazz); 483 484 SharedClassObject obj = (de == null) ? null : de.get(); 486 boolean created = false; 487 488 if ((obj == null) && create) { 489 SetAccessibleAction action = new SetAccessibleAction(clazz); 491 492 try { 493 obj = AccessController.doPrivileged(action); 494 } catch (PrivilegedActionException e) { 495 Exception ex = e.getException(); 496 IllegalArgumentException newEx = new IllegalArgumentException (ex.toString()); 497 newEx.initCause(ex); 498 throw newEx; 499 } 500 501 created = true; 502 } 503 504 de = values.get(clazz); 505 506 if (de != null) { 507 SharedClassObject obj2 = de.get(); 508 509 if ((obj != null) && (obj != obj2)) { 510 if ((obj2 == null) && create) { 516 throw new IllegalStateException ("Inconsistent state: " + clazz); } 518 519 return clazz.cast(obj2); 520 } 521 } 522 523 if (created) { 524 if (obj.isSystemOption()) { 532 final Lookup.Result<T> r = Lookup.getDefault().lookup(new Lookup.Template<T>(clazz)); 534 535 if (r.allInstances().isEmpty()) { 536 obj.waitingOnSystemOption = true; 542 543 final SharedClassObject _obj = obj; 544 final IllegalStateException start = new IllegalStateException ( 545 "Making a SystemOption here that is not in lookup..." 546 ); 548 class SOLoader implements LookupListener { 549 public void resultChanged(LookupEvent ev) { 550 if (!r.allInstances().isEmpty()) { 551 r.removeLookupListener(SOLoader.this); 553 554 synchronized (_obj.getLock()) { 555 _obj.waitingOnSystemOption = false; 556 557 if (_obj.prematureSystemOptionMutation != null) { 558 warn(start); 559 warn(_obj.prematureSystemOptionMutation); 560 warn( 561 new IllegalStateException ( 562 "...and maybe getting clobbered here, see #17711." 563 ) 564 ); _obj.prematureSystemOptionMutation = null; 566 } 567 } 568 } 569 } 570 } 571 r.addLookupListener(new SOLoader()); 572 } 573 } 574 } 575 576 if ((obj == null) && create) { 577 throw new IllegalStateException ("Inconsistent state: " + clazz); } 579 580 return clazz.cast(obj); 581 } 582 } 583 584 586 private boolean isSystemOption() { 587 Class c = this.getClass(); 588 589 while (c != SharedClassObject.class) { 590 if ("org.openide.options.SystemOption".equals(c.getName())) { 591 return true; } 593 594 c = c.getSuperclass(); 595 } 596 597 return false; 598 } 599 600 private static void warn(Throwable t) { 602 err.log(Level.WARNING, null, t); 603 } 604 605 static SharedClassObject createInstancePrivileged(Class <? extends SharedClassObject> clazz) 606 throws Exception { 607 java.lang.reflect.Constructor <? extends SharedClassObject> c = clazz.getDeclaredConstructor(new Class [0]); 608 c.setAccessible(true); 609 610 String name = clazz.getName(); 611 assert instancesBeingCreated != null; 612 613 synchronized (instancesBeingCreated) { 614 Integer i = instancesBeingCreated.get(name); 615 instancesBeingCreated.put(name, (i == null) ? new Integer (1) : new Integer (i.intValue() + 1)); 616 } 617 618 try { 619 return c.newInstance(new Object [0]); 620 } finally { 621 synchronized (instancesBeingCreated) { 622 Integer i = instancesBeingCreated.get(name); 623 624 if (i.intValue() == 1) { 625 instancesBeingCreated.remove(name); 626 } else { 627 instancesBeingCreated.put(name, new Integer (i.intValue() - 1)); 628 } 629 } 630 631 c.setAccessible(false); 632 } 633 } 634 635 646 protected void reset() { 647 } 648 649 651 static final class WriteReplace extends Object implements Serializable { 652 653 static final long serialVersionUID = 1327893248974327640L; 654 655 656 private Class <? extends SharedClassObject> clazz; 657 658 659 private String name; 660 661 662 private transient SharedClassObject object; 663 664 667 public WriteReplace(SharedClassObject object) { 668 this.object = object; 669 this.clazz = object.getClass(); 670 this.name = clazz.getName(); 671 } 672 673 675 private void writeObject(ObjectOutputStream oos) 676 throws IOException { 677 oos.defaultWriteObject(); 678 679 object.writeExternal(oos); 680 } 681 682 684 private void readObject(ObjectInputStream ois) 685 throws IOException , ClassNotFoundException { 686 ois.defaultReadObject(); 687 688 if (clazz == null) { 689 if (name != null) { 692 throw new ClassNotFoundException (name); 693 } else { 694 throw new ClassNotFoundException (); 696 } 697 } 698 699 object = findObject(clazz, true); 700 object.inReadExternal = true; 701 702 try { 703 object.readExternal(ois); 704 } finally { 705 object.inReadExternal = false; 706 } 707 } 708 709 713 private Object readResolve() throws ObjectStreamException { 714 SharedClassObject resolved = object; 715 716 Method resolveMethod = findReadResolveMethod(object.getClass()); 717 718 if (resolveMethod != null) { 719 try { 721 resolveMethod.setAccessible(true); 723 724 return resolveMethod.invoke(object); 725 } catch (Exception ex) { 726 String banner = "Skipping " + object.getClass() + " resolution:"; err.log(Level.WARNING, banner, ex); 729 } finally { 730 resolveMethod.setAccessible(false); 731 } 732 } 733 734 return resolved; 735 } 736 737 740 private static Method findReadResolveMethod(Class clazz) { 741 Method result = null; 742 743 for (Class <?> i = clazz; i != null; i = i.getSuperclass()) { 745 try { 746 result = accept(i.getDeclaredMethod("readResolve")); 748 if (result != null) { 750 break; 751 } 752 } catch (NoSuchMethodException exc) { 753 } 755 } 756 757 return result; 758 } 759 760 764 private static Method accept(Method candidate) { 765 if (candidate != null) { 766 Class [] result = candidate.getExceptionTypes(); 768 769 if ((result.length == 1) && ObjectStreamException .class.equals(result[0])) { 770 if (Object .class.equals(candidate.getReturnType())) { 772 return candidate; 773 } 774 } 775 } 776 777 return null; 778 } 779 } 780 781 784 static final class DataEntry extends Object { 785 786 private HashMap <Object ,Object > map; 787 788 789 private int count = 0; 790 791 792 private WeakReference <SharedClassObject> ref = new WeakReference <SharedClassObject>(null); 793 794 795 private boolean initialized = false; 796 private boolean initializeInProgress = false; 797 798 799 private Throwable invalid = null; 800 801 public String toString() { 803 return "SCO.DataEntry[ref=" + ref.get() + ",count=" + count + ",initialized=" + initialized + ",invalid=" + 804 invalid + ",map=" + map + "]"; } 806 807 808 boolean isInInitialize() { 809 return initializeInProgress; 810 } 811 812 816 Map <Object ,Object > getMap(SharedClassObject obj) { 817 ensureValid(obj); 818 819 if (map == null) { 820 map = new HashMap <Object ,Object >(); 822 } 823 824 if (!initialized) { 825 initialized = true; 826 827 tryToInitialize(obj); 829 } 830 831 return map; 832 } 833 834 838 Object get(SharedClassObject obj, Object key) { 839 ensureValid(obj); 840 841 Object ret; 842 843 if (map == null) { 844 map = new HashMap <Object ,Object >(); 846 ret = null; 847 } else { 848 ret = map.get(key); 849 } 850 851 if ((ret == null) && !initialized) { 852 if (key == PROP_SUPPORT) { 853 return null; 854 } 855 856 initialized = true; 857 858 tryToInitialize(obj); 860 ret = map.get(key); 861 } 862 863 return ret; 864 } 865 866 869 Map getMap() { 870 ensureValid(get()); 871 872 if (map == null) { 873 map = new HashMap <Object ,Object >(); 875 } 876 877 return map; 878 } 879 880 private void ensureValid(SharedClassObject obj) 881 throws IllegalStateException { 882 if (invalid != null) { 883 String msg; 884 885 if (obj != null) { 886 msg = obj.toString(); 887 } else { 888 msg = "<unknown object>"; } 890 891 throw (IllegalStateException ) new IllegalStateException (msg).initCause(invalid); 892 } 893 } 895 896 private void tryToInitialize(SharedClassObject obj) 897 throws IllegalStateException { 898 initializeInProgress = true; 899 obj.initializeSuper = false; 900 901 try { 902 obj.initialize(); 903 } catch (Exception e) { 904 invalid = e; 905 throw (IllegalStateException ) new IllegalStateException (invalid.toString() + " from " + obj).initCause(invalid); } catch (LinkageError e) { 907 invalid = e; 908 throw (IllegalStateException ) new IllegalStateException (invalid.toString() + " from " + obj).initCause(invalid); } finally { 910 initializeInProgress = false; 911 } 912 913 if (!obj.initializeSuper) { 914 String msg = "You must call super.initialize() from " + obj.getClass().getName() + ".initialize()"; err.warning(msg); 916 } 917 } 918 919 922 int increase() { 923 return ++count; 924 } 925 926 929 int decrease() { 930 return --count; 931 } 932 933 937 SharedClassObject first(SharedClassObject obj) { 938 SharedClassObject s = ref.get(); 939 940 if (s == null) { 941 ref = new WeakReference <SharedClassObject>(obj); 942 943 return obj; 944 } else { 945 return s; 946 } 947 } 948 949 951 public SharedClassObject get() { 952 return ref.get(); 953 } 954 955 956 public void reset(SharedClassObject obj) { 957 SharedClassObject s = get(); 958 959 if ((s != null) && (s != obj)) { 960 return; 961 } 962 963 invalid = null; 964 getMap().clear(); 965 966 initialized = true; 967 tryToInitialize(obj); 968 } 969 } 970 971 static final class SetAccessibleAction implements PrivilegedExceptionAction <SharedClassObject> { 972 Class <? extends SharedClassObject> klass; 973 974 SetAccessibleAction(Class <? extends SharedClassObject> klass) { 975 this.klass = klass; 976 } 977 978 public SharedClassObject run() throws Exception { 979 return createInstancePrivileged(klass); 980 } 981 } 982 } 983 | Popular Tags |