langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/CompilerThread.java
changeset 26110 6ce251a87137
parent 26086 cfcea23d2d19
parent 26109 0430c63da650
child 26111 915a71858aef
equal deleted inserted replaced
26086:cfcea23d2d19 26110:6ce251a87137
     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