From: gumartinm Date: Sun, 29 Jan 2012 16:00:44 +0000 (+0100) Subject: first commit X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=6c2426dc33d8f1b623cc221e8241a3d4844cbd8c;p=JavaTCPFork%2F.git first commit --- 6c2426dc33d8f1b623cc221e8241a3d4844cbd8c diff --git a/Daemon/Makefile b/Daemon/Makefile new file mode 100644 index 0000000..d785678 --- /dev/null +++ b/Daemon/Makefile @@ -0,0 +1,8 @@ +all: javafork + +javafork: javafork.c javafork.h + gcc -Wall -g -o javafork javafork.c -lpthread -D_GNU_SOURCE + +clean: + rm -f javafork + diff --git a/Daemon/README.txt b/Daemon/README.txt new file mode 100644 index 0000000..604f093 --- /dev/null +++ b/Daemon/README.txt @@ -0,0 +1,3 @@ +Launch: + +javafork IPADDRESS TCP_PORT MAX_LISTEN diff --git a/Daemon/javafork b/Daemon/javafork new file mode 100755 index 0000000..135062e Binary files /dev/null and b/Daemon/javafork differ diff --git a/Daemon/javafork.c b/Daemon/javafork.c new file mode 100644 index 0000000..9fe2210 --- /dev/null +++ b/Daemon/javafork.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "javafork.h" + + +pid_t daemonPID; /*Stores the daemon server PID*/ +int sockfd = -1; /*Stores the daemon server TCP socket.*/ + + + +static int restartableClose(int fd) +{ + return TEMP_FAILURE_RETRY(close(fd)); +} + + + +static int closeSafely(int fd) +{ + /*If we always initialize file descriptor variables with value -1*/ + /*this method should work like a charm*/ + return (fd == -1) ? 0 : restartableClose(fd); +} + + + +int main (int argc, char *argv[]) +{ + int c; /*Getopt parameter*/ + /*Default values*/ + char *avalue = IPADDRESS; /*Address: numeric value or hostname*/ + int pvalue = PORT; /*TCP port*/ + int qvalue = QUEUE; /*TCP listen queue*/ + + + /*This process is intended to be used as a daemon, it sould be launched by the INIT process, because of that*/ + /*we are not forking it (INIT needs it)*/ + if (daemonize(argv[0], LOG_SYSLOG, LOG_PID) < 0) + return 1; + + /*Changing session.*/ + setsid(); + + if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) { + syslog (LOG_ERR, "signal SIGPIPE desactivation failed: %m"); + return 1; + } + + opterr = 0; + while ((c = getopt (argc, argv, "a:p:q:")) != -1) { + switch (c) { + case 'a': + avalue = optarg; + break; + case 'p': + pvalue = atoi(optarg); + if ((pvalue > 65535) || (pvalue <= 0)) { + syslog (LOG_ERR, "Port value %d out of range", pvalue); + return 1; + } + break; + case 'q': + qvalue = atoi(optarg); + break; + case '?': + if ((optopt == 'a') || (optopt == 'p') || (optopt == 'q')) + syslog (LOG_ERR, "Option -%c requires an argument.", optopt); + else if (isprint (optopt)) + syslog (LOG_ERR, "Invalid option '-%c'.", optopt); + else + syslog (LOG_ERR, "Unknown option character '\\x%x'.", optopt); + return 1; + default: + abort (); + } + } + + /*This program does not admit options*/ + if (optind < argc) { + syslog (LOG_ERR,"This program does not admit options just argument elements with their values."); + return 1; + } + + /*INIT process sending SIGINT? Should I catch that signal?*/ + daemonPID = getpid(); + if (signal(SIGINT, sigint_handler) == SIG_ERR) { + syslog (LOG_ERR, "SIGTERM signal handler failed: %m"); + return 1; + } + + if (main_daemon (avalue, pvalue, qvalue) < 0) + return 1; + + return 0; +} + + + +int main_daemon (char *address, int port, int queue) +{ + struct protoent *protocol; /*Network protocol*/ + struct sockaddr_in addr_server; /*struct with the server socket address*/ + struct sockaddr_in addr_client; /*struct with the client socket address*/ + int sockclient = -1; /*File descriptor for the accepted socket*/ + pthread_t idThread; /*Thread identifier number*/ + socklen_t clilen; + int optval; + int returnValue = 0; /*The return value from this function, OK by default*/ + + + /*Retrieve protocol number from /etc/protocols file */ + protocol=getprotobyname("tcp"); + if (protocol == NULL) { + syslog(LOG_ERR, "cannot map \"tcp\" to protocol number: %m"); + goto err; + } + + bzero((char*) &addr_server, sizeof(addr_server)); + addr_server.sin_family = AF_INET; + if (inet_pton(AF_INET, address, &addr_server.sin_addr.s_addr) <= 0) { + syslog (LOG_ERR, "error inet_pton: %m"); + goto err; + } + + addr_server.sin_port = htons(port); + + if ((sockfd = socket(AF_INET, SOCK_STREAM, protocol->p_proto)) < 0) { + syslog (LOG_ERR, "socket creation failed: %m"); + goto err; + } + + + /*We want to avoid issues while trying to bind a socket in TIME_WAIT state*/ + optval = 1; + if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + syslog (LOG_ERR, "setsockopt failed: %m"); + goto err; + } + + if (bind(sockfd, (struct sockaddr *) &addr_server, sizeof(addr_server)) < 0) { + syslog (LOG_ERR, "socket bind failed: %m"); + goto err; + } + + if (listen (sockfd, queue) < 0 ) { + syslog (LOG_ERR, "socket listen failed: %m"); + goto err; + } + + while(1) { + clilen = sizeof(addr_client); + if ((sockclient = TEMP_FAILURE_RETRY(accept (sockfd, (struct sockaddr *) &addr_client, &clilen))) < 0) { + syslog (LOG_ERR, "socket accept failed: %m"); + goto err; + } + + if (pthread_create (&idThread, NULL, serverThread, (void *)sockclient) != 0 ) { + syslog (LOG_ERR, "thread creation failed: %m"); + } + } + +end: + closeSafely (sockfd); + return returnValue; +err: + /*When there is an error.*/ + returnValue = -1; + goto end; +} + + + +int daemonize(const char *pname, int facility, int option) +{ + int fd = -1; /*Temporaly store for the /dev/tty and /dev/null file descriptors*/ + + if ((fd = TEMP_FAILURE_RETRY(open( "/dev/tty", O_RDWR, 0))) == -1) { + /*We already have no tty control*/ + closeSafely(fd); + return 0; + } + + /*Sending messages to log*/ + openlog(pname, option, facility); + + /*To get a controlling tty*/ + if (ioctl(fd, TIOCNOTTY, (caddr_t)0) <0 ) { + syslog (LOG_ERR, "Getting tty failed: %m"); + return -1; + } + + if (closeSafely(fd) < 0) { + syslog (LOG_ERR, "Closing tty failed: %m"); + return -1; + } + + if ((fd = TEMP_FAILURE_RETRY(open( "/dev/null", O_RDWR, 0))) == -1) { + closeSafely(fd); + return -1; + } + + if (TEMP_FAILURE_RETRY(dup2(fd,0)) < 0 || + TEMP_FAILURE_RETRY(dup2(fd,1)) < 0 || + TEMP_FAILURE_RETRY(dup2(fd,2)) < 0) { + closeSafely(fd); + return -1; + } + + closeSafely(fd); + + return 0; +} + + + +void *serverThread (void * arg) +{ + int socket = -1; /*Open socket by the Java client*/ + long timeout, utimeout; /*Timeout for reading data from client: secs and usecs respectively*/ + int len; /*Control parameter used while receiving data from the client*/ + char buffer[1025]; /*This buffer is intended to store the data received from the client*/ + char *command = NULL; /*The command sent by the client, to be executed by this process*/ + uint32_t *commandLength = NULL; /*Store the command length*/ + + socket = (int) arg; + + pthread_detach(pthread_self()); + + + if (required_sock_options (socket) < 0) + goto err; + + + /****************************************************************************************/ + /* Just over 1 TCP connection */ + /* COMMAND_LENGTH: Java integer 4 bytes, BIG-ENDIAN (the same as network order) */ + /* COMMAND: locale character set encoding */ + /* RESULTS: locale character set encoding */ + /* */ + /* JAVA CLIENT: ------------ COMMAND_LENGTH -------> :SERVER */ + /* JAVA CLIENT: -------------- COMMAND ------------> :SERVER */ + /* JAVA CLIENT: <-------------- RESULTS ------------ :SERVER */ + /* JAVA CLIENT: <---------- CLOSE CONNECTION ------- :SERVER */ + /* */ + /****************************************************************************************/ + + /*Wait max 2 seconds for data coming from client, otherwise exits with error.*/ + timeout = 2; + utimeout = 0; + + + /*1. COMMAND LENGTH*/ + /*First of all we receive the command size as a Java integer (4 bytes primitive type)*/ + if ((commandLength = (uint32_t *) malloc(sizeof(uint32_t))) == NULL) { + syslog (LOG_ERR, "commandLength malloc failed: %m"); + goto err; + } + + bzero(buffer, sizeof(buffer)); + len = sizeof(uint32_t); + + if (receive_from_socket (socket, buffer, len, timeout, utimeout) < 0) + goto err; + + /*Retrieve integer (4 bytes) from buffer*/ + memcpy (commandLength, buffer, sizeof(uint32_t)); + /*Java sends the primitive integer using big-endian order (it is the same as network order)*/ + *commandLength = be32toh (*commandLength); + + + /*2. COMMAND*/ + /*Reserving commandLength + 1 because of the string end character*/ + if ((command = (char *) malloc(*commandLength + 1)) == NULL) { + syslog (LOG_ERR, "command malloc failed: %m"); + goto err; + } + + bzero(command, ((*commandLength) + 1)); + len = *commandLength; + /*Wait max 2 seconds for data coming from client, otherwise exits with error.*/ + if (receive_from_socket (socket, command, len, timeout, utimeout) < 0) + goto err; + + + /*3. RESULTS*/ + pre_fork_system(socket, command); + + + /*4. CLOSE CONNECTION AND FINISH*/ + +err: + free(command); + closeSafely(socket); + free(commandLength); + + pthread_exit(0); +} + + + +int required_sock_options (int socket) +{ + int optval, flags; + + /*We want non blocking sockets.*/ + /*See the discussion of spurious readiness notifications under the BUGS section of select(2) */ + if ((flags = TEMP_FAILURE_RETRY(fcntl(socket,F_GETFL,0))) < 0) { + syslog (LOG_ERR, "read socket status flags failed: %m"); + return -1; + } + + if (TEMP_FAILURE_RETRY(fcntl(socket, F_SETFL, O_NONBLOCK|flags)) < 0){ + syslog (LOG_ERR, "set socket status flags failed: %m"); + return -1; + } + + /*Portable programs should not rely on inheritance or noninheritance of file status flags and */ + /*always explicitly set all required flags*/ + optval = 1; + if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + syslog (LOG_ERR, "setsockopt SO_REUSEADDR failed: %m"); + return -1; + } + + /*Enable keepalive for this socket*/ + optval = 1; + if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) { + syslog (LOG_ERR, "setsockopt SO_KEEPALIVE failed: %m"); + return -1; + } + + /*TODO: keepalive is not enough to find out if the connection is broken */ + /* apparently it just works while the handshake phase. See: */ + /* http://stackoverflow.com/questions/4345415/socket-detect-connection-is-lost */ + /* I have to implement an echo/ping messages protocol (application layer) */ + + return 0; +} + + + +int readable_timeout (int fd, long timeout, long utimeout) +{ + struct timeval ptime; /*Timeout, secs and usecs*/ + fd_set fd_read; /*Values for select function.*/ + + ptime.tv_sec = timeout; + ptime.tv_usec = utimeout; + FD_ZERO(&fd_read); + FD_SET(fd, &fd_read); + + return TEMP_FAILURE_RETRY(select(fd+1, &fd_read, NULL, NULL, &ptime)); +} + + + +int receive_from_socket (int socket, char *data, int len, long timeout, long utimeout) +{ + int nData, iPos; /*Control variables.*/ + int ret; /*Store return value from select.*/ + + nData = iPos = 0; + do { + ret = readable_timeout(socket, timeout, utimeout); + + if (ret == 0) { + syslog(LOG_INFO, "receiving timeout error"); + return -1; + } else if (ret == -1) { + syslog(LOG_ERR, "receiving error: %m"); + return -1; + } + + nData = TEMP_FAILURE_RETRY(recv(socket, &data[iPos], len, 0)); + + if (nData < 0) { + if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { + syslog (LOG_ERR, "read TCP socket failed: %m"); + return -1; + } else { + /*see spurious readiness man select(2) BUGS section*/ + nData = 0; + syslog (LOG_INFO, "read TCP socket spurious readiness"); + } + } else if (nData == 0) { + /*if nData is 0, client closed connection but we wanted to receive more data, this is an error */ + syslog (LOG_ERR, "expected more data from client"); + return -1; + } + + len -= nData; + iPos += nData; + } while (len > 0); + + return 0; +} + + +int pre_fork_system(int socket, char *command) +{ + /*Required variables in order to share memory between processes*/ + key_t keyvalue; + int idreturnstatus = -1; + /*Store the return status from the process launched using system or execve*/ + /*Using shared memory between the child and parent process*/ + int *returnstatus = NULL; + + /*Required variables in order to share the semaphore between processes*/ + key_t keysemaphore; + int idsemaphore = -1; + sem_t *semaphore = NULL; /*Used as a barrier: the child process just can start after sending the XML init code*/ + + int returnValue = -1; /*Return value from this function can be caught by upper layers, NOK by default*/ + + + + /*Allocate shared memory because we can not use named semaphores*/ + /*We are using this semaphore as a barrier, because we just want to start the child process when the parent process has sent*/ + /*the XML header (see: for_system function)*/ + + keysemaphore=ftok("/bin/ls", SHAREMEMSEM); /*the /bin/ls must exist otherwise this does not work... */ + if (keysemaphore == -1) { + syslog (LOG_ERR, "ftok failed: %m"); + goto end; + } + + /*Attach shared memory*/ + if ((idsemaphore = shmget(keysemaphore,sizeof(sem_t), 0660 | IPC_CREAT)) < 0) { + syslog (LOG_ERR, "semaphore initialization failed: %m"); + goto end_release_sem; + } + + if ((semaphore = (sem_t *)shmat(idsemaphore, (void *)0, 0)) < 0) { + goto end_release_sem; + } + + if (sem_init(semaphore, 1, 1) < 0) { + syslog (LOG_ERR, "semaphore initialization failed: %m"); + goto end_destroy_sem; + } + + if (TEMP_FAILURE_RETRY(sem_wait(semaphore)) < 0) { + syslog (LOG_ERR, "semaphore wait failed: %m"); + goto end_destroy_sem; + } + + + + /*Allocate shared memory for the return status code from the process which is going to be launched by the system function.*/ + /*We want to share the returnstatus variable between this process and the child that is going to be created in the fork_system method.*/ + /*The goal is to store in this variable the return status code received from the process launched with the system method by the child process,*/ + /*then the parent process can retrieve that return status code and send it by TCP to the Java client.*/ + /*There are not concurrency issues because the parent process will just try to read this variable when the child process is dead, taking*/ + /*in that moment its last value and sending it to the Java client.*/ + + + keyvalue=ftok("/bin/ls", SHAREMEMKEY); /*the /bin/ls must exist otherwise this does not work... */ + if (keyvalue == -1) { + syslog (LOG_ERR, "ftok failed: %m"); + goto end_destroy_sem; + } + + /*Attach shared memory*/ + if ((idreturnstatus=shmget(keyvalue,sizeof(int), 0660 | IPC_CREAT)) < 0) { + syslog (LOG_ERR, "shmget failed: %m"); + goto end_release_mem; + } + + returnstatus = (int *)shmat(idreturnstatus, (void *)0, 0); + if ((*returnstatus)== -1) { + syslog (LOG_ERR, "shmat failed: %m"); + goto end_release_mem; + } + + /*After allocating and attaching shared memory we reach this code if everything went OK.*/ + + returnValue = fork_system(socket, command, semaphore, returnstatus); + + +end_release_mem: + if (returnstatus != NULL) { + /*detach memory*/ + if (shmdt ((int *)returnstatus) < 0) + syslog (LOG_ERR, "returnstatus shared variable shmdt failed: %m"); + } + + /*Mark the segment to be destroyed.*/ + if (shmctl (idreturnstatus, IPC_RMID, (struct shmid_ds *)NULL) < 0 ) + syslog (LOG_ERR, "returnstatus shared variable shmctl failed: %m"); +end_destroy_sem: + if (sem_destroy(semaphore) <0) + syslog (LOG_ERR, "semaphore destroy failed: %m"); +end_release_sem: + /*after sem_destroy-> input/output parameter NULL?*/ + if (semaphore != NULL) { + /*detach memory*/ + if (shmdt ((sem_t *)semaphore) < 0) + syslog (LOG_ERR, "semaphore shmdt failed: %m"); + } + + /*Mark the segment to be destroyed.*/ + if (shmctl (idsemaphore, IPC_RMID, (struct shmid_ds *)NULL) < 0 ) + syslog (LOG_ERR, "semaphore shmctl failed: %m"); +end: + return returnValue; +} + + + +int fork_system(int socket, char *command, sem_t *semaphore, int *returnstatus) +{ + int pid; + int out[2], err[2]; /*Store pipes file descriptors. Write ends attached to the stdout and stderr streams.*/ + char buf[2000]; /*Read data buffer.*/ + char string[100]; + /*We are going to use a poll in order to find out if there are data coming from the*/ + /*pipes attached to the stdout and stderr streams.*/ + struct pollfd polls[2]; + int n; + int childreturnstatus; + int returnValue = 0; /*return value from this function can be caught by upper layers, OK by default*/ + + + /*Value by default*/ + (*returnstatus) = 0; + + + out[0] = out[1] = err[0] = err[1] = -1; + + + /*Creating the pipes, they will be attached to the stderr and stdout streams*/ + if (pipe(out) < 0 || pipe(err) < 0) { + syslog (LOG_ERR, "pipe failed: %m"); + goto err; + } + + if ((pid=fork()) == -1) { + syslog (LOG_ERR, "fork failed: %m"); + goto err; + } + + if (pid == 0) { + /*Child process*/ + /*It has to launch another one using system or execve*/ + if ((TEMP_FAILURE_RETRY(dup2(out[1],1)) < 0) || (TEMP_FAILURE_RETRY(dup2(err[1],2)) < 0)) { + syslog (LOG_ERR, "child dup2 failed: %m"); + /*Going to zombie state, hopefully waitpid will catch it*/ + exit(-1); + } + + if (TEMP_FAILURE_RETRY(sem_wait(semaphore)) < 0) { + syslog (LOG_ERR, "child semaphore wait failed: %m"); + /*Going to zombie state, hopefully waitpid will catch it*/ + exit(-1); + } + + *returnstatus=system(command); + if (WIFEXITED(returnstatus) == 1) + (*returnstatus) = WEXITSTATUS(*returnstatus); + else + (*returnstatus) = -1; + /*Going to zombie state, hopefully waitpid will catch it*/ + exit(0); + } + else { + /*Parent process*/ + /*It sends data to the Java client using a TCP connection.*/ + polls[0].fd=out[0]; + polls[1].fd=err[0]; + polls[0].events=POLLIN; + polls[1].events=POLLIN; + sprintf(string,""); + send(socket,string,strlen(string),0); + sprintf(string,""); + send(socket,string,strlen(string),0); + + /*Releasing barrier, the child process can keep running*/ + if (sem_post(semaphore) < 0 ) { + syslog (LOG_ERR, "parent error releasing barrier: %m"); + /*TODO: May I kill a child process if it is already dead? I mean, */ + /* what could happen if the child process is dead? */ + /* Should I implement a SIGCHILD handler? */ + /*TODO: Should I have a SIGTERM handler in the child process? */ + kill(pid, SIGTERM); + goto err; + } + + while(1) { + if(poll(polls,2,100)) { + if(polls[0].revents&&POLLIN) { + bzero(buf,2000); + n=read(out[0],buf,1990); + sprintf(string,""); + send(socket,string,strlen(string),0); + } + + if(polls[1].revents&&POLLIN) { + bzero(buf,2000); + n=read(err[0],buf,1990); + sprintf(string,""); + send(socket,string,strlen(string),0); + } + + if(!polls[0].revents&&POLLIN && !polls[1].revents&&POLLIN) { + syslog (LOG_ERR, "parent error polling pipes: %m"); + /*TODO: May I kill a child process if it is already dead? I mean, */ + /* what could happen if the child process is dead? */ + /* Should I implement a SIGCHILD handler? */ + /*TODO: Should I have a SIGTERM handler in the child process? */ + kill(pid, SIGTERM); + /*I want to send an error status to the remote calling process */ + /*TODO: Before giving this value I should make sure the child process is dead */ + /* otherwise I could finish having in *returnstatus the value from the child process */ + (*returnstatus) = -1; + break; + } + } + else { + /*When timeout*/ + if(waitpid(pid, &childreturnstatus, WNOHANG)) { + /*Child is dead, we can finish the connection*/ + /*First of all, we check the exit status of our child process*/ + /*In case of error send an error status to the remote calling process*/ + if (WIFEXITED(childreturnstatus) != 1) + (*returnstatus) = -1; + break; + } + /*The child process is not dead, keep polling more data from stdout or stderr streams*/ + } + } + } + /*Reaching this code when child finished or if error while polling pipes*/ + sprintf(string,"", (*returnstatus)); + send(socket,string,strlen(string),0); + sprintf(string,""); + send(socket,string,strlen(string),0); + + /*Stuff just done by the Parent process. The child process ends with exit*/ + +end: + closeSafely (out[0]); + closeSafely (out[1]); + closeSafely (err[0]); + closeSafely (err[1]); + return returnValue; +err: + returnValue = -1; + goto end; +} + + +void sigint_handler(int sig) +{ + if (daemonPID != getpid()) { + //Do nothing + return; + } + + if (signal (SIGINT, SIG_IGN) == SIG_ERR) + syslog (LOG_ERR, "signal SIGINT desactivation failed: %m"); + + closeSafely (sockfd); + /*TODO: kill child processes and release allocate memory*/ + exit (0); +} diff --git a/Daemon/javafork.h b/Daemon/javafork.h new file mode 100644 index 0000000..8abacc1 --- /dev/null +++ b/Daemon/javafork.h @@ -0,0 +1,77 @@ +/*System V IPC keys*/ +#define SHAREMEMKEY 1 +#define SHAREMEMSEM 2 + +/*Non-argument default values*/ +#define PORT 5193 +#define IPADDRESS "127.0.0.1" +#define QUEUE 6 + + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* RETURNS: void * +****************************************************************************************/ +void *serverThread (void *arg); + + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* INPUT PARAMETER: +* INPUT PARAMETER: +* RETURNS: void * +****************************************************************************************/ +int daemonize(const char *pname, int facility, int option); + + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* RETURNS: int * +****************************************************************************************/ +int main_daemon (char *address, int port, int queue); + + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* RETURNS: void * +****************************************************************************************/ +int fork_system(int socket, char *command, sem_t *semaphore, int *returnst); + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* RETURNS: void * +****************************************************************************************/ +int pre_fork_system(int socket, char *command); + + + + + +/**************************************************************************************** +* This method is used by pthread_create * +* * +* INPUT PARAMETER: socket file descriptor * +* RETURNS: void * +****************************************************************************************/ +void sigint_handler(); + + + +int required_sock_options (int socket); +int receive_from_socket (int socket, char *data, int len, long timeout, long utimeout); +int readable_timeout (int fd, long timeout, long utimeout); diff --git a/JavaExample/javafork-example/pom.xml b/JavaExample/javafork-example/pom.xml new file mode 100644 index 0000000..f24711c --- /dev/null +++ b/JavaExample/javafork-example/pom.xml @@ -0,0 +1,22 @@ + + + + + 4.0.0 + + javafork + de.fork.java + 2.0-SNAPSHOT + + + javafork-example + javafork-example + http://gumartinm.name + + + + + diff --git a/JavaExample/javafork-example/src/main/java/de/fork/java/LauncherProcesses.java b/JavaExample/javafork-example/src/main/java/de/fork/java/LauncherProcesses.java new file mode 100644 index 0000000..dfb7d14 --- /dev/null +++ b/JavaExample/javafork-example/src/main/java/de/fork/java/LauncherProcesses.java @@ -0,0 +1,285 @@ +package de.fork.java; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.UnknownHostException; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.log4j.Logger; +import org.xml.sax.SAXException; + +/** + * + */ +public class LauncherProcesses { + // Exit process status + private static final int STATUS_ERR = -1; + private static final int DEFAULT_PORT = 5193; + private static final String DEFAULT_HOST = "127.0.0.1"; + + /** + * Run a process. + * + * @param command system command to be executed. + * + * @return return code. + */ + public static int exec(final String command) throws IOException, InterruptedException { + + return exec(command, null, null); + } + + /** + * Run a process. + * + * @param command system command to execute. + * @param standarOutPut if not null, the standard output is redirected to this parameter. + * + * @return return code. + */ + public static int exec(final String command, final PrintStream standarOutPut) throws IOException, InterruptedException { + + return exec(command, standarOutPut, null); + } + + + /** + * Run a process. + * + * @param command system command to be executed. + * @param standarOutPut if not null, the standard output is redirected to this parameter. + * @param errorOutPut if not null, the error output is redirected to this parameter. + * + * @return return code from the executed system command. + */ + public static int exec(final String command, final PrintStream standarOutPut, final PrintStream errorOutPut) throws IOException, InterruptedException { + + return exec(command, standarOutPut, errorOutPut, DEFAULT_HOST, DEFAULT_PORT); + } + + /** + * Run a process. + * + * @param command system command to be executed. + * @param aLogger send the information to log. + */ + public static int exec(final String command, final Logger aLogger) throws IOException, InterruptedException { + + //calling private method to handle logger input/ouput in a common method + return execHandlingLogger(command, aLogger, DEFAULT_HOST, DEFAULT_PORT); + } + + + /** + * Run process. + * + * @param commandAndArguments String array containing system command and its + * arguments to be executed.
+ * For example: + *
+	 * commandAndArguments[0]="ls";
+	 * commandAndArguments[1]="-lr";
+	 * 
+ * @param aLogger + * + * @return return code from the executed system command. + * + * @throws IOException + * @throws InterruptedException + */ + public static int exec(final String[] commandAndArguments, final Logger aLogger) throws IOException, InterruptedException { + String wholeCommand=""; + + for(String argument : commandAndArguments) { + wholeCommand = wholeCommand + " " + argument; + } + + //calling private method to handle logger input/ouput in a common method + return execHandlingLogger(wholeCommand, aLogger, DEFAULT_HOST, DEFAULT_PORT); + } + + + /** + * Run process using a remote process runner. + * + * @param command system command to be executed. + * @param standarOutPut the stdout stream from that command as a PrintStream + * @param errorOutPut the stderr stream from that command as a PrintStream + * @param host the specified host. + * @param port the where the remote process runner accepts connections. + * + *

The host name can either be a machine name, such as + * "java.sun.com", or a textual representation of its + * IP address. If a literal IP address is supplied, only the + * validity of the address format is checked. + *

+ *

For host specified in literal IPv6 address, + * either the form defined in RFC 2732 or the literal IPv6 address + * format defined in RFC 2373 is accepted. IPv6 scoped addresses are also + * supported. See here for a description of IPv6 + * scoped addresses. + *

+ * + * @return the executed command's return code. + * + * @throws UnknownHostException + * @throws IOException + */ + public static int exec(final String command, final PrintStream standarOutPut, + final PrintStream errorOutPut, final String host, final int port) + throws IOException, InterruptedException { + int exitStatus = LauncherProcesses.STATUS_ERR; + XmlForkParser forkParser = null; + TCPForkDaemon process = null; + + try { + forkParser = new XmlForkParser(); + process = new TCPForkDaemon(forkParser, host, port); + exitStatus = process.exec(command); + } catch (ParserConfigurationException e) { + // This is not a crazy thing, we are trying to insert this new method without + // breaking the old methods which did not throw SAXException or ParserConfigurationException + // Do not blame me. + throw new IOException(e); + } catch (SAXException e) { + // This is not a crazy thing, we are trying to insert this new method without + // breaking the old methods which did not throw SAXException or ParserConfigurationException + // Do not blame me. + throw new IOException(e); + } + + + + if ((standarOutPut != null) && (process.getStdout() != null)){ + standarOutPut.println(process.getStdout()); + } + + if ((errorOutPut != null) && (process.getStderr() != null)){ + errorOutPut.println(process.getStderr()); + } + + return exitStatus; + } + + + /** + * Run process. + * + * @param command system command to be executed. + * @param aLogger + * @param host the specified host. + * @param port the TCP port where the daemon accepts connections. + * + * @return the executed command's return code. + * + * @throws IOException + * @throws InterruptedException + */ + private static int execHandlingLogger(final String command, final Logger aLogger, + final String host, int port) throws IOException, InterruptedException { + int exitStatus = LauncherProcesses.STATUS_ERR; + XmlForkParser forkParser = null; + TCPForkDaemon process = null; + + try { + forkParser = new XmlForkParser(); + process = new TCPForkDaemon(forkParser, host, port); + exitStatus = process.exec(command); + } catch (ParserConfigurationException e) { + // This is not a crazy thing, we are trying to insert this new method without + // breaking the old methods which did not throw SAXException or ParserConfigurationException + // Do not blame me. + throw new IOException(e); + } catch (SAXException e) { + // This is not a crazy thing, we are trying to insert this new method without + // breaking the old methods which did not throw SAXException or ParserConfigurationException + // Do not blame me. + throw new IOException(e); + } + + + + if (process.getStdout() != null) { + aLogger.info(process.getStdout()); + } + if (process.getStderr() != null) { + aLogger.error(process.getStderr()); + } + + return exitStatus; + } + + + /** + * Run process + * + * @param command command and its arguments to be executed.
+ * For example: + *
+	 * commandAndArguments[0]="ls";
+	 * commandAndArguments[1]="-lr";
+	 * 
+ * @param aLogger send information to log + * + * @return the executed command's return code. + * + * @throws IOException + * @throws InterruptedException + */ + public static InputStream execStream (final String [] command, final Logger aLogger) + throws IOException, InterruptedException { + int exitStatus = LauncherProcesses.STATUS_ERR; + InputStream stdInput = null; + XmlForkParser forkParser = null; + TCPForkDaemon process = null; + String wholeCommand=""; + + for(String argument : command) { + wholeCommand = wholeCommand + " " + argument; + } + + try { + forkParser = new XmlForkParser(); + process = new TCPForkDaemon(forkParser, DEFAULT_HOST, DEFAULT_PORT); + exitStatus = process.exec(wholeCommand); + } catch (ParserConfigurationException e) { + throw new IOException(e); + } catch (SAXException e) { + throw new IOException(e); + } + + + if(exitStatus == 0) { + stdInput = new ByteArrayInputStream(process.getStdout().getBytes("UTF-8")); + } + else { + aLogger.error(process.getStderr()); + } + + + return stdInput; + } + + /** + *

The command is lunched from location + *

  • #>cd location
  • + *
  • #location> command
  • + * + * @param command the command to be executed by the daemon. + * @param location + * + * @return the executed command's return code.
    + * Usually 0 if execution is OK, otherwise !=0 + * + * @throws IOException + * @throws InterruptedException + */ + public static int execInLocation (final String command, final String location) throws IOException, InterruptedException { + int exitStatus = LauncherProcesses.STATUS_ERR; + final String wholeCommand = "cd " + location + " && " + command; + + exitStatus = exec(wholeCommand, null, null, DEFAULT_HOST, DEFAULT_PORT); + return exitStatus; + } +} diff --git a/JavaExample/javafork-example/src/main/java/de/fork/java/RemoteForkMain.java b/JavaExample/javafork-example/src/main/java/de/fork/java/RemoteForkMain.java new file mode 100644 index 0000000..5b9c57d --- /dev/null +++ b/JavaExample/javafork-example/src/main/java/de/fork/java/RemoteForkMain.java @@ -0,0 +1,36 @@ +package de.fork.java; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; + + +public class RemoteForkMain { + + /** + * @param args + * @throws InterruptedException + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + * @throws FileNotFoundException + */ + public static void main(String[] args) throws IOException, InterruptedException { + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final PrintStream stdout = new PrintStream(baos); + final String command = "ls -lah ~/Desktop; ls -lah * bbbb aaa"; + final ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + final PrintStream stderr = new PrintStream(baos2); + int result; + + result = LauncherProcesses.exec(command,stdout, stderr, "127.0.0.1", 5193); + System.out.println(result); + System.out.println("Stdout: " + baos.toString()); + System.out.println("Stderr: " + baos2.toString()); + } + +} \ No newline at end of file diff --git a/JavaExample/javafork-example/src/main/java/de/fork/java/TCPForkDaemon.java b/JavaExample/javafork-example/src/main/java/de/fork/java/TCPForkDaemon.java new file mode 100644 index 0000000..c9da0cb --- /dev/null +++ b/JavaExample/javafork-example/src/main/java/de/fork/java/TCPForkDaemon.java @@ -0,0 +1,184 @@ +package de.fork.java; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + *

    + * With this class we can run processes using the intended daemon which is + * waiting for TCP connections in a specified port. + *

    + *

    + * Receiving the results from the daemon where we can find three kinds of + * different fields: stderror, stdout and the return value of the command which was + * run by the remote daemon. Each field is related to the stderr, stdout and + * return code respectively. + *

    + *

    + * This class has to retrieve the results from the remote daemon and it offers two + * methods wich can be used to retrieve the stderr and stdout in a right way + * without having to know about the coding used by the daemon to send us the results. + * The user does not have to know about how the daemon sends the data, he or she + * will work directly with the strings related to each stream using these methods: + * {@link TCPForkDaemon#getStdout()} and {@link TCPForkDaemon#getStderr()}. + * The return code from the command executed by the daemon can be retrieved as the + * return parameter from the method {@link TCPForkDaemon#exec(String, String, int)} + *

    + *

    + * Instances of this class are mutable. To use them concurrently, clients must surround each + * method invocation (or invocation sequence) with external synchronization of the clients choosing. + *

    + */ +public class TCPForkDaemon { + private final XmlForkParser parser; + private final String host; + private final int port; + + + /** + * Default constructor for this {@link TCPForkDaemon} implementation. + * + *

    The host name can either be a machine name, such as + * "java.sun.com", or a textual representation of its + * IP address. If a literal IP address is supplied, only the + * validity of the address format is checked. + *

    + *

    For host specified in literal IPv6 address, + * either the form defined in RFC 2732 or the literal IPv6 address + * format defined in RFC 2373 is accepted. IPv6 scoped addresses are also + * supported. See here for a description of IPv6 + * scoped addresses. + *

    + * @param parser instance implemeting {@link XmlForkParser} which knows about what + * codification uses the daemon to send us the results of the command sent to + * by the remote daemon by the {@link TCPForkDaemon.#exec(String)} method. + * @param host the specified host. + * @param port the TCP port where the daemon accepts connections. + * + */ + public TCPForkDaemon (final XmlForkParser parser, final String host, final int port) { + this.parser = parser; + this.host = host; + this.port = port; + } + + + /** + *

    + * This method sends commands to a remote daemon using a TCP socket. + * We create a new TCP socket every time we send commands. + *

    + *

    + * It uses a TCP connection in order to send commands and receive + * the results related to that command from the remote daemon. The command's + * result code which was run by the remote daemon can be retrieved from the + * return parameter of this method. + *

    + * @param command the command to be executed by the daemon. + * @return the executed command's return code. + * @throws IOException + * @throws UnknownHostException + * @throws SAXException + * @throws SecurityException if a security manager exists + */ + public int exec(final String command) throws UnknownHostException, IOException, SAXException { + PrintWriter out = null; + Socket socket = null; + + /******************************************************************************************/ + /* Just over 1 TCP connection */ + /* COMMAND_LENGTH: Java integer 4 bytes, BIG-ENDIAN (the same as network order) */ + /* COMMAND: remote locale character set encoding */ + /* RESULTS: remote locale character set encoding */ + /* */ + /* JAVA CLIENT: ------------ COMMAND_LENGTH -------> :SERVER */ + /* JAVA CLIENT: -------------- COMMAND ------------> :SERVER */ + /* JAVA CLIENT: <-------------- RESULTS ------------ :SERVER */ + /* JAVA CLIENT: <---------- CLOSE CONNECTION ------- :SERVER */ + /* */ + /******************************************************************************************/ + + + + socket = new Socket(InetAddress.getByName(host), port); + try { + //By default in UNIX systems the keepalive message is sent after 20hours + //with Java we can not use the TCP_KEEPCNT, TCP_KEEPIDLE and TCP_KEEPINTVL options by session. + //It is up to the server administrator and client user to configure them. + //I guess it is because Solaris does not implement those options... + //see: Net.c openjdk 6 and net_util_md.c openjdk 7 + //So in Java applications the only way to find out if the connection is broken (one router down) + //is sending ping messages or something similar from the application layer. Java is a toy language... + //Anyway I think the keepalive messages just work during the handshake phase, just after sending some + //data over the link the keepalive does not work. + // See: http://stackoverflow.com/questions/4345415/socket-detect-connection-is-lost + socket.setKeepAlive(true); + + //It must be used the remote locale character set encoding + byte [] commandEncoded = command.getBytes("UTF-8"); + + DataOutputStream sendData = new DataOutputStream(socket.getOutputStream()); + + // 1. COMMAND_LENGTH + sendData.writeInt(commandEncoded.length); + + // 2. COMMAND + sendData.write(commandEncoded); + + // 3. RESULTS + // TODO: When the network infrastructure (between client and server) fails in this point + // (broken router for example) Could we stay here until TCP keepalive is sent? + // (20 hours by default in Linux) + // Impossible to use a timeout, because we do not know how much time is going to long the command :/ + // the only way to fix this issue in Java is sending ping messages (Could we fix it using custom settings in the OS + // of the client and server machines? for example in Linux see /proc/sys/net/ipv4/) + InputSource inputSource = new InputSource(socket.getInputStream()); + //Must be used the remote locale character set encoding? + inputSource.setEncoding("UTF-8"); + parser.setStream(socket.getInputStream()); + + // 4. SERVER CLOSED CONNECTION + } + finally { + if (out != null) { + out.close(); + } + socket.close(); + } + + //If everything went alright we should be able to retrieve the return + //status of the remotely executed command. + return parser.getReturnValue(); + } + + + /** + * Retrieve the standard output.
    + * When there is nothing from the standard output this method returns null. + * + * @see {@link TCPForkDaemon#getStderr()} + * @return the stdout stream + */ + public String getStdout() { + return parser.getStdout(); + } + + + /** + * Retrieve the stderr stream as a {@link String} from the command which + * was run by the remote daemon + * + * @see {@link TCPForkDaemon#getStdout()} + * @return the stderr stream + */ + public String getStderr() { + return parser.getStderr(); + } +} diff --git a/JavaExample/javafork-example/src/main/java/de/fork/java/XmlForkParser.java b/JavaExample/javafork-example/src/main/java/de/fork/java/XmlForkParser.java new file mode 100644 index 0000000..255cb0f --- /dev/null +++ b/JavaExample/javafork-example/src/main/java/de/fork/java/XmlForkParser.java @@ -0,0 +1,195 @@ +package de.fork.java; + +import java.io.IOException; +import java.io.InputStream; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.apache.log4j.Logger; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.ext.DefaultHandler2; + +/** + *

    + * Class intended to parse the XML stream received from the daemon which is + * waiting to run commands. These commands are sent by the method + * {@link de.fork.java.TCPForkDaemon#exec(String, String, int)} + *

    + *

    + * After processing one command the daemon sends a XML stream with the stderr, + * stdout and return status of that command. With this class we extract those values + * and we can retrieve them using the methods {@link #getStderr() }, {@link #getStdout()} + * and {@link #getReturnValue()} + *

    + *

    + *

    + * Example, stream received from daemon:
    + * {@code
    + * 
    + * }
    + * 
    + *

    + *

    + * Instances of this class are mutable. To use them concurrently, clients must surround each + * method invocation (or invocation sequence) with external synchronization of the clients choosing. + *

    + */ +public class XmlForkParser extends DefaultHandler2 { + private static final Logger logger = Logger.getLogger(XmlForkParser.class); + private StringBuffer accumulator = new StringBuffer(); + private String stderr = new String(); + private String stdout = new String(); + private String returnCode = new String(); + final SAXParserFactory spf = SAXParserFactory.newInstance(); + private final SAXParser saxParser; + + public XmlForkParser() throws ParserConfigurationException, SAXException { + saxParser = spf.newSAXParser(); + } + + public void setStream(InputStream stream) throws SAXException, IOException { + saxParser.parse(stream, this); + } + + /** + *

    + * The daemon sends a XML stream, we parse that stream and the results are + * stored in the instace fields {@link #stderr}, {@link #stdout} and {@link returnCode} + *

    + *

    + * Later we can retrieve the results with {@link #getStderr()}, {@link #getStdout()} and + * {@link #getReturnValue()} + *

    + */ + @Override + public void endElement (final String uri, final String localName, final String qName) { + if (qName.equals("error")) { + // After , we've got the stderror + stderr = stderr + accumulator.toString(); + } else if (qName.equals("out")) { + // After , we've got the stdout + stdout = stdout + accumulator.toString(); + } else if (qName.equals("ret")) { + returnCode = returnCode + accumulator.toString(); + } + } + + /** + *

    + * This method removes the \n characters at the end of the stdout + * or stderr stream. + *

    + * + * @throws SAXException If any SAX errors occur during processing. + */ + @Override + public void endDocument () throws SAXException + { + if (stderr.length() != 0) { + String lastStderr = stderr.replaceFirst("\\\n$", ""); + stderr = lastStderr; + } + else { + //if there is nothing from the stderr stream + stderr = null; + } + if (stdout.length() != 0) { + String lastStdout = stdout.replaceFirst("\\\n$", ""); + stdout = lastStdout; + } + else { + //if there is nothing from the stdout stream + stdout = null; + } + } + + /** + * Retrieve the standard error. + * When there is nothing from the standard error this method returns null. + * + *
    +	 * Example, stream received from daemon:
    +	 * {@code
    +	 * 
    +	 * }
    +	 * 
    + *

    + *

    + *

    +	 * From that example with this method we are going to obtain this return parameter:
    +	 * {@code
    +	 * ls: no se puede acceder a bbb: No existe el fichero o el directorio
    +	 * ls: no se puede acceder a aaa: No existe el fichero o el directorio
    +	 * ls: no se puede acceder a dddd: No existe el fichero o el directorio
    +	 * }
    +	 * 
    + * + * @return stderr + */ + public String getStderr() { + return stderr; + + } + + + /** + * Retrieve the standard output. + * When there is nothing from the standard output this method returns null. + * + * @see {@link XmlForkParser#getStderr()} + * @return stdout + */ + public String getStdout() { + return stdout; + } + + + /** + * Retrieve the return code from the executed command. + * + * @return return status, usually 0 means the command went OK. + */ + public int getReturnValue() { + return new Integer(returnCode).intValue(); + } + + + @Override + public void startElement (final String uri, final String localName, + final String qName, final Attributes attributes) { + accumulator.setLength(0); + } + + + @Override + public void characters(final char[] buffer, final int start, final int length) { + accumulator.append(buffer, start, length); + } + + + @Override + public void warning(final SAXParseException exception) { + logger.error("WARNING line:" + exception.getLineNumber(), exception); + } + + + @Override + public void error(final SAXParseException exception) { + logger.error("ERROR line:" + exception.getLineNumber(), exception); + } + + + @Override + public void fatalError(final SAXParseException exception) throws SAXException { + logger.error("FATAL ERROR line:" + exception.getLineNumber(), exception); + throw (exception); + } +} diff --git a/JavaExample/pom.xml b/JavaExample/pom.xml new file mode 100644 index 0000000..aaf5867 --- /dev/null +++ b/JavaExample/pom.xml @@ -0,0 +1,663 @@ + + + + 4.0.0 + de.fork.java + javafork + 2.0-SNAPSHOT + javafork + http://www.gumartinm.name + Java fork with Linux daemon + + MyOrganization + http://www.gumartinm.name + + pom + + trac + http://gumartinm.name/trac + + + jenkins + http://gumartinm.name//jenkins/ + + + scm:svn:http://gumartinm.name + http://gumartinm.name + + + + com.sun.jdmk + jmxtools + 1.2.1 + + + javax.activation + activation + 1.1 + + + log4j + log4j + 1.2.15 + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + javax.mail + mail + + + javax.jms + jms + + + + + junit + junit + 4.4 + test + + + + + + c3p0 + c3p0 + 0.9.1.2 + + + cglib + cglib-nodep + 2.1_3 + + + commons-collections + commons-collections + 3.2.1 + + + commons-configuration + commons-configuration + 1.6 + + + commons-dbcp + commons-dbcp + 1.2.2 + + + commons-io + commons-io + 1.4 + + + commons-lang + commons-lang + 2.4 + + + commons-logging + commons-logging + 1.1.1 + + + commons-net + commons-net + 2.0 + + + commons-pool + commons-pool + 1.3 + + + com.h2database + h2 + 1.2.130 + + + dom4j + dom4j + 1.6.1 + + + xml-apis + xml-apis + + + + + hsqldb + hsqldb + 1.8.0.7 + + + javatar + javatar + 2.5 + + + jpos + jpos + 1.12.2 + provided + + + jpos + jpos-controls + 1.12.2 + provided + + + org.python + jython + 2.5.2b2 + + + urbanophile + java-getopt + 1.0.13 + + + mysql + mysql-connector-java + 5.1.6 + + + org.apache.ibatis + ibatis-sqlmap + 2.3.4.726 + + + org.apache.mina + mina-core + 2.0.0-M6 + + + org.aspectj + aspectjrt + 1.6.5 + + + org.aspectj + aspectjweaver + 1.6.5 + + + org.dbunit + dbunit + 2.4.4 + test + + + org.eclipse.jetty + jetty-continuation + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-http + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-io + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-security + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-server + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-servlet + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-webapp + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-util + 7.0.0.v20091005 + + + org.eclipse.jetty + jetty-xml + 7.0.0.v20091005 + + + org.slf4j + slf4j-api + 1.5.2 + + + org.slf4j + slf4j-log4j12 + 1.5.2 + + + log4j + log4j + + + + + org.springframework + spring-aop + ${spring.version} + + + org.springframework + spring-beans + ${spring.version} + + + org.springframework + spring-core + ${spring.version} + + + org.springframework + spring-context + ${spring.version} + + + org.springframework + spring-jdbc + ${spring.version} + + + org.springframework + spring-tx + ${spring.version} + + + org.springframework + spring-web + ${spring.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework + spring-context-support + ${spring.version} + + + org.springframework + spring-test + ${spring.version} + test + + + org.springframework.batch + spring-batch-test + 2.0.4.RELEASE + + + org.springframework.batch + spring-batch-core + 2.0.4.RELEASE + + + org.springframework.batch + spring-batch-infrastructure + 2.0.4.RELEASE + + + org.springframework + spring-orm + ${spring.version} + + + net.sf.ehcache + ehcache + 1.6.2 + + + org.springmodules + spring-modules-cache + 0.9 + + + p6spy + p6spy + 1.3 + + + javax.transaction + jta + 1.0.1B + + + javax.servlet + servlet-api + 2.5 + + + com.caucho + hessian + 3.1.6 + + + org.codehaus.jettison + jettison + 1.0 + + + com.thoughtworks.xstream + xstream + 1.3 + + + org.ini4j + ini4j + 0.5.1 + + + org.easymock + easymock + 2.4 + test + + + org.easymock + easymockclassextension + 2.4 + test + + + + org.apache.commons + commons-compress + 1.0 + + + org.apache.commons + commons-math + 2.0 + + + dtgjpos_forms + dtgjpos_forms + 1.4.12 + provided + + + org.codehaus.castor + castor-xml + 1.3.1 + + + org.apache.xmlbeans + xmlbeans + 2.5.0 + + + org.apache.velocity.tools + velocity-tools-generic + 1.4 + + + org.apache.velocity + velocity + 1.6.2 + + + org.tmatesoft.svnkit + svnkit + 1.3.1 + + + + + javafork-example + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.0.2 + + 1.6 + 1.6 + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.6 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.4.2 + + true + + + + org.codehaus.mojo + jdepend-maven-plugin + 2.0-beta-2 + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.1 + + + + ${project.description} + ${project.version} + ${project.organization.name} + ${project.description} + ${project.version} + ${project.organization.name} + ${BUILD_TAG} + ${BUILD_ID} + ${BUILD_NUMBER} + ${prefix.committedRevision} + ${prefix.repository} + ${prefix.path} + + + + + + com.google.code.maven-svn-revision-number-plugin + maven-svn-revision-number-plugin + 1.6 + + + + revision + + + + + + + prefix + + + + + + + + + + releases + releases + http://noserver/artifactory/custom-annotations-libs-releases-local + + + snapshots-releases + snapshots-releases + http://noserver/artifactory/custom-annotations-libs-snapshots-local + + + noserver + file:///mnt/sdb1/data/downloads/jenkins/ + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.1.2 + + + + index + dependencies + cim + issue-tracking + scm + summary + project-team + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.6.1 + + + html + + MYPROJECT API for ${project.name} ${project.version} + MYPROJECT API for ${project.name} ${project.version} + + + javadoc + aggregate + + + + + + org.codehaus.mojo + taglist-maven-plugin + 2.3 + + + TODO + @todo + FIXME + XXX + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.4.3 + + + integration-tests + + report-only + + + failsafe-report + + target/failsafe-reports + + + + + junit-tests + + report-only + + + surefire-report + + target/surefire-reports + + + + + + + + + UTF-8 + 2.5.6 + + + + + + + + diff --git a/README b/README new file mode 100644 index 0000000..bf09334 --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +Fork process from Java using a remote daemon. + +It is useful in environments where there are not enough memory +and you could finish having OutOfMemory exceptions while +trying to run system processes. + + +If you want to know more about memory overcommit in Linux see: +http://developers.sun.com/solaris/articles/subprocess/subprocess.html