1 /* 2 * Copyright (c) 2012, 2014, 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 com.sun.tools.sjavac.server; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.InputStreamReader; 32 import java.io.OutputStreamWriter; 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.net.Socket; 36 import java.net.URI; 37 import java.net.URISyntaxException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.concurrent.Future; 45 46 import javax.tools.JavaCompiler.CompilationTask; 47 import javax.tools.JavaFileObject; 48 import javax.tools.StandardJavaFileManager; 49 50 import com.sun.tools.javac.api.JavacTaskImpl; 51 import com.sun.tools.javac.util.Context; 52 import com.sun.tools.javac.util.ListBuffer; 53 import com.sun.tools.javac.util.Options; 54 import com.sun.tools.javac.util.StringUtils; 55 import com.sun.tools.sjavac.comp.AttrWithDeps; 56 import com.sun.tools.sjavac.comp.Dependencies; 57 import com.sun.tools.sjavac.comp.JavaCompilerWithDeps; 58 import com.sun.tools.sjavac.comp.JavacServiceImpl; 59 import com.sun.tools.sjavac.comp.ResolveWithDeps; 60 import com.sun.tools.sjavac.comp.SmartFileManager; 61 62 /** 63 * The compiler thread maintains a JavaCompiler instance and 64 * can receive a request from the client, perform the compilation 65 * requested and report back the results. 66 * 67 * * <p><b>This is NOT part of any supported API. 68 * If you write code that depends on this, you do so at your own 69 * risk. This code and its internal interfaces are subject to change 70 * or deletion without notice.</b></p> 71 */ 72 public class CompilerThread implements Runnable { 73 private JavacServer javacServer; 74 private CompilerPool compilerPool; 75 private JavacServiceImpl javacServiceImpl; 76 private List<Future<?>> subTasks; 77 78 // Communicating over this socket. 79 private Socket socket; 80 81 // The necessary classes to do a compilation. 82 private com.sun.tools.javac.api.JavacTool compiler; 83 private StandardJavaFileManager fileManager; 84 private SmartFileManager smartFileManager; 85 private Context context; 86 87 // If true, then this thread is serving a request. 88 private boolean inUse = false; 89 90 CompilerThread(CompilerPool cp, JavacServiceImpl javacServiceImpl) { 91 compilerPool = cp; 92 javacServer = cp.getJavacServer(); 93 this.javacServiceImpl = javacServiceImpl; 94 } 95 96 /** 97 * Execute a minor task, for example generating bytecodes and writing them to disk, 98 * that belong to a major compiler thread task. 99 */ 100 public synchronized void executeSubtask(Runnable r) { 101 subTasks.add(compilerPool.executeSubtask(this, r)); 102 } 103 104 /** 105 * Count the number of active sub tasks. 106 */ 107 public synchronized int numActiveSubTasks() { 108 int c = 0; 109 for (Future<?> f : subTasks) { 110 if (!f.isDone() && !f.isCancelled()) { 111 c++; 112 } 113 } 114 return c; 115 } 116 117 /** 118 * Use this socket for the upcoming request. 119 */ 120 public void setSocket(Socket s) { 121 socket = s; 122 } 123 124 /** 125 * Prepare the compiler thread for use. It is not yet started. 126 * It will be started by the executor service. 127 */ 128 public synchronized void use() { 129 assert(!inUse); 130 inUse = true; 131 compiler = com.sun.tools.javac.api.JavacTool.create(); 132 fileManager = compiler.getStandardFileManager(null, null, null); 133 smartFileManager = new SmartFileManager(fileManager); 134 context = new Context(); 135 ResolveWithDeps.preRegister(context); 136 AttrWithDeps.preRegister(context); 137 JavaCompilerWithDeps.preRegister(context, javacServiceImpl); 138 subTasks = new ArrayList<>(); 139 } 140 141 /** 142 * Prepare the compiler thread for idleness. 143 */ 144 public synchronized void unuse() { 145 assert(inUse); 146 inUse = false; 147 compiler = null; 148 fileManager = null; 149 smartFileManager = null; 150 context = null; 151 subTasks = null; 152 } 153 154 /** 155 * Expect this key on the next line read from the reader. 156 */ 157 private static boolean expect(BufferedReader in, String key) throws IOException { 158 String s = in.readLine(); 159 if (s != null && s.equals(key)) { 160 return true; 161 } 162 return false; 163 } 164 165 // The request identifier, for example GENERATE_NEWBYTECODE 166 String id = ""; 167 168 public String currentRequestId() { 169 return id; 170 } 171 172 PrintWriter stdout; 173 PrintWriter stderr; 174 int forcedExitCode = 0; 175 176 public void logError(String msg) { 177 stderr.println(msg); 178 forcedExitCode = -1; 179 } 180 181 /** 182 * Invoked by the executor service. 183 */ 184 public void run() { 185 // Unique nr that identifies this request. 186 int thisRequest = compilerPool.startRequest(); 187 long start = System.currentTimeMillis(); 188 int numClasses = 0; 189 StringBuilder compiledPkgs = new StringBuilder(); 190 use(); 191 192 PrintWriter out = null; 193 try { 194 javacServer.log("<"+thisRequest+"> Connect from "+socket.getRemoteSocketAddress()+" activethreads="+compilerPool.numActiveRequests()); 195 BufferedReader in = new BufferedReader(new InputStreamReader( 196 socket.getInputStream())); 197 out = new PrintWriter(new OutputStreamWriter( 198 socket.getOutputStream())); 199 if (!expect(in, JavacServer.PROTOCOL_COOKIE_VERSION)) { 200 javacServer.log("<"+thisRequest+"> Bad protocol from ip "+socket.getRemoteSocketAddress()); 201 return; 202 } 203 204 String cookie = in.readLine(); 205 if (cookie == null || !cookie.equals(""+javacServer.getCookie())) { 206 javacServer.log("<"+thisRequest+"> Bad cookie from ip "+socket.getRemoteSocketAddress()); 207 return; 208 } 209 if (!expect(in, JavacServer.PROTOCOL_CWD)) { 210 return; 211 } 212 String cwd = in.readLine(); 213 if (cwd == null) 214 return; 215 if (!expect(in, JavacServer.PROTOCOL_ID)) { 216 return; 217 } 218 id = in.readLine(); 219 if (id == null) 220 return; 221 if (!expect(in, JavacServer.PROTOCOL_ARGS)) { 222 return; 223 } 224 ArrayList<String> the_options = new ArrayList<>(); 225 ArrayList<File> the_classes = new ArrayList<>(); 226 Iterable<File> path = Arrays.<File> asList(new File(cwd)); 227 228 for (;;) { 229 String l = in.readLine(); 230 if (l == null) 231 return; 232 if (l.equals(JavacServer.PROTOCOL_SOURCES_TO_COMPILE)) 233 break; 234 if (l.startsWith("--server:")) 235 continue; 236 if (!l.startsWith("-") && l.endsWith(".java")) { 237 the_classes.add(new File(l)); 238 numClasses++; 239 } else { 240 the_options.add(l); 241 } 242 continue; 243 } 244 245 // Load sources to compile 246 Set<URI> sourcesToCompile = new HashSet<>(); 247 for (;;) { 248 String l = in.readLine(); 249 if (l == null) 250 return; 251 if (l.equals(JavacServer.PROTOCOL_VISIBLE_SOURCES)) 252 break; 253 try { 254 sourcesToCompile.add(new URI(l)); 255 numClasses++; 256 } catch (URISyntaxException e) { 257 return; 258 } 259 } 260 // Load visible sources 261 Set<URI> visibleSources = new HashSet<>(); 262 boolean fix_drive_letter_case = 263 StringUtils.toLowerCase(System.getProperty("os.name")).startsWith("windows"); 264 for (;;) { 265 String l = in.readLine(); 266 if (l == null) 267 return; 268 if (l.equals(JavacServer.PROTOCOL_END)) 269 break; 270 try { 271 URI u = new URI(l); 272 if (fix_drive_letter_case) { 273 // Make sure the driver letter is lower case. 274 String s = u.toString(); 275 if (s.startsWith("file:/") && 276 Character.isUpperCase(s.charAt(6))) { 277 u = new URI("file:/"+Character.toLowerCase(s.charAt(6))+s.substring(7)); 278 } 279 } 280 visibleSources.add(u); 281 } catch (URISyntaxException e) { 282 return; 283 } 284 } 285 286 // A completed request has been received. 287 288 // Now setup the actual compilation.... 289 // First deal with explicit source files on cmdline and in at file. 290 ListBuffer<JavaFileObject> compilationUnits = new ListBuffer<>(); 291 for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(the_classes)) { 292 compilationUnits.append(i); 293 } 294 // Now deal with sources supplied as source_to_compile. 295 ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>(); 296 for (URI u : sourcesToCompile) { 297 sourcesToCompileFiles.append(new File(u)); 298 } 299 for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) { 300 compilationUnits.append(i); 301 } 302 // Log the options to be used. 303 StringBuilder options = new StringBuilder(); 304 for (String s : the_options) { 305 options.append(">").append(s).append("< "); 306 } 307 javacServer.log(id+" <"+thisRequest+"> options "+options.toString()); 308 309 forcedExitCode = 0; 310 // Create a new logger. 311 StringWriter stdoutLog = new StringWriter(); 312 StringWriter stderrLog = new StringWriter(); 313 stdout = new PrintWriter(stdoutLog); 314 stderr = new PrintWriter(stderrLog); 315 com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK; 316 try { 317 if (compilationUnits.size() > 0) { 318 smartFileManager.setVisibleSources(visibleSources); 319 smartFileManager.cleanArtifacts(); 320 smartFileManager.setLog(stdout); 321 322 // Do the compilation! 323 CompilationTask task = compiler.getTask(stderr, smartFileManager, null, the_options, null, compilationUnits, context); 324 smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file")); 325 rc = ((JavacTaskImpl) task).doCall(); 326 327 while (numActiveSubTasks()>0) { 328 try { Thread.sleep(1000); } catch (InterruptedException e) { } 329 } 330 331 smartFileManager.flush(); 332 } 333 } catch (Exception e) { 334 stderr.println(e.getMessage()); 335 forcedExitCode = -1; 336 } 337 338 // Send the response.. 339 out.println(JavacServer.PROTOCOL_STDOUT); 340 out.print(stdoutLog); 341 out.println(JavacServer.PROTOCOL_STDERR); 342 out.print(stderrLog); 343 // The compilation is complete! And errors will have already been printed on out! 344 out.println(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS); 345 Map<String,Set<URI>> pa = smartFileManager.getPackageArtifacts(); 346 for (String aPkgName : pa.keySet()) { 347 out.println("+"+aPkgName); 348 Set<URI> as = pa.get(aPkgName); 349 for (URI a : as) { 350 out.println(" "+a.toString()); 351 } 352 } 353 Dependencies deps = Dependencies.instance(context); 354 out.println(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES); 355 Map<String,Set<String>> pd = deps.getDependencies(); 356 for (String aPkgName : pd.keySet()) { 357 out.println("+"+aPkgName); 358 Set<String> ds = pd.get(aPkgName); 359 // Everything depends on java.lang 360 if (!ds.contains(":java.lang")) ds.add(":java.lang"); 361 for (String d : ds) { 362 out.println(" "+d); 363 } 364 } 365 out.println(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS); 366 Map<String,String> pp = deps.getPubapis(); 367 for (String aPkgName : pp.keySet()) { 368 out.println("+"+aPkgName); 369 String ps = pp.get(aPkgName); 370 // getPubapis added a space to each line! 371 out.println(ps); 372 compiledPkgs.append(aPkgName+" "); 373 } 374 out.println(JavacServer.PROTOCOL_SYSINFO); 375 out.println("num_cores=" + Runtime.getRuntime().availableProcessors()); 376 out.println("max_memory=" + Runtime.getRuntime().maxMemory()); 377 out.println(JavacServer.PROTOCOL_RETURN_CODE); 378 379 // Errors from sjavac that affect compilation status! 380 int rcv = rc.exitCode; 381 if (rcv == 0 && forcedExitCode != 0) { 382 rcv = forcedExitCode; 383 } 384 out.println("" + rcv); 385 out.println(JavacServer.PROTOCOL_END); 386 out.flush(); 387 } catch (IOException e) { 388 e.printStackTrace(); 389 } finally { 390 try { 391 if (out != null) out.close(); 392 if (!socket.isClosed()) { 393 socket.close(); 394 } 395 socket = null; 396 } catch (Exception e) { 397 javacServer.log("ERROR "+e); 398 e.printStackTrace(); 399 } 400 compilerPool.stopRequest(); 401 long duration = System.currentTimeMillis()-start; 402 javacServer.addBuildTime(duration); 403 float classpersec = ((float)numClasses)*(((float)1000.0)/((float)duration)); 404 javacServer.log(id+" <"+thisRequest+"> "+compiledPkgs+" duration " + duration+ " ms num_classes="+numClasses+ 405 " classpersec="+classpersec+" subtasks="+subTasks.size()); 406 javacServer.flushLog(); 407 unuse(); 408 compilerPool.returnCompilerThread(this); 409 } 410 } 411 } 412