View Javadoc
1   package nl.tno.imb;
2   
3   import nl.tno.imb.TEventEntry;
4   
5   import java.io.InputStream;
6   import java.io.OutputStream;
7   import java.util.Arrays;
8   import java.io.IOException;
9   import java.net.Socket;
10  import java.net.SocketException;
11  
12  
13  /**The connection to the framework and starting point to use IMB.
14   * All further actions are started through a new object of this class.
15   * Main actions are:<br>
16   * subscribe to events to receive events<br>
17   * publish events to send events (called signal). if autoPublish is true you start sending events immediately<br>
18   * set global framework variables<br>
19   * send streams over the framework<br>
20   * set/update the current status for the connected client<br>
21   * optionally define the owner (specific: connected model name and id)<br>
22   * optionally set specific socket and connection options<br>
23   * <br>
24   * Subscribe and publish return TEventEntry objects that can be used to set handlers for receiving specific events.<br>
25   * Default a reading thread is started to handle all socket reading. The thread calls registered event handlers for the received events.<br>
26   * 
27   *  
28   * @author hans.cornelissen@tno.nl
29   */
30  public class TConnection {
31  
32      // constructors
33      /**Create an IMB connection to the framework
34       * @param aRemoteHost IP address or DNS name of the IMB hub to connect to
35       * @param aRemotePort TCP port of the IMB hub to connect to
36       * @param aOwnerName optional description of the connecting client
37       * @param aOwnerID optional id of the connecting client 
38       * @param aFederation federation to connect with; this is default prefixed to subscribed and published event names 
39       */
40      public TConnection(String aRemoteHost, int aRemotePort, String aOwnerName, int aOwnerID, String aFederation) {
41          this(aRemoteHost, aRemotePort, aOwnerName, aOwnerID, aFederation, true);
42      }
43  
44      /**Create an IMB connection to the framework
45       * @param aRemoteHost IP address or DNS name of the IMB hub to connect to
46       * @param aRemotePort TCP port of the IMB hub to connect to
47       * @param aOwnerName optional description of the connecting client
48       * @param aOwnerID optional id of the connecting client 
49       * @param aFederation federation to connect with; this is default prefixed to subscribed and published event names
50       * @param aStartReadingThread use an internal reader thread for processing events and commands from the connected hub
51       */
52      public TConnection(String aRemoteHost, int aRemotePort, String aOwnerName, int aOwnerID, String aFederation,
53              boolean aStartReadingThread) {
54          this.ffederation = aFederation;
55          this.fownerName = aOwnerName;
56          this.fownerID = aOwnerID;
57          open(aRemoteHost, aRemotePort, aStartReadingThread);
58      }
59      
60      // destructor
61      protected void finalize() {
62          close();
63      }
64  
65      // internals/privates
66      
67      /** event id's on the hub are different from local id's. this list is used to translate incoming hub event id's */
68      private class TEventTranslation {
69          public final static int INVALID_TRANSLATED_EVENT_ID = -1;
70  
71          private int[] feventTranslation;
72  
73          public TEventTranslation() {
74              this.feventTranslation = new int[32];
75              // mark all entries as invalid
76              for (int i = 0; i < this.feventTranslation.length; i++)
77                  this.feventTranslation[i] = INVALID_TRANSLATED_EVENT_ID;
78          }
79  
80          public int getTranslateEventID(int aRxEventID) {
81              if ((0 <= aRxEventID) && (aRxEventID < this.feventTranslation.length))
82                  return this.feventTranslation[aRxEventID];
83              else
84                  return INVALID_TRANSLATED_EVENT_ID;
85          }
86  
87          public void setEventTranslation(int aRxEventID, int aTxEventID) {
88              if (aRxEventID >= 0) {
89                  // grow event translation list until it can contain the
90                  // requested id
91                  while (aRxEventID >= this.feventTranslation.length) {
92                      int FormerSize = this.feventTranslation.length;
93                      // resize event translation array to double the size
94                      this.feventTranslation = Arrays.copyOf(this.feventTranslation, this.feventTranslation.length * 2);
95                      // mark all new entries as invalid
96                      for (int i = FormerSize; i < this.feventTranslation.length; i++)
97                          this.feventTranslation[i] = INVALID_TRANSLATED_EVENT_ID;
98                  }
99                  this.feventTranslation[aRxEventID] = aTxEventID;
100             }
101         }
102     }
103 
104     /** internal list of all local events */
105     private class TEventEntryList {
106         
107         TEventEntryList(int aInitialSize) {
108             this.FCount = 0;
109             this.fevents = new TEventEntry[aInitialSize];
110         }
111 
112         private TEventEntry[] fevents;
113         private int FCount = 0;
114 
115         public TEventEntry getEventEntry(int aEventID) {
116             if (0 <= aEventID && aEventID < this.FCount)
117                 return this.fevents[aEventID];
118             else
119                 return null;
120         }
121 
122         public String getEventName(int aEventID) {
123             if (0 <= aEventID && aEventID < this.FCount) {
124                 if (this.fevents[aEventID] != null)
125                     return this.fevents[aEventID].getEventName();
126                 else
127                     return null;
128             } else
129                 return "";
130         }
131 
132         public TEventEntry addEvent(TConnection aConnection, String aEventName) {
133             this.FCount++;
134             if (this.FCount>this.fevents.length)
135                 this.fevents = Arrays.copyOf(this.fevents, this.fevents.length * 2);
136             this.fevents[this.FCount - 1] = new TEventEntry(aConnection, this.FCount - 1, aEventName);
137 
138             return this.fevents[this.FCount - 1];
139         }
140 
141         public TEventEntry getEventEntryOnName(String aEventName) {
142             int i = this.FCount - 1;
143             while (i >= 0 && !getEventName(i).equals(aEventName))
144                 i--;
145             if (i >= 0)
146                 return this.fevents[i];
147             else
148                 return null;
149         }
150     }
151     // TODO: description
152     public static final String EVENT_FILTER_POST_FIX = "*";
153     /** postfix of the variable name for the model status */
154     private static final String MODEL_Status_VAR_NAME = "ModelStatus";
155     /** separator char for the postfix of the variable name for the model status */
156     private static final String MODEL_STATUS_VAR_SEP_CHAR = "|";
157     // constants
158     /**magic bytes to identify the start of a valid IMB packet */
159     public static final byte[] MAGIC_BYTES = new byte[] {
160             0x2F, 0x47, 0x61, 0x71, (byte) 0x95, (byte) 0xAD, (byte) 0xC5, (byte) 0xFB
161     };
162     // private static final long MagicBytesInt64 = 0xFBC5AD957161472FL;
163     /**magic bytes to identify the end of the payload on a valid IMB packet (as 32 bit integer)*/
164     private static final int MAGIC_STRING_CHECK_INT32 = 0x10F13467;
165     /**magic bytes to identify the end of the payload on a valid IMB packet (as array of bytes)*/
166     public static final byte[] MAGIC_STRING_CHECK = new byte[] {
167             0x67, 0x34, (byte) 0xF1, 0x10
168     };
169     // fields
170     /** TCP Socket the connection is based on */
171     private Socket fsocket = null;
172     /** output stream linked to the socket */
173     private OutputStream foutputStream = null;
174     /** input stream linked to the socket */
175     private InputStream finputStream = null;
176     /** address the socket is connected to */
177     private String fremoteHost = "";
178     /** TCP port the socket is connected to */
179     private int fremotePort = 0;
180     /** optional reader thread */
181     private Thread freadingThread = null;
182     /** event id's on the hub are different from local id's. this list is used to translate incoming hub event id's */
183     private TEventTranslation feventTranslation = new TEventTranslation();
184     /** internal list of all local events */
185     private TEventEntryList feventEntryList = new TEventEntryList(8);
186     /** active federation */
187     private String ffederation = DEFAULT_FEDERATION;
188     // standard event references
189     private static final String FOCUS_EVENT_NAME = "Focus";
190     private TEventEntry ffocusEvent = null;
191     private static final String CHANGE_FEDERATION_EVENT_NAME = "META_CurrentSession";
192     private TEventEntry fchangeFederationEvent = null;
193     private TEventEntry flogEvent = null;
194     // broker time
195     // private long fbrokerAbsoluteTime = 0;
196     // private int fbrokerTick = 0;
197     // private int fbrokerTickDelta = 0;
198     private int funiqueClientID = 0;
199     private int fclientHandle = 0;
200     private int fownerID = 0;
201     private String fownerName = "";
202 
203     private TEventEntry eventIDToEventL(int aEventID) {
204         synchronized (this.feventEntryList) {
205             return this.feventEntryList.getEventEntry(aEventID);
206         }
207     }
208 
209     private TEventEntry addEvent(String aEventName) {
210         int EventID = 0;
211         TEventEntry Event;
212         while (EventID < this.feventEntryList.FCount && !this.feventEntryList.getEventEntry(EventID).isEmpty())
213             EventID += 1;
214         if (EventID < this.feventEntryList.FCount) {
215             Event = this.feventEntryList.getEventEntry(EventID);
216             Event.feventName = aEventName;
217             Event.fparent = null;
218         } else
219             Event = this.feventEntryList.addEvent(this, aEventName);
220         return Event;
221     }
222     
223     private TEventEntry addEventL(String aEventName) {
224         TEventEntry Event;
225         synchronized (this.feventEntryList) {
226             Event = addEvent(aEventName);
227         }
228         return Event;
229     }
230     
231     private TEventEntry findOrAddEventL(String aEventName) {
232         synchronized (this.feventEntryList) {
233             TEventEntry Event = this.feventEntryList.getEventEntryOnName(aEventName);
234             if (Event == null) {
235                 int EventID = 0;
236                 while (EventID < this.feventEntryList.FCount && !this.feventEntryList.getEventEntry(EventID).isEmpty())
237                     EventID += 1;
238                 if (EventID < this.feventEntryList.FCount) {
239                     Event = this.feventEntryList.getEventEntry(EventID);
240                     Event.feventName = aEventName;
241                 } else
242                     Event = this.feventEntryList.addEvent(this, aEventName);
243             }
244             return Event;
245         }
246     }
247 
248     private TEventEntry findEventL(String aEventName) {
249         synchronized (this.feventEntryList) {
250             return this.feventEntryList.getEventEntryOnName(aEventName);
251         }
252     }
253 
254     private TEventEntry findEventParentL(String aEventName) {
255         String ParentEventName;
256         String EventName;
257         ParentEventName = "";
258         TEventEntry Event = null;
259         synchronized (this.feventEntryList) {
260             for (int EventID = 0; EventID<this.feventEntryList.FCount; EventID++)
261             {
262                 EventName = this.feventEntryList.getEventName(EventID);
263                 if (EventName.endsWith(EVENT_FILTER_POST_FIX))
264                 {
265                     EventName = EventName.substring(0, EventName.length()-2);
266                     if (aEventName.startsWith(EventName))
267                     {
268                         if (ParentEventName.length()<EventName.length())
269                         {
270                             Event = this.feventEntryList.getEventEntry(EventID);
271                             ParentEventName = EventName;
272                         }
273                     }
274                 }
275             }
276             return Event;
277         }
278     }
279 
280     private TEventEntry findEventAutoPublishL(String aEventName) {
281         TEventEntry Event = findEventL(aEventName);
282         if (Event == null && this.autoPublish)
283             Event = publish(aEventName, false);
284         return Event;
285     }
286 
287     private int readBytesFromNetStream(TByteBuffer aBuffer) {
288         try {
289             int Count = 0;
290             int NumBytesRead = -1;
291             while (aBuffer.getwriteAvailable() > 0 && NumBytesRead != 0) {
292                 NumBytesRead = this.finputStream.read(aBuffer.getBuffer(), aBuffer.getWriteCursor(), aBuffer.getwriteAvailable());
293                 aBuffer.written(NumBytesRead);
294                 Count += NumBytesRead;
295             }
296             return Count;
297         } catch (IOException ex) {
298             return 0; // signal connection error
299         }
300     }
301 
302     // function returns payload of command, fills found command and returns
303     // problems during read in result
304     // commandmagic + command + payloadsize [ + payload + payloadmagic]
305     private int readCommand(TByteBuffer aFixedCommandPart, TByteBuffer aPayload, TByteBuffer aPayloadCheck)
306             throws IOException {
307         int NumBytesRead = this.finputStream.read(aFixedCommandPart.getBuffer(), 0, aFixedCommandPart.getLength());
308         if (NumBytesRead > 0) {
309             while (!aFixedCommandPart.compare(MAGIC_BYTES, 0)) {
310                 int rbr = this.finputStream.read();
311                 // skipped bytes because of invalid magic in read command
312                 if (rbr != -1)
313                     aFixedCommandPart.shiftLeftOneByte((byte) rbr);
314                 else
315                     return TEventEntry.IC_END_OF_SESSION;
316             }
317             // we found the magic in the stream
318             int aCommand = aFixedCommandPart.peekInt32(MAGIC_BYTES.length);
319             int PayloadSize = aFixedCommandPart.peekInt32(MAGIC_BYTES.length + TByteBuffer.SIZE_OF_INT32);
320             if (PayloadSize <= MAX_PAYLOAD_SIZE) {
321                 aPayload.clear(PayloadSize);
322                 if (PayloadSize > 0) {
323                     int Len = readBytesFromNetStream(aPayload);
324                     if (Len == aPayload.getLength()) {
325                         NumBytesRead = this.finputStream.read(aPayloadCheck.getBuffer(), 0, aPayloadCheck.getLength());
326                         if (NumBytesRead == TByteBuffer.SIZE_OF_INT32 && aPayloadCheck.compare(MAGIC_STRING_CHECK, 0))
327                             return aCommand;
328                         else
329                             return TEventEntry.IC_INVALID_COMMAND;
330                     } else
331                         // error, payload size mismatch
332                         return TEventEntry.IC_INVALID_COMMAND;
333                 } else
334                     return aCommand; // OK, no payload
335             } else
336                 return TEventEntry.IC_INVALID_COMMAND; // error, payload is over max size
337         } else
338             return TEventEntry.IC_END_OF_SESSION; // error, no valid connection
339     }
340 
341     /** place holder for the Lock for writing commands to the framework */
342     private Integer fwiteCommandLock = new Integer(0);
343 
344     /**Write a single command to the framework
345      * @param aCommand
346      * @param aPayload
347      * @return see ICE_* constants
348      */
349     protected int writeCommand(int aCommand, byte[] aPayload)
350     {
351         synchronized (this.fwiteCommandLock) {
352             TByteBuffer Buffer = new TByteBuffer();
353 
354             Buffer.prepare(MAGIC_BYTES);
355             Buffer.prepare(aCommand);
356 
357             if ((aPayload != null) && (aPayload.length > 0)) {
358                 Buffer.prepare(aPayload.length);
359                 Buffer.prepare(aPayload);
360                 Buffer.prepare(MAGIC_STRING_CHECK_INT32);
361             } else
362                 Buffer.prepare((int) 0); // payload size=0
363             Buffer.prepareApply();
364             Buffer.qWrite(MAGIC_BYTES);
365             Buffer.qWrite(aCommand);
366             if ((aPayload != null) && (aPayload.length > 0)) {
367                 Buffer.qWrite(aPayload.length);
368                 Buffer.qWrite(aPayload);
369                 Buffer.qWrite(MAGIC_STRING_CHECK_INT32);
370             } else
371                 Buffer.qWrite((int) 0);
372             // send buffer over socket
373             if (isConnected()) {
374                 try {
375                     this.foutputStream.write(Buffer.getBuffer(), 0, Buffer.getLength());
376                     return Buffer.getLength();
377                 } catch (Exception ex) {
378                     close();
379                     return ICE_CONNECTION_CLOSED;
380                 }
381             } else {
382                 return ICE_CONNECTION_CLOSED;
383             }
384         }
385     }
386 
387     protected String prefixFederation(String aName) {
388         return prefixFederation(aName, true);
389     }
390 
391     protected String prefixFederation(String aName, boolean aUseFederationPrefix) {
392         if (!this.ffederation.equals("") && aUseFederationPrefix)
393             return this.ffederation + "." + aName;
394         else
395             return aName;
396     }
397 
398     /**Main framework command dispatcher
399      * @param aCommand
400      * @param aPayload
401      */
402     private void handleCommand(int aCommand, TByteBuffer aPayload) {
403         switch (aCommand) {
404         case TEventEntry.IC_EVENT:
405             handleCommandEvent(aPayload);
406             break;
407         case TEventEntry.IC_SET_VARIABLE:
408             handleCommandVariable(aPayload);
409             break;
410         case TEventEntry.IC_SET_EVENT_ID_TRANSLATION:
411             this.feventTranslation.setEventTranslation(aPayload.peekInt32(0, TEventTranslation.INVALID_TRANSLATED_EVENT_ID),
412                     aPayload.peekInt32(TByteBuffer.SIZE_OF_INT32, TEventTranslation.INVALID_TRANSLATED_EVENT_ID));
413             break;
414         case TEventEntry.IC_UNIQUE_CLIENT_ID:
415             this.funiqueClientID = aPayload.readInt32();
416             this.fclientHandle = aPayload.readInt32();
417             break;
418         /*
419         case icTimeStamp:
420             // ignore for now, only when using and syncing local time (we trust hub time for now)
421             fbrokerAbsoluteTime = aPayload.ReadInt64();
422             fbrokerTick = aPayload.ReadInt32();
423             fbrokerTickDelta = aPayload.ReadInt32();
424             break;
425         */
426         case TEventEntry.IC_EVENT_NAMES:
427             handleCommandEventNames(aPayload);
428             break;
429         case TEventEntry.IC_END_OF_SESSION:
430             close();
431             break;
432         case TEventEntry.IC_SUBSCRIBE:
433         case TEventEntry.IC_PUBLISH:
434         case TEventEntry.IC_UNSUBSCRIBE:
435         case TEventEntry.IC_UNPUBLISH:
436             handleSubAndPub(aCommand, aPayload);
437             break;
438         default:
439             handleCommandOther(aCommand, aPayload);
440             break;
441         }
442     }
443 
444     private void handleCommandEvent(TByteBuffer aPayload) {
445         int TxEventID = this.feventTranslation.getTranslateEventID(aPayload.readInt32());
446         if (TxEventID != TEventTranslation.INVALID_TRANSLATED_EVENT_ID)
447             eventIDToEventL(TxEventID).handleEvent(aPayload);
448     }
449 
450     private void handleCommandVariable(TByteBuffer aPayload) {
451         if (this.onVariable != null || this.onStatusUpdate != null) {
452             String VarName = aPayload.readString();
453             // check if it is a status update
454             // TODO: model name is prefixed by federation. Is this correct?
455             if (VarName.toUpperCase().endsWith(MODEL_STATUS_VAR_SEP_CHAR + MODEL_Status_VAR_NAME.toUpperCase())) {
456                 VarName = VarName.substring(0, VarName.length() - (MODEL_STATUS_VAR_SEP_CHAR.length() + MODEL_Status_VAR_NAME.length()));
457                 String ModelName = VarName.substring(8, VarName.length());
458                 String ModelUniqueClientID = VarName.substring(0, 8);
459                 aPayload.readInt32();
460                 int Status = aPayload.readInt32(-1);
461                 int Progress = aPayload.readInt32(-1);
462                 if (this.onStatusUpdate != null)
463                     this.onStatusUpdate.dispatch(this, ModelUniqueClientID, ModelName, Progress, Status);
464             } else {
465                 TByteBuffer VarValue = aPayload.readByteBuffer();
466                 TByteBuffer PrevValue = new TByteBuffer();
467                 if (this.onVariable != null)
468                     this.onVariable.dispatch(this, VarName, VarValue.getBuffer(), PrevValue.getBuffer());
469             }
470         }
471     }
472 
473     private void handleCommandEventNames(TByteBuffer aPayload) {
474         if (this.onEventNames != null) {
475             int ec = aPayload.readInt32();
476             TEventNameEntry[] EventNames = new TEventNameEntry[ec];
477             for (int en = 0; en < EventNames.length; en++) {
478                 EventNames[en] = new TEventNameEntry();
479                 EventNames[en].eventName = aPayload.readString();
480                 EventNames[en].publishers = aPayload.readInt32();
481                 EventNames[en].subscribers = aPayload.readInt32();
482                 EventNames[en].timers = aPayload.readInt32();
483             }
484             this.onEventNames.dispatch(this, EventNames);
485         }
486     }
487 
488     private void handleSubAndPub(int aCommand, TByteBuffer aPayload) {
489         String EventName;
490         TEventEntry EE;
491         TEventEntry EP;
492         switch (aCommand) {
493         case TEventEntry.IC_SUBSCRIBE:
494         case TEventEntry.IC_PUBLISH:
495             aPayload.readInt32(); // event id
496             aPayload.readInt32(); //  event entry type
497             EventName = aPayload.readString();
498             EE = findEventL(EventName);
499             if (EE == null)
500             {
501                 // find parent
502                 EP = findEventParentL(EventName);
503                 if (EP != null)
504                 {
505                     EE = addEventL(EventName);
506                     EE.fparent = EP;
507                     EE.copyHandlersFrom(EP);
508                 }
509             }
510             else
511             {
512                 if ((this.onSubAndPub !=null) && !EE.isEmpty())
513                     this.onSubAndPub.dispatch(this, aCommand, EventName);
514                 
515             }
516             if (EE != null)
517                 EE.handleSubAndPub(aCommand);
518             break;
519         case TEventEntry.IC_UNSUBSCRIBE:
520         case TEventEntry.IC_UNPUBLISH:
521             EventName = aPayload.readString();
522             if (this.onSubAndPub !=null)
523                 this.onSubAndPub.dispatch(this, aCommand, EventName);
524             EE = findEventL(EventName);
525             if (EE != null)
526                 EE.handleSubAndPub(aCommand);
527             break;
528         }
529     }
530     
531     protected void handleCommandOther(int aCommand, TByteBuffer aPayload) {
532         // override to implement protocol extensions
533     }
534 
535     private int requestUniqueClientID() {
536         TByteBuffer Payload = new TByteBuffer();
537         Payload.prepare((int) 0);
538         Payload.prepare((int) 0);
539         Payload.prepareApply();
540         Payload.qWrite((int) 0);
541         Payload.qWrite((int) 0);
542         return writeCommand(TEventEntry.IC_UNIQUE_CLIENT_ID, Payload.getBuffer());
543     }
544 
545     private int setOwner() {
546         if (isConnected()) {
547             TByteBuffer Payload = new TByteBuffer();
548             Payload.prepare(this.fownerID);
549             Payload.prepare(this.fownerName);
550             Payload.prepareApply();
551             Payload.qWrite(this.fownerID);
552             Payload.qWrite(this.fownerName);
553             return writeCommand(TEventEntry.IC_SET_CLIENT_INFO, Payload.getBuffer());
554         } else
555             return ICE_CONNECTION_CLOSED;
556     }
557 
558     private void readCommands() {
559         // TODO: more like Delphi code
560         int Command = TEventEntry.IC_INVALID_COMMAND;
561         // define once
562         // magic + command + payload size
563         TByteBuffer FixedCommandPart = new TByteBuffer(MAGIC_BYTES.length + TByteBuffer.SIZE_OF_INT32
564                 + TByteBuffer.SIZE_OF_INT32);
565         TByteBuffer Payload = new TByteBuffer();
566         TByteBuffer PayloadCheck = new TByteBuffer(TByteBuffer.SIZE_OF_INT32);
567         do {
568             try {
569                 try {
570                     Command = readCommand(FixedCommandPart, Payload, PayloadCheck);
571                     if (Command != TEventEntry.IC_INVALID_COMMAND)
572                         handleCommand(Command, Payload);
573                 } catch (ThreadDeath ex) {
574                     Command = TEventEntry.IC_END_OF_SESSION;
575                 }
576             } catch (Exception ex) {
577                 if (isConnected())
578                 {
579                     System.out.println("## Exception in ReadCommands loop: " + ex.getMessage());
580                     ex.printStackTrace();
581                 }
582             }
583         } while ((Command != TEventEntry.IC_END_OF_SESSION) && isConnected());
584     }
585 
586     
587     protected enum TConnectionState {
588         icsUninitialized(0),
589         icsInitialized(1),
590         icsClient(2),
591         icsHub(3),
592         icsEnded(4),
593         // room for extensions ..
594         // gateway values are used over network and should be same over all connected clients/brokers
595         icsGateway(100), // equal
596         icsGatewayClient(101), // this gateway acts as a client; subscribes are not received
597         icsGatewayServer(102); // this gateway treats connected broker as client
598 
599         public final int value;
600 
601         TConnectionState(int aValue) {
602             this.value = aValue;
603         }
604     }
605 
606     protected void setState(TConnectionState aState) {
607         TByteBuffer Payload = new TByteBuffer();
608         Payload.prepare(aState.ordinal());
609         Payload.prepareApply();
610         Payload.qWrite(aState.ordinal());
611         writeCommand(TEventEntry.IC_SET_STATE, Payload.getBuffer());
612     }
613 
614     protected boolean open(String aHost, int aPort) {
615         return open(aHost, aPort, true);
616     }
617 
618     protected boolean open(String aHost, int aPort, boolean aStartReadingThread) {
619         close();
620         try {
621             this.fremoteHost = aHost;
622             this.fremotePort = aPort;
623             this.fsocket = new Socket(this.fremoteHost, this.fremotePort);
624             if (this.fsocket.isConnected()) {
625                 this.foutputStream = this.fsocket.getOutputStream();
626                 this.finputStream = this.fsocket.getInputStream();
627                 // FClient.Connect(FRemoteHost, FRemotePort);
628                 // FNetStream = FClient.GetStream();
629                 if (aStartReadingThread) {
630                     this.freadingThread = new Thread(new Runnable() {
631                         public void run() {
632                             readCommands();
633                         }
634                     });
635                     this.freadingThread.setName("imb command reader");
636                     this.freadingThread.start();
637                 }
638                 if (this.imb2Compatible)
639                     requestUniqueClientID();
640                 // SetState(State.icsClient);
641                 setOwner();
642                 // request all variables if delegates defined
643                 if (this.onVariable != null || this.onStatusUpdate != null)
644                     writeCommand(TEventEntry.IC_ALL_VARIABLES, null);
645             }
646             return this.fsocket.isConnected();
647         } catch (Exception ex) {
648             return false;
649         }
650     }
651 
652     
653     // publics
654     
655     /** returned object (array) on an event name list query */
656     public class TEventNameEntry {
657         public String eventName;
658         public int publishers;
659         public int subscribers;
660         public int timers;
661     }
662 
663     public enum TVarPrefix {
664         vpUniqueClientID,
665         vpClientHandle
666     }
667 
668     // constants
669     /** The maximum size of the payload in a low level IMB command */
670     public static final int MAX_PAYLOAD_SIZE = 10 * 1024 * 1024; // in bytes
671     /** value to be used when no specific remote server is used */
672     public static final String DEFAULT_HUB = "localhost";
673     /** value to be used when no specific port is used */
674     public static final int DEFAULT_PORT = 4000;
675     /** value to be used when no specific federation is used */
676     public static final String DEFAULT_FEDERATION = "TNOdemo";
677     // command results
678     /** command result: the connection is closed */
679     public static final int ICE_CONNECTION_CLOSED = -1;
680     /** command result: the event was not published */
681     public static final int ICE_EVENT_NOT_PUBLISHED = -2;
682 
683     // fields
684     /**Returns the current federation */
685     public String getFederation() {
686         return this.ffederation;
687     }
688 
689     /**Set the current federation. All subscribed and published events are unsubscribed/unpublished, 
690      * then the federation is changed and all previously subscribed/publuished events are re-subscribed/re-published
691      * @param aFederation the new federation
692      */
693     public void setFederation(String aFederation) {
694         String OldFederation = this.ffederation;
695         TEventEntry Event;
696         if (isConnected() && (OldFederation != "")) {
697             // un-publish and un-subscribe all
698             for (int i = 0; i < this.feventEntryList.FCount; i++) {
699                 String EventName = this.feventEntryList.getEventName(i);
700                 if (!EventName.equals("") && EventName.startsWith(OldFederation + ".")) {
701                     Event = this.feventEntryList.getEventEntry(i);
702                     if (Event.isSubscribed())
703                         Event.unSubscribe(false);
704                     if (Event.isPublished())
705                         Event.unPublish(false);
706                 }
707             }
708         }
709         this.ffederation = aFederation;
710         if (isConnected() && (OldFederation != "")) {
711             // publish and subscribe all
712             for (int i = 0; i < this.feventEntryList.FCount; i++) {
713                 String EventName = this.feventEntryList.getEventName(i);
714                 if (!EventName.equals("") && EventName.startsWith(OldFederation + ".")) {
715                     Event = this.feventEntryList.getEventEntry(i);
716                     Event.feventName = this.ffederation + Event.feventName.substring(0, OldFederation.length());
717                     if (Event.isSubscribed())
718                         Event.subscribe();
719                     if (Event.isPublished())
720                         Event.publish();
721                 }
722             }
723         }
724     }
725 
726     /**when true events send on not-publuished events are automatically published */
727     public boolean autoPublish = true;
728     // TODO: check what should be the default, for now backwards compatible?
729     /**when true IMB2 features are used if possible to emulate IMB3 behavior */
730     public boolean imb2Compatible = true;
731 
732     // connection
733     /**Returns the IP address or DNS name of the currently connected hub */
734     public String getRemoteHost() {
735         return this.fremoteHost;
736     }
737 
738     /**Returns the TCP port of the currently connected hub */
739     public int getRemotePort() {
740         return this.fremotePort;
741     }
742 
743     /**Returns the state of the NAGLE algorithm on the connected socket
744      * @return if true NAGLE is disabled (default false)
745      * @throws SocketException
746      */
747     public boolean getNoDelay() throws SocketException {
748         if (isConnected())
749             return this.fsocket.getTcpNoDelay();
750         else
751             return false;
752     }
753 
754     /**Sets the state of the NAGLE algorithm on the socket
755      * @param aValue if true the NAGLE algorithm is DISABLED (default false)
756      * @throws SocketException
757      */
758     public void setNoDelay(boolean aValue) throws SocketException {
759         if (isConnected())
760             this.fsocket.setTcpNoDelay(aValue);
761     }
762 
763     /**Returns the status of the linger option on the connected socket
764      * @return if true the linger option is enabled 
765      * @throws SocketException
766      */
767     public boolean getLinger() throws SocketException {
768         if (isConnected())
769             return this.fsocket.getSoLinger() != -1;
770         else
771             return false;
772     }
773 
774     /**Sets the status of the linger option on the connected socket
775      * @param aValue if true the linger option is enabled with a linger time of 2 seconds
776      * @throws SocketException
777      */
778     public void setLinger(boolean aValue) throws SocketException {
779         if (isConnected())
780             this.fsocket.setSoLinger(aValue, 2); // set linger time to 2 seconds
781     }
782 
783     /** Returns the connected state of the connection */
784     public boolean isConnected() {
785         return (this.fsocket != null) && this.fsocket.isConnected();
786     }
787 
788     /** Closes the connection and cleans up socket, streams and thread */
789     public void close() {
790         if ((this.fsocket != null) && this.fsocket.isConnected()) {
791             if (this.onDisconnect != null)
792                 this.onDisconnect.dispatch(this);
793             writeCommand(TEventEntry.IC_END_OF_SESSION, null);
794             try {
795                 this.foutputStream.close();
796                 this.foutputStream = null;
797                 this.finputStream.close();
798                 this.finputStream = null;
799                 this.fsocket.close();
800                 this.fsocket = null;
801                 this.freadingThread = null;
802             } catch (IOException e) {
803                 // TODO Auto-generated catch block
804                 e.printStackTrace();
805             }
806         }
807     }
808 
809     /** Override dispatch to implement a disconnect handler */
810     public interface TOnDisconnect {
811         public void dispatch(TConnection aConnection);
812     }
813 
814     /** Handler to be called on a disconnect */
815     public TOnDisconnect onDisconnect = null;
816 
817     /**Throttle down buffer events send to this client if specific flags are set on events  
818      * @param aThrottle
819      */
820     public void setThrottle(int aThrottle) {
821         TByteBuffer Payload = new TByteBuffer();
822         Payload.prepare(aThrottle);
823         Payload.prepareApply();
824         Payload.qWrite(aThrottle);
825         writeCommand(TEventEntry.IC_SET_THROTTLE, Payload.getBuffer());
826     }
827 
828     /**Manually reading commands when not using a reader thread.
829      * Commands are read until connection is idle.
830      * @throws IOException
831      */
832     public void readCommandsNonBlocking() throws IOException {
833         if (this.finputStream.available() != 0) {
834             int Command = TEventEntry.IC_INVALID_COMMAND;
835             // define once
836             // magic + command + payload size
837             TByteBuffer FixedCommandPart = new TByteBuffer(MAGIC_BYTES.length + TByteBuffer.SIZE_OF_INT32
838                     + TByteBuffer.SIZE_OF_INT32);
839             TByteBuffer Payload = new TByteBuffer();
840             TByteBuffer PayloadCheck = new TByteBuffer(TByteBuffer.SIZE_OF_INT32);
841             do {
842                 try {
843                     try {
844                         Command = readCommand(FixedCommandPart, Payload, PayloadCheck);
845                         if (Command != TEventEntry.IC_INVALID_COMMAND)
846                             handleCommand(Command, Payload);
847                     } catch (ThreadDeath ex) {
848                         Command = TEventEntry.IC_END_OF_SESSION;
849                     }
850                 } catch (Exception ex) {
851                     if (isConnected())
852                         System.out.println("## Exception in ReadCommands loop: " + ex.getMessage());
853                 }
854             } while ((Command != TEventEntry.IC_END_OF_SESSION) && isConnected() && (this.finputStream.available() != 0));
855         }
856     }
857 
858     /**Manually reading commands when not using a reader thread.
859      * Commands are processed until the reading on the connection times out
860      * @param aTimeOut
861      * @throws SocketException
862      */
863     public void readCommandsNonThreaded(int aTimeOut) throws SocketException {
864         this.fsocket.setSoTimeout(aTimeOut);
865         int Command = TEventEntry.IC_INVALID_COMMAND;
866         // define once
867         // magic + command + payload size
868         TByteBuffer FixedCommandPart = new TByteBuffer(MAGIC_BYTES.length + TByteBuffer.SIZE_OF_INT32
869                 + TByteBuffer.SIZE_OF_INT32);
870         TByteBuffer Payload = new TByteBuffer();
871         TByteBuffer PayloadCheck = new TByteBuffer(TByteBuffer.SIZE_OF_INT32);
872         do {
873             try {
874                 try {
875                     Command = readCommand(FixedCommandPart, Payload, PayloadCheck);
876                     if (Command != TEventEntry.IC_INVALID_COMMAND)
877                         handleCommand(Command, Payload);
878                 } catch (ThreadDeath ex) {
879                     Command = TEventEntry.IC_END_OF_SESSION;
880                 }
881             } catch (Exception ex) {
882                 if (isConnected())
883                     System.out.println("## Exception in ReadCommands loop: " + ex.getMessage());
884             }
885         } while ((Command != TEventEntry.IC_END_OF_SESSION) && isConnected());
886     }
887 
888     // owner
889     /**Returns the currently specified owner id */
890     public int getOwnerID() {
891         return this.fownerID;
892     }
893 
894     /**Changes the owner id
895      * @param aValue the new owner id
896      */
897     public void setOwnerID(int aValue) {
898         if (this.fownerID != aValue) {
899             this.fownerID = aValue;
900             setOwner();
901         }
902     }
903 
904     /**Returns the currently specified owner name */
905     public String getOwnerName() {
906         return this.fownerName;
907     }
908 
909     /**Changes the owner name
910      * @param aValue the new owner name
911      */
912     public void setOwnerName(String aValue) {
913         if (this.fownerName != aValue) {
914             this.fownerName = aValue;
915             setOwner();
916         }
917     }
918 
919     /**Returns the unique client id the hub assigned to this connection */
920     public int getUniqueClientID() {
921         return this.funiqueClientID;
922     }
923     
924     /**Returns the client handle the hub assigned to this connection */
925     public int getClientHandle() {
926         return this.fclientHandle;
927     }
928 
929     // subscribe/publish
930     /**Subscribe to the specified event
931      * @param aEventName the event name to subscribe to (it will be prefixed with the current federation)
932      * @return event entry that is to be used to assign the handler for the received events 
933      */
934     public TEventEntry subscribe(String aEventName) {
935         return subscribe(aEventName, true);
936     }
937 
938     /**Subscribe to the specified event
939      * @param aEventName the event name to subscribe to
940      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
941      * @return event entry that is to be used to assign the handler for the received events
942      */
943     public TEventEntry subscribe(String aEventName, boolean aUseFederationPrefix) {
944         TEventEntry Event = findOrAddEventL(prefixFederation(aEventName, aUseFederationPrefix));
945         if (!Event.isSubscribed())
946             Event.subscribe();
947         return Event;
948     }
949 
950     /**Publishes on the specified event
951      * @param aEventName the event name to publish on (it will be prefixed with the current federation)
952      * @return event entry that is to be used to signal events on
953      */
954     public TEventEntry publish(String aEventName) {
955         return publish(aEventName, true);
956     }
957 
958     /**Publishes on the specified event
959      * @param aEventName the event name to publish on
960      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
961      * @return event entry that is to be used to signal events on
962      */
963     public TEventEntry publish(String aEventName, boolean aUseFederationPrefix) {
964         TEventEntry Event = findOrAddEventL(prefixFederation(aEventName, aUseFederationPrefix));
965         if (!Event.isPublished())
966             Event.publish();
967         return Event;
968     }
969 
970     /**Unsubscribe from the specified event
971      * @param aEventName the event name to unsubscribe from (it will be prefixed with the current federation)
972      */
973     public void unSubscribe(String aEventName) {
974         unSubscribe(aEventName, true);
975     }
976 
977     /**Unsubscribe from the specified event
978      * @param aEventName the event name to unsubscribe from
979      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
980      */
981     public void unSubscribe(String aEventName, boolean aUseFederationPrefix) {
982         TEventEntry Event = findEventL(prefixFederation(aEventName, aUseFederationPrefix));
983         if (Event != null && Event.isSubscribed())
984             Event.unSubscribe(true);
985     }
986 
987     /**Unpublish on the specified event.
988      * @param aEventName the event name to unpublish on (it will be prefixed with the current federation)
989      */
990     public void unPublish(String aEventName) {
991         unPublish(aEventName, true);
992     }
993 
994     /**Unpublish on the specified event.
995      * @param aEventName the event name to unpublish on
996      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
997      */
998     public void unPublish(String aEventName, boolean aUseFederationPrefix) {
999         TEventEntry Event = findEventL(prefixFederation(aEventName, aUseFederationPrefix));
1000         if (Event != null && Event.isPublished())
1001             Event.unPublish(true);
1002     }
1003 
1004     /**Send an event to the framework.
1005      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1006      * @param aEventName
1007      * @param aEventKind
1008      * @param aEventPayload
1009      * @return result of the command (see ICE_* constants)
1010      */
1011     public int signalEvent(String aEventName, int aEventKind, TByteBuffer aEventPayload) {
1012         return signalEvent(aEventName, aEventKind, aEventPayload, true);
1013     }
1014 
1015     /**Send an event to the framework.
1016      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1017      * @param aEventName
1018      * @param aEventKind
1019      * @param aEventPayload
1020      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
1021      * @return result of the command (see ICE_* constants)
1022      */
1023     public int signalEvent(String aEventName, int aEventKind, TByteBuffer aEventPayload, boolean aUseFederationPrefix) {
1024         TEventEntry Event = findEventAutoPublishL(prefixFederation(aEventName, aUseFederationPrefix));
1025         if (Event != null)
1026             return Event.signalEvent(aEventKind, aEventPayload.getBuffer());
1027         else
1028             return ICE_EVENT_NOT_PUBLISHED;
1029     }
1030 
1031     /**Send a buffer event to the framework.
1032      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1033      * @param aEventName
1034      * @param aBufferID
1035      * @param aBuffer
1036      * @return result of the command (see ICE_* constants)
1037      */
1038     public int signalBuffer(String aEventName, int aBufferID, byte[] aBuffer) {
1039         return signalBuffer(aEventName, aBufferID, aBuffer, 0, true);
1040     }
1041 
1042     /**Send a buffer event to the framework.
1043      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1044      * @param aEventName
1045      * @param aBufferID
1046      * @param aBuffer
1047      * @param aEventFlags
1048      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
1049      * @return result of the command (see ICE_* constants)
1050      */
1051     public int signalBuffer(String aEventName, int aBufferID, byte[] aBuffer, int aEventFlags,
1052             boolean aUseFederationPrefix) {
1053         TEventEntry Event = findEventAutoPublishL(prefixFederation(aEventName, aUseFederationPrefix));
1054         if (Event != null)
1055             return Event.signalBuffer(aBufferID, aBuffer, aEventFlags);
1056         else
1057             return ICE_EVENT_NOT_PUBLISHED;
1058     }
1059 
1060     /**Send a ChangeObject event to the framework
1061      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1062      * @param aEventName
1063      * @param aAction
1064      * @param aObjectID
1065      * @param aAttribute
1066      * @return result of the command (see ICE_* constants)
1067      */
1068     public int signalChangeObject(String aEventName, int aAction, int aObjectID, String aAttribute) {
1069         return signalChangeObject(aEventName, aAction, aObjectID, aAttribute, true);
1070     }
1071 
1072     /**Send a ChangeObject event to the framework
1073      * This is the simple way to send events. More performance can be gained by using the returned event entry from publish().
1074      * @param aEventName
1075      * @param aAction
1076      * @param aObjectID
1077      * @param aAttribute
1078      * @param aUseFederationPrefix if true the given event name will be prefixed with the current federation
1079      * @return result of the command (see ICE_* constants)
1080      */
1081     public int signalChangeObject(String aEventName, int aAction, int aObjectID, String aAttribute,
1082             boolean aUseFederationPrefix) {
1083         TEventEntry Event = findEventAutoPublishL(prefixFederation(aEventName, aUseFederationPrefix));
1084         if (Event != null)
1085             return Event.signalChangeObject(aAction, aObjectID, aAttribute);
1086         else
1087             return ICE_EVENT_NOT_PUBLISHED;
1088     }
1089 
1090     /**Send a stream to the framework
1091      * @param aEventName
1092      * @param aStreamName name of the stream to identify the stream by the receiver
1093      * @param aStream
1094      * @return result of the command (see ICE_* constants)
1095      */
1096     public int signalStream(String aEventName, String aStreamName, InputStream aStream) {
1097         return signalStream(aEventName, aStreamName, aStream, true);
1098     }
1099 
1100     /**Send a stream to the framework
1101      * @param aEventName
1102      * @param aStreamName
1103      * @param aStream
1104      * @param aUseFederationPrefix
1105      * @return result of the command (see ICE_* constants)
1106      */
1107     public int signalStream(String aEventName, String aStreamName, InputStream aStream, boolean aUseFederationPrefix) {
1108         TEventEntry Event = findEventAutoPublishL(prefixFederation(aEventName, aUseFederationPrefix));
1109         if (Event != null)
1110             return Event.signalStream(aStreamName, aStream);
1111         else
1112             return ICE_EVENT_NOT_PUBLISHED;
1113     }
1114 
1115     // variables
1116     /** Override dispatch to implement a variable change handler */
1117     public interface TOnVariable {
1118         public void dispatch(TConnection aConnection, String aVarName, byte[] aVarValue, byte[] aPrevValue);
1119     }
1120 
1121     // extra calls when adding a delegate to OnVariable: requestAllVariables();
1122     /** Handler to be called on a variable change */
1123     private TOnVariable onVariable = null;
1124     /**Set the callback handler for framework variable changes
1125      * @param aValue
1126      */
1127     public void setOnVariable(TOnVariable aValue)
1128     {
1129         this.onVariable = aValue;
1130         requestAllVariables();
1131     }
1132 
1133     /** Send a request to the framework to send all variables with their contents to this client */
1134     protected void requestAllVariables() {
1135         writeCommand(TEventEntry.IC_ALL_VARIABLES, null); // request all variables for initial values
1136     }
1137 
1138     /**Set the value of a global framework variable
1139      * @param aVarName
1140      * @param aVarValue
1141      */
1142     public void setVariableValue(String aVarName, String aVarValue) {
1143         TByteBuffer Payload = new TByteBuffer();
1144         Payload.prepare(aVarName);
1145         Payload.prepare(aVarValue);
1146         Payload.prepareApply();
1147         Payload.qWrite(aVarName);
1148         Payload.qWrite(aVarValue);
1149         writeCommand(TEventEntry.IC_SET_VARIABLE, Payload.getBuffer());
1150     }
1151 
1152     /**Set the value of a global framework variable
1153      * @param aVarName
1154      * @param aVarValue
1155      */
1156     public void setVariableValue(String aVarName, TByteBuffer aVarValue) {
1157         TByteBuffer Payload = new TByteBuffer();
1158         Payload.prepare(aVarName);
1159         Payload.prepare(aVarValue);
1160         Payload.prepareApply();
1161         Payload.qWrite(aVarName);
1162         Payload.qWrite(aVarValue);
1163         writeCommand(TEventEntry.IC_SET_VARIABLE, Payload.getBuffer());
1164     }
1165 
1166     /**Set the value of a global framework variable
1167      * @param aVarName
1168      * @param aVarValue
1169      * @param aVarPrefix
1170      */
1171     public void setVariableValue(String aVarName, String aVarValue, TVarPrefix aVarPrefix) {
1172         TByteBuffer Payload = new TByteBuffer();
1173         Payload.prepare(aVarPrefix.ordinal());
1174         Payload.prepare(aVarName);
1175         Payload.prepare(aVarValue);
1176         Payload.prepareApply();
1177         Payload.qWrite(aVarPrefix.ordinal());
1178         Payload.qWrite(aVarName);
1179         Payload.qWrite(aVarValue);
1180         writeCommand(TEventEntry.IC_SET_VARIABLE_PREFIXED, Payload.getBuffer());
1181     }
1182 
1183     /**Set the value of a global framework variable
1184      * @param aVarName
1185      * @param aVarValue
1186      * @param aVarPrefix
1187      */
1188     public void setVariableValue(String aVarName, TByteBuffer aVarValue, TVarPrefix aVarPrefix) {
1189         TByteBuffer Payload = new TByteBuffer();
1190         Payload.prepare(aVarPrefix.ordinal());
1191         Payload.prepare(aVarName);
1192         Payload.prepare(aVarValue);
1193         Payload.prepareApply();
1194         Payload.qWrite(aVarPrefix.ordinal());
1195         Payload.qWrite(aVarName);
1196         Payload.qWrite(aVarValue);
1197         writeCommand(TEventEntry.IC_SET_VARIABLE_PREFIXED, Payload.getBuffer());
1198     }
1199 
1200     /** Override dispatch to implement a status change handler */
1201     public interface TOnStatusUpdate {
1202         public void dispatch(TConnection aConnection, String aModelUniqueClientID, String aModelName, int aProgress, int aStatus);
1203     }
1204 
1205     // TODO: extra calls when adding a delegate to OnVariable: RequestAllVariables();
1206     /** Handler to be called on a status update */
1207     private TOnStatusUpdate onStatusUpdate = null;
1208     /**Set the callback handler for status updates
1209      * @param aValue
1210      */
1211     public void setOnStatusUpdate(TOnStatusUpdate aValue)
1212     {
1213         this.onStatusUpdate = aValue;
1214         requestAllVariables();
1215     }
1216 
1217     // status for UpdateStatus
1218     /** signal client status: ready (see updateStatus) */
1219     public final static int STATUS_READY = 0; // R
1220     /** signal client status: calculating (see updateStatus) */
1221     public final static int STATUS_CALCULATING = 1; // C
1222     /** signal client status: busy (see updateStatus) */
1223     public final static int STATUS_BUSY = 2; // B
1224 
1225     /**Update the central status for this client
1226      * @param aProgress the progress, if available, from 0 to 100 or counting down to 0
1227      * @param aStatus the current status of the client (see STATUS_* constants)
1228      * @throws InterruptedException
1229      */
1230     public void updateStatus(int aProgress, int aStatus) throws InterruptedException {
1231         TByteBuffer Payload = new TByteBuffer();
1232         Payload.prepare(aStatus);
1233         Payload.prepare(aProgress);
1234         Payload.prepareApply();
1235         Payload.qWrite(aStatus);
1236         Payload.qWrite(aProgress);
1237         if (this.imb2Compatible) {
1238             // wait for unique client id
1239             if (this.funiqueClientID == 0) {
1240                 int SpinCount = 10; // 10*500 ms
1241                 while (this.funiqueClientID == 0 && SpinCount > 0) {
1242                     Thread.sleep(500);
1243                     SpinCount--;
1244                 }
1245             }
1246             // set variable using unique client id
1247             setVariableValue(Integer.toHexString(this.funiqueClientID) + prefixFederation(this.fownerName).toUpperCase() + MODEL_STATUS_VAR_SEP_CHAR
1248                     + MODEL_Status_VAR_NAME, Payload);
1249         } else
1250             setVariableValue(prefixFederation(this.fownerName).toUpperCase() + MODEL_STATUS_VAR_SEP_CHAR + MODEL_Status_VAR_NAME, Payload,
1251                     TVarPrefix.vpUniqueClientID);
1252     }
1253 
1254     /** Removes the current status for this client  */
1255     public void removeStatus() {
1256         if (this.imb2Compatible)
1257 
1258             setVariableValue(Integer.toHexString(this.funiqueClientID) + prefixFederation(this.fownerName) + MODEL_STATUS_VAR_SEP_CHAR
1259                     + MODEL_Status_VAR_NAME, "");
1260         else
1261             setVariableValue(prefixFederation(this.fownerName) + MODEL_STATUS_VAR_SEP_CHAR + MODEL_Status_VAR_NAME, "",
1262                     TVarPrefix.vpUniqueClientID);
1263     }
1264 
1265     // TODO: delegates
1266     /**Subscribe to focus events and registers the callback handler for these events.
1267      * @param aOnFocus callback event handler
1268      */
1269     public void subscribeOnFocus(TEventEntry.TOnFocus aOnFocus) {
1270         if (this.ffocusEvent == null)
1271             this.ffocusEvent = subscribe(FOCUS_EVENT_NAME);
1272         this.ffocusEvent.onFocus = aOnFocus;
1273     }
1274 
1275     /**Signal a new focus point to the framework
1276      * @param aX
1277      * @param aY
1278      * @return result of the command (see ICE_* constants)
1279      */
1280     public int signalFocus(double aX, double aY) {
1281         if (this.ffocusEvent == null)
1282             this.ffocusEvent = findEventAutoPublishL(prefixFederation(FOCUS_EVENT_NAME));
1283         if (this.ffocusEvent != null) {
1284             TByteBuffer Payload = new TByteBuffer();
1285             Payload.prepare(aX);
1286             Payload.prepare(aY);
1287             Payload.prepareApply();
1288             Payload.qWrite(aX);
1289             Payload.qWrite(aY);
1290             return this.ffocusEvent.signalEvent(TEventEntry.EK_CHANGE_OBJECT_EVENT, Payload.getBuffer());
1291         } else
1292             return ICE_EVENT_NOT_PUBLISHED;
1293     }
1294 
1295     // IMB 2 change federation
1296     /**Subscribe to federation change events and register the callback handler for these events
1297      * @param aOnChangeFederation
1298      */
1299     public void subscribeOnFederationChange(TEventEntry.TOnChangeFederation aOnChangeFederation) {
1300         if (this.fchangeFederationEvent == null)
1301             this.fchangeFederationEvent = subscribe(CHANGE_FEDERATION_EVENT_NAME);
1302         this.fchangeFederationEvent.onChangeFederation = aOnChangeFederation;
1303     }
1304 
1305     /**Signal a new federation to the framework
1306      * @param aNewFederationID
1307      * @param aNewFederation
1308      * @return result of the command (see ICE_* constants)
1309      */
1310     public int signalChangeFederation(int aNewFederationID, String aNewFederation) {
1311         if (this.fchangeFederationEvent == null)
1312             this.fchangeFederationEvent = findEventAutoPublishL(prefixFederation(CHANGE_FEDERATION_EVENT_NAME));
1313         if (this.fchangeFederationEvent != null)
1314             return this.fchangeFederationEvent.signalChangeObject(TEventEntry.ACTION_CHANGE, aNewFederationID, aNewFederation);
1315         else
1316             return ICE_EVENT_NOT_PUBLISHED;
1317     }
1318 
1319     // log
1320     /**Log an entry to the framework
1321      * @param aLogEventName
1322      * @param aLine
1323      * @param aLevel
1324      * @return result of the command (see ICE_* constants)
1325      */
1326     public int logWriteLn(String aLogEventName, String aLine, TEventEntry.TLogLevel aLevel) {
1327         if (this.flogEvent == null)
1328             this.flogEvent = findEventAutoPublishL(prefixFederation(aLogEventName));
1329         if (this.flogEvent != null)
1330             return this.flogEvent.logWriteLn(aLine, aLevel);
1331         else
1332             return ICE_EVENT_NOT_PUBLISHED;
1333     }
1334 
1335     // remote event info
1336 
1337     // TODO: delegates
1338     /** Override dispatch to implement an event names request callback handler */
1339     public interface TOnEventnames {
1340         public void dispatch(TConnection aConnection, TEventNameEntry[] aEventNames);
1341     }
1342 
1343     /** Handler to be called on a event names request callback */
1344     public TOnEventnames onEventNames = null;
1345 
1346     // TODO: description
1347     public interface TOnSubAndPub {
1348         public void dispatch(TConnection aConnection, int aCommand, String aEventName);
1349     }
1350 
1351     // TODO: description
1352     public TOnSubAndPub onSubAndPub = null;
1353     
1354     // event filters
1355     /** request event name filter: requesting publisher counts */
1356     public static final int EF_PUBLISHERS = 1;
1357     /** request event name filter: requesting subscriber counts */
1358     public static final int EF_SUBSCRIBERS = 2;
1359     /** request event name filter: requesting timer counts */
1360     public static final int EF_TIMERS = 4;
1361 
1362     /**Query the framework for registered event names
1363      * @param aEventNameFilter
1364      * @param aEventFilters
1365      * @return result of the command (see ICE_* constants)
1366      */
1367     public int requestEventname(String aEventNameFilter, int aEventFilters) {
1368         TByteBuffer Payload = new TByteBuffer();
1369         Payload.prepare(aEventNameFilter);
1370         Payload.prepare(aEventFilters);
1371         Payload.prepareApply();
1372         Payload.qWrite(aEventNameFilter);
1373         Payload.qWrite(aEventFilters);
1374         return writeCommand(TEventEntry.IC_REQUEST_EVENT_NAMES, Payload.getBuffer());
1375     }
1376 
1377     /** {@inheritDoc} */
1378     @Override
1379     public final String toString()
1380     {
1381         return "TConnection [remoteHost=" + this.fremoteHost + ", remotePort=" + this.fremotePort + ", federation="
1382                 + this.ffederation + ", isConnected()=" + this.isConnected() + "]";
1383     }
1384 }