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: package org.netbeans.api.java.source;
042:
043: import java.io.File;
044: import java.io.IOException;
045: import java.lang.ref.Reference;
046: import java.lang.ref.WeakReference;
047: import java.net.MalformedURLException;
048: import java.net.URL;
049: import java.util.ArrayList;
050: import java.util.Arrays;
051: import java.util.Collections;
052: import java.util.HashMap;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.concurrent.CountDownLatch;
056: import java.util.logging.Handler;
057: import java.util.logging.Level;
058: import java.util.logging.LogRecord;
059: import java.util.logging.Logger;
060: import org.netbeans.api.java.classpath.ClassPath;
061: import org.netbeans.api.java.source.CancellableTask;
062: import org.netbeans.api.java.source.CompilationInfo;
063: import org.netbeans.api.java.source.JavaSource;
064: import org.netbeans.api.java.source.JavaSource.Priority;
065: import org.netbeans.api.java.source.JavaSourceTaskFactory;
066: import org.netbeans.api.java.source.JavaSource.Phase;
067: import org.netbeans.api.java.source.SourceUtilsTestUtil;
068: import org.netbeans.junit.NbTestCase;
069: import org.netbeans.modules.java.JavaDataLoader;
070: import org.netbeans.modules.java.source.JavaSourceTaskFactoryManager;
071: import org.netbeans.spi.java.classpath.ClassPathProvider;
072: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
073: import org.openide.filesystems.FileObject;
074: import org.openide.filesystems.FileUtil;
075: import org.openide.util.Lookup;
076: import org.openide.util.lookup.ProxyLookup;
077:
078: /**
079: *
080: * @author Jan Lahoda
081: */
082: public class JavaSourceTaskFactoryTest extends NbTestCase {
083:
084: public JavaSourceTaskFactoryTest(String testName) {
085: super (testName);
086: }
087:
088: private List<FileObject> files;
089: private List<FileObject> filesWithTasks = new ArrayList<FileObject>();
090: private Map<FileObject, CancellableTask<CompilationInfo>> file2Task = new HashMap();
091:
092: private Map<FileObject, CancellableTask<CompilationInfo>> addedTasks = new HashMap<FileObject, CancellableTask<CompilationInfo>>();
093: private Map<FileObject, CancellableTask<CompilationInfo>> removedTasks = new HashMap<FileObject, CancellableTask<CompilationInfo>>();
094: private Map<FileObject, CancellableTask<CompilationInfo>> rescheduled = new HashMap<FileObject, CancellableTask<CompilationInfo>>();
095:
096: private FileObject testDir;
097: private FileObject testFile1;
098: private FileObject testFile2;
099: private DummyCancellableTask<CompilationInfo> task1;
100: private DummyCancellableTask<CompilationInfo> task2;
101:
102: private JavaSourceTaskFactoryImplImpl jstf;
103: private ClassPathProvider cpp;
104:
105: private Lookup.Result<JavaSourceTaskFactory> factories;
106:
107: protected void setUp() throws Exception {
108: cpp = new ClassPathProvider() {
109: public ClassPath findClassPath(FileObject file, String type) {
110: if (type == ClassPath.SOURCE)
111: return ClassPathSupport
112: .createClassPath(new FileObject[] { FileUtil
113: .toFileObject(getDataDir()) });
114: if (type == ClassPath.COMPILE)
115: return ClassPathSupport
116: .createClassPath(new FileObject[0]);
117: if (type == ClassPath.BOOT)
118: return createBootPath();
119: return null;
120: }
121: };
122: SourceUtilsTestUtil.setLookup(new Object[] {
123: JavaDataLoader.getLoader(JavaDataLoader.class), cpp },
124: this .getClass().getClassLoader());
125:
126: JavaSourceTaskFactoryManager.register();
127:
128: jstf = new JavaSourceTaskFactoryImplImpl();
129: JavaSourceTaskFactory.ACCESSOR2 = new AccessorImpl();
130: testDir = SourceUtilsTestUtil.makeScratchDir(this );
131: testFile1 = testDir.createData("test1.java");
132: testFile2 = testDir.createData("test2.java");
133: task1 = new DummyCancellableTask<CompilationInfo>();
134: task2 = new DummyCancellableTask<CompilationInfo>();
135:
136: file2Task.put(testFile1, task1);
137: file2Task.put(testFile1, task2);
138:
139: assertNotNull(JavaSource.forFileObject(testFile1));
140: assertNotNull(JavaSource.forFileObject(testFile2));
141: }
142:
143: public void testTasksRegistration() throws Exception {
144: JavaSourceTaskFactory.SYNCHRONOUS_EVENTS = true;
145:
146: files = Arrays.asList(testFile1);
147:
148: SourceUtilsTestUtil.setLookup(new Object[] {
149: JavaDataLoader.getLoader(JavaDataLoader.class), jstf,
150: cpp }, this .getClass().getClassLoader());
151:
152: assertEquals(1, addedTasks.size());
153: assertEquals(testFile1, addedTasks.keySet().iterator().next());
154: assertEquals(file2Task.get(testFile1), addedTasks.values()
155: .iterator().next());
156:
157: assertEquals(0, removedTasks.size());
158:
159: files = Arrays.asList(testFile2);
160:
161: addedTasks.clear();
162:
163: jstf.fireChangeEvent();
164:
165: assertEquals(1, removedTasks.size());
166: assertEquals(testFile1, removedTasks.keySet().iterator().next());
167: assertEquals(file2Task.get(testFile1), removedTasks.values()
168: .iterator().next());
169:
170: assertEquals(1, addedTasks.size());
171: assertEquals(testFile2, addedTasks.keySet().iterator().next());
172: assertEquals(file2Task.get(testFile2), addedTasks.values()
173: .iterator().next());
174:
175: files = Collections.emptyList();
176:
177: addedTasks.clear();
178: removedTasks.clear();
179:
180: jstf.fireChangeEvent();
181:
182: assertEquals(1, removedTasks.size());
183: assertEquals(testFile2, removedTasks.keySet().iterator().next());
184: assertEquals(file2Task.get(testFile2), removedTasks.values()
185: .iterator().next());
186:
187: assertEquals(0, addedTasks.size());
188:
189: files = Arrays.asList(testFile1);
190:
191: addedTasks.clear();
192: removedTasks.clear();
193:
194: jstf.fireChangeEvent();
195:
196: assertEquals(1, addedTasks.size());
197: assertEquals(testFile1, addedTasks.keySet().iterator().next());
198: assertEquals(file2Task.get(testFile1), addedTasks.values()
199: .iterator().next());
200:
201: assertEquals(0, removedTasks.size());
202:
203: files = Collections.emptyList();
204:
205: addedTasks.clear();
206: removedTasks.clear();
207:
208: jstf.fireChangeEvent();
209:
210: assertEquals(1, removedTasks.size());
211: assertEquals(testFile1, removedTasks.keySet().iterator().next());
212: assertEquals(file2Task.get(testFile1), removedTasks.values()
213: .iterator().next());
214:
215: assertEquals(0, addedTasks.size());
216: }
217:
218: public void testTasksRescheduling() throws Exception {
219: files = Arrays.asList(testFile1);
220:
221: SourceUtilsTestUtil.setLookup(new Object[] {
222: JavaDataLoader.getLoader(JavaDataLoader.class), jstf,
223: cpp }, this .getClass().getClassLoader());
224:
225: assertEquals(1, addedTasks.size());
226: assertEquals(testFile1, addedTasks.keySet().iterator().next());
227: assertEquals(file2Task.get(testFile1), addedTasks.values()
228: .iterator().next());
229:
230: jstf.reschedule(testFile1);
231:
232: assertEquals(1, rescheduled.size());
233: assertEquals(testFile1, rescheduled.keySet().iterator().next());
234: assertEquals(file2Task.get(testFile1), rescheduled.values()
235: .iterator().next());
236:
237: //#84783: the IAE was temporarily disabled:
238: // //test if the IllegalArgumentException is thrown correctly:
239: // try {
240: // jstf.reschedule(testFile2);
241: // fail("Did not throw an IllegalArgumentException");
242: // } catch (IllegalArgumentException e) {
243: // }
244: }
245:
246: public void testFileIsReclaimable() throws Exception {
247: Reference fileRef = new WeakReference(testFile1);
248: Reference jsRef = new WeakReference(JavaSource
249: .forFileObject(testFile1));
250: files = Arrays.asList(testFile1);
251:
252: SourceUtilsTestUtil.setLookup(
253: new Object[] {
254: JavaDataLoader.getLoader(JavaDataLoader.class),
255: jstf, }, this .getClass().getClassLoader());
256:
257: assertEquals(1, addedTasks.size());
258: assertEquals(testFile1, addedTasks.keySet().iterator().next());
259: assertEquals(file2Task.get(testFile1), addedTasks.values()
260: .iterator().next());
261:
262: files = Collections.emptyList();
263:
264: jstf.fireChangeEvent();
265:
266: filesWithTasks.clear();
267: file2Task.clear();
268:
269: addedTasks.clear();
270: removedTasks.clear();
271: rescheduled.clear();
272:
273: testDir = null;
274: testFile1 = null;
275: testFile2 = null;
276: task1 = null;
277: task2 = null;
278:
279: assertGC("", fileRef);
280: assertGC("", jsRef);
281: }
282:
283: public void testDeadlock88782() throws Exception {
284: files = Collections.emptyList();
285:
286: SourceUtilsTestUtil.setLookup(new Object[] {
287: JavaDataLoader.getLoader(JavaDataLoader.class), jstf,
288: cpp }, this .getClass().getClassLoader());
289:
290: final CountDownLatch l = new CountDownLatch(2);
291: final Object lock = new Object();
292:
293: Logger.getLogger(JavaSourceTaskFactory.class.getName())
294: .setLevel(Level.FINEST);
295:
296: Logger.getLogger(JavaSourceTaskFactory.class.getName())
297: .addHandler(new Handler() {
298: public void publish(LogRecord record) {
299: if (JavaSourceTaskFactory.BEFORE_ADDING_REMOVING_TASKS
300: .equals(record.getMessage())) {
301: l.countDown();
302: try {
303: l.await();
304: } catch (InterruptedException e) {
305: Logger.global.log(Level.SEVERE, "", e);
306: }
307: synchronized (lock) {
308: }
309: }
310: if (JavaSourceTaskFactory.FILEOBJECTS_COMPUTATION
311: .equals(record.getMessage())) {
312: l.countDown();
313: try {
314: l.await();
315: } catch (InterruptedException e) {
316: Logger.global.log(Level.SEVERE, "", e);
317: }
318: }
319: }
320:
321: public void flush() {
322: }
323:
324: public void close() throws SecurityException {
325: }
326: });
327:
328: Thread t1 = new Thread() {
329: public void run() {
330: synchronized (lock) {
331: SourceUtilsTestUtil.setLookup(new Object[] {
332: JavaDataLoader
333: .getLoader(JavaDataLoader.class),
334: jstf, new JavaSourceTaskFactoryImplImpl(),
335: cpp }, this .getClass().getClassLoader());
336: }
337: }
338: };
339:
340: t1.start();
341:
342: Thread t2 = new Thread() {
343: public void run() {
344: jstf.fireChangeEvent();
345: }
346: };
347:
348: t2.start();
349:
350: t1.join();
351: t2.join();
352: }
353:
354: private ClassPath createBootPath() {
355: try {
356: String bootPath = System.getProperty("sun.boot.class.path");
357: String[] paths = bootPath.split(File.pathSeparator);
358: List<URL> roots = new ArrayList<URL>(paths.length);
359: for (String path : paths) {
360: File f = new File(path);
361: if (!f.exists()) {
362: continue;
363: }
364: URL url = f.toURI().toURL();
365: if (FileUtil.isArchiveFile(url)) {
366: url = FileUtil.getArchiveRoot(url);
367: }
368: roots.add(url);
369: }
370: return ClassPathSupport.createClassPath(roots
371: .toArray(new URL[roots.size()]));
372: } catch (MalformedURLException ex) {
373: }
374: return null;
375: }
376:
377: private class AccessorImpl implements
378: JavaSourceTaskFactory.Accessor2 {
379:
380: public void addPhaseCompletionTask(JavaSource js,
381: CancellableTask<CompilationInfo> task, Phase phase,
382: Priority priority) throws IOException {
383: addedTasks.put(js.getFileObjects().iterator().next(), task);
384: }
385:
386: public void removePhaseCompletionTask(JavaSource js,
387: CancellableTask<CompilationInfo> task) {
388: removedTasks.put(js.getFileObjects().iterator().next(),
389: task);
390: }
391:
392: public void rescheduleTask(JavaSource js,
393: CancellableTask<CompilationInfo> task) {
394: rescheduled
395: .put(js.getFileObjects().iterator().next(), task);
396: }
397:
398: }
399:
400: private static class DummyCancellableTask<CompilationInfo>
401: implements CancellableTask<CompilationInfo> {
402:
403: public void cancel() {
404: }
405:
406: public void run(CompilationInfo parameter) {
407: }
408:
409: }
410:
411: private class JavaSourceTaskFactoryImplImpl extends
412: JavaSourceTaskFactory {
413: public JavaSourceTaskFactoryImplImpl() {
414: super (Phase.UP_TO_DATE, Priority.MAX);
415: }
416:
417: public CancellableTask<CompilationInfo> createTask(
418: FileObject file) {
419: filesWithTasks.add(file);
420: return file2Task.get(file);
421: }
422:
423: public synchronized List<FileObject> getFileObjects() {
424: return files;
425: }
426:
427: private void fireChangeEvent() {
428: super .fileObjectsChanged();
429: }
430:
431: }
432:
433: private static class ChangeableLookup extends ProxyLookup {
434:
435: public void setLookupsImpl(Lookup[] lookups) {
436: setLookups(lookups);
437: }
438: }
439:
440: }
|