1/* 2 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26/*****************************************************************************/ 27/* Copyright (c) IBM Corporation 1998 */ 28/* */ 29/* (C) Copyright IBM Corp. 1998 */ 30/* */ 31/*****************************************************************************/ 32 33package sun.rmi.rmic; 34 35import java.io.File; 36import java.io.IOException; 37import java.io.OutputStream; 38import java.util.Collection; 39import java.util.Enumeration; 40import java.util.Iterator; 41import java.util.LinkedHashSet; 42import java.util.StringTokenizer; 43import java.util.Vector; 44import java.util.jar.JarFile; 45import java.util.jar.Manifest; 46import java.util.jar.Attributes; 47import sun.tools.java.ClassPath; 48 49/** 50 * BatchEnvironment for rmic extends javac's version in four ways: 51 * 1. It overrides errorString() to handle looking for rmic-specific 52 * error messages in rmic's resource bundle 53 * 2. It provides a mechanism for recording intermediate generated 54 * files so that they can be deleted later. 55 * 3. It holds a reference to the Main instance so that generators 56 * can refer to it. 57 * 4. It provides access to the ClassPath passed to the constructor. 58 * 59 * WARNING: The contents of this source file are not part of any 60 * supported API. Code that depends on them does so at its own risk: 61 * they are subject to change or removal without notice. 62 */ 63 64@SuppressWarnings("deprecation") 65public class BatchEnvironment extends sun.tools.javac.BatchEnvironment { 66 67 /** instance of Main which created this environment */ 68 private Main main; 69 70 /** 71 * Create a ClassPath object for rmic from a class path string. 72 */ 73 public static ClassPath createClassPath(String classPathString) { 74 ClassPath[] paths = classPaths(null, classPathString, null); 75 return paths[1]; 76 } 77 78 /** 79 * Create a ClassPath object for rmic from the relevant command line 80 * options for class path and boot class path. 81 */ 82 public static ClassPath createClassPath(String classPathString, 83 String sysClassPathString) 84 { 85 /** 86 * Previously, this method delegated to the 87 * sun.tools.javac.BatchEnvironment.classPaths method in order 88 * to supply default values for paths not specified on the 89 * command line, expand extensions directories into specific 90 * JAR files, and construct the ClassPath object-- but as part 91 * of the fix for 6473331, which adds support for Class-Path 92 * manifest entries in JAR files, those steps are now handled 93 * here directly, with the help of a Path utility class copied 94 * from the new javac implementation (see below). 95 */ 96 Path path = new Path(); 97 98 if (sysClassPathString == null) { 99 sysClassPathString = System.getProperty("sun.boot.class.path"); 100 } 101 if (sysClassPathString != null) { 102 path.addFiles(sysClassPathString); 103 } 104 105 /* 106 * Class-Path manifest entries are supported for JAR files 107 * everywhere except in the boot class path. 108 */ 109 path.expandJarClassPaths(true); 110 111 /* 112 * In the application class path, an empty element means 113 * the current working directory. 114 */ 115 path.emptyPathDefault("."); 116 117 if (classPathString == null) { 118 // The env.class.path property is the user's CLASSPATH 119 // environment variable, and it set by the wrapper (ie, 120 // javac.exe). 121 classPathString = System.getProperty("env.class.path"); 122 if (classPathString == null) { 123 classPathString = "."; 124 } 125 } 126 path.addFiles(classPathString); 127 128 return new ClassPath(path.toArray(new String[path.size()])); 129 } 130 131 /** 132 * Create a BatchEnvironment for rmic with the given class path, 133 * stream for messages and Main. 134 */ 135 public BatchEnvironment(OutputStream out, ClassPath path, Main main) { 136 super(out, new ClassPath(""), path); 137 // use empty "sourcePath" (see 4666958) 138 this.main = main; 139 } 140 141 /** 142 * Get the instance of Main which created this environment. 143 */ 144 public Main getMain() { 145 return main; 146 } 147 148 /** 149 * Get the ClassPath. 150 */ 151 public ClassPath getClassPath() { 152 return binaryPath; 153 } 154 155 /** list of generated source files created in this environment */ 156 private Vector<File> generatedFiles = new Vector<>(); 157 158 /** 159 * Remember a generated source file generated so that it 160 * can be removed later, if appropriate. 161 */ 162 public void addGeneratedFile(File file) { 163 generatedFiles.addElement(file); 164 } 165 166 /** 167 * Delete all the generated source files made during the execution 168 * of this environment (those that have been registered with the 169 * "addGeneratedFile" method). 170 */ 171 public void deleteGeneratedFiles() { 172 synchronized(generatedFiles) { 173 Enumeration<File> enumeration = generatedFiles.elements(); 174 while (enumeration.hasMoreElements()) { 175 File file = enumeration.nextElement(); 176 file.delete(); 177 } 178 generatedFiles.removeAllElements(); 179 } 180 } 181 182 /** 183 * Release resources, if any. 184 */ 185 public void shutdown() { 186 main = null; 187 generatedFiles = null; 188 super.shutdown(); 189 } 190 191 /** 192 * Return the formatted, localized string for a named error message 193 * and supplied arguments. For rmic error messages, with names that 194 * being with "rmic.", look up the error message in rmic's resource 195 * bundle; otherwise, defer to java's superclass method. 196 */ 197 public String errorString(String err, 198 Object arg0, Object arg1, Object arg2) 199 { 200 if (err.startsWith("rmic.") || err.startsWith("warn.rmic.")) { 201 String result = Main.getText(err, 202 (arg0 != null ? arg0.toString() : null), 203 (arg1 != null ? arg1.toString() : null), 204 (arg2 != null ? arg2.toString() : null)); 205 206 if (err.startsWith("warn.")) { 207 result = "warning: " + result; 208 } 209 return result; 210 } else { 211 return super.errorString(err, arg0, arg1, arg2); 212 } 213 } 214 public void reset() { 215 } 216 217 /** 218 * Utility for building paths of directories and JAR files. This 219 * class was copied from com.sun.tools.javac.util.Paths as part of 220 * the fix for 6473331, which adds support for Class-Path manifest 221 * entries in JAR files. Diagnostic code is simply commented out 222 * because rmic silently ignored these conditions historically. 223 */ 224 private static class Path extends LinkedHashSet<String> { 225 private static final long serialVersionUID = 0; 226 private static final boolean warn = false; 227 228 private static class PathIterator implements Collection<String> { 229 private int pos = 0; 230 private final String path; 231 private final String emptyPathDefault; 232 233 public PathIterator(String path, String emptyPathDefault) { 234 this.path = path; 235 this.emptyPathDefault = emptyPathDefault; 236 } 237 public PathIterator(String path) { this(path, null); } 238 public Iterator<String> iterator() { 239 return new Iterator<String>() { 240 public boolean hasNext() { 241 return pos <= path.length(); 242 } 243 public String next() { 244 int beg = pos; 245 int end = path.indexOf(File.pathSeparator, beg); 246 if (end == -1) 247 end = path.length(); 248 pos = end + 1; 249 250 if (beg == end && emptyPathDefault != null) 251 return emptyPathDefault; 252 else 253 return path.substring(beg, end); 254 } 255 public void remove() { 256 throw new UnsupportedOperationException(); 257 } 258 }; 259 } 260 261 // required for Collection. 262 public int size() { 263 throw new UnsupportedOperationException(); 264 } 265 public boolean isEmpty() { 266 throw new UnsupportedOperationException(); 267 } 268 public boolean contains(Object o) { 269 throw new UnsupportedOperationException(); 270 } 271 public Object[] toArray() { 272 throw new UnsupportedOperationException(); 273 } 274 public <T> T[] toArray(T[] a) { 275 throw new UnsupportedOperationException(); 276 } 277 public boolean add(String o) { 278 throw new UnsupportedOperationException(); 279 } 280 public boolean remove(Object o) { 281 throw new UnsupportedOperationException(); 282 } 283 public boolean containsAll(Collection<?> c) { 284 throw new UnsupportedOperationException(); 285 } 286 public boolean addAll(Collection<? extends String> c) { 287 throw new UnsupportedOperationException(); 288 } 289 public boolean removeAll(Collection<?> c) { 290 throw new UnsupportedOperationException(); 291 } 292 public boolean retainAll(Collection<?> c) { 293 throw new UnsupportedOperationException(); 294 } 295 public void clear() { 296 throw new UnsupportedOperationException(); 297 } 298 public boolean equals(Object o) { 299 throw new UnsupportedOperationException(); 300 } 301 public int hashCode() { 302 throw new UnsupportedOperationException(); 303 } 304 } 305 306 /** Is this the name of a zip file? */ 307 private static boolean isZip(String name) { 308 return new File(name).isFile(); 309 } 310 311 private boolean expandJarClassPaths = false; 312 313 public Path expandJarClassPaths(boolean x) { 314 expandJarClassPaths = x; 315 return this; 316 } 317 318 /** What to use when path element is the empty string */ 319 private String emptyPathDefault = null; 320 321 public Path emptyPathDefault(String x) { 322 emptyPathDefault = x; 323 return this; 324 } 325 326 public Path() { super(); } 327 328 public Path addDirectories(String dirs, boolean warn) { 329 if (dirs != null) 330 for (String dir : new PathIterator(dirs)) 331 addDirectory(dir, warn); 332 return this; 333 } 334 335 public Path addDirectories(String dirs) { 336 return addDirectories(dirs, warn); 337 } 338 339 private void addDirectory(String dir, boolean warn) { 340 if (! new File(dir).isDirectory()) { 341// if (warn) 342// log.warning(Position.NOPOS, 343// "dir.path.element.not.found", dir); 344 return; 345 } 346 347 for (String direntry : new File(dir).list()) { 348 String canonicalized = direntry.toLowerCase(); 349 if (canonicalized.endsWith(".jar") || 350 canonicalized.endsWith(".zip")) 351 addFile(dir + File.separator + direntry, warn); 352 } 353 } 354 355 public Path addFiles(String files, boolean warn) { 356 if (files != null) 357 for (String file : new PathIterator(files, emptyPathDefault)) 358 addFile(file, warn); 359 return this; 360 } 361 362 public Path addFiles(String files) { 363 return addFiles(files, warn); 364 } 365 366 private void addFile(String file, boolean warn) { 367 if (contains(file)) { 368 /* Discard duplicates and avoid infinite recursion */ 369 return; 370 } 371 372 File ele = new File(file); 373 if (! ele.exists()) { 374 /* No such file or directory exist */ 375 if (warn) 376// log.warning(Position.NOPOS, 377// "path.element.not.found", file); 378 return; 379 } 380 381 if (ele.isFile()) { 382 /* File is an ordinay file */ 383 String arcname = file.toLowerCase(); 384 if (! (arcname.endsWith(".zip") || 385 arcname.endsWith(".jar"))) { 386 /* File name don't have right extension */ 387// if (warn) 388// log.warning(Position.NOPOS, 389// "invalid.archive.file", file); 390 return; 391 } 392 } 393 394 /* Now what we have left is either a directory or a file name 395 confirming to archive naming convention */ 396 397 super.add(file); 398 if (expandJarClassPaths && isZip(file)) 399 addJarClassPath(file, warn); 400 } 401 402 // Adds referenced classpath elements from a jar's Class-Path 403 // Manifest entry. In some future release, we may want to 404 // update this code to recognize URLs rather than simple 405 // filenames, but if we do, we should redo all path-related code. 406 private void addJarClassPath(String jarFileName, boolean warn) { 407 try { 408 String jarParent = new File(jarFileName).getParent(); 409 JarFile jar = new JarFile(jarFileName); 410 411 try { 412 Manifest man = jar.getManifest(); 413 if (man == null) return; 414 415 Attributes attr = man.getMainAttributes(); 416 if (attr == null) return; 417 418 String path = attr.getValue(Attributes.Name.CLASS_PATH); 419 if (path == null) return; 420 421 for (StringTokenizer st = new StringTokenizer(path); 422 st.hasMoreTokens();) { 423 String elt = st.nextToken(); 424 if (jarParent != null) 425 elt = new File(jarParent, elt).getCanonicalPath(); 426 addFile(elt, warn); 427 } 428 } finally { 429 jar.close(); 430 } 431 } catch (IOException e) { 432// log.error(Position.NOPOS, 433// "error.reading.file", jarFileName, 434// e.getLocalizedMessage()); 435 } 436 } 437 } 438} 439