source: trunk/legacy/jvmlink/src/main/c++/JVMLinkClient.cpp @ 7817

Revision 7817, 13.7 KB checked in by mario, 8 years ago (diff)

added in idea of exception handling between Java and C++ exec methods

Line 
1//
2// JVMLinkClient.cpp
3//
4
5/*
6JVMLink client/server architecture for communicating between Java and
7non-Java programs using sockets.
8Copyright (c) 2008 Hidayath Ansari and Curtis Rueden. All rights reserved.
9
10Redistribution and use in source and binary forms, with or without
11modification, are permitted provided that the following conditions are met:
12  * Redistributions of source code must retain the above copyright
13    notice, this list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright
15    notice, this list of conditions and the following disclaimer in the
16    documentation and/or other materials provided with the distribution.
17  * Neither the name of the UW-Madison LOCI nor the names of its
18    contributors may be used to endorse or promote products derived from
19    this software without specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE UW-MADISON LOCI ``AS IS'' AND ANY
22EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
25DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31*/
32
33#ifdef _WIN32
34#include "stdafx.h"
35#include <windows.h>
36#else
37#include <sys/types.h>
38#include <sys/wait.h>
39#include <sys/socket.h>
40#include <unistd.h>
41#include <netdb.h>
42#endif
43
44#include "JVMLinkClient.h"
45
46#include <iostream>
47#include <string>
48#include <string.h>
49#include <sstream>
50#include <stdlib.h>
51
52#define DEFAULT_PORT 20345
53
54//TODO: clear memory at appropriate points.
55
56JVMException::JVMException(const std::string& aMessage) throw()
57{
58        mMessage = aMessage;
59}
60
61JVMException::~JVMException() throw()
62{
63}
64
65const char* JVMException::what() const throw()
66{
67        return mMessage.c_str();
68}
69
70
71JVMLinkClient::JVMLinkClient(void)
72{
73}
74
75JVMLinkClient::~JVMLinkClient(void)
76{
77}
78
79// -- Public API methods --
80
81void JVMLinkClient::startJava(unsigned short arg_port, const std::string& classpath) {
82        port = arg_port == 0 ? DEFAULT_PORT : arg_port;
83        std::stringstream tmpportstr;
84        tmpportstr << port;
85        const std::string portstr = tmpportstr.str();
86
87#ifdef _WIN32
88        // NB: Toggle comments to control debugging output for the server.
89        const std::string command = "-cp " + classpath + " loci.jvmlink.JVMLinkServer " + portstr;
90        //const std::string command = "-cp " + classpath + " loci.jvmlink.JVMLinkServer -debug " + portstr;
91        debug("java " << command);
92        ShellExecute(NULL, "open", "javaw.exe" , command, "", SW_SHOW);
93        //ShellExecute(NULL, "open", "java.exe" , command, "", SW_SHOW);
94#else
95        pid_t vProcID = vfork();
96        if (vProcID == 0) {
97                // executed by child process
98                execlp("java", "java", "-cp", classpath.c_str(), "loci.jvmlink.JVMLinkServer", portstr.c_str(), "-debug", (char*)NULL);
99                //execlp("java", "java", "-cp", classpath.c_str(), "loci.jvmlink.JVMLinkServer", portstr.c_str(), (char*)NULL);
100
101                std::cerr << "Error: Child failed to execute process." << std::endl;
102                _exit(1);
103        } else if (vProcID < 0) {
104                // failed to fork
105                debug("Error: Failed to fork, will exit now.");
106                exit(1);
107        } else {
108                // Code only executed by parent process
109                // TODO: check if the forked child is alive and working
110        }
111#endif
112}
113
114void JVMLinkClient::shutJava() {
115        debug("Terminating JVMLink server");
116        sendInt(EXIT_CMD);
117}
118
119JVMLinkClient::ConnectionCode JVMLinkClient::establishConnection() {
120        const std::string servername = "127.0.0.1";
121
122#ifdef _WIN32
123        WSADATA wsaData;
124        struct hostent *hp;
125        unsigned int addr;
126        struct sockaddr_in server;
127
128        int wsaret=WSAStartup(0x101,&wsaData);
129        if (wsaret) return WINSOCK_ERR;
130        debug("Initialized WinSock");
131
132        conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
133        if (conn==INVALID_SOCKET) return SOCKET_ERR;
134        debug("Socket created");
135
136        if (inet_addr(servername)==INADDR_NONE) {
137                hp=gethostbyname(servername);
138        }
139        else {
140                addr=inet_addr(servername);
141                hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
142        }
143        if (hp == NULL) {
144                closesocket(conn);
145                debug("Could not resolve network address: " << servername);
146                return RESOLVE_ERR;
147        }
148        debug("Network address resolved");
149
150        server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
151        server.sin_family=AF_INET;
152        server.sin_port=htons(port);
153        if (connect(conn,(struct sockaddr*)&server,sizeof(server))) {
154                closesocket(conn);
155                debug("No server response on port " << port);
156                return RESPONSE_ERR;
157        }
158#else
159        debug("starting to create socket");
160        struct addrinfo hints;
161        struct addrinfo *res;
162
163        memset(&hints, 0, sizeof hints);
164        hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
165        hints.ai_socktype = SOCK_STREAM;
166        hints.ai_flags = AI_PASSIVE;  // fill in my IP for me
167
168        // do the lookup
169        std::stringstream tmpportstr;
170        tmpportstr << port;
171        const std::string portstr = tmpportstr.str();
172        getaddrinfo(servername.c_str(), portstr.c_str(), &hints, &res);
173
174        // TODO: should do error-checking on getaddrinfo(), and walk
175        // the "res" linked list looking for valid entries instead of just
176        // assuming the first one is good
177        conn = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
178        if (conn < 0) return SOCKET_ERR;
179
180        debug("finished to create socket");
181
182        if (connect(conn, res->ai_addr, res->ai_addrlen) < 0) {
183                close(conn);
184                debug("No server response on port " << port);
185                return RESPONSE_ERR;
186        }
187#endif
188
189        debug("Connected to server: " << servername);
190        return CONNECTION_SUCCESS;
191}
192
193int JVMLinkClient::closeConnection() {
194        debug("Closing connection");
195#ifdef _WIN32
196        shutdown(conn, SD_SEND);
197        closesocket(conn);
198        debug("Socket closed");
199        WSACleanup();
200        debug("De-initialized WinSock");
201#else
202        close(conn);
203        conn = 0;
204#endif
205        return CONNECTION_SUCCESS;
206}
207
208JVMLinkObject* JVMLinkClient::getVar(const std::string& name) {
209        debug("getVar: requesting " << name);
210        JVMLinkObject* obj = new JVMLinkObject(name);
211        sendInt(GETVAR_CMD);
212        sendMessage(name);
213        obj->type = (Type) readInt();
214        if (obj->type == ARRAY_TYPE) {
215                obj->insideType = (Type) readInt();
216                obj->length = readInt();
217                if (obj->insideType == STRING_TYPE) {
218                        std::string* s = new std::string[obj->length];
219                        for (int i=0; i<obj->length; i++) s[i] = *readString();
220                        obj->data = s;
221                }
222                else {
223                        obj->size = readInt();
224                        obj->data = readMessage(obj->size * obj->length);
225                }
226                debug("getVar: got array: length=" << obj->length << ", type=" << obj->insideType);
227        }
228        else if (obj->type == STRING_TYPE) {
229                obj->data = readString();
230                obj->size = 0;
231                debug("getVar: got string: length=" << len << ", value=" << buf);
232        }
233        else if (obj->type == NULL_TYPE) {
234                obj->data = NULL;
235                obj->size = 0;
236                debug("getVar: got NULL value");
237        }
238        else {
239                int size = readInt();
240                obj->data = readMessage(size);
241                obj->size = size;
242                obj->insideType = NULL_TYPE;
243                debug("getVar: got object: type=" << obj->type << ", size=" << obj->size);
244        }
245        return obj;
246}
247
248void JVMLinkClient::exec(const std::string& command) {
249        debug("exec: " << command);
250        sendInt(EXEC_CMD);
251        sendMessage(command);
252        int status = readInt();
253        if (status != 0) {
254                std::string vMessage = *readString();
255                JVMException vJVMException(vMessage);
256                throw vJVMException;
257        }
258}
259
260void JVMLinkClient::setVar(JVMLinkObject* obj) {
261        sendInt(SETVAR_CMD);
262        sendMessage(obj->name);
263        sendInt((int) obj->type);
264        if (obj->type == ARRAY_TYPE) {
265                sendInt((int) obj->insideType);
266                sendInt(obj->length);
267                if (obj->insideType == STRING_TYPE) {
268                        std::string* s = (std::string*) obj->data;
269                        for (int i=0; i<obj->length; i++) {
270                                sendMessage(s[i]);
271                        }
272                }
273                else {
274                        int sent = 0;
275                        int total = obj->size * obj->length;
276                        char* buf = (char*) obj->data;
277                        while (sent < total) {
278                                sent += send(conn, buf + sent, total - sent, 0);
279                        }
280                }
281        }
282        else {
283                if (obj->type == STRING_TYPE) sendMessage(*(std::string*) obj->data);
284                else send(conn, (char*) obj->data, obj->size, 0);
285        }
286}
287
288void JVMLinkClient::setVar(const std::string& argname, int obj) {
289        debug("setVar: " << argname << " = " << obj << " (int)");
290        JVMLinkObject* jvmObj = new JVMLinkObject(argname, INT_TYPE, &obj);
291        setVar(jvmObj);
292        delete jvmObj;
293}
294
295void JVMLinkClient::setVar(const std::string& argname, int* obj, int length) {
296        debug("setVar: " << argname << " (int array)");
297        JVMLinkObject* jvmObj = new JVMLinkObject(argname, INT_TYPE, length, obj);
298        setVar(jvmObj);
299        delete jvmObj;
300}
301
302void JVMLinkClient::setVar(const std::string& argname, std::string* obj) {
303        debug("setVar: " << argname << " = " << obj << " (string)");
304        JVMLinkObject* jvmObj = new JVMLinkObject(argname, STRING_TYPE, obj);
305        setVar(jvmObj);
306        delete jvmObj;
307}
308
309void JVMLinkClient::setVar(const std::string& argname, std::string* obj, int length) {
310        debug("setVar: " << argname << " (string array)");
311        JVMLinkObject* jvmObj = new JVMLinkObject(argname, STRING_TYPE, length, obj);
312        setVar(jvmObj);
313        delete jvmObj;
314}
315
316void JVMLinkClient::setVar(const std::string& argname, char obj) {
317        debug("setVar: " << argname << " = " << obj << " (char)");
318        JVMLinkObject* jvmObj = new JVMLinkObject(argname, CHAR_TYPE, &obj);
319        setVar(jvmObj);
320        delete jvmObj;
321}
322
323void JVMLinkClient::setVar(const std::string& argname, char* obj, int length) {
324        debug("setVar: " << argname << " (char array)");
325        JVMLinkObject* jvmObj = new JVMLinkObject(argname, CHAR_TYPE, length, obj);
326        setVar(jvmObj);
327        delete jvmObj;
328}
329
330void JVMLinkClient::setVar(const std::string& argname, Byte obj) {
331        debug("setVar: " << argname << " = " << obj.data << " (byte)");
332        JVMLinkObject* jvmObj = new JVMLinkObject(argname, BYTE_TYPE, &obj);
333        setVar(jvmObj);
334        delete jvmObj;
335}
336
337void JVMLinkClient::setVar(const std::string& argname, Byte* obj, int length) {
338        debug("setVar: " << argname << " (byte array)");
339        JVMLinkObject* jvmObj = new JVMLinkObject(argname, BYTE_TYPE, length, obj);
340        setVar(jvmObj);
341        delete jvmObj;
342}
343
344void JVMLinkClient::setVar(const std::string& argname, float obj) {
345        debug("setVar: " << argname << " = " << obj << " (float)");
346        JVMLinkObject* jvmObj = new JVMLinkObject(argname, FLOAT_TYPE, &obj);
347        setVar(jvmObj);
348        delete jvmObj;
349}
350
351void JVMLinkClient::setVar(const std::string& argname, float* obj, int length) {
352        debug("setVar: " << argname << " (float array)");
353        JVMLinkObject* jvmObj = new JVMLinkObject(argname, FLOAT_TYPE, length, obj);
354        setVar(jvmObj);
355        delete jvmObj;
356}
357
358void JVMLinkClient::setVar(const std::string& argname, bool obj) {
359        debug("setVar: " << argname << " = " << obj << " (bool)");
360        JVMLinkObject* jvmObj = new JVMLinkObject(argname, BOOL_TYPE, &obj);
361        setVar(jvmObj);
362        delete jvmObj;
363}
364
365void JVMLinkClient::setVar(const std::string& argname, bool* obj, int length) {
366        debug("setVar: " << argname << " (bool array)");
367        JVMLinkObject* jvmObj = new JVMLinkObject(argname, BOOL_TYPE, length, obj);
368        setVar(jvmObj);
369        delete jvmObj;
370}
371
372void JVMLinkClient::setVar(const std::string& argname, double obj) {
373        debug("setVar: " << argname << " = " << obj << " (double)");
374        JVMLinkObject* jvmObj = new JVMLinkObject(argname, DOUBLE_TYPE, &obj);
375        setVar(jvmObj);
376        delete jvmObj;
377}
378
379void JVMLinkClient::setVar(const std::string& argname, double* obj, int length) {
380        debug("setVar: " << argname << " (double array)");
381        JVMLinkObject* jvmObj = new JVMLinkObject(argname, DOUBLE_TYPE, length, obj);
382        setVar(jvmObj);
383        delete jvmObj;
384}
385
386void JVMLinkClient::setVar(const std::string& argname, long long obj) {
387        debug("setVar: " << argname << " = " << obj << " (long)");
388        JVMLinkObject* jvmObj = new JVMLinkObject(argname, LONG_TYPE, &obj);
389        setVar(jvmObj);
390        delete jvmObj;
391}
392
393void JVMLinkClient::setVar(const std::string& argname, long long* obj, int length) {
394        debug("setVar: " << argname << " (long array)");
395        JVMLinkObject* jvmObj = new JVMLinkObject(argname, LONG_TYPE, length, obj);
396        setVar(jvmObj);
397        delete jvmObj;
398}
399
400void JVMLinkClient::setVar(const std::string& argname, short obj) {
401        debug("setVar: " << argname << " = " << obj << " (short)");
402        JVMLinkObject* jvmObj = new JVMLinkObject(argname, SHORT_TYPE, &obj);
403        setVar(jvmObj);
404        delete jvmObj;
405}
406
407void JVMLinkClient::setVar(const std::string& argname, short* obj, int length) {
408        debug("setVar: " << argname << " (short array)");
409        JVMLinkObject* jvmObj = new JVMLinkObject(argname, SHORT_TYPE, length, obj);
410        setVar(jvmObj);
411        delete jvmObj;
412}
413
414void JVMLinkClient::setVarNull(const std::string& argname) {
415        debug("setVarNull: " << argname);
416        JVMLinkObject* jvmObj = new JVMLinkObject(argname, NULL_TYPE, NULL);
417        setVar(jvmObj);
418        delete jvmObj;
419}
420
421// -- Private methods --
422
423void JVMLinkClient::sendMessage(const std::string& message) {
424        int sent = 0;
425        const char* buf = message.c_str();
426        int total = message.length();
427        sendInt(total);
428        while (sent < total) sent += send(conn, buf + sent, total - sent, 0);
429}
430
431void JVMLinkClient::sendInt(int value) {
432        char* buf = (char*) (&value);
433        send(conn, buf, 4, 0);
434}
435
436void* JVMLinkClient::readMessage(int size) {
437        int read = 0;
438        char* buf = (char*) malloc(size);
439        while (read < size) read += recv(conn, buf + read, size - read, 0);
440        return (void*) buf;
441}
442
443int JVMLinkClient::readInt() {
444        return *(int*) readMessage(4);
445}
446
447std::string* JVMLinkClient::readString() {
448        int read = 0;
449        int total = readInt();
450        char* buf = new char[total + 1];
451        while (read < total) read += recv(conn, buf + read, total - read, 0);
452        buf[total] = '\0';
453        return new std::string(buf);
454}
Note: See TracBrowser for help on using the repository browser.