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      */
createClassPath(String classPathString)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      */
createClassPath(String classPathString, String sysClassPathString, String extDirsString)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      */
BatchEnvironment(OutputStream out, ClassPath path, Main main)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      */
getMain()151     public Main getMain() {
152         return main;
153     }
154 
155     /**
156      * Get the ClassPath.
157      */
getClassPath()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      */
addGeneratedFile(File file)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      */
deleteGeneratedFiles()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      */
shutdown()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      */
errorString(String err, Object arg0, Object arg1, Object arg2)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     }
reset()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 
PathIterator(String path, String emptyPathDefault)240             public PathIterator(String path, String emptyPathDefault) {
241                 this.path = path;
242                 this.emptyPathDefault = emptyPathDefault;
243             }
PathIterator(String path)244             public PathIterator(String path) { this(path, null); }
iterator()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.
size()269             public int size() {
270                 throw new UnsupportedOperationException();
271             }
isEmpty()272             public boolean isEmpty() {
273                 throw new UnsupportedOperationException();
274             }
contains(Object o)275             public boolean contains(Object o) {
276                 throw new UnsupportedOperationException();
277             }
toArray()278             public Object[] toArray() {
279                 throw new UnsupportedOperationException();
280             }
toArray(T[] a)281             public <T> T[] toArray(T[] a) {
282                 throw new UnsupportedOperationException();
283             }
add(String o)284             public boolean add(String o) {
285                 throw new UnsupportedOperationException();
286             }
remove(Object o)287             public boolean remove(Object o) {
288                 throw new UnsupportedOperationException();
289             }
containsAll(Collection<?> c)290             public boolean containsAll(Collection<?> c) {
291                 throw new UnsupportedOperationException();
292             }
addAll(Collection<? extends String> c)293             public boolean addAll(Collection<? extends String> c) {
294                 throw new UnsupportedOperationException();
295             }
removeAll(Collection<?> c)296             public boolean removeAll(Collection<?> c) {
297                 throw new UnsupportedOperationException();
298             }
retainAll(Collection<?> c)299             public boolean retainAll(Collection<?> c) {
300                 throw new UnsupportedOperationException();
301             }
clear()302             public void clear() {
303                 throw new UnsupportedOperationException();
304             }
equals(Object o)305             public boolean equals(Object o) {
306                 throw new UnsupportedOperationException();
307             }
hashCode()308             public int hashCode() {
309                 throw new UnsupportedOperationException();
310             }
311         }
312 
313         /** Is this the name of a zip file? */
isZip(String name)314         private static boolean isZip(String name) {
315             return new File(name).isFile();
316         }
317 
318         private boolean expandJarClassPaths = false;
319 
expandJarClassPaths(boolean x)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 
emptyPathDefault(String x)328         public Path emptyPathDefault(String x) {
329             emptyPathDefault = x;
330             return this;
331         }
332 
Path()333         public Path() { super(); }
334 
addDirectories(String dirs, boolean warn)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 
addDirectories(String dirs)342         public Path addDirectories(String dirs) {
343             return addDirectories(dirs, warn);
344         }
345 
addDirectory(String dir, boolean warn)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 
addFiles(String files, boolean warn)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 
addFiles(String files)369         public Path addFiles(String files) {
370             return addFiles(files, warn);
371         }
372 
addFile(String file, boolean warn)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.
addJarClassPath(String jarFileName, boolean warn)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 }
446