/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.beans.*;
import java.io.*;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.*;
import org.openide.ErrorManager;
/** Shared object that allows different instances of the same class
* to share common data.
* <p>The data are shared only between instances of the same class (not subclasses).
* Thus, such "variables" have neither instance nor static behavior.
*
* @author Ian Formanek, Jaroslav Tulach
*/
public abstract class SharedClassObject extends Object
implements Externalizable {
/** serialVersionUID */
private static final long serialVersionUID = 4527891234589143259L;
/** Name of the method used to determine whether an option is global or not. */
static final String GLOBAL_METHOD_NAME = "isGlobal"; // NOI18N
private byte [] defaultInstance = null;
/** property change support (PropertyChangeSupport) */
private static final Object PROP_SUPPORT = new Object ();
/** Map (Class, DataEntry) that maps Classes to maps of any objects */
private static final Map values = new WeakHashMap (4);
/** data entry for this class */
private final DataEntry dataEntry;
/** Lock for the object */
private Object lock;
/** hard reference to primary instance of this class
* This is here not to allow the finalization till at least
* one object exists
*/
private final SharedClassObject first;
private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.util.SharedClassObject"); // NOI18N
/** Stack trace indicating where the first instance was created.
* This is only set on the first instance; and only with the error manager on.
*/
private Throwable firstTrace = null;
/** A set of all classes for which we are currently inside createInstancePrivileged.
* If a SCO constructor is called when an instance of that class already exists, normally
* this will print a warning. However it is common to create an instance inside a static
* block; in this case the constructor is actually called twice. Only the first instance
* is ever returned, but this set ensures that no warning is printed during creation of the
* second instance (because it is nobody's fault and it will be handled OK).
* Map from class name to nesting count.
*/
private static final Map instancesBeingCreated = new HashMap (3); // Map<String,int>
/** Set of classes to not warn about any more.
* Names only.
*/
private static final Set alreadyWarnedAboutDupes = new HashSet (); // Set<String>
/** Set by {@link SystemOption}s through the special property, see {@link #putProperty}.
* SystemOption needs special handling, e.g. it needs to be deserialized by the lookup
* after its first instance is created in {@link #findObject} method, only
* SystemOption can be reset.
*/
private boolean systemOption = false;
/** If set, this means we have a system option waiting to be loaded from lookup.
* If anyone changes a property on it before this happens, the exception is filled in,
* so we know when it is loaded that something went wrong.
*/
private boolean waitingOnSystemOption = false;
private IllegalStateException prematureSystemOptionMutation = null;
private boolean inReadExternal = false;
/** Check that addNotify, removeNotify, initialize call super sometime. */
private boolean addNotifySuper, removeNotifySuper, initializeSuper;
/* Calls a referenceLost to decrease the counter on the shared data.
* This method is final so no descendant can override it, but
* it calls the method unreferenced() that can be overriden to perform any
* additional tasks on finalizing.
*/
protected final void finalize() throws Throwable {
referenceLost ();
}
/** Indicate whether the shared data of the last existing instance of this class
* should be cleared when that instance is finalized.
*
* Subclasses may perform additional tasks
* on finalization if desired. This method should be overridden
* in lieu of {@link #finalize}.
* <p>The default implementation returns <code>true</code>.
* Classes which have precious shared data may want to return <code>false</code>, so that
* all instances may be finalized, after which new instances will pick up the same shared variables
* without requiring a recalculation.
*
* @return <code>true</code> if all shared data should be cleared,
* <code>false</code> if it should stay in memory
*/
protected boolean clearSharedData () {
return true;
}
/** Test whether the classes of the compared objects are the same.
* @param obj the object to compare to
* @return <code>true</code> if the classes are equal
*/
public final boolean equals (Object obj) {
return ((obj instanceof SharedClassObject) && (getClass().equals(obj.getClass())));
}
/** Get a hashcode of the shared class.
* @return the hash code
*/
public final int hashCode () {
return getClass().hashCode();
}
/** Obtain lock for synchronization on manipulation with this
* class.
* Can be used by subclasses when performing nonatomic writes, e.g.
* @return an arbitrary synchronizable lock object
*/
protected final Object getLock () {
if (lock == null) {
lock = getClass ().getName ().intern ();
}
return lock;
}
/** Create a shared object.
* Typically shared-class constructors should not take parameters, since there
* will conventionally be no instance variables.
* @see #initialize
*/
protected SharedClassObject () {
synchronized (getLock ()) {
DataEntry de = (DataEntry)values.get (getClass ());
//System.err.println("SCO create: " + this + " de=" + de);
if (de == null) {
de = new DataEntry ();
values.put (getClass (), de);
}
dataEntry = de;
de.increase();
// finds reference for the first object of the class
first = de.first (this);
}
if (first != null) {
if (first == this) {
// Could be a performance hit, so only do this when developing.
if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
Throwable t = new Throwable ("First instance created here"); // NOI18N
t.fillInStackTrace ();
first.firstTrace = t;
}
} else {
String clazz = getClass ().getName ();
boolean creating;
synchronized (instancesBeingCreated) {
creating = instancesBeingCreated.containsKey (clazz);
}
if (creating) {
//System.err.println ("Nesting: " + getClass ().getName () + " " + instancesBeingCreated.get (clazz));
} else {
if (! alreadyWarnedAboutDupes.contains (clazz)) {
alreadyWarnedAboutDupes.add (clazz);
Exception e = new IllegalStateException
("Warning: multiple instances of shared class " + clazz + " created."); // NOI18N
if (first.firstTrace != null) {
err.annotate (e, first.firstTrace);
} else {
err.annotate (e, "(Run with -J-Dorg.openide.util.SharedClassObject=0 for more details.)"); // NOI18N
}
err.notify (ErrorManager.INFORMATIONAL, e);
}
}
}
}
}
/** Should be called from within a finalize method to manage references
* to the shared data (when the last reference is lost, the object is
* removed)
*/
private void referenceLost() {
//System.err.println ("SharedClassObject.referenceLost:");
//System.err.println ("\tLock: " + getLock());
//System.err.println ("\tDataEntry: " + dataEntry);
//System.err.println ("\tValues: " + values.containsKey(getClass()));
synchronized (getLock ()) {
if (dataEntry == null || dataEntry.decrease() == 0) {
if (clearSharedData ()) {
// clears the data
values.remove (getClass());
}
}
}
//System.err.println("\tValues after: " + values.containsKey(getClass()));
}
/** Set a shared variable.
* Automatically {@link #getLock locks}.
* @param key name of the property
* @param value value for that property (may be null)
* @return the previous value assigned to the property, or <code>null</code> if none
*/
protected final Object putProperty (Object key, Object value) {
synchronized (getLock ()) {
if (key.equals ("netbeans.systemoption.hack")) { // NOI18N
systemOption = true;
return null;
}
if (waitingOnSystemOption && key != PROP_SUPPORT &&
prematureSystemOptionMutation == null && !dataEntry.isInInitialize() && !inReadExternal) {
// See below in findObject. Note that if we are still in initialize(),
// it is harmless to set default values of properties, and from readExternal()
// it is expected.
prematureSystemOptionMutation = new IllegalStateException("...setting property here..."); // NOI18N
}
return dataEntry.getMap (this).put (key, value);
//return dataEntry.getMap().put (key, value);
}
}
/** Set a shared variable available only for string names.
* Automatically {@link #getLock locks}.
* <p><strong>Important:</strong> remember that <code>SharedClassObject</code>s
* are like singleton beans; when you use <code>putProperty</code> with a value
* of <code>true</code>, or call {@link #firePropertyChange}, you must consider that
* the property name should match the JavaBeans name for a natural (introspected) property
* for the bean, if such a property uses this key. For example, if you have a method
* <code>getFoo</code> which uses {@link #getProperty} and a method <code>setFoo</code>
* which uses <code>putProperty(..., true)</code>, then the key used <em>must</em>
* be named <code>foo</code> (assuming you did not override this name in a BeanInfo).
* Otherwise various listeners may not be prepared for the property change and may just
* ignore it. For example, the property sheet for a {@link BeanNode} based on a
* <code>SharedClassObject</code> which stores its properties using a misnamed key
* will probably not refresh correctly.
* @param key name of the property
* @param value value for that property (may be null)
* @param notify should all listeners be notified about property change?
* @return the previous value assigned to the property, or <code>null</code> if none
*/
protected final Object putProperty (String key, Object value, boolean notify) {
Object previous = putProperty (key, value);
if (notify) {
firePropertyChange (key, previous, value);
}
return previous;
}
/** Get a shared variable.
* Automatically {@link #getLock locks}.
* @param key name of the property
* @return value of the property, or <code>null</code> if none
*/
protected final Object getProperty (Object key) {
synchronized (getLock ()) {
//System.err.println("SCO: " + this + " get: " + key + " de=" + dataEntry);
return dataEntry.get(this, key);
}
}
/** Initialize shared state.
* Should use {@link #putProperty} to set up variables.
* Subclasses should always call the super method.
* <p>This method need <em>not</em> be called explicitly; it will be called once
* the first time a given shared class is used (not for each instance!).
*/
protected void initialize () {
initializeSuper = true;
}
/** Adds the specified property change listener to receive property
* change events from this object.
* @param l the property change listener
*/
public final void addPropertyChangeListener(PropertyChangeListener l) {
boolean noListener;
synchronized (getLock ()) {
// System.out.println ("added listener: " + l + " to: " + getClass ()); // NOI18N
PropertyChangeSupport supp = (PropertyChangeSupport)getProperty (PROP_SUPPORT);
if (supp == null) {
// System.out.println ("Creating support"); // NOI18N
putProperty (PROP_SUPPORT, supp = new PropertyChangeSupport (this));
}
noListener = !supp.hasListeners (null);
supp.addPropertyChangeListener(l);
}
if (noListener) {
addNotifySuper = false;
addNotify ();
if (!addNotifySuper) {
// [PENDING] theoretical race condition for this warning if listeners are added
// and removed very quickly from two threads, I guess, and addNotify() impl is slow
String msg = "You must call super.addNotify() from " + getClass().getName() + ".addNotify()"; // NOI18N
err.log(ErrorManager.WARNING, msg);
}
}
}
/**
* Removes the specified property change listener so that it
* no longer receives property change events from this object.
* @param l the property change listener
*/
public final void removePropertyChangeListener(PropertyChangeListener l) {
boolean callRemoved;
synchronized (getLock ()) {
PropertyChangeSupport supp = (PropertyChangeSupport)getProperty (PROP_SUPPORT);
if (supp == null) return;
boolean hasListener = supp.hasListeners (null);
supp.removePropertyChangeListener(l);
callRemoved = hasListener && !supp.hasListeners (null);
}
if (callRemoved) {
putProperty (PROP_SUPPORT, null); // clean the PCS, see #25417
removeNotifySuper = false;
removeNotify ();
if (!removeNotifySuper) {
String msg = "You must call super.removeNotify() from " + getClass().getName() + ".removeNotify()"; // NOI18N
err.log(ErrorManager.WARNING, msg);
}
}
}
/** Notify subclasses that the first listener has been added to this object.
* Subclasses should always call the super method.
* The default implementation does nothing.
*/
protected void addNotify () {
addNotifySuper = true;
}
/** Notify subclasses that the last listener has been removed from this object.
* Subclasses should always call the super method.
* The default implementation does nothing.
*/
protected void removeNotify () {
removeNotifySuper = true;
}
/** Fire a property change event to all listeners.
* @param name the name of the property
* @param oldValue the old value
* @param newValue the new value
*/
// not final - SystemOption overrides it, e.g.
protected void firePropertyChange (String name, Object oldValue, Object newValue) {
PropertyChangeSupport supp = (PropertyChangeSupport)getProperty (PROP_SUPPORT);
if (supp != null)
supp.firePropertyChange (name, oldValue, newValue);
}
/** Writes nothing to the stream.
* @param oo ignored
*/
public void writeExternal (ObjectOutput oo) throws IOException {
}
/** Reads nothing from the stream.
* @param oi ignored
*/
public void readExternal (ObjectInput oi)
throws IOException, ClassNotFoundException {
}
/** This method provides correct handling of serialization and deserialization.
* When serialized the method writeExternal is used to store the state.
* When deserialized first an instance is located by a call to findObject (clazz, true)
* and then a method readExternal is called to read its state from stream.
* <P>
* This allows to have only one instance of the class in the system and work
* only with it.
*
* @return write replace object that handles the described serialization/deserialization process
*/
protected Object writeReplace () {
return new WriteReplace (this);
}
/** Obtain an instance of the desired class, if there is one.
* @param clazz the shared class to look for
* @return the instance, or <code>null</code> if such does not exists
*/
public static SharedClassObject findObject (Class clazz) {
return findObject (clazz, false);
}
/** Find an existing object, possibly creating a new one as needed.
* To create a new instance the class must be public and have a public
* default constructor.
*
* @param clazz the class of the object to find (must extend <code>SharedClassObject</code>)
* @param create <code>true</code> if the object should be created if it does not yet exist
* @return an instance, or <code>null</code> if there was none and <code>create</code> was <code>false</code>
* @exception IllegalArgumentException if a new instance could not be created for some reason
*/
public static SharedClassObject findObject (Class clazz, boolean create) {
// synchronizing on the same object as returned from getLock()
synchronized (clazz.getName().intern()) {
DataEntry de = (DataEntry)values.get (clazz);
// either null or the object
SharedClassObject obj = de == null ? null : de.get ();
boolean created = false;
if (obj == null && create) {
// try to create new instance
PrivilegedExceptionAction action = new SetAccessibleAction(clazz);
try {
obj = (SharedClassObject) AccessController.doPrivileged(action);
} catch (PrivilegedActionException e) {
Exception ex = e.getException();
IllegalArgumentException newEx = new IllegalArgumentException (ex.toString());
err.annotate(newEx, ex);
throw newEx;
}
created = true;
}
de = (DataEntry) values.get (clazz);
if (de != null) {
SharedClassObject obj2 = de.get ();
if (obj != null && obj != obj2) {
// Tricked! The static initializer for the class called findObject on itself.
// So we created two instances of it.
// Returning only the first (that created by the static initializer, rather
// than by us explicitly), to avoid duplication.
//System.err.println ("Nesting #2: " + clazz.getName ());
if (obj2 == null && create) throw new IllegalStateException("Inconsistent state: " + clazz); // NOI18N
return obj2;
}
}
if (created) {
obj.reset ();
// This hack was created due to the remove of SystemOptions deserialization
// from project open operation, all SystemOptions are deserialized at this place
// the first time anybody asks for the option.
// It's crutial to do this just for SystemOptions and not for any other SharedClassObject,
// otherwise it can cause deadlocks.
// Lookup in the active session is used to find serialized state of the option,
// if such state exists it is deserialized before the object is returned from lookup.
if (obj != null && obj.systemOption) {
// Lookup will find serialized version of searched object and deserialize it
final Lookup.Result r = Lookup.getDefault().lookup(new Lookup.Template(clazz));
if (r.allInstances().isEmpty()) {
// #17711: folder lookup not yet initialized. Try to load the option later.
// In the meantime the default state of the option will be available.
// If any attempt is made to change the option, _and_ it is later loaded,
// then we print a stack trace of the mutation for debugging (since the mutations
// would get clobbered by loading the settings from layer or whatever).
obj.waitingOnSystemOption = true;
final SharedClassObject _obj = obj;
final IllegalStateException start = new IllegalStateException("Making a SystemOption here that is not in lookup..."); // NOI18N
class SOLoader implements LookupListener {
public void resultChanged(LookupEvent ev) {
if (!r.allInstances().isEmpty()) {
// Got it.
r.removeLookupListener(SOLoader.this);
synchronized (_obj.getLock()) {
_obj.waitingOnSystemOption = false;
if (_obj.prematureSystemOptionMutation != null) {
warn(start);
warn(_obj.prematureSystemOptionMutation);
warn(new IllegalStateException("...and maybe getting clobbered here, see #17711.")); // NOI18N
_obj.prematureSystemOptionMutation = null;
}
}
}
}
}
r.addLookupListener(new SOLoader());
}
}
}
if (obj == null && create) throw new IllegalStateException("Inconsistent state: " + clazz); // NOI18N
return obj;
}
}
// See above:
private static void warn(Throwable t) {
err.notify(ErrorManager.INFORMATIONAL, t);
}
static Object createInstancePrivileged(Class clazz) throws Exception {
java.lang.reflect.Constructor c = clazz.getDeclaredConstructor(new Class[0]);
c.setAccessible(true);
String name = clazz.getName ();
synchronized (instancesBeingCreated) {
Integer i = (Integer) instancesBeingCreated.get (name);
instancesBeingCreated.put (name, i == null ? new Integer (1) : new Integer (i.intValue () + 1));
}
try {
return c.newInstance (new Object[0]);
} finally {
synchronized (instancesBeingCreated) {
Integer i = (Integer) instancesBeingCreated.get (name);
if (i.intValue () == 1) {
instancesBeingCreated.remove (name);
} else {
instancesBeingCreated.put (name, new Integer (i.intValue () - 1));
}
}
c.setAccessible(false);
}
}
/** Resets shared data to it default value. */
private void reset () {
if (!systemOption || !isProjectOption ()) {
return;
}
synchronized (getLock ()) {
// [PENDING] should be changed to next line after all options in layers will
// use put{get}Property and initilaize properly
// dataEntry.reset (this);
if (defaultInstance == null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream (1024);
ObjectOutput oo = new org.openide.util.io.NbObjectOutputStream (baos);
oo.writeObject (this);
defaultInstance = baos.toByteArray ();
} catch (IOException e) {
defaultInstance = null;
}
return;
}
try {
ByteArrayInputStream bais = new ByteArrayInputStream (defaultInstance);
ObjectInputStream oi = new org.openide.util.io.NbObjectInputStream (bais);
oi.readObject ();
} catch (Exception e) {
// ignore and leave it as it is
}
}
}
/**
* Test if the object is Project specific.
* @return true if the object is Project specific
*/
private boolean isProjectOption () {
try {
Class clazz = getClass ();
// the old hack with undocumented method isGlobal
Method m = clazz.getMethod(GLOBAL_METHOD_NAME, new Class[] {});
m.setAccessible(true);
Boolean b = (Boolean) m.invoke(this, new Object[] {});
return !b.booleanValue();
} catch (Exception ex) {
// ignore and return default
}
return false;
}
/** Class that is used as default write replace.
*/
static final class WriteReplace extends Object implements Serializable {
/** serialVersionUID */
static final long serialVersionUID = 1327893248974327640L;
/** the class */
private Class clazz;
/** class name, in case clazz could not be reloaded */
private String name;
/** shared instance */
private transient SharedClassObject object;
/** Constructor.
* @param the instance
*/
public WriteReplace (SharedClassObject object) {
this.object = object;
this.clazz = object.getClass ();
this.name = clazz.getName();
}
/** Write object.
*/
private void writeObject (ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject ();
object.writeExternal (oos);
}
/** Read object.
*/
private void readObject (ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject ();
if (clazz == null) {
// Means that the class is no longer available in the restoring classloader.
// Normal enough if the module has been uninstalled etc. #15654
if (name != null) {
throw new ClassNotFoundException(name);
} else {
// Compatibility with older WR's.
throw new ClassNotFoundException();
}
}
object = findObject (clazz, true);
object.inReadExternal = true;
try {
object.readExternal (ois);
} finally {
object.inReadExternal = false;
}
}
/** Read resolve to the read object.
* We give chance to actual instance to do its own resolution as well. It
* is necessary for achieving back compatability of certain types of settings etc.
*/
private Object readResolve () throws ObjectStreamException {
SharedClassObject resolved = object;
Method resolveMethod = findReadResolveMethod(object.getClass());
if (resolveMethod != null) {
// invoke resolve method and accept its result
try {
// make readResolve accessible (it can have any access modifier)
resolveMethod.setAccessible(true);
return resolveMethod.invoke(object, null);
} catch (Exception ex) {
// checked or runtime does not matter - we must survive
String banner = "Skipping " + object.getClass() + " resolution:"; //NOI18N
err.annotate(ex, ErrorManager.UNKNOWN, banner, null, null, null);
err.notify (ErrorManager.INFORMATIONAL, ex);
} finally {
resolveMethod.setAccessible(false);
}
}
return resolved;
}
/** Tries to find readResolve method in given class. Finds
* both public and non-public occurences of the method and
* searches also in superclasses */
private static Method findReadResolveMethod (Class clazz) {
Method result = null;
// try ANY-MODIFIER occurences; search also in superclasses
for (Class i = clazz; i != null; i = i.getSuperclass()) {
try {
result = accept(i.getDeclaredMethod("readResolve", new Class[0])); // NOI18N
// get out of cycle if method found
if (result != null) break;
} catch (NoSuchMethodException exc) {
// readResolve does not exist in current class
}
}
return result;
}
/*
* @return passed method if method matches exactly readResolve declaration as defined in
* Serializetion specification otherwise null
*/
private static Method accept(Method candidate) {
if (candidate != null) {
// check exceptions clause
Class[] result = candidate.getExceptionTypes();
if ((result.length == 1) &&
ObjectStreamException.class.equals(result[0])) {
// returned value type
if (Object.class.equals(candidate.getReturnType())) {
return candidate;
}
}
}
return null;
}
}
/** The inner class that encapsulates the shared data together with
* a reference counter
*/
static final class DataEntry extends Object {
/** The data */
private HashMap map;
/** The reference counter */
private int count = 0;
/** weak reference to an object of this class */
private WeakReference ref = new WeakReference (null);
/** inited? */
private boolean initialized = false;
private boolean initializeInProgress = false;
/** #7479: if initialize() threw unchecked exception, keep it here */
private Throwable invalid = null;
public String toString() { // for debugging
return "SCO.DataEntry[ref=" + ref.get() + ",count=" + count + ",initialized=" + initialized + ",invalid=" + invalid + ",map=" + map + "]"; // NOI18N
}
/** initialize() is in progress? */
boolean isInInitialize() {
return initializeInProgress;
}
/** Returns the data
* @param obj the requestor object
* @return the data
*/
Map getMap (SharedClassObject obj) {
ensureValid (obj);
if (map == null) {
// to signal invalid state
map = new HashMap ();
}
if (! initialized) {
initialized = true;
// no data for this class yet
tryToInitialize (obj);
}
return map;
}
/** Returns a value for given key
* @param obj the requestor object
* @return the data
*/
Object get(SharedClassObject obj, Object key) {
ensureValid (obj);
Object ret;
if (map == null) {
// to signal invalid state
map = new HashMap ();
ret = null;
} else {
ret = map.get(key);
}
if ((ret == null) && !initialized) {
if (key == PROP_SUPPORT) {
return null;
}
initialized = true;
// no data for this class yet
tryToInitialize (obj);
ret = map.get(key);
}
return ret;
}
/** Returns the data
* @return the data
*/
Map getMap() {
ensureValid (get ());
if (map == null) {
// to signal invalid state
map = new HashMap ();
}
return map;
}
private void ensureValid (SharedClassObject obj) throws IllegalStateException {
if (invalid != null) {
String msg;
if (obj != null) {
msg = obj.toString ();
} else {
msg = "<unknown object>"; // NOI18N
}
IllegalStateException ise = new IllegalStateException (msg);
err.annotate (ise, invalid);
throw ise;
} // else fine
}
private void tryToInitialize (SharedClassObject obj) throws IllegalStateException {
initializeInProgress = true;
obj.initializeSuper = false;
try {
obj.initialize ();
} catch (Exception e) {
invalid = e;
IllegalStateException ise = new IllegalStateException(invalid.toString() + " from " + obj); // NOI18N
err.annotate (ise, invalid);
throw ise;
} catch (LinkageError e) {
invalid = e;
IllegalStateException ise = new IllegalStateException(invalid.toString() + " from " + obj); // NOI18N
err.annotate (ise, invalid);
throw ise;
} finally {
initializeInProgress = false;
}
if (!obj.initializeSuper) {
String msg = "You must call super.initialize() from " + obj.getClass().getName() + ".initialize()"; // NOI18N
err.log(ErrorManager.WARNING, msg);
}
}
/** Increases the counter (thread safe)
* @return new counter value
*/
int increase () {
return ++count;
}
/** Dereases the counter (thread safe)
* @return new counter value
*/
int decrease () {
return --count;
}
/** Request for first object. If there is none, use the requestor
* @param obj requestor
* @return the an object of this type
*/
SharedClassObject first (SharedClassObject obj) {
SharedClassObject s = (SharedClassObject)ref.get ();
if (s == null) {
ref = new WeakReference (obj);
return obj;
} else {
return s;
}
}
/** @return shared object or null
*/
public SharedClassObject get () {
return (SharedClassObject)ref.get ();
}
/** Reset map of values. */
public void reset (SharedClassObject obj) {
SharedClassObject s = get ();
if (s != null && s != obj)
return;
invalid = null;
getMap ().clear ();
initialized = true;
tryToInitialize (obj);
}
}
static final class SetAccessibleAction implements PrivilegedExceptionAction {
Class klass;
SetAccessibleAction(Class klass) {
this.klass = klass;
}
public Object run() throws Exception {
return createInstancePrivileged(klass);
}
}
}