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 
  33 package sun.rmi.rmic;
  34 
  35 import java.io.File;
  36 import java.io.IOException;
  37 import java.io.OutputStream;
  38 import java.util.Collection;
  39 import java.util.Enumeration;
  40 import java.util.Iterator;
  41 import java.util.LinkedHashSet;
  42 import java.util.StringTokenizer;
  43 import java.util.Vector;
  44 import java.util.jar.JarFile;
  45 import java.util.jar.Manifest;
  46 import java.util.jar.Attributes;
  47 import 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")
  65 public 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                        arcname.endsWith(".jimage"))) {
 387                     /* File name don't have right extension */
 388 //                      if (warn)
 389 //                          log.warning(Position.NOPOS,
 390 //                              "invalid.archive.file", file);
 391                     return;
 392                 }
 393             }
 394 
 395             /* Now what we have left is either a directory or a file name
 396                confirming to archive naming convention */
 397 
 398             super.add(file);
 399             if (expandJarClassPaths && isZip(file))
 400                 addJarClassPath(file, warn);
 401         }
 402 
 403         // Adds referenced classpath elements from a jar's Class-Path
 404         // Manifest entry.  In some future release, we may want to
 405         // update this code to recognize URLs rather than simple
 406         // filenames, but if we do, we should redo all path-related code.
 407         private void addJarClassPath(String jarFileName, boolean warn) {
 408             try {
 409                 String jarParent = new File(jarFileName).getParent();
 410                 JarFile jar = new JarFile(jarFileName);
 411 
 412                 try {
 413                     Manifest man = jar.getManifest();
 414                     if (man == null) return;
 415 
 416                     Attributes attr = man.getMainAttributes();
 417                     if (attr == null) return;
 418 
 419                     String path = attr.getValue(Attributes.Name.CLASS_PATH);
 420                     if (path == null) return;
 421 
 422                     for (StringTokenizer st = new StringTokenizer(path);
 423                         st.hasMoreTokens();) {
 424                         String elt = st.nextToken();
 425                         if (jarParent != null)
 426                             elt = new File(jarParent, elt).getCanonicalPath();
 427                         addFile(elt, warn);
 428                     }
 429                 } finally {
 430                     jar.close();
 431                 }
 432             } catch (IOException e) {
 433 //              log.error(Position.NOPOS,
 434 //                        "error.reading.file", jarFileName,
 435 //                        e.getLocalizedMessage());
 436             }
 437         }
 438     }
 439 }