1 19 20 package org.netbeans.modules.javacore.classpath; 21 22 import java.beans.PropertyChangeListener ; 23 import java.beans.PropertyChangeSupport ; 24 import java.io.File ; 25 import java.io.IOException ; 26 import java.net.URL ; 27 import java.util.ArrayList ; 28 import java.util.Collections ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.SortedSet ; 32 import java.util.TreeSet ; 33 import java.util.concurrent.CountDownLatch ; 34 import java.util.logging.Handler ; 35 import java.util.logging.Level ; 36 import java.util.logging.LogRecord ; 37 import java.util.logging.Logger ; 38 import junit.framework.Assert; 39 import org.netbeans.api.java.classpath.ClassPath; 40 import org.netbeans.api.java.classpath.GlobalPathRegistry; 41 import org.netbeans.api.java.queries.SourceForBinaryQuery.Result; 42 import org.netbeans.junit.NbTestCase; 43 import org.netbeans.spi.java.classpath.ClassPathFactory; 44 import org.netbeans.spi.java.classpath.ClassPathImplementation; 45 import org.netbeans.spi.java.classpath.PathResourceImplementation; 46 import org.netbeans.spi.java.classpath.support.ClassPathSupport; 47 import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation; 48 import org.openide.filesystems.FileObject; 49 import org.openide.filesystems.FileUtil; 50 import org.openide.filesystems.URLMapper; 51 import org.openide.util.Lookup; 52 import org.openide.util.lookup.Lookups; 53 import org.openide.util.lookup.ProxyLookup; 54 55 58 public class MergedClassPathImplementationTest extends NbTestCase { 59 60 private boolean failed; 61 62 static { 64 System.setProperty("org.openide.util.Lookup", Lkp.class.getName()); 66 Assert.assertEquals(Lkp.class, Lookup.getDefault().getClass()); 67 } 68 69 public static final class Lkp extends ProxyLookup { 70 private static Lkp DEFAULT; 71 public Lkp() { 72 Assert.assertNull(DEFAULT); 73 DEFAULT = this; 74 setLookup(new Object [0]); 75 } 76 public static void setLookup(Object [] instances) { 77 ClassLoader l = Lkp.class.getClassLoader(); 78 DEFAULT.setLookups(new Lookup[] { 79 Lookups.fixed(instances), 80 Lookups.metaInfServices(l), 81 Lookups.singleton(l), 82 }); 83 } 84 } 85 86 private static final String ROOT = "cproot_"; 87 private static final int ROOT_COUNT = 6; 88 private FileObject[] roots; 89 90 91 public MergedClassPathImplementationTest (String name) { 92 super (name); 93 } 94 95 96 97 protected void setUp() throws Exception { 98 super.setUp(); 99 this.clearWorkDir(); 100 File f = this.getWorkDir(); 101 FileObject wd = getWorkDirFileObject (); 102 this.roots = new FileObject[ROOT_COUNT]; 103 for (int i=0; i< ROOT_COUNT; i++) { 104 this.roots[i] = wd.createFolder(ROOT+i); 105 } 106 } 107 108 protected void tearDown() throws Exception { 109 super.tearDown(); 110 } 111 112 113 public void testMergedClassPath () throws IOException { 114 ClassPath cp_1 = ClassPathSupport.createClassPath (new FileObject[] {roots[0], roots[1]}); 115 assertNotNull ("ClassPathSupport.createClassPath() returned null",cp_1); 116 ClassPath cp_2 = ClassPathSupport.createClassPath (new FileObject[] {roots[2], roots[3]}); 117 assertNotNull ("ClassPathSupport.createClassPath() returned null",cp_2); 118 DynamicClassPath dympl = new DynamicClassPath(); 119 ClassPath cp_3 = ClassPathFactory.createClassPath(dympl); 120 assertNotNull ("ClassPathSupport.createClassPath() returned null",cp_3); 121 GlobalPathRegistry regs = GlobalPathRegistry.getDefault(); 122 regs.register(ClassPath.COMPILE, new ClassPath[] {cp_1, cp_3}); 123 MergedClassPathImplementation mcpi = MergedClassPathImplementation.getDefault(); 124 ClassPath mcp = ClassPathFactory.createClassPath (mcpi); 125 assertNotNull ("ClassPathSupport.createClassPath() returned null for MergedClassPathImplementation" ,mcp); 126 PathResourceImplementation[] resources = mcpi.getUnresolvedRoots(); 127 assertEquivalent (resources, new FileObject[] {roots[0],roots[1]}); 128 regs.register(ClassPath.COMPILE, new ClassPath[] {cp_2}); 129 resources = mcpi.getUnresolvedRoots(); 130 assertEquivalent (resources,new FileObject[] {roots[0],roots[1],roots[2],roots[3]}); 131 dympl.setRoots(new FileObject[]{roots[4],roots[5]}); 132 resources = mcpi.getUnresolvedRoots(); 133 assertEquivalent (resources,new FileObject[] {roots[0],roots[1],roots[2],roots[3],roots[4],roots[5]}); 134 dympl.setRoots(new FileObject[]{}); 135 resources = mcpi.getUnresolvedRoots(); 136 assertEquivalent (resources,new FileObject[] {roots[0],roots[1],roots[2],roots[3]}); 137 dympl.setRoots(new FileObject[]{roots[2],roots[3]}); 138 resources = mcpi.getUnresolvedRoots(); 139 assertEquivalent (resources,new FileObject[] {roots[0],roots[1],roots[2],roots[3],roots[2],roots[3]}); 140 dympl.setRoots(new FileObject[]{}); 141 resources = mcpi.getUnresolvedRoots(); 142 assertEquivalent (resources,new FileObject[] {roots[0],roots[1],roots[2],roots[3]}); 143 } 144 145 public void testDeadlock() throws Exception { 146 final MergedClassPathImplementation mcpi = MergedClassPathImplementation.getDefault(); 148 Lkp.setLookup(new Object [] { 149 new SourceForBinaryQueryImplementation() { 150 public Result findSourceRoots(URL binaryRoot) { 151 Thread mcpiLocker = new Thread (new Runnable () { 152 public void run() { 153 synchronized(mcpi) {} 154 } 155 }); 156 mcpiLocker.start(); 157 try { 158 mcpiLocker.join(); 159 } catch (InterruptedException ie) { 160 ie.printStackTrace(); 161 failed = true; 162 } 163 return null; 164 } 165 } 166 }); 167 final DynamicClassPath dympl = new DynamicClassPath(); 168 ClassPath cp = ClassPathFactory.createClassPath(dympl); 169 GlobalPathRegistry regs = GlobalPathRegistry.getDefault(); 170 regs.register(ClassPath.COMPILE, new ClassPath[] {cp}); 171 dympl.setRoots(new FileObject[]{roots[0]}); 172 assertFalse(failed); 173 } 174 175 public void testRaceCondition84603 () throws Exception { 176 final MergedClassPathImplementation mcpi = MergedClassPathImplementation.getDefault(); 177 final DynamicClassPath dympl = new DynamicClassPath(); 178 final ClassPath cp = ClassPathFactory.createClassPath(dympl); 179 final GlobalPathRegistry regs = GlobalPathRegistry.getDefault(); 180 regs.register(ClassPath.COMPILE, new ClassPath[] {cp}); 181 final CountDownLatch start = new CountDownLatch (1); 182 final CountDownLatch end = new CountDownLatch (1); 183 class RCHandler extends Handler implements Runnable { 184 185 private Thread thread; 186 187 public void publish(LogRecord record) { 188 if (this.thread == Thread.currentThread()) { 189 start.countDown(); 190 try { 191 end.await(); 192 } catch (InterruptedException ie) {} 193 } 194 } 195 196 public void run () { 197 try { 198 this.thread = Thread.currentThread(); 199 dympl.setRoots(new FileObject[] {roots[0]}); 200 } catch (IOException ioe) { 201 ioe.printStackTrace(); 202 } 203 } 204 205 public void flush() { 206 } 207 208 public void close() throws SecurityException { 209 } 210 }; 211 212 final RCHandler handler = new RCHandler (); 213 final Logger log = Logger.getLogger("TEST-"+MergedClassPathImplementation.class.getName()); 214 log.addHandler(handler); 215 try { 216 Thread t = new Thread (handler); 217 t.start(); 218 try { 219 start.await(); 220 dympl.setRoots(new FileObject[] {roots[1]}); } finally { 222 end.countDown(); 223 } 224 } finally { 225 log.removeHandler(handler); 226 } 227 } 228 229 private FileObject getWorkDirFileObject () throws IOException { 230 FileObject fos = FileUtil.toFileObject(this.getWorkDir()); 231 if (fos != null) { 232 return fos; 233 } 234 else { 235 throw new IllegalStateException ("Can not resolve FileObject for home dir"); 236 } 237 } 238 239 private static void assertEquivalent(List entries, FileObject[] roots) throws IOException { 240 assertNotNull("Entries are null", entries); 241 assertNotNull("Roots are null", roots); 242 SortedSet expected = new TreeSet (), actual = new TreeSet (); 243 for (int i = 0; i < roots.length; i++) { 244 expected.add(roots[i].getURL().toExternalForm()); 245 } 246 Iterator it = entries.iterator(); 247 while (it.hasNext()) { 248 ClassPath.Entry entry = (ClassPath.Entry) it.next(); 249 actual.add(entry.getURL().toExternalForm()); 250 } 251 assertEquals("Correct entry URLs", expected, actual); 252 } 253 254 private static void assertEquivalent (PathResourceImplementation[] resources, FileObject[] roots) { 255 assertNotNull("Entries are null", resources); 256 assertNotNull("Roots are null", roots); 257 assertEquals("Different length",resources.length,roots.length); 258 for (int i=0; i<resources.length; i++) { 259 URL [] urls = resources[i].getRoots(); 260 for (int j=0; j<urls.length; j++) { 261 FileObject fo = URLMapper.findFileObject(urls[j]); 262 assertNotNull (fo); 263 assertEquals ("Invalid root",roots[i],fo); 264 } 265 } 266 } 267 268 protected Level logLevel() { 269 return Level.FINEST; 270 } 271 272 private static class DynamicClassPath implements ClassPathImplementation { 273 274 public PropertyChangeSupport support; 275 private List roots = Collections.EMPTY_LIST; 276 277 public DynamicClassPath () { 278 this.support = new PropertyChangeSupport (this); 279 } 280 281 public void setRoots (FileObject[] roots) throws IOException { 282 List newRoots = new ArrayList (); 283 for (int i=0; i< roots.length; i++) { 284 PathResourceImplementation impl = ClassPathSupport.createResource(roots[i].getURL()); 285 newRoots.add(impl); 286 } 287 this.roots = newRoots; 288 this.support.firePropertyChange(PROP_RESOURCES,null,null); 289 } 290 291 public void addPropertyChangeListener(PropertyChangeListener listener) { 292 this.support.addPropertyChangeListener (listener); 293 } 294 295 public List getResources() { 296 return Collections.unmodifiableList(this.roots); 297 } 298 299 public void removePropertyChangeListener(PropertyChangeListener listener) { 300 this.support.removePropertyChangeListener (listener); 301 } 302 } 303 } 304 | Popular Tags |