diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/AliasManager.java b/nailgun-server/src/main/java/com/facebook/nailgun/AliasManager.java
index d8390b59..a5248c8a 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/AliasManager.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/AliasManager.java
@@ -35,14 +35,10 @@ public class AliasManager {
private Map aliases;
/** Creates a new AliasManager, populating it with default Aliases. */
- public AliasManager() {
+ public AliasManager(ClassLoader cl) {
aliases = new java.util.HashMap();
Properties props = new Properties();
- ClassLoader cl = getClass().getClassLoader();
- if (cl == null)
- cl = ClassLoader.getSystemClassLoader(); // needed if nailgun classes are loaded in the boot
- // classpath.
try (InputStream is =
cl.getResourceAsStream("com/facebook/nailgun/builtins/builtins.properties")) {
props.load(is);
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGClientListener.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGClientListener.java
index d0be5d4a..0baeb71c 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGClientListener.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGClientListener.java
@@ -16,6 +16,7 @@
package com.facebook.nailgun;
+@FunctionalInterface
public interface NGClientListener {
/**
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGCommunicator.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGCommunicator.java
index 8038a1c3..f3f575ec 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGCommunicator.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGCommunicator.java
@@ -17,7 +17,6 @@
package com.facebook.nailgun;
import java.io.ByteArrayInputStream;
-import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
@@ -48,7 +47,7 @@
* using underlying socket streams. Also handles client disconnect events based on client
* heartbeats.
*/
-public class NGCommunicator implements Closeable {
+public class NGCommunicator implements AutoCloseable {
private static final Logger LOG = Logger.getLogger(NGCommunicator.class.getName());
private final ExecutorService orchestratorExecutor;
@@ -383,6 +382,7 @@ private void stopOut() throws IOException {
if (outClosed) {
return;
}
+ out.flush();
outClosed = true;
LOG.log(Level.FINE, "Shutting down socket for output");
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGConstants.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGConstants.java
index ececaffa..d4d3122e 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGConstants.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGConstants.java
@@ -17,8 +17,12 @@
package com.facebook.nailgun;
+import java.io.File;
import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.Properties;
+import java.util.logging.Logger;
/**
* Just a simple holder for various NailGun-related contants.
@@ -27,6 +31,8 @@
*/
public class NGConstants {
+ private static final Logger LOG = Logger.getLogger(NGConstants.class.getName());
+
/** The default NailGun port (2113) */
public static final int DEFAULT_PORT = 2113;
/** The exit code sent to clients if nail completed successfully */
@@ -63,9 +69,6 @@ public class NGConstants {
/** Chunk type marker for heartbeats sent to let the server know the client is still alive. */
public static final byte CHUNKTYPE_HEARTBEAT = 'H';
- /** Server version number */
- public static final String VERSION = getVersion();
-
/** Expected interval between heartbeats in milliseconds. */
public static final short HEARTBEAT_INTERVAL_MILLIS = 1000;
@@ -78,17 +81,38 @@ public class NGConstants {
/** Maximum chunk len sent from client. */
public static final short MAXIMUM_CHUNK_LENGTH = 2048;
+ private static String VERSION = null;
+
/** Loads the version number from a file generated by Maven. */
- private static String getVersion() {
- Properties props = new Properties();
- try (InputStream is =
- NGConstants.class.getResourceAsStream(
- "/META-INF/maven/com.facebook/nailgun-server/pom.properties")) {
- props.load(is);
- } catch (Throwable e) {
- // In static initialization context, outputting or logging an exception is dangerous
- // It smells bad, but let's ignore it
+ public static String getVersion() throws MalformedURLException {
+ if (VERSION == null) {
+ Properties props = new Properties();
+ URL url =
+ NGServer.getInstance()
+ .getClassLoader()
+ .getResource("META-INF/maven/com.facebook/nailgun-server/pom.properties");
+ if (url == null) {
+ File file = new File("target/maven-archiver/pom.properties");
+ if (file.isFile()) {
+ url = file.toURI().toURL();
+ }
+ }
+ if (url == null) {
+ File file = new File("nailgun-server/target/maven-archiver/pom.properties");
+ if (file.isFile()) {
+ url = file.toURI().toURL();
+ }
+ }
+ LOG.info("get version info from " + url);
+ try (InputStream is = url.openStream()) {
+ props.load(is);
+ } catch (Throwable e) {
+ // In static initialization context, outputting or logging an exception is dangerous
+ // It smells bad, but let's ignore it
+ }
+ VERSION =
+ props.getProperty("version", System.getProperty("nailgun.server.version", "[UNKNOWN]"));
}
- return props.getProperty("version", System.getProperty("nailgun.server.version", "[UNKNOWN]"));
+ return VERSION;
}
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGContext.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGContext.java
index b723207b..aa058822 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGContext.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGContext.java
@@ -63,18 +63,22 @@ public class NGContext {
private String workingDirectory = null;
/** The client's stdin */
- public InputStream in = null;
+ private InputStream in = null;
/** The client's stdout */
- public PrintStream out = null;
+ private PrintStream out = null;
/** The client's stderr */
- public PrintStream err = null;
+ private PrintStream err = null;
private NGCommunicator communicator = null;
/** Creates a new, empty NGContext */
- public NGContext() {}
+ public NGContext(InputStream in, PrintStream out, PrintStream err) {
+ setIn(in);
+ setOut(out);
+ setErr(err);
+ }
public void setCommunicator(NGCommunicator comm) {
this.communicator = comm;
@@ -123,7 +127,7 @@ public String getWorkingDirectory() {
* @param in The {@link InputStream} to use as stdin for the current nail. This should be an
* InputStream that ultimately reads from {@link NGInputStream}.
*/
- public void setIn(InputStream in) {
+ private void setIn(InputStream in) {
this.in = in;
if (!(System.in instanceof ThreadLocalInputStream)) {
throw new IllegalStateException("System.in should be set by nailgun.");
@@ -138,7 +142,7 @@ public void setIn(InputStream in) {
* @param out The {@link PrintStream} to use as stdout for the current nail. This should be a
* PrintStream that ultimately writes to {@link NGOutputStream}.
*/
- public void setOut(PrintStream out) {
+ private void setOut(PrintStream out) {
this.out = out;
if (!(System.out instanceof ThreadLocalPrintStream)) {
throw new IllegalStateException("System.out should be set by nailgun.");
@@ -153,7 +157,7 @@ public void setOut(PrintStream out) {
* @param err The {@link PrintStream} to use as stderr for the current nail. This should be a
* PrintStream that ultimately writes to {@link NGOutputStream}.
*/
- public void setErr(PrintStream err) {
+ private void setErr(PrintStream err) {
this.err = err;
if (!(System.err instanceof ThreadLocalPrintStream)) {
throw new IllegalStateException("System.err should be set by nailgun.");
@@ -162,6 +166,18 @@ public void setErr(PrintStream err) {
tls.init(err);
}
+ public InputStream getIn() {
+ return in;
+ }
+
+ public PrintStream getOut() {
+ return out;
+ }
+
+ public PrintStream getErr() {
+ return err;
+ }
+
void setEnv(Properties remoteEnvironment) {
this.remoteEnvironment = remoteEnvironment;
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGHeartbeatListener.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGHeartbeatListener.java
index 512986cc..0acbe238 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGHeartbeatListener.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGHeartbeatListener.java
@@ -16,6 +16,7 @@
package com.facebook.nailgun;
+@FunctionalInterface
public interface NGHeartbeatListener {
/**
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGInputStream.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGInputStream.java
index c7ebb9fd..ca97c1aa 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGInputStream.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGInputStream.java
@@ -51,6 +51,7 @@ public boolean markSupported() {
}
/** @see java.io.InputStream#read() */
+ @Override
public int read() throws IOException {
// have to synchronize all one byte reads to be able to reuse internal buffer and not
// recreate new buffer on heap each time
@@ -60,11 +61,13 @@ public int read() throws IOException {
}
/** @see java.io.InputStream#read(byte[]) */
+ @Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/** @see java.io.InputStream#read(byte[], int, int) */
+ @Override
public int read(byte[] b, int offset, int length) throws IOException {
try {
return communicator.receive(b, offset, length);
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGServer.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGServer.java
index 0bacb67d..f036fcf2 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGServer.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGServer.java
@@ -25,6 +25,8 @@
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
@@ -46,9 +48,17 @@ public class NGServer implements Runnable {
private static final Logger LOG = Logger.getLogger(NGServer.class.getName());
+ private static NGServer INSTANCE = null;
+
/** Default size for thread pool */
public static final int DEFAULT_SESSIONPOOLSIZE = 2;
+ public static final NGServer getInstance() {
+ return INSTANCE;
+ }
+
+ private final URLClassLoader classLoader;
+
/** The address on which to listen */
private final NGListeningAddress listeningAddress;
@@ -99,7 +109,7 @@ public class NGServer implements Runnable {
* @param port the port on which to listen.
* @param sessionPoolSize the max number of idle sessions allowed by the pool
*/
- public NGServer(InetAddress addr, int port, int sessionPoolSize, int timeoutMillis) {
+ private NGServer(InetAddress addr, int port, int sessionPoolSize, int timeoutMillis) {
this(new NGListeningAddress(addr, port), sessionPoolSize, timeoutMillis);
}
@@ -111,7 +121,7 @@ public NGServer(InetAddress addr, int port, int sessionPoolSize, int timeoutMill
* @param addr the address at which to listen, or null
to bind to all local addresses
* @param port the port on which to listen.
*/
- public NGServer(InetAddress addr, int port) {
+ private NGServer(InetAddress addr, int port) {
this(
new NGListeningAddress(addr, port),
DEFAULT_SESSIONPOOLSIZE,
@@ -123,7 +133,7 @@ public NGServer(InetAddress addr, int port) {
* NGConstants.DEFAULT_PORT). This does not cause the server to start listening. To
* do so, create a new Thread
wrapping this NGServer
and start it.
*/
- public NGServer() {
+ private NGServer() {
this(
new NGListeningAddress(null, NGConstants.DEFAULT_PORT),
DEFAULT_SESSIONPOOLSIZE,
@@ -141,9 +151,20 @@ public NGServer() {
* disconnecting them
*/
public NGServer(NGListeningAddress listeningAddress, int sessionPoolSize, int timeoutMillis) {
+ if (INSTANCE == null) {
+ INSTANCE = this;
+ } else {
+ throw new IllegalStateException("NGServer singleton already initialized");
+ }
+ Thread current = Thread.currentThread();
+ ClassLoader parent = current.getContextClassLoader();
+ // TODO
+ classLoader = new URLClassLoader(new URL[0], parent);
+ current.setContextClassLoader(classLoader);
+
this.listeningAddress = listeningAddress;
- aliasManager = new AliasManager();
+ aliasManager = new AliasManager(classLoader);
allNailStats = new HashMap();
// allow a maximum of 10 idle threads. probably too high a number
// and definitely should be configurable in the future
@@ -151,6 +172,10 @@ public NGServer(NGListeningAddress listeningAddress, int sessionPoolSize, int ti
heartbeatTimeoutMillis = timeoutMillis;
}
+ public URLClassLoader getClassLoader() {
+ return classLoader;
+ }
+
/**
* Sets a flag that determines whether Nails can be executed by class name. If this is false,
* Nails can only be run via aliases (and you should probably remove ng-alias from the
@@ -372,7 +397,7 @@ public void run() {
// test_ng.py on *nix relies on reading this line from stdout to start connecting to server.
out.println(
"NGServer "
- + NGConstants.VERSION
+ + NGConstants.getVersion()
+ " started on "
+ listeningAddress.toString()
+ portDescription
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGSession.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGSession.java
index 8ac4d3c6..30c9ab9d 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGSession.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGSession.java
@@ -17,6 +17,7 @@
package com.facebook.nailgun;
+import com.facebook.nailgun.builtins.NGClasspath;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
@@ -24,6 +25,7 @@
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketException;
+import java.net.URLClassLoader;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -38,12 +40,14 @@
public class NGSession extends Thread {
@FunctionalInterface
- public interface CommnunicatorCreator {
+ public static interface CommnunicatorCreator {
NGCommunicator get(Socket socket) throws IOException;
}
private static final Logger LOG = Logger.getLogger(NGSession.class.getName());
+ private static final Class>[] NGCLASSPATH_ARGS = new Class>[] {URLClassLoader.class};
+
/** The server this NGSession is working for */
private final NGServer server;
/** The pool this NGSession came from, and to which it will return itself */
@@ -76,18 +80,6 @@ public interface CommnunicatorCreator {
NGContext.class,
};
- /** A ClassLoader that may be set by a client. Defaults to the classloader of this class. */
- public static volatile ClassLoader classLoader =
- null; // initialized in the static initializer - see below
-
- static {
- try {
- classLoader = NGSession.class.getClassLoader();
- } catch (SecurityException e) {
- throw e;
- }
- }
-
/**
* Creates a new NGSession running for the specified NGSessionPool and NGServer.
*
@@ -249,12 +241,15 @@ private void runImpl(NGCommunicator comm, Socket socket) {
if (alias != null) {
cmdclass = alias.getAliasedClass();
} else if (server.allowsNailsByClassName()) {
- cmdclass = Class.forName(cmdContext.getCommand(), true, classLoader);
+ cmdclass = Class.forName(cmdContext.getCommand(), true, server.getClassLoader());
} else {
cmdclass = server.getDefaultNailClass();
}
} catch (ClassNotFoundException ex) {
- throw new NGNailNotFoundException("Nail class not found: " + cmdContext.getCommand(), ex);
+ NGNailNotFoundException e =
+ new NGNailNotFoundException("Nail class not found: " + cmdContext.getCommand(), ex);
+ LOG.log(Level.SEVERE, e.toString(), e);
+ throw e;
}
Object[] methodArgs = new Object[1];
@@ -264,16 +259,7 @@ private void runImpl(NGCommunicator comm, Socket socket) {
.getCommandArguments()
.toArray(new String[cmdContext.getCommandArguments().size()]);
- boolean isStaticNail = true; // See: NonStaticNail.java
-
- Class[] interfaces = cmdclass.getInterfaces();
-
- for (int i = 0; i < interfaces.length; i++) {
- if (interfaces[i].equals(NonStaticNail.class)) {
- isStaticNail = false;
- break;
- }
- }
+ boolean isStaticNail = !NonStaticNail.class.isAssignableFrom(cmdclass);
if (!isStaticNail) {
mainMethod = cmdclass.getMethod("nailMain", new Class[] {String[].class});
@@ -281,11 +267,8 @@ private void runImpl(NGCommunicator comm, Socket socket) {
} else {
try {
mainMethod = cmdclass.getMethod("nailMain", nailMainSignature);
- NGContext context = new NGContext();
+ NGContext context = new NGContext(in, out, err);
context.setArgs(cmdlineArgs);
- context.in = in;
- context.out = out;
- context.err = err;
context.setCommand(cmdContext.getCommand());
context.setNGServer(server);
context.setCommunicator(comm);
@@ -301,8 +284,11 @@ private void runImpl(NGCommunicator comm, Socket socket) {
methodArgs[0] = cmdlineArgs;
} catch (NoSuchMethodException ex) {
// failed to find 'main' too, so give up and throw
- throw new NGNailNotFoundException(
- "Can't find nailMain or main functions in " + cmdclass.getName(), ex);
+ NGNailNotFoundException e =
+ new NGNailNotFoundException(
+ "Can't find nailMain or main functions in " + cmdclass.getName(), ex);
+ LOG.log(Level.SEVERE, e.toString(), e);
+ throw e;
}
}
}
@@ -310,9 +296,20 @@ private void runImpl(NGCommunicator comm, Socket socket) {
server.nailStarted(cmdclass);
try {
- mainMethod.invoke(isStaticNail ? null : cmdclass.newInstance(), methodArgs);
+ Object target = null;
+ if (!isStaticNail) {
+ target = cmdclass.getDeclaredConstructor().newInstance(new Object[0]);
+ } else if (NGClasspath.class.isAssignableFrom(cmdclass)) {
+ target =
+ cmdclass
+ .getDeclaredConstructor(NGCLASSPATH_ARGS)
+ .newInstance(server.getClassLoader());
+ }
+ mainMethod.invoke(target, methodArgs);
} catch (InvocationTargetException ite) {
- throw ite.getCause();
+ Throwable e = ite.getCause();
+ LOG.log(Level.SEVERE, "invocation on " + mainMethod + " failed", e);
+ throw e;
} finally {
server.nailFinished(cmdclass);
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NGWin32NamedPipeSocket.java b/nailgun-server/src/main/java/com/facebook/nailgun/NGWin32NamedPipeSocket.java
index 8e88bfa4..36836a37 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NGWin32NamedPipeSocket.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NGWin32NamedPipeSocket.java
@@ -39,7 +39,8 @@ public class NGWin32NamedPipeSocket extends Socket {
private final HANDLE readerWaitable;
private final HANDLE writerWaitable;
- interface CloseCallback {
+ @FunctionalInterface
+ static interface CloseCallback {
void onNamedPipeSocketClose(HANDLE handle) throws IOException;
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/NonStaticNail.java b/nailgun-server/src/main/java/com/facebook/nailgun/NonStaticNail.java
index 74bb3a2d..fe3b4818 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/NonStaticNail.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/NonStaticNail.java
@@ -23,7 +23,8 @@
*
* Implementations of this interface MUST provide a public, no-args constructor.
*/
+@FunctionalInterface
public interface NonStaticNail {
- public void nailMain(String[] args);
+ void nailMain(String[] args);
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/ThreadLocalPrintStream.java b/nailgun-server/src/main/java/com/facebook/nailgun/ThreadLocalPrintStream.java
index 13411c4c..78e45159 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/ThreadLocalPrintStream.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/ThreadLocalPrintStream.java
@@ -32,7 +32,7 @@
class ThreadLocalPrintStream extends PrintStream {
/** The PrintStreams for the various threads */
- private InheritableThreadLocal streams = null;
+ private InheritableThreadLocal streams = null;
private PrintStream defaultPrintStream = null;
@@ -44,7 +44,7 @@ class ThreadLocalPrintStream extends PrintStream {
*/
public ThreadLocalPrintStream(PrintStream defaultPrintStream) {
super(defaultPrintStream);
- streams = new InheritableThreadLocal();
+ streams = new InheritableThreadLocal<>();
this.defaultPrintStream = defaultPrintStream;
init(null);
}
@@ -65,127 +65,154 @@ void init(PrintStream streamForCurrentThread) {
*/
PrintStream getPrintStream() {
PrintStream result = (PrintStream) streams.get();
- return ((result == null) ? defaultPrintStream : result);
+ if (result == null || result.checkError()) {
+ result = defaultPrintStream;
+ }
+ return result;
}
// BEGIN delegated java.io.PrintStream methods
/** @see java.io.PrintStream#checkError() */
+ @Override
public boolean checkError() {
- return (getPrintStream().checkError());
+ return getPrintStream().checkError();
}
/** @see java.io.PrintStream#close() */
+ @Override
public void close() {
getPrintStream().close();
}
/** @see java.io.PrintStream#flush() */
+ @Override
public void flush() {
getPrintStream().flush();
}
/** @see java.io.PrintStream#print(boolean) */
+ @Override
public void print(boolean b) {
getPrintStream().print(b);
}
/** @see java.io.PrintStream#print(char) */
+ @Override
public void print(char c) {
getPrintStream().print(c);
}
/** @see java.io.PrintStream#print(char[]) */
+ @Override
public void print(char[] s) {
getPrintStream().print(s);
}
/** @see java.io.PrintStream#print(double) */
+ @Override
public void print(double d) {
getPrintStream().print(d);
}
/** @see java.io.PrintStream#print(float) */
+ @Override
public void print(float f) {
getPrintStream().print(f);
}
/** @see java.io.PrintStream#print(int) */
+ @Override
public void print(int i) {
getPrintStream().print(i);
}
/** @see java.io.PrintStream#print(long) */
+ @Override
public void print(long l) {
getPrintStream().print(l);
}
/** @see java.io.PrintStream#print(Object) */
+ @Override
public void print(Object obj) {
getPrintStream().print(obj);
}
/** @see java.io.PrintStream#print(String) */
+ @Override
public void print(String s) {
getPrintStream().print(s);
}
/** @see java.io.PrintStream#println() */
+ @Override
public void println() {
getPrintStream().println();
}
/** @see java.io.PrintStream#println(boolean) */
+ @Override
public void println(boolean x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(char) */
+ @Override
public void println(char x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(char[]) */
+ @Override
public void println(char[] x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(double) */
+ @Override
public void println(double x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(float) */
+ @Override
public void println(float x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(int) */
+ @Override
public void println(int x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(long) */
+ @Override
public void println(long x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(Object) */
+ @Override
public void println(Object x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#println(String) */
+ @Override
public void println(String x) {
getPrintStream().println(x);
}
/** @see java.io.PrintStream#write(byte[],int,int) */
+ @Override
public void write(byte[] buf, int off, int len) {
getPrintStream().write(buf, off, len);
}
/** @see java.io.PrintStream#write(int) */
+ @Override
public void write(int b) {
getPrintStream().write(b);
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/DefaultNail.java b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/DefaultNail.java
index 56843430..4c01e133 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/DefaultNail.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/DefaultNail.java
@@ -32,7 +32,7 @@
public class DefaultNail {
public static void nailMain(NGContext context) {
- context.err.println("No such command: " + context.getCommand());
+ context.getErr().println("No such command: " + context.getCommand());
context.exit(NGConstants.EXIT_NOSUCHCOMMAND);
}
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGAlias.java b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGAlias.java
index bc7d1215..fae60719 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGAlias.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGAlias.java
@@ -72,12 +72,15 @@ public static void nailMain(NGContext context) throws ClassNotFoundException {
}
for (Iterator i = aliases.iterator(); i.hasNext(); ) {
Alias alias = (Alias) i.next();
- context.out.println(
- padl(alias.getName(), maxAliasLength)
- + "\t"
- + padl(alias.getAliasedClass().getName(), maxClassnameLength));
- context.out.println(padl("", maxAliasLength) + "\t" + alias.getDescription());
- context.out.println();
+ context
+ .getOut()
+ .println(
+ padl(alias.getName(), maxAliasLength)
+ + "\t"
+ + padl(alias.getAliasedClass().getName(), maxClassnameLength));
+ context.getOut().println(padl("", maxAliasLength) + "\t" + alias.getDescription());
+ context.getOut().println();
+ context.getOut().flush();
}
} else if (args.length == 2) {
server.getAliasManager().addAlias(new Alias(args[0], "", Class.forName(args[1])));
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGClasspath.java b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGClasspath.java
index 9f508599..c54d2824 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGClasspath.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGClasspath.java
@@ -23,6 +23,8 @@
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* Provides a means to display and add to the system classpath at runtime. If called with no
@@ -36,6 +38,17 @@
*/
public class NGClasspath {
+ // TODO: EXTREMELY DANGEROUS IF SET TO TRUE
+ private static final boolean ALLOW_CLASSPATH_MODIFICATION = true;
+
+ private static final Logger LOG = Logger.getLogger(NGClasspath.class.getName());
+
+ private final URLClassLoader classLoader;
+
+ public NGClasspath(URLClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
/**
* Adds the specified URL (for a jar or a directory) to the System ClassLoader. This code was
* written by antony_miguel and posted on
@@ -47,28 +60,43 @@ public class NGClasspath {
* would be that your VM is not using a URLClassLoader as the System ClassLoader. This would
* result in a ClassClastException that you probably can't do much about.
*/
- private static void addToSystemClassLoader(URL url) throws Exception {
- URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
- Class sysclass = URLClassLoader.class;
-
- java.lang.reflect.Method method = sysclass.getDeclaredMethod("addURL", new Class[] {URL.class});
+ private void addToClassLoader(URL url) throws Exception {
+ // TODO: non-public method
+ java.lang.reflect.Method method =
+ classLoader.getClass().getDeclaredMethod("addURL", new Class[] {URL.class});
method.setAccessible(true);
- method.invoke(sysloader, new Object[] {url});
+ method.invoke(classLoader, new Object[] {url});
}
- public static void nailMain(NGContext context) throws Exception {
+ public void nailMain(NGContext context) throws Exception {
String[] args = context.getArgs();
if (args.length == 0) {
- URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
- URL[] urls = sysLoader.getURLs();
+ ClassLoader cl = classLoader;
+ URL[] urls = classLoader.getURLs();
+ context.getOut().println("classloader urls:");
for (int i = 0; i < urls.length; ++i) {
- context.out.println(urls[i]);
+ context.getOut().println("\t" + urls[i]);
}
+ context.getOut().println("end classloader urls");
+ do {
+ cl = cl.getParent();
+ context.getOut().println("parent classloader: " + cl);
+ } while (cl != null);
} else {
- for (int i = 0; i < args.length; ++i) {
- File file = new File(args[i]);
- addToSystemClassLoader(file.toURL());
+ if (ALLOW_CLASSPATH_MODIFICATION) {
+ for (int i = 0; i < args.length; ++i) {
+ File file = new File(args[i]);
+ URL url = file.toURI().toURL();
+ if (file.exists()) {
+ addToClassLoader(url);
+ } else {
+ LOG.log(Level.WARNING, "not adding " + url + " as it does not exist");
+ }
+ }
+ } else {
+ LOG.log(Level.SEVERE, "ng-cp classpath changes have been disabled for security");
}
}
+ context.getOut().flush();
}
}
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGServerStats.java b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGServerStats.java
index cab01911..1cbe8d46 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGServerStats.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGServerStats.java
@@ -41,7 +41,7 @@ public static void nailShutdown(NGServer server) {
}
public static void nailMain(NGContext context) {
- dumpStats(context.getNGServer(), context.out);
+ dumpStats(context.getNGServer(), context.getOut());
}
private static void dumpStats(NGServer server, java.io.PrintStream out) {
diff --git a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGVersion.java b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGVersion.java
index b6ff3d28..42ebcb5a 100644
--- a/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGVersion.java
+++ b/nailgun-server/src/main/java/com/facebook/nailgun/builtins/NGVersion.java
@@ -19,6 +19,7 @@
import com.facebook.nailgun.NGConstants;
import com.facebook.nailgun.NGContext;
+import java.net.MalformedURLException;
/**
* Displays the version of the NailGun server and exits.
@@ -27,7 +28,8 @@
*/
public class NGVersion {
- public static void nailMain(NGContext context) {
- context.out.println("NailGun server version " + NGConstants.VERSION);
+ public static void nailMain(NGContext context) throws MalformedURLException {
+ context.getOut().println("NailGun server version " + NGConstants.getVersion());
+ context.getOut().flush();
}
}
diff --git a/nailgun-server/src/main/resources/logging.properties b/nailgun-server/src/main/resources/logging.properties
new file mode 100644
index 00000000..720c9735
--- /dev/null
+++ b/nailgun-server/src/main/resources/logging.properties
@@ -0,0 +1,13 @@
+# http://tutorials.jenkov.com/java-logging/configuration.html
+# https://docs.oracle.com/javase/7/docs/api/index.html?java/util/logging/SimpleFormatter.html
+# https://stackoverflow.com/questions/54554045/using-simple-class-name-in-java-logging-formatter
+
+handlers=java.util.logging.ConsoleHandler
+.level=FINEST
+java.util.logging.ConsoleHandler.level=FINEST
+java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
+#java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n
+
+sun.awt.level=WARNING
+java.awt.level=WARNING
+javax.level=INFO
diff --git a/pom.xml b/pom.xml
index ad0591b7..cdbb17a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
com.facebook
nailgun-all
- 1.0.1
+ 1.0.2-SNAPSHOT
pom