1 // xmlBlaster/demo/javaclients/HelloWorldSubscribe.java
  2 package javaclients;
  3 
  4 import java.util.logging.Logger;
  5 import java.util.logging.Level;
  6 
  7 import org.xmlBlaster.util.FileLocator;
  8 import org.xmlBlaster.util.Global;
  9 import org.xmlBlaster.util.XmlBlasterException;
 10 import org.xmlBlaster.util.def.Constants;
 11 import org.xmlBlaster.util.def.ErrorCode;
 12 import org.xmlBlaster.util.property.Args;
 13 import org.xmlBlaster.util.qos.HistoryQos;
 14 import org.xmlBlaster.util.qos.ClientProperty;
 15 import org.xmlBlaster.client.qos.ConnectQos;
 16 import org.xmlBlaster.client.qos.ConnectReturnQos;
 17 import org.xmlBlaster.client.qos.DisconnectQos;
 18 import org.xmlBlaster.client.I_Callback;
 19 import org.xmlBlaster.client.key.UpdateKey;
 20 import org.xmlBlaster.client.key.SubscribeKey;
 21 import org.xmlBlaster.client.key.UnSubscribeKey;
 22 import org.xmlBlaster.client.qos.UpdateQos;
 23 import org.xmlBlaster.client.qos.SubscribeQos;
 24 import org.xmlBlaster.client.qos.SubscribeReturnQos;
 25 import org.xmlBlaster.client.qos.UnSubscribeQos;
 26 import org.xmlBlaster.client.qos.UnSubscribeReturnQos;
 27 import org.xmlBlaster.util.qos.AccessFilterQos;
 28 import org.xmlBlaster.client.I_XmlBlasterAccess;
 29 import org.xmlBlaster.client.I_ConnectionStateListener;
 30 import org.xmlBlaster.util.dispatch.ConnectionStateEnum;
 31 import java.util.Map;
 32 import java.util.Iterator;
 33 import java.io.File;
 34 import java.text.DateFormat;
 35 import java.text.SimpleDateFormat;
 36 import java.util.TimeZone;
 37 import java.util.Date;
 38 
 39 
 40 /**
 41  * This client connects to xmlBlaster and subscribes to messages. 
 42  * <p>
 43  * This is a nice client to experiment and play with xmlBlaster as there are many
 44  * command line options to specify the type and amount of messages published.
 45  * </p>
 46  * <p>
 47  * Try using 'java javaclients.HelloWorldPublish' in another window to publish some
 48  * messages.
 49  * Further you can type 'd' in the window running xmlBlaster to get a server dump.
 50  * </p>
 51  *
 52  * Invoke (after starting the xmlBlaster server):
 53  * <pre>
 54  * java javaclients.HelloWorldSubscribe -xpath //key -initialUpdate true -unSubscribe true
 55  *
 56  * java javaclients.HelloWorldSubscribe -interactive false -oid Hello -initialUpdate true -unSubscribe true
 57  *
 58  * java javaclients.HelloWorldSubscribe -session.name joeSubscriber/5 -passwd secret -initialUpdate true -dump[HelloWorldSubscribe] true
 59  *
 60  * java javaclients.HelloWorldSubscribe -xpath //key -filter.type GnuRegexFilter -filter.query "^__sys__jdbc.*"
 61  *
 62  * java javaclients.HelloWorldSubscribe -xpath //key -filter.type XPathFilter -filter.query "//tomato"
 63  *
 64  * java javaclients.HelloWorldSubscribe -xpath //key -filter.type ContentLenFilter -filter.query "10"
 65  * </pre>
 66  * <p>
 67  * If unSubscribe=false the message is not unsubscribed at the end, if disconnect=false we don't logout at the end.
 68  * </p>
 69  * @see java javaclients.HelloWorldPublish
 70  * @see java javaclients.HelloWorldGet
 71  * @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/interface.html" target="others">xmlBlaster interface</a>
 72  */
 73 public class HelloWorldSubscribe implements I_Callback
 74 {
 75    
 76    public class HelloThread extends Thread {
 77       
 78       private Global global;
 79       
 80       HelloThread(Global global) {
 81          super();
 82          this.global = global;
 83       }
 84       
 85       public void run() {
 86          process(this.global);
 87       }
 88    }
 89    
 90    private final String ME = "HelloWorldSubscribe";
 91    private Global glob;
 92    private static Logger log = Logger.getLogger(HelloWorldSubscribe.class.getName());
 93    private I_XmlBlasterAccess con;
 94    private SubscribeReturnQos srq;
 95    private String subscribeServerId;
 96    private int updateCounter;
 97    private boolean connectPersistent;
 98    private boolean firstConnect=true;
 99    private boolean interactive;
100    private boolean autoSubscribe;
101    private boolean interactiveUpdate;
102    private long updateSleep;
103    private String updateExceptionErrorCode;
104    private String updateExceptionMessage;
105    private String updateExceptionRuntime;
106    private boolean shutdownCbServer;
107    private String oid;
108    private String domain;
109    private String xpath;
110    private boolean multiSubscribe;
111    private boolean persistentSubscribe;
112    private boolean notifyOnErase;
113    private boolean local;
114    private boolean initialUpdate;
115    private boolean updateOneway;
116    private boolean wantContent;
117    private boolean dumpToFile;
118    // only IF dumpToFile==true:
119    private String fileExtension;
120    private String filePrefix;
121    private String fileDateFormat;
122    private volatile DateFormat formatter;
123    private String fileLock;
124    private String fileHeader;
125    private int historyNumUpdates;
126    private boolean historyNewestFirst;
127    private String filterType;
128    private String filterVersion;
129    private String filterQuery;
130    private boolean unSubscribe;
131    private int maxContentLength;
132    private boolean connectRefreshSession;
133    private boolean runAsDaemon;
134    private boolean dumpToConsole;
135    private Map clientPropertyMap;
136    private Map connectQosClientPropertyMap;
137 
138    public HelloWorldSubscribe() {
139       
140    }
141 
142    public HelloWorldSubscribe(Global global_) {
143       process(global_);
144    }
145    
146    private void readEnv() {
147       this.connectPersistent = glob.getProperty().get("connect/qos/persistent", false);
148       this.interactive = glob.getProperty().get("interactive", true);
149       this.autoSubscribe = glob.getProperty().get("autoSubscribe", false);
150       this.interactiveUpdate = glob.getProperty().get("interactiveUpdate", false);
151       this.updateSleep = glob.getProperty().get("updateSleep", 0L);
152       this.updateExceptionErrorCode = glob.getProperty().get("updateException.errorCode", (String)null);
153       this.updateExceptionMessage = glob.getProperty().get("updateException.message", (String)null);
154       this.updateExceptionRuntime = glob.getProperty().get("updateException.runtime", (String)null);
155       this.shutdownCbServer = glob.getProperty().get("shutdownCbServer", false);
156       this.oid = glob.getProperty().get("oid", "");
157       this.domain = glob.getProperty().get("domain", "");
158       this.xpath = glob.getProperty().get("xpath", "");
159       this.multiSubscribe = glob.getProperty().get("multiSubscribe", true);
160       this.persistentSubscribe = glob.getProperty().get("persistentSubscribe", false);
161       this.notifyOnErase = glob.getProperty().get("notifyOnErase", true);
162       this.local = glob.getProperty().get("local", true);
163       this.initialUpdate = glob.getProperty().get("initialUpdate", true);
164       this.updateOneway = glob.getProperty().get("updateOneway", false);
165       this.wantContent = glob.getProperty().get("wantContent", true);
166       this.dumpToFile = glob.getProperty().get("dumpToFile", false);
167       // only IF dumpToFile==true:
168       this.fileExtension = glob.getProperty().get("fileExtension", ""); // for example ".jpg"
169       this.filePrefix = glob.getProperty().get("filePrefix", "");       // Fixed file name instead of topic as file name
170       this.fileDateFormat = glob.getProperty().get("fileDateFormat", "yyyy-MM-dd'T'HHmmss.S"); // How to format the date of the file name (ISO 8601)
171       this.fileLock = glob.getProperty().get("fileLock", "");           // add extension for lock file during Fixed file name instead of topic as file name, ".lck"
172       this.fileHeader = glob.getProperty().get("fileHeader", "");       // add a header text to the file, e.g. "<?xml version='1.0' encoding='UTF-8' ?>\n"
173       this.historyNumUpdates = glob.getProperty().get("historyNumUpdates", 1);
174       this.historyNewestFirst = glob.getProperty().get("historyNewestFirst", true);
175       this.filterType = glob.getProperty().get("filter.type", "GnuRegexFilter");// XPathFilter | ContentLenFilter
176       this.filterVersion = glob.getProperty().get("filter.version", "1.0");
177       this.filterQuery = glob.getProperty().get("filter.query", "");
178       this.unSubscribe = glob.getProperty().get("unSubscribe", true);
179       this.maxContentLength = glob.getProperty().get("maxContentLength", 250);
180       this.connectRefreshSession = glob.getProperty().get("connect/qos/sessionRefresh", false);
181       this.runAsDaemon = glob.getProperty().get("runAsDaemon", false);
182       this.dumpToConsole = glob.getProperty().get("dumpToConsole", true);
183       this.clientPropertyMap = glob.getProperty().get("clientProperty", (Map)null);
184       this.connectQosClientPropertyMap = glob.getProperty().get("connect/qos/clientProperty", (Map)null);
185       int numClients = glob.getProperty().get("numClients", 1);
186    }
187    
188    public void processAsync(Global glob_) {
189       log.info("Starting one thread");
190       HelloThread th = new HelloThread(glob_);
191       th.start();
192    }
193    
194    public void process(Global glob_) {
195       this.glob = glob_;
196 
197       boolean disconnect = glob.getProperty().get("disconnect", true);
198       try {
199          readEnv();
200 
201          if (oid.length() < 1 && xpath.length() < 1) {
202             log.warning("No -oid or -xpath given, we subscribe to oid='Hello'.");
203             oid = "Hello";
204          }
205 
206          if (this.updateSleep > 0L && interactiveUpdate == true) {
207             log.warning("You can't set 'updateSleep' and  'interactiveUpdate' simultaneous, we reset interactiveUpdate to false");
208             this.interactiveUpdate = false;
209          }
210 
211          if (this.updateExceptionErrorCode != null && this.updateExceptionRuntime != null) {
212             log.warning("You can't throw a runtime and an XmlBlasterException simultaneous, please check your settings " +
213                           " -updateException.errorCode and -updateException.runtime");
214             this.updateExceptionRuntime = null;
215          }
216 
217          log.info("Used settings are:");
218          log.info("   -connect/qos/persistent     " + connectPersistent);
219          log.info("   -connect/qos/sessionRefresh " + connectRefreshSession);
220          if (connectQosClientPropertyMap != null) {
221             Iterator it = connectQosClientPropertyMap.keySet().iterator();
222             while (it.hasNext()) {
223                String key = (String)it.next();
224                log.info("   -connect/qos/clientProperty["+key+"]   " + connectQosClientPropertyMap.get(key).toString());
225             }
226          }
227          else {
228             log.info("   -connect/qos/clientProperty[]   ");
229          }
230          log.info("   -interactive       " + interactive);
231          log.info("   -autoSubscribe     " + autoSubscribe);
232          log.info("   -interactiveUpdate " + this.interactiveUpdate);
233          log.info("   -updateSleep       " + this.updateSleep);
234          log.info("   -updateException.errorCode " + this.updateExceptionErrorCode);
235          log.info("   -updateException.message   " + this.updateExceptionMessage);
236          log.info("   -updateException.runtime   " + this.updateExceptionRuntime);
237          log.info("   -shutdownCbServer          " + shutdownCbServer);
238          log.info("   -oid               " + oid);
239          log.info("   -domain            " + domain);
240          log.info("   -xpath             " + xpath);
241          log.info("   -multiSubscribe    " + multiSubscribe);
242          log.info("   -persistentSubscribe " + persistentSubscribe);
243          log.info("   -notifyOnErase     " + notifyOnErase);
244          log.info("   -local             " + local);
245          log.info("   -initialUpdate     " + initialUpdate);
246          log.info("   -updateOneway      " + updateOneway);
247          log.info("   -historyNumUpdates " + historyNumUpdates);
248          log.info("   -historyNewestFirst " + historyNewestFirst);
249          log.info("   -wantContent       " + wantContent);
250          log.info("   -dumpToFile        " + dumpToFile);
251          log.info("   -fileExtension     " + fileExtension);
252          log.info("   -unSubscribe       " + unSubscribe);
253          log.info("   -disconnect        " + disconnect); // false: leaveServer
254          log.info("   -filter.type       " + filterType);
255          log.info("   -filter.version    " + filterVersion);
256          log.info("   -filter.query      " + filterQuery);
257          if (this.clientPropertyMap != null) {
258             Iterator it = this.clientPropertyMap.keySet().iterator();
259             while (it.hasNext()) {
260                String key = (String)it.next();
261                log.info("   -clientProperty["+key+"]   " + this.clientPropertyMap.get(key).toString());
262             }
263          }
264          else {
265             log.info("   -clientProperty[]   ");
266          }
267          
268          log.info("For more info please read:");
269          log.info("   http://www.xmlBlaster.org/xmlBlaster/doc/requirements/interface.subscribe.html");
270 
271          con = glob.getXmlBlasterAccess();
272 
273          // Do fail safe handling:
274          con.registerConnectionListener(new I_ConnectionStateListener() {
275             public void reachedAlive(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
276                if (connection.getConnectReturnQos().isReconnected())
277                   log.info("I_ConnectionStateListener.reachedAlive(): Same server instance found");
278                else
279                   log.info("I_ConnectionStateListener.reachedAlive(): New server instance found, connected to " +
280                         connection.getConnectReturnQos().getSessionName());
281 
282                if (connection.getQueue().getNumOfEntries() > 0) {
283                   log.info("I_ConnectionStateListener.reachedAlive(): Queue contains " +
284                            connection.getQueue().getNumOfEntries() + " messages: " +
285                            connection.getQueue().toXml(""));
286                   // connection.getQueue().clear(); -> Would destroy ConnectQos if new connected
287                }
288 
289                String id = connection.getConnectReturnQos().getSecretSessionId() + connection.getConnectReturnQos().getServerInstanceId();
290 
291                if (!firstConnect && (subscribeServerId == null ||
292                    !subscribeServerId.equals(id) && !persistentSubscribe)) {
293                   subscribe(); // We lost the old subscription, initialize subscription again
294                }
295             }
296             public void reachedPolling(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
297                log.warning("I_ConnectionStateListener.reachedPolling(): No connection to " + glob.getId() + ", we are polling ...");
298             }
299             public void reachedDead(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
300                log.severe("I_ConnectionStateListener.reachedDead(): Connection to " + glob.getId() + " is dead, good bye");
301                System.exit(1);
302             }
303             public void reachedAliveSync(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
304             }
305 
306          });
307 
308          // ConnectQos checks -session.name and -passwd from command line
309          ConnectQos qos = new ConnectQos(glob);
310          qos.setPersistent(connectPersistent);
311          qos.setRefreshSession(connectRefreshSession);
312          if (connectQosClientPropertyMap != null) {
313             Iterator it = connectQosClientPropertyMap.keySet().iterator();
314             while (it.hasNext()) {
315                String key = (String)it.next();
316                qos.addClientProperty(key, connectQosClientPropertyMap.get(key).toString());
317             }
318          }
319          log.info("ConnectQos is " + qos.toXml());
320          ConnectReturnQos crq = con.connect(qos, this);  // Login to xmlBlaster, register for updates
321          // crq can be null if '-dispatch/connection/doSendConnect false' is set
322          log.info("Connect success as " + ((crq==null)?" faked connect":crq.toXml()));
323 
324          subscribe(); // first time
325 
326          if (shutdownCbServer) {
327             Global.waitOnKeyboardHit("Hit a key to shutdown callback server");
328             con.getCbServer().shutdown();
329             log.info("Callback server halted, no update should arrive ...");
330             /*
331             for (int ii=0; ii<4; ii++) {
332                Global.waitOnKeyboardHit("Hit a key to publish " + ii + "/4 ...");
333                org.xmlBlaster.util.MsgUnit msgUnit = new org.xmlBlaster.util.MsgUnit("<key oid='FromSubscriber'/>", (new String("BLA")).getBytes(), "<qos/>");
334                con.publish(msgUnit);
335                log.info("Published message");
336             }
337             */
338          }
339          else {
340             log.info("Waiting on update ...");
341          }
342 
343          if (interactiveUpdate) {
344             try { Thread.sleep(1000000000); } catch( InterruptedException i) {}
345          }
346 
347          char ret = 0;
348          if (unSubscribe && srq!=null) {
349             if (interactive) {
350                while (ret != 'q' && ret != 'u')
351                   ret = (char)Global.waitOnKeyboardHit("Hit 'u' to unSubscribe, 'q' to quit");
352             }
353 
354             if (ret == 0 || ret == 'u') {
355                UnSubscribeKey uk = new UnSubscribeKey(glob, srq.getSubscriptionId());
356                if (domain.length() > 0)  // cluster routing information
357                   uk.setDomain(domain);
358                UnSubscribeQos uq = new UnSubscribeQos(glob);
359                log.info("UnSubscribeKey=\n" + uk.toXml());
360                log.info("UnSubscribeQos=\n" + uq.toXml());
361                UnSubscribeReturnQos[] urqArr = con.unSubscribe(uk, uq);
362                log.info("UnSubscribe on " + urqArr.length + " subscriptions done");
363             }
364          }
365 
366          if (runAsDaemon) {
367             while (true) {
368                try {
369                   Thread.sleep(1000000000L);
370                }
371                catch (Exception e) {}
372             }
373          }
374          else {
375             if (ret != 'q')
376                Global.waitOnKeyboardHit("Hit a key to exit");
377          }
378       }
379       catch (XmlBlasterException e) {
380          log.severe(e.getMessage());
381       }
382       catch (Exception e) {
383          e.printStackTrace();
384          log.severe(e.toString());
385       }
386       finally {
387          if (con != null) {
388             if (disconnect) {
389                DisconnectQos dq = new DisconnectQos(glob);
390                con.disconnect(dq);
391                log.info("Disconnected, the server session is destroyed, bye");
392             }
393             else {
394                con.leaveServer(null);
395                log.info("Left server, our server side session remains, bye");
396             }
397          }
398       }
399    }
400 
401    /**
402     * Does the xmlBlaster subscribe. 
403     */
404    private void subscribe() {
405       try {
406          SubscribeKey sk = null;
407          String qStr = null;
408          if (oid.length() > 0) {
409             sk = new SubscribeKey(glob, oid);
410             qStr = oid;
411          }
412          else if (xpath.length() > 0) {
413             sk = new SubscribeKey(glob, xpath, Constants.XPATH);
414             qStr = xpath;
415          }
416          if (domain.length() > 0) {  // cluster routing information
417             if (sk == null) sk = new SubscribeKey(glob, "", Constants.DOMAIN); // usually never
418             sk.setDomain(domain);
419             qStr = domain;
420          }
421          SubscribeQos sq = new SubscribeQos(glob);
422          sq.setWantInitialUpdate(initialUpdate);
423          sq.setWantUpdateOneway(updateOneway);
424          sq.setMultiSubscribe(multiSubscribe);
425          sq.setPersistent(persistentSubscribe);
426          sq.setWantNotify(notifyOnErase);
427          sq.setWantLocal(local);
428          sq.setWantContent(wantContent);
429          
430          HistoryQos historyQos = new HistoryQos(glob);
431          historyQos.setNumEntries(historyNumUpdates);
432          historyQos.setNewestFirst(historyNewestFirst);
433          sq.setHistoryQos(historyQos);
434 
435          if (filterQuery.length() > 0) {
436             AccessFilterQos filter = new AccessFilterQos(glob, filterType, filterVersion, filterQuery);
437             sq.addAccessFilter(filter);
438          }
439          if (clientPropertyMap != null) {
440             Iterator it = clientPropertyMap.keySet().iterator();
441             while (it.hasNext()) {
442                String key = (String)it.next();
443                sq.addClientProperty(key, clientPropertyMap.get(key).toString());
444             }
445          }
446 
447          log.info("SubscribeKey=\n" + sk.toXml());
448          log.info("SubscribeQos=\n" + sq.toXml());
449 
450          if (firstConnect && (interactive && !autoSubscribe)) {
451             Global.waitOnKeyboardHit("Hit a key to subscribe '" + qStr + "'");
452          }
453          firstConnect = false;
454 
455          this.srq = con.subscribe(sk, sq);
456 
457          subscribeServerId = con.getConnectReturnQos().getSecretSessionId() + con.getConnectReturnQos().getServerInstanceId();
458 
459          log.info("Subscribed on topic '" + ((oid.length() > 0) ? oid : xpath) +
460                         "', got subscription id='" + this.srq.getSubscriptionId() + "'\n" + this.srq.toXml());
461          if (log.isLoggable(Level.FINEST)) log.finest("Subscribed: " + sk.toXml() + sq.toXml() + srq.toXml());
462       }
463       catch (XmlBlasterException e) {
464          log.severe(e.getMessage());
465       }
466    }
467 
468    /**
469     * Here the messages from xmlBlaster arrive. 
470     */
471    public String update(String cbSessionId, UpdateKey updateKey, byte[] content,
472                         UpdateQos updateQos) throws XmlBlasterException {
473       if (updateQos.isErased() && oid.length() > 0) { // Erased topic with EXACT subscription?
474          if (dumpToConsole) {
475             System.out.println("============= Topic '" + updateKey.getOid() + "' is ERASED =======================");
476             System.out.println(updateKey.toXml());
477          }
478          subscribe();             // topic is erased -> re-subsribe
479          return Constants.RET_OK; // "<qos><state id='OK'/></qos>";
480       }
481       ++updateCounter;
482       if (dumpToConsole) {
483          System.out.println("");
484          System.out.println("============= START #" + updateCounter + " '" + updateKey.getOid() + "' =======================");
485          log.info("Receiving update #" + updateCounter + " of a message ...");
486          System.out.println("<xmlBlaster>");
487          System.out.println(updateKey.toXml());
488          System.out.println("");
489          System.out.println("<content size='"+content.length+"'>");
490          if (maxContentLength < 0 || content.length < maxContentLength) {
491             System.out.println(new String(content));
492          }
493          else {
494             String str = new String(content, 0,maxContentLength-5);
495             System.out.println(str + " ...");
496          }
497          System.out.println("</content>");
498          System.out.println(updateQos.toXml());
499          System.out.println("</xmlBlaster>");
500       }
501 
502       if (dumpToFile) {
503          String pre = (this.filePrefix.length() > 0) ? this.filePrefix : (updateKey.getOid() + "-");
504          String time = formatDate(updateQos.getRcvTimestamp().getMillis()); // 2005-06-15T052536
505          String fileName = pre + time;
506          if (fileExtension != null && fileExtension.length() > 0) {
507             fileName += fileExtension;
508          }
509          String lckFile = "";
510          if (this.fileLock.length() > 0) {
511             lckFile = fileName + this.fileLock;
512          }
513          try {
514             if (lckFile.length() > 0) {
515                FileLocator.writeFile(lckFile, "Writing " + fileName + " ...");
516             }
517             //byte[] tmp = "<?xml version='1.0' encoding='UTF-8' ?>\n".getBytes() + content;
518             byte[] tmp = content;
519             if (this.fileHeader.length() > 0) {
520                byte[] fh = this.fileHeader.getBytes();
521                tmp = new byte[fh.length+content.length];
522                System.arraycopy(fh, 0, tmp, 0, fh.length);
523                System.arraycopy(content, 0, tmp, fh.length, content.length);
524             }
525 
526             FileLocator.writeFile(fileName, tmp);
527             log.info("Dumped content #" + updateCounter + " of topic '" + updateKey.getOid() + "' to file '" + fileName + "'");
528          }
529          catch (XmlBlasterException e) {
530             log.severe("Can't dump content to file '" + fileName + "': " + e.toString());
531          }
532          finally {
533             try {
534                if (lckFile.length() > 0) {
535                   File f = new File(lckFile);
536                   if (f.exists())
537                      f.delete();
538                }
539             }
540             catch (Exception e) {
541                log.severe("Can't remove lock file '" + lckFile + "': " + e.toString());
542             }
543          }
544       }
545 
546       // If clientProperty is base64 encoded we print the real value as well:
547       Map map = updateQos.getClientProperties();
548       Iterator it = map.values().iterator();
549       while (it.hasNext()) {
550          ClientProperty clientProperty = (ClientProperty)it.next();
551          if (clientProperty.isBase64()) {
552             if (dumpToConsole) {
553                System.out.println("\nClientProperty decoded: " + clientProperty.getName() + "='" + clientProperty.getStringValue() + "'");
554             }
555          }
556       }
557 
558       if (dumpToConsole) {
559          System.out.println("============= END #" + updateCounter + " '" + updateKey.getOid() + "' =========================");
560          System.out.println("");
561       }
562 
563       if (this.updateSleep > 0L) {
564          log.info("Sleeping for " + this.updateSleep + " millis ...");
565          try { Thread.sleep(this.updateSleep); } catch( InterruptedException i) {}
566          log.info("Waking up.");
567       } else if (this.interactiveUpdate) {
568          Global.waitOnKeyboardHit("Hit a key to return from update() (we are blocking the server callback) ...");
569          log.info("Returning update() - control goes back to server");
570       }
571 
572       if (this.updateExceptionErrorCode != null) {
573          log.info("Throwing XmlBlasterException with errorCode='" + this.updateExceptionErrorCode + "' back to server ...");
574          ErrorCode errorCode;
575          try {
576             errorCode = ErrorCode.toErrorCode(this.updateExceptionErrorCode);
577          }
578          catch (IllegalArgumentException e) {
579             log.severe("Please supply a valid exception errorCode (see ErrorCode.java) for instead of -updateException.errorCode " + this.updateExceptionErrorCode + "");
580             return Constants.RET_OK; // "<qos><state id='OK'/></qos>";
581          }
582          throw new XmlBlasterException(updateKey.getGlobal(), errorCode, ME, this.updateExceptionMessage); 
583       }
584 
585       if (this.updateExceptionRuntime != null) {
586          log.info("Throwing RuntimeException '" + this.updateExceptionRuntime + "'");
587          throw new RuntimeException(this.updateExceptionRuntime);
588       }
589 
590       return Constants.RET_OK; // "<qos><state id='OK'/></qos>";
591    }
592 
593    /**
594     * Convert the long milli second time to a readable format as given with fileDateFormat. 
595     * "yyyy-MM-dd'T'HHmmss" or with milli seconds "yyyy-MM-dd'T'HHmmss.S"
596     * @return Defaults to "2005-06-19T152029.344" (ISO 8601)
597     */
598    private String formatDate(long timestamp) {
599       if (this.fileDateFormat == null || this.fileDateFormat.length() == 0) {
600          return "";
601       }
602       if (this.fileDateFormat.equals("long")) {
603          return ""+timestamp;
604       }
605       if (this.formatter == null) { // cache
606          synchronized (this) {
607             if (this.formatter == null) {
608                this.formatter = new SimpleDateFormat(this.fileDateFormat); //, Locale.US);
609                this.formatter.setTimeZone(TimeZone.getDefault()); // from "user.timezone"
610             }
611          }
612       }
613       return this.formatter.format(new Date(timestamp));
614    }
615 
616    /**
617     * Try
618     * <pre>
619     *   java javaclients.HelloWorldSubscribe -help
620     * </pre>
621     * for usage help
622     */
623    public static void main(String args[]) {
624       Global glob = new Global();
625       
626       if (glob.init(args) != 0) { // Get help with -help
627          System.out.println(glob.usage());
628          System.err.println("\nExample:");
629          System.err.println("  java javaclients.HelloWorldSubscribe -oid Hello -initialUpdate true\n");
630          System.exit(1);
631       }
632       try {
633          // check if the numClients parameter is set:
634          
635          int numClients = Args.getArg(args, "-numClients", 1);
636          log.info("Number of clients to instantiate " + numClients);
637          if (numClients > 1) {
638             for (int i=0; i < numClients; i++) {
639                glob = new Global();
640                glob.init(args);
641                HelloWorldSubscribe demo = new HelloWorldSubscribe();
642                demo.processAsync(glob);
643             }
644          }
645          else {
646             HelloWorldSubscribe demo = new HelloWorldSubscribe();
647             demo.process(glob);
648          }
649       }
650       catch (XmlBlasterException ex) {
651          ex.printStackTrace();
652       }
653    }
654 }


syntax highlighted by Code2HTML, v. 0.9.1