1 11 12 package org.eclipse.jdt.apt.core.internal; 13 14 import java.io.File ; 15 import java.io.FileNotFoundException ; 16 import java.io.IOException ; 17 import java.net.MalformedURLException ; 18 import java.net.URL ; 19 import java.net.URLClassLoader ; 20 import java.util.ArrayList ; 21 import java.util.Collections ; 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.Iterator ; 25 import java.util.LinkedHashMap ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.Set ; 29 import java.util.Map.Entry; 30 31 import org.eclipse.core.resources.IMarker; 32 import org.eclipse.core.resources.IProject; 33 import org.eclipse.core.resources.IResource; 34 import org.eclipse.core.resources.IResourceChangeEvent; 35 import org.eclipse.core.resources.IResourceChangeListener; 36 import org.eclipse.core.resources.IResourceDelta; 37 import org.eclipse.core.resources.IResourceDeltaVisitor; 38 import org.eclipse.core.resources.IWorkspaceRoot; 39 import org.eclipse.core.resources.ResourcesPlugin; 40 import org.eclipse.core.runtime.CoreException; 41 import org.eclipse.core.runtime.IPath; 42 import org.eclipse.core.runtime.Path; 43 import org.eclipse.jdt.apt.core.internal.util.FactoryContainer; 44 import org.eclipse.jdt.apt.core.internal.util.FactoryPath; 45 import org.eclipse.jdt.apt.core.internal.util.FactoryPathUtil; 46 import org.eclipse.jdt.apt.core.internal.util.FactoryContainer.FactoryType; 47 import org.eclipse.jdt.apt.core.internal.util.FactoryPath.Attributes; 48 import org.eclipse.jdt.core.IJavaProject; 49 import org.eclipse.jdt.core.JavaCore; 50 51 import com.sun.mirror.apt.AnnotationProcessorFactory; 52 53 118 public class AnnotationProcessorFactoryLoader { 119 120 121 private static AnnotationProcessorFactoryLoader LOADER; 122 123 private static final String JAR_EXTENSION = "jar"; 125 private final Map <IJavaProject, Map <AnnotationProcessorFactory, FactoryPath.Attributes>> _project2Java5Factories = 128 new HashMap <IJavaProject, Map <AnnotationProcessorFactory, FactoryPath.Attributes>>(); 129 130 private final Map <IJavaProject, Map <IServiceFactory, FactoryPath.Attributes>> _project2Java6Factories = 131 new HashMap <IJavaProject, Map <IServiceFactory, FactoryPath.Attributes>>(); 132 133 private final Map <IJavaProject, ClassLoader > _iterativeLoaders = 138 new HashMap <IJavaProject, ClassLoader >(); 139 140 private final Map <IJavaProject,ClassLoader > _batchLoaders = 141 new HashMap <IJavaProject,ClassLoader >(); 142 143 private final Map <String , Set <IJavaProject>> _container2Project = 147 new HashMap <String , Set <IJavaProject>>(); 148 149 150 154 private class ResourceListener implements IResourceChangeListener { 155 156 public void resourceChanged(IResourceChangeEvent event) { 157 Map <IJavaProject, LoadFailureHandler> failureHandlers = new HashMap <IJavaProject, LoadFailureHandler>(); 158 synchronized (AnnotationProcessorFactoryLoader.this) { 159 switch (event.getType()) { 160 161 case (IResourceChangeEvent.PRE_DELETE) : 163 IResource project = event.getResource(); 164 if (project != null && project instanceof IProject) { 165 IJavaProject jproj = JavaCore.create((IProject)project); 166 if (jproj != null) { 167 uncacheProject(jproj); 168 } 169 } 170 break; 171 172 case (IResourceChangeEvent.PRE_BUILD) : 174 IResourceDelta rootDelta = event.getDelta(); 175 FactoryPathDeltaVisitor visitor = new FactoryPathDeltaVisitor(); 176 try { 177 rootDelta.accept(visitor); 178 } catch (CoreException e) { 179 AptPlugin.log(e, "Unable to determine whether resource change affects annotation processor factory path"); } 181 Set <IJavaProject> affected = visitor.getAffectedProjects(); 182 if (affected != null) { 183 processChanges(affected, failureHandlers); 184 } 185 break; 186 187 } 188 } 189 for (LoadFailureHandler handler : failureHandlers.values()) { 190 handler.reportFailureMarkers(); 191 } 192 } 193 194 } 195 196 201 private class FactoryPathDeltaVisitor implements IResourceDeltaVisitor { 202 203 private Set <IJavaProject> _affected = null; 206 207 private void addAffected(Set <IJavaProject> projects) { 208 if (_affected == null) { 209 _affected = new HashSet <IJavaProject>(5); 210 } 211 _affected.addAll(projects); 212 } 213 214 220 public Set <IJavaProject> getAffectedProjects() { 221 return _affected; 222 } 223 224 227 public boolean visit(IResourceDelta delta) { 228 switch (delta.getKind()) { 229 default: 230 return true; 231 case IResourceDelta.ADDED : 232 case IResourceDelta.REMOVED : 233 case IResourceDelta.CHANGED : 234 break; 235 } 236 IResource res = delta.getResource(); 239 if (res == null) { 240 return true; 241 } 242 IProject proj = res.getProject(); 243 if (FactoryPathUtil.isFactoryPathFile(res)) { 244 addAffected(Collections.singleton(JavaCore.create(proj))); 245 return true; 246 } 247 if (res.getType() != IResource.FILE) { 250 return true; 251 } 252 IPath relativePath = res.getFullPath(); 253 String ext = relativePath.getFileExtension(); 254 try { 255 if (JAR_EXTENSION.equals(ext)) { 256 IPath absolutePath = res.getLocation(); 257 if (absolutePath == null) { 258 for (Entry<String , Set <IJavaProject>> entry : _container2Project.entrySet()) { 262 IPath jarPath = new Path(entry.getKey()); 263 if (relativePath.lastSegment().equals(jarPath.lastSegment())) { 264 addAffected(entry.getValue()); 265 } 266 } 267 } 268 else { 269 String key = null; 271 key = absolutePath.toFile().getCanonicalPath(); 272 Set <IJavaProject> projects = _container2Project.get(key); 273 if (projects != null) { 274 addAffected(projects); 275 } 276 } 277 } 278 } catch (Exception e) { 279 AptPlugin.log(e, 280 "Couldn't determine whether any factory paths were affected by change to resource " + res.getName()); } 282 return true; 283 } 284 285 } 286 287 290 public static synchronized AnnotationProcessorFactoryLoader getLoader() { 291 if ( LOADER == null ) { 292 LOADER = new AnnotationProcessorFactoryLoader(); 293 LOADER.registerListener(); 294 } 295 return LOADER; 296 } 297 298 private void registerListener() { 299 ResourcesPlugin.getWorkspace().addResourceChangeListener( 300 new ResourceListener(), 301 IResourceChangeEvent.PRE_DELETE 302 | IResourceChangeEvent.PRE_BUILD); 303 } 304 305 310 public synchronized void resetAll() { 311 removeAptBuildProblemMarkers( null ); 312 _project2Java5Factories.clear(); 313 _project2Java6Factories.clear(); 314 for (ClassLoader cl : _iterativeLoaders.values()) { 316 if (cl instanceof JarClassLoader) 317 ((JarClassLoader)cl).close(); 318 } 319 _iterativeLoaders.clear(); 320 _container2Project.clear(); 321 322 for (ClassLoader cl : _batchLoaders.values()) { 323 if (cl instanceof JarClassLoader) 324 ((JarClassLoader)cl).close(); 325 } 326 _batchLoaders.clear(); 327 328 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 330 for (IProject proj : root.getProjects()) { 331 verifyFactoryPath(JavaCore.create(proj)); 332 } 333 } 334 335 339 public synchronized void resetBatchProcessors(IJavaProject javaProj) { 340 Iterable <Attributes> attrs = null; 341 Map <AnnotationProcessorFactory, Attributes> factories = _project2Java5Factories.get(javaProj); 342 if (factories != null) { 343 attrs = factories.values(); 344 } 345 else { 346 Map <IServiceFactory, Attributes> java6factories = _project2Java6Factories.get(javaProj); 347 if (java6factories != null) { 348 attrs = java6factories.values(); 349 } 350 else { 351 return; 353 } 354 } 355 boolean batchProcsFound = false; 356 for (Attributes attr : attrs) { 357 if (attr.runInBatchMode()) { 358 batchProcsFound = true; 359 break; 360 } 361 } 362 if (batchProcsFound) { 363 _project2Java5Factories.remove(javaProj); 364 _project2Java6Factories.remove(javaProj); 365 } 366 367 ClassLoader c = _batchLoaders.remove(javaProj); 368 if (c instanceof JarClassLoader) ((JarClassLoader)c).close(); 369 } 370 371 378 public Map <AnnotationProcessorFactory, FactoryPath.Attributes> 379 getJava5FactoriesAndAttributesForProject(IJavaProject jproj){ 380 381 LoadFailureHandler failureHandler = new LoadFailureHandler(jproj); 383 384 synchronized (this) { 385 Map <AnnotationProcessorFactory, FactoryPath.Attributes> factories = _project2Java5Factories.get(jproj); 386 if( factories != null ) 387 return Collections.unmodifiableMap(factories); 388 389 FactoryPath fp = FactoryPathUtil.getFactoryPath(jproj); 391 Map <FactoryContainer, FactoryPath.Attributes> containers = fp.getEnabledContainers(); 392 loadFactories(containers, jproj, failureHandler); 393 } 394 395 failureHandler.reportFailureMarkers(); 396 return Collections.unmodifiableMap(_project2Java5Factories.get(jproj)); 397 398 } 399 400 407 public Map <IServiceFactory, FactoryPath.Attributes> 408 getJava6FactoriesAndAttributesForProject(IJavaProject jproj){ 409 410 LoadFailureHandler failureHandler = new LoadFailureHandler(jproj); 412 413 synchronized (this) { 414 415 Map <IServiceFactory, FactoryPath.Attributes> factories = _project2Java6Factories.get(jproj); 416 if( factories != null ) 417 return Collections.unmodifiableMap(factories); 418 419 FactoryPath fp = FactoryPathUtil.getFactoryPath(jproj); 421 Map <FactoryContainer, FactoryPath.Attributes> containers = fp.getEnabledContainers(); 422 loadFactories(containers, jproj, failureHandler); 423 } 424 425 failureHandler.reportFailureMarkers(); 426 return Collections.unmodifiableMap(_project2Java6Factories.get(jproj)); 427 428 } 429 430 434 public synchronized List <AnnotationProcessorFactory> getJava5FactoriesForProject( IJavaProject jproj ) { 435 436 Map <AnnotationProcessorFactory, FactoryPath.Attributes> factoriesAndAttrs = 437 getJava5FactoriesAndAttributesForProject(jproj); 438 final List <AnnotationProcessorFactory> factories = 439 new ArrayList <AnnotationProcessorFactory>(factoriesAndAttrs.keySet()); 440 return Collections.unmodifiableList(factories); 441 } 442 443 449 private void addToResourcesMap(String key, IJavaProject jproj) { 450 Set <IJavaProject> s = _container2Project.get(key); 451 if (s == null) { 452 s = new HashSet <IJavaProject>(); 453 _container2Project.put(key, s); 454 } 455 s.add(jproj); 456 } 457 458 461 private Object loadInstance( String factoryName, ClassLoader cl, IJavaProject jproj, LoadFailureHandler failureHandler ) 462 { 463 Object f = null; 464 try 465 { 466 Class <?> c = cl.loadClass( factoryName ); 467 f = c.newInstance(); 468 } 469 catch( Exception e ) 470 { 471 AptPlugin.trace("Failed to load factory " + factoryName, e); failureHandler.addFailedFactory(factoryName); 473 } 474 catch ( NoClassDefFoundError ncdfe ) 475 { 476 AptPlugin.trace("Failed to load " + factoryName, ncdfe); failureHandler.addFailedFactory(factoryName); 478 } 479 return f; 480 } 481 482 487 private void loadFactories( 488 Map <FactoryContainer, FactoryPath.Attributes> containers, 489 IJavaProject project, 490 LoadFailureHandler failureHandler) 491 { 492 Map <AnnotationProcessorFactory, FactoryPath.Attributes> java5Factories = 493 new LinkedHashMap <AnnotationProcessorFactory, FactoryPath.Attributes>(); 494 Map <IServiceFactory, FactoryPath.Attributes> java6Factories = 495 new LinkedHashMap <IServiceFactory, FactoryPath.Attributes>(); 496 497 removeAptBuildProblemMarkers(project); 498 Set <FactoryContainer> badContainers = verifyFactoryPath(project); 499 if (badContainers != null) { 500 for (FactoryContainer badFC : badContainers) { 501 failureHandler.addFailedFactory(badFC.getId()); 502 containers.remove(badFC); 503 } 504 } 505 506 ClassLoader iterativeClassLoader = _iterativeLoaders.get(project); 508 if (iterativeClassLoader == null) { 509 iterativeClassLoader = _createIterativeClassLoader(containers); 510 _iterativeLoaders.put(project, iterativeClassLoader); 511 } 512 513 _createBatchClassLoader(containers, project); 514 ClassLoader batchClassLoader = _batchLoaders.get(project); 515 516 for ( Map.Entry <FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet() ) 517 { 518 try { 519 final FactoryContainer fc = entry.getKey(); 520 final FactoryPath.Attributes attr = entry.getValue(); 521 assert !attr.runInBatchMode() || (batchClassLoader != null); 522 ClassLoader cl = attr.runInBatchMode() ? batchClassLoader : iterativeClassLoader; 523 524 List <AnnotationProcessorFactory> java5FactoriesInContainer; 526 java5FactoriesInContainer = loadJava5FactoryClasses(fc, cl, project, failureHandler); 527 for ( AnnotationProcessorFactory apf : java5FactoriesInContainer ) { 528 java5Factories.put( apf, entry.getValue() ); 529 } 530 531 if (AptPlugin.canRunJava6Processors()) { 532 List <IServiceFactory> java6FactoriesInContainer; 534 java6FactoriesInContainer = loadJava6FactoryClasses(fc, cl, project, failureHandler); 535 for ( IServiceFactory isf : java6FactoriesInContainer ) { 536 java6Factories.put( isf, entry.getValue() ); 537 } 538 } 539 } 540 catch (FileNotFoundException fnfe) { 541 AptPlugin.log(fnfe, Messages.AnnotationProcessorFactoryLoader_jarNotFound + fnfe.getLocalizedMessage()); 543 } 544 catch (IOException ioe) { 545 AptPlugin.log(ioe, Messages.AnnotationProcessorFactoryLoader_ioError + ioe.getLocalizedMessage()); 546 } 547 } 548 _project2Java5Factories.put(project, java5Factories); 549 _project2Java6Factories.put(project, java6Factories); 550 } 551 552 private List <AnnotationProcessorFactory> loadJava5FactoryClasses( 553 FactoryContainer fc, ClassLoader classLoader, IJavaProject jproj, LoadFailureHandler failureHandler ) 554 throws IOException 555 { 556 Map <String , String > factoryNames = fc.getFactoryNames(); 557 List <AnnotationProcessorFactory> factories = new ArrayList <AnnotationProcessorFactory>(); 558 for ( Entry<String , String > entry : factoryNames.entrySet() ) 559 { 560 if (AptPlugin.JAVA5_FACTORY_NAME.equals(entry.getValue())) { 561 String factoryName = entry.getKey(); 562 AnnotationProcessorFactory factory; 563 if ( fc.getType() == FactoryType.PLUGIN ) 564 factory = FactoryPluginManager.getJava5FactoryFromPlugin( factoryName ); 565 else 566 factory = (AnnotationProcessorFactory)loadInstance( factoryName, classLoader, jproj, failureHandler ); 567 568 if ( factory != null ) 569 factories.add( factory ); 570 } 571 } 572 return factories; 573 } 574 575 private List <IServiceFactory> loadJava6FactoryClasses( 576 FactoryContainer fc, ClassLoader classLoader, IJavaProject jproj, LoadFailureHandler failureHandler ) 577 throws IOException 578 { 579 Map <String , String > factoryNames = fc.getFactoryNames(); 580 List <IServiceFactory> factories = new ArrayList <IServiceFactory>(); 581 for ( Entry<String , String > entry : factoryNames.entrySet() ) 582 { 583 if (AptPlugin.JAVA6_FACTORY_NAME.equals(entry.getValue())) { 584 String factoryName = entry.getKey(); 585 IServiceFactory factory = null; 586 if ( fc.getType() == FactoryType.PLUGIN ) { 587 factory = FactoryPluginManager.getJava6FactoryFromPlugin( factoryName ); 588 } 589 else { 590 Class <?> clazz; 591 try { 592 clazz = classLoader.loadClass(factoryName); 593 factory = new ClassServiceFactory(clazz); 594 } catch (ClassNotFoundException e) { 595 AptPlugin.trace("Unable to load annotation processor " + factoryName, e); failureHandler.addFailedFactory(factoryName); 597 } 598 } 599 600 if ( factory != null ) 601 factories.add( factory ); 602 } 603 } 604 return factories; 605 } 606 607 613 private void processChanges(Set <IJavaProject> affected, Map <IJavaProject,LoadFailureHandler> handlers) { 614 for (IJavaProject jproj : affected) { 615 removeAptBuildProblemMarkers(jproj); 616 uncacheProject(jproj); 617 } 618 for (IJavaProject jproj : affected) { 624 if (jproj.exists()) { 625 Set <FactoryContainer> badContainers = verifyFactoryPath(jproj); 626 if (badContainers != null) { 627 LoadFailureHandler handler = handlers.get(jproj); 628 if (handler == null) { 629 handler = new LoadFailureHandler(jproj); 630 handlers.put(jproj, handler); 631 } 632 for (FactoryContainer container : badContainers) { 633 handler.addMissingLibrary(container.getId()); 634 } 635 } 636 } 637 } 638 639 } 641 642 646 private void uncacheProject(IJavaProject jproj) { 647 _project2Java5Factories.remove(jproj); 648 _project2Java6Factories.remove(jproj); 649 ClassLoader c = _iterativeLoaders.remove(jproj); 650 if (c instanceof JarClassLoader) 651 ((JarClassLoader)c).close(); 652 653 ClassLoader cl = _batchLoaders.remove(jproj); 654 if (cl instanceof JarClassLoader) ((JarClassLoader)cl).close(); 655 656 removeProjectFromResourceMap(jproj); 657 } 658 659 664 private void removeAptBuildProblemMarkers( IJavaProject jproj ) { 665 Set <IJavaProject> jprojects = (jproj == null) ? _project2Java5Factories.keySet() : Collections.singleton(jproj); 667 try { 668 for (IJavaProject jp : jprojects) { 669 if (jp.exists()) { 670 IProject p = jp.getProject(); 671 IMarker[] markers = p.findMarkers(AptPlugin.APT_LOADER_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); 672 if( markers != null ){ 673 for( IMarker marker : markers ) 674 marker.delete(); 675 } 676 } 677 } 678 } 679 catch(CoreException e){ 680 AptPlugin.log(e, "Unable to delete APT build problem marker"); } 682 } 683 684 689 private void removeProjectFromResourceMap(IJavaProject jproj) { 690 Iterator <Entry<String , Set <IJavaProject>>> i = _container2Project.entrySet().iterator(); 691 while (i.hasNext()) { 692 Entry<String , Set <IJavaProject>> e = i.next(); 693 Set <IJavaProject> s = e.getValue(); 694 s.remove(jproj); 695 if (s.isEmpty()) { 697 i.remove(); 698 } 699 } 700 } 701 702 713 private Set <FactoryContainer> verifyFactoryPath(IJavaProject jproj) { 714 Set <FactoryContainer> badContainers = null; 715 FactoryPath fp = FactoryPathUtil.getFactoryPath(jproj); 716 Map <FactoryContainer, FactoryPath.Attributes> containers = fp.getEnabledContainers(); 717 for (FactoryContainer fc : containers.keySet()) { 718 if (fc instanceof JarFactoryContainer) { 719 try { 720 final File jarFile = ((JarFactoryContainer)fc).getJarFile(); 721 if( jarFile != null ){ 723 String key = jarFile.getCanonicalPath(); 724 addToResourcesMap(key, jproj); 725 } 726 } catch (IOException e) { 727 } 733 } 734 if ( !fc.exists() ) { 735 if (badContainers == null) { 736 badContainers = new HashSet <FactoryContainer>(); 737 } 738 badContainers.add(fc); 739 } 740 } 741 return badContainers; 742 } 743 744 747 private ClassLoader _createIterativeClassLoader( Map <FactoryContainer, FactoryPath.Attributes> containers ) 748 { 749 ArrayList <File > fileList = new ArrayList <File >( containers.size() ); 750 for (Map.Entry <FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet()) { 751 FactoryPath.Attributes attr = entry.getValue(); 752 FactoryContainer fc = entry.getKey(); 753 if (!attr.runInBatchMode() && fc instanceof JarFactoryContainer) { 754 JarFactoryContainer jfc = (JarFactoryContainer)fc; 755 fileList.add( jfc.getJarFile() ); 756 } 757 } 758 759 ClassLoader cl; 760 if ( fileList.size() > 0 ) { 761 cl = createClassLoader( fileList, AnnotationProcessorFactoryLoader.class.getClassLoader() ); 762 } 763 else { 764 cl = AnnotationProcessorFactoryLoader.class.getClassLoader(); 765 } 766 return cl; 767 } 768 769 private void _createBatchClassLoader(Map <FactoryContainer, FactoryPath.Attributes> containers, 770 IJavaProject p) 771 { 772 773 ArrayList <File > fileList = new ArrayList <File >( containers.size() ); 774 for (Map.Entry <FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet()) { 775 FactoryPath.Attributes attr = entry.getValue(); 776 FactoryContainer fc = entry.getKey(); 777 if (attr.runInBatchMode() && fc instanceof JarFactoryContainer) { 778 779 JarFactoryContainer jfc = (JarFactoryContainer)fc; 780 File f = jfc.getJarFile(); 781 fileList.add( f ); 782 783 } 784 } 785 786 ClassLoader parentCL = _iterativeLoaders.get(p); 788 if (parentCL == null) { 789 parentCL = AnnotationProcessorFactoryLoader.class.getClassLoader(); 790 } 791 792 if ( fileList.size() > 0 ) { 793 _batchLoaders.put(p,createClassLoader( fileList, parentCL)); 794 } 795 } 796 797 private static ClassLoader createClassLoader(List <File > files, ClassLoader parentCL) { 798 List <URL > urls = new ArrayList <URL >(files.size()); 800 for (int i=0;i<files.size();i++) { 801 try { 802 urls.add(files.get(i).toURI().toURL()); 803 } 804 catch (MalformedURLException mue) { 805 } 807 } 808 URL [] urlArray = urls.toArray(new URL [urls.size()]); 809 return new URLClassLoader (urlArray, parentCL); 810 } 811 } 812 | Popular Tags |