org.apache.xbean.classloader.UrlResourceFinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-eclipse-batch Show documentation
Show all versions of groovy-eclipse-batch Show documentation
Groovy Eclipse Compiler wrapped for Batch Use from Maven
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xbean.classloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
/**
* @author Dain Sundstrom
*/
public class UrlResourceFinder implements ResourceFinder {
private static final JarFileFactory DEFAULT_JAR_FILE_FACTORY = new JarFileFactory() {
public JarFile newJarFile(File file) throws IOException {
return new JarFile(file);
}
};
private final Object lock = new Object();
private final LinkedHashSet urls = new LinkedHashSet();
private final LinkedHashMap classPath = new LinkedHashMap();
private final LinkedHashSet watchedFiles = new LinkedHashSet();
private boolean destroyed = false;
private JarFileFactory jarFileFactory;
public UrlResourceFinder(JarFileFactory jarFileFactory) {
this(jarFileFactory, new URL[] {});
}
public UrlResourceFinder(JarFileFactory jarFileFactory, URL[] urls) {
this.jarFileFactory = (jarFileFactory == null ? DEFAULT_JAR_FILE_FACTORY : jarFileFactory);
addUrls(urls);
}
protected final JarFileFactory getJarFileFactory() {
return jarFileFactory;
}
public void destroy() {
synchronized (lock) {
if (destroyed) {
return;
}
destroyed = true;
urls.clear();
for (Iterator iterator = classPath.values().iterator(); iterator.hasNext();) {
ResourceLocation resourceLocation = iterator.next();
resourceLocation.close();
}
classPath.clear();
}
}
public ResourceHandle getResource(String resourceName) {
synchronized (lock) {
if (destroyed) {
return null;
}
for (Iterator> iterator = getClassPath().entrySet().iterator(); iterator
.hasNext();) {
Map.Entry entry = iterator.next();
ResourceLocation resourceLocation = entry.getValue();
ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName);
if (resourceHandle != null && !resourceHandle.isDirectory()) {
return resourceHandle;
}
}
}
return null;
}
public URL findResource(String resourceName) {
synchronized (lock) {
if (destroyed) {
return null;
}
for (Iterator> iterator = getClassPath().entrySet().iterator(); iterator
.hasNext();) {
Map.Entry entry = iterator.next();
ResourceLocation resourceLocation = entry.getValue();
ResourceHandle resourceHandle = resourceLocation.getResourceHandle(resourceName);
if (resourceHandle != null) {
return resourceHandle.getUrl();
}
}
}
return null;
}
public Enumeration findResources(String resourceName) {
synchronized (lock) {
return new ResourceEnumeration(new ArrayList(getClassPath().values()), resourceName);
}
}
public void addUrl(URL url) {
addUrls(Collections.singletonList(url));
}
public URL[] getUrls() {
synchronized (lock) {
return urls.toArray(new URL[urls.size()]);
}
}
/**
* Adds an array of urls to the end of this class loader.
* @param urls the URLs to add
*/
protected void addUrls(URL[] urls) {
addUrls(Arrays.asList(urls));
}
/**
* Adds a list of urls to the end of this class loader.
* @param urls the URLs to add
*/
protected void addUrls(List urls) {
synchronized (lock) {
if (destroyed) {
throw new IllegalStateException("UrlResourceFinder has been destroyed");
}
boolean shouldRebuild = this.urls.addAll(urls);
if (shouldRebuild) {
rebuildClassPath();
}
}
}
private LinkedHashMap getClassPath() {
assert Thread.holdsLock(lock) : "This method can only be called while holding the lock";
for (Iterator iterator = watchedFiles.iterator(); iterator.hasNext();) {
File file = iterator.next();
if (file.canRead()) {
rebuildClassPath();
break;
}
}
return classPath;
}
/**
* Rebuilds the entire class path. This class is called when new URLs are added or one of the watched files becomes
* readable. This method will not open jar files again, but will add any new entries not alredy open to the class
* path. If any file based url is does not exist, we will watch for that file to appear.
*/
private void rebuildClassPath() {
assert Thread.holdsLock(lock) : "This method can only be called while holding the lock";
// copy all of the existing locations into a temp map and clear the class path
Map existingJarFiles = new LinkedHashMap(classPath);
classPath.clear();
LinkedList locationStack = new LinkedList(urls);
try {
while (!locationStack.isEmpty()) {
URL url = locationStack.removeFirst();
// Skip any duplicate urls in the claspath
if (classPath.containsKey(url)) {
continue;
}
// Check is this URL has already been opened
ResourceLocation resourceLocation = existingJarFiles.remove(url);
// If not opened, cache the url and wrap it with a resource location
if (resourceLocation == null) {
try {
File file = cacheUrl(url);
resourceLocation = createResourceLocation(url, file);
} catch (FileNotFoundException e) {
// if this is a file URL, the file doesn't exist yet... watch to see if it appears later
if ("file".equals(url.getProtocol())) {
File file = new File(url.getPath());
watchedFiles.add(file);
continue;
}
} catch (IOException ignored) {
// can't seem to open the file... this is most likely a bad jar file
// so don't keep a watch out for it because that would require lots of checking
// Dain: We may want to review this decision later
continue;
}
}
// add the jar to our class path
classPath.put(resourceLocation.getCodeSource(), resourceLocation);
// push the manifest classpath on the stack (make sure to maintain the order)
List manifestClassPath = getManifestClassPath(resourceLocation);
locationStack.addAll(0, manifestClassPath);
}
} catch (Error e) {
destroy();
throw e;
}
for (Iterator iterator = existingJarFiles.values().iterator(); iterator.hasNext();) {
ResourceLocation resourceLocation = iterator.next();
resourceLocation.close();
}
}
protected File cacheUrl(URL url) throws IOException {
if (!"file".equals(url.getProtocol())) {
// download the jar
throw new Error("Only local file jars are supported " + url);
}
File file;
try {
file = new File(url.toURI());
} catch (URISyntaxException e) {
file = new File(url.getPath());
}
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
}
if (!file.canRead()) {
throw new IOException("File is not readable: " + file.getAbsolutePath());
}
return file;
}
protected ResourceLocation createResourceLocation(URL codeSource, File cacheFile) throws IOException {
if (!cacheFile.exists()) {
throw new FileNotFoundException(cacheFile.getAbsolutePath());
}
if (!cacheFile.canRead()) {
throw new IOException("File is not readable: " + cacheFile.getAbsolutePath());
}
ResourceLocation resourceLocation = null;
if (cacheFile.isDirectory()) {
// DirectoryResourceLocation will only return "file" URLs within this directory
// do not user the DirectoryResourceLocation for non file based urls
resourceLocation = new DirectoryResourceLocation(cacheFile);
} else {
resourceLocation = new JarResourceLocation(codeSource, jarFileFactory.newJarFile(cacheFile));
}
return resourceLocation;
}
private List getManifestClassPath(ResourceLocation resourceLocation) {
try {
// get the manifest, if possible
Manifest manifest = resourceLocation.getManifest();
if (manifest == null) {
// some locations don't have a manifest
return Collections.emptyList();
}
// get the class-path attribute, if possible
String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
if (manifestClassPath == null) {
return Collections.emptyList();
}
// build the urls...
// the class-path attribute is space delimited
URL codeSource = resourceLocation.getCodeSource();
LinkedList classPathUrls = new LinkedList();
for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
String entry = tokenizer.nextToken();
try {
// the class path entry is relative to the resource location code source
URL entryUrl = new URL(codeSource, entry);
classPathUrls.addLast(entryUrl);
} catch (MalformedURLException ignored) {
// most likely a poorly named entry
}
}
return classPathUrls;
} catch (IOException ignored) {
// error opening the manifest
return Collections.emptyList();
}
}
/**
* Factory interface that is used by the {@link UrlResourceFinder} to create a new {@link JarFile} instance. Allows
* for various different {@link JarFile} implementations to be supported by the finder.
*/
public static interface JarFileFactory {
/**
* Factory method used to create a {@link JarFile} instance for the specified {@link File}.
* @param file The file to read
* @return A {@link JarFile} instance, this can also be a specialised subclass of {@link JarFile}.
* @throws IOException
*/
public JarFile newJarFile(File file) throws IOException;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy