alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (JavacServer.java)

This example Java source code file (JavacServer.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bufferedreader, error_but_try_again, error_fatal, exception, hashmap, map, net, network, portfile, printstream, printwriter, quitting, set, string, stringbuffer, sysinfo, util

The JavacServer.java Java example source code

/*
 * Copyright (c) 2011-2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.tools.sjavac.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Random;

import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.ProblemException;
import java.io.*;
import java.util.*;

/**
 * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
 *
 * <p>This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are
 * subject to change or deletion without notice.</b>

*/ public class JavacServer { // Responding to this tcp/ip port on localhost. private final ServerSocket serverSocket; // The secret cookie shared between server and client through the port file. private final long myCookie; // When the server was started. private long serverStart; // Accumulated build time for all requests, not counting idle time. private long totalBuildTime; // The javac server specific log file. PrintWriter theLog; // The compiler pool that maintains the compiler threads. CompilerPool compilerPool; // For the client, all port files fetched, one per started javac server. // Though usually only one javac server is started by a client. private static Map<String, PortFile> allPortFiles; private static Map<String, Long> maxServerMemory; final static int ERROR_FATAL = -1; final static int ERROR_BUT_TRY_AGAIN = -4712; final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----"; final static String PROTOCOL_CWD = "----THE-CWD----"; final static String PROTOCOL_ID = "----THE-ID----"; final static String PROTOCOL_ARGS = "----THE-ARGS----"; final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----"; final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----"; final static String PROTOCOL_END = "----THE-END----"; final static String PROTOCOL_STDOUT = "----THE-STDOUT----"; final static String PROTOCOL_STDERR = "----THE-STDERR----"; final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----"; final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----"; final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----"; final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----"; final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----"; // Check if the portfile is gone, every 5 seconds. static int CHECK_PORTFILE_INTERVAL = 5; // Wait 2 seconds for response, before giving up on javac server. static int CONNECTION_TIMEOUT = 2; static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1; static int MAX_NUM_CONNECT_ATTEMPTS = 3; /** * Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time. */ private static synchronized PortFile getPortFile(String filename) throws FileNotFoundException { if (allPortFiles == null) { allPortFiles = new HashMap<String, PortFile>(); } PortFile pf = allPortFiles.get(filename); if (pf == null) { pf = new PortFile(filename); allPortFiles.put(filename, pf); } return pf; } /** * Get the cookie used for this server. */ long getCookie() { return myCookie; } /** * Get the port used for this server. */ int getPort() { return serverSocket.getLocalPort(); } /** * Sum up the total build time for this javac server. */ public void addBuildTime(long inc) { totalBuildTime += inc; } /** * Log this message. */ public void log(String msg) { if (theLog != null) { theLog.println(msg); } else { System.err.println(msg); } } /** * Make sure the log is flushed. */ public void flushLog() { if (theLog != null) { theLog.flush(); } } /** * Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3" * is sent as the settings parameter. Returns 0 on success, -1 on failure. */ public static int startServer(String settings, PrintStream err) { try { String portfile = Util.extractStringOption("portfile", settings); // The log file collects more javac server specific log information. String logfile = Util.extractStringOption("logfile", settings); // The stdouterr file collects all the System.out and System.err writes to disk. String stdouterrfile = Util.extractStringOption("stdouterrfile", settings); // We could perhaps use System.setOut and setErr here. // But for the moment we rely on the client to spawn a shell where stdout // and stderr are redirected already. // The pool size is a limit the number of concurrent compiler threads used. // The server might use less than these to avoid memory problems. int poolsize = Util.extractIntOption("poolsize", settings); if (poolsize <= 0) { // If not set, default to the number of cores. poolsize = Runtime.getRuntime().availableProcessors(); } // How many seconds of inactivity will the server accept before quitting? int keepalive = Util.extractIntOption("keepalive", settings); if (keepalive <= 0) { keepalive = 120; } // The port file is locked and the server port and cookie is written into it. PortFile portFile = getPortFile(portfile); JavacServer s; synchronized (portFile) { portFile.lock(); portFile.getValues(); if (portFile.containsPortInfo()) { err.println("Javac server not started because portfile exists!"); portFile.unlock(); return -1; } s = new JavacServer(poolsize, logfile); portFile.setValues(s.getPort(), s.getCookie()); portFile.unlock(); } // Run the server. Will delete the port file when shutting down. // It will shut down automatically when no new requests have come in // during the last 125 seconds. s.run(portFile, err, keepalive); // The run loop for the server has exited. return 0; } catch (Exception e) { e.printStackTrace(err); return -1; } } /** * Dispatch a compilation request to a javac server. * * @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac. * * The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk. * * @param sources_to_compile The sources to compile. * * @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see! * (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath * * @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server * can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar. * * The server return meta data about the build in the following parameters. * @param package_artifacts, map from package name to set of created artifacts for that package. * @param package_dependencies, map from package name to set of packages that it depends upon. * @param package_pubapis, map from package name to unique string identifying its pub api. */ public static int useServer(String settings, String[] args, Set<URI> sourcesToCompile, Set<URI> visibleSources, Map<URI, Set visibleClasses, Map<String, Set packageArtifacts, Map<String, Set packageDependencies, Map<String, String> packagePubapis, SysInfo sysinfo, PrintStream out, PrintStream err) { try { // The id can perhaps be used in the future by the javac server to reuse the // JavaCompiler instance for several compiles using the same id. String id = Util.extractStringOption("id", settings); String portfile = Util.extractStringOption("portfile", settings); String logfile = Util.extractStringOption("logfile", settings); String stdouterrfile = Util.extractStringOption("stdouterrfile", settings); String background = Util.extractStringOption("background", settings); if (background == null || !background.equals("false")) { background = "true"; } // The sjavac option specifies how the server part of sjavac is spawned. // If you have the experimental sjavac in your path, you are done. If not, you have // to point to a com.sun.tools.sjavac.Main that supports --startserver // for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main String sjavac = Util.extractStringOption("sjavac", settings); int poolsize = Util.extractIntOption("poolsize", settings); int keepalive = Util.extractIntOption("keepalive", settings); if (keepalive <= 0) { // Default keepalive for server is 120 seconds. // I.e. it will accept 120 seconds of inactivity before quitting. keepalive = 120; } if (portfile == null) { err.println("No portfile was specified!"); return -1; } if (logfile == null) { logfile = portfile + ".javaclog"; } if (stdouterrfile == null) { stdouterrfile = portfile + ".stdouterr"; } // Default to sjavac and hope it is in the path. if (sjavac == null) { sjavac = "sjavac"; } int attempts = 0; int rc = -1; do { PortFile port_file = getPortFile(portfile); synchronized (port_file) { port_file.lock(); port_file.getValues(); port_file.unlock(); } if (!port_file.containsPortInfo()) { String cmd = fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, err, stdouterrfile, background); if (background.equals("true") && !port_file.waitForValidValues()) { // Ouch the server did not start! Lets print its stdouterrfile and the command used. printFailedAttempt(cmd, stdouterrfile, err); // And give up. return -1; } } rc = connectAndCompile(port_file, id, args, sourcesToCompile, visibleSources, packageArtifacts, packageDependencies, packagePubapis, sysinfo, out, err); // Try again until we manage to connect. Any error after that // will cause the compilation to fail. if (rc == ERROR_BUT_TRY_AGAIN) { // We could not connect to the server. Try again. attempts++; try { Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS); } catch (InterruptedException e) { } } } while (rc == ERROR_BUT_TRY_AGAIN && attempts < MAX_NUM_CONNECT_ATTEMPTS); return rc; } catch (Exception e) { e.printStackTrace(err); return -1; } } private static void printFailedAttempt(String cmd, String f, PrintStream err) { err.println("---- Failed to start javac server with this command -----"); err.println(cmd); try { BufferedReader in = new BufferedReader(new FileReader(f)); err.println("---- stdout/stderr output from attempt to start javac server -----"); for (;;) { String l = in.readLine(); if (l == null) { break; } err.println(l); } err.println("------------------------------------------------------------------"); } catch (Exception e) { err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start."); } } /** * Spawn the server instance. */ private JavacServer(int poolSize, String logfile) throws IOException { serverStart = System.currentTimeMillis(); // Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface. // I.e only local processes can connect to this port. serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null)); compilerPool = new CompilerPool(poolSize, this); Random rnd = new Random(); myCookie = rnd.nextLong(); theLog = new PrintWriter(logfile); log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize); flushLog(); } /** * Fork a background process. Returns the command line used that can be printed if something failed. */ private static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive, final PrintStream err, String stdouterrfile, String background) throws IOException, ProblemException { if (stdouterrfile != null && stdouterrfile.trim().equals("")) { stdouterrfile = null; } final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive; if (background.equals("true")) { sjavac += "%20" + startserver; sjavac = sjavac.replaceAll("%20", " "); sjavac = sjavac.replaceAll("%2C", ","); // If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here. String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"}; if (!(new File("/bin/sh")).canExecute()) { ArrayList<String> wincmd = new ArrayList(); wincmd.add("cmd"); wincmd.add("/c"); wincmd.add("start"); wincmd.add("cmd"); wincmd.add("/c"); wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1"); cmd = wincmd.toArray(new String[wincmd.size()]); } Process pp = null; try { pp = Runtime.getRuntime().exec(cmd); } catch (Exception e) { e.printStackTrace(err); e.printStackTrace(new PrintWriter(stdouterrfile)); } StringBuilder rs = new StringBuilder(); for (String s : cmd) { rs.append(s + " "); } return rs.toString(); } // Do not spawn a background server, instead run it within the same JVM. Thread t = new Thread() { @Override public void run() { try { JavacServer.startServer(startserver, err); } catch (Throwable t) { t.printStackTrace(err); } } }; t.start(); return ""; } /** * Expect this key on the next line read from the reader. */ private static boolean expect(BufferedReader in, String key) throws IOException { String s = in.readLine(); if (s != null && s.equals(key)) { return true; } return false; } /** * Make a request to the server only to get the maximum possible heap size to use for compilations. * * @param port_file The port file used to synchronize creation of this server. * @param id The identify of the compilation. * @param out Standard out information. * @param err Standard err information. * @return The maximum heap size in bytes. */ public static SysInfo connectGetSysInfo(String serverSettings, PrintStream out, PrintStream err) { SysInfo sysinfo = new SysInfo(-1, -1); String id = Util.extractStringOption("id", serverSettings); String portfile = Util.extractStringOption("portfile", serverSettings); try { PortFile pf = getPortFile(portfile); useServer(serverSettings, new String[0], new HashSet<URI>(), new HashSet<URI>(), new HashMap<URI, Set(), new HashMap<String, Set(), new HashMap<String, Set(), new HashMap<String, String>(), sysinfo, out, err); } catch (Exception e) { e.printStackTrace(err); } return sysinfo; } /** * Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are * supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis. */ private static int connectAndCompile(PortFile portFile, String id, String[] args, Set<URI> sourcesToCompile, Set<URI> visibleSources, Map<String, Set packageArtifacts, Map<String, Set packageDependencies, Map<String, String> packagePublicApis, SysInfo sysinfo, PrintStream out, PrintStream err) { int rc = -3; try { int port = portFile.containsPortInfo() ? portFile.getPort() : 0; if (port == 0) { return ERROR_BUT_TRY_AGAIN; } long cookie = portFile.getCookie(); // Acquire the localhost/127.0.0.1 address. InetAddress addr = InetAddress.getByName(null); SocketAddress sockaddr = new InetSocketAddress(addr, port); Socket sock = new Socket(); int timeoutMs = CONNECTION_TIMEOUT * 1000; try { sock.connect(sockaddr, timeoutMs); } catch (java.net.ConnectException e) { err.println("Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e); return ERROR_BUT_TRY_AGAIN; } if (!sock.isConnected()) { err.println("Could not connect to javac server found in portfile: " + portFile.getFilename()); return ERROR_BUT_TRY_AGAIN; } BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream())); PrintWriter sockout = new PrintWriter(sock.getOutputStream()); sockout.println(PROTOCOL_COOKIE_VERSION); sockout.println("" + cookie); sockout.println(PROTOCOL_CWD); sockout.println(System.getProperty("user.dir")); sockout.println(PROTOCOL_ID); sockout.println(id); sockout.println(PROTOCOL_ARGS); for (String s : args) { StringBuffer buf = new StringBuffer(); String[] paths = s.split(File.pathSeparator); int c = 0; for (String path : paths) { File f = new File(path); if (f.isFile() || f.isDirectory()) { buf.append(f.getAbsolutePath()); c++; if (c < paths.length) { buf.append(File.pathSeparator); } } else { buf = new StringBuffer(s); break; } } sockout.println(buf.toString()); } sockout.println(PROTOCOL_SOURCES_TO_COMPILE); for (URI uri : sourcesToCompile) { sockout.println(uri.toString()); } sockout.println(PROTOCOL_VISIBLE_SOURCES); for (URI uri : visibleSources) { sockout.println(uri.toString()); } sockout.println(PROTOCOL_END); sockout.flush(); StringBuffer stdout = new StringBuffer(); StringBuffer stderr = new StringBuffer(); if (!expect(in, PROTOCOL_STDOUT)) { return ERROR_FATAL; } // Load stdout for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_STDERR)) { break; } stdout.append(l); stdout.append('\n'); } // Load stderr for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_PACKAGE_ARTIFACTS)) { break; } stderr.append(l); stderr.append('\n'); } // Load the package artifacts Set<URI> lastUriSet = null; for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_PACKAGE_DEPENDENCIES)) { break; } if (l.length() > 1 && l.charAt(0) == '+') { String pkg = l.substring(1); lastUriSet = new HashSet<URI>(); packageArtifacts.put(pkg, lastUriSet); } else if (l.length() > 1 && lastUriSet != null) { lastUriSet.add(new URI(l.substring(1))); } } // Load package dependencies Set<String> lastPackageSet = null; for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_PACKAGE_PUBLIC_APIS)) { break; } if (l.length() > 1 && l.charAt(0) == '+') { String pkg = l.substring(1); lastPackageSet = new HashSet<String>(); packageDependencies.put(pkg, lastPackageSet); } else if (l.length() > 1 && lastPackageSet != null) { lastPackageSet.add(l.substring(1)); } } // Load package pubapis Map<String, StringBuffer> tmp = new HashMap(); StringBuffer lastPublicApi = null; for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_SYSINFO)) { break; } if (l.length() > 1 && l.charAt(0) == '+') { String pkg = l.substring(1); lastPublicApi = new StringBuffer(); tmp.put(pkg, lastPublicApi); } else if (l.length() > 1 && lastPublicApi != null) { lastPublicApi.append(l.substring(1)); lastPublicApi.append("\n"); } } for (String p : tmp.keySet()) { assert (packagePublicApis.get(p) == null); String api = tmp.get(p).toString(); packagePublicApis.put(p, api); } // Now reading the max memory possible. for (;;) { String l = in.readLine(); if (l == null) { return ERROR_FATAL; } if (l.equals(PROTOCOL_RETURN_CODE)) { break; } if (l.startsWith("num_cores=") && sysinfo != null) { sysinfo.numCores = Integer.parseInt(l.substring(10)); } if (l.startsWith("max_memory=") && sysinfo != null) { sysinfo.maxMemory = Long.parseLong(l.substring(11)); } } String l = in.readLine(); if (l == null) { err.println("No return value from the server!"); return ERROR_FATAL; } rc = Integer.parseInt(l); out.print(stdout); err.print(stderr); } catch (Exception e) { e.printStackTrace(err); } return rc; } /** * Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other * javac server. */ private void run(PortFile portFile, PrintStream err, int keepalive) { boolean fileDeleted = false; long timeSinceLastCompile; try { // Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit // Or if the last request was finished more than 125 seconds ago => quit // 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000); for (;;) { try { Socket s = serverSocket.accept(); CompilerThread ct = compilerPool.grabCompilerThread(); ct.setSocket(s); compilerPool.execute(ct); flushLog(); } catch (java.net.SocketTimeoutException e) { if (compilerPool.numActiveRequests() > 0) { // Never quit while there are active requests! continue; } // If this is the timeout after the portfile // has been deleted by us. Then we truly stop. if (fileDeleted) { log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!"); break; } // Check if the portfile is still there. if (!portFile.exists()) { // Time to quit because the portfile was deleted by another // process, probably by the makefile that is done building. log("Quitting because portfile was deleted!"); flushLog(); break; } // Check if portfile.stop is still there. if (portFile.markedForStop()) { // Time to quit because another process touched the file // server.port.stop to signal that the server should stop. // This is necessary on some operating systems that lock // the port file hard! log("Quitting because a portfile.stop file was found!"); portFile.delete(); flushLog(); break; } // Does the portfile still point to me? if (!portFile.stillMyValues()) { // Time to quit because another build has started. log("Quitting because portfile is now owned by another javac server!"); flushLog(); break; } // Check how long since the last request finished. long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished(); if (diff < keepalive * 1000) { // Do not quit if we have waited less than 120 seconds. continue; } // Ok, time to quit because of inactivity. Perhaps the build // was killed and the portfile not cleaned up properly. portFile.delete(); fileDeleted = true; log("" + keepalive + " seconds of inactivity quitting in " + CHECK_PORTFILE_INTERVAL + " seconds!"); flushLog(); // Now we have a second 5 second grace // period where javac remote requests // that have loaded the data from the // recently deleted portfile can connect // and complete their requests. } } } catch (Exception e) { e.printStackTrace(err); e.printStackTrace(theLog); flushLog(); } finally { compilerPool.shutdown(); } long realTime = System.currentTimeMillis() - serverStart; log("Shutting down."); log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms"); flushLog(); } public static void cleanup(String... args) { String settings = Util.findServerSettings(args); if (settings == null) return; String portfile = Util.extractStringOption("portfile", settings); String background = Util.extractStringOption("background", settings); if (background != null && background.equals("false")) { // If the server runs within this jvm, then delete the portfile, // since this jvm is about to exit soon. File f = new File(portfile); f.delete(); } } }

Other Java examples (source code examples)

Here is a short list of links related to this Java JavacServer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.