View Javadoc
1   /*
2    * Copyright (c) 1996, 2012, 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  public class BatchEnvironment extends sun.tools.javac.BatchEnvironment {
65  
66      /** instance of Main which created this environment */
67      private Main main;
68  
69      /**
70       * Create a ClassPath object for rmic from a class path string.
71       */
72      public static ClassPath createClassPath(String classPathString) {
73          ClassPath[] paths = classPaths(null, classPathString, null, null);
74          return paths[1];
75      }
76  
77      /**
78       * Create a ClassPath object for rmic from the relevant command line
79       * options for class path, boot class path, and extension directories.
80       */
81      public static ClassPath createClassPath(String classPathString,
82                                              String sysClassPathString,
83                                              String extDirsString)
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         if (extDirsString == null) {
112             extDirsString = System.getProperty("java.ext.dirs");
113         }
114         if (extDirsString != null) {
115             path.addDirectories(extDirsString);
116         }
117 
118         /*
119          * In the application class path, an empty element means
120          * the current working directory.
121          */
122         path.emptyPathDefault(".");
123 
124         if (classPathString == null) {
125             // The env.class.path property is the user's CLASSPATH
126             // environment variable, and it set by the wrapper (ie,
127             // javac.exe).
128             classPathString = System.getProperty("env.class.path");
129             if (classPathString == null) {
130                 classPathString = ".";
131             }
132         }
133         path.addFiles(classPathString);
134 
135         return new ClassPath(path.toArray(new String[path.size()]));
136     }
137 
138     /**
139      * Create a BatchEnvironment for rmic with the given class path,
140      * stream for messages and Main.
141      */
142     public BatchEnvironment(OutputStream out, ClassPath path, Main main) {
143         super(out, new ClassPath(""), path);
144                                 // use empty "sourcePath" (see 4666958)
145         this.main = main;
146     }
147 
148     /**
149      * Get the instance of Main which created this environment.
150      */
151     public Main getMain() {
152         return main;
153     }
154 
155     /**
156      * Get the ClassPath.
157      */
158     public ClassPath getClassPath() {
159         return binaryPath;
160     }
161 
162     /** list of generated source files created in this environment */
163     private Vector<File> generatedFiles = new Vector<>();
164 
165     /**
166      * Remember a generated source file generated so that it
167      * can be removed later, if appropriate.
168      */
169     public void addGeneratedFile(File file) {
170         generatedFiles.addElement(file);
171     }
172 
173     /**
174      * Delete all the generated source files made during the execution
175      * of this environment (those that have been registered with the
176      * "addGeneratedFile" method).
177      */
178     public void deleteGeneratedFiles() {
179         synchronized(generatedFiles) {
180             Enumeration<File> enumeration = generatedFiles.elements();
181             while (enumeration.hasMoreElements()) {
182                 File file = enumeration.nextElement();
183                 file.delete();
184             }
185             generatedFiles.removeAllElements();
186         }
187     }
188 
189     /**
190      * Release resources, if any.
191      */
192     public void shutdown() {
193         main = null;
194         generatedFiles = null;
195         super.shutdown();
196     }
197 
198     /**
199      * Return the formatted, localized string for a named error message
200      * and supplied arguments.  For rmic error messages, with names that
201      * being with "rmic.", look up the error message in rmic's resource
202      * bundle; otherwise, defer to java's superclass method.
203      */
204     public String errorString(String err,
205                               Object arg0, Object arg1, Object arg2)
206     {
207         if (err.startsWith("rmic.") || err.startsWith("warn.rmic.")) {
208             String result =  Main.getText(err,
209                                           (arg0 != null ? arg0.toString() : null),
210                                           (arg1 != null ? arg1.toString() : null),
211                                           (arg2 != null ? arg2.toString() : null));
212 
213             if (err.startsWith("warn.")) {
214                 result = "warning: " + result;
215             }
216             return result;
217         } else {
218             return super.errorString(err, arg0, arg1, arg2);
219         }
220     }
221     public void reset() {
222     }
223 
224     /**
225      * Utility for building paths of directories and JAR files.  This
226      * class was copied from com.sun.tools.javac.util.Paths as part of
227      * the fix for 6473331, which adds support for Class-Path manifest
228      * entries in JAR files.  Diagnostic code is simply commented out
229      * because rmic silently ignored these conditions historically.
230      */
231     private static class Path extends LinkedHashSet<String> {
232         private static final long serialVersionUID = 0;
233         private static final boolean warn = false;
234 
235         private static class PathIterator implements Collection<String> {
236             private int pos = 0;
237             private final String path;
238             private final String emptyPathDefault;
239 
240             public PathIterator(String path, String emptyPathDefault) {
241                 this.path = path;
242                 this.emptyPathDefault = emptyPathDefault;
243             }
244             public PathIterator(String path) { this(path, null); }
245             public Iterator<String> iterator() {
246                 return new Iterator<String>() {
247                     public boolean hasNext() {
248                         return pos <= path.length();
249                     }
250                     public String next() {
251                         int beg = pos;
252                         int end = path.indexOf(File.pathSeparator, beg);
253                         if (end == -1)
254                             end = path.length();
255                         pos = end + 1;
256 
257                         if (beg == end && emptyPathDefault != null)
258                             return emptyPathDefault;
259                         else
260                             return path.substring(beg, end);
261                     }
262                     public void remove() {
263                         throw new UnsupportedOperationException();
264                     }
265                 };
266             }
267 
268             // required for Collection.
269             public int size() {
270                 throw new UnsupportedOperationException();
271             }
272             public boolean isEmpty() {
273                 throw new UnsupportedOperationException();
274             }
275             public boolean contains(Object o) {
276                 throw new UnsupportedOperationException();
277             }
278             public Object[] toArray() {
279                 throw new UnsupportedOperationException();
280             }
281             public <T> T[] toArray(T[] a) {
282                 throw new UnsupportedOperationException();
283             }
284             public boolean add(String o) {
285                 throw new UnsupportedOperationException();
286             }
287             public boolean remove(Object o) {
288                 throw new UnsupportedOperationException();
289             }
290             public boolean containsAll(Collection<?> c) {
291                 throw new UnsupportedOperationException();
292             }
293             public boolean addAll(Collection<? extends String> c) {
294                 throw new UnsupportedOperationException();
295             }
296             public boolean removeAll(Collection<?> c) {
297                 throw new UnsupportedOperationException();
298             }
299             public boolean retainAll(Collection<?> c) {
300                 throw new UnsupportedOperationException();
301             }
302             public void clear() {
303                 throw new UnsupportedOperationException();
304             }
305             public boolean equals(Object o) {
306                 throw new UnsupportedOperationException();
307             }
308             public int hashCode() {
309                 throw new UnsupportedOperationException();
310             }
311         }
312 
313         /** Is this the name of a zip file? */
314         private static boolean isZip(String name) {
315             return new File(name).isFile();
316         }
317 
318         private boolean expandJarClassPaths = false;
319 
320         public Path expandJarClassPaths(boolean x) {
321             expandJarClassPaths = x;
322             return this;
323         }
324 
325         /** What to use when path element is the empty string */
326         private String emptyPathDefault = null;
327 
328         public Path emptyPathDefault(String x) {
329             emptyPathDefault = x;
330             return this;
331         }
332 
333         public Path() { super(); }
334 
335         public Path addDirectories(String dirs, boolean warn) {
336             if (dirs != null)
337                 for (String dir : new PathIterator(dirs))
338                     addDirectory(dir, warn);
339             return this;
340         }
341 
342         public Path addDirectories(String dirs) {
343             return addDirectories(dirs, warn);
344         }
345 
346         private void addDirectory(String dir, boolean warn) {
347             if (! new File(dir).isDirectory()) {
348 //              if (warn)
349 //                  log.warning(Position.NOPOS,
350 //                              "dir.path.element.not.found", dir);
351                 return;
352             }
353 
354             for (String direntry : new File(dir).list()) {
355                 String canonicalized = direntry.toLowerCase();
356                 if (canonicalized.endsWith(".jar") ||
357                     canonicalized.endsWith(".zip"))
358                     addFile(dir + File.separator + direntry, warn);
359             }
360         }
361 
362         public Path addFiles(String files, boolean warn) {
363             if (files != null)
364                 for (String file : new PathIterator(files, emptyPathDefault))
365                     addFile(file, warn);
366             return this;
367         }
368 
369         public Path addFiles(String files) {
370             return addFiles(files, warn);
371         }
372 
373         private void addFile(String file, boolean warn) {
374             if (contains(file)) {
375                 /* Discard duplicates and avoid infinite recursion */
376                 return;
377             }
378 
379             File ele = new File(file);
380             if (! ele.exists()) {
381                 /* No such file or directory exist */
382                 if (warn)
383 //                      log.warning(Position.NOPOS,
384 //                          "path.element.not.found", file);
385                     return;
386             }
387 
388             if (ele.isFile()) {
389                 /* File is an ordinay file  */
390                 String arcname = file.toLowerCase();
391                 if (! (arcname.endsWith(".zip") ||
392                        arcname.endsWith(".jar"))) {
393                     /* File name don't have right extension */
394 //                      if (warn)
395 //                          log.warning(Position.NOPOS,
396 //                              "invalid.archive.file", file);
397                     return;
398                 }
399             }
400 
401             /* Now what we have left is either a directory or a file name
402                confirming to archive naming convention */
403 
404             super.add(file);
405             if (expandJarClassPaths && isZip(file))
406                 addJarClassPath(file, warn);
407         }
408 
409         // Adds referenced classpath elements from a jar's Class-Path
410         // Manifest entry.  In some future release, we may want to
411         // update this code to recognize URLs rather than simple
412         // filenames, but if we do, we should redo all path-related code.
413         private void addJarClassPath(String jarFileName, boolean warn) {
414             try {
415                 String jarParent = new File(jarFileName).getParent();
416                 JarFile jar = new JarFile(jarFileName);
417 
418                 try {
419                     Manifest man = jar.getManifest();
420                     if (man == null) return;
421 
422                     Attributes attr = man.getMainAttributes();
423                     if (attr == null) return;
424 
425                     String path = attr.getValue(Attributes.Name.CLASS_PATH);
426                     if (path == null) return;
427 
428                     for (StringTokenizer st = new StringTokenizer(path);
429                         st.hasMoreTokens();) {
430                         String elt = st.nextToken();
431                         if (jarParent != null)
432                             elt = new File(jarParent, elt).getCanonicalPath();
433                         addFile(elt, warn);
434                     }
435                 } finally {
436                     jar.close();
437                 }
438             } catch (IOException e) {
439 //              log.error(Position.NOPOS,
440 //                        "error.reading.file", jarFileName,
441 //                        e.getLocalizedMessage());
442             }
443         }
444     }
445 }