From 6c2426dc33d8f1b623cc221e8241a3d4844cbd8c Mon Sep 17 00:00:00 2001 From: gumartinm Date: Sun, 29 Jan 2012 17:00:44 +0100 Subject: [PATCH 1/1] first commit --- Daemon/Makefile | 8 + Daemon/README.txt | 3 + Daemon/javafork | Bin 0 -> 30977 bytes Daemon/javafork.c | 692 +++++++++++++++++++++ Daemon/javafork.h | 77 +++ JavaExample/javafork-example/pom.xml | 22 + .../main/java/de/fork/java/LauncherProcesses.java | 285 +++++++++ .../src/main/java/de/fork/java/RemoteForkMain.java | 36 ++ .../src/main/java/de/fork/java/TCPForkDaemon.java | 184 ++++++ .../src/main/java/de/fork/java/XmlForkParser.java | 195 ++++++ JavaExample/pom.xml | 663 ++++++++++++++++++++ README | 9 + 12 files changed, 2174 insertions(+) create mode 100644 Daemon/Makefile create mode 100644 Daemon/README.txt create mode 100755 Daemon/javafork create mode 100644 Daemon/javafork.c create mode 100644 Daemon/javafork.h create mode 100644 JavaExample/javafork-example/pom.xml create mode 100644 JavaExample/javafork-example/src/main/java/de/fork/java/LauncherProcesses.java create mode 100644 JavaExample/javafork-example/src/main/java/de/fork/java/RemoteForkMain.java create mode 100644 JavaExample/javafork-example/src/main/java/de/fork/java/TCPForkDaemon.java create mode 100644 JavaExample/javafork-example/src/main/java/de/fork/java/XmlForkParser.java create mode 100644 JavaExample/pom.xml create mode 100644 README 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 0000000000000000000000000000000000000000..135062edaedb22d804862d0836ac9250a1f09a10 GIT binary patch literal 30977 zcmchAdwf*Ywf~-(1V#uXB*-(u@C+b?r-;a75C#Vgf;=n=I+-Mshva2u1_Bkq2GHqT zYUtIL_O@4iw3l8>jjgqaHb$U;)~l)YT5hYATD2L`U#ZXhTH4(2cRyy%88Y7c$M5so zaI*LM?zPrld+o>B=bSy~?DUl^cRC!x1gBUeNOfN7m5}-?#ITK$)Yv@X7G5z?j1zr; zWaIG49L6eT;ts9a#BR;!LY&34Qv*(~O6a&yQNqN=LdGmF6}&pB;``?+(!@e|BtSXZ zdO+tNm|H4w6F*CX5W012wJ=-W{VcjX&~1vFyP(=rZQN^1&a!_rp5|y2JdSUy~O>24b-@9;v`oNQUys$CD=Cf z

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 +#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 -- 2.1.4