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

Revision 7819, 13.2 KB checked in by mario, 8 years ago (diff)

sanity-check some buffer sizes, and use a bit faster readInt() method

RevLine 
[3685]1//
2// JVMLinkClient.cpp
3//
4
5/*
[3725]6JVMLink client/server architecture for communicating between Java and
7non-Java programs using sockets.
[3685]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
[7812]33#ifdef _WIN32
[3725]34#include "stdafx.h"
[7812]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
[3678]44#include "JVMLinkClient.h"
45
[7812]46#include <iostream>
47#include <string>
48#include <string.h>
49#include <sstream>
50#include <stdlib.h>
51
[3678]52#define DEFAULT_PORT 20345
53
54//TODO: clear memory at appropriate points.
55
[7817]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
[3678]71JVMLinkClient::JVMLinkClient(void)
72{
[3725]73}
[3678]74
[3725]75JVMLinkClient::~JVMLinkClient(void)
76{
[3678]77}
78
[3725]79// -- Public API methods --
[3678]80
[7817]81void JVMLinkClient::startJava(unsigned short arg_port, const std::string& classpath) {
[7812]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
[3726]88        // NB: Toggle comments to control debugging output for the server.
[7812]89        const std::string command = "-cp " + classpath + " loci.jvmlink.JVMLinkServer " + portstr;
90        //const std::string command = "-cp " + classpath + " loci.jvmlink.JVMLinkServer -debug " + portstr;
[3725]91        debug("java " << command);
[3830]92        ShellExecute(NULL, "open", "javaw.exe" , command, "", SW_SHOW);
93        //ShellExecute(NULL, "open", "java.exe" , command, "", SW_SHOW);
[7812]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
[3678]112}
113
[3725]114void JVMLinkClient::shutJava() {
115        debug("Terminating JVMLink server");
[3756]116        sendInt(EXIT_CMD);
[3725]117}
118
119JVMLinkClient::ConnectionCode JVMLinkClient::establishConnection() {
[7812]120        const std::string servername = "127.0.0.1";
121
122#ifdef _WIN32
[3678]123        WSADATA wsaData;
124        struct hostent *hp;
125        unsigned int addr;
126        struct sockaddr_in server;
127
128        int wsaret=WSAStartup(0x101,&wsaData);
[3725]129        if (wsaret) return WINSOCK_ERR;
130        debug("Initialized WinSock");
131
[3678]132        conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
[3725]133        if (conn==INVALID_SOCKET) return SOCKET_ERR;
134        debug("Socket created");
[3678]135
[3824]136        if (inet_addr(servername)==INADDR_NONE) {
[3678]137                hp=gethostbyname(servername);
138        }
[3824]139        else {
[3678]140                addr=inet_addr(servername);
141                hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
142        }
[3824]143        if (hp == NULL) {
[3678]144                closesocket(conn);
[3725]145                debug("Could not resolve network address: " << servername);
146                return RESOLVE_ERR;
[3678]147        }
[3725]148        debug("Network address resolved");
[3678]149
150        server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
151        server.sin_family=AF_INET;
[3725]152        server.sin_port=htons(port);
[3824]153        if (connect(conn,(struct sockaddr*)&server,sizeof(server))) {
[3678]154                closesocket(conn);
[3725]155                debug("No server response on port " << port);
[7812]156                return RESPONSE_ERR;
[3678]157        }
[7812]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
[3725]189        debug("Connected to server: " << servername);
190        return CONNECTION_SUCCESS;
[3678]191}
192
[3725]193int JVMLinkClient::closeConnection() {
194        debug("Closing connection");
[7812]195#ifdef _WIN32
[3824]196        shutdown(conn, SD_SEND);
[3725]197        closesocket(conn);
198        debug("Socket closed");
199        WSACleanup();
200        debug("De-initialized WinSock");
[7812]201#else
202        close(conn);
203        conn = 0;
204#endif
[3824]205        return CONNECTION_SUCCESS;
[3678]206}
207
[7813]208JVMLinkObject* JVMLinkClient::getVar(const std::string& name) {
[3725]209        debug("getVar: requesting " << name);
[3678]210        JVMLinkObject* obj = new JVMLinkObject(name);
[3756]211        sendInt(GETVAR_CMD);
[3678]212        sendMessage(name);
[3755]213        obj->type = (Type) readInt();
[3678]214        if (obj->type == ARRAY_TYPE) {
[3755]215                obj->insideType = (Type) readInt();
[3830]216                obj->length = readInt();
217                if (obj->insideType == STRING_TYPE) {
[7812]218                        std::string* s = new std::string[obj->length];
[3830]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                }
[3725]226                debug("getVar: got array: length=" << obj->length << ", type=" << obj->insideType);
[3678]227        }
[7812]228        else if (obj->type == STRING_TYPE) {
[3830]229                obj->data = readString();
230                obj->size = 0;
[3833]231                debug("getVar: got string: length=" << len << ", value=" << buf);
[3678]232        }
[3834]233        else if (obj->type == NULL_TYPE) {
234                obj->data = NULL;
235                obj->size = 0;
236                debug("getVar: got NULL value");
237        }
[3678]238        else {
[3755]239                int size = readInt();
[3678]240                obj->data = readMessage(size);
241                obj->size = size;
[3725]242                obj->insideType = NULL_TYPE;
243                debug("getVar: got object: type=" << obj->type << ", size=" << obj->size);
[3678]244        }
245        return obj;
246}
247
[7813]248void JVMLinkClient::exec(const std::string& command) {
[3725]249        debug("exec: " << command);
[3756]250        sendInt(EXEC_CMD);
[3678]251        sendMessage(command);
[7817]252        int status = readInt();
253        if (status != 0) {
254                std::string vMessage = *readString();
255                JVMException vJVMException(vMessage);
256                throw vJVMException;
257        }
[3678]258}
259
[7818]260void JVMLinkClient::setVar(const JVMLinkObject& obj) {
[3756]261        sendInt(SETVAR_CMD);
[7818]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++) {
[3830]270                                sendMessage(s[i]);
271                        }
[3823]272                }
273                else {
[3833]274                        int sent = 0;
[7818]275                        int total = obj.size * obj.length;
276                        char* buf = (char*) obj.data;
[3833]277                        while (sent < total) {
278                                sent += send(conn, buf + sent, total - sent, 0);
[3725]279                        }
[3678]280                }
281        }
[3758]282        else {
[7818]283                if (obj.type == STRING_TYPE) sendMessage(*(std::string*) obj.data);
284                else send(conn, (char*) obj.data, obj.size, 0);
[3758]285        }
[3678]286}
287
[7813]288void JVMLinkClient::setVar(const std::string& argname, int obj) {
[3725]289        debug("setVar: " << argname << " = " << obj << " (int)");
[7818]290        JVMLinkObject jvmObj(argname, INT_TYPE, &obj);
[3758]291        setVar(jvmObj);
[3678]292}
293
[7813]294void JVMLinkClient::setVar(const std::string& argname, int* obj, int length) {
[3766]295        debug("setVar: " << argname << " (int array)");
[7819]296        if (length < 1) return;
[7818]297        JVMLinkObject jvmObj(argname, INT_TYPE, length, obj);
[3766]298        setVar(jvmObj);
299}
300
[7813]301void JVMLinkClient::setVar(const std::string& argname, std::string* obj) {
[3725]302        debug("setVar: " << argname << " = " << obj << " (string)");
[7818]303        JVMLinkObject jvmObj(argname, STRING_TYPE, obj);
[3758]304        setVar(jvmObj);
[3678]305}
306
[7813]307void JVMLinkClient::setVar(const std::string& argname, std::string* obj, int length) {
[3766]308        debug("setVar: " << argname << " (string array)");
[7819]309        if (length < 1) return;
[7818]310        JVMLinkObject jvmObj(argname, STRING_TYPE, length, obj);
[3766]311        setVar(jvmObj);
312}
313
[7813]314void JVMLinkClient::setVar(const std::string& argname, char obj) {
[3725]315        debug("setVar: " << argname << " = " << obj << " (char)");
[7818]316        JVMLinkObject jvmObj(argname, CHAR_TYPE, &obj);
[3758]317        setVar(jvmObj);
[3678]318}
319
[7813]320void JVMLinkClient::setVar(const std::string& argname, char* obj, int length) {
[3766]321        debug("setVar: " << argname << " (char array)");
[7819]322        if (length < 1) return;
[7818]323        JVMLinkObject jvmObj(argname, CHAR_TYPE, length, obj);
[3766]324        setVar(jvmObj);
325}
326
[7813]327void JVMLinkClient::setVar(const std::string& argname, Byte obj) {
[3725]328        debug("setVar: " << argname << " = " << obj.data << " (byte)");
[7818]329        JVMLinkObject jvmObj(argname, BYTE_TYPE, &obj);
[3758]330        setVar(jvmObj);
[3678]331}
332
[7813]333void JVMLinkClient::setVar(const std::string& argname, Byte* obj, int length) {
[3766]334        debug("setVar: " << argname << " (byte array)");
[7819]335        if (length < 1) return;
[7818]336        JVMLinkObject jvmObj(argname, BYTE_TYPE, length, obj);
[3766]337        setVar(jvmObj);
338}
339
[7813]340void JVMLinkClient::setVar(const std::string& argname, float obj) {
[3725]341        debug("setVar: " << argname << " = " << obj << " (float)");
[7818]342        JVMLinkObject jvmObj(argname, FLOAT_TYPE, &obj);
[3758]343        setVar(jvmObj);
[3678]344}
345
[7813]346void JVMLinkClient::setVar(const std::string& argname, float* obj, int length) {
[3766]347        debug("setVar: " << argname << " (float array)");
[7819]348        if (length < 1) return;
[7818]349        JVMLinkObject jvmObj(argname, FLOAT_TYPE, length, obj);
[3766]350        setVar(jvmObj);
351}
352
[7813]353void JVMLinkClient::setVar(const std::string& argname, bool obj) {
[3725]354        debug("setVar: " << argname << " = " << obj << " (bool)");
[7818]355        JVMLinkObject jvmObj(argname, BOOL_TYPE, &obj);
[3758]356        setVar(jvmObj);
[3678]357}
358
[7813]359void JVMLinkClient::setVar(const std::string& argname, bool* obj, int length) {
[3766]360        debug("setVar: " << argname << " (bool array)");
[7819]361        if (length < 1) return;
[7818]362        JVMLinkObject jvmObj(argname, BOOL_TYPE, length, obj);
[3766]363        setVar(jvmObj);
364}
365
[7813]366void JVMLinkClient::setVar(const std::string& argname, double obj) {
[3725]367        debug("setVar: " << argname << " = " << obj << " (double)");
[7818]368        JVMLinkObject jvmObj(argname, DOUBLE_TYPE, &obj);
[3758]369        setVar(jvmObj);
[3678]370}
371
[7813]372void JVMLinkClient::setVar(const std::string& argname, double* obj, int length) {
[3766]373        debug("setVar: " << argname << " (double array)");
[7819]374        if (length < 1) return;
[7818]375        JVMLinkObject jvmObj(argname, DOUBLE_TYPE, length, obj);
[3766]376        setVar(jvmObj);
377}
378
[7813]379void JVMLinkClient::setVar(const std::string& argname, long long obj) {
[3725]380        debug("setVar: " << argname << " = " << obj << " (long)");
[7818]381        JVMLinkObject jvmObj(argname, LONG_TYPE, &obj);
[3758]382        setVar(jvmObj);
[3678]383}
384
[7813]385void JVMLinkClient::setVar(const std::string& argname, long long* obj, int length) {
[3766]386        debug("setVar: " << argname << " (long array)");
[7819]387        if (length < 1) return;
[7818]388        JVMLinkObject jvmObj(argname, LONG_TYPE, length, obj);
[3766]389        setVar(jvmObj);
390}
391
[7813]392void JVMLinkClient::setVar(const std::string& argname, short obj) {
[3725]393        debug("setVar: " << argname << " = " << obj << " (short)");
[7818]394        JVMLinkObject jvmObj(argname, SHORT_TYPE, &obj);
[3758]395        setVar(jvmObj);
[3678]396}
397
[7813]398void JVMLinkClient::setVar(const std::string& argname, short* obj, int length) {
[3766]399        debug("setVar: " << argname << " (short array)");
[7819]400        if (length < 1) return;
[7818]401        JVMLinkObject jvmObj(argname, SHORT_TYPE, length, obj);
[3766]402        setVar(jvmObj);
403}
404
[7813]405void JVMLinkClient::setVarNull(const std::string& argname) {
[3834]406        debug("setVarNull: " << argname);
[7818]407        JVMLinkObject jvmObj(argname, NULL_TYPE, NULL);
[3834]408        setVar(jvmObj);
409}
410
[3725]411// -- Private methods --
412
[7813]413void JVMLinkClient::sendMessage(const std::string& message) {
[3833]414        int sent = 0;
[7812]415        const char* buf = message.c_str();
416        int total = message.length();
[3833]417        sendInt(total);
418        while (sent < total) sent += send(conn, buf + sent, total - sent, 0);
[3678]419}
[3725]420
[3833]421void JVMLinkClient::sendInt(int value) {
422        char* buf = (char*) (&value);
423        send(conn, buf, 4, 0);
[3685]424}
[3725]425
426void* JVMLinkClient::readMessage(int size) {
[3833]427        int read = 0;
428        char* buf = (char*) malloc(size);
429        while (read < size) read += recv(conn, buf + read, size - read, 0);
430        return (void*) buf;
[3725]431}
[3755]432
433int JVMLinkClient::readInt() {
[7819]434        int buf;
435        recv(conn, (char*)&buf, 4, 0);
436        return buf;
[3830]437}
438
[7812]439std::string* JVMLinkClient::readString() {
[3833]440        int read = 0;
441        int total = readInt();
442        char* buf = new char[total + 1];
443        while (read < total) read += recv(conn, buf + read, total - read, 0);
444        buf[total] = '\0';
[7812]445        return new std::string(buf);
[3830]446}
Note: See TracBrowser for help on using the repository browser.