org.bimserver.plugins.classloaders.FileJarClassLoader Maven / Gradle / Ivy
package org.bimserver.plugins.classloaders;
/******************************************************************************
* Copyright (C) 2009-2018 BIMserver.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see {@literal }.
*****************************************************************************/
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.bimserver.plugins.PluginManager;
import org.bimserver.utils.PathUtils;
import org.bimserver.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FileJarClassLoader extends JarClassLoader implements Closeable {
private static final Logger LOGGER = LoggerFactory.getLogger(FileJarClassLoader.class);
private final Map> loadedClasses = new HashMap>();
private final Map jarContent = new HashMap<>();
private final Path jarFile;
private FileSystem fileSystem;
private boolean embeddedJarFilesLoaded = false;
public FileJarClassLoader(PluginManager pluginManager, ClassLoader parentClassLoader, Path jarFile) throws FileNotFoundException, IOException {
super(parentClassLoader);
this.jarFile = jarFile;
URI uri = jarFile.toUri();
try {
URI x = new URI("jar:" + uri.toString());
fileSystem = pluginManager.getOrCreateFileSystem(x);
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
}
private void loadEmbeddedJarFileSystems() {
if (!embeddedJarFilesLoaded) {
loadEmbeddedJarFileSystems(fileSystem.getPath("/"));
embeddedJarFilesLoaded = true;
}
}
private void loadEmbeddedJarFileSystems(Path path) {
try {
if (Files.isDirectory(path)) {
for (Path subPath : PathUtils.list(path)) {
loadEmbeddedJarFileSystems(subPath);
}
} else {
// This is annoying, but we are caching the contents of JAR files within JAR files in memory, could not get the JarFileSystem to work with jar:jar:file URI's
// Also there is a problem with not being able to change position within a file, at least in the JarFileSystem
// It looks like there are 2 other solutions to this problem:
// - Copy the embedded JAR files to a tmp directory, and load from there with a JarFileSystem wrapper (at some stage we were doing this for all JAR contents,
// resulted in 50.000 files, which was annoying, but a few JAR files probably won't hurt
// - Don't allow plugins to have embedded JAR's, could force them to extract all dependencies...
//
if (path.getFileName().toString().toLowerCase().endsWith(".jar")) {
JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(path));
try {
JarEntry jarEntry = jarInputStream.getNextJarEntry();
while (jarEntry != null) {
jarContent.put(jarEntry.getName(), IOUtils.toByteArray(jarInputStream));
jarEntry = jarInputStream.getNextJarEntry();
}
} finally {
jarInputStream.close();
}
}
}
} catch (IOException e) {
LOGGER.error("", e);
}
}
@Override
public URL findResource(String name) {
try {
final Lazy lazyInputStream = findPath(name);
if (lazyInputStream != null) {
try {
URL baseUrl = new URL("file:" + name);
URL url = new URL(baseUrl, name, new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
return lazyInputStream.get();
}
};
}
});
return url;
} catch (MalformedURLException e) {
LOGGER.error("", e);
}
} else {
LOGGER.debug("File not found: " + name + " (in " + jarFile.getFileName().toString() + ")");
}
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
private Lazy findPath(final String name) throws IOException {
loadEmbeddedJarFileSystems();
final Path file = this.fileSystem.getPath(name);
if (Files.exists(file)) {
return new Lazy(){
@Override
public InputStream get() {
try {
return Files.newInputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}};
}
if (jarContent.containsKey(name)) {
return new Lazy(){
@Override
public InputStream get() {
return new ByteArrayInputStream(jarContent.get(name));
}};
}
return null;
}
@Override
public Class> findClass(String name) throws ClassNotFoundException {
String fileName = name.replace(".", "/") + ".class";
if (loadedClasses.containsKey(fileName)) {
return loadedClasses.get(fileName);
}
try {
Lazy lazyInputStream = findPath(fileName);
if (lazyInputStream == null) {
throw new ClassNotFoundException(name);
}
InputStream inputStream = lazyInputStream.get();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
IOUtils.copy(inputStream, byteArrayOutputStream);
} finally {
inputStream.close();
}
Class> defineClass = defineClass(name, byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.toByteArray().length);
loadedClasses.put(fileName, defineClass);
/*
* This is a fix to actually load the package-info.class file with
* the annotations about for example namespaces required for JAXB to
* work. Found this code here:
* https://issues.jboss.org/browse/JBPM-1404
*/
if (defineClass != null) {
final int packageIndex = name.lastIndexOf('.');
if (packageIndex != -1) {
final String packageName = name.substring(0, packageIndex);
final Package classPackage = getPackage(packageName);
if (classPackage == null) {
definePackage(packageName, null, null, null, null, null, null, null);
}
}
}
return defineClass;
} catch (FileNotFoundException e) {
throw new ClassNotFoundException(name);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
@Override
public void dumpStructure(int indent) {
System.out.print(StringUtils.gen(" ", indent));
System.out.println("FileJarClassLoader " + jarFile.getFileName().toString());
}
public void close() throws IOException {
fileSystem.close();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy