View Javadoc

1   /*
2    * Copyright (c) 2007, James Leigh All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    * 
7    * - Redistributions of source code must retain the above copyright notice, this
8    *   list of conditions and the following disclaimer.
9    * - Redistributions in binary form must reproduce the above copyright notice,
10   *   this list of conditions and the following disclaimer in the documentation
11   *   and/or other materials provided with the distribution. 
12   * - Neither the name of the openrdf.org nor the names of its contributors may
13   *   be used to endorse or promote products derived from this software without
14   *   specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   * 
28   */
29  package org.openrdf.elmo.codegen;
30  
31  import java.io.BufferedReader;
32  import java.io.File;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.InputStreamReader;
36  import java.io.OutputStream;
37  import java.lang.reflect.InvocationTargetException;
38  import java.lang.reflect.Method;
39  import java.net.URL;
40  import java.net.URLClassLoader;
41  import java.util.ArrayList;
42  import java.util.List;
43  
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  public class JavaCompiler {
48  
49  	final Logger logger = LoggerFactory.getLogger(JavaCompiler.class);
50  
51  	private String version = "5";
52  
53  	public String getVersion() {
54  		return version;
55  	}
56  
57  	public void setVersion(String version) {
58  		this.version = version;
59  	}
60  
61  	public void compile(File dir, List<String> content, URLClassLoader cl) throws Exception {
62  		List<File> classpath = new ArrayList<File>();
63  		for (URL jar : cl.getURLs()) {
64  			classpath.add(new File(jar.getPath()));
65  		}
66  		String classPath = System.getProperty("java.class.path");
67  		for (String path : classPath.split(File.pathSeparator)) {
68  			classpath.add(new File(path));
69  		}
70  		List<File> source = new ArrayList<File>();
71  		for (String name : content) {
72  			String filename = name.replace('.', File.separatorChar);
73  			source.add(new File(dir, filename + ".java"));
74  		}
75  		if (javac(buildJavacArgs(source, classpath)) != 0)
76  			throw new AssertionError("Could not compile");
77  	}
78  
79  	private int javac(String[] args) throws Exception {
80  		try {
81  			return javaCompilerTool(args);
82  		} catch (Exception e) {
83  		}
84  		try {
85  			return javaSunTools(args);
86  		} catch (Exception e) {
87  		}
88  		return javacCommand(args);
89  	}
90  
91  	/**
92  	 * Requires JDK6.
93  	 */
94  	private int javaCompilerTool(String[] args) throws Exception {
95  		Class<?> provider = Class.forName("javax.tools.ToolProvider");
96  		Method getJavaCompiler = provider.getMethod("getSystemJavaCompiler");
97  		Class<?> tool = Class.forName("javax.tools.Tool");
98  		Method run = tool.getMethod("run", InputStream.class,
99  				OutputStream.class, OutputStream.class, args.getClass());
100 		Object compiler = getJavaCompiler.invoke(null);
101 		logger.debug("invoke javax.tools.JavaCompiler#run");
102 		try {
103 			Object[] param = new Object[] { null, null, null, args };
104 			Object result = run.invoke(compiler, param);
105 			return ((Number) result).intValue();
106 		} catch (InvocationTargetException e) {
107 			if (e.getCause() instanceof Exception)
108 				throw (Exception) e.getCause();
109 			throw e;
110 		}
111 	}
112 
113 	/**
114 	 * Requires Sun tools.jar in class-path.
115 	 */
116 	private int javaSunTools(String[] args) throws Exception {
117 		Class<?> sun = Class.forName("com.sun.tools.javac.Main");
118 		Method method = sun.getMethod("compile", args.getClass());
119 		logger.debug("invoke com.sun.tools.javac.Main#compile");
120 		try {
121 			Object result = method.invoke(null, new Object[] { args });
122 			return ((Number) result).intValue();
123 		} catch (InvocationTargetException e) {
124 			if (e.getCause() instanceof Exception)
125 				throw (Exception) e.getCause();
126 			throw e;
127 		}
128 	}
129 
130 	/**
131 	 * Requires JDK installation.
132 	 */
133 	private int javacCommand(String[] args) throws Exception {
134 		return exec(findJavac(), args);
135 	}
136 
137 	private String findJavac() {
138 		String javac = findJavac(System.getProperty("jdk.home"));
139 		if (javac == null)
140 			javac = findJavac(System.getProperty("java.home"));
141 		if (javac == null)
142 			javac = findJavac(System.getenv("JAVA_HOME"));
143 		if (javac == null) {
144 			String systemPath = System.getenv("PATH");
145 			for (String path : systemPath.split(File.pathSeparator)) {
146 				File file = new File(path, "javac");
147 				if (file.exists())
148 					return file.getPath();
149 			}
150 		}
151 		if (javac == null)
152 			throw new AssertionError("No compiler found");
153 		return javac;
154 	}
155 
156 	private String findJavac(String home) {
157 		if (home == null)
158 			return null;
159 		File javac = new File(new File(home, "bin"), "javac");
160 		if (javac.exists())
161 			return javac.getPath();
162 		javac = new File(new File(home, "bin"), "javac.exe");
163 		if (javac.exists())
164 			return javac.getPath();
165 		File parent = new File(home).getParentFile();
166 		javac = new File(new File(parent, "bin"), "javac");
167 		if (javac.exists())
168 			return javac.getPath();
169 		javac = new File(new File(parent, "bin"), "javac.exe");
170 		if (javac.exists())
171 			return javac.getPath();
172 		return null;
173 	}
174 
175 	private int exec(String cmd, String[] args) throws IOException,
176 			InterruptedException {
177 		logger.debug("exec {}", cmd);
178 		String[] cmdArray = new String[1 + args.length];
179 		cmdArray[0] = cmd;
180 		System.arraycopy(args, 0, cmdArray, 1, args.length);
181 		final Process exec = Runtime.getRuntime().exec(cmdArray);
182 		Thread gobbler = new Thread() {
183 			@Override
184 			public void run() {
185 				try {
186 					InputStream in = exec.getInputStream();
187 					InputStreamReader isr = new InputStreamReader(in);
188 					BufferedReader br = new BufferedReader(isr);
189 					String line = null;
190 					while ((line = br.readLine()) != null)
191 						System.out.println(line);
192 				} catch (IOException ioe) {
193 					logger.error(ioe.getMessage(), ioe);
194 				}
195 			}
196 		};
197 		gobbler.start();
198 		InputStream stderr = exec.getErrorStream();
199 		InputStreamReader isr = new InputStreamReader(stderr);
200 		BufferedReader br = new BufferedReader(isr);
201 		String line = null;
202 		while ((line = br.readLine()) != null)
203 			System.err.println(line);
204 		return exec.waitFor();
205 	}
206 
207 	private String[] buildJavacArgs(List<File> sources, List<File> classpath) {
208 		String[] args = new String[6 + sources.size()];
209 		args[0] = "-source";
210 		args[1] = version;
211 		args[2] = "-target";
212 		args[3] = version;
213 		args[4] = "-classpath";
214 		StringBuilder sb = new StringBuilder();
215 		for (File jar : classpath) {
216 			sb.append(jar.getAbsolutePath());
217 			sb.append(File.pathSeparatorChar);
218 		}
219 		args[5] = sb.toString();
220 		for (int i = 0, n = sources.size(); i < n; i++) {
221 			args[6 + i] = sources.get(i).getAbsolutePath();
222 		}
223 		return args;
224 	}
225 }