001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.util;
043:
044: import java.io.ByteArrayInputStream;
045: import java.io.ByteArrayOutputStream;
046: import java.io.ObjectInputStream;
047: import java.io.ObjectOutputStream;
048: import java.io.PrintStream;
049: import java.lang.ref.Reference;
050: import java.lang.ref.WeakReference;
051: import java.lang.reflect.Field;
052: import java.lang.reflect.Method;
053: import java.net.URL;
054: import java.net.URLClassLoader;
055: import org.netbeans.junit.NbTestCase;
056: import org.openide.util.SharedClassObject;
057: import org.openide.util.test.MockPropertyChangeListener;
058:
059: /** Test SharedClassObject singletons: esp. initialization semantics.
060: * @author Jesse Glick
061: */
062: public class SharedClassObjectTest extends NbTestCase {
063:
064: public SharedClassObjectTest(String name) {
065: super (name);
066: }
067:
068: public void testSimpleSCO() throws Exception {
069: Class<? extends SharedClassObject> c = makeClazz("SimpleSCO");
070: assertTrue(c != SimpleSCO.class);
071: assertNull("No instance created yet", SharedClassObject
072: .findObject(c, false));
073: SharedClassObject o = SharedClassObject.findObject(c, true);
074: assertNotNull(o);
075: assertEquals(
076: "org.openide.util.SharedClassObjectTest$SimpleSCO", o
077: .getClass().getName());
078: assertEquals(c, o.getClass());
079: assertEquals("has not been initialized", 0, o.getClass()
080: .getField("initcount").getInt(o));
081: assertNull(o.getProperty("foo"));
082: assertEquals("has been initialized", 1, o.getClass().getField(
083: "initcount").getInt(o));
084: assertNull(o.getProperty("bar"));
085: assertEquals("has been initialized just once", 1, o.getClass()
086: .getField("initcount").getInt(null));
087: Class<? extends SharedClassObject> c2 = makeClazz("SimpleSCO");
088: assertTrue("Call to makeClazz created a fresh class", c != c2);
089: SharedClassObject o2 = SharedClassObject.findObject(c2, true);
090: o2.getProperty("baz");
091: assertEquals(1, o2.getClass().getField("initcount")
092: .getInt(null));
093: }
094:
095: public void testClearSharedData() throws Exception {
096: Class<? extends SharedClassObject> c = makeClazz("DontClearSharedDataSCO");
097: SharedClassObject o = SharedClassObject.findObject(c, true);
098: o.putProperty("inited", true);
099: assertEquals("DCSD has been initialized", true, o
100: .getProperty("inited"));
101: Reference<?> r = new WeakReference<Object>(o);
102: o = null;
103: assertGC("collected SCO instance", r);
104: assertNull(
105: "findObject(Class,false) gives nothing after running GC + finalization #1",
106: SharedClassObject.findObject(c));
107: o = SharedClassObject.findObject(c, true);
108: assertEquals("has still been initialized", true, o
109: .getProperty("inited"));
110: c = makeClazz("ClearSharedDataSCO");
111: o = SharedClassObject.findObject(c, true);
112: o.putProperty("inited", true);
113: assertEquals("CSD has been initialized", true, o
114: .getProperty("inited"));
115: r = new WeakReference<Object>(o);
116: o = null;
117: assertGC("collected SCO instance", r);
118: assertNull(
119: "findObject(Class,false) gives nothing after running GC + finalization #2",
120: SharedClassObject.findObject(c));
121: o = SharedClassObject.findObject(c, true);
122: assertEquals("is no longer initialized", null, o
123: .getProperty("inited"));
124: o.putProperty("inited", true);
125: assertEquals("has now been initialized again", true, o
126: .getProperty("inited"));
127: }
128:
129: public void testIllegalState() throws Exception {
130: Class<? extends SharedClassObject> c = makeClazz("InitErrorSCO");
131: SharedClassObject o = SharedClassObject.findObject(c, true);
132: assertNotNull(o);
133: try {
134: o.getProperty("foo");
135: fail("should not be able to do anything with it");
136: } catch (IllegalStateException ise) {
137: // Good.
138: }
139: try {
140: o.getProperty("bar");
141: fail("should still not be able to do anything with it");
142: } catch (IllegalStateException ise) {
143: // Good.
144: }
145: }
146:
147: public void testPropertyChanges() throws Exception {
148: Class<? extends SharedClassObject> c = makeClazz("PropFirerSCO");
149: Method putprop = c.getMethod("putprop", Object.class,
150: Boolean.TYPE);
151: Method getprop = c.getMethod("getprop");
152: Field count = c.getField("addCount");
153: SharedClassObject o = SharedClassObject.findObject(c, true);
154: assertNull(getprop.invoke(o));
155: assertEquals(0, count.getInt(o));
156: MockPropertyChangeListener l = new MockPropertyChangeListener(
157: "key");
158: o.addPropertyChangeListener(l);
159: assertEquals(1, count.getInt(o));
160: MockPropertyChangeListener l2 = new MockPropertyChangeListener(
161: "key");
162: o.addPropertyChangeListener(l2);
163: assertEquals(1, count.getInt(o));
164: o.removePropertyChangeListener(l2);
165: assertEquals(1, count.getInt(o));
166: putprop.invoke(o, "something", false);
167: l.assertEventCount(0);
168: assertEquals("something", getprop.invoke(o));
169: putprop.invoke(o, "somethingelse", true);
170: l.assertEventCount(1);
171: assertEquals("somethingelse", getprop.invoke(o));
172: // Check that setting the same val does not fire an additional change (cf. #37769):
173: putprop.invoke(o, "somethingelse", true);
174: l.assertEventCount(0);
175: assertEquals("somethingelse", getprop.invoke(o));
176: // Check equals() as well as ==:
177: putprop.invoke(o, new String("somethingelse"), true);
178: l.assertEventCount(0);
179: assertEquals("somethingelse", getprop.invoke(o));
180: o.removePropertyChangeListener(l);
181: assertEquals(0, count.getInt(o));
182: o.addPropertyChangeListener(l);
183: assertEquals(1, count.getInt(o));
184: o.removePropertyChangeListener(l);
185: assertEquals(0, count.getInt(o));
186: }
187:
188: public void testRecursiveInit() throws Exception {
189: Class<? extends SharedClassObject> c = makeClazz("RecursiveInitSCO");
190: SharedClassObject o = SharedClassObject.findObject(c, true);
191: assertEquals(0, c.getField("count").getInt(null));
192: o.getProperty("foo");
193: assertEquals(1, c.getField("count").getInt(null));
194: assertEquals(o, c.getField("INSTANCE").get(null));
195: }
196:
197: public void testAbilityToReadResolveToAnyObject() throws Exception {
198: SharedClassObject o = SharedClassObject.findObject(
199: SharedClassObjectWithReadResolve.class, true);
200: ByteArrayOutputStream os = new ByteArrayOutputStream();
201: ObjectOutputStream oos = new ObjectOutputStream(os);
202: oos.writeObject(o);
203: oos.close();
204:
205: ObjectInputStream ois = new ObjectInputStream(
206: new ByteArrayInputStream(os.toByteArray()));
207: Object result = ois.readObject();
208: ois.close();
209:
210: assertEquals("Result should be the string", String.class,
211: result.getClass());
212:
213: }
214:
215: /** Create a fresh Class object from one of this test's inner classes.
216: * Produces a new classloader so the class is always fresh.
217: */
218: private Class<? extends SharedClassObject> makeClazz(String name)
219: throws Exception {
220: return Class.forName(
221: "org.openide.util.SharedClassObjectTest$" + name,
222: false, new MaskingURLClassLoader()).asSubclass(
223: SharedClassObject.class);
224: }
225:
226: private static final class MaskingURLClassLoader extends
227: URLClassLoader {
228: public MaskingURLClassLoader() {
229: super (new URL[] { SharedClassObjectTest.class
230: .getProtectionDomain().getCodeSource()
231: .getLocation() }, SharedClassObject.class
232: .getClassLoader());
233: }
234:
235: protected Class loadClass(String name, boolean resolve)
236: throws ClassNotFoundException {
237: if (name
238: .startsWith("org.openide.util.SharedClassObjectTest")) {
239: // Do not proxy to parent!
240: Class c = findLoadedClass(name);
241: if (c != null)
242: return c;
243: c = findClass(name);
244: if (resolve)
245: resolveClass(c);
246: return c;
247: } else {
248: return super .loadClass(name, resolve);
249: }
250: }
251: }
252:
253: public static class SimpleSCO extends SharedClassObject {
254: public static int initcount = 0;
255: private static String firstinit = null;
256:
257: protected void initialize() {
258: super .initialize();
259: initcount++;
260: if (initcount > 1) {
261: System.err
262: .println("Multiple initializations of SimpleSCO: see http://www.netbeans.org/issues/show_bug.cgi?id=14700");
263: System.err.print(firstinit);
264: new Throwable("Init #" + initcount + " here")
265: .printStackTrace();
266: } else {
267: ByteArrayOutputStream baos = new ByteArrayOutputStream();
268: new Throwable("Init #1 here")
269: .printStackTrace(new PrintStream(baos));
270: firstinit = baos.toString();
271: // don't print anything unless there is a problem later
272: }
273: }
274:
275: // Protect against random workings of GC:
276: protected boolean clearSharedData() {
277: return false;
278: }
279: }
280:
281: public static class ClearSharedDataSCO extends SharedClassObject {
282: protected boolean clearSharedData() {
283: return true;
284: }
285: }
286:
287: public static class DontClearSharedDataSCO extends
288: SharedClassObject {
289: protected boolean clearSharedData() {
290: return false;
291: }
292: }
293:
294: // SCO.DataEntry.tryToInitialize in absence of EM will try to print
295: // stack trace of unexpected exceptions, so just suppress it
296: private static final class QuietException extends
297: NullPointerException {
298: public void printStackTrace() {
299: // do nothing
300: }
301: }
302:
303: public static class InitErrorSCO extends SharedClassObject {
304: protected void initialize() {
305: throw new QuietException();
306: }
307: }
308:
309: public static class PropFirerSCO extends SharedClassObject {
310: public int addCount = 0;
311:
312: protected void addNotify() {
313: super .addNotify();
314: addCount++;
315: }
316:
317: protected void removeNotify() {
318: addCount--;
319: super .removeNotify();
320: }
321:
322: public void putprop(Object val, boolean notify) {
323: putProperty("key", val, notify);
324: }
325:
326: public Object getprop() {
327: return getProperty("key");
328: }
329: }
330:
331: public static class RecursiveInitSCO extends SharedClassObject {
332: public static final RecursiveInitSCO INSTANCE = SharedClassObject
333: .findObject(RecursiveInitSCO.class, true);
334: public static int count = 0;
335:
336: protected void initialize() {
337: super .initialize();
338: count++;
339: }
340: }
341:
342: public static final class SharedClassObjectWithReadResolve extends
343: SharedClassObject {
344: public Object readResolve()
345: throws java.io.ObjectStreamException {
346: return "Ahoj";
347: }
348: }
349: }
|