blob: cf4cf804e4f1b2b71555336b24a2795e205f82f8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* wharley@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.apt.core.AptPlugin;
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer;
import org.eclipse.jdt.apt.core.internal.util.FactoryPath;
import org.eclipse.jdt.apt.core.internal.util.FactoryPathUtil;
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer.FactoryType;
import org.eclipse.jdt.apt.core.internal.util.FactoryPath.Attributes;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import com.sun.mirror.apt.AnnotationProcessorFactory;
/**
* Stores annotation processor factories, and handles mapping from projects
* to them. This is a singleton object, created by the first call to getLoader().
* <p>
* Factories contained in plugins are loaded at APT initialization time.
* Factories contained in jar files are loaded for a given project the first time
* getFactoriesForProject() is called, and cached thereafter. Factories are loaded
* from one of two custom classloaders depending on whether the factory container
* is to be run in batch processing mode or normal (iterative) mode; the batch
* classloader for a project is parented by the iterative classloader for that
* project.
* <p>
* <strong>Caches</strong>
* <p>
* Factory classes and iterative-mode classloaders are cached for each project,
* the first time that the classes are needed (e.g., during a build or reconcile).
* The cache is cleared when the project's factory path changes, when a resource
* listed on the factory path is changed, or when the project is deleted.
* If a project contains batch-mode processors, the cache is also cleared at
* the beginning of every full build (batch-mode processors do not run at all
* during reconcile).
* <p>
* If a project's factory path includes containers which cannot be located on
* disk, problem markers will be added to the project. This validation process
* occurs when the cache for a project is first loaded, and whenever the cache
* is invalidated. We do not validate the workspace-level factory path as such;
* it is only used to construct a project-specific factory path for projects
* that do not have their own factory path.
* <p>
* In order to efficiently perform re-validation when resources change, we keep
* track of which projects' factory paths mention which containers. This is
* stored as a map from canonicalized resource path to project. Entries are
* created and updated during factory path validation, and removed upon project
* deletion.
* <p>
* Resource changes are presented as delta trees which may contain more than
* one change. When a change arrives, we build up a list of all potentially
* affected projects, and then perform re-validation after the list is complete.
* That way we avoid redundant validations if a project is affected by more
* than one change.
* <p>
* Note that markers and factory classes have different lifetimes: they are
* discarded at the same time (when something changes), but markers are recreated
* immediately (as a result of validation) while factory classes are not reloaded
* until the next time a build or reconcile occurs.
* <p>
* <strong>Synchronization</strong>
* <p>
* The loader is often accessed on multiple threads, e.g., a build thread, a
* reconcile thread, and a change notification thread all at once. It is
* important to maintain consistency across the various cache objects.
*/
public class AnnotationProcessorFactoryLoader {
/** Loader instance -- holds all workspace and project data */
private static AnnotationProcessorFactoryLoader LOADER;
private static boolean VERBOSE_LOAD = false;
private static final String JAR_EXTENSION = "jar"; //$NON-NLS-1$
// Caches the factory classes associated with each project.
// See class comments for lifecycle of items in this cache.
private final Map<IJavaProject, Map<AnnotationProcessorFactory, FactoryPath.Attributes>> _project2Factories =
new HashMap<IJavaProject, Map<AnnotationProcessorFactory, FactoryPath.Attributes>>();
// Caches the iterative classloaders so that iterative processors
// are not reloaded on every batch build, unlike batch processors
// which are.
// See class comments for lifecycle of items in this cache.
private final Map<IJavaProject, ClassLoader> _project2IterativeClassloaders =
new HashMap<IJavaProject, ClassLoader>();
// Caches information about which resources affect which projects'
// factory paths.
// See class comments for lifecycle of items in this cache.
private final Map<String, Set<IJavaProject>> _container2Project =
new HashMap<String, Set<IJavaProject>>();
private ClassLoader _batchClassLoader;
/**
* Listen for changes that would affect the factory caches or
* build markers.
*/
private class ResourceListener implements IResourceChangeListener {
public void resourceChanged(IResourceChangeEvent event) {
synchronized (AnnotationProcessorFactoryLoader.this) {
switch (event.getType()) {
// Project deletion
case (IResourceChangeEvent.PRE_DELETE) :
IResource project = event.getResource();
if (project != null && project instanceof IProject) {
IJavaProject jproj = JavaCore.create((IProject)project);
if (jproj != null) {
uncacheProject(jproj);
}
}
break;
// Changes to jar files or .factorypath files
case (IResourceChangeEvent.PRE_BUILD) :
IResourceDelta rootDelta = event.getDelta();
FactoryPathDeltaVisitor visitor = new FactoryPathDeltaVisitor();
try {
rootDelta.accept(visitor);
} catch (CoreException e) {
AptPlugin.log(e, "Unable to determine whether resource change affects annotation processor factory path"); //$NON-NLS-1$
}
Set<IJavaProject> affected = visitor.getAffectedProjects();
if (affected != null) {
processChanges(affected);
}
break;
}
}
}
}
/**
* Walk the delta tree to see if there have been changes to
* a factory path or the containers it references. If so,
* re-validate the affected projects' factory paths.
*/
private class FactoryPathDeltaVisitor implements IResourceDeltaVisitor {
// List of projects affected by this change.
// Lazy construction because we assume most changes won't affect any projects.
private Set<IJavaProject> _affected = null;
private void addAffected(Set<IJavaProject> projects) {
if (_affected == null) {
_affected = new HashSet<IJavaProject>(5);
}
_affected.addAll(projects);
}
/**
* Get the list of IJavaProject affected by the delta we visited.
* Not valid until done visiting.
* @return null if there were no affected projects, or a non-empty
* set of IJavaProject otherwise.
*/
public Set<IJavaProject> getAffectedProjects() {
return _affected;
}
/**
* @return true to visit children
*/
public boolean visit(IResourceDelta delta) {
switch (delta.getKind()) {
default:
return true;
case IResourceDelta.ADDED :
case IResourceDelta.REMOVED :
case IResourceDelta.CHANGED :
break;
}
// If the resource is a factory path file, then the project it
// belongs to is affected.
IResource res = delta.getResource();
if (res == null) {
return true;
}
IProject proj = res.getProject();
if (FactoryPathUtil.isFactoryPathFile(res)) {
addAffected(Collections.singleton(JavaCore.create(proj)));
return true;
}
// If the resource is a jar file named in at least one factory
// path, then the projects owning those factorypaths are affected.
if (res.getType() != IResource.FILE) {
return true;
}
IPath relativePath = res.getFullPath();
String ext = relativePath.getFileExtension();
try {
if (JAR_EXTENSION.equals(ext)) {
IPath absolutePath = res.getLocation();
if (absolutePath == null) {
// Jar file within a deleted project. In this case getLocation()
// returns null, so we can't get a canonical path. Bounce every
// factory path that contains anything resembling this jar.
for (Entry<String, Set<IJavaProject>> entry : _container2Project.entrySet()) {
IPath jarPath = new Path(entry.getKey());
if (relativePath.lastSegment().equals(jarPath.lastSegment())) {
addAffected(entry.getValue());
}
}
}
else {
// Lookup key is the canonical path of the resource
String key = null;
key = absolutePath.toFile().getCanonicalPath();
Set<IJavaProject> projects = _container2Project.get(key);
if (projects != null) {
addAffected(projects);
}
}
}
} catch (Exception e) {
AptPlugin.log(e,
"Couldn't determine whether any factory paths were affected by change to resource " + res.getName()); //$NON-NLS-1$
}
return true;
}
}
/**
* Singleton
*/
public static synchronized AnnotationProcessorFactoryLoader getLoader() {
if ( LOADER == null ) {
LOADER = new AnnotationProcessorFactoryLoader();
LOADER.registerListener();
}
return LOADER;
}
private void registerListener() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(
new ResourceListener(),
IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_BUILD);
}
private AnnotationProcessorFactoryLoader() {
FactoryPathUtil.loadPluginFactories();
}
/**
* Called when workspace preferences change. Resource changes, including
* changes to project-specific factory paths, are picked up through the
* ResourceChangedListener mechanism instead.
*/
public synchronized void resetAll() {
removeAptBuildProblemMarkers( null );
_project2Factories.clear();
// Need to close the iterative classloaders
for (ClassLoader cl : _project2IterativeClassloaders.values()) {
if (cl instanceof JarClassLoader) {
((JarClassLoader)cl).close();
}
}
_project2IterativeClassloaders.clear();
_container2Project.clear();
// Validate all projects
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (IProject proj : root.getProjects()) {
verifyFactoryPath(JavaCore.create(proj));
}
}
public synchronized void closeBatchClassLoader() {
if (_batchClassLoader == null)
return;
if (_batchClassLoader instanceof JarClassLoader) {
((JarClassLoader)_batchClassLoader).close();
}
_batchClassLoader = null;
}
/**
* Called when doing a clean build -- resets
* the classloaders for the batch processors
*/
public synchronized void resetBatchProcessors(IJavaProject javaProj) {
// Only need to do a reset if we have batch processors
Map<AnnotationProcessorFactory, Attributes> factories = _project2Factories.get(javaProj);
if (factories == null) {
// Already empty
return;
}
boolean batchProcsFound = false;
for (Attributes attr : factories.values()) {
if (attr.runInBatchMode()) {
batchProcsFound = true;
break;
}
}
if (batchProcsFound) {
_project2Factories.remove(javaProj);
}
}
/**
* @param jproj must not be null
* @return order preserving map of annotation processor factories to their attributes.
* The order the annotation processor factories respect the order of factory containers in
* <code>jproj</code>
*/
public synchronized Map<AnnotationProcessorFactory, FactoryPath.Attributes>
getFactoriesAndAttributesForProject(IJavaProject jproj){
Map<AnnotationProcessorFactory, FactoryPath.Attributes> factories = _project2Factories.get(jproj);
if( factories != null )
return Collections.unmodifiableMap(factories);
// Load the project
FactoryPath fp = FactoryPathUtil.getFactoryPath(jproj);
Map<FactoryContainer, FactoryPath.Attributes> containers = fp.getEnabledContainers();
factories = loadFactories(containers, jproj);
_project2Factories.put(jproj, factories);
return Collections.unmodifiableMap(factories);
}
/**
* Convenience method: get the key set of the map returned by
* @see #getFactoriesAndAttributesForProject(IJavaProject), as a List.
*/
public synchronized List<AnnotationProcessorFactory> getFactoriesForProject( IJavaProject jproj ) {
Map<AnnotationProcessorFactory, FactoryPath.Attributes> factoriesAndAttrs =
getFactoriesAndAttributesForProject(jproj);
final List<AnnotationProcessorFactory> factories =
new ArrayList<AnnotationProcessorFactory>(factoriesAndAttrs.keySet());
return Collections.unmodifiableList(factories);
}
/**
* Add the resource/project pair 'key' -> 'jproj' to the
* _container2Project map.
* @param key the canonicalized pathname of the resource
* @param jproj must not be null
*/
private void addToResourcesMap(String key, IJavaProject jproj) {
Set<IJavaProject> s = _container2Project.get(key);
if (s == null) {
s = new HashSet<IJavaProject>();
_container2Project.put(key, s);
}
s.add(jproj);
}
/**
* @param containers an ordered map.
* @return order preserving map of annotation processor factories to their attributes.
* The order of the factories respect the order of the containers.
*/
private Map<AnnotationProcessorFactory, FactoryPath.Attributes> loadFactories(
Map<FactoryContainer, FactoryPath.Attributes> containers, IJavaProject project )
{
Map<AnnotationProcessorFactory, FactoryPath.Attributes> factoriesAndAttrs =
new LinkedHashMap<AnnotationProcessorFactory, FactoryPath.Attributes>(containers.size() * 4 / 3 + 1);
removeAptBuildProblemMarkers(project);
Set<FactoryContainer> badContainers = verifyFactoryPath(project);
if (badContainers != null) {
reportMissingFactoryContainers(badContainers, project);
for (FactoryContainer badFC : badContainers) {
containers.remove(badFC);
}
}
// Need to use the cached classloader if we have one
ClassLoader iterativeClassLoader = _project2IterativeClassloaders.get(project);
if (iterativeClassLoader == null) {
iterativeClassLoader = _createIterativeClassLoader(containers);
_project2IterativeClassloaders.put(project, iterativeClassLoader);
}
_createBatchClassLoader(containers, iterativeClassLoader);
for ( Map.Entry<FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet() )
{
try {
final FactoryContainer fc = entry.getKey();
final FactoryPath.Attributes attr = entry.getValue();
List<AnnotationProcessorFactory> factories;
if (attr.runInBatchMode()) {
factories = loadFactoryClasses(fc, _batchClassLoader, project);
}
else {
factories = loadFactoryClasses(fc, iterativeClassLoader, project);
}
for ( AnnotationProcessorFactory apf : factories )
factoriesAndAttrs.put( apf, entry.getValue() );
}
catch (FileNotFoundException fnfe) {
// it would be bizarre to get this, given that we already checked for file existence up above.
AptPlugin.log(fnfe, Messages.AnnotationProcessorFactoryLoader_jarNotFound + fnfe.getLocalizedMessage());
}
catch (IOException ioe) {
AptPlugin.log(ioe, Messages.AnnotationProcessorFactoryLoader_ioError + ioe.getLocalizedMessage());
}
}
return factoriesAndAttrs;
}
private List<AnnotationProcessorFactory> loadFactoryClasses(
FactoryContainer fc, ClassLoader classLoader, IJavaProject jproj )
throws IOException
{
List<String> factoryNames = fc.getFactoryNames();
List<AnnotationProcessorFactory> factories = new ArrayList<AnnotationProcessorFactory>( factoryNames.size() );
for ( String factoryName : factoryNames )
{
AnnotationProcessorFactory factory;
if ( fc.getType() == FactoryType.PLUGIN )
factory = FactoryPathUtil.getFactoryFromPlugin( factoryName );
else
factory = loadFactoryFromClassLoader( factoryName, classLoader, jproj );
if ( factory != null )
factories.add( factory );
}
return factories;
}
private AnnotationProcessorFactory loadFactoryFromClassLoader( String factoryName, ClassLoader cl, IJavaProject jproj )
{
AnnotationProcessorFactory f = null;
try
{
Class c = cl.loadClass( factoryName );
f = (AnnotationProcessorFactory)c.newInstance();
}
catch( Exception e )
{
reportFailureToLoadFactory(factoryName, jproj);
}
catch ( NoClassDefFoundError ncdfe )
{
reportFailureToLoadFactory(factoryName, jproj);
}
return f;
}
/**
* Re-validate projects whose factory paths may have been affected
* by a resource change (e.g., adding a previously absent jar file).
* This will cause build problem markers to be removed and regenerated,
* and factory class caches to be cleared.
*/
private void processChanges(Set<IJavaProject> affected) {
for (IJavaProject jproj : affected) {
removeAptBuildProblemMarkers(jproj);
uncacheProject(jproj);
}
// We will do another clear and re-verify when loadFactories()
// is called. But we have to do it then, because things might
// have changed in the interim; and if we don't do it here, then
// we'll have an empty _resources2Project cache, so we'll ignore
// all resource changes until the next build. Is that a problem?
for (IJavaProject jproj : affected) {
if (jproj.exists()) {
Set<FactoryContainer> badContainers = verifyFactoryPath(jproj);
if (badContainers != null) {
reportMissingFactoryContainers(badContainers, jproj);
}
}
}
// TODO: flag the affected projects for rebuild.
}
/**
* When a project is deleted, remove its factory path information from the loader.
* @param jproj
*/
private void uncacheProject(IJavaProject jproj) {
_project2Factories.remove(jproj);
_project2IterativeClassloaders.remove(jproj);
removeProjectFromResourceMap(jproj);
}
/**
* Remove APT build problem markers, e.g., "missing factory jar".
* @param jproj if null, remove markers from all projects that have
* factory paths associated with them.
*/
private void removeAptBuildProblemMarkers( IJavaProject jproj ) {
Set<IJavaProject> jprojects = (jproj == null) ? _project2Factories.keySet() : Collections.singleton(jproj);
try {
for (IJavaProject jp : jprojects) {
if (jp.exists()) {
IProject p = jp.getProject();
IMarker[] markers = p.findMarkers(AptPlugin.APT_BUILD_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
if( markers != null ){
for( IMarker marker : markers )
marker.delete();
}
}
}
}
catch(CoreException e){
AptPlugin.log(e, "Unable to delete APT build problem marker"); //$NON-NLS-1$
}
}
/**
* Remove references to the project from _container2Project. This is done
* when a project is deleted, or before re-verifying the project's
* factory path.
*/
private void removeProjectFromResourceMap(IJavaProject jproj) {
Iterator<Entry<String, Set<IJavaProject>>> i = _container2Project.entrySet().iterator();
while (i.hasNext()) {
Entry<String, Set<IJavaProject>> e = i.next();
Set<IJavaProject> s = e.getValue();
s.remove(jproj);
// Remove any resulting orphaned resources.
if (s.isEmpty()) {
i.remove();
}
}
}
/**
* Enter problem markers for factory containers that could not be found on
* disk. This routine does not check whether markers already exist.
* See class comments for information about the lifecycle of these markers.
* @param jarName the name of the jar file. This string is used only in
* the text of the message, so it doesn't matter whether it's a relative
* path, absolute path, or complete garbage.
* @param jproj must not be null.
*/
private void reportMissingFactoryContainers(Set<FactoryContainer> badContainers, IJavaProject jproj) {
IProject project = jproj.getProject();
for (FactoryContainer fc : badContainers) {
try {
String message = Messages.bind(
Messages.AnnotationProcessorFactoryLoader_factorypath_missingLibrary,
new String[] {fc.getId(), project.getName()});
IMarker marker = project.createMarker(AptPlugin.APT_BUILD_PROBLEM_MARKER);
marker.setAttributes(
new String[] {
IMarker.MESSAGE,
IMarker.SEVERITY,
IMarker.LOCATION
},
new Object[] {
message,
IMarker.SEVERITY_ERROR,
Messages.AnnotationProcessorFactoryLoader_factorypath
}
);
} catch (CoreException e) {
AptPlugin.log(e, "Unable to create APT build problem marker on project " + project.getName()); //$NON-NLS-1$
}
}
}
/**
* Enter a marker for a factory class that could not be loaded.
* Note that if a jar is missing, we won't be able to load its factory
* names, and thus we won't even try loading its factory classes; but
* we can still fail to load a factory class if, for instance, the
* jar is corrupted or the factory constructor throws an exception.
* See class comments for information about the lifecycle of these markers.
* @param factoryName the fully qualified class name of the factory
* @param jproj must not be null
*/
private void reportFailureToLoadFactory(String factoryName, IJavaProject jproj) {
IProject project = jproj.getProject();
try {
String message = Messages.bind(
Messages.AnnotationProcessorFactoryLoader_unableToLoadFactoryClass,
new String[] {factoryName, project.getName()});
IMarker marker = project.createMarker(AptPlugin.APT_BUILD_PROBLEM_MARKER);
marker.setAttributes(
new String[] {
IMarker.MESSAGE,
IMarker.SEVERITY,
IMarker.LOCATION
},
new Object[] {
message,
IStatus.ERROR,
Messages.AnnotationProcessorFactoryLoader_factorypath
}
);
} catch (CoreException e) {
AptPlugin.log(e, "Unable to create build problem marker"); //$NON-NLS-1$
}
}
/**
* Check the factory path for a project and ensure that all the
* containers it lists are available. Adds jar factory container
* resources to the _container2Project cache, whether or not the
* resource can actually be found.
*
* @param jproj the project, or null to check all projects that
* are in the cache.
* @return a Set of all invalid containers, or null if all containers
* on the path were valid.
*/
private Set<FactoryContainer> verifyFactoryPath(IJavaProject jproj) {
Set<FactoryContainer> badContainers = null;
FactoryPath fp = FactoryPathUtil.getFactoryPath(jproj);
Map<FactoryContainer, FactoryPath.Attributes> containers = fp.getEnabledContainers();
for (FactoryContainer fc : containers.keySet()) {
if (fc instanceof JarFactoryContainer) {
try {
final File jarFile = ((JarFactoryContainer)fc).getJarFile();
// if null, will add to bad container set below.
if( jarFile != null ){
String key = jarFile.getCanonicalPath();
addToResourcesMap(key, jproj);
}
} catch (IOException e) {
// If there's something this malformed on the factory path,
// don't bother putting it on the resources map; we'll never
// get notified about a change to it anyway. It should get
// reported either as a bad container (below) or as a failure
// to load (later on).
}
}
if ( !fc.exists() ) {
if (badContainers == null) {
badContainers = new HashSet<FactoryContainer>();
}
badContainers.add(fc);
}
}
return badContainers;
}
/**
* @param containers an ordered map.
*/
private ClassLoader _createIterativeClassLoader( Map<FactoryContainer, FactoryPath.Attributes> containers )
{
ArrayList<File> fileList = new ArrayList<File>( containers.size() );
for (Map.Entry<FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet()) {
FactoryPath.Attributes attr = entry.getValue();
FactoryContainer fc = entry.getKey();
if (!attr.runInBatchMode() && fc instanceof JarFactoryContainer) {
JarFactoryContainer jfc = (JarFactoryContainer)fc;
fileList.add( jfc.getJarFile() );
}
}
ClassLoader cl;
if ( fileList.size() > 0 ) {
//cl = new JarClassLoader( fileList, AnnotationProcessorFactoryLoader.class.getClassLoader() );
// Temporary revert to URLClassLoader, as the JarClassLoader doesn't properly define packages
List<URL> urls = new ArrayList<URL>(fileList.size());
for (File f : fileList) {
try {
urls.add(f.toURL());
}
catch (MalformedURLException mue) {
mue.printStackTrace();
}
}
URL[] urlArray = urls.toArray(new URL[urls.size()]);
cl = new URLClassLoader( urlArray, AnnotationProcessorFactoryLoader.class.getClassLoader() );
}
else {
cl = AnnotationProcessorFactoryLoader.class.getClassLoader();
}
return cl;
}
private void _createBatchClassLoader(
Map<FactoryContainer, FactoryPath.Attributes> containers,
ClassLoader iterativeClassLoader) {
assert _batchClassLoader == null : "Previous batch classloader was non-null -- it was not closed"; //$NON-NLS-1$
ArrayList<File> fileList = new ArrayList<File>( containers.size() );
for (Map.Entry<FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet()) {
FactoryPath.Attributes attr = entry.getValue();
FactoryContainer fc = entry.getKey();
if (attr.runInBatchMode() && fc instanceof JarFactoryContainer) {
JarFactoryContainer jfc = (JarFactoryContainer)fc;
File f = jfc.getJarFile();
fileList.add( f );
}
}
if ( fileList.size() > 0 ) {
//_batchClassLoader = new JarClassLoader( fileList, iterativeClassLoader );
// Temporary revert to URLClassLoader, as the JarClassLoader doesn't properly define packages
List<URL> urls = new ArrayList<URL>(fileList.size());
for (File f : fileList) {
try {
urls.add(f.toURL());
}
catch (MalformedURLException mue) {
mue.printStackTrace();
}
}
URL[] urlArray = urls.toArray(new URL[urls.size()]);
_batchClassLoader = new URLClassLoader( urlArray, AnnotationProcessorFactoryLoader.class.getClassLoader() );
}
else {
// No batch classloader
_batchClassLoader = null;
}
}
}