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

Revision 7818, 13.0 KB checked in by mario, 9 years ago (diff)

use again a bit stricter types, and use const reference for some JVMLinkObjects (instead of new and delete objects)

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(const 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(argname, INT_TYPE, &obj);
291        setVar(jvmObj);
292}
293
294void JVMLinkClient::setVar(const std::string& argname, int* obj, int length) {
295        debug("setVar: " << argname << " (int array)");
296        JVMLinkObject jvmObj(argname, INT_TYPE, length, obj);
297        setVar(jvmObj);
298}
299
300void JVMLinkClient::setVar(const std::string& argname, std::string* obj) {
301        debug("setVar: " << argname << " = " << obj << " (string)");
302        JVMLinkObject jvmObj(argname, STRING_TYPE, obj);
303        setVar(jvmObj);
304}
305
306void JVMLinkClient::setVar(const std::string& argname, std::string* obj, int length) {
307        debug("setVar: " << argname << " (string array)");
308        JVMLinkObject jvmObj(argname, STRING_TYPE, length, obj);
309        setVar(jvmObj);
310}
311
312void JVMLinkClient::setVar(const std::string& argname, char obj) {
313        debug("setVar: " << argname << " = " << obj << " (char)");
314        JVMLinkObject jvmObj(argname, CHAR_TYPE, &obj);
315        setVar(jvmObj);
316}
317
318void JVMLinkClient::setVar(const std::string& argname, char* obj, int length) {
319        debug("setVar: " << argname << " (char array)");
320        JVMLinkObject jvmObj(argname, CHAR_TYPE, length, obj);
321        setVar(jvmObj);
322}
323
324void JVMLinkClient::setVar(const std::string& argname, Byte obj) {
325        debug("setVar: " << argname << " = " << obj.data << " (byte)");
326        JVMLinkObject jvmObj(argname, BYTE_TYPE, &obj);
327        setVar(jvmObj);
328}
329
330void JVMLinkClient::setVar(const std::string& argname, Byte* obj, int length) {
331        debug("setVar: " << argname << " (byte array)");
332        JVMLinkObject jvmObj(argname, BYTE_TYPE, length, obj);
333        setVar(jvmObj);
334}
335
336void JVMLinkClient::setVar(const std::string& argname, float obj) {
337        debug("setVar: " << argname << " = " << obj << " (float)");
338        JVMLinkObject jvmObj(argname, FLOAT_TYPE, &obj);
339        setVar(jvmObj);
340}
341
342void JVMLinkClient::setVar(const std::string& argname, float* obj, int length) {
343        debug("setVar: " << argname << " (float array)");
344        JVMLinkObject jvmObj(argname, FLOAT_TYPE, length, obj);
345        setVar(jvmObj);
346}
347
348void JVMLinkClient::setVar(const std::string& argname, bool obj) {
349        debug("setVar: " << argname << " = " << obj << " (bool)");
350        JVMLinkObject jvmObj(argname, BOOL_TYPE, &obj);
351        setVar(jvmObj);
352}
353
354void JVMLinkClient::setVar(const std::string& argname, bool* obj, int length) {
355        debug("setVar: " << argname << " (bool array)");
356        JVMLinkObject jvmObj(argname, BOOL_TYPE, length, obj);
357        setVar(jvmObj);
358}
359
360void JVMLinkClient::setVar(const std::string& argname, double obj) {
361        debug("setVar: " << argname << " = " << obj << " (double)");
362        JVMLinkObject jvmObj(argname, DOUBLE_TYPE, &obj);
363        setVar(jvmObj);
364}
365
366void JVMLinkClient::setVar(const std::string& argname, double* obj, int length) {
367        debug("setVar: " << argname << " (double array)");
368        JVMLinkObject jvmObj(argname, DOUBLE_TYPE, length, obj);
369        setVar(jvmObj);
370}
371
372void JVMLinkClient::setVar(const std::string& argname, long long obj) {
373        debug("setVar: " << argname << " = " << obj << " (long)");
374        JVMLinkObject jvmObj(argname, LONG_TYPE, &obj);
375        setVar(jvmObj);
376}
377
378void JVMLinkClient::setVar(const std::string& argname, long long* obj, int length) {
379        debug("setVar: " << argname << " (long array)");
380        JVMLinkObject jvmObj(argname, LONG_TYPE, length, obj);
381        setVar(jvmObj);
382}
383
384void JVMLinkClient::setVar(const std::string& argname, short obj) {
385        debug("setVar: " << argname << " = " << obj << " (short)");
386        JVMLinkObject jvmObj(argname, SHORT_TYPE, &obj);
387        setVar(jvmObj);
388}
389
390void JVMLinkClient::setVar(const std::string& argname, short* obj, int length) {
391        debug("setVar: " << argname << " (short array)");
392        JVMLinkObject jvmObj(argname, SHORT_TYPE, length, obj);
393        setVar(jvmObj);
394}
395
396void JVMLinkClient::setVarNull(const std::string& argname) {
397        debug("setVarNull: " << argname);
398        JVMLinkObject jvmObj(argname, NULL_TYPE, NULL);
399        setVar(jvmObj);
400}
401
402// -- Private methods --
403
404void JVMLinkClient::sendMessage(const std::string& message) {
405        int sent = 0;
406        const char* buf = message.c_str();
407        int total = message.length();
408        sendInt(total);
409        while (sent < total) sent += send(conn, buf + sent, total - sent, 0);
410}
411
412void JVMLinkClient::sendInt(int value) {
413        char* buf = (char*) (&value);
414        send(conn, buf, 4, 0);
415}
416
417void* JVMLinkClient::readMessage(int size) {
418        int read = 0;
419        char* buf = (char*) malloc(size);
420        while (read < size) read += recv(conn, buf + read, size - read, 0);
421        return (void*) buf;
422}
423
424int JVMLinkClient::readInt() {
425        return *(int*) readMessage(4);
426}
427
428std::string* JVMLinkClient::readString() {
429        int read = 0;
430        int total = readInt();
431        char* buf = new char[total + 1];
432        while (read < total) read += recv(conn, buf + read, total - read, 0);
433        buf[total] = '\0';
434        return new std::string(buf);
435}
Note: See TracBrowser for help on using the repository browser.