Skip to content
Commits on Source (36)
/nailgun-server/target/
/nailgun-examples/target/
/nailgun-client/target/
/target/
ng
*.iml
*.pyc
.idea/
\ No newline at end of file
# Code of Conduct
Facebook has adopted a Code of Conduct that we expect project participants to adhere to.
Please read the [full text](https://code.fb.com/codeofconduct/)
so that you can understand what actions will and will not be tolerated.
\ No newline at end of file
# Contributing to Nailgun
We encourage the reporting of issues and bugs, along with pull requests to help make Nailgun codebase better. The following are some information and guidelines to help you contribute to Nailgun.
## Tour of the Codebase
This is a high-level overview of how the Nailgun repository is organized.
### `nailgun-server/`
That is where server side code lives, written in Java. It contains both core code under `src/main` and test code under `src/tests`.
### `nailgun-client/`
Client part of Nailgun, both C and Python versions in appropriate folders.
### `nailgun-examples/`
Some simple implementations of a Nail, helpful to understand how to write server-side code. One can also execute one of those nails for debugging or integration testing.
### `scripts/`
Automation scripts, mostly for continuous integration (i.e. Travis CI).
### `tools/`
Third-party dependencies used in tooling, like linter or code formatter.
## Development Workflow
### Building Nailgun
As simple as running `mvn clean package`.
### Running Tests
Unit tests and integration tests are JUnit and they are run by Maven when you say 'mvn package'. To run E2E test, just execute `./scripts/travis_ci.sh`. It will also run `mvn package` first with all unit tests.
### Using the IntelliJ IDE
Just open project from Nailgun's root folder
### Code Style
Code is autoformatted with Maven plugin and Google code style is used. Be ready to have some changed files after building the project with Maven, so you may have to amend the change to git.
Nailgun Copyright © 2004-2012, Martian Software, Inc.
Copyright © 2017-present Facebook, Inc.
Apache License
Version 2.0, January 2004
......
......@@ -8,19 +8,22 @@
WIN32_CC=/usr/bin/i686-w64-mingw32-gcc
CFLAGS=-Wall -pedantic -O2
SRCDIR=nailgun-client
SRCDIR=nailgun-client/c
TARGETDIR=nailgun-client/target
PREFIX=/usr/local
ng: ${SRCDIR}/ng.c
@echo "Building ng client. To build a Windows binary, type 'make ng.exe'"
${CC} $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o ng ${SRCDIR}/ng.c
@echo "Building ng client. To build a Windows binary, type 'make ng.exe'"
mkdir -p ${TARGETDIR}
${CC} $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o ${TARGETDIR}/ng ${SRCDIR}/ng.c
install: ng
install -d ${PREFIX}/bin
install ng ${PREFIX}/bin
install ${TARGETDIR}/ng ${PREFIX}/bin
ng.exe: ${SRCDIR}/ng.c
${WIN32_CC} -o ng.exe ${SRCDIR}/ng.c -lwsock32 -O3 ${CFLAGS}
mkdir -p ${TARGETDIR}
${WIN32_CC} -o ${TARGETDIR}/ng.exe ${SRCDIR}/ng.c -lwsock32 -O3 ${CFLAGS}
# any idea why the command line is so sensitive to the order of
# the arguments? If CFLAGS is at the beginning, it won't link.
......@@ -30,5 +33,5 @@ clean:
@echo "You must remove this manually. Most users won't have MinGW"
@echo "installed - so I'd rather not delete something you can't rebuild."
@echo ""
rm -f ng
# rm -f ng.exe
rm -f ${TARGETDIR}/ng
# rm -f ${TARGETDIR}/ng.exe
......@@ -22,6 +22,6 @@ you will additionally need to "make ng.exe".
A ruby client is available through the [railgun](https://github.com/timuralp/railgun) project.
For more information, see [the nailgun website](http://martiansoftware.com/nailgun/).
For more information, see [the nailgun website](https://github.com/facebook/nailgun).
......@@ -21,4 +21,4 @@ build_script:
- "mvn package"
test_script:
- "python -m pynailgun.test_ng"
- "python nailgun-client/py/test_ng.py"
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,7 +13,6 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
......@@ -42,7 +41,7 @@
#include <fcntl.h>
#include <errno.h>
#define NAILGUN_VERSION "0.9.0"
#define NAILGUN_VERSION "1.0.0"
#define BUFSIZE (2048)
......
# Copyright 2004-2015, Martian Software, Inc.
# Copyright 2017-Present Facebook, Inc.
from ng import NailgunConnection, NailgunException
#!/usr/bin/env python
#
# Copyright 2004-2015, Martian Software, Inc.
# Copyright 2017-Present Facebook, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import subprocess
import os
import time
......@@ -7,22 +24,24 @@ import shutil
import uuid
import sys
from pynailgun import NailgunException, NailgunConnection
from ng import NailgunException, NailgunConnection
is_py2 = sys.version[0] == '2'
is_py2 = sys.version[0] == "2"
if is_py2:
from StringIO import StringIO
else:
from io import StringIO
if os.name == 'posix':
if os.name == "posix":
def transport_exists(transport_file):
return os.path.exists(transport_file)
if os.name == 'nt':
if os.name == "nt":
import ctypes
from ctypes.wintypes import WIN32_FIND_DATAW as WIN32_FIND_DATA
INVALID_HANDLE_VALUE = -1
FindFirstFile = ctypes.windll.kernel32.FindFirstFileW
FindClose = ctypes.windll.kernel32.FindClose
......@@ -48,25 +67,26 @@ class TestNailgunConnection(unittest.TestCase):
def setUpTransport(self):
self.tmpdir = tempfile.mkdtemp()
if os.name == 'posix':
self.transport_file = os.path.join(self.tmpdir, 'sock')
self.transport_address = 'local:{0}'.format(self.transport_file)
if os.name == "posix":
self.transport_file = os.path.join(self.tmpdir, "sock")
self.transport_address = "local:{0}".format(self.transport_file)
else:
pipe_name = u'nailgun-test-{0}'.format(uuid.uuid4().hex)
self.transport_address = u'local:{0}'.format(pipe_name)
self.transport_file = u'\\\\.\\pipe\{0}'.format(pipe_name)
pipe_name = u"nailgun-test-{0}".format(uuid.uuid4().hex)
self.transport_address = u"local:{0}".format(pipe_name)
self.transport_file = u"\\\\.\\pipe\{0}".format(pipe_name)
def getClassPath(self):
cp = [
'nailgun-server/target/nailgun-server-0.9.3-SNAPSHOT-uber.jar',
'nailgun-examples/target/nailgun-examples-0.9.3-SNAPSHOT.jar',
]
if os.name == 'nt':
return ';'.join(cp)
return ':'.join(cp)
"nailgun-server/target/nailgun-server-1.0.0-SNAPSHOT-uber.jar",
"nailgun-examples/target/nailgun-examples-1.0.0-SNAPSHOT.jar",
]
if os.name == "nt":
return ";".join(cp)
return ":".join(cp)
def startNailgun(self):
if os.name == 'posix':
if os.name == "posix":
def preexec_fn():
# Close any open file descriptors to further separate buckd from its
# invoking context (e.g. otherwise we'd hang when running things like
......@@ -75,6 +95,7 @@ class TestNailgunConnection(unittest.TestCase):
os.dup2(dev_null_fd, 0)
os.dup2(dev_null_fd, 2)
os.close(dev_null_fd)
creationflags = 0
else:
preexec_fn = None
......@@ -83,42 +104,56 @@ class TestNailgunConnection(unittest.TestCase):
creationflags = DETACHED_PROCESS
stdout = None
if os.name == 'posix':
stdout=subprocess.PIPE
log_config_file = os.path.join(self.tmpdir, 'logging.properties')
self.log_file = os.path.join(self.tmpdir, 'test_ng.log')
with open(log_config_file, 'w') as config_file:
config_file.write('handlers = java.util.logging.FileHandler\n')
config_file.write('.level = ALL\n')
config_file.write('java.util.logging.FileHandler.level = ALL\n')
config_file.write('java.util.logging.FileHandler.pattern = ' + self.log_file + '\n')
config_file.write('java.util.logging.FileHandler.count = 1\n')
config_file.write('java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter\n')
cmd = ['java', '-Djna.nosys=true', '-Djava.util.logging.config.file=' + log_config_file, '-classpath', self.getClassPath()]
debug_mode = os.environ.get('DEBUG_MODE') or ''
if debug_mode != '':
suspend = 'n' if debug_mode == '2' else 'y'
cmd.append('-agentlib:jdwp=transport=dt_socket,address=localhost:8888,server=y,suspend=' + suspend)
cmd = cmd + ['com.martiansoftware.nailgun.NGServer', self.transport_address, str(self.heartbeat_timeout_ms)]
if os.name == "posix":
stdout = subprocess.PIPE
log_config_file = os.path.join(self.tmpdir, "logging.properties")
self.log_file = os.path.join(self.tmpdir, "test_ng.log")
with open(log_config_file, "w") as config_file:
config_file.write("handlers = java.util.logging.FileHandler\n")
config_file.write(".level = ALL\n")
config_file.write("java.util.logging.FileHandler.level = ALL\n")
config_file.write(
"java.util.logging.FileHandler.pattern = " + self.log_file + "\n"
)
config_file.write("java.util.logging.FileHandler.count = 1\n")
config_file.write(
"java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter\n"
)
cmd = [
"java",
"-Djna.nosys=true",
"-Djava.util.logging.config.file=" + log_config_file,
"-classpath",
self.getClassPath(),
]
debug_mode = os.environ.get("DEBUG_MODE") or ""
if debug_mode != "":
suspend = "n" if debug_mode == "2" else "y"
cmd.append(
"-agentlib:jdwp=transport=dt_socket,address=localhost:8888,server=y,suspend="
+ suspend
)
cmd = cmd + [
"com.facebook.nailgun.NGServer",
self.transport_address,
str(self.heartbeat_timeout_ms),
]
self.ng_server_process = subprocess.Popen(
cmd,
preexec_fn=preexec_fn,
creationflags=creationflags,
stdout=stdout,
cmd, preexec_fn=preexec_fn, creationflags=creationflags, stdout=stdout
)
self.assertIsNone(self.ng_server_process.poll())
if os.name == 'posix':
if os.name == "posix":
# on *nix we have to wait for server to be ready to accept connections
while True:
the_first_line = str(self.ng_server_process.stdout.readline().strip())
if "NGServer" in the_first_line and "started" in the_first_line:
break
if the_first_line is None or the_first_line == '':
if the_first_line is None or the_first_line == "":
break
else:
for _ in range(0, 600):
......@@ -137,8 +172,9 @@ class TestNailgunConnection(unittest.TestCase):
cwd=os.getcwd(),
stderr=None,
stdin=None,
stdout=None) as c:
c.send_command('ng-stop')
stdout=None,
) as c:
c.send_command("ng-stop")
except NailgunException as e:
# stopping server is a best effort
# if something wrong has happened, we will kill it anyways
......@@ -150,99 +186,116 @@ class TestNailgunConnection(unittest.TestCase):
process_exit_code = self.ng_server_process.poll()
if process_exit_code is not None:
break
time.sleep(0.02) # 1 second total
time.sleep(0.02) # 1 second total
if process_exit_code is None:
# some test has failed, ng-server was not stopped. killing it
self.ng_server_process.kill()
debug_logs = os.environ.get('DEBUG_LOGS') or ''
if debug_logs != '':
with open(self.log_file, 'r') as log_file:
print('NAILGUN SERVER LOG:\n')
debug_logs = os.environ.get("DEBUG_LOGS") or ""
if debug_logs != "":
with open(self.log_file, "r") as log_file:
print("NAILGUN SERVER LOG:\n")
print(log_file.read())
shutil.rmtree(self.tmpdir)
class TestNailgunConnectionMain(TestNailgunConnection):
def __init__(self, *args, **kwargs):
super(TestNailgunConnectionMain, self).__init__(*args, **kwargs)
def test_nailgun_stats(self):
output = StringIO()
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output) as c:
exit_code = c.send_command('ng-stats')
self.transport_address, stderr=None, stdin=None, stdout=output
) as c:
exit_code = c.send_command("ng-stats")
self.assertEqual(exit_code, 0)
actual_out = output.getvalue().strip()
expected_out = 'com.martiansoftware.nailgun.builtins.NGServerStats: 1/1'
expected_out = "com.facebook.nailgun.builtins.NGServerStats: 1/1"
self.assertEqual(actual_out, expected_out)
def test_nailgun_exit_code(self):
output = StringIO()
expected_exit_code = 10
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output) as c:
exit_code = c.send_command('com.martiansoftware.nailgun.examples.Exit', [str(expected_exit_code)])
self.transport_address, stderr=None, stdin=None, stdout=output
) as c:
exit_code = c.send_command(
"com.facebook.nailgun.examples.Exit", [str(expected_exit_code)]
)
self.assertEqual(exit_code, expected_exit_code)
def test_nailgun_stdin(self):
lines = [str(i) for i in range(100)]
echo = '\n'.join(lines)
echo = "\n".join(lines)
output = StringIO()
input = StringIO(echo)
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=input,
stdout=output) as c:
exit_code = c.send_command('com.martiansoftware.nailgun.examples.Echo')
self.transport_address, stderr=None, stdin=input, stdout=output
) as c:
exit_code = c.send_command("com.facebook.nailgun.examples.Echo")
self.assertEqual(exit_code, 0)
actual_out = output.getvalue().strip()
self.assertEqual(actual_out, echo)
def test_nailgun_default_streams(self):
with NailgunConnection(self.transport_address) as c:
exit_code = c.send_command('ng-stats')
exit_code = c.send_command("ng-stats")
self.assertEqual(exit_code, 0)
def test_nailgun_heartbeats(self):
output = StringIO()
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=0.1) as c:
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=0.1,
) as c:
# just run Heartbeat nail for 5 seconds. During this period there should be
# heartbeats received and printed back
exit_code = c.send_command('com.martiansoftware.nailgun.examples.Heartbeat', ['5000'])
self.assertTrue(output.getvalue().count('H') > 10)
exit_code = c.send_command(
"com.facebook.nailgun.examples.Heartbeat", ["5000"]
)
self.assertTrue(output.getvalue().count("H") > 10)
def test_nailgun_no_heartbeat(self):
output = StringIO()
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=0,
) as c:
exit_code = c.send_command(
"com.facebook.nailgun.examples.Heartbeat", ["3000"]
)
self.assertTrue(output.getvalue().count("H") == 0)
def test_stress_nailgun_socket_close_without_race_condition(self):
output = StringIO()
for i in range(1000):
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=0) as c:
exit_code = c.send_command('com.martiansoftware.nailgun.examples.Heartbeat', ['3000'])
self.assertTrue(output.getvalue().count('H') == 0)
heartbeat_interval_sec=0.001,
) as c:
exit_code = c.send_command(
"com.facebook.nailgun.examples.Heartbeat", ["10"]
)
self.assertEqual(exit_code, 0)
class TestNailgunConnectionSmallHeartbeatTimeout(TestNailgunConnection):
def __init__(self, *args, **kwargs):
super(TestNailgunConnectionSmallHeartbeatTimeout, self).__init__(*args, **kwargs)
super(TestNailgunConnectionSmallHeartbeatTimeout, self).__init__(
*args, **kwargs
)
def setUp(self):
self.heartbeat_timeout_ms = 1000
......@@ -258,16 +311,19 @@ class TestNailgunConnectionSmallHeartbeatTimeout(TestNailgunConnection):
"""
output = StringIO()
with NailgunConnection(
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=5) as c:
exit_code = c.send_command('com.martiansoftware.nailgun.examples.Heartbeat', ['30000'])
self.assertTrue(output.getvalue().count('H') < 3)
if __name__ == '__main__':
self.transport_address,
stderr=None,
stdin=None,
stdout=output,
heartbeat_interval_sec=5,
) as c:
exit_code = c.send_command(
"com.facebook.nailgun.examples.Heartbeat", ["30000"]
)
self.assertTrue(output.getvalue().count("H") < 3)
if __name__ == "__main__":
was_successful = unittest.main(exit=False).result.wasSuccessful()
if not was_successful:
sys.exit(1)
......@@ -2,32 +2,33 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.martiansoftware</groupId>
<groupId>com.facebook</groupId>
<artifactId>nailgun-examples</artifactId>
<packaging>jar</packaging>
<name>nailgun-examples</name>
<description>
Nailgun is a client, protocol, and server for running Java programs from
the command line without incurring the JVM startup overhead. Programs run
in the server (which is implemented in Java), and are triggered by the
client (written in C), which handles all I/O.
Nailgun is a client, protocol and server for running Java programs
from the command line without incurring the JVM startup overhead.
Programs run in the server (which is implemented in Java), and are
triggered by the client (C and Python clients available), which
handles all I/O.
This project contains the EXAMPLE CODE ONLY.
</description>
<url>http://martiansoftware.com/nailgun</url>
<url>http://github.com/facebook/nailgun</url>
<parent>
<groupId>com.martiansoftware</groupId>
<groupId>com.facebook</groupId>
<artifactId>nailgun-all</artifactId>
<version>0.9.3-SNAPSHOT</version>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.martiansoftware</groupId>
<groupId>com.facebook</groupId>
<artifactId>nailgun-server</artifactId>
<version>${version}</version>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
......
/*
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,42 +13,39 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.martiansoftware.nailgun.examples;
package com.facebook.nailgun.examples;
import java.util.Iterator;
import com.facebook.nailgun.NGContext;
import java.util.TreeSet;
import com.martiansoftware.nailgun.NGContext;
/**
* Simply displays command line arguments to System.out.
*
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class DumpAll {
public static void nailMain(NGContext context) {
context.out.println();
context.out.println(" context.getCommand(): " + context.getCommand());
context.out.println(" context.getInetAddress(): " + context.getInetAddress());
context.out.println(" context.getPort(): " + context.getPort());
context.out.println("context.getWorkingDirectory(): " + context.getWorkingDirectory());
context.out.println(" context.getFileSeparator(): " + context.getFileSeparator());
context.out.println(" context.getPathSeparator(): " + context.getPathSeparator());
context.out.println("\ncontext.getArgs():");
for (int i = 0; i < context.getArgs().length; ++i) {
context.out.println(" args[" + i + "]=" + context.getArgs()[i]);
}
context.out.println("\ncontext.getEnv():");
TreeSet keys = new TreeSet(context.getEnv().keySet());
for (Iterator i = keys.iterator(); i.hasNext();) {
String key = (String) i.next();
context.out.println(" env[\"" + key + "\"]=" + context.getEnv().getProperty(key));
}
}
public static void nailMain(NGContext context) {
context.out.println();
context.out.println(" context.getCommand(): " + context.getCommand());
context.out.println(" context.getInetAddress(): " + context.getInetAddress());
context.out.println(" context.getPort(): " + context.getPort());
context.out.println("context.getWorkingDirectory(): " + context.getWorkingDirectory());
context.out.println(" context.getFileSeparator(): " + context.getFileSeparator());
context.out.println(" context.getPathSeparator(): " + context.getPathSeparator());
context.out.println("\ncontext.getArgs():");
for (int i = 0; i < context.getArgs().length; ++i) {
context.out.println(" args[" + i + "]=" + context.getArgs()[i]);
}
context.out.println("\ncontext.getEnv():");
TreeSet keys = new TreeSet(context.getEnv().keySet());
for (Object okey : keys) {
String key = (String) okey;
context.out.println(" env[\"" + key + "\"]=" + context.getEnv().getProperty(key));
}
}
}
/*
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,24 +13,23 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.martiansoftware.nailgun.examples;
package com.facebook.nailgun.examples;
/**
* Echos everything it reads from System.in to System.out.
*
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class Echo {
public static void main(String[] args) throws Exception {
byte[] b = new byte[1024];
int bytesRead = System.in.read(b);
while (bytesRead != -1) {
System.out.write(b, 0, bytesRead);
bytesRead = System.in.read(b);
}
}
public static void main(String[] args) throws Exception {
byte[] b = new byte[1024];
int bytesRead = System.in.read(b);
while (bytesRead != -1) {
System.out.write(b, 0, bytesRead);
bytesRead = System.in.read(b);
}
}
}
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.facebook.nailgun.examples;
/** Finish nail with provided exit code */
public class Exit {
public static void main(String[] args) {
int exitCode = (int) ((Math.random() * 1000) + 1);
if (args.length > 0) {
try {
exitCode = Integer.parseInt(args[0]);
} catch (Exception e) {
}
}
// Close stdout to test the exit code is returned properly
// even in such case
System.out.close();
System.exit(exitCode);
}
}
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.facebook.nailgun.examples;
import com.facebook.nailgun.NGContext;
import java.security.MessageDigest;
import java.security.Provider;
import java.security.Security;
import java.util.Set;
import java.util.TreeSet;
/**
* Hashes the client's stdin to the client's stdout in the form of a hexadecimal string. Command
* line requires one parameter: either the name of the algorithm to use (e.g., "MD5"), or "?" to
* request a list of available algorithms.
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class Hash {
// used to turn byte[] to string
private static final char[] HEXCHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/**
* Provides a list of algorithms for the specified service (which, for our purposes, is
* "MessageDigest".
*
* <p>This method was only very slightly adapted (to use a TreeSet) from the Java Almanac at
* http://javaalmanac.com/egs/java.security/ListServices.html
*
* @param serviceType The name of the service we're looking for. It's "MessageDigest"
*/
private static Set getCryptoImpls(String serviceType) {
Set result = new TreeSet();
// All all providers
Provider[] providers = Security.getProviders();
for (int i = 0; i < providers.length; i++) {
// Get services provided by each provider
Set keys = providers[i].keySet();
for (Object okey : providers[i].keySet()) {
String key = (String) okey;
key = key.split(" ")[0];
if (key.startsWith(serviceType + ".")) {
result.add(key.substring(serviceType.length() + 1));
} else if (key.startsWith("Alg.Alias." + serviceType + ".")) {
// This is an alias
result.add(key.substring(serviceType.length() + 11));
}
}
}
return result;
}
/**
* Hashes client stdin, displays hash result to client stdout. Requires one command line
* parameter, either the name of the hash algorithm to use (e.g., "MD5") or "?" to request a list
* of available algorithms. Any exceptions become the problem of the user.
*/
public static void nailMain(NGContext context)
throws java.security.NoSuchAlgorithmException, java.io.IOException {
String[] args = context.getArgs();
if (args.length == 0) {
// display available algorithms
Set algs = getCryptoImpls("MessageDigest");
for (Object alg : algs) {
context.out.println(alg);
}
return;
}
// perform the actual hash. throw any exceptions back to the user.
MessageDigest md = MessageDigest.getInstance(args[0]);
byte[] b = new byte[1024];
int bytesRead = context.in.read(b);
while (bytesRead != -1) {
md.update(b, 0, bytesRead);
bytesRead = System.in.read(b);
}
byte[] result = md.digest();
// convert hash result to a string of hex characters and print it to the client.
StringBuffer buf = new StringBuffer();
for (int i = 0; i < result.length; ++i) {
buf.append(HEXCHARS[(result[i] >> 4) & 0x0f]);
buf.append(HEXCHARS[result[i] & 0x0f]);
}
context.out.println(buf);
}
}
/*
/*
Copyright 2004-2012, Jim Purbrick.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,55 +13,50 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.martiansoftware.nailgun.examples;
package com.facebook.nailgun.examples;
import com.martiansoftware.nailgun.NGContext;
import com.facebook.nailgun.NGContext;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Print one hash per second to standard out while the client is running. Whenever heartbeat is
* received from client, print H.
*/
/** Print H for each heartbeat received */
public class Heartbeat {
public static void nailMain(final NGContext context) {
long start = System.nanoTime();
long run_nanos = Long.MAX_VALUE;
String[] args = context.getArgs();
if (args.length > 0) {
// first argument is the number of milliseconds to run a command
// if omitted it will never interrupt by itself
try {
run_nanos = Long.parseUnsignedLong(args[0]) * 1000 * 1000;
} catch (Exception e) {}
}
try {
Object lock = new Object();
AtomicBoolean shutdown = new AtomicBoolean(false);
context.addClientListener(reason -> {
synchronized (lock) {
shutdown.set(true);
lock.notifyAll();
}
});
public static void nailMain(final NGContext context) {
long runTimeout = Long.MAX_VALUE;
String[] args = context.getArgs();
if (args.length > 0) {
// first argument is the number of milliseconds to run a command
// if omitted it will never interrupt by itself
try {
runTimeout = Long.parseUnsignedLong(args[0]);
} catch (Exception e) {
}
}
context.addHeartbeatListener(() -> context.out.print("H"));
try {
Object lock = new Object();
AtomicBoolean shutdown = new AtomicBoolean(false);
context.addClientListener(
reason -> {
synchronized (lock) {
// print hashes every second when client is active
while (!shutdown.get() && (System.nanoTime() - start) < run_nanos) {
context.out.print("S");
lock.wait(1000);
}
shutdown.set(true);
lock.notifyAll();
}
} catch (InterruptedException ignored) {
System.exit(42);
});
context.addHeartbeatListener(() -> context.out.print("H"));
synchronized (lock) {
if (!shutdown.get()) {
lock.wait(runTimeout);
}
System.exit(0);
}
} catch (InterruptedException ignored) {
System.exit(42);
}
}
\ No newline at end of file
System.exit(0);
}
}
/*
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,19 +13,18 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.martiansoftware.nailgun.examples;
package com.facebook.nailgun.examples;
/**
* A truly amazing program that must be seen to be believed.
*
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
/*
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -13,28 +13,26 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.martiansoftware.nailgun.examples;
package com.facebook.nailgun.examples;
import com.martiansoftware.nailgun.NGContext;
import com.facebook.nailgun.NGContext;
/**
* Prompts the user for input using a JOptionPane, and displays the
* result to the client's stdout. If the user clicks "cancel", the
* client exits with exit code 1.
*
* Prompts the user for input using a JOptionPane, and displays the result to the client's stdout.
* If the user clicks "cancel", the client exits with exit code 1.
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class Prompt {
public static void nailMain(NGContext context) {
String result = javax.swing.JOptionPane.showInputDialog(null, "Input:");
if (result == null) {
context.exit(1);
} else {
context.out.println(result);
}
}
public static void nailMain(NGContext context) {
String result = javax.swing.JOptionPane.showInputDialog(null, "Input:");
if (result == null) {
context.exit(1);
} else {
context.out.println(result);
}
}
}
/*
Copyright 2004-2012, Martian Software, Inc.
Copyright 2017-Present Facebook, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.facebook.nailgun.examples;
import com.facebook.nailgun.NGContext;
import com.facebook.nailgun.NGServer;
/**
* Provides some nice command-line stack operations. This nail must have the aliases "push" and
* "pop" associated with it in order to work properly.
*
* <p>If the "push" command is used, each argument on the command line is pushed onto the stack (in
* order) and the program returns immediately.
*
* <p>If the "pop" command is used, the top item on the stack is displayed to the client's stdout.
* If the stack is empty, the client will block until another process calls push. If the nailgun
* server is shutdown while pop is blocking, pop will cause the client to exit with exit code 1.
* This is thread-safe: you can have multiple clients waiting on "pop" and only one of them
* (determined by the VM and the magic of synchronization) will receive any one pushed item.
*
* @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
*/
public class Stack {
private static java.util.Stack sharedStack = new java.util.Stack();
private static boolean done = false;
public static void nailShutdown(NGServer server) {
done = true;
synchronized (sharedStack) {
sharedStack.notifyAll();
}
}
public static void nailMain(NGContext context) throws InterruptedException {
if (context.getCommand().equals("push")) {
synchronized (sharedStack) {
for (String arg: context.getArgs()) {
sharedStack.push(arg);
}
sharedStack.notifyAll();
context.exit(0);
return;
}
}
if (context.getCommand().equals("pop")) {
int exitCode = 1;
synchronized (sharedStack) {
while (!done && (sharedStack.size() == 0)) {
sharedStack.wait();
}
if (sharedStack.size() > 0) {
context.out.println(sharedStack.pop());
exitCode = 0;
}
}
context.exit(exitCode);
return;
}
}
}