pax_global_header 0000666 0000000 0000000 00000000064 13233655467 0014530 g ustar 00root root 0000000 0000000 52 comment=2b0ade9c035ffc37d072cdb3683c269531f83a99
nailgun-nailgun-all-0.9.3/ 0000775 0000000 0000000 00000000000 13233655467 0015377 5 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/.gitignore 0000664 0000000 0000000 00000000120 13233655467 0017360 0 ustar 00root root 0000000 0000000 /nailgun-server/target/
/nailgun-examples/target/
/target/
ng
*.iml
*.pyc
.idea/ nailgun-nailgun-all-0.9.3/.travis.yml 0000664 0000000 0000000 00000000106 13233655467 0017505 0 ustar 00root root 0000000 0000000 language: java
jdk:
- oraclejdk8
script:
- scripts/travis_run.sh
nailgun-nailgun-all-0.9.3/LICENSE.txt 0000664 0000000 0000000 00000026164 13233655467 0017233 0 ustar 00root root 0000000 0000000 Nailgun Copyright © 2004-2012, Martian Software, Inc.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.
nailgun-nailgun-all-0.9.3/Makefile 0000664 0000000 0000000 00000002063 13233655467 0017040 0 ustar 00root root 0000000 0000000 # 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
CFLAGS=-Wall -pedantic -O2
SRCDIR=nailgun-client
PREFIX=/usr/local
ng: ${SRCDIR}/ng.c
@echo "Building ng client. To build a Windows binary, type 'make ng.exe'"
${CC} $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o ng ${SRCDIR}/ng.c
install: ng
install -d ${PREFIX}/bin
install ng ${PREFIX}/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.3/README.md 0000664 0000000 0000000 00000002077 13233655467 0016664 0 ustar 00root root 0000000 0000000 nailgun
=======
---
**Note:** Nailgun is based on original code developed by Marty Lamb.
In October, 2017, Marty transferred the repository to Facebook, where it is currently
maintained by Buck team. Nailgun will remain available under the Apache license, version 2.0.
---
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".
A ruby client is available through the [railgun](https://github.com/timuralp/railgun) project.
For more information, see [the nailgun website](http://martiansoftware.com/nailgun/).
nailgun-nailgun-all-0.9.3/appveyor.yml 0000664 0000000 0000000 00000000621 13233655467 0017766 0 ustar 00root root 0000000 0000000 os: Visual Studio 2015
version: '{build}'
environment:
matrix:
- JAVA_HOME: C:\Program Files\Java\jdk1.8.0
install:
- cmd: choco install maven -y -f -i
- cmd: refreshenv
- cmd: set
- cmd: python --version
- cmd: java -version
- cmd: mvn -version
# to disable automatic builds by MsBuild
build: off
build_script:
- "mvn package"
test_script:
- "python -m pynailgun.test_ng"
nailgun-nailgun-all-0.9.3/nailgun-client/ 0000775 0000000 0000000 00000000000 13233655467 0020310 5 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-client/ng.c 0000664 0000000 0000000 00000057745 13233655467 0021102 0 ustar 00root root 0000000 0000000 /*
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 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. 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. 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
* 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.3/nailgun-server/src/main/java/com/martiansoftware/nailgun/AliasManager.java0000664 0000000 0000000 00000010613 13233655467 0033605 0 ustar 00root root 0000000 0000000 /*
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();
ClassLoader cl = getClass().getClassLoader();
if (cl == null) cl = ClassLoader.getSystemClassLoader(); // needed if nailgun classes are loaded in the boot classpath.
props.load(cl.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:
*
each of
* which may have an optional
* [alias name]=[fully qualified classname]
*
* For example, to create an alias called "
* [alias name].desc=[alias description]
myprog
" for class
* com.mydomain.myapp.MyProg
, the following properties would be
* defined:
*
*
*
* @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));
}
}
NGClientListener.java 0000664 0000000 0000000 00000000510 13233655467 0034346 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun package com.martiansoftware.nailgun;
public interface NGClientListener {
/**
* Called by an internal nailgun thread when the server detects that the nailgun client has disconnected.
* {@link NGClientListener}s can be registered using {@link NGContext#addClientListener}.
*/
void clientDisconnected();
}
nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGConstants.java 0000664 0000000 0000000 00000007651 13233655467 0033472 0 ustar 00root root 0000000 0000000 /*
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_SENDINPUT = 'S';
/**
* Chunk type marker for heartbeats sent to let the server know the client is still alive.
*/
public static final byte CHUNKTYPE_HEARTBEAT = 'H';
/**
* Server version number
*/
public static final String VERSION;
/**
* Expected interval between heartbeats in milliseconds.
*/
public static final short HEARTBEAT_INTERVAL_MILLIS = 1000;
/**
* Maximum interval to wait between heartbeats before considering client to have disconnected.
*/
public static final short HEARTBEAT_TIMEOUT_MILLIS = 10000;
/**
* Maximum chunk len sent from client.
*/
public static final short MAXIMUM_CHUNK_LENGTH = 2048;
/**
* 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.3/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGContext.java 0000664 0000000 0000000 00000020335 13233655467 0033134 0 ustar 00root root 0000000 0000000 /*
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;
/**
* myprog=com.mydomain.myapp.MyProg
*myprog.desc=Runs my program.
*
*
* method, that method will be called by NailGun instead of the traditional
*
* public static void nailMain(NGContext context)
*
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
*/
public NGContext() {
super();
}
public void setExitStream(PrintStream exitStream) {
this.exitStream = exitStream;
}
public void setPort(int remotePort) {
this.remotePort = remotePort;
}
public 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;
}
public 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."));
}
}
/**
* @return the {@link NGInputStream} for this session.
*/
private NGInputStream getInputStream() {
return (NGInputStream) this.in;
}
/**
* @return true if client is connected, false if a client exit has been detected.
*/
public boolean isClientConnected() {
return getInputStream().isClientConnected();
}
/**
* @param listener the {@link NGClientListener} to be notified of client events.
*/
public void addClientListener(NGClientListener listener) {
getInputStream().addClientListener(listener);
}
/**
* @param listener the {@link NGClientListener} to no longer be notified of client events.
*/
public void removeClientListener(NGClientListener listener) {
getInputStream().removeClientListener(listener);
}
/**
* Do not notify about client exit
*/
public void removeAllClientListeners() {
getInputStream().removeAllClientListeners();
}
/**
* @param listener the {@link com.martiansoftware.nailgun.NGHeartbeatListener} to be notified of client events.
*/
public void addHeartbeatListener(NGHeartbeatListener listener) {
getInputStream().addHeartbeatListener(listener);
}
/**
* @param listener the {@link NGHeartbeatListener} to no longer be notified of client events.
*/
public void removeHeartbeatListener(NGHeartbeatListener listener) {
getInputStream().removeHeartbeatListener(listener);
}
}
NGExitException.java 0000664 0000000 0000000 00000007346 13233655467 0034230 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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
* 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 final Map allNailStats;
/**
* Remember the security manager we start with so we can restore it later
*/
private SecurityManager originalSecurityManager = null;
private final int heartbeatTimeoutMillis;
/**
* 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, int timeoutMillis) {
this(new NGListeningAddress(addr, port), sessionPoolSize, timeoutMillis);
}
/**
* 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.
*/
public NGServer(InetAddress addr, int port) {
this(new NGListeningAddress(addr, port), DEFAULT_SESSIONPOOLSIZE, NGConstants.HEARTBEAT_TIMEOUT_MILLIS);
}
/**
* 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() {
this(new NGListeningAddress(null, NGConstants.DEFAULT_PORT), DEFAULT_SESSIONPOOLSIZE, NGConstants.HEARTBEAT_TIMEOUT_MILLIS);
}
/**
* 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 listeningAddress the address at which to listen
* @param sessionPoolSize the max number of idle sessions allowed by the
* pool
* @param timeoutMillis timeout in millis to wait for a heartbeat from the client
* before disconnecting them
*/
public NGServer(NGListeningAddress listeningAddress, int sessionPoolSize, int timeoutMillis) {
this.listeningAddress = listeningAddress;
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);
heartbeatTimeoutMillis = timeoutMillis;
}
/**
* 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;
synchronized (allNailStats) {
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);
}
/**
*
method
* will have this method called with this NGServer as its sole
* parameter.public static void nailShutdown(NGServer)
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 > 2) {
usage();
return;
}
// null server address means bind to everything local
NGListeningAddress listeningAddress;
int timeoutMillis = NGConstants.HEARTBEAT_TIMEOUT_MILLIS;
// parse the command line parameters, which
// may be an inetaddress to bind to, a port number,
// an inetaddress followed by a port, separated
// by a colon, or the string "local:/path/to/socket"
// for a Unix domain socket or Windows named pipe.
// if a second parameter is provided it
// is interpreted as the number of milliseconds to
// wait between heartbeats before considering the
// client to have disconnected.
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 ("local".equals(addrPart) && portPart != null) {
// Treat the port part as a path to a local Unix domain socket
// or Windows named pipe.
listeningAddress = new NGListeningAddress(portPart);
} else if (addrPart != null && portPart != null) {
listeningAddress = new NGListeningAddress(
InetAddress.getByName(addrPart), Integer.parseInt(portPart));
} else if (addrPart != null && portPart == null) {
listeningAddress = new NGListeningAddress(
InetAddress.getByName(addrPart), NGConstants.DEFAULT_PORT);
} else {
listeningAddress = new NGListeningAddress(null, Integer.parseInt(portPart));
}
if (args.length == 2) {
timeoutMillis = Integer.parseInt(args[1]);
}
} else {
listeningAddress = new NGListeningAddress(null, NGConstants.DEFAULT_PORT);
}
NGServer server = new NGServer(listeningAddress, DEFAULT_SESSIONPOOLSIZE, timeoutMillis);
Thread t = new Thread(server);
t.setName("NGServer(" + listeningAddress.toString() + ")");
t.start();
Runtime.getRuntime().addShutdownHook(new NGServerShutdowner(server));
}
public int getHeartbeatTimeout() {
return heartbeatTimeoutMillis;
}
/**
* 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.3/nailgun-server/src/main/java/com/martiansoftware/nailgun/NGSession.java 0000664 0000000 0000000 00000037242 13233655467 0033140 0 ustar 00root root 0000000 0000000 /*
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.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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
*/
public class NGSession extends Thread {
private static final Logger LOG = Logger.getLogger(NGSession.class.getName());
/**
* The server this NGSession is working for
*/
private final NGServer server;
/**
* The pool this NGSession came from, and to which it will return itself
*/
private final NGSessionPool sessionPool;
/**
* Synchronization object
*/
private final 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 final long instanceNumber;
/**
* The interval to wait between heartbeats before considering the client to have disconnected.
*/
private final int heartbeatTimeoutMillis;
/**
* The instance counter shared among all NGSessions
*/
private static AtomicLong instanceCounter = new AtomicLong(0);
/**
* signature of main(String[]) for reflection operations
*/
private final static Class[] mainSignature = {
String[].class,
};
/**
* signature of nailMain(NGContext) for reflection operations
*/
private final static Class[] nailMainSignature = {
NGContext.class,
};
/**
* A ClassLoader that may be set by a client. Defaults to the classloader of this class.
*/
public static volatile ClassLoader classLoader = null; // initialized in the static initializer - see below
static {
try {
classLoader = NGSession.class.getClassLoader();
} catch (SecurityException e) {
throw e;
}
}
/**
* Creates a new NGSession running for the specified NGSessionPool and
* NGServer.
*
* @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;
this.heartbeatTimeoutMillis = server.getHeartbeatTimeout();
this.instanceNumber = instanceCounter.incrementAndGet();
}
/**
* Shuts down this NGSession gracefully
*/
void shutdown() {
synchronized (lock) {
done = true;
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;
synchronized (lock) {
result = nextSocket;
while (!done && result == null) {
try {
lock.wait();
} catch (InterruptedException e) {
done = true;
}
result = nextSocket;
}
nextSocket = null;
}
if (result != null) {
// Java InputStream API is blocking by default with no reliable way to stop pending
// read() call. Setting the timeout to underlying socket will make socket's underlying
// read() calls throw SocketTimeoutException which unblocks read(). The exception must
// be properly handled by calling code.
try {
result.setSoTimeout(this.heartbeatTimeoutMillis);
} catch (SocketException e) {
// this exception might be thrown if socket is already closed
// so we just return null
return 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);
LOG.log(Level.FINE, "Waiting for first client to connect");
Socket socket = nextSocket();
while (socket != null) {
LOG.log(Level.FINE, "Client connected");
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, "UTF-8");
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?
}
}
String threadName;
if (socket.getInetAddress() != null) {
threadName = socket.getInetAddress().getHostAddress() + ": " + command;
} else {
threadName = command;
}
updateThreadName(threadName);
// 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
try (
InputStream in = new NGInputStream(sockin, sockout, heartbeatTimeoutMillis);
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, true, classLoader);
} 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()]);
boolean isStaticNail = true; // See: NonStaticNail.java
Class[] interfaces = cmdclass.getInterfaces();
for (int i = 0; i < interfaces.length; i++){
if (interfaces[i].equals(NonStaticNail.class)){
isStaticNail = false; break;
}
}
if (!isStaticNail){
mainMethod = cmdclass.getMethod("nailMain", new Class[]{ String[].class });
methodArgs[0] = cmdlineArgs;
} else {
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 {
if (isStaticNail){
mainMethod.invoke(null, methodArgs);
} else {
mainMethod.invoke(cmdclass.newInstance(), methodArgs);
}
} catch (InvocationTargetException ite) {
throw (ite.getCause());
} catch (InstantiationException e){
throw (e);
} catch (IllegalAccessException e){
throw (e);
} catch (Throwable t) {
throw (t);
} finally {
server.nailFinished(cmdclass);
}
exit.println(0);
}
} catch (NGExitException exitEx) {
LOG.log(Level.INFO, "Server cleanly exited with status " + exitEx.getStatus(), exitEx);
exit.println(exitEx.getStatus());
server.out.println(Thread.currentThread().getName() + " exited with status " + exitEx.getStatus());
} catch (Throwable t) {
LOG.log(Level.INFO, "Server unexpectedly exited with unhandled exception", t);
t.printStackTrace();
exit.println(NGConstants.EXIT_EXCEPTION); // remote exception constant
}
} finally {
LOG.log(Level.FINE, "Flushing client socket");
sockout.flush();
try {
socket.shutdownInput();
socket.shutdownOutput();
} catch (IOException e) {
LOG.log(
Level.FINE,
"Error shutting down socket I/O (this is expected if the client disconnected already)",
e);
}
LOG.log(Level.FINE, "Closing client socket");
socket.close();
LOG.log(Level.FINE, "Finished tearing down client socket");
}
} catch (Throwable t) {
LOG.log(Level.WARNING, "Internal error in session", t);
t.printStackTrace();
}
((ThreadLocalInputStream) System.in).init(null);
((ThreadLocalPrintStream) System.out).init(null);
((ThreadLocalPrintStream) System.err).init(null);
updateThreadName(null);
sessionPool.give(this);
LOG.log(Level.FINE, "Waiting for next client to connect");
socket = nextSocket();
}
LOG.log(Level.INFO, "NGSession shutting down");
}
/**
* Updates the current thread name (useful for debugging).
*/
private void updateThreadName(String detail) {
setName("NGSession " + instanceNumber + ": " + ((detail == null) ? "(idle)" : detail));
}
}
NGSessionPool.java 0000664 0000000 0000000 00000005642 13233655467 0033712 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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
*/
final int poolSize;
/**
* the pool itself
*/
final NGSession[] pool;
/**
* The number of sessions currently in the pool
*/
int poolEntries = 0;
/**
* reference to server we're working for
*/
final NGServer server;
/**
* have we been shut down?
*/
boolean done = false;
/**
* synchronization object
*/
private final 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() {
synchronized(lock) {
done = true;
while (poolEntries > 0) {
take().shutdown();
}
}
}
}
NGUnixDomainServerSocket.java 0000664 0000000 0000000 00000013315 13233655467 0036044 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2015, 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.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicInteger;
import com.sun.jna.LastErrorException;
import com.sun.jna.ptr.IntByReference;
/**
* Implements a {@link ServerSocket} which binds to a local Unix domain socket
* and returns instances of {@link NGUnixDomainSocket} from
* {@link #accept()}.
*/
public class NGUnixDomainServerSocket extends ServerSocket {
private static final int DEFAULT_BACKLOG = 50;
// We use an AtomicInteger to prevent a race in this situation which
// could happen if fd were just an int:
//
// Thread 1 -> NGUnixDomainServerSocket.accept()
// -> lock this
// -> check isBound and isClosed
// -> unlock this
// -> descheduled while still in method
// Thread 2 -> NGUnixDomainServerSocket.close()
// -> lock this
// -> check isClosed
// -> NGUnixDomainSocketLibrary.close(fd)
// -> now fd is invalid
// -> unlock this
// Thread 1 -> re-scheduled while still in method
// -> NGUnixDomainSocketLibrary.accept(fd, which is invalid and maybe re-used)
//
// By using an AtomicInteger, we'll set this to -1 after it's closed, which
// will cause the accept() call above to cleanly fail instead of possibly
// being called on an unrelated fd (which may or may not fail).
private final AtomicInteger fd;
private final int backlog;
private boolean isBound;
private boolean isClosed;
public static class NGUnixDomainServerSocketAddress extends SocketAddress {
private final String path;
public NGUnixDomainServerSocketAddress(String path) {
this.path = path;
}
public String getPath() {
return path;
}
}
/**
* Constructs an unbound Unix domain server socket.
*/
public NGUnixDomainServerSocket() throws IOException {
this(DEFAULT_BACKLOG, null);
}
/**
* Constructs an unbound Unix domain server socket with the specified listen backlog.
*/
public NGUnixDomainServerSocket(int backlog) throws IOException {
this(backlog, null);
}
/**
* Constructs and binds a Unix domain server socket to the specified path.
*/
public NGUnixDomainServerSocket(String path) throws IOException {
this(DEFAULT_BACKLOG, path);
}
/**
* Constructs and binds a Unix domain server socket to the specified path
* with the specified listen backlog.
*/
public NGUnixDomainServerSocket(int backlog, String path) throws IOException {
try {
fd = new AtomicInteger(
NGUnixDomainSocketLibrary.socket(
NGUnixDomainSocketLibrary.PF_LOCAL,
NGUnixDomainSocketLibrary.SOCK_STREAM,
0));
this.backlog = backlog;
if (path != null) {
bind(new NGUnixDomainServerSocketAddress(path));
}
} catch (LastErrorException e) {
throw new IOException(e);
}
}
public synchronized void bind(SocketAddress endpoint) throws IOException {
if (!(endpoint instanceof NGUnixDomainServerSocketAddress)) {
throw new IllegalArgumentException(
"endpoint must be an instance of NGUnixDomainServerSocketAddress");
}
if (isBound) {
throw new IllegalStateException("Socket is already bound");
}
if (isClosed) {
throw new IllegalStateException("Socket is already closed");
}
NGUnixDomainServerSocketAddress unEndpoint = (NGUnixDomainServerSocketAddress) endpoint;
NGUnixDomainSocketLibrary.SockaddrUn address =
new NGUnixDomainSocketLibrary.SockaddrUn(unEndpoint.getPath());
try {
int socketFd = fd.get();
NGUnixDomainSocketLibrary.bind(socketFd, address, address.size());
NGUnixDomainSocketLibrary.listen(socketFd, backlog);
isBound = true;
} catch (LastErrorException e) {
throw new IOException(e);
}
}
public Socket accept() throws IOException {
// We explicitly do not make this method synchronized, since the
// call to NGUnixDomainSocketLibrary.accept() will block
// indefinitely, causing another thread's call to close() to deadlock.
synchronized (this) {
if (!isBound) {
throw new IllegalStateException("Socket is not bound");
}
if (isClosed) {
throw new IllegalStateException("Socket is already closed");
}
}
try {
NGUnixDomainSocketLibrary.SockaddrUn sockaddrUn =
new NGUnixDomainSocketLibrary.SockaddrUn();
IntByReference addressLen = new IntByReference();
addressLen.setValue(sockaddrUn.size());
int clientFd = NGUnixDomainSocketLibrary.accept(fd.get(), sockaddrUn, addressLen);
return new NGUnixDomainSocket(clientFd);
} catch (LastErrorException e) {
throw new IOException(e);
}
}
public synchronized void close() throws IOException {
if (isClosed) {
throw new IllegalStateException("Socket is already closed");
}
try {
// Ensure any pending call to accept() fails.
NGUnixDomainSocketLibrary.close(fd.getAndSet(-1));
isClosed = true;
} catch (LastErrorException e) {
throw new IOException(e);
}
}
}
NGUnixDomainSocket.java 0000664 0000000 0000000 00000011326 13233655467 0034655 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2015, 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 com.sun.jna.LastErrorException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.net.Socket;
/**
* Implements a {@link Socket} backed by a native Unix domain socket.
*
* Instances of this class always return {@code null} for
* {@link Socket#getInetAddress()}, {@link Socket#getLocalAddress()},
* {@link Socket#getLocalSocketAddress()}, {@link Socket#getRemoteSocketAddress()}.
*/
public class NGUnixDomainSocket extends Socket {
private final ReferenceCountedFileDescriptor fd;
private final InputStream is;
private final OutputStream os;
/**
* Creates a Unix domain socket backed by a native file descriptor.
*/
public NGUnixDomainSocket(int fd) {
this.fd = new ReferenceCountedFileDescriptor(fd);
this.is = new NGUnixDomainSocketInputStream();
this.os = new NGUnixDomainSocketOutputStream();
}
public InputStream getInputStream() {
return is;
}
public OutputStream getOutputStream() {
return os;
}
public void shutdownInput() throws IOException {
doShutdown(NGUnixDomainSocketLibrary.SHUT_RD);
}
public void shutdownOutput() throws IOException {
doShutdown(NGUnixDomainSocketLibrary.SHUT_WR);
}
private void doShutdown(int how) throws IOException {
try {
int socketFd = fd.acquire();
if (socketFd != -1) {
NGUnixDomainSocketLibrary.shutdown(socketFd, how);
}
} catch (LastErrorException e) {
throw new IOException(e);
} finally {
fd.release();
}
}
public void close() throws IOException {
super.close();
try {
// This might not close the FD right away. In case we are about
// to read or write on another thread, it will delay the close
// until the read or write completes, to prevent the FD from
// being re-used for a different purpose and the other thread
// reading from a different FD.
fd.close();
} catch (LastErrorException e) {
throw new IOException(e);
}
}
private class NGUnixDomainSocketInputStream extends InputStream {
public int read() throws IOException {
ByteBuffer buf = ByteBuffer.allocate(1);
int result;
if (doRead(buf) == 0) {
result = -1;
} else {
// Make sure to & with 0xFF to avoid sign extension
result = 0xFF & buf.get();
}
return result;
}
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
ByteBuffer buf = ByteBuffer.wrap(b, off, len);
int result = doRead(buf);
if (result == 0) {
result = -1;
}
return result;
}
private int doRead(ByteBuffer buf) throws IOException {
try {
int fdToRead = fd.acquire();
if (fdToRead == -1) {
return -1;
}
return NGUnixDomainSocketLibrary.read(fdToRead, buf, buf.remaining());
} catch (LastErrorException e) {
throw new IOException(e);
} finally {
fd.release();
}
}
}
private class NGUnixDomainSocketOutputStream extends OutputStream {
public void write(int b) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(1);
buf.put(0, (byte) (0xFF & b));
doWrite(buf);
}
public void write(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return;
}
ByteBuffer buf = ByteBuffer.wrap(b, off, len);
doWrite(buf);
}
private void doWrite(ByteBuffer buf) throws IOException {
try {
int fdToWrite = fd.acquire();
if (fdToWrite == -1) {
return;
}
int ret = NGUnixDomainSocketLibrary.write(fdToWrite, buf, buf.remaining());
if (ret != buf.remaining()) {
// This shouldn't happen with standard blocking Unix domain sockets.
throw new IOException("Could not write " + buf.remaining() + " bytes as requested " +
"(wrote " + ret + " bytes instead)");
}
} catch (LastErrorException e) {
throw new IOException(e);
} finally {
fd.release();
}
}
}
}
NGUnixDomainSocketLibrary.java 0000664 0000000 0000000 00000011144 13233655467 0036200 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2015, 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 com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Structure;
import com.sun.jna.Union;
import com.sun.jna.ptr.IntByReference;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
/**
* Utility class to bridge native Unix domain socket calls to Java using JNA.
*/
public class NGUnixDomainSocketLibrary {
public static final int PF_LOCAL = 1;
public static final int AF_LOCAL = 1;
public static final int SOCK_STREAM = 1;
public static final int SHUT_RD = 0;
public static final int SHUT_WR = 1;
// Utility class, do not instantiate.
private NGUnixDomainSocketLibrary() { }
// BSD platforms write a length byte at the start of struct sockaddr_un.
private static final boolean HAS_SUN_LEN =
Platform.isMac() || Platform.isFreeBSD() || Platform.isNetBSD() ||
Platform.isOpenBSD() || Platform.iskFreeBSD();
/**
* Bridges {@code struct sockaddr_un} to and from native code.
*/
public static class SockaddrUn extends Structure implements Structure.ByReference {
/**
* On BSD platforms, the {@code sun_len} and {@code sun_family} values in
* {@code struct sockaddr_un}.
*/
public static class SunLenAndFamily extends Structure {
public byte sunLen;
public byte sunFamily;
protected List getFieldOrder() {
return Arrays.asList(new String[] { "sunLen", "sunFamily" });
}
}
/**
* On BSD platforms, {@code sunLenAndFamily} will be present.
* On other platforms, only {@code sunFamily} will be present.
*/
public static class SunFamily extends Union {
public SunLenAndFamily sunLenAndFamily;
public short sunFamily;
}
public SunFamily sunFamily = new SunFamily();
public byte[] sunPath = new byte[104];
/**
* Constructs an empty {@code struct sockaddr_un}.
*/
public SockaddrUn() {
if (HAS_SUN_LEN) {
sunFamily.sunLenAndFamily = new SunLenAndFamily();
sunFamily.setType(SunLenAndFamily.class);
} else {
sunFamily.setType(Short.TYPE);
}
allocateMemory();
}
/**
* Constructs a {@code struct sockaddr_un} with a path whose bytes are encoded
* using the default encoding of the platform.
*/
public SockaddrUn(String path) throws IOException {
byte[] pathBytes = path.getBytes();
if (pathBytes.length > sunPath.length - 1) {
throw new IOException("Cannot fit name [" + path + "] in maximum unix domain socket length");
}
System.arraycopy(pathBytes, 0, sunPath, 0, pathBytes.length);
sunPath[pathBytes.length] = (byte) 0;
if (HAS_SUN_LEN) {
int len = fieldOffset("sunPath") + pathBytes.length;
sunFamily.sunLenAndFamily = new SunLenAndFamily();
sunFamily.sunLenAndFamily.sunLen = (byte) len;
sunFamily.sunLenAndFamily.sunFamily = AF_LOCAL;
sunFamily.setType(SunLenAndFamily.class);
} else {
sunFamily.sunFamily = AF_LOCAL;
sunFamily.setType(Short.TYPE);
}
allocateMemory();
}
protected List getFieldOrder() {
return Arrays.asList(new String[] { "sunFamily", "sunPath" });
}
}
static {
Native.register(Platform.C_LIBRARY_NAME);
}
public static native int socket(int domain, int type, int protocol) throws LastErrorException;
public static native int bind(int fd, SockaddrUn address, int addressLen)
throws LastErrorException;
public static native int listen(int fd, int backlog) throws LastErrorException;
public static native int accept(int fd, SockaddrUn address, IntByReference addressLen)
throws LastErrorException;
public static native int read(int fd, ByteBuffer buffer, int count)
throws LastErrorException;
public static native int write(int fd, ByteBuffer buffer, int count)
throws LastErrorException;
public static native int close(int fd) throws LastErrorException;
public static native int shutdown(int fd, int how) throws LastErrorException;
}
NGWin32NamedPipeLibrary.java 0000664 0000000 0000000 00000005160 13233655467 0035442 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2017, 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.nio.ByteBuffer;
import com.sun.jna.*;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.W32APIOptions;
public interface NGWin32NamedPipeLibrary extends WinNT {
int PIPE_ACCESS_DUPLEX = 3;
int PIPE_UNLIMITED_INSTANCES = 255;
int FILE_FLAG_FIRST_PIPE_INSTANCE = 524288;
NGWin32NamedPipeLibrary INSTANCE =
(NGWin32NamedPipeLibrary) Native.loadLibrary(
"kernel32",
NGWin32NamedPipeLibrary.class,
W32APIOptions.UNICODE_OPTIONS);
HANDLE CreateNamedPipe(
String lpName,
int dwOpenMode,
int dwPipeMode,
int nMaxInstances,
int nOutBufferSize,
int nInBufferSize,
int nDefaultTimeOut,
SECURITY_ATTRIBUTES lpSecurityAttributes);
boolean ConnectNamedPipe(
HANDLE hNamedPipe,
Pointer lpOverlapped);
boolean DisconnectNamedPipe(
HANDLE hObject);
boolean ReadFile(
HANDLE hFile,
Memory lpBuffer,
int nNumberOfBytesToRead,
IntByReference lpNumberOfBytesRead,
Pointer lpOverlapped);
boolean WriteFile(
HANDLE hFile,
ByteBuffer lpBuffer,
int nNumberOfBytesToWrite,
IntByReference lpNumberOfBytesWritten,
Pointer lpOverlapped);
boolean CloseHandle(
HANDLE hObject);
boolean GetOverlappedResult(
HANDLE hFile,
Pointer lpOverlapped,
IntByReference lpNumberOfBytesTransferred,
boolean wait);
boolean CancelIoEx(
HANDLE hObject,
Pointer lpOverlapped);
HANDLE CreateEvent(
SECURITY_ATTRIBUTES lpEventAttributes,
boolean bManualReset,
boolean bInitialState,
String lpName);
int WaitForSingleObject(
HANDLE hHandle,
int dwMilliseconds
);
int GetLastError();
}
NGWin32NamedPipeServerSocket.java 0000664 0000000 0000000 00000017600 13233655467 0036457 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2017, 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 com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class NGWin32NamedPipeServerSocket extends ServerSocket {
private static final NGWin32NamedPipeLibrary API = NGWin32NamedPipeLibrary.INSTANCE;
private static final String WIN32_PIPE_PREFIX = "\\\\.\\pipe\\";
private static final int BUFFER_SIZE = 65535;
private final LinkedBlockingQueueCollects and provides statistics on a nail.
* * @author Marty Lamb */ public class NailStats implements Cloneable { private final Class nailclass; private long runCounter; private long refCounter; private final 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() { synchronized (lock) { return (runCounter); } } /** * Returns the number of sessions currently running this nail. * @return the number of sessions currently running this nail. */ public long getRefCount() { synchronized (lock) { 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 specifiedNailStats
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());
}
}
NonStaticNail.java 0000664 0000000 0000000 00000000522 13233655467 0033706 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun package com.martiansoftware.nailgun;
/** Allows providing a instance (non-static) main method.
* Potentially helpful for users of JVM languages other than Java.
*
* Implementations of this interface MUST provide a public, no-args constructor. */
public interface NonStaticNail {
public void nailMain(String[] args);
} ReferenceCountedFileDescriptor.java 0000664 0000000 0000000 00000003744 13233655467 0037270 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/nailgun-server/src/main/java/com/martiansoftware/nailgun /*
Copyright 2004-2015, 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 com.sun.jna.LastErrorException;
import java.io.IOException;
/**
* Encapsulates a file descriptor plus a reference count to ensure close requests
* only close the file descriptor once the last reference to the file descriptor
* is released.
*
* If not explicitly closed, the file descriptor will be closed when
* this object is finalized.
*/
public class ReferenceCountedFileDescriptor {
private int fd;
private int fdRefCount;
private boolean closePending;
public ReferenceCountedFileDescriptor(int fd) {
this.fd = fd;
this.fdRefCount = 0;
this.closePending = false;
}
protected void finalize() throws IOException {
close();
}
public synchronized int acquire() {
fdRefCount++;
return fd;
}
public synchronized void release() throws IOException {
fdRefCount--;
if (fdRefCount == 0 && closePending && fd != -1) {
doClose();
}
}
public synchronized void close() throws IOException {
if (fd == -1 || closePending) {
return;
}
if (fdRefCount == 0) {
doClose();
} else {
// Another thread has the FD. We'll close it when they release the reference.
closePending = true;
}
}
private void doClose() throws IOException {
try {
NGUnixDomainSocketLibrary.close(fd);
fd = -1;
} catch (LastErrorException e) {
throw new IOException(e);
}
}
}
ThreadLocalInputStream.java 0000664 0000000 0000000 00000007764 13233655467 0035575 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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.java 0000664 0000000 0000000 00000014340 13233655467 0035556 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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.3/nailgun-server/src/main/java/com/martiansoftware/nailgun/builtins/ 0000775 0000000 0000000 00000000000 13233655467 0032246 5 ustar 00root root 0000000 0000000 DefaultNail.java 0000664 0000000 0000000 00000002351 13233655467 0035223 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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.java 0000664 0000000 0000000 00000005403 13233655467 0034312 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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.java 0000664 0000000 0000000 00000005443 13233655467 0035207 0 ustar 00root root 0000000 0000000 nailgun-nailgun-all-0.9.3/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
".
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
".
Shuts down the currently running server.
* *This is aliased by default to the command "ng-stop
".