2 |
26 Feb 07 |
jari |
1 |
package org.tigr.microarray.mev.r; |
2 |
26 Feb 07 |
jari |
2 |
|
2 |
26 Feb 07 |
jari |
3 |
import java.io.InputStream; |
2 |
26 Feb 07 |
jari |
4 |
import java.io.OutputStream; |
2 |
26 Feb 07 |
jari |
5 |
|
2 |
26 Feb 07 |
jari |
/** This class encapsulates the QAP1 protocol used by Rserv. |
2 |
26 Feb 07 |
jari |
it is independent of the underying protocol(s), therefore Rtalk |
2 |
26 Feb 07 |
jari |
can be used over any transport layer |
2 |
26 Feb 07 |
jari |
<p> |
2 |
26 Feb 07 |
jari |
The current implementation supports long (0.3+/0102) data format only |
2 |
26 Feb 07 |
jari |
up to 32-bit and only for incoming packets. |
2 |
26 Feb 07 |
jari |
<p> |
2 |
26 Feb 07 |
jari |
@version $Id$ |
2 |
26 Feb 07 |
jari |
14 |
*/ |
2 |
26 Feb 07 |
jari |
15 |
public class Rtalk { |
2 |
26 Feb 07 |
jari |
16 |
public static final int DT_INT=1; |
2 |
26 Feb 07 |
jari |
17 |
public static final int DT_CHAR=2; |
2 |
26 Feb 07 |
jari |
18 |
public static final int DT_DOUBLE=3; |
2 |
26 Feb 07 |
jari |
19 |
public static final int DT_STRING=4; |
2 |
26 Feb 07 |
jari |
20 |
public static final int DT_BYTESTREAM=5; |
2 |
26 Feb 07 |
jari |
21 |
public static final int DT_SEXP=10; |
2 |
26 Feb 07 |
jari |
22 |
public static final int DT_ARRAY=11; |
2 |
26 Feb 07 |
jari |
23 |
|
2 |
26 Feb 07 |
jari |
/** this is a flag saying that the contents is large (>0xfffff0) and hence uses 56-bit length field */ |
2 |
26 Feb 07 |
jari |
25 |
public static final int DT_LARGE=64; |
2 |
26 Feb 07 |
jari |
26 |
|
2 |
26 Feb 07 |
jari |
27 |
public static final int CMD_login=0x001; |
2 |
26 Feb 07 |
jari |
28 |
public static final int CMD_voidEval=0x002; |
2 |
26 Feb 07 |
jari |
29 |
public static final int CMD_eval=0x003; |
2 |
26 Feb 07 |
jari |
30 |
public static final int CMD_shutdown=0x004; |
2 |
26 Feb 07 |
jari |
31 |
public static final int CMD_openFile=0x010; |
2 |
26 Feb 07 |
jari |
32 |
public static final int CMD_createFile=0x011; |
2 |
26 Feb 07 |
jari |
33 |
public static final int CMD_closeFile=0x012; |
2 |
26 Feb 07 |
jari |
34 |
public static final int CMD_readFile=0x013; |
2 |
26 Feb 07 |
jari |
35 |
public static final int CMD_writeFile=0x014; |
2 |
26 Feb 07 |
jari |
36 |
public static final int CMD_removeFile=0x015; |
2 |
26 Feb 07 |
jari |
37 |
public static final int CMD_setSEXP=0x020; |
2 |
26 Feb 07 |
jari |
38 |
public static final int CMD_assignSEXP=0x021; |
2 |
26 Feb 07 |
jari |
39 |
|
2 |
26 Feb 07 |
jari |
40 |
public static final int CMD_setBufferSize=0x081; |
2 |
26 Feb 07 |
jari |
41 |
|
2 |
26 Feb 07 |
jari |
// errors as returned by Rserve |
2 |
26 Feb 07 |
jari |
43 |
public static final int ERR_auth_failed=0x41; |
2 |
26 Feb 07 |
jari |
44 |
public static final int ERR_conn_broken=0x42; |
2 |
26 Feb 07 |
jari |
45 |
public static final int ERR_inv_cmd=0x43; |
2 |
26 Feb 07 |
jari |
46 |
public static final int ERR_inv_par=0x44; |
2 |
26 Feb 07 |
jari |
47 |
public static final int ERR_Rerror=0x45; |
2 |
26 Feb 07 |
jari |
48 |
public static final int ERR_IOerror=0x46; |
2 |
26 Feb 07 |
jari |
49 |
public static final int ERR_not_open=0x47; |
2 |
26 Feb 07 |
jari |
50 |
public static final int ERR_access_denied=0x48; |
2 |
26 Feb 07 |
jari |
51 |
public static final int ERR_unsupported_cmd=0x49; |
2 |
26 Feb 07 |
jari |
52 |
public static final int ERR_unknown_cmd=0x4a; |
2 |
26 Feb 07 |
jari |
53 |
public static final int ERR_data_overflow=0x4b; |
2 |
26 Feb 07 |
jari |
54 |
public static final int ERR_object_too_big=0x4c; |
2 |
26 Feb 07 |
jari |
55 |
public static final int ERR_out_of_mem=0x4d; |
2 |
26 Feb 07 |
jari |
56 |
|
2 |
26 Feb 07 |
jari |
57 |
InputStream is; |
2 |
26 Feb 07 |
jari |
58 |
OutputStream os; |
2 |
26 Feb 07 |
jari |
59 |
|
2 |
26 Feb 07 |
jari |
/** constructor; parameters specify the streams |
2 |
26 Feb 07 |
jari |
@param sis socket input stream |
2 |
26 Feb 07 |
jari |
@param sos socket output stream */ |
2 |
26 Feb 07 |
jari |
63 |
|
2 |
26 Feb 07 |
jari |
64 |
public Rtalk(InputStream sis, OutputStream sos) { |
2 |
26 Feb 07 |
jari |
65 |
is=sis; os=sos; |
2 |
26 Feb 07 |
jari |
66 |
} |
2 |
26 Feb 07 |
jari |
67 |
|
2 |
26 Feb 07 |
jari |
/** writes bit-wise int to a byte buffer at specified position in Intel-endian form |
2 |
26 Feb 07 |
jari |
@param v value to be written |
2 |
26 Feb 07 |
jari |
@param buf buffer |
2 |
26 Feb 07 |
jari |
@param o offset in the buffer to start at. An int takes always 4 bytes */ |
2 |
26 Feb 07 |
jari |
72 |
public static void setInt(int v, byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
73 |
buf[o]=(byte)(v&255); o++; |
2 |
26 Feb 07 |
jari |
74 |
buf[o]=(byte)((v&0xff00)>>8); o++; |
2 |
26 Feb 07 |
jari |
75 |
buf[o]=(byte)((v&0xff0000)>>16); o++; |
2 |
26 Feb 07 |
jari |
76 |
buf[o]=(byte)((v&0xff000000)>>24); |
2 |
26 Feb 07 |
jari |
77 |
} |
2 |
26 Feb 07 |
jari |
78 |
|
2 |
26 Feb 07 |
jari |
/** writes cmd/resp/type byte + 3/7 bytes len into a byte buffer at specified offset. |
2 |
26 Feb 07 |
jari |
@param ty type/cmd/resp byte |
2 |
26 Feb 07 |
jari |
@param len length |
2 |
26 Feb 07 |
jari |
@param buf buffer |
2 |
26 Feb 07 |
jari |
@param o offset |
2 |
26 Feb 07 |
jari |
@return offset in buf just after the header. Please note that since Rserve 0.3 the header can be either 4 or 8 bytes long, depending on the len parameter. |
2 |
26 Feb 07 |
jari |
85 |
*/ |
2 |
26 Feb 07 |
jari |
86 |
public static int setHdr(int ty, int len, byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
87 |
buf[o]=(byte)((ty&255)|((len>0xfffff0)?DT_LARGE:0)); o++; |
2 |
26 Feb 07 |
jari |
88 |
buf[o]=(byte)(len&255); o++; |
2 |
26 Feb 07 |
jari |
89 |
buf[o]=(byte)((len&0xff00)>>8); o++; |
2 |
26 Feb 07 |
jari |
90 |
buf[o]=(byte)((len&0xff0000)>>16); o++; |
2 |
26 Feb 07 |
jari |
91 |
if (len>0xfffff0) { // for large data we need to set the next 4 bytes as well |
2 |
26 Feb 07 |
jari |
92 |
buf[o]=(byte)((len&0xff000000)>>24); o++; |
2 |
26 Feb 07 |
jari |
93 |
buf[o]=0; o++; // since len is int, we get 32-bits only |
2 |
26 Feb 07 |
jari |
94 |
buf[o]=0; o++; |
2 |
26 Feb 07 |
jari |
95 |
buf[o]=0; o++; |
2 |
26 Feb 07 |
jari |
96 |
} |
2 |
26 Feb 07 |
jari |
97 |
return o; |
2 |
26 Feb 07 |
jari |
98 |
} |
2 |
26 Feb 07 |
jari |
99 |
|
2 |
26 Feb 07 |
jari |
/** creates a new header according to the type and length of the parameter |
2 |
26 Feb 07 |
jari |
@param ty type/cmd/resp byte |
2 |
26 Feb 07 |
jari |
@param len length */ |
2 |
26 Feb 07 |
jari |
103 |
public static byte[] newHdr(int ty, int len) { |
2 |
26 Feb 07 |
jari |
104 |
byte[] hdr=new byte[(len>0xfffff0)?8:4]; |
2 |
26 Feb 07 |
jari |
105 |
setHdr(ty,len,hdr,0); |
2 |
26 Feb 07 |
jari |
106 |
return hdr; |
2 |
26 Feb 07 |
jari |
107 |
} |
2 |
26 Feb 07 |
jari |
108 |
|
2 |
26 Feb 07 |
jari |
/** converts bit-wise stored int in Intel-endian form into Java int |
2 |
26 Feb 07 |
jari |
@param buf buffer containg the representation |
2 |
26 Feb 07 |
jari |
@param o offset where to start (4 bytes will be used) |
2 |
26 Feb 07 |
jari |
@return the int value. no bounds checking is done so you need to |
2 |
26 Feb 07 |
jari |
make sure that the buffer is big enough */ |
2 |
26 Feb 07 |
jari |
114 |
public static int getInt(byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
115 |
return ((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24)); |
2 |
26 Feb 07 |
jari |
116 |
} |
2 |
26 Feb 07 |
jari |
117 |
|
2 |
26 Feb 07 |
jari |
/** converts bit-wise stored length from a header. "long" format is supported up to 32-bit |
2 |
26 Feb 07 |
jari |
@param buf buffer |
2 |
26 Feb 07 |
jari |
@param o offset of the header (length is at o+1) |
2 |
26 Feb 07 |
jari |
@return length */ |
2 |
26 Feb 07 |
jari |
122 |
public static int getLen(byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
123 |
|
2 |
26 Feb 07 |
jari |
124 |
return |
2 |
26 Feb 07 |
jari |
125 |
((buf[o]&64)>0)? // "long" format; still - we support 32-bit only |
2 |
26 Feb 07 |
jari |
126 |
((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16)|((buf[o+4]&255)<<24)) |
2 |
26 Feb 07 |
jari |
127 |
: |
2 |
26 Feb 07 |
jari |
128 |
((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16)); |
2 |
26 Feb 07 |
jari |
129 |
} |
2 |
26 Feb 07 |
jari |
130 |
|
2 |
26 Feb 07 |
jari |
/** converts bit-wise Intel-endian format into long |
2 |
26 Feb 07 |
jari |
@param buf buffer |
2 |
26 Feb 07 |
jari |
@param o offset (8 bytes will be used) |
2 |
26 Feb 07 |
jari |
@return long value */ |
2 |
26 Feb 07 |
jari |
135 |
public static long getLong(byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
136 |
long low=((long)getInt(buf,o))&0xffffffffL; |
2 |
26 Feb 07 |
jari |
137 |
long hi=((long)getInt(buf,o+4))&0xffffffffL; |
2 |
26 Feb 07 |
jari |
138 |
hi<<=32; hi|=low; |
2 |
26 Feb 07 |
jari |
139 |
return hi; |
2 |
26 Feb 07 |
jari |
140 |
} |
2 |
26 Feb 07 |
jari |
141 |
|
2 |
26 Feb 07 |
jari |
142 |
public static void setLong(long l, byte[] buf, int o) { |
2 |
26 Feb 07 |
jari |
143 |
setInt((int)(l&0xffffffffL),buf,o); |
2 |
26 Feb 07 |
jari |
144 |
setInt((int)(l>>32),buf,o+4); |
2 |
26 Feb 07 |
jari |
145 |
} |
2 |
26 Feb 07 |
jari |
146 |
|
2 |
26 Feb 07 |
jari |
/** sends a request with no attached parameters |
2 |
26 Feb 07 |
jari |
@param cmd command |
2 |
26 Feb 07 |
jari |
@return returned packet or <code>null</code> if something went wrong */ |
2 |
26 Feb 07 |
jari |
150 |
public Rpacket request(int cmd) { |
2 |
26 Feb 07 |
jari |
151 |
byte[] d = new byte[0]; |
2 |
26 Feb 07 |
jari |
152 |
return request(cmd,d); |
2 |
26 Feb 07 |
jari |
153 |
} |
2 |
26 Feb 07 |
jari |
154 |
|
2 |
26 Feb 07 |
jari |
/** sends a request with attached parameters |
2 |
26 Feb 07 |
jari |
@param cmd command |
2 |
26 Feb 07 |
jari |
@param cont contents - parameters |
2 |
26 Feb 07 |
jari |
@return returned packet or <code>null</code> if something went wrong */ |
2 |
26 Feb 07 |
jari |
159 |
public Rpacket request(int cmd, byte[] cont) { |
2 |
26 Feb 07 |
jari |
160 |
return request(cmd,null,cont,0,(cont==null)?0:cont.length); |
2 |
26 Feb 07 |
jari |
161 |
} |
2 |
26 Feb 07 |
jari |
162 |
|
2 |
26 Feb 07 |
jari |
/** sends a request with attached prefix and parameters. Both prefix and cont can be <code>null</code>. Effectively <code>request(a,b,null)</code> and <code>request(a,null,b)</code> are equivalent. |
2 |
26 Feb 07 |
jari |
@param cmd command |
2 |
26 Feb 07 |
jari |
@param prefix - this content is sent *before* cont. It is provided to save memory copy operations where a small header precedes a large data chunk (usually prefix conatins the parameter header and cont contains the actual data). |
2 |
26 Feb 07 |
jari |
@param cont contents |
2 |
26 Feb 07 |
jari |
@param offset offset in cont where to start sending (if <0 then 0 is assumed, if >cont.length then no cont is sent) |
2 |
26 Feb 07 |
jari |
@param len number of bytes in cont to send (it is clipped to the length of cont if necessary) |
2 |
26 Feb 07 |
jari |
@return returned packet or <code>null</code> if something went wrong */ |
2 |
26 Feb 07 |
jari |
170 |
public Rpacket request(int cmd, byte[] prefix, byte[] cont, int offset, int len) { |
2 |
26 Feb 07 |
jari |
171 |
if (cont!=null) { |
2 |
26 Feb 07 |
jari |
172 |
if (offset>=cont.length) { cont=null; len=0; } |
2 |
26 Feb 07 |
jari |
173 |
else if (len>cont.length-offset) len=cont.length-offset; |
2 |
26 Feb 07 |
jari |
174 |
} |
2 |
26 Feb 07 |
jari |
175 |
if (offset<0) offset=0; |
2 |
26 Feb 07 |
jari |
176 |
if (len<0) len=0; |
2 |
26 Feb 07 |
jari |
177 |
int contlen=(cont==null)?0:len; |
2 |
26 Feb 07 |
jari |
178 |
if (prefix!=null && prefix.length>0) contlen+=prefix.length; |
2 |
26 Feb 07 |
jari |
179 |
byte[] hdr=new byte[16]; |
2 |
26 Feb 07 |
jari |
180 |
setInt(cmd,hdr,0); |
2 |
26 Feb 07 |
jari |
181 |
setInt(contlen,hdr,4); |
2 |
26 Feb 07 |
jari |
182 |
for(int i=8;i<16;i++) hdr[i]=0; |
2 |
26 Feb 07 |
jari |
183 |
try { |
2 |
26 Feb 07 |
jari |
184 |
os.write(hdr); |
2 |
26 Feb 07 |
jari |
185 |
if (prefix!=null && prefix.length>0) |
2 |
26 Feb 07 |
jari |
186 |
os.write(prefix); |
2 |
26 Feb 07 |
jari |
187 |
if (cont!=null && cont.length>0) |
2 |
26 Feb 07 |
jari |
188 |
os.write(cont,offset,len); |
2 |
26 Feb 07 |
jari |
189 |
|
2 |
26 Feb 07 |
jari |
190 |
byte[] ih=new byte[16]; |
2 |
26 Feb 07 |
jari |
191 |
if (is.read(ih)!=16) |
2 |
26 Feb 07 |
jari |
192 |
return null; |
2 |
26 Feb 07 |
jari |
193 |
int rep=getInt(ih,0); |
2 |
26 Feb 07 |
jari |
194 |
int rl =getInt(ih,4); |
2 |
26 Feb 07 |
jari |
195 |
if (rl>0) { |
2 |
26 Feb 07 |
jari |
196 |
byte[] ct=new byte[rl]; |
2 |
26 Feb 07 |
jari |
197 |
int n=0; |
2 |
26 Feb 07 |
jari |
198 |
while (n<rl) { |
2 |
26 Feb 07 |
jari |
199 |
int rd=is.read(ct,n,rl-n); |
2 |
26 Feb 07 |
jari |
200 |
n+=rd; |
2 |
26 Feb 07 |
jari |
201 |
} |
2 |
26 Feb 07 |
jari |
202 |
return new Rpacket(rep,ct); |
2 |
26 Feb 07 |
jari |
203 |
} |
2 |
26 Feb 07 |
jari |
204 |
return new Rpacket(rep,null); |
2 |
26 Feb 07 |
jari |
205 |
} catch(Exception e) { |
2 |
26 Feb 07 |
jari |
206 |
return null; |
2 |
26 Feb 07 |
jari |
207 |
} |
2 |
26 Feb 07 |
jari |
208 |
} |
2 |
26 Feb 07 |
jari |
209 |
|
2 |
26 Feb 07 |
jari |
/** sends a request with one string parameter attached |
2 |
26 Feb 07 |
jari |
@param cmd command |
2 |
26 Feb 07 |
jari |
@param par parameter - length and DT_STRING will be prepended |
2 |
26 Feb 07 |
jari |
@return returned packet or <code>null</code> if something went wrong */ |
2 |
26 Feb 07 |
jari |
214 |
public Rpacket request(int cmd, String par) { |
2 |
26 Feb 07 |
jari |
215 |
try { |
2 |
26 Feb 07 |
jari |
216 |
byte[] b=par.getBytes(Rconnection.transferCharset); |
2 |
26 Feb 07 |
jari |
217 |
int sl=par.length()+1; |
2 |
26 Feb 07 |
jari |
218 |
if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the length is divisible by 4 |
2 |
26 Feb 07 |
jari |
219 |
byte[] rq=new byte[sl+5]; |
2 |
26 Feb 07 |
jari |
220 |
int i; |
2 |
26 Feb 07 |
jari |
221 |
for(i=0;i<b.length;i++) |
2 |
26 Feb 07 |
jari |
222 |
rq[i+4]=b[i]; |
2 |
26 Feb 07 |
jari |
223 |
while (i<sl) { // pad with 0 |
2 |
26 Feb 07 |
jari |
224 |
rq[i+4]=0; i++; |
2 |
26 Feb 07 |
jari |
225 |
}; |
2 |
26 Feb 07 |
jari |
226 |
setHdr(DT_STRING,sl,rq,0); |
2 |
26 Feb 07 |
jari |
227 |
return request(cmd,rq); |
2 |
26 Feb 07 |
jari |
228 |
} catch (Exception e) { |
2 |
26 Feb 07 |
jari |
229 |
}; |
2 |
26 Feb 07 |
jari |
230 |
return null; |
2 |
26 Feb 07 |
jari |
231 |
} |
2 |
26 Feb 07 |
jari |
232 |
|
2 |
26 Feb 07 |
jari |
/** sends a request with one string parameter attached |
2 |
26 Feb 07 |
jari |
@param cmd command |
2 |
26 Feb 07 |
jari |
@param par parameter of the type DT_INT |
2 |
26 Feb 07 |
jari |
@return returned packet or <code>null</code> if something went wrong */ |
2 |
26 Feb 07 |
jari |
237 |
public Rpacket request(int cmd, int par) { |
2 |
26 Feb 07 |
jari |
238 |
try { |
2 |
26 Feb 07 |
jari |
239 |
byte[] rq=new byte[8]; |
2 |
26 Feb 07 |
jari |
240 |
setInt(par,rq,4); |
2 |
26 Feb 07 |
jari |
241 |
setHdr(DT_INT,4,rq,0); |
2 |
26 Feb 07 |
jari |
242 |
return request(cmd,rq); |
2 |
26 Feb 07 |
jari |
243 |
} catch (Exception e) { |
2 |
26 Feb 07 |
jari |
244 |
}; |
2 |
26 Feb 07 |
jari |
245 |
return null; |
2 |
26 Feb 07 |
jari |
246 |
} |
2 |
26 Feb 07 |
jari |
247 |
} |