39 import java.nio.file.DirectoryStream; |
39 import java.nio.file.DirectoryStream; |
40 import java.nio.file.Files; |
40 import java.nio.file.Files; |
41 import java.nio.file.FileSystem; |
41 import java.nio.file.FileSystem; |
42 import java.nio.file.FileSystems; |
42 import java.nio.file.FileSystems; |
43 import java.nio.file.Path; |
43 import java.nio.file.Path; |
|
44 import java.nio.file.ProviderNotFoundException; |
44 import java.nio.file.spi.FileSystemProvider; |
45 import java.nio.file.spi.FileSystemProvider; |
45 |
46 |
46 /** |
47 /** |
47 * This class is used to represent a class path, which can contain both |
48 * This class is used to represent a class path, which can contain both |
48 * directories and zip files. |
49 * directories and zip files. |
51 * supported API. Code that depends on them does so at its own risk: |
52 * supported API. Code that depends on them does so at its own risk: |
52 * they are subject to change or removal without notice. |
53 * they are subject to change or removal without notice. |
53 */ |
54 */ |
54 public |
55 public |
55 class ClassPath { |
56 class ClassPath { |
56 private static final String JIMAGE_EXT = ".jimage"; |
|
57 private FileSystem getJrtFileSystem() { |
57 private FileSystem getJrtFileSystem() { |
58 return FileSystems.getFileSystem(URI.create("jrt:/")); |
58 return FileSystems.getFileSystem(URI.create("jrt:/")); |
59 } |
59 } |
60 |
60 |
61 static final char dirSeparator = File.pathSeparatorChar; |
61 static final char dirSeparator = File.pathSeparatorChar; |
96 * Build a default class path from the path strings specified by |
96 * Build a default class path from the path strings specified by |
97 * the properties sun.boot.class.path and env.class.path, in that |
97 * the properties sun.boot.class.path and env.class.path, in that |
98 * order. |
98 * order. |
99 */ |
99 */ |
100 public ClassPath() { |
100 public ClassPath() { |
|
101 // though this property is removed. Check for null and use only |
|
102 // if it is not null (when bootstrap JDK is used). |
101 String syscp = System.getProperty("sun.boot.class.path"); |
103 String syscp = System.getProperty("sun.boot.class.path"); |
102 String envcp = System.getProperty("env.class.path"); |
104 String envcp = System.getProperty("env.class.path"); |
103 if (envcp == null) envcp = "."; |
105 if (envcp == null) envcp = "."; |
104 String cp = syscp + File.pathSeparator + envcp; |
106 |
|
107 // add syscp only if not null! |
|
108 String cp = syscp == null? envcp : (syscp + File.pathSeparator + envcp); |
105 init(cp); |
109 init(cp); |
106 } |
110 } |
107 |
111 |
108 private void init(String pathstr) { |
112 private void init(String pathstr) { |
109 int i, j, n; |
113 int i, j, n; |
119 while ((i = pathstr.indexOf(dirSeparator, i)) != -1) { |
123 while ((i = pathstr.indexOf(dirSeparator, i)) != -1) { |
120 n++; i++; |
124 n++; i++; |
121 } |
125 } |
122 // Build the class path |
126 // Build the class path |
123 ClassPathEntry[] path = new ClassPathEntry[n+1]; |
127 ClassPathEntry[] path = new ClassPathEntry[n+1]; |
|
128 |
124 int len = pathstr.length(); |
129 int len = pathstr.length(); |
125 boolean jrtAdded = false; |
|
126 for (i = n = 0; i < len; i = j + 1) { |
130 for (i = n = 0; i < len; i = j + 1) { |
127 if ((j = pathstr.indexOf(dirSeparator, i)) == -1) { |
131 if ((j = pathstr.indexOf(dirSeparator, i)) == -1) { |
128 j = len; |
132 j = len; |
129 } |
133 } |
130 if (i == j) { |
134 if (i == j) { |
131 path[n++] = new DirClassPathEntry(new File(".")); |
135 path[n++] = new DirClassPathEntry(new File(".")); |
132 } else { |
136 } else { |
133 String filename = pathstr.substring(i, j); |
137 String filename = pathstr.substring(i, j); |
134 File file = new File(filename); |
138 File file = new File(filename); |
135 if (file.isFile()) { |
139 if (file.isFile()) { |
136 if (filename.endsWith(JIMAGE_EXT)) { |
140 try { |
137 if (jrtAdded) continue; |
141 ZipFile zip = new ZipFile(file); |
138 FileSystem fs = getJrtFileSystem(); |
142 path[n++] = new ZipClassPathEntry(zip); |
139 path[n++] = new JrtClassPathEntry(fs); |
143 } catch (ZipException e) { |
140 jrtAdded = true; |
144 } catch (IOException e) { |
141 } else { |
145 // Ignore exceptions, at least for now... |
142 try { |
|
143 ZipFile zip = new ZipFile(file); |
|
144 path[n++] = new ZipClassPathEntry(zip); |
|
145 } catch (ZipException e) { |
|
146 } catch (IOException e) { |
|
147 // Ignore exceptions, at least for now... |
|
148 } |
|
149 } |
146 } |
150 } else { |
147 } else { |
151 path[n++] = new DirClassPathEntry(file); |
148 path[n++] = new DirClassPathEntry(file); |
152 } |
149 } |
153 } |
150 } |
154 } |
151 } |
|
152 |
|
153 // add jrt file system at the end |
|
154 try { |
|
155 FileSystem fs = getJrtFileSystem(); |
|
156 path[n++] = new JrtClassPathEntry(fs); |
|
157 } catch (ProviderNotFoundException ignored) { |
|
158 // this could happen during jdk build with earlier JDK as bootstrap |
|
159 } |
|
160 |
155 // Trim class path to exact size |
161 // Trim class path to exact size |
156 this.path = new ClassPathEntry[n]; |
162 this.path = new ClassPathEntry[n]; |
157 System.arraycopy((Object)path, 0, (Object)this.path, 0, n); |
163 System.arraycopy((Object)path, 0, (Object)this.path, 0, n); |
158 } |
164 } |
159 |
165 |
169 } |
175 } |
170 this.pathstr = sb.toString(); |
176 this.pathstr = sb.toString(); |
171 } |
177 } |
172 |
178 |
173 // Build the class path |
179 // Build the class path |
174 ClassPathEntry[] path = new ClassPathEntry[patharray.length]; |
180 ClassPathEntry[] path = new ClassPathEntry[patharray.length + 1]; |
175 int n = 0; |
181 int n = 0; |
176 boolean jrtAdded = false; |
|
177 for (String name : patharray) { |
182 for (String name : patharray) { |
178 File file = new File(name); |
183 File file = new File(name); |
179 if (file.isFile()) { |
184 if (file.isFile()) { |
180 if (name.endsWith(JIMAGE_EXT)) { |
185 try { |
181 if (jrtAdded) continue; |
186 ZipFile zip = new ZipFile(file); |
182 FileSystem fs = getJrtFileSystem(); |
187 path[n++] = new ZipClassPathEntry(zip); |
183 path[n++] = new JrtClassPathEntry(fs); |
188 } catch (ZipException e) { |
184 jrtAdded = true; |
189 } catch (IOException e) { |
185 } else { |
190 // Ignore exceptions, at least for now... |
186 try { |
191 } |
187 ZipFile zip = new ZipFile(file); |
|
188 path[n++] = new ZipClassPathEntry(zip); |
|
189 } catch (ZipException e) { |
|
190 } catch (IOException e) { |
|
191 // Ignore exceptions, at least for now... |
|
192 } |
|
193 } |
|
194 } else { |
192 } else { |
195 path[n++] = new DirClassPathEntry(file); |
193 path[n++] = new DirClassPathEntry(file); |
196 } |
194 } |
197 } |
195 } |
|
196 |
|
197 // add jrt file system at the end |
|
198 try { |
|
199 FileSystem fs = getJrtFileSystem(); |
|
200 path[n++] = new JrtClassPathEntry(fs); |
|
201 } catch (ProviderNotFoundException ignored) { |
|
202 // this could happen with earlier version of JDK used as bootstrap |
|
203 } |
|
204 |
198 // Trim class path to exact size |
205 // Trim class path to exact size |
199 this.path = new ClassPathEntry[n]; |
206 this.path = new ClassPathEntry[n]; |
200 System.arraycopy((Object)path, 0, (Object)this.path, 0, n); |
207 System.arraycopy((Object)path, 0, (Object)this.path, 0, n); |
201 } |
208 } |
202 |
209 |
381 } |
388 } |
382 |
389 |
383 // a ClassPathEntry that represents jrt file system |
390 // a ClassPathEntry that represents jrt file system |
384 final class JrtClassPathEntry extends ClassPathEntry { |
391 final class JrtClassPathEntry extends ClassPathEntry { |
385 private final FileSystem fs; |
392 private final FileSystem fs; |
386 // module directory paths in jrt fs |
|
387 private final Set<Path> jrtModules; |
|
388 // package name to package directory path mapping (lazily filled) |
393 // package name to package directory path mapping (lazily filled) |
389 private final Map<String, Path> pkgDirs; |
394 private final Map<String, Path> pkgDirs; |
390 |
395 |
391 JrtClassPathEntry(FileSystem fs) { |
396 JrtClassPathEntry(FileSystem fs) { |
392 this.fs = fs; |
397 this.fs = fs; |
393 this.jrtModules = new LinkedHashSet<>(); |
|
394 this.pkgDirs = new HashMap<>(); |
398 this.pkgDirs = new HashMap<>(); |
395 |
|
396 // fill in module directories at the root dir |
|
397 Path root = fs.getPath("/modules"); |
|
398 try { |
|
399 try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) { |
|
400 for (Path entry: stream) { |
|
401 if (Files.isDirectory(entry)) |
|
402 jrtModules.add(entry); |
|
403 } |
|
404 } |
|
405 } catch (IOException ioExp) { |
|
406 throw new UncheckedIOException(ioExp); |
|
407 } |
|
408 } |
399 } |
409 |
400 |
410 void close() throws IOException { |
401 void close() throws IOException { |
411 } |
402 } |
412 |
403 |
415 // check the cache first |
406 // check the cache first |
416 if (pkgDirs.containsKey(pkgName)) { |
407 if (pkgDirs.containsKey(pkgName)) { |
417 return pkgDirs.get(pkgName); |
408 return pkgDirs.get(pkgName); |
418 } |
409 } |
419 |
410 |
420 for (Path modPath : jrtModules) { |
411 Path pkgLink = fs.getPath("/packages/" + pkgName.replace('/', '.')); |
421 Path pkgDir = fs.getPath(modPath.toString(), pkgName); |
412 // check if /packages/$PACKAGE directory exists |
422 // check if package directory is under any of the known modules |
413 if (Files.isDirectory(pkgLink)) { |
423 if (Files.exists(pkgDir)) { |
414 try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgLink)) { |
424 // it is a package directory only if contains atleast one .class file |
415 for (Path p : stream) { |
425 try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgDir)) { |
416 // find first symbolic link to module directory |
426 for (Path p : stream) { |
417 if (Files.isSymbolicLink(p)) { |
427 if (Files.isRegularFile(p) && p.toString().endsWith(".class")) { |
418 Path modDir = Files.readSymbolicLink(p); |
428 // cache package-to-package dir mapping for future |
419 if (Files.isDirectory(modDir)) { |
429 pkgDirs.put(pkgName, pkgDir); |
420 // get package subdirectory under /modules/$MODULE/ |
430 return pkgDir; |
421 Path pkgDir = fs.getPath(modDir.toString() + "/" + pkgName); |
|
422 if (Files.isDirectory(pkgDir)) { |
|
423 // it is a package directory only if contains |
|
424 // at least one .class file |
|
425 try (DirectoryStream<Path> pstream = |
|
426 Files.newDirectoryStream(pkgDir)) { |
|
427 for (Path f : pstream) { |
|
428 if (Files.isRegularFile(f) |
|
429 && f.toString().endsWith(".class")) { |
|
430 pkgDirs.put(pkgName, pkgDir); |
|
431 return pkgDir; |
|
432 } |
|
433 } |
|
434 } |
|
435 } |
431 } |
436 } |
432 } |
437 } |
433 } |
438 } |
434 } |
439 } |
435 } |
440 } |