1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.test.vcl.client; 25 26 import java.io.ByteArrayInputStream; 27 import java.io.ByteArrayOutputStream; 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 33 /** 34 * The class is used to send a statement to the automation server. 35 * 36 * 37 */ 38 public class CommandCaller implements CommunicationListener, Constant { 39 40 private ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(1024); 41 42 private ByteArrayInputStream dataInput = null; 43 44 private CommunicationManager communicationManager = null; 45 46 private int sequence = 0; 47 48 private WinInfoReceiver winInfoReceiver = null; 49 50 private boolean receivingWinInfo = false; 51 52 /**Store the response**/ 53 private Object response = null; 54 55 private SmartId responseExceptionId = null; 56 57 private String responseExceptionMessage = null; 58 59 /**A variable to indicate if the server answered the request **/ 60 private boolean answered = false; 61 CommandCaller(CommunicationManager communicationManager)62 public CommandCaller(CommunicationManager communicationManager) { 63 this.communicationManager = communicationManager; 64 communicationManager.addListener(this); 65 } 66 write(byte[] bytes)67 private void write(byte[] bytes) { 68 try { 69 dataOutput.write(bytes); 70 } catch (IOException e) { 71 e.printStackTrace(); 72 } 73 } 74 read(int len)75 private byte[] read(int len) { 76 byte[] bytes = new byte[len]; 77 try { 78 dataInput.read(bytes); 79 } catch (IOException e) { 80 e.printStackTrace(); 81 } 82 return bytes; 83 } 84 85 86 /** 87 * Write the data type 88 * @param i 89 */ writeChar(int i)90 private void writeChar(int i) { 91 byte[] bytes = new byte[2]; 92 bytes[1] = (byte) ((i & 0xFF00) >> 8); 93 bytes[0] = (byte) (i & 0x00FF); 94 write(bytes); 95 } 96 readChar()97 private int readChar() { 98 byte[] bytes = read(2); 99 return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8); 100 } 101 102 103 /** 104 * Check the type of next data 105 * The bytes used to identify the type will not be read out from the stream 106 * @return 107 */ nextType()108 private int nextType() { 109 dataInput.mark(0); 110 int type = readChar(); 111 dataInput.reset(); 112 return type; 113 } 114 115 /** 116 * Write an unsigned 16-bit integer 117 * @param i 118 */ writeUShort(int i)119 private void writeUShort(int i) { 120 writeChar(BinUSHORT); 121 writeChar(i); 122 } 123 124 /** 125 * Read an unsigned 16-bit integer 126 * @return 127 */ readUShort()128 private int readUShort() { 129 if (readChar() != BinUSHORT) 130 throw new RuntimeException("Bad data!"); 131 132 byte[] bytes = read(2); 133 return (bytes[0] & 0x00FF) + ((bytes[1] & 0x00FF) << 8); 134 } 135 136 /** 137 * Write an unsigned 32-bit integer 138 * @param i 139 */ writeULong(long i)140 private void writeULong(long i) { 141 writeChar(BinULONG); 142 byte[] bytes = new byte[4]; 143 bytes[3] = (byte) ((i & 0xFF000000L) >> 24); 144 bytes[2] = (byte) ((i & 0x00FF0000L) >> 16); 145 bytes[1] = (byte) ((i & 0x0000FF00L) >> 8); 146 bytes[0] = (byte) ((i & 0x000000FFL)); 147 write(bytes); 148 } 149 150 /** 151 * Read an unsigned 32-bit integer 152 * @return 153 */ readULong()154 private long readULong() { 155 if (readChar() != BinULONG) 156 throw new RuntimeException("Bad data!"); 157 byte[] bytes = read(4); 158 return (bytes[0] & 0x00FFL) + ((bytes[1] & 0x00FFL) << 8) + ((bytes[2] & 0x00FFL) << 16) + ((bytes[3] & 0x00FFL) << 24); 159 } 160 161 /** 162 * Write boolean 163 * @param bBool 164 */ writeBoolean(boolean bBool)165 private void writeBoolean(boolean bBool) { 166 writeChar(BinBool); 167 write(new byte[] { bBool ? (byte) 1 : 0 }); 168 } 169 170 /** 171 * Read boolean 172 * @return 173 */ readBoolean()174 private boolean readBoolean() { 175 if (readChar() != BinBool) 176 throw new RuntimeException("Bad data!"); 177 byte[] bytes = read(1); 178 return bytes[0] != 0; 179 } 180 181 /** 182 * Write a string 183 * @param str 184 */ writeString(String str)185 private void writeString(String str) { 186 writeChar(BinString); 187 int len = str.length(); 188 if (len > 0xFFFF) { 189 throw new RuntimeException("String is too long."); 190 } 191 writeChar(len); 192 char[] chars = str.toCharArray(); 193 for (int i = 0 ; i < len; i++) 194 writeChar(chars[i]); 195 } 196 197 /** 198 * Read a string 199 * @return 200 */ readString()201 private String readString() { 202 if (readChar() != BinString) 203 throw new RuntimeException("Bad data!"); 204 int len = readChar(); 205 char[] chars = new char[len]; 206 for (int i = 0; i < len; i++) { 207 chars[i] = (char) readChar(); 208 } 209 return new String(chars); 210 } 211 writeParams(Object[] args)212 private void writeParams(Object[] args) { 213 int nParams = PARAM_NONE; 214 int nNr1=0; 215 int nNr2=0; 216 int nNr3=0; 217 int nNr4=0; 218 long nLNr1=0; 219 String aString1=null; 220 String aString2=null; 221 boolean bBool1=false; 222 boolean bBool2=false; 223 224 if (args != null) { 225 for (int i = 0; i < args.length; i++) { 226 if (args[i] instanceof Short || args[i] instanceof Integer) { 227 int c = ((Number) args[i]).intValue(); 228 if ((nParams & PARAM_USHORT_1) == 0) { 229 nParams |= PARAM_USHORT_1; 230 nNr1 = c; 231 } else if ((nParams & PARAM_USHORT_2) == 0) { 232 nParams |= PARAM_USHORT_2; 233 nNr2 = c; 234 } else if ((nParams & PARAM_USHORT_3) == 0) { 235 nParams |= PARAM_USHORT_3; 236 nNr3 = c; 237 } else if ((nParams & PARAM_USHORT_4) == 0) { 238 nParams |= PARAM_USHORT_4; 239 nNr4 = c; 240 } else { 241 //TODO error 242 } 243 } else if (args[i] instanceof Long) { 244 long l = ((Long) args[i]).longValue(); 245 nParams |= PARAM_ULONG_1; 246 nLNr1 = l; 247 } else if (args[i] instanceof Boolean) { 248 if ((nParams & PARAM_BOOL_1) == 0) { 249 nParams |= PARAM_BOOL_1; 250 bBool1 = ((Boolean) args[i]).booleanValue(); 251 } else if ((nParams & PARAM_BOOL_2) == 0) { 252 nParams |= PARAM_BOOL_2; 253 bBool2 = ((Boolean) args[i]).booleanValue(); 254 } else { 255 //TODO error 256 } 257 258 } else if (args[i] instanceof String) { 259 if ((nParams & PARAM_STR_1) == 0) { 260 nParams |= PARAM_STR_1; 261 aString1 = (String) args[i]; 262 } else if ((nParams & PARAM_STR_2) == 0) { 263 nParams |= PARAM_STR_2; 264 aString2 = (String) args[i]; 265 } else { 266 //TODO error 267 } 268 } 269 } 270 } 271 272 writeUShort(nParams); 273 if ((nParams & PARAM_USHORT_1) != 0) { 274 writeUShort(nNr1); 275 } 276 277 if ((nParams & PARAM_USHORT_2) != 0) { 278 writeUShort(nNr2); 279 } 280 281 if ((nParams & PARAM_USHORT_3) != 0) { 282 writeUShort(nNr3); 283 } 284 285 if ((nParams & PARAM_USHORT_4) != 0) { 286 writeUShort(nNr4); 287 } 288 289 if ((nParams & PARAM_ULONG_1) != 0) { 290 writeULong(nLNr1); 291 } 292 293 if ((nParams & PARAM_STR_1) != 0) { 294 writeString(aString1); 295 } 296 297 if ((nParams & PARAM_STR_2) != 0) { 298 writeString(aString2); 299 } 300 301 if ((nParams & PARAM_BOOL_1) != 0) { 302 writeBoolean(bBool1); 303 } 304 if ((nParams & PARAM_BOOL_2) != 0) { 305 writeBoolean(bBool2); 306 } 307 308 } 309 send()310 private void send() { 311 byte[] data = dataOutput.toByteArray(); 312 dataOutput.reset(); 313 int protocal = CM_PROTOCOL_OLDSTYLE; 314 byte[] header = new byte[]{(byte)((protocal >>> 8) & 0xFF), (byte) ((protocal >>> 0) & 0xFF)}; 315 communicationManager.sendPackage(CH_SimpleMultiChannel, header, data); 316 } 317 318 /** 319 * The data arrives 320 */ received(int headerType, byte[] header, byte[] data)321 public synchronized void received(int headerType, byte[] header, byte[] data) { 322 if (headerType != CommunicationManager.CH_Handshake) { 323 dataInput = new ByteArrayInputStream(data); 324 handleResponse(); 325 dataInput = null; 326 answered = true; 327 notifyAll(); 328 } 329 } 330 331 /** 332 * This method is called when the communication is started. 333 */ start()334 public void start() { 335 } 336 337 /** 338 * This method is called when the communication is closed 339 */ stop()340 public synchronized void stop() { 341 answered = true; 342 notifyAll(); 343 } 344 345 readId()346 private SmartId readId() { 347 int type = nextType(); 348 if ( type == BinString) { 349 return new SmartId(readString()); 350 } else if (type == BinULONG) { 351 return new SmartId(readULong()); 352 } 353 354 throw new RuntimeException("Bad data!"); 355 } 356 handleResponse()357 private void handleResponse() { 358 this.response = null; 359 this.responseExceptionId = null; 360 this.responseExceptionMessage = null; 361 362 while (dataInput.available() >= 2) { 363 int id = readUShort(); 364 switch (id) { 365 case SIReturn: 366 int returnType = readUShort(); 367 SmartId sid = readId(); 368 int nNr1 = 0; 369 long nLNr1 = 0; 370 String aString1 = null; 371 boolean bBool1 = false; 372 int params = readUShort(); 373 if ((params & PARAM_USHORT_1) != 0) 374 nNr1 = readUShort(); 375 if ((params & PARAM_ULONG_1) != 0) 376 nLNr1 = readULong(); 377 if ((params & PARAM_STR_1) != 0) 378 aString1 = readString(); 379 if ((params & PARAM_BOOL_1) != 0) 380 bBool1 = readBoolean(); 381 if ((params & PARAM_SBXVALUE_1) != 0) { 382 // Don't support???? 383 } 384 385 switch (returnType) { 386 case RET_Sequence: 387 if (sid.getId() != sequence) 388 this.responseExceptionMessage = "Bad sequence of command."; 389 break; 390 case RET_Value: 391 List<Object> ret = new ArrayList<Object>(); 392 if ((params & PARAM_USHORT_1) != 0) 393 ret.add(new Integer(nNr1)); 394 if ((params & PARAM_ULONG_1) != 0) 395 ret.add(new Long(nLNr1)); 396 if ((params & PARAM_STR_1) != 0) 397 ret.add(aString1); 398 if ((params & PARAM_BOOL_1) != 0) 399 ret.add(new Boolean(bBool1)); 400 this.response = ret.size() == 1 ? ret.get(0): ret; 401 break; 402 case RET_WinInfo: 403 if (bBool1) { 404 receivingWinInfo = true; 405 if (winInfoReceiver != null) 406 winInfoReceiver.onStartReceiving(); 407 } else { 408 if (winInfoReceiver != null) 409 winInfoReceiver.addWinInfo(sid, nLNr1, aString1); 410 } 411 break; 412 } 413 414 break; 415 case SIReturnError: 416 this.responseExceptionId = readId(); 417 this.responseExceptionMessage = readString(); 418 break; 419 } 420 } 421 422 if (receivingWinInfo) { 423 if (winInfoReceiver != null) 424 winInfoReceiver.onFinishReceiving(); 425 receivingWinInfo = false; 426 } 427 } 428 429 setWinInfoReceiver(WinInfoReceiver receiver)430 public void setWinInfoReceiver(WinInfoReceiver receiver) { 431 this.winInfoReceiver = receiver; 432 } 433 callFlow(int nArt)434 private void callFlow(int nArt) { 435 writeUShort(SIFlow); 436 writeUShort(nArt); 437 writeUShort(PARAM_NONE); 438 } 439 440 callFlow(int nArt, long nLNr1)441 private void callFlow(int nArt, long nLNr1) { 442 writeUShort(SIFlow); 443 writeUShort(nArt); 444 writeUShort(PARAM_ULONG_1); 445 writeULong(nLNr1); 446 } 447 448 /** 449 * Tell automation server to execute a 'StatementCommand' 450 * @param methodId The method ID 451 * @param args the arguments. The arguments can be Integer, Long, Boolean and String. 452 * @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object. 453 */ callCommand(int methodId, Object... args)454 public synchronized Object callCommand(int methodId, Object... args) { 455 beginBlock(); 456 writeUShort(SICommand); 457 writeUShort(methodId); 458 writeParams(args); 459 endBlock(); 460 461 if ((methodId & M_WITH_RETURN) != 0) { 462 return response; 463 } 464 465 return null; 466 } 467 callCommand(int methodId)468 public synchronized Object callCommand(int methodId) { 469 return callCommand(methodId, (Object)null); 470 } 471 472 /** 473 * Tell automation server to execute a 'StatementControl' 474 * @param id the control ID 475 * @param methodId the method ID defined Constant class 476 * @param args the arguments. The arguments can be Integer, Long, Boolean and String. 477 * @return The return can be Integer, Long, String and Boolean or an Object[] includes these types of object. 478 */ callControl(String id, int methodId, Object... args)479 public synchronized Object callControl(String id, int methodId, Object... args){ 480 beginBlock(); 481 try { 482 long noId = Long.parseLong(id); 483 writeUShort(SIControl); 484 writeULong(noId); 485 } catch (NumberFormatException e) { 486 writeUShort(SIStringControl); 487 writeString(id); 488 } 489 writeUShort(methodId); 490 writeParams(args); 491 endBlock(); 492 493 if ((methodId & M_WITH_RETURN) != 0) { 494 return response; 495 } 496 497 return null; 498 } 499 500 /** 501 * Tell automation server to execute a 'StatementUNOSlot' 502 * @param url the UNO slot url 503 */ callUNOSlot(String url)504 public synchronized void callUNOSlot(String url) { 505 beginBlock(); 506 writeUShort(SIUnoSlot); 507 writeString(url); 508 endBlock(); 509 } 510 511 /** 512 * Tell automation server to execute a 'StatementSlot' 513 * @param id the slot ID 514 * @param args the slot args 515 */ callSlot(int id, Object... args)516 public synchronized void callSlot(int id, Object... args) { 517 beginBlock(); 518 writeUShort(SISlot); 519 writeUShort(id); 520 if (args.length % 2 != 0) 521 throw new RuntimeException("bad arg"); 522 writeUShort(args.length / 2); 523 for (int i = 0; i < args.length; i++) { 524 if (!(args[i] instanceof String)) 525 throw new RuntimeException("bad arg"); 526 writeString((String)args[i]); 527 i++; 528 if (args[i] instanceof Boolean) { 529 writeBoolean((Boolean)args[i]); 530 } else if (args[i] instanceof String) { 531 writeString((String)args[i]); 532 } else if (args[i] instanceof Short || args[i] instanceof Integer) { 533 writeUShort(((Number) args[i]).intValue()); 534 } else if (args[i] instanceof Long) { 535 writeULong((Long) args[i]); 536 } else { 537 throw new RuntimeException("bad arg"); 538 } 539 } 540 endBlock(); 541 } 542 543 beginBlock()544 private void beginBlock() { 545 callFlow(F_Sequence, ++sequence); 546 } 547 endBlock()548 private void endBlock() { 549 callFlow(F_EndCommandBlock); 550 answered = false; 551 send(); 552 int MAX_RETRY = 240;//max waiting time is two minutes. 553 for (int i = 0; !answered && i < MAX_RETRY; i++) { 554 try { 555 wait(500); 556 } catch (InterruptedException e) { 557 558 } 559 } 560 561 // Still answered 562 if (!answered) { 563 communicationManager.stop(); 564 throw new CommunicationException("Failed to get data from automation server!"); 565 } 566 567 if (responseExceptionId != null || responseExceptionMessage != null) 568 throw new VclHookException(responseExceptionId, responseExceptionMessage); 569 570 } 571 } 572