1 /*
2  * Copyright (c) 2005, 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 package javax.tools;
27 
28 import java.io.File;
29 import java.lang.ref.Reference;
30 import java.lang.ref.WeakReference;
31 import java.net.URL;
32 import java.net.URLClassLoader;
33 import java.net.MalformedURLException;
34 import java.util.HashMap;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.logging.Logger;
38 import java.util.logging.Level;
39 import static java.util.logging.Level.*;
40 
41 /**
42  * Provides methods for locating tool providers, for example,
43  * providers of compilers.  This class complements the
44  * functionality of {@link java.util.ServiceLoader}.
45  *
46  * @author Peter von der Ahé
47  * @since 1.6
48  */
49 public class ToolProvider {
50 
51     private static final String propertyName = "sun.tools.ToolProvider";
52     private static final String loggerName   = "javax.tools";
53 
54     /*
55      * Define the system property "sun.tools.ToolProvider" to enable
56      * debugging:
57      *
58      *     java ... -Dsun.tools.ToolProvider ...
59      */
trace(Level level, Object reason)60     static <T> T trace(Level level, Object reason) {
61         // NOTE: do not make this method private as it affects stack traces
62         try {
63             if (System.getProperty(propertyName) != null) {
64                 StackTraceElement[] st = Thread.currentThread().getStackTrace();
65                 String method = "???";
66                 String cls = ToolProvider.class.getName();
67                 if (st.length > 2) {
68                     StackTraceElement frame = st[2];
69                     method = String.format((Locale)null, "%s(%s:%s)",
70                                            frame.getMethodName(),
71                                            frame.getFileName(),
72                                            frame.getLineNumber());
73                     cls = frame.getClassName();
74                 }
75                 Logger logger = Logger.getLogger(loggerName);
76                 if (reason instanceof Throwable) {
77                     logger.logp(level, cls, method,
78                                 reason.getClass().getName(), (Throwable)reason);
79                 } else {
80                     logger.logp(level, cls, method, String.valueOf(reason));
81                 }
82             }
83         } catch (SecurityException ex) {
84             System.err.format((Locale)null, "%s: %s; %s%n",
85                               ToolProvider.class.getName(),
86                               reason,
87                               ex.getLocalizedMessage());
88         }
89         return null;
90     }
91 
92     private static final String defaultJavaCompilerName
93         = "com.sun.tools.javac.api.JavacTool";
94 
95     /**
96      * Gets the Java&trade; programming language compiler provided
97      * with this platform.
98      * @return the compiler provided with this platform or
99      * {@code null} if no compiler is provided
100      */
getSystemJavaCompiler()101     public static JavaCompiler getSystemJavaCompiler() {
102         return instance().getSystemTool(JavaCompiler.class, defaultJavaCompilerName);
103     }
104 
105     private static final String defaultDocumentationToolName
106         = "com.sun.tools.javadoc.api.JavadocTool";
107 
108     /**
109      * Gets the Java&trade; programming language documentation tool provided
110      * with this platform.
111      * @return the documentation tool provided with this platform or
112      * {@code null} if no documentation tool is provided
113      */
getSystemDocumentationTool()114     public static DocumentationTool getSystemDocumentationTool() {
115         return instance().getSystemTool(DocumentationTool.class, defaultDocumentationToolName);
116     }
117 
118     /**
119      * Returns the class loader for tools provided with this platform.
120      * This does not include user-installed tools.  Use the
121      * {@linkplain java.util.ServiceLoader service provider mechanism}
122      * for locating user installed tools.
123      *
124      * @return the class loader for tools provided with this platform
125      * or {@code null} if no tools are provided
126      */
getSystemToolClassLoader()127     public static ClassLoader getSystemToolClassLoader() {
128         try {
129             Class<? extends JavaCompiler> c =
130                     instance().getSystemToolClass(JavaCompiler.class, defaultJavaCompilerName);
131             return c.getClassLoader();
132         } catch (Throwable e) {
133             return trace(WARNING, e);
134         }
135     }
136 
137 
138     private static ToolProvider instance;
139 
instance()140     private static synchronized ToolProvider instance() {
141         if (instance == null)
142             instance = new ToolProvider();
143         return instance;
144     }
145 
146     // Cache for tool classes.
147     // Use weak references to avoid keeping classes around unnecessarily
148     private Map<String, Reference<Class<?>>> toolClasses = new HashMap<String, Reference<Class<?>>>();
149 
150     // Cache for tool classloader.
151     // Use a weak reference to avoid keeping it around unnecessarily
152     private Reference<ClassLoader> refToolClassLoader = null;
153 
154 
ToolProvider()155     private ToolProvider() { }
156 
getSystemTool(Class<T> clazz, String name)157     private <T> T getSystemTool(Class<T> clazz, String name) {
158         Class<? extends T> c = getSystemToolClass(clazz, name);
159         try {
160             return c.asSubclass(clazz).newInstance();
161         } catch (Throwable e) {
162             trace(WARNING, e);
163             return null;
164         }
165     }
166 
getSystemToolClass(Class<T> clazz, String name)167     private <T> Class<? extends T> getSystemToolClass(Class<T> clazz, String name) {
168         Reference<Class<?>> refClass = toolClasses.get(name);
169         Class<?> c = (refClass == null ? null : refClass.get());
170         if (c == null) {
171             try {
172                 c = findSystemToolClass(name);
173             } catch (Throwable e) {
174                 return trace(WARNING, e);
175             }
176             toolClasses.put(name, new WeakReference<Class<?>>(c));
177         }
178         return c.asSubclass(clazz);
179     }
180 
181     private static final String[] defaultToolsLocation = { "lib", "tools.jar" };
182 
findSystemToolClass(String toolClassName)183     private Class<?> findSystemToolClass(String toolClassName)
184         throws MalformedURLException, ClassNotFoundException
185     {
186         // try loading class directly, in case tool is on the bootclasspath
187         try {
188             return Class.forName(toolClassName, false, null);
189         } catch (ClassNotFoundException e) {
190             trace(FINE, e);
191 
192             // if tool not on bootclasspath, look in default tools location (tools.jar)
193             ClassLoader cl = (refToolClassLoader == null ? null : refToolClassLoader.get());
194             if (cl == null) {
195                 File file = new File(System.getProperty("java.home"));
196                 if (file.getName().equalsIgnoreCase("jre"))
197                     file = file.getParentFile();
198                 for (String name : defaultToolsLocation)
199                     file = new File(file, name);
200 
201                 // if tools not found, no point in trying a URLClassLoader
202                 // so rethrow the original exception.
203                 if (!file.exists())
204                     throw e;
205 
206                 URL[] urls = { file.toURI().toURL() };
207                 trace(FINE, urls[0].toString());
208 
209                 cl = URLClassLoader.newInstance(urls);
210                 refToolClassLoader = new WeakReference<ClassLoader>(cl);
211             }
212 
213             return Class.forName(toolClassName, false, cl);
214         }
215     }
216 }
217