Skip to content

Commit

Permalink
Support for Java 9 JRT FS (classes packaged into jimage files), see #105
Browse files Browse the repository at this point in the history


The support is not complete, currently packages which exist in multiple
modules aren't properly supported (java.awt is an example).
  • Loading branch information
iloveeclipse committed Jun 5, 2016
1 parent a9eb52b commit 9e10194
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ private static Set<String> createJavaClasspath(IJavaProject javaProject, Set<IPr
for (IClasspathEntry iClasspathEntry : classpathEntries) {
IPath path = iClasspathEntry.getPath();
// smallest possible fix for #1228 Eclipse plugin always uses host VM to resolve JDK classes
if (isValidPath(path) && "rt.jar".equals(path.lastSegment())) {
if (isValidPath(path) && ("rt.jar".equals(path.lastSegment())
|| "jrt-fs.jar".equals(path.lastSegment()))) {
classPath.add(path.toOSString());
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ static IScannableCodeBase createFilesystemCodeBase(FilesystemCodeBaseLocator cod
throw new IOException("File " + file.getAbsolutePath() + " is not a normal file");
} else if (fileName.endsWith(".class")) {
return new SingleFileCodeBase(codeBaseLocator, fileName);
} else if (fileName.endsWith(File.separator + "jrt-fs.jar")) {
return new JrtfsCodeBase(codeBaseLocator, fileName);
} else {
return ZipCodeBaseFactory.makeZipCodeBase(codeBaseLocator, file);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
Expand Down Expand Up @@ -390,9 +393,20 @@ private LinkedList<WorkListItem> buildSystemCodebaseList() {
}
}

if(isJava9orLater()){
Path jrtFsJar = Paths.get(System.getProperty("java.home", ""), "jrt-fs.jar");

This comment has been minimized.

Copy link
@iloveeclipse

iloveeclipse Jul 18, 2017

Author Member

jrt-fs.jar was moved to the /lib directory, see https://bugs.openjdk.java.net/browse/JDK-8169816

This comment has been minimized.

Copy link
@mlchung

mlchung Jul 24, 2017

Right. line 397 has to be updated per the new location for jrt-fs.jar.

Note that isJava9orLater() may throw NumberFormatException for example when running on JDK 8 since the jvm spec version is "1.8"

This comment has been minimized.

Copy link
@iloveeclipse

iloveeclipse Jul 25, 2017

Author Member

This version check code doesn't exist anymore in HEAD, it is fixed long time ago.

if(Files.isRegularFile(jrtFsJar)){
addWorkListItemsForClasspath(workList, jrtFsJar.toString());
}
}
return workList;
}

private static boolean isJava9orLater() {
String jvmSpec = System.getProperty("java.vm.specification.version", "8");
return Integer.parseInt(jvmSpec) >= 9;
}

/**
* Create a worklist that will add the FindBugs lib/annotations.jar to the
* classpath.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2003-2008 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

package edu.umd.cs.findbugs.classfile.impl;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Nonnull;

import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
import edu.umd.cs.findbugs.classfile.ICodeBaseIterator;
import edu.umd.cs.findbugs.classfile.ICodeBaseLocator;
import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
import edu.umd.cs.findbugs.classfile.ResourceNotFoundException;

/**
*
* Code base supporting Java 9 new jimage packed modules
*
* @author andrey
*/
public class JrtfsCodeBase extends AbstractScannableCodeBase {

private FileSystem fs;
private final String fileName;
private Path root;
private Map<String, String> packageToModuleMap;

public JrtfsCodeBase(ICodeBaseLocator codeBaseLocator, @Nonnull String fileName) {
super(codeBaseLocator);
this.fileName = fileName;
URL url;
try {
url = Paths.get(fileName).toUri().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] { url });
fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap(), loader);
root = fs.getPath("modules");
packageToModuleMap = createPackageToModuleMap(fs);
} catch (IOException e) {
e.printStackTrace();
}
}

public Map<String, String> createPackageToModuleMap(FileSystem fs) throws IOException{
HashMap<String, String> packageToModule = new LinkedHashMap<>();
Path path = fs.getPath("packages");
Files.list(path).forEach(p -> {
try {
Files.list(p).findFirst().ifPresent(c -> packageToModule.put(fileName(p).replace('.', '/') , fileName(c)));
} catch (IOException e) {
e.printStackTrace();
}
});
return packageToModule;
}

@Override
public ICodeBaseEntry lookupResource(String resourceName) {
resourceName = translateResourceName(resourceName);
String packageName = getPackage(resourceName);
String moduleName = packageToModuleMap.get(packageName);
if(moduleName == null){
return null;
}
Path resolved = root.resolve(moduleName + "/" + resourceName);
if(Files.exists(resolved)){
return new JrtfsCodebaseEntry(resolved, root, this);
}
return null;
}

private static String getPackage(String resourceName) {
int lastSlash = resourceName.lastIndexOf('/');
if(lastSlash > 0){
return resourceName.substring(0, lastSlash);
}
return resourceName;
}

@Override
public String getPathName() {
return fileName;
}

@Override
public int hashCode() {
return 31 + fileName.hashCode();
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof JrtfsCodeBase)) {
return false;
}
return fileName.equals(((JrtfsCodeBase) obj).fileName);
}


@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("JrtfsCodeBase [");
if (fileName != null) {
builder.append("file=");
builder.append(fileName);
}
builder.append("]");
return builder.toString();
}

@Override
public void close() {
if(fs != null){
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

@Override
public ICodeBaseIterator iterator() throws InterruptedException {
return new JrtfsCodeBaseIterator();
}

static String fileName(Path p){
Path name = p.getFileName();
return name != null? name.toString() : "";
}

static boolean isClassFile(Path p) {
return p.endsWith(".class") && !p.endsWith("module-info.class") && Files.isRegularFile(p);
}

public class JrtfsCodeBaseIterator implements ICodeBaseIterator {

private Iterator<Path> iterator;

public JrtfsCodeBaseIterator() {
try {

iterator = Files.walk(root).filter(p -> isClassFile(p)).iterator();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public boolean hasNext() throws InterruptedException {
return iterator.hasNext();
}

@Override
public ICodeBaseEntry next() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
Path next = iterator.next();
return new JrtfsCodebaseEntry(next, root, JrtfsCodeBase.this);
}

}

public static class JrtfsCodebaseEntry extends AbstractScannableCodeBaseEntry {

private final Path path;
private final Path root;
private final JrtfsCodeBase codebase;

public JrtfsCodebaseEntry(Path next, Path root, JrtfsCodeBase codebase) {
this.path = next;
this.root = root;
this.codebase = codebase;
}

@Override
public int getNumBytes() {
try {
return (int) Files.size(path);
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}

@Override
public InputStream openResource() throws IOException {
return new BufferedInputStream(Files.newInputStream(path, StandardOpenOption.READ));
}

@Override
public JrtfsCodeBase getCodeBase() {
return codebase;
}

@Override
public ClassDescriptor getClassDescriptor() throws ResourceNotFoundException, InvalidClassFileFormatException {
return DescriptorFactory.createClassDescriptorFromResourceName(getResourceName());
}

@Override
public String getRealResourceName() {
return root.relativize(path).toString();
}

@Override
public int hashCode() {
final int prime = 31;
int result = prime + codebase.hashCode();
result = prime * result + path.hashCode();
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof JrtfsCodebaseEntry)) {
return false;
}
JrtfsCodebaseEntry other = (JrtfsCodebaseEntry) obj;
if(!Objects.equals(codebase, other.codebase)){
return false;
}
return Objects.equals(path, other.path);
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("JrtfsCodebaseEntry [");
if (path != null) {
builder.append("path=");
builder.append(path);
builder.append(", ");
}
if (codebase != null) {
builder.append("codebase=");
builder.append(codebase);
}
builder.append("]");
return builder.toString();
}

}


}

0 comments on commit 9e10194

Please sign in to comment.