KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > SharedClassObject


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.io.Externalizable JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectInput JavaDoc;
27 import java.io.ObjectInputStream JavaDoc;
28 import java.io.ObjectOutput JavaDoc;
29 import java.io.ObjectOutputStream JavaDoc;
30 import java.io.ObjectStreamException JavaDoc;
31 import java.io.Serializable JavaDoc;
32 import java.lang.ref.WeakReference JavaDoc;
33 import java.lang.reflect.Method JavaDoc;
34 import java.security.AccessController JavaDoc;
35 import java.security.PrivilegedActionException JavaDoc;
36 import java.security.PrivilegedExceptionAction JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.HashSet JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.Set JavaDoc;
41 import java.util.WeakHashMap JavaDoc;
42 import java.util.logging.Level JavaDoc;
43 import java.util.logging.Logger JavaDoc;
44
45 /** Shared object that allows different instances of the same class
46 * to share common data.
47 * <p>The data are shared only between instances of the same class (not subclasses).
48 * Thus, such "variables" have neither instance nor static behavior.
49 *
50 * @author Ian Formanek, Jaroslav Tulach
51 */

52 public abstract class SharedClassObject extends Object JavaDoc implements Externalizable JavaDoc {
53     /** serialVersionUID */
54     private static final long serialVersionUID = 4527891234589143259L;
55
56     /** property change support (PropertyChangeSupport) */
57     private static final Object JavaDoc PROP_SUPPORT = new Object JavaDoc();
58
59     /** Map (Class, DataEntry) that maps Classes to maps of any objects */
60     private static final Map JavaDoc<Class JavaDoc,DataEntry> values = new WeakHashMap JavaDoc<Class JavaDoc,DataEntry>(37);
61
62     /** A set of all classes for which we are currently inside createInstancePrivileged.
63      * If a SCO constructor is called when an instance of that class already exists, normally
64      * this will print a warning. However it is common to create an instance inside a static
65      * block; in this case the constructor is actually called twice. Only the first instance
66      * is ever returned, but this set ensures that no warning is printed during creation of the
67      * second instance (because it is nobody's fault and it will be handled OK).
68      * Map from class name to nesting count.
69      */

70     private static final Map JavaDoc<String JavaDoc,Integer JavaDoc> instancesBeingCreated = new HashMap JavaDoc<String JavaDoc,Integer JavaDoc>(7);
71
72     /** Set of classes to not warn about any more.
73      * Names only.
74      */

75     private static final Set JavaDoc<String JavaDoc> alreadyWarnedAboutDupes = new HashSet JavaDoc<String JavaDoc>();
76     private static final Logger JavaDoc err = Logger.getLogger("org.openide.util.SharedClassObject"); // NOI18N
77

78     /** data entry for this class */
79     private final DataEntry dataEntry;
80
81     /** Lock for the object */
82     private Object JavaDoc lock;
83
84     /** hard reference to primary instance of this class
85     * This is here not to allow the finalization till at least
86     * one object exists
87     */

88     private final SharedClassObject first;
89
90     /** Stack trace indicating where the first instance was created.
91      * This is only set on the first instance; and only with the error manager on.
92      */

93     private Throwable JavaDoc firstTrace = null;
94
95     /** Set by {@link SystemOption}s through the special property, see {@link #putProperty}.
96      * SystemOption needs special handling, e.g. it needs to be deserialized by the lookup
97      * after its first instance is created in {@link #findObject} method, only
98      * SystemOption can be reset.
99      */

100     private boolean systemOption = false;
101
102     /** If set, this means we have a system option waiting to be loaded from lookup.
103      * If anyone changes a property on it before this happens, the exception is filled in,
104      * so we know when it is loaded that something went wrong.
105      */

106     private boolean waitingOnSystemOption = false;
107     private IllegalStateException JavaDoc prematureSystemOptionMutation = null;
108     private boolean inReadExternal = false;
109
110     /** Check that addNotify, removeNotify, initialize call super sometime. */
111     private boolean addNotifySuper;
112
113     /** Check that addNotify, removeNotify, initialize call super sometime. */
114     private boolean removeNotifySuper;
115
116     /** Check that addNotify, removeNotify, initialize call super sometime. */
117     private boolean initializeSuper;
118
119     /** Create a shared object.
120     * Typically shared-class constructors should not take parameters, since there
121     * will conventionally be no instance variables.
122     * @see #initialize
123     */

124     protected SharedClassObject() {
125         synchronized (getLock()) {
126             DataEntry de = values.get(getClass());
127
128             //System.err.println("SCO create: " + this + " de=" + de);
129
if (de == null) {
130                 de = new DataEntry();
131                 values.put(getClass(), de);
132             }
133
134             dataEntry = de;
135             de.increase();
136
137             // finds reference for the first object of the class
138
first = de.first(this);
139         }
140
141         if (first != null) {
142             if (first == this) {
143                 // Could be a performance hit, so only do this when developing.
144
if (err.isLoggable(Level.FINE)) {
145                     Throwable JavaDoc t = new Throwable JavaDoc("First instance created here"); // NOI18N
146
t.fillInStackTrace();
147                     first.firstTrace = t;
148                 }
149             } else {
150                 String JavaDoc clazz = getClass().getName();
151                 boolean creating;
152
153                 synchronized (instancesBeingCreated) {
154                     creating = instancesBeingCreated.containsKey(clazz);
155                 }
156
157                 if (creating) {
158                     //System.err.println ("Nesting: " + getClass ().getName () + " " + instancesBeingCreated.get (clazz));
159
} else {
160                     if (!alreadyWarnedAboutDupes.contains(clazz)) {
161                         alreadyWarnedAboutDupes.add(clazz);
162
163                         Exception JavaDoc e = new IllegalStateException JavaDoc(
164                                 "Warning: multiple instances of shared class " + clazz + " created."
165                             ); // NOI18N
166

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.)"); // NOI18N
171
}
172
173                         err.log(Level.WARNING, null, e);
174                     }
175                 }
176             }
177         }
178     }
179
180     /* Calls a referenceLost to decrease the counter on the shared data.
181     * This method is final so no descendant can override it, but
182     * it calls the method unreferenced() that can be overriden to perform any
183     * additional tasks on finalizing.
184     */

185     protected final void finalize() throws Throwable JavaDoc {
186         referenceLost();
187     }
188
189     /** Indicate whether the shared data of the last existing instance of this class
190     * should be cleared when that instance is finalized.
191     *
192     * Subclasses may perform additional tasks
193     * on finalization if desired. This method should be overridden
194     * in lieu of {@link #finalize}.
195     * <p>The default implementation returns <code>true</code>.
196     * Classes which have precious shared data may want to return <code>false</code>, so that
197     * all instances may be finalized, after which new instances will pick up the same shared variables
198     * without requiring a recalculation.
199     *
200     * @return <code>true</code> if all shared data should be cleared,
201     * <code>false</code> if it should stay in memory
202     */

203     protected boolean clearSharedData() {
204         return true;
205     }
206
207     /** Test whether the classes of the compared objects are the same.
208     * @param obj the object to compare to
209     * @return <code>true</code> if the classes are equal
210     */

211     public final boolean equals(Object JavaDoc obj) {
212         return ((obj instanceof SharedClassObject) && (getClass().equals(obj.getClass())));
213     }
214
215     /** Get a hashcode of the shared class.
216     * @return the hash code
217     */

218     public final int hashCode() {
219         return getClass().hashCode();
220     }
221
222     /** Obtain lock for synchronization on manipulation with this
223     * class.
224     * Can be used by subclasses when performing nonatomic writes, e.g.
225     * @return an arbitrary synchronizable lock object
226     */

227     protected final Object JavaDoc getLock() {
228         if (lock == null) {
229             lock = getClass().getName().intern();
230         }
231
232         return lock;
233     }
234
235     /** Should be called from within a finalize method to manage references
236     * to the shared data (when the last reference is lost, the object is
237     * removed)
238     */

239     private void referenceLost() {
240         //System.err.println ("SharedClassObject.referenceLost:");
241
//System.err.println ("\tLock: " + getLock());
242
//System.err.println ("\tDataEntry: " + dataEntry);
243
//System.err.println ("\tValues: " + values.containsKey(getClass()));
244
synchronized (getLock()) {
245             if ((dataEntry == null) || (dataEntry.decrease() == 0)) {
246                 if (clearSharedData()) {
247                     // clears the data
248
values.remove(getClass());
249                 }
250             }
251         }
252
253         //System.err.println("\tValues after: " + values.containsKey(getClass()));
254
}
255
256     /** Set a shared variable.
257     * Automatically {@link #getLock locks}.
258     * @param key name of the property
259     * @param value value for that property (may be null)
260     * @return the previous value assigned to the property, or <code>null</code> if none
261     */

262     protected final Object JavaDoc putProperty(Object JavaDoc key, Object JavaDoc value) {
263         if (key == null) {
264             throw new NullPointerException JavaDoc("Tried to pass null key (value=" + value + ") to putProperty"); // NOI18N
265
}
266
267         synchronized (getLock()) {
268             if (
269                 waitingOnSystemOption && (key != PROP_SUPPORT) && (prematureSystemOptionMutation == null) &&
270                     !dataEntry.isInInitialize() && !inReadExternal
271             ) {
272                 // See below in findObject. Note that if we are still in initialize(),
273
// it is harmless to set default values of properties, and from readExternal()
274
// it is expected.
275
prematureSystemOptionMutation = new IllegalStateException JavaDoc("...setting property here..."); // NOI18N
276
}
277
278             return dataEntry.getMap(this).put(key, value);
279
280             //return dataEntry.getMap().put (key, value);
281
}
282     }
283
284     /** Set a shared variable available only for string names.
285     * Automatically {@link #getLock locks}.
286      * <p><strong>Important:</strong> remember that <code>SharedClassObject</code>s
287      * are like singleton beans; when you use <code>putProperty</code> with a value
288      * of <code>true</code>, or call {@link #firePropertyChange}, you must consider that
289      * the property name should match the JavaBeans name for a natural (introspected) property
290      * for the bean, if such a property uses this key. For example, if you have a method
291      * <code>getFoo</code> which uses {@link #getProperty} and a method <code>setFoo</code>
292      * which uses <code>putProperty(..., true)</code>, then the key used <em>must</em>
293      * be named <code>foo</code> (assuming you did not override this name in a BeanInfo).
294      * Otherwise various listeners may not be prepared for the property change and may just
295      * ignore it. For example, the property sheet for a <a HREF="@org-openide-nodes@/org/openide/nodes/BeanNode.html">BeanNode</a> based on a
296      * <code>SharedClassObject</code> which stores its properties using a misnamed key
297      * will probably not refresh correctly.
298     * @param key name of the property
299     * @param value value for that property (may be null)
300     * @param notify should all listeners be notified about property change?
301     * @return the previous value assigned to the property, or <code>null</code> if none
302     */

303     protected final Object JavaDoc putProperty(String JavaDoc key, Object JavaDoc value, boolean notify) {
304         Object JavaDoc previous = putProperty(key, value);
305
306         if (notify) {
307             firePropertyChange(key, previous, value);
308         }
309
310         return previous;
311     }
312
313     /** Get a shared variable.
314     * Automatically {@link #getLock locks}.
315     * @param key name of the property
316     * @return value of the property, or <code>null</code> if none
317     */

318     protected final Object JavaDoc getProperty(Object JavaDoc key) {
319         synchronized (getLock()) {
320             //System.err.println("SCO: " + this + " get: " + key + " de=" + dataEntry);
321
if ("org.openide.util.SharedClassObject.initialize".equals(key)) { // NOI18N
322

323                 return dataEntry.isInInitialize() ? Boolean.TRUE : null;
324             }
325
326             return dataEntry.get(this, key);
327         }
328     }
329
330     /** Initialize shared state.
331     * Should use {@link #putProperty} to set up variables.
332     * Subclasses should always call the super method.
333     * <p>This method need <em>not</em> be called explicitly; it will be called once
334     * the first time a given shared class is used (not for each instance!).
335     */

336     protected void initialize() {
337         initializeSuper = true;
338     }
339
340     /** Adds the specified property change listener to receive property
341      * change events from this object.
342      * @param l the property change listener
343      */

344     public final void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
345         boolean noListener;
346
347         synchronized (getLock()) {
348             // System.out.println ("added listener: " + l + " to: " + getClass ()); // NOI18N
349
PropertyChangeSupport JavaDoc supp = (PropertyChangeSupport JavaDoc) getProperty(PROP_SUPPORT);
350
351             if (supp == null) {
352                 // System.out.println ("Creating support"); // NOI18N
353
putProperty(PROP_SUPPORT, supp = new PropertyChangeSupport JavaDoc(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                 // [PENDING] theoretical race condition for this warning if listeners are added
366
// and removed very quickly from two threads, I guess, and addNotify() impl is slow
367
String JavaDoc msg = "You must call super.addNotify() from " + getClass().getName() + ".addNotify()"; // NOI18N
368
err.warning(msg);
369             }
370         }
371     }
372
373     /**
374      * Removes the specified property change listener so that it
375      * no longer receives property change events from this object.
376      * @param l the property change listener
377      */

378     public final void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
379         boolean callRemoved;
380
381         synchronized (getLock()) {
382             PropertyChangeSupport JavaDoc supp = (PropertyChangeSupport JavaDoc) 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); // clean the PCS, see #25417
395
removeNotifySuper = false;
396             removeNotify();
397
398             if (!removeNotifySuper) {
399                 String JavaDoc msg = "You must call super.removeNotify() from " + getClass().getName() + ".removeNotify()"; // NOI18N
400
err.warning(msg);
401             }
402         }
403     }
404
405     /** Notify subclasses that the first listener has been added to this object.
406     * Subclasses should always call the super method.
407     * The default implementation does nothing.
408     */

409     protected void addNotify() {
410         addNotifySuper = true;
411     }
412
413     /** Notify subclasses that the last listener has been removed from this object.
414     * Subclasses should always call the super method.
415     * The default implementation does nothing.
416     */

417     protected void removeNotify() {
418         removeNotifySuper = true;
419     }
420
421     /** Fire a property change event to all listeners.
422     * @param name the name of the property
423     * @param oldValue the old value
424     * @param newValue the new value
425     */

426
427     // not final - SystemOption overrides it, e.g.
428
protected void firePropertyChange(String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) {
429         PropertyChangeSupport JavaDoc supp = (PropertyChangeSupport JavaDoc) getProperty(PROP_SUPPORT);
430
431         if (supp != null) {
432             supp.firePropertyChange(name, oldValue, newValue);
433         }
434     }
435
436     /** Writes nothing to the stream.
437     * @param oo ignored
438     */

439     public void writeExternal(ObjectOutput JavaDoc oo) throws IOException JavaDoc {
440     }
441
442     /** Reads nothing from the stream.
443     * @param oi ignored
444     */

445     public void readExternal(ObjectInput JavaDoc oi) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
446     }
447
448     /** This method provides correct handling of serialization and deserialization.
449     * When serialized the method writeExternal is used to store the state.
450     * When deserialized first an instance is located by a call to findObject (clazz, true)
451     * and then a method readExternal is called to read its state from stream.
452     * <P>
453     * This allows to have only one instance of the class in the system and work
454     * only with it.
455     *
456     * @return write replace object that handles the described serialization/deserialization process
457     */

458     protected Object JavaDoc writeReplace() {
459         return new WriteReplace(this);
460     }
461
462     /** Obtain an instance of the desired class, if there is one.
463     * @param clazz the shared class to look for
464     * @return the instance, or <code>null</code> if such does not exists
465     */

466     public static <T extends SharedClassObject> T findObject(Class JavaDoc<T> clazz) {
467         return findObject(clazz, false);
468     }
469
470     /** Find an existing object, possibly creating a new one as needed.
471     * To create a new instance the class must be public and have a public
472     * default constructor.
473     *
474     * @param clazz the class of the object to find (must extend <code>SharedClassObject</code>)
475     * @param create <code>true</code> if the object should be created if it does not yet exist
476     * @return an instance, or <code>null</code> if there was none and <code>create</code> was <code>false</code>
477     * @exception IllegalArgumentException if a new instance could not be created for some reason
478     */

479     public static <T extends SharedClassObject> T findObject(Class JavaDoc<T> clazz, boolean create) {
480         // synchronizing on the same object as returned from getLock()
481
synchronized (clazz.getName().intern()) {
482             DataEntry de = values.get(clazz);
483
484             // either null or the object
485
SharedClassObject obj = (de == null) ? null : de.get();
486             boolean created = false;
487
488             if ((obj == null) && create) {
489                 // try to create new instance
490
SetAccessibleAction action = new SetAccessibleAction(clazz);
491
492                 try {
493                     obj = AccessController.doPrivileged(action);
494                 } catch (PrivilegedActionException JavaDoc e) {
495                     Exception JavaDoc ex = e.getException();
496                     IllegalArgumentException JavaDoc newEx = new IllegalArgumentException JavaDoc(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                     // Tricked! The static initializer for the class called findObject on itself.
511
// So we created two instances of it.
512
// Returning only the first (that created by the static initializer, rather
513
// than by us explicitly), to avoid duplication.
514
//System.err.println ("Nesting #2: " + clazz.getName ());
515
if ((obj2 == null) && create) {
516                         throw new IllegalStateException JavaDoc("Inconsistent state: " + clazz); // NOI18N
517
}
518
519                     return clazz.cast(obj2);
520                 }
521             }
522
523             if (created) {
524                 // This hack was created due to the remove of SystemOptions deserialization
525
// from project open operation, all SystemOptions are deserialized at this place
526
// the first time anybody asks for the option.
527
// It's crutial to do this just for SystemOptions and not for any other SharedClassObject,
528
// otherwise it can cause deadlocks.
529
// Lookup in the active session is used to find serialized state of the option,
530
// if such state exists it is deserialized before the object is returned from lookup.
531
if (obj.isSystemOption()) {
532                     // Lookup will find serialized version of searched object and deserialize it
533
final Lookup.Result<T> r = Lookup.getDefault().lookup(new Lookup.Template<T>(clazz));
534
535                     if (r.allInstances().isEmpty()) {
536                         // #17711: folder lookup not yet initialized. Try to load the option later.
537
// In the meantime the default state of the option will be available.
538
// If any attempt is made to change the option, _and_ it is later loaded,
539
// then we print a stack trace of the mutation for debugging (since the mutations
540
// would get clobbered by loading the settings from layer or whatever).
541
obj.waitingOnSystemOption = true;
542
543                         final SharedClassObject _obj = obj;
544                         final IllegalStateException JavaDoc start = new IllegalStateException JavaDoc(
545                                 "Making a SystemOption here that is not in lookup..."
546                             ); // NOI18N
547

548                         class SOLoader implements LookupListener {
549                             public void resultChanged(LookupEvent ev) {
550                                 if (!r.allInstances().isEmpty()) {
551                                     // Got it.
552
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 JavaDoc(
562                                                     "...and maybe getting clobbered here, see #17711."
563                                                 )
564                                             ); // NOI18N
565
_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 JavaDoc("Inconsistent state: " + clazz); // NOI18N
578
}
579
580             return clazz.cast(obj);
581         }
582     }
583
584     /** checks whether we are instance of system option.
585      */

586     private boolean isSystemOption() {
587         Class JavaDoc c = this.getClass();
588
589         while (c != SharedClassObject.class) {
590             if ("org.openide.options.SystemOption".equals(c.getName())) {
591                 return true; // NOI18N
592
}
593
594             c = c.getSuperclass();
595         }
596
597         return false;
598     }
599
600     // See above:
601
private static void warn(Throwable JavaDoc t) {
602         err.log(Level.WARNING, null, t);
603     }
604
605     static SharedClassObject createInstancePrivileged(Class JavaDoc<? extends SharedClassObject> clazz)
606     throws Exception JavaDoc {
607         java.lang.reflect.Constructor JavaDoc<? extends SharedClassObject> c = clazz.getDeclaredConstructor(new Class JavaDoc[0]);
608         c.setAccessible(true);
609
610         String JavaDoc name = clazz.getName();
611         assert instancesBeingCreated != null;
612
613         synchronized (instancesBeingCreated) {
614             Integer JavaDoc i = instancesBeingCreated.get(name);
615             instancesBeingCreated.put(name, (i == null) ? new Integer JavaDoc(1) : new Integer JavaDoc(i.intValue() + 1));
616         }
617
618         try {
619             return c.newInstance(new Object JavaDoc[0]);
620         } finally {
621             synchronized (instancesBeingCreated) {
622                 Integer JavaDoc i = instancesBeingCreated.get(name);
623
624                 if (i.intValue() == 1) {
625                     instancesBeingCreated.remove(name);
626                 } else {
627                     instancesBeingCreated.put(name, new Integer JavaDoc(i.intValue() - 1));
628                 }
629             }
630
631             c.setAccessible(false);
632         }
633     }
634
635     /** Is called by the infrastructure in cases when a clean instance is requested.
636      * As instances of <code>SharedClassObject</code> are singletons, there is
637      * no way how to create new instance that would not contain the same data
638      * as previously existing one. This method allows all subclasses that care
639      * about the ability to refresh the settings (like <code>SystemOption</code>s)
640      * to be notified about the cleaning request and clean their settings themselves.
641      * <p>
642      * Default implementation does nothing.
643      *
644      * @since made protected in version 4.46
645      */

646     protected void reset() {
647     }
648
649     /** Class that is used as default write replace.
650     */

651     static final class WriteReplace extends Object JavaDoc implements Serializable JavaDoc {
652         /** serialVersionUID */
653         static final long serialVersionUID = 1327893248974327640L;
654
655         /** the class */
656         private Class JavaDoc<? extends SharedClassObject> clazz;
657
658         /** class name, in case clazz could not be reloaded */
659         private String JavaDoc name;
660
661         /** shared instance */
662         private transient SharedClassObject object;
663
664         /** Constructor.
665         * @param the instance
666         */

667         public WriteReplace(SharedClassObject object) {
668             this.object = object;
669             this.clazz = object.getClass();
670             this.name = clazz.getName();
671         }
672
673         /** Write object.
674         */

675         private void writeObject(ObjectOutputStream JavaDoc oos)
676         throws IOException JavaDoc {
677             oos.defaultWriteObject();
678
679             object.writeExternal(oos);
680         }
681
682         /** Read object.
683         */

684         private void readObject(ObjectInputStream JavaDoc ois)
685         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
686             ois.defaultReadObject();
687
688             if (clazz == null) {
689                 // Means that the class is no longer available in the restoring classloader.
690
// Normal enough if the module has been uninstalled etc. #15654
691
if (name != null) {
692                     throw new ClassNotFoundException JavaDoc(name);
693                 } else {
694                     // Compatibility with older WR's.
695
throw new ClassNotFoundException JavaDoc();
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         /** Read resolve to the read object.
710          * We give chance to actual instance to do its own resolution as well. It
711          * is necessary for achieving back compatability of certain types of settings etc.
712          */

713         private Object JavaDoc readResolve() throws ObjectStreamException JavaDoc {
714             SharedClassObject resolved = object;
715
716             Method JavaDoc resolveMethod = findReadResolveMethod(object.getClass());
717
718             if (resolveMethod != null) {
719                 // invoke resolve method and accept its result
720
try {
721                     // make readResolve accessible (it can have any access modifier)
722
resolveMethod.setAccessible(true);
723
724                     return resolveMethod.invoke(object);
725                 } catch (Exception JavaDoc ex) {
726                     // checked or runtime does not matter - we must survive
727
String JavaDoc banner = "Skipping " + object.getClass() + " resolution:"; //NOI18N
728
err.log(Level.WARNING, banner, ex);
729                 } finally {
730                     resolveMethod.setAccessible(false);
731                 }
732             }
733
734             return resolved;
735         }
736
737         /** Tries to find readResolve method in given class. Finds
738         * both public and non-public occurences of the method and
739         * searches also in superclasses */

740         private static Method JavaDoc findReadResolveMethod(Class JavaDoc clazz) {
741             Method JavaDoc result = null;
742
743             // try ANY-MODIFIER occurences; search also in superclasses
744
for (Class JavaDoc<?> i = clazz; i != null; i = i.getSuperclass()) {
745                 try {
746                     result = accept(i.getDeclaredMethod("readResolve")); // NOI18N
747

748                     // get out of cycle if method found
749
if (result != null) {
750                         break;
751                     }
752                 } catch (NoSuchMethodException JavaDoc exc) {
753                     // readResolve does not exist in current class
754
}
755             }
756
757             return result;
758         }
759
760         /*
761          * @return passed method if method matches exactly readResolve declaration as defined in
762          * Serializetion specification otherwise null
763          */

764         private static Method JavaDoc accept(Method JavaDoc candidate) {
765             if (candidate != null) {
766                 // check exceptions clause
767
Class JavaDoc[] result = candidate.getExceptionTypes();
768
769                 if ((result.length == 1) && ObjectStreamException JavaDoc.class.equals(result[0])) {
770                     // returned value type
771
if (Object JavaDoc.class.equals(candidate.getReturnType())) {
772                         return candidate;
773                     }
774                 }
775             }
776
777             return null;
778         }
779     }
780
781     /** The inner class that encapsulates the shared data together with
782     * a reference counter
783     */

784     static final class DataEntry extends Object JavaDoc {
785         /** The data */
786         private HashMap JavaDoc<Object JavaDoc,Object JavaDoc> map;
787
788         /** The reference counter */
789         private int count = 0;
790
791         /** weak reference to an object of this class */
792         private WeakReference JavaDoc<SharedClassObject> ref = new WeakReference JavaDoc<SharedClassObject>(null);
793
794         /** inited? */
795         private boolean initialized = false;
796         private boolean initializeInProgress = false;
797
798         /** #7479: if initialize() threw unchecked exception, keep it here */
799         private Throwable JavaDoc invalid = null;
800
801         public String JavaDoc toString() { // for debugging
802

803             return "SCO.DataEntry[ref=" + ref.get() + ",count=" + count + ",initialized=" + initialized + ",invalid=" +
804             invalid + ",map=" + map + "]"; // NOI18N
805
}
806
807         /** initialize() is in progress? */
808         boolean isInInitialize() {
809             return initializeInProgress;
810         }
811
812         /** Returns the data
813         * @param obj the requestor object
814         * @return the data
815         */

816         Map JavaDoc<Object JavaDoc,Object JavaDoc> getMap(SharedClassObject obj) {
817             ensureValid(obj);
818
819             if (map == null) {
820                 // to signal invalid state
821
map = new HashMap JavaDoc<Object JavaDoc,Object JavaDoc>();
822             }
823
824             if (!initialized) {
825                 initialized = true;
826
827                 // no data for this class yet
828
tryToInitialize(obj);
829             }
830
831             return map;
832         }
833
834         /** Returns a value for given key
835         * @param obj the requestor object
836         * @return the data
837         */

838         Object JavaDoc get(SharedClassObject obj, Object JavaDoc key) {
839             ensureValid(obj);
840
841             Object JavaDoc ret;
842
843             if (map == null) {
844                 // to signal invalid state
845
map = new HashMap JavaDoc<Object JavaDoc,Object JavaDoc>();
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                 // no data for this class yet
859
tryToInitialize(obj);
860                 ret = map.get(key);
861             }
862
863             return ret;
864         }
865
866         /** Returns the data
867         * @return the data
868         */

869         Map JavaDoc getMap() {
870             ensureValid(get());
871
872             if (map == null) {
873                 // to signal invalid state
874
map = new HashMap JavaDoc<Object JavaDoc,Object JavaDoc>();
875             }
876
877             return map;
878         }
879
880         private void ensureValid(SharedClassObject obj)
881         throws IllegalStateException JavaDoc {
882             if (invalid != null) {
883                 String JavaDoc msg;
884
885                 if (obj != null) {
886                     msg = obj.toString();
887                 } else {
888                     msg = "<unknown object>"; // NOI18N
889
}
890
891                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc(msg).initCause(invalid);
892             }
893              // else fine
894
}
895
896         private void tryToInitialize(SharedClassObject obj)
897         throws IllegalStateException JavaDoc {
898             initializeInProgress = true;
899             obj.initializeSuper = false;
900
901             try {
902                 obj.initialize();
903             } catch (Exception JavaDoc e) {
904                 invalid = e;
905                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc(invalid.toString() + " from " + obj).initCause(invalid); // NOI18N
906
} catch (LinkageError JavaDoc e) {
907                 invalid = e;
908                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc(invalid.toString() + " from " + obj).initCause(invalid); // NOI18N
909
} finally {
910                 initializeInProgress = false;
911             }
912
913             if (!obj.initializeSuper) {
914                 String JavaDoc msg = "You must call super.initialize() from " + obj.getClass().getName() + ".initialize()"; // NOI18N
915
err.warning(msg);
916             }
917         }
918
919         /** Increases the counter (thread safe)
920         * @return new counter value
921         */

922         int increase() {
923             return ++count;
924         }
925
926         /** Dereases the counter (thread safe)
927         * @return new counter value
928         */

929         int decrease() {
930             return --count;
931         }
932
933         /** Request for first object. If there is none, use the requestor
934         * @param obj requestor
935         * @return the an object of this type
936         */

937         SharedClassObject first(SharedClassObject obj) {
938             SharedClassObject s = ref.get();
939
940             if (s == null) {
941                 ref = new WeakReference JavaDoc<SharedClassObject>(obj);
942
943                 return obj;
944             } else {
945                 return s;
946             }
947         }
948
949         /** @return shared object or null
950         */

951         public SharedClassObject get() {
952             return ref.get();
953         }
954
955         /** Reset map of values. */
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 JavaDoc<SharedClassObject> {
972         Class JavaDoc<? extends SharedClassObject> klass;
973
974         SetAccessibleAction(Class JavaDoc<? extends SharedClassObject> klass) {
975             this.klass = klass;
976         }
977
978         public SharedClassObject run() throws Exception JavaDoc {
979             return createInstancePrivileged(klass);
980         }
981     }
982 }
983
Popular Tags