pax_global_header00006660000000000000000000000064120567747300014525gustar00rootroot0000000000000052 comment=cd3ff159548c2ba60fcd1f3341611b1bf6e8997b nailgun-nailgun-all-0.9.1/000077500000000000000000000000001205677473000153725ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/.gitignore000066400000000000000000000000721205677473000173610ustar00rootroot00000000000000/nailgun-server/target/ /nailgun-examples/target/ /target/nailgun-nailgun-all-0.9.1/Makefile000066400000000000000000000017731205677473000170420ustar00rootroot00000000000000# This Makefile has only been tested on linux. It uses # MinGW32 to cross-compile for windows. To install and # configure MinGW32 on linux, see http://www.mingw.org # This is where the mingw32 compiler exists in Ubuntu 8.04. # Your compiler location may vary. WIN32_CC=/usr/bin/i586-mingw32msvc-gcc CC=gcc CFLAGS=-Wall -pedantic -s -O3 SRCDIR=nailgun-client ng: ${SRCDIR}/ng.c @echo "Building ng client. To build a Windows binary, type 'make ng.exe'" ${CC} ${CFLAGS} -o ng ${SRCDIR}/ng.c install: ng install ng /usr/local/bin ng.exe: ${SRCDIR}/ng.c ${WIN32_CC} -o 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. clean: @echo "" @echo "If you have a Windows binary, 'make clean' won't delete it." @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 nailgun-nailgun-all-0.9.1/README.md000066400000000000000000000012211205677473000166450ustar00rootroot00000000000000nailgun ======= 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. The server and examples are built using maven. From the project directory, "mvn clean install" will do it. The client is built using make. From the project directory, "make && sudo make install" will do it. To create the windows client you will additionally need to "make ng.exe". For more information, see [the nailgun website](http://martiansoftware.com/nailgun/). nailgun-nailgun-all-0.9.1/nailgun-client/000077500000000000000000000000001205677473000203035ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-client/ng.c000066400000000000000000000464601205677473000210650ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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. */ /** * @author Marty Lamb * @author Pete Kirkham (Win32 port) */ #ifdef WIN32 #include #include #else #include #include #include #include #include #endif #include #include #include #include #include #define NAILGUN_VERSION "0.9.0" #define BUFSIZE (2048) #ifdef WIN32 HANDLE NG_STDIN_FILENO; HANDLE NG_STDOUT_FILENO; HANDLE NG_STDERR_FILENO; #define FILE_SEPARATOR '\\' #define MSG_WAITALL 0 #else #define NG_STDIN_FILENO STDIN_FILENO #define NG_STDOUT_FILENO STDOUT_FILENO #define NG_STDERR_FILENO STDERR_FILENO #define FILE_SEPARATOR '/' typedef int HANDLE; typedef unsigned int SOCKET; /* buffer used for reading an writing chunk data */ char buf[BUFSIZE]; #endif #ifndef MIN #define MIN(a,b) ((a> 24) & 0xff; header[1] = (size >> 16) & 0xff; header[2] = (size >> 8) & 0xff; header[3] = size & 0xff; header[4] = chunkType; sendAll(nailgunsocket, header, CHUNK_HEADER_LEN); } /** * Sends the contents of the specified file as a long argument (--nailgun-filearg) * This is sent as one or more chunks of type CHUNK_LONGARG. The end of the argument * is indicated by an empty chunk. * * @param filename the name of the file to send. * @return nonzero on failure */ int sendFileArg(char *filename) { int i, f; if ((f = open(filename, O_RDONLY)) < 0) { perror("--nailgun-filearg"); return 1; } i = read(f, buf, BUFSIZE); while (i > 0) { sendHeader(i, CHUNKTYPE_LONGARG); sendAll(nailgunsocket, buf, i); i = read(f, buf, BUFSIZE); } if (i < 0) { perror("--nailgun-filearg"); return 1; } sendHeader(0, CHUNKTYPE_LONGARG); close(f); return 0; } /** * Sends a null-terminated string with the specified chunk type. * * @param chunkType the chunk type identifier * @param text the null-terminated string to send */ void sendText(char chunkType, char *text) { int len = text ? strlen(text) : 0; sendHeader(len, chunkType); sendAll(nailgunsocket, text, len); } /** * Exits the client if the nailgun server ungracefully shut down the connection. */ void handleSocketClose() { cleanUpAndExit(NAILGUN_CONNECTION_BROKEN); } /** * Receives len bytes from the nailgun socket and copies them to the specified file descriptor. * Used to route data to stdout or stderr on the client. * * @param destFD the destination file descriptor (stdout or stderr) * @param len the number of bytes to copy */ void recvToFD(HANDLE destFD, char *buf, unsigned long len) { unsigned long bytesRead = 0; int bytesCopied; while (bytesRead < len) { unsigned long bytesRemaining = len - bytesRead; int bytesToRead = (BUFSIZE < bytesRemaining) ? BUFSIZE : bytesRemaining; int thisPass = 0; thisPass = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL); if (thisPass < bytesToRead) handleSocketClose(); bytesRead += thisPass; bytesCopied = 0; while(bytesCopied < thisPass) { #ifdef WIN32 DWORD thisWrite = 0; WriteFile(destFD, buf + bytesCopied, thisPass - bytesCopied, &thisWrite, NULL); if (thisWrite < 0) { break; } bytesCopied += thisWrite; #else bytesCopied += write(destFD, buf + bytesCopied, thisPass - bytesCopied); #endif } } } /** * Processes an exit chunk from the server. This is just a string * containing the exit code in decimal format. It should fit well * within our buffer, so assume that it does. * * @param len the current length of the buffer containing the exit code. */ void processExit(char *buf, unsigned long len) { int exitcode; int bytesToRead = (BUFSIZE - 1 < len) ? BUFSIZE - 1 : len; int bytesRead = recv(nailgunsocket, buf, bytesToRead, MSG_WAITALL); if (bytesRead < 0) { handleSocketClose(); } buf[bytesRead] = 0; exitcode = atoi(buf); cleanUpAndExit(exitcode); } /** * Sends len bytes from buf to the nailgun server in a stdin chunk. * * @param buf the bytes to send * @param len the number of bytes to send */ void sendStdin(char *buf, unsigned int len) { sendHeader(len, CHUNKTYPE_STDIN); sendAll(nailgunsocket, buf, len); } /** * Sends a stdin-eof chunk to the nailgun server */ void processEof() { sendHeader(0, CHUNKTYPE_STDIN_EOF); } #ifdef WIN32 /** * Thread main for reading from stdin and sending */ DWORD WINAPI processStdin (LPVOID lpParameter) { /* buffer used for reading and sending stdin chunks */ char wbuf[BUFSIZE]; for (;;) { DWORD numberOfBytes = 0; if (!ReadFile(NG_STDIN_FILENO, wbuf, BUFSIZE, &numberOfBytes, NULL)) { if (numberOfBytes != 0) { handleError(); } } if (numberOfBytes > 0) { sendStdin(wbuf, numberOfBytes); } else { processEof(); break; } } return 0; } #else /** * Reads from stdin and transmits it to the nailgun server in a stdin chunk. * Sends a stdin-eof chunk if necessary. * * @return zero if eof has been reached. */ int processStdin() { int bytesread = read(STDIN_FILENO, buf, BUFSIZE); if (bytesread > 0) { sendStdin(buf, bytesread); } else if (bytesread == 0) { processEof(); } return(bytesread); } #endif #ifdef WIN32 /** * Initialise Windows sockets */ void initSockets () { WSADATA win_socket_data; /* required to initialise winsock */ WSAStartup(2, &win_socket_data); } #endif #ifdef WIN32 /** * Initialise the asynchronous io. */ void initIo () { /* create non-blocking console io */ AllocConsole(); NG_STDIN_FILENO = GetStdHandle(STD_INPUT_HANDLE); NG_STDOUT_FILENO = GetStdHandle(STD_OUTPUT_HANDLE); NG_STDERR_FILENO = GetStdHandle(STD_ERROR_HANDLE); } #endif #ifdef WIN32 /** * Initialise the asynchronous io. */ void winStartInput () { SECURITY_ATTRIBUTES securityAttributes; DWORD threadId = 0; securityAttributes.bInheritHandle = TRUE; securityAttributes.lpSecurityDescriptor = NULL; securityAttributes.nLength = 0; if (!CreateThread(&securityAttributes, 0, &processStdin, NULL, 0, &threadId)) { handleError(); } } #endif /** * Processes data from the nailgun server. */ void processnailgunstream() { /*for (;;) {*/ int bytesRead = 0; unsigned long len; char chunkType; bytesRead = recv(nailgunsocket, buf, CHUNK_HEADER_LEN, MSG_WAITALL); if (bytesRead < CHUNK_HEADER_LEN) { handleSocketClose(); } len = ((buf[0] << 24) & 0xff000000) | ((buf[1] << 16) & 0x00ff0000) | ((buf[2] << 8) & 0x0000ff00) | ((buf[3]) & 0x000000ff); chunkType = buf[4]; switch(chunkType) { case CHUNKTYPE_STDOUT: recvToFD(NG_STDOUT_FILENO, buf, len); break; case CHUNKTYPE_STDERR: recvToFD(NG_STDERR_FILENO, buf, len); break; case CHUNKTYPE_EXIT: processExit(buf, len); break; case CHUNKTYPE_STARTINPUT: if (!startedInput) { #ifdef WIN32 winStartInput(); #endif startedInput = 1; } break; default: fprintf(stderr, "Unexpected chunk type %d ('%c')\n", chunkType, chunkType); cleanUpAndExit(NAILGUN_UNEXPECTED_CHUNKTYPE); } /*}*/ } /** * Trims any path info from the beginning of argv[0] to determine * the name used to launch the client. * * @param s argv[0] */ char *shortClientName(char *s) { char *result = strrchr(s, FILE_SEPARATOR); return ((result == NULL) ? s : result + 1); } /** * Returns true if the specified string is the name of the nailgun * client. The comparison is made case-insensitively for windows. * * @param s the program name to check */ int isNailgunClientName(char *s) { #ifdef WIN32 return (!strcasecmp(s, NAILGUN_CLIENT_NAME) || !strcasecmp(s, NAILGUN_CLIENT_NAME_EXE)); #else return(!(strcmp(s, NAILGUN_CLIENT_NAME))); #endif } /** * Displays usage info and bails */ void usage(int exitcode) { fprintf(stderr, "NailGun v%s\n\n", NAILGUN_VERSION); fprintf(stderr, "Usage: ng class [--nailgun-options] [args]\n"); fprintf(stderr, " (to execute a class)\n"); fprintf(stderr, " or: ng alias [--nailgun-options] [args]\n"); fprintf(stderr, " (to execute an aliased class)\n"); fprintf(stderr, " or: alias [--nailgun-options] [args]\n"); fprintf(stderr, " (to execute an aliased class, where \"alias\"\n"); fprintf(stderr, " is both the alias for the class and a symbolic\n"); fprintf(stderr, " link to the ng client)\n\n"); fprintf(stderr, "where options include:\n"); fprintf(stderr, " --nailgun-D= set/override a client environment variable\n"); fprintf(stderr, " --nailgun-version print product version and exit\n"); fprintf(stderr, " --nailgun-showversion print product version and continue\n"); fprintf(stderr, " --nailgun-server to specify the address of the nailgun server\n"); fprintf(stderr, " (default is NAILGUN_SERVER environment variable\n"); fprintf(stderr, " if set, otherwise localhost)\n"); fprintf(stderr, " --nailgun-port to specify the port of the nailgun server\n"); fprintf(stderr, " (default is NAILGUN_PORT environment variable\n"); fprintf(stderr, " if set, otherwise 2113)\n"); fprintf(stderr, " --nailgun-filearg FILE places the entire contents of FILE into the\n"); fprintf(stderr, " next argument, which is interpreted as a string\n"); fprintf(stderr, " using the server's default character set. May be\n"); fprintf(stderr, " specified more than once.\n"); fprintf(stderr, " --nailgun-help print this message and exit\n"); cleanUpAndExit(exitcode); } int main(int argc, char *argv[], char *env[]) { int i; struct sockaddr_in server_addr; char *nailgun_server; /* server as specified by user */ char *nailgun_port; /* port as specified by user */ char *cwd; u_short port; /* port */ struct hostent *hostinfo; char *cmd; int firstArgIndex; /* the first argument _to pass to the server_ */ #ifndef WIN32 fd_set readfds; int eof = 0; #endif #ifdef WIN32 initSockets(); #endif /* start with environment variable. default to localhost if not defined. */ nailgun_server = getenv("NAILGUN_SERVER"); if (nailgun_server == NULL) { nailgun_server = "127.0.0.1"; } /* start with environment variable. default to normal nailgun port if not defined */ nailgun_port = getenv("NAILGUN_PORT"); if (nailgun_port == NULL) { nailgun_port = NAILGUN_PORT_DEFAULT; } /* look at the command used to launch this program. if it was "ng", then the actual command to issue to the server must be specified as another argument. if it wasn't ng, assume that the desired command name was symlinked to ng in the user's filesystem, and use the symlink name (without path info) as the command for the server. */ cmd = shortClientName(argv[0]); if (isNailgunClientName(cmd)) { cmd = NULL; } /* if executing just the ng client with no arguments or -h|--help, then display usage and exit. Don't handle -h|--help if a command other than ng or ng.exe was used, since the appropriate nail should then handle --help. */ if (cmd == NULL && (argc == 1 || (argc == 2 && strcmp("--help", argv[1]) == 0) || (argc == 2 && strcmp("-h", argv[1]) == 0))) usage(0); firstArgIndex = 1; /* quite possibly the lamest commandline parsing ever. look for the two args we care about (--nailgun-server and --nailgun-port) and NULL them and their parameters after reading them if found. later, when we send args to the server, skip the null args. */ for (i = 1; i < argc; ++i) { if (!strcmp("--nailgun-server", argv[i])) { if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS); nailgun_server = argv[i + 1]; argv[i] = argv[i + 1] = NULL; ++i; } else if(!strcmp("--nailgun-port", argv[i])) { if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS); nailgun_port = argv[i + 1]; argv[i] = argv[i + 1]= NULL; ++i; } else if (!strcmp("--nailgun-filearg", argv[i])) { /* just verify usage here. do the rest when sending args. */ if (i == argc - 1) usage (NAILGUN_BAD_ARGUMENTS); } else if (!strcmp("--nailgun-version", argv[i])) { printf("NailGun client version %s\n", NAILGUN_VERSION); cleanUpAndExit(0); } else if (!strcmp("--nailgun-showversion", argv[i])) { printf("NailGun client version %s\n", NAILGUN_VERSION); argv[i] = NULL; } else if (!strcmp("--nailgun-help", argv[i])) { usage(0); } else if (cmd == NULL) { cmd = argv[i]; firstArgIndex = i + 1; } } /* if there's no command, we should only display usage info if the version number was not displayed. */ if (cmd == NULL) { usage(NAILGUN_BAD_ARGUMENTS); } /* jump through a series of connection hoops */ hostinfo = gethostbyname(nailgun_server); if (hostinfo == NULL) { fprintf(stderr, "Unknown host: %s\n", nailgun_server); cleanUpAndExit(NAILGUN_CONNECT_FAILED); } port = atoi(nailgun_port); if ((nailgunsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); cleanUpAndExit(NAILGUN_SOCKET_FAILED); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; memset(&(server_addr.sin_zero), '\0', 8); if (connect(nailgunsocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); cleanUpAndExit(NAILGUN_CONNECT_FAILED); } /* ok, now we're connected. first send all of the command line arguments for the server, if any. remember that we may have marked some arguments NULL if we read them to specify the nailgun server and/or port */ for(i = firstArgIndex; i < argc; ++i) { if (argv[i] != NULL) { if (!strcmp("--nailgun-filearg", argv[i])) { sendFileArg(argv[++i]); } else sendText(CHUNKTYPE_ARG, argv[i]); } } /* now send environment */ sendText(CHUNKTYPE_ENV, NAILGUN_FILESEPARATOR); sendText(CHUNKTYPE_ENV, NAILGUN_PATHSEPARATOR); for(i = 0; env[i]; ++i) { sendText(CHUNKTYPE_ENV, env[i]); } /* now send the working directory */ cwd = getcwd(NULL, 0); sendText(CHUNKTYPE_DIR, cwd); free(cwd); /* and finally send the command. this marks the point at which streams are linked between client and server. */ sendText(CHUNKTYPE_CMD, cmd); /* initialise the std-* handles and the thread to send stdin to the server */ #ifdef WIN32 initIo(); #endif /* stream forwarding loop */ while(1) { #ifndef WIN32 FD_ZERO(&readfds); /* don't select on stdin if we've already reached its end */ if (startedInput && !eof) { FD_SET(NG_STDIN_FILENO, &readfds); } FD_SET(nailgunsocket, &readfds); if (select (nailgunsocket + 1, &readfds, NULL, NULL, NULL) == -1) { perror("select"); } if (FD_ISSET(nailgunsocket, &readfds)) { #endif processnailgunstream(); #ifndef WIN32 } else if (FD_ISSET(NG_STDIN_FILENO, &readfds)) { if (!processStdin()) { FD_CLR(NG_STDIN_FILENO, &readfds); eof = 1; } } #endif } /* normal termination is triggered by the server, and so occurs in processExit(), above */ } nailgun-nailgun-all-0.9.1/nailgun-examples/000077500000000000000000000000001205677473000206435ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/pom.xml000066400000000000000000000024001205677473000221540ustar00rootroot00000000000000 4.0.0 com.martiansoftware nailgun-examples jar nailgun-examples 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. This project contains the EXAMPLE CODE ONLY. http://martiansoftware.com/nailgun com.martiansoftware nailgun-all 0.9.1 com.martiansoftware nailgun-server ${version} compile nailgun-nailgun-all-0.9.1/nailgun-examples/src/000077500000000000000000000000001205677473000214325ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/000077500000000000000000000000001205677473000223565ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/000077500000000000000000000000001205677473000232775ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/000077500000000000000000000000001205677473000240555ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/000077500000000000000000000000001205677473000272635ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/000077500000000000000000000000001205677473000307205ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/000077500000000000000000000000001205677473000325365ustar00rootroot00000000000000DumpAll.java000066400000000000000000000036631205677473000346700ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; import java.util.Iterator; import java.util.TreeSet; import com.martiansoftware.nailgun.NGContext; /** * Simply displays command line arguments to System.out. * * @author Marty Lamb */ 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)); } } } Echo.java000066400000000000000000000020321205677473000341750ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; /** * Echos everything it reads from System.in to System.out. * * @author Marty Lamb */ 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); } } } Exit.java000066400000000000000000000016371205677473000342420ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; 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) {} } System.exit(exitCode); } }Hash.java000066400000000000000000000076461205677473000342220ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; import java.security.MessageDigest; import java.security.Provider; import java.security.Security; import java.util.Iterator; import java.util.Set; import com.martiansoftware.nailgun.NGContext; /** * 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 Marty Lamb */ 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". * * 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 java.util.TreeSet(); // All all providers Provider[] providers = Security.getProviders(); for (int i=0; i> 4) & 0x0f]); buf.append(HEXCHARS[result[i] & 0x0f]); } context.out.println(buf); } } } HelloWorld.java000066400000000000000000000016161205677473000354010ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; /** * A truly amazing program that must be seen to be believed. * * @author Marty Lamb */ public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } } Prompt.java000066400000000000000000000022551205677473000346070ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; import com.martiansoftware.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. * * @author Marty Lamb */ 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); } } } Stack.java000066400000000000000000000047641205677473000344020ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; import com.martiansoftware.nailgun.NGContext; import com.martiansoftware.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. * * If the "push" command is used, each argument on the command line * is pushed onto the stack (in order) and the program returns * immediately. * * 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 Marty Lamb */ 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) { String[] args = context.getArgs(); for (int i = 0; i < args.length; ++i) { sharedStack.push(args[i]); } sharedStack.notifyAll(); context.exit(0); return; } } else 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; } } } ThreadTest.java000066400000000000000000000030311205677473000353660ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-examples/src/main/java/com/martiansoftware/nailgun/examples/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.examples; /** * A very silly test to verify that the System.in/out/err overrides are * inherited by child threads. A bunch of numbers and thread ids are displayed * to the client's stdout. The important thing is that all threads launched * by the nail are writing to the same client. * * @author Marty Lamb */ public class ThreadTest implements Runnable { private String name = null; public ThreadTest(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; ++i) { System.out.println(name + ": " + i); try {Thread.sleep(100);} catch (Throwable t){} } } public static void main(String[] args) { for (int i = 0; i < 3; ++i) { Thread t = new Thread(new ThreadTest("T" + i)); t.start(); System.out.println("Started number " + i); } try {Thread.sleep(2000);} catch (Throwable t) {} System.out.println("Done."); } } nailgun-nailgun-all-0.9.1/nailgun-server/000077500000000000000000000000001205677473000203335ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/pom.xml000066400000000000000000000031221205677473000216460ustar00rootroot00000000000000 4.0.0 com.martiansoftware nailgun-server jar nailgun-server 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. This project contains the SERVER ONLY. http://martiansoftware.com/nailgun com.martiansoftware nailgun-all 0.9.1 org.apache.maven.plugins maven-jar-plugin 2.4 true com.martiansoftware.nailgun.NGServer nailgun-nailgun-all-0.9.1/nailgun-server/src/000077500000000000000000000000001205677473000211225ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/000077500000000000000000000000001205677473000220465ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/000077500000000000000000000000001205677473000227675ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/000077500000000000000000000000001205677473000235455ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/000077500000000000000000000000001205677473000267535ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/000077500000000000000000000000001205677473000304105ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/Alias.java000066400000000000000000000063501205677473000323100ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; /** * Provides a means to map memorable, short names to classes in order * to make the issuing of commands more convenient. For example, an * Alias can map the "mycommand" command to the com.yourdomain.yourpackage.YourClass * class. Obviously, it's a lot easier to type "ng mycommand" than the fully * qualified class name. * * @author Marty Lamb */ public class Alias implements Comparable { /** * The alias name */ private String name; /** * The alias description (may be used to provide help to users) */ private String description; /** * The class providing a main() or nailMain() method */ private Class clazz; /** * Creates a new Alias with the specified properties. * @param name the alias name (short command) * @param description a description of the command * @param clazz the class implementing the command */ public Alias(String name, String description, Class clazz) { if (name == null) throw (new IllegalArgumentException("Alias must have a name.")); this.name = name.trim(); if (this.name.length() == 0) throw (new IllegalArgumentException("Alias must have a name.")); if (clazz == null) throw (new IllegalArgumentException("Alias must have an associated class.")); this.description = description; this.clazz = clazz; } /** * Returns the Class object providing a static main() or nailMain() method * for this command. * @return the Class object providing a static main() or nailMain() method * for this command. */ public Class getAliasedClass() { return(clazz); } /** * Returns the name of the aliased command * @return the name of the aliased command */ public String getName() { return (name); } /** * Returns a description for the aliased command * @return a description for the aliased command */ public String getDescription() { return (description); } /** * @see Object#hashCode() */ public int hashCode() { return (name.hashCode()); } /** * Checks whether two Aliases have the same name. Does not * compare any other fields. * @param o the other Alias to check * @return true if the specified Alias has the same name as this Alias. */ public boolean equals(Object o) { return (compareTo(o) == 0); } /** * Compares Alias names - no other fields are compared. * @see Comparable#compareTo(Object) */ public int compareTo(Object o) { return (name.compareTo(((Alias) o).getName())); } } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/AliasManager.java000066400000000000000000000103461205677473000336030ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; /** * An AliasManager is used to store and lookup command Aliases by name. See Alias for more details. * * @author Marty Lamb */ public class AliasManager { /** * actual alias storage */ private Map aliases; /** * Creates a new AliasManager, populating it with default Aliases. */ public AliasManager() { aliases = new java.util.HashMap(); try { Properties props = new Properties(); props.load(getClass().getClassLoader().getResourceAsStream("com/martiansoftware/nailgun/builtins/builtins.properties")); loadFromProperties(props); } catch (java.io.IOException e) { System.err.println("Unable to load builtins.properties: " + e.getMessage()); } } /** * Loads Aliases from a java.util.Properties file located at the specified * URL. The properties must be of the form: *
[alias name]=[fully qualified classname]
each of * which may have an optional *
[alias name].desc=[alias description]
* * For example, to create an alias called " * myprog" for class * com.mydomain.myapp.MyProg, the following properties would be * defined: * *
myprog=com.mydomain.myapp.MyProg
     *myprog.desc=Runs my program.
     * 
* * @param properties the Properties to load. */ public void loadFromProperties(java.util.Properties properties) { for (Iterator i = properties.keySet().iterator(); i.hasNext();) { String key = (String) i.next(); if (!key.endsWith(".desc")) { try { Class clazz = Class.forName(properties.getProperty(key)); String desc = properties.getProperty(key + ".desc", ""); addAlias(new Alias(key, desc, clazz)); } catch (ClassNotFoundException e) { System.err.println("Unable to locate class " + properties.getProperty(key)); } } } } /** * Adds an Alias, replacing any previous entries with the same name. * * @param alias the Alias to add */ public void addAlias(Alias alias) { synchronized (aliases) { aliases.put(alias.getName(), alias); } } /** * Returns a Set that is a snapshot of the Alias list. Modifications to this * Set will not impact the AliasManager in any way. * * @return a Set that is a snapshot of the Alias list. */ public Set getAliases() { Set result = new java.util.TreeSet(); synchronized (aliases) { result.addAll(aliases.values()); } return (result); } /** * Removes the Alias with the specified name from the AliasManager. If no * such Alias exists in this AliasManager, this method has no effect. * * @param aliasName the name of the Alias to remove */ public void removeAlias(String aliasName) { synchronized (aliases) { aliases.remove(aliasName); } } /** * Returns the Alias with the specified name * * @param aliasName the name of the Alias to retrieve * @return the requested Alias, or null if no such Alias is defined in this * AliasManager. */ public Alias getAlias(String aliasName) { return ((Alias) aliases.get(aliasName)); } } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGConstants.java000066400000000000000000000065101205677473000334560ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.util.Properties; /** * Just a simple holder for various NailGun-related contants. * * @author Marty Lamb */ public class NGConstants { /** * The default NailGun port (2113) */ public static final int DEFAULT_PORT = 2113; /** * The exit code sent to clients if an exception occurred on the server */ public static final int EXIT_EXCEPTION = 899; /** * The exit code sent to clients if an invalid command is sent */ public static final int EXIT_NOSUCHCOMMAND = 898; /** * Chunk type marker for command line arguments */ public static final byte CHUNKTYPE_ARGUMENT = 'A'; /** * Chunk type marker for client environment variables */ public static final byte CHUNKTYPE_ENVIRONMENT = 'E'; /** * Chunk type marker for the command (alias or class) */ public static final byte CHUNKTYPE_COMMAND = 'C'; /** * Chunk type marker for client working directory */ public static final byte CHUNKTYPE_WORKINGDIRECTORY = 'D'; /** * Chunk type marker for stdin */ public static final byte CHUNKTYPE_STDIN = '0'; /** * Chunk type marker for the end of stdin */ public static final byte CHUNKTYPE_STDIN_EOF = '.'; /** * Chunk type marker for stdout */ public static final byte CHUNKTYPE_STDOUT = '1'; /** * Chunk type marker for stderr */ public static final byte CHUNKTYPE_STDERR = '2'; /** * Chunk type marker for client exit chunks */ public static final byte CHUNKTYPE_EXIT = 'X'; /** * Chunk type marker for a "startinput" chunk. This chunk type is sent from * the server to the client and indicates that the client should begin * sending stdin to the server. It is automatically sent the first time the * client's inputstream is read. */ public static final byte CHUNKTYPE_STARTINPUT = 'S'; /** * Server version number */ public static final String VERSION; /** * Returns the Nailgun version number * * @return the Nailgun version number */ public static String getVersion() { String result = "[unknown]"; return result; } /** * Loads the version number from a file generated by Maven. */ static { Properties props = new Properties(); try { props.load(NGConstants.class.getResourceAsStream("/META-INF/maven/com.martiansoftware/nailgun-server/pom.properties")); } catch (Exception e) { System.err.println("Unable to load nailgun-version.properties."); } VERSION = props.getProperty("version", "[UNKNOWN]"); } } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGContext.java000066400000000000000000000153441205677473000331330ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.InputStream; import java.io.PrintStream; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Properties; /** *

Provides quite a bit of potentially useful information to classes * specifically written for NailGun. The NailGun server itself, its * AliasManager, the remote client's environment variables, and other * information is available via this class. For all intents and purposes, * the NGContext represents a single connection from a NailGun client.

* * If a class is written with a * *

 * public static void nailMain(NGContext context)
 * 
* * method, that method will be called by NailGun instead of the traditional * main(String[]) method normally used for programs. A fully populated NGContext * object will then be provided to nailMain(). * * @author Marty Lamb */ public class NGContext { /** * The remote host's environment variables */ private Properties remoteEnvironment = null; /** * The remote host's address */ private InetAddress remoteHost = null; /** * The port on the remote host that is communicating with NailGun */ private int remotePort = 0; /** * Command line arguments for the nail */ private String[] args = null; /** * A stream to which a client exit code can be printed */ private PrintStream exitStream = null; /** * The NGServer that accepted this connection */ private NGServer server = null; /** * The command that was issued for this connection */ private String command = null; private String workingDirectory = null; /** * The client's stdin */ public InputStream in = null; /** * The client's stdout */ public PrintStream out = null; /** * The client's stderr */ public PrintStream err = null; /** * Creates a new, empty NGContext */ NGContext() { super(); } void setExitStream(PrintStream exitStream) { this.exitStream = exitStream; } void setPort(int remotePort) { this.remotePort = remotePort; } void setCommand(String command) { this.command = command; } /** * Returns the command that was issued by the client (either an alias or the name of a class). * This allows multiple aliases to point to the same class but result in different behaviors. * @return the command issued by the client */ public String getCommand() { return (command); } void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; } /** * Returns the current working directory of the client, as reported by the client. * This is a String that will use the client's File.separator ('/' or '\'), * which may differ from the separator on the server. * @return the current working directory of the client */ public String getWorkingDirectory() { return (workingDirectory); } void setEnv(Properties remoteEnvironment) { this.remoteEnvironment = remoteEnvironment; } void setInetAddress(InetAddress remoteHost) { this.remoteHost = remoteHost; } void setArgs(String[] args) { this.args = args; } void setNGServer(NGServer server) { this.server = server; } /** * Returns a java.util.Properties object containing a copy * of the client's environment variables * @see java.util.Properties * @return a java.util.Properties object containing a copy * of the client's environment variables */ public Properties getEnv() { return (remoteEnvironment); } /** * Returns the file separator ('/' or '\\') used by the client's os. * @return the file separator ('/' or '\\') used by the client's os. */ public String getFileSeparator() { return (remoteEnvironment.getProperty("NAILGUN_FILESEPARATOR")); } /** * Returns the path separator (':' or ';') used by the client's os. * @return the path separator (':' or ';') used by the client's os. */ public String getPathSeparator() { return (remoteEnvironment.getProperty("NAILGUN_PATHSEPARATOR")); } /** * Returns the address of the client at the other side of this connection. * @return the address of the client at the other side of this connection. */ public InetAddress getInetAddress() { return (remoteHost); } /** * Returns the command line arguments for the command * implementation (nail) on the server. * @return the command line arguments for the command * implementation (nail) on the server. */ public String[] getArgs() { return (args); } /** * Returns the NGServer that accepted this connection * @return the NGServer that accepted this connection */ public NGServer getNGServer() { return (server); } /** * Sends an exit command with the specified exit code to * the client. The client will exit immediately with * the specified exit code; you probably want to return * from nailMain immediately after calling this. * * @param exitCode the exit code with which the client * should exit */ public void exit(int exitCode) { exitStream.println(exitCode); } /** * Returns the port on the client connected to the NailGun * server. * @return the port on the client connected to the NailGun * server. */ public int getPort() { return (remotePort); } /** * Throws a java.lang.SecurityException if the client is not * connected via the loopback address. */ public void assertLoopbackClient() { if (!getInetAddress().isLoopbackAddress()) { throw (new SecurityException("Client is not at loopback address.")); } } /** * Throws a java.lang.SecurityException if the client is not * connected from the local machine. */ public void assertLocalClient() { NetworkInterface iface = null; try { iface = NetworkInterface.getByInetAddress(getInetAddress()); } catch (java.net.SocketException se) { throw (new SecurityException("Unable to determine if client is local. Assuming he isn't.")); } if ((iface == null) && (!getInetAddress().isLoopbackAddress())) { throw (new SecurityException("Client is not local.")); } } }NGExitException.java000066400000000000000000000073461205677473000342230ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* * This class is based upon org.apache.tools.ant.ExitException and is * subject to the following: * * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package com.martiansoftware.nailgun; import java.io.PrintStream; /** * Security exception which wraps an exit status code. * * @author Pete Kirkham */ public class NGExitException extends SecurityException { /** * Status code */ private int status; /** * Constructs an exit exception. * * @param status the status code returned via System.exit() */ public NGExitException(int status) { super("ExitException: status " + status); this.status = status; } /** * A lot of code out there, for example ant's Launcher, runs inside a * try/catch (Throwable) which will squash this exception; most also calll * printStackTrace(), so this re-throws the exception to escape the handling * code. */ public void printStackTrace(PrintStream out) { throw this; } public void reallyPrintStackTrace(PrintStream out) { super.printStackTrace(out); } /** * The status code returned by System.exit() * * @return the status code returned by System.exit() */ public int getStatus() { return status; } }NGInputStream.java000066400000000000000000000066671205677473000337130ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FilterInputStream; import java.io.IOException; /** * A FilterInputStream that is able to read the chunked stdin stream * from a NailGun client. * * @author Marty Lamb */ class NGInputStream extends FilterInputStream { private DataInputStream din; private boolean eof = false; private long remaining = 0; private byte[] oneByteBuffer = null; private final DataOutputStream out; private boolean started = false; /** * Creates a new NGInputStream wrapping the specified InputStream * @param in the InputStream to wrap * @param out the OutputStream to which a STARTINPUT chunk should * be sent prior to the first read. */ public NGInputStream(java.io.InputStream in, DataOutputStream out) { super(in); din = (DataInputStream) this.in; this.out = out; } /** * Reads a NailGun chunk header from the underlying InputStream. * * @throws IOException if thrown by the underlying InputStream, * or if an unexpected NailGun chunk type is encountered. */ private void readHeader() throws IOException { if (eof) return; int hlen = din.readInt(); byte chunkType = din.readByte(); switch(chunkType) { case NGConstants.CHUNKTYPE_STDIN: remaining = hlen; break; case NGConstants.CHUNKTYPE_STDIN_EOF: eof = true; break; default: throw(new IOException("Unknown stream type: " + (char) chunkType)); } } /** * @see java.io.InputStream#available() */ public int available() throws IOException { if (eof) return(0); if (remaining > 0) return (in.available()); return (Math.max(0, in.available() - 5)); } /** * @see java.io.InputStream#markSupported() */ public boolean markSupported() { return (false); } /** * @see java.io.InputStream#read() */ public int read() throws IOException { if (oneByteBuffer == null) oneByteBuffer = new byte[1]; return((read(oneByteBuffer, 0, 1) == -1) ? -1 : (int) oneByteBuffer[0]); } /** * @see java.io.InputStream.read(byte[]) */ public int read(byte[] b) throws IOException { return (read(b, 0, b.length)); } /** * @see java.io.InputStream.read(byte[],offset,length) */ public int read(byte[] b, int offset, int length) throws IOException { if (!started) { synchronized(out) { out.writeInt(0); out.writeByte(NGConstants.CHUNKTYPE_STARTINPUT); out.flush(); started = true; } } if (remaining == 0) readHeader(); if (eof) return(-1); int bytesToRead = Math.min((int) remaining, length); int result = in.read(b, offset, bytesToRead); remaining -= result; return (result); } } NGOutputStream.java000066400000000000000000000042531205677473000341010ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.IOException; /** * Wraps an OutputStream to send writes in NailGun chunks. Because * multiple NGOutputStreams wrap the same OutputStream (that is, * the OutputStream obtained from the Socket connection with * the client), writes are synchronized on the underlying OutputStream. * If this were not the case, write interleaving could completely * break the NailGun protocol. * * @author Marty Lamb */ class NGOutputStream extends java.io.DataOutputStream { private final Object lock; private byte streamCode; /** * Creates a new NGOutputStream wrapping the specified * OutputStream and using the specified Nailgun chunk code. * @param out the OutputStream to wrap * @param streamCode the NailGun chunk code associated with this * stream (i.e., '1' for stdout, '2' for stderr). */ public NGOutputStream(java.io.OutputStream out, byte streamCode) { super(out); this.lock = out; this.streamCode = streamCode; } /** * @see java.io.OutputStream.write(byte[]) */ public void write(byte[] b) throws IOException { write(b, 0, b.length); } /** * @see java.io.OutputStream.write(int) */ public void write(int b) throws IOException { byte[] b2 = {(byte) b}; write(b2, 0, 1); } /** * @see java.io.OutputStream.write(byte[],int,int) */ public void write(byte[] b, int offset, int len) throws IOException { synchronized(lock) { writeInt(len); writeByte(streamCode); out.write(b, offset, len); } flush(); } } NGSecurityManager.java000066400000000000000000000042241205677473000345250ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.security.Permission; import java.io.PrintStream; /** * Security manager which does nothing other than trap * checkExit, or delegate all non-deprecated methods to * a base manager. * * @author Pete Kirkham * */ public class NGSecurityManager extends SecurityManager { private static final ThreadLocal EXIT = new InheritableThreadLocal(); final SecurityManager base; /** * Construct an NGSecurityManager with the given base. * @param base the base security manager, or null for no base. */ public NGSecurityManager (SecurityManager base) { this.base = base; } public void checkExit (int status) { if (base != null) { base.checkExit(status); } final PrintStream exit = (PrintStream)EXIT.get(); if (exit != null) { exit.println(status); } throw new NGExitException(status); } public void checkPermission(Permission perm) { if (base != null) { base.checkPermission(perm); } } public void checkPermission(Permission perm, Object context) { if (base != null) { base.checkPermission(perm, context); } } public static void setExit (PrintStream exit) { EXIT.set(exit); } }nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGServer.java000066400000000000000000000437061205677473000327600ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.InputStream; import java.io.PrintStream; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.Iterator; import java.util.Map; import com.martiansoftware.nailgun.builtins.DefaultNail; import java.util.Properties; /** *

Listens for new connections from NailGun clients and launches NGSession * threads to process them.

* *

This class can be run as a standalone server or can be embedded within * larger applications as a means of providing command-line interaction with the * application.

* * @author Marty Lamb */ public class NGServer implements Runnable { /** * Default size for thread pool */ public static final int DEFAULT_SESSIONPOOLSIZE = 10; /** * The address on which to listen, or null to listen on all local addresses */ private InetAddress addr = null; /** * The port on which to listen, or zero to select a port automatically */ private int port = 0; /** * The socket doing the listening */ private ServerSocket serversocket; /** * True if this NGServer has received instructions to shut down */ private boolean shutdown = false; /** * True if this NGServer has been started and is accepting connections */ private boolean running = false; /** * This NGServer's AliasManager, which maps aliases to classes */ private AliasManager aliasManager; /** * If true, fully-qualified classnames are valid commands */ private boolean allowNailsByClassName = true; /** * The default class to use if an invalid alias or classname is specified by * the client. */ private Class defaultNailClass = null; /** * A pool of NGSessions ready to handle client connections */ private NGSessionPool sessionPool = null; /** * System.out at the time of the NGServer's creation */ public final PrintStream out = System.out; /** * System.err at the time of the NGServer's creation */ public final PrintStream err = System.err; /** * System.in at the time of the NGServer's creation */ public final InputStream in = System.in; /** * a collection of all classes executed by this server so far */ private Map allNailStats = null; /** * Remember the security manager we start with so we can restore it later */ private SecurityManager originalSecurityManager = null; /** * Creates a new NGServer that will listen at the specified address and on * the specified port with the specified session pool size. This does * not cause the server to start listening. To do so, create a new * Thread wrapping this * NGServer and start it. * * @param addr the address at which to listen, or * null to bind to all local addresses * @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) { init(addr, port, sessionPoolSize); } /** * Creates a new NGServer that will listen at the specified address and on * the specified port with the default session pool size. This does * not cause the server to start listening. To do so, create a new * Thread wrapping this * NGServer and start it. * * @param addr the address at which to listen, or * null to bind to all local addresses * @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) { init(addr, port, DEFAULT_SESSIONPOOLSIZE); } /** * Creates a new NGServer that will listen on the default port (defined in * 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() { init(null, NGConstants.DEFAULT_PORT, DEFAULT_SESSIONPOOLSIZE); } /** * Sets up the NGServer internals * * @param addr the InetAddress to bind to * @param port the port on which to listen * @param sessionPoolSize the max number of idle sessions allowed by the * pool */ private void init(InetAddress addr, int port, int sessionPoolSize) { this.addr = addr; this.port = port; this.aliasManager = new AliasManager(); allNailStats = new java.util.HashMap(); // allow a maximum of 10 idle threads. probably too high a number // and definitely should be configurable in the future sessionPool = new NGSessionPool(this, sessionPoolSize); } /** * 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 AliasManager). * * @param allowNailsByClassName true iff Nail lookups by classname are * allowed */ public void setAllowNailsByClassName(boolean allowNailsByClassName) { this.allowNailsByClassName = allowNailsByClassName; } /** * Returns a flag that indicates whether Nail lookups by classname are * allowed. If this is false, Nails can only be run via aliases. * * @return a flag that indicates whether Nail lookups by classname are * allowed. */ public boolean allowsNailsByClassName() { return (allowNailsByClassName); } /** * Sets the default class to use for the Nail if no Nails can be found via * alias or classname. (may be * null, in which case NailGun will use its own default) * * @param defaultNailClass the default class to use for the Nail if no Nails * can be found via alias or classname. (may be * null, in which case NailGun will use its own default) */ public void setDefaultNailClass(Class defaultNailClass) { this.defaultNailClass = defaultNailClass; } /** * Returns the default class that will be used if no Nails can be found via * alias or classname. * * @return the default class that will be used if no Nails can be found via * alias or classname. */ public Class getDefaultNailClass() { return ((defaultNailClass == null) ? DefaultNail.class : defaultNailClass); } /** * Returns the current NailStats object for the specified class, creating a * new one if necessary * * @param nailClass the class for which we're gathering stats * @return a NailStats object for the specified class */ private NailStats getOrCreateStatsFor(Class nailClass) { NailStats result = null; synchronized (allNailStats) { result = (NailStats) allNailStats.get(nailClass); if (result == null) { result = new NailStats(nailClass); allNailStats.put(nailClass, result); } } return (result); } /** * Provides a means for an NGSession to register the starting of a nail * execution with the server. * * @param nailClass the nail class that was launched */ void nailStarted(Class nailClass) { NailStats stats = getOrCreateStatsFor(nailClass); stats.nailStarted(); } /** * Provides a means for an NGSession to register the completion of a nails * execution with the server. * * @param nailClass the nail class that finished */ void nailFinished(Class nailClass) { NailStats stats = (NailStats) allNailStats.get(nailClass); stats.nailFinished(); } /** * Returns a snapshot of this NGServer's nail statistics. The result is a * java.util.Map, keyed by class name, with NailStats objects as values. * * @return a snapshot of this NGServer's nail statistics. */ public Map getNailStats() { Map result = new java.util.TreeMap(); synchronized (allNailStats) { for (Iterator i = allNailStats.keySet().iterator(); i.hasNext();) { Class nailclass = (Class) i.next(); result.put(nailclass.getName(), ((NailStats) allNailStats.get(nailclass)).clone()); } } return (result); } /** * Returns the AliasManager in use by this NGServer. * * @return the AliasManager in use by this NGServer. */ public AliasManager getAliasManager() { return (aliasManager); } /** *

Shuts down the server. The server will stop listening and its thread * will finish. Any running nails will be allowed to finish.

* *

Any nails that provide a *

public static void nailShutdown(NGServer)
method * will have this method called with this NGServer as its sole * parameter.

* * @param exitVM if true, this method will also exit the JVM after calling * nailShutdown() on any nails. This may prevent currently running nails * from exiting gracefully, but may be necessary in order to perform some * tasks, such as shutting down any AWT or Swing threads implicitly launched * by your nails. */ public void shutdown(boolean exitVM) { synchronized (this) { if (shutdown) { return; } shutdown = true; } try { serversocket.close(); } catch (Throwable toDiscard) { } sessionPool.shutdown(); Class[] argTypes = new Class[1]; argTypes[0] = NGServer.class; Object[] argValues = new Object[1]; argValues[0] = this; // make sure that all aliased classes have associated nailstats // so they can be shut down. for (Iterator i = getAliasManager().getAliases().iterator(); i.hasNext();) { Alias alias = (Alias) i.next(); getOrCreateStatsFor(alias.getAliasedClass()); } synchronized (allNailStats) { for (Iterator i = allNailStats.values().iterator(); i.hasNext();) { NailStats ns = (NailStats) i.next(); Class nailClass = ns.getNailClass(); // yes, I know this is lazy, relying upon the exception // to handle the case of no nailShutdown method. try { Method nailShutdown = nailClass.getMethod("nailShutdown", argTypes); nailShutdown.invoke(null, argValues); } catch (Throwable toDiscard) { } } } // restore system streams System.setIn(in); System.setOut(out); System.setErr(err); System.setSecurityManager(originalSecurityManager); if (exitVM) { System.exit(0); } } /** * Returns true iff the server is currently running. * * @return true iff the server is currently running. */ public boolean isRunning() { return (running); } /** * Returns the port on which this server is (or will be) listening. * * @return the port on which this server is (or will be) listening. */ public int getPort() { return ((serversocket == null) ? port : serversocket.getLocalPort()); } /** * Listens for new connections and launches NGSession threads to process * them. */ public void run() { running = true; NGSession sessionOnDeck = null; originalSecurityManager = System.getSecurityManager(); System.setSecurityManager( new NGSecurityManager( originalSecurityManager)); synchronized (System.in) { if (!(System.in instanceof ThreadLocalInputStream)) { System.setIn(new ThreadLocalInputStream(in)); System.setOut(new ThreadLocalPrintStream(out)); System.setErr(new ThreadLocalPrintStream(err)); } } try { if (addr == null) { serversocket = new ServerSocket(port); } else { serversocket = new ServerSocket(port, 0, addr); } while (!shutdown) { sessionOnDeck = sessionPool.take(); Socket socket = serversocket.accept(); sessionOnDeck.run(socket); } } catch (Throwable t) { // if shutdown is called while the accept() method is blocking, // an exception will be thrown that we don't care about. filter // those out. if (!shutdown) { t.printStackTrace(); } } if (sessionOnDeck != null) { sessionOnDeck.shutdown(); } running = false; } private static void usage() { System.err.println("Usage: java com.martiansoftware.nailgun.NGServer"); System.err.println(" or: java com.martiansoftware.nailgun.NGServer port"); System.err.println(" or: java com.martiansoftware.nailgun.NGServer IPAddress"); System.err.println(" or: java com.martiansoftware.nailgun.NGServer IPAddress:port"); } /** * Creates and starts a new * NGServer. A single optional argument is valid, specifying * the port on which this * NGServer should listen. If omitted, * NGServer.DEFAULT_PORT will be used. * * @param args a single optional argument specifying the port on which to * listen. * @throws NumberFormatException if a non-numeric port is specified */ public static void main(String[] args) throws NumberFormatException, UnknownHostException { if (args.length > 1) { usage(); return; } // null server address means bind to everything local InetAddress serverAddress = null; int port = NGConstants.DEFAULT_PORT; // parse the sole command line parameter, which // may be an inetaddress to bind to, a port number, // or an inetaddress followed by a port, separated // by a colon if (args.length != 0) { String[] argParts = args[0].split(":"); String addrPart = null; String portPart = null; if (argParts.length == 2) { addrPart = argParts[0]; portPart = argParts[1]; } else if (argParts[0].indexOf('.') >= 0) { addrPart = argParts[0]; } else { portPart = argParts[0]; } if (addrPart != null) { serverAddress = InetAddress.getByName(addrPart); } if (portPart != null) { port = Integer.parseInt(portPart); } } NGServer server = new NGServer(serverAddress, port, DEFAULT_SESSIONPOOLSIZE); Thread t = new Thread(server); t.setName("NGServer(" + serverAddress + ", " + port + ")"); t.start(); Runtime.getRuntime().addShutdownHook(new NGServerShutdowner(server)); // if the port is 0, it will be automatically determined. // add this little wait so the ServerSocket can fully // initialize and we can see what port it chose. int runningPort = server.getPort(); while (runningPort == 0) { try { Thread.sleep(50); } catch (Throwable toIgnore) { } runningPort = server.getPort(); } System.out.println("NGServer " + NGConstants.VERSION + " started on " + ((serverAddress == null) ? "all interfaces" : serverAddress.getHostAddress()) + ", port " + runningPort + "."); } /** * A shutdown hook that will cleanly bring down the NGServer if it is * interrupted. * * @author Marty * Lamb */ private static class NGServerShutdowner extends Thread { private NGServer server = null; NGServerShutdowner(NGServer server) { this.server = server; } public void run() { int count = 0; server.shutdown(false); // give the server up to five seconds to stop. is that enough? // remember that the shutdown will call nailShutdown in any // nails as well while (server.isRunning() && (count < 50)) { try { Thread.sleep(100); } catch (InterruptedException e) { } ++count; } if (server.isRunning()) { System.err.println("Unable to cleanly shutdown server. Exiting JVM Anyway."); } else { System.out.println("NGServer shut down."); } } } } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGSession.java000066400000000000000000000271471205677473000331360ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.Socket; import java.util.List; import java.util.Properties; /** * Reads the NailGun stream from the client through the command, then hands off * processing to the appropriate class. The NGSession obtains its sockets from * an NGSessionPool, which created this NGSession. * * @author Marty Lamb */ class NGSession extends Thread { /** * The server this NGSession is working for */ private NGServer server = null; /** * The pool this NGSession came from, and to which it will return itself */ private NGSessionPool sessionPool = null; /** * Synchronization object */ private Object lock = new Object(); /** * The next socket this NGSession has been tasked with processing (by * NGServer) */ private Socket nextSocket = null; /** * True if the server has been shutdown and this NGSession should terminate * completely */ private boolean done = false; /** * The instance number of this NGSession. That is, if this is the Nth * NGSession to be created, then this is the value for N. */ private long instanceNumber = 0; /** * A lock shared among all NGSessions */ private static Object sharedLock = new Object(); /** * The instance counter shared among all NGSessions */ private static long instanceCounter = 0; /** * signature of main(String[]) for reflection operations */ private static Class[] mainSignature; /** * signature of nailMain(NGContext) for reflection operations */ private static Class[] nailMainSignature; static { // initialize the signatures mainSignature = new Class[1]; mainSignature[0] = String[].class; nailMainSignature = new Class[1]; nailMainSignature[0] = NGContext.class; } /** * Creates a new NGSession running for the specified NGSessionPool and * NGServer. * * @param sessionPool The NGSessionPool we're working for * @param server The NGServer we're working for */ NGSession(NGSessionPool sessionPool, NGServer server) { super(); this.sessionPool = sessionPool; this.server = server; synchronized (sharedLock) { this.instanceNumber = ++instanceCounter; } // server.out.println("Created NGSession " + instanceNumber); } /** * Shuts down this NGSession gracefully */ void shutdown() { done = true; synchronized (lock) { nextSocket = null; lock.notifyAll(); } } /** * Instructs this NGSession to process the specified socket, after which * this NGSession will return itself to the pool from which it came. * * @param socket the socket (connected to a client) to process */ public void run(Socket socket) { synchronized (lock) { nextSocket = socket; lock.notify(); } Thread.yield(); } /** * Returns the next socket to process. This will block the NGSession thread * until there's a socket to process or the NGSession has been shut down. * * @return the next socket to process, or * null if the NGSession has been shut down. */ private Socket nextSocket() { Socket result = null; synchronized (lock) { result = nextSocket; while (!done && result == null) { try { lock.wait(); } catch (InterruptedException e) { done = true; } result = nextSocket; } nextSocket = null; } return (result); } /** * The main NGSession loop. This gets the next socket to process, runs the * nail for the socket, and loops until shut down. */ public void run() { updateThreadName(null); Socket socket = nextSocket(); while (socket != null) { try { DataInputStream sockin = new DataInputStream(socket.getInputStream()); DataOutputStream sockout = new DataOutputStream(socket.getOutputStream()); // client info - command line arguments and environment List remoteArgs = new java.util.ArrayList(); Properties remoteEnv = new Properties(); String cwd = null; // working directory String command = null; // alias or class name // read everything from the client up to and including the command while (command == null) { int bytesToRead = sockin.readInt(); byte chunkType = sockin.readByte(); byte[] b = new byte[(int) bytesToRead]; sockin.readFully(b); String line = new String(b, "US-ASCII"); switch (chunkType) { case NGConstants.CHUNKTYPE_ARGUMENT: // command line argument remoteArgs.add(line); break; case NGConstants.CHUNKTYPE_ENVIRONMENT: // parse environment into property int equalsIndex = line.indexOf('='); if (equalsIndex > 0) { remoteEnv.setProperty( line.substring(0, equalsIndex), line.substring(equalsIndex + 1)); } String key = line.substring(0, equalsIndex); break; case NGConstants.CHUNKTYPE_COMMAND: // command (alias or classname) command = line; break; case NGConstants.CHUNKTYPE_WORKINGDIRECTORY: // client working directory cwd = line; break; default: // freakout? } } updateThreadName(socket.getInetAddress().getHostAddress() + ": " + command); // can't create NGInputStream until we've received a command, because at // that point the stream from the client will only include stdin and stdin-eof // chunks InputStream in = new NGInputStream(sockin, sockout); PrintStream out = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDOUT)); PrintStream err = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_STDERR)); PrintStream exit = new PrintStream(new NGOutputStream(sockout, NGConstants.CHUNKTYPE_EXIT)); // ThreadLocal streams for System.in/out/err redirection ((ThreadLocalInputStream) System.in).init(in); ((ThreadLocalPrintStream) System.out).init(out); ((ThreadLocalPrintStream) System.err).init(err); try { Alias alias = server.getAliasManager().getAlias(command); Class cmdclass = null; if (alias != null) { cmdclass = alias.getAliasedClass(); } else if (server.allowsNailsByClassName()) { cmdclass = Class.forName(command); } else { cmdclass = server.getDefaultNailClass(); } Object[] methodArgs = new Object[1]; Method mainMethod = null; // will be either main(String[]) or nailMain(NGContext) String[] cmdlineArgs = (String[]) remoteArgs.toArray(new String[remoteArgs.size()]); try { mainMethod = cmdclass.getMethod("nailMain", nailMainSignature); NGContext context = new NGContext(); context.setArgs(cmdlineArgs); context.in = in; context.out = out; context.err = err; context.setCommand(command); context.setExitStream(exit); context.setNGServer(server); context.setEnv(remoteEnv); context.setInetAddress(socket.getInetAddress()); context.setPort(socket.getPort()); context.setWorkingDirectory(cwd); methodArgs[0] = context; } catch (NoSuchMethodException toDiscard) { // that's ok - we'll just try main(String[]) next. } if (mainMethod == null) { mainMethod = cmdclass.getMethod("main", mainSignature); methodArgs[0] = cmdlineArgs; } if (mainMethod != null) { server.nailStarted(cmdclass); NGSecurityManager.setExit(exit); try { mainMethod.invoke(null, methodArgs); } catch (InvocationTargetException ite) { throw (ite.getCause()); } catch (Throwable t) { throw (t); } finally { server.nailFinished(cmdclass); } exit.println(0); } } catch (NGExitException exitEx) { exit.println(exitEx.getStatus()); server.out.println(Thread.currentThread().getName() + " exited with status " + exitEx.getStatus()); } catch (Throwable t) { t.printStackTrace(); exit.println(NGConstants.EXIT_EXCEPTION); // remote exception constant } sockout.flush(); socket.close(); } catch (Throwable t) { t.printStackTrace(); } ((ThreadLocalInputStream) System.in).init(null); ((ThreadLocalPrintStream) System.out).init(null); ((ThreadLocalPrintStream) System.err).init(null); updateThreadName(null); sessionPool.give(this); socket = nextSocket(); } // server.out.println("Shutdown NGSession " + instanceNumber); } /** * Updates the current thread name (useful for debugging). */ private void updateThreadName(String detail) { setName("NGSession " + instanceNumber + ": " + ((detail == null) ? "(idle)" : detail)); } } NGSessionPool.java000066400000000000000000000056631205677473000337100ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; /** * Provides NGSession pooling functionality. One parameter, "maxIdle", * governs its behavior by setting the maximum number of idle NGSession * threads it will allow. It creates a pool of size maxIdle - 1, because * one NGSession is kept "on deck" by the NGServer in order to eke out * a little extra responsiveness. * * @author Marty Lamb */ class NGSessionPool { /** * number of sessions to store in the pool */ int poolSize = 0; /** * the pool itself */ NGSession[] pool = null; /** * The number of sessions currently in the pool */ int poolEntries = 0; /** * reference to server we're working for */ NGServer server = null; /** * have we been shut down? */ boolean done = false; /** * synchronization object */ private Object lock = new Object(); /** * Creates a new NGSessionRunner operating for the specified server, with * the specified number of threads * @param server the server to work for * @param poolsize the maximum number of idle threads to allow */ NGSessionPool(NGServer server, int poolsize) { this.server = server; this.poolSize = Math.min(0, poolsize); pool = new NGSession[poolSize]; poolEntries = 0; } /** * Returns an NGSession from the pool, or creates one if necessary * @return an NGSession ready to work */ NGSession take() { NGSession result; synchronized(lock) { if (poolEntries == 0) { result = new NGSession(this, server); result.start(); } else { --poolEntries; result = pool[poolEntries]; } } return (result); } /** * Returns an NGSession to the pool. The pool may choose to shutdown * the thread if the pool is full * @param session the NGSession to return to the pool */ void give(NGSession session) { boolean shutdown = false; synchronized(lock) { if (done || poolEntries == poolSize) { shutdown = true; } else { pool[poolEntries] = session; ++poolEntries; } } if (shutdown) session.shutdown(); } /** * Shuts down the pool. Running nails are allowed to finish. */ void shutdown() { done = true; synchronized(lock) { while (poolEntries > 0) { take().shutdown(); } } } } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/NailStats.java000066400000000000000000000062221205677473000331570ustar00rootroot00000000000000/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; /** *

Collects and provides statistics on a nail.

* * @author Marty Lamb */ public class NailStats implements Cloneable { private Class nailclass; private long runCounter; private long refCounter; private Object lock; /** * Creates a new NailStats object for the specified class * @param nailclass the class for which we'll collect statistics */ NailStats(Class nailclass) { this.nailclass = nailclass; runCounter = 0; refCounter = 0; lock = new Object(); } /** * Logs the fact that an instance of this nail has started */ void nailStarted() { synchronized(lock) { ++runCounter; ++refCounter; } } /** * Logs the fact that an instance of this nail has finished */ void nailFinished() { synchronized(lock) { --refCounter; } } /** * Returns the number of times this nail has been run. Nails * that have started but not yet finished are included in this * number. * @return the number of times this nail has been run. */ public long getRunCount() { return (runCounter); } /** * Returns the number of sessions currently running this nail. * @return the number of sessions currently running this nail. */ public long getRefCount() { return (refCounter); } /** * Returns the class for which we're tracking statistics * @return the class for which we're tracking statistics */ public Class getNailClass() { return (nailclass); } /** * @see java.lang.Object#hashCode */ public int hashCode() { return (nailclass.hashCode()); } /** * Returns true iff the specified NailStats object * is tracking the same class. * @param o the NailStats object to check * @return true iff the specified NailStats object * is tracking the same class. */ public boolean equals(Object o) { NailStats other = (NailStats) o; return (nailclass.equals(other.nailclass)); } /** * Creates a copy of this NailStats object. * @return a copy of this NailStats object. */ public Object clone() { Object result = null; try { result = super.clone(); } catch (CloneNotSupportedException toDiscard) {} return (result); } /** * Returns a String representation of this NailStats * object, in the form "classname: runcount/refcount". * *return a String representation of this NailStats * object. */ public String toString() { return (nailclass.getName() + ": " + getRunCount() + "/" + getRefCount()); } } ThreadLocalInputStream.java000066400000000000000000000077641205677473000355700ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.IOException; import java.io.InputStream; /** * The class name is pretty descriptive. This creates an InputStream * much like a FilterInputStream, but with the wrapped InputStream * being local to the current Thread. By setting System.in to a * ThreadLocalInputStream, different Threads can read from different * InputStreams simply by using System.in. Of course, the init() * method must be called by the Thread that wishes to use the * wrapped stream. * * @author Marty Lamb */ class ThreadLocalInputStream extends InputStream { /** * The InputStreams for the various threads */ private InheritableThreadLocal streams = null; private InputStream defaultInputStream = null; /** * @param defaultInputStream the InputStream that will be used if the * current thread has not called init() */ ThreadLocalInputStream(InputStream defaultInputStream) { super(); streams = new InheritableThreadLocal(); this.defaultInputStream = defaultInputStream; init(null); } /** * Sets the InputStream for the current thread * @param streamForCurrentThread the InputStream for the current thread */ void init(InputStream streamForCurrentThread) { streams.set(streamForCurrentThread); } /** * Returns this thread's InputStream * @return this thread's InputStream */ InputStream getInputStream() { InputStream result = (InputStream) streams.get(); return ((result == null) ? defaultInputStream : result); } // BEGIN delegated java.io.InputStream methods /** * @see java.io.InputStream#available() */ public int available() throws IOException { return (getInputStream().available()); } /** * @see java.io.InputStream#close() */ public void close() throws IOException { getInputStream().close(); } /** * @see java.io.InputStream#mark(int) */ public void mark(int readlimit) { getInputStream().mark(readlimit); } /** * @see java.io.InputStream#markSupported() */ public boolean markSupported() { return (getInputStream().markSupported()); } /** * @see java.io.InputStream#read() */ public int read() throws IOException { return (getInputStream().read()); } /** * @see java.io.InputStream#read(byte[]) */ public int read(byte[] b) throws IOException { return (getInputStream().read(b)); } /** * @see java.io.InputStream#read(byte[],int,int) */ public int read(byte[] b, int off, int len) throws IOException { return (getInputStream().read(b, off, len)); } /** * @see java.io.InputStream#reset() */ public void reset() throws IOException { getInputStream().reset(); } /** * @see java.io.InputStream#skip(long) */ public long skip(long n) throws IOException { return (getInputStream().skip(n)); } // BEGIN delegated java.io.InputStream methods // Note: Should java.lang.Object methods be delegated? If not, and // someone synchronizes on this stream, processes might be blocked // that shouldn't be. It would certainly be stupid to delegate // finalize(). Not so clear are hashcode(), equals(), notify(), and // the wait() methods. } ThreadLocalPrintStream.java000066400000000000000000000143401205677473000355510ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun; import java.io.IOException; import java.io.PrintStream; /** * The class name is pretty descriptive. This creates a PrintStream * much like a FilterOutputStream, but with the wrapped PrintStream * being local to the current Thread. By setting System.out to a * ThreadLocalPrintStream, different Threads can write to different * PrintStreams simply by using System.out. Of course, the init() * method must be called by the Thread that wishes to use the * wrapped stream. * * @author Marty Lamb */ class ThreadLocalPrintStream extends PrintStream { /** * The PrintStreams for the various threads */ private InheritableThreadLocal streams = null; private PrintStream defaultPrintStream = null; /** * Creates a new InheritedThreadLocalPrintStream * @param defaultPrintStream the PrintStream that will be used if the * current thread has not called init() */ public ThreadLocalPrintStream(PrintStream defaultPrintStream) { super(defaultPrintStream); streams = new InheritableThreadLocal(); this.defaultPrintStream = defaultPrintStream; init(null); } /** * Sets the PrintStream for the current thread * @param streamForCurrentThread the PrintStream for the current thread */ void init(PrintStream streamForCurrentThread) { streams.set(streamForCurrentThread); } /** * Returns this thread's PrintStream * @return this thread's PrintStream */ PrintStream getPrintStream() { PrintStream result = (PrintStream) streams.get(); return ((result == null) ? defaultPrintStream : result); } // BEGIN delegated java.io.PrintStream methods /** * @see java.io.PrintStream#checkError() */ public boolean checkError() { return (getPrintStream().checkError()); } /** * @see java.io.PrintStream#close() */ public void close() { getPrintStream().close(); } /** * @see java.io.PrintStream#flush() */ public void flush() { getPrintStream().flush(); } /** * @see java.io.PrintStream#print(boolean) */ public void print(boolean b) { getPrintStream().print(b); } /** * @see java.io.PrintStream#print(char) */ public void print(char c) { getPrintStream().print(c); } /** * @see java.io.PrintStream#print(char[]) */ public void print(char[] s) { getPrintStream().print(s); } /** * @see java.io.PrintStream#print(double) */ public void print(double d) { getPrintStream().print(d); } /** * @see java.io.PrintStream#print(float) */ public void print(float f) { getPrintStream().print(f); } /** * @see java.io.PrintStream#print(int) */ public void print(int i) { getPrintStream().print(i); } /** * @see java.io.PrintStream#print(long) */ public void print(long l) { getPrintStream().print(l); } /** * @see java.io.PrintStream#print(Object) */ public void print(Object obj) { getPrintStream().print(obj); } /** * @see java.io.PrintStream#print(String) */ public void print(String s) { getPrintStream().print(s); } /** * @see java.io.PrintStream#println() */ public void println() { getPrintStream().println(); } /** * @see java.io.PrintStream#println(boolean) */ public void println(boolean x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(char) */ public void println(char x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(char[]) */ public void println(char[] x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(double) */ public void println(double x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(float) */ public void println(float x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(int) */ public void println(int x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(long) */ public void println(long x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(Object) */ public void println(Object x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#println(String) */ public void println(String x) { getPrintStream().println(x); } /** * @see java.io.PrintStream#write(byte[],int,int) */ public void write(byte[] buf, int off, int len) { getPrintStream().write(buf, off, len); } /** * @see java.io.PrintStream#write(int) */ public void write(int b) { getPrintStream().write(b); } // END delegated java.io.PrintStream methods // BEGIN delegated java.io.FilterOutputStream methods /** * @see java.io.FilterOutputStream#write(byte[]) */ public void write(byte[] b) throws IOException { getPrintStream().write(b); } // END delegated java.io.FilterOutputStream methods // Note: Should java.lang.Object methods be delegated? If not, and // someone synchronizes on this stream, processes might be blocked // that shouldn't be. It would certainly be stupid to delegate // finalize(). Not so clear are hashcode(), equals(), notify(), and // the wait() methods. } nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/000077500000000000000000000000001205677473000322415ustar00rootroot00000000000000DefaultNail.java000066400000000000000000000023511205677473000352160ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.builtins; import com.martiansoftware.nailgun.NGContext; import com.martiansoftware.nailgun.NGConstants; /** * The default nail class used by the server when an invalid command (nonexisting * classname or alias) is issued. This simply displays an error message to the * client's stdout and has the client exit with value NGConstants.EXIT_NOSUCHCOMMAND. * * @author Marty Lamb */ public class DefaultNail { public static void nailMain(NGContext context) { context.err.println("No such command: " + context.getCommand()); context.exit(NGConstants.EXIT_NOSUCHCOMMAND); } } NGAlias.java000066400000000000000000000054031205677473000343050ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.builtins; import java.util.Iterator; import java.util.Set; import com.martiansoftware.nailgun.Alias; import com.martiansoftware.nailgun.NGContext; import com.martiansoftware.nailgun.NGServer; /** *

Provides a means to view and add aliases. This is aliased by default * to the command "ng-alias".

* *

No command line validation is performed. If you trigger an exception, * your client will display it.

* *

To view the current alias list, issue the command: *

ng-alias
* with no arguments.

* *

To add or replace an alias, issue the command: *

ng-alias [alias name] [fully qualified aliased class name]
*

* * @author Marty Lamb */ public class NGAlias { private static String padl(String s, int len) { StringBuffer buf = new StringBuffer(s); while(buf.length() < len) buf.append(" "); return (buf.toString()); } public static void nailMain(NGContext context) throws ClassNotFoundException { String[] args = context.getArgs(); NGServer server = context.getNGServer(); if (args.length == 0) { Set aliases = server.getAliasManager().getAliases(); // let's pad this nicely. first, find the longest alias // name. then pad the others to that width. int maxAliasLength = 0; int maxClassnameLength = 0; for (Iterator i = aliases.iterator(); i.hasNext();) { Alias alias = (Alias) i.next(); maxAliasLength = Math.max(maxAliasLength, alias.getName().length()); maxClassnameLength = Math.max(maxClassnameLength, alias.getAliasedClass().getName().length()); } 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(); } } else if (args.length == 2) { server.getAliasManager().addAlias(new Alias(args[0], "", Class.forName(args[1]))); } } } NGClasspath.java000066400000000000000000000054431205677473000352020ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.builtins; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import com.martiansoftware.nailgun.NGContext; /** *

Provides a means to display and add to the system classpath at runtime. * If called with no arguments, the classpath is displayed. Otherwise, each * argument is turned into a java.io.File and added to the classpath. Relative * paths will be resolved relative to the directory in which the nailgun server * is running. This is very likely to change in the future.

* *

This is aliased by default to the command "ng-cp".

* * @author Marty Lamb */ public class NGClasspath { /** * Adds the specified URL (for a jar or a directory) to the System * ClassLoader. This code was written by antony_miguel and posted on * http://forum.java.sun.com/thread.jsp?forum=32&thread=300557&message=1191210 * I assume it has been placed in the public domain. * * @param url the URL of the resource (directory or jar) to add to the * System classpath * @throws Exception if anything goes wrong. The most likely culprit, should * this ever arise, 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}); method.setAccessible(true); method.invoke(sysloader, new Object[]{url}); } public static void nailMain(NGContext context) throws Exception { String[] args = context.getArgs(); if (args.length == 0) { URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); URL[] urls = sysLoader.getURLs(); for (int i = 0; i < urls.length; ++i) { context.out.println(urls[i]); } } else { for (int i = 0; i < args.length; ++i) { File file = new File(args[i]); addToSystemClassLoader(file.toURL()); } } } } NGServerStats.java000066400000000000000000000032031205677473000355350ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.builtins; import java.util.Iterator; import java.util.Map; import com.martiansoftware.nailgun.NGServer; import com.martiansoftware.nailgun.NGContext; /** *

Displays all NailStats tracked by the server.

* *

This can be run standalone with no arguments. It will also run automatically * upon NGServer shutdown, sending its output to the server's System.out.

* *

This is aliased by default to the command "ng-stats".

* @author Marty Lamb */ public class NGServerStats { public static void nailShutdown(NGServer server) { dumpStats(server, server.out); } public static void nailMain(NGContext context) { dumpStats(context.getNGServer(), context.out); } private static void dumpStats(NGServer server, java.io.PrintStream out) { Map stats = server.getNailStats(); for (Iterator i = stats.values().iterator(); i.hasNext();) { out.println(i.next()); } } } NGStop.java000066400000000000000000000020131205677473000341730ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/* Copyright 2004-2012, Martian Software, 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.martiansoftware.nailgun.builtins; import com.martiansoftware.nailgun.NGContext; /** *

Shuts down the currently running server.

* *

This is aliased by default to the command "ng-stop".

* * @author Marty Lamb */ public class NGStop { public static void nailMain(NGContext context) { context.getNGServer().shutdown(true); } } NGVersion.java000066400000000000000000000006751205677473000347070ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtinspackage com.martiansoftware.nailgun.builtins; import com.martiansoftware.nailgun.NGConstants; import com.martiansoftware.nailgun.NGContext; /** * Displays the version of the NailGun server and exits. * * @author Marty Lamb */ public class NGVersion { public static void nailMain(NGContext context) { context.out.println("NailGun server version " + NGConstants.VERSION); } } nailgun-version.properties000066400000000000000000000001321205677473000355630ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/java/com/martiansoftware/nailgun#Automatically generated by Ant build script. #Fri May 22 21:04:25 EDT 2009 version=0.9.0 nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/000077500000000000000000000000001205677473000240605ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/com/000077500000000000000000000000001205677473000246365ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/com/martiansoftware/000077500000000000000000000000001205677473000300445ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/com/martiansoftware/nailgun/000077500000000000000000000000001205677473000315015ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/com/martiansoftware/nailgun/builtins/000077500000000000000000000000001205677473000333325ustar00rootroot00000000000000builtins.properties000066400000000000000000000010211205677473000372140ustar00rootroot00000000000000nailgun-nailgun-all-0.9.1/nailgun-server/src/main/resources/com/martiansoftware/nailgun/builtinsng-alias=com.martiansoftware.nailgun.builtins.NGAlias ng-alias.desc=Displays and manages command aliases ng-cp=com.martiansoftware.nailgun.builtins.NGClasspath ng-cp.desc=Displays and manages the current system classpath ng-stop=com.martiansoftware.nailgun.builtins.NGStop ng-stop.desc=Shuts down the nailgun server ng-stats=com.martiansoftware.nailgun.builtins.NGServerStats ng-stats.desc=Displays nail statistics ng-version=com.martiansoftware.nailgun.builtins.NGVersion ng-version.desc=Displays the server version number. nailgun-nailgun-all-0.9.1/pom.xml000066400000000000000000000116021205677473000167070ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.martiansoftware nailgun-all 0.9.1 pom UTF-8 nailgun-all 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. This project contains the server and examples. http://martiansoftware.com/nailgun The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo scm:git:git@github.com:martylamb/nailgun.git scm:git:git@github.com:martylamb/nailgun.git scm:git:git@github.com:martylamb/nailgun.git nailgun-all-0.9.1 mlamb@martiansoftware.com Marty Lamb http://martiansoftware.com nailgun-server nailgun-examples org.apache.maven.plugins maven-compiler-plugin 3.0 1.4 1.4 org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources jar org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs jar org.apache.maven.plugins maven-release-plugin 2.3.2 -Dgpg.passphrase=${gpg.passphrase} sonatype-nexus-snapshots Sonatype Nexus snapshot repository https://oss.sonatype.org/content/repositories/snapshots sonatype-nexus-staging Sonatype Nexus release repository https://oss.sonatype.org/service/local/staging/deploy/maven2/ release-sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.4 ${gpg.passphrase} sign-artifacts verify sign