xref: /trunk/test/testcommon/source/org/openoffice/test/vcl/client/CommandCaller.java (revision 3309286857f19787ae62bd793a98b5af4edd2ad3)
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