1 /*------------------------------------------------------------------------------
  2 Name:      GraphicChat.java
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 ------------------------------------------------------------------------------*/
  6 
  7 package javaclients.graphical;
  8 
  9 import CH.ifa.draw.framework.FigureChangeEvent;
 10 import CH.ifa.draw.framework.Figure;
 11 import CH.ifa.draw.standard.StandardDrawing;
 12 
 13 import java.util.logging.Logger;
 14 import java.util.logging.Level;
 15 import org.xmlBlaster.util.def.Constants;
 16 import org.xmlBlaster.util.Global;
 17 import org.xmlBlaster.util.Timestamp;
 18 import org.xmlBlaster.util.XmlBlasterException;
 19 import org.xmlBlaster.util.I_Timeout;
 20 import org.xmlBlaster.util.Timeout;
 21 import org.xmlBlaster.client.I_XmlBlasterAccess;
 22 import org.xmlBlaster.client.qos.*;
 23 import org.xmlBlaster.client.key.*;
 24 import org.xmlBlaster.util.qos.HistoryQos;
 25 import org.xmlBlaster.util.MsgUnit;
 26 
 27 import org.xmlBlaster.client.I_Callback;
 28 
 29 import java.util.HashMap;
 30 import java.util.HashSet;
 31 import java.util.Iterator;
 32 import java.io.IOException;
 33 import CH.ifa.draw.framework.Drawing;
 34 
 35 
 36 /**
 37  * @author <a href="mailto:michele@laghi.eu">Michele Laghi</a>
 38  */
 39 public  class XmlBlasterDrawing extends StandardDrawing implements I_Timeout, I_Callback {
 40 
 41    private static final String ME = "XmlBlasterDrawing";
 42    private Global global;
 43    private static Logger log = Logger.getLogger(XmlBlasterDrawing.class.getName());
 44    /** inhibit events to avoid loops when events are generated in update method */
 45    private boolean doPublishEvent; 
 46    /** key = timestamp, value = object reference (Figure) */
 47    private HashMap timestampFigureTable;
 48    /** key = object reference (Figure), value = timestamp */
 49    private HashMap figureTimestampTable;
 50    private HashSet[] newChanged;
 51    private int currentIndex = 0;
 52    private int publishIndex = 1;
 53    private Timeout timeout;
 54    private long publishDelay = 5000L;
 55    private I_XmlBlasterAccess access;
 56    private SubscribeReturnQos subRetQos;
 57    private String drawingName = "GraphicChat";
 58    private SubscribeReturnQos subscribeReturnQos;
 59    private boolean isBurstPublish; // if true publish are collected and sent on bulk
 60 
 61    public XmlBlasterDrawing() {
 62       super();
 63       init(Global.instance());
 64       if (log.isLoggable(Level.FINER)) this.log.finer("default contructor");
 65    }
 66 
 67    public XmlBlasterDrawing(Global global) {
 68       super();
 69       init(global);
 70       if (log.isLoggable(Level.FINER)) this.log.finer("global contructor");
 71    }
 72 
 73    public void init(Global global) {
 74       this.global = global.getClone(null);
 75 
 76       if (log.isLoggable(Level.FINER)) this.log.finer("init");
 77       this.doPublishEvent = true;
 78       this.timestampFigureTable = new HashMap();
 79       this.figureTimestampTable = new HashMap();
 80       this.newChanged = new HashSet[2];
 81       for (int i=0; i < 2; i++) {
 82          this.newChanged[i] = new HashSet();
 83       }
 84       this.publishDelay = this.global.getProperty().get("publishDelay", 200L);
 85       if (this.publishDelay > 0L) this.isBurstPublish = true;
 86       else isBurstPublish = false;
 87       if (this.isBurstPublish) {
 88          this.timeout = new Timeout("PublishTimer");
 89          this.timeout.addTimeoutListener(this, this.publishDelay, this);
 90       }  
 91       if (this.getTitle() != null) this.drawingName = this.getTitle();
 92       initConnection();
 93    }
 94 
 95    public void initConnection() {
 96       try {
 97          if (log.isLoggable(Level.FINER)) this.log.finer("initConnection");
 98          this.access = this.global.getXmlBlasterAccess();
 99          ConnectQos qos = new ConnectQos(this.global);
100          this.access.connect(qos, this);  // Login to xmlBlaster, register for updates
101 
102          SubscribeKey sk = new SubscribeKey(this.global, "/xmlBlaster/key[drawingName='" + this.drawingName + "']", Constants.XPATH);
103          SubscribeQos sq = new SubscribeQos(this.global);
104          sq.setWantLocal(false);
105          sq.setWantInitialUpdate(true);
106          HistoryQos historyQos = new HistoryQos(this.global);
107          historyQos.setNumEntries(1);
108          sq.setHistoryQos(historyQos);
109          
110          // this.publishMetaInfo(this.drawingName);
111          this.subscribeReturnQos = this.access.subscribe(sk, sq);
112       }
113       catch (XmlBlasterException ex) {
114          log.severe("initConnection. Exception : " + ex.getMessage());
115          ex.printStackTrace();
116       }
117    }
118 
119    synchronized public void shutdown() {
120       if (log.isLoggable(Level.FINER)) this.log.finer("shutdown");
121       try {
122          if (this.subscribeReturnQos != null) {
123             this.access.unSubscribe(new UnSubscribeKey(this.global, this.subscribeReturnQos.getSubscriptionId()), new UnSubscribeQos(this.global));
124          }
125          this.access.disconnect(new DisconnectQos(this.global));
126          log.info("successfully shutdown drawing (unsubscribed and disconnected");
127       }
128       catch (XmlBlasterException ex) {
129          log.severe("shutdown. Exception : " + ex.getMessage());
130       }
131    }
132 
133 
134    synchronized private void swapIndex() {
135       if (this.currentIndex == 0) {
136          this.currentIndex = 1; 
137          this.publishIndex = 0;
138       }
139       else {
140          this.currentIndex = 0;
141          this.publishIndex = 1;
142       }
143    }
144 
145    synchronized public void figureChanged(FigureChangeEvent e) {
146       if (log.isLoggable(Level.FINER)) this.log.finer("figureChanged event='" + e.toString() + "'");
147       if (e.getFigure() instanceof Drawing) {
148          if (log.isLoggable(Level.FINE)) this.log.fine("figureChanged for a Drawing instance " + e.getFigure());
149          return;
150       }
151       super.figureChanged(e);
152       if (this.doPublishEvent) {
153          StorableFigureHolder figureHolder = getFigureHolder(e.getFigure());
154          if (this.isBurstPublish) this.newChanged[this.currentIndex].add(figureHolder); 
155          else publish(figureHolder);
156       }
157       if (log.isLoggable(Level.FINER)) this.log.finer("figureChanged " + e.getFigure());
158    }
159 
160    protected void addToTable(StorableFigureHolder figureHolder) {
161       String figureId = figureHolder.getFigureId();
162       Figure figure = figureHolder.getFigure();
163       synchronized (this.timestampFigureTable) {
164          this.figureTimestampTable.put(figure, figureHolder);
165          this.timestampFigureTable.put(figureId, figureHolder);
166       }
167    }   
168 
169    protected void removeFromTable(StorableFigureHolder figureHolder) {
170       if (log.isLoggable(Level.FINER)) this.log.finer("remove");
171       String figureId = figureHolder.getFigureId();
172       Figure figure = figureHolder.getFigure();
173       synchronized (this.timestampFigureTable) {
174          if (figure != null) {
175             this.figureTimestampTable.remove(figure);
176          }
177          if (figureId != null) {
178             this.timestampFigureTable.remove(figureId);
179          }
180       }
181    }
182 
183    private StorableFigureHolder getFigureHolder(Figure figure) {
184       // MEMO: getAttribute does not work when using 'group or lines'
185       StorableFigureHolder figureHolder = (StorableFigureHolder)this.figureTimestampTable.get(figure);
186       if (figureHolder == null) {
187          String timestamp = "" + (new Timestamp()).getTimestampLong();
188          String figureId = this.drawingName + "-" + timestamp;
189          figureHolder = new StorableFigureHolder(figure, figureId, null);
190          addToTable(figureHolder);
191       }
192       return figureHolder;
193    }
194 
195    synchronized public Figure add(Figure figure) {
196       if (log.isLoggable(Level.FINER)) this.log.finer("add");
197       if (figure instanceof Drawing) return figure;
198       if (this.doPublishEvent) {
199          StorableFigureHolder figureHolder = getFigureHolder(figure);
200          if (this.isBurstPublish) this.newChanged[this.currentIndex].add(figureHolder);
201          else publish(figureHolder);
202          if (log.isLoggable(Level.FINE)) this.log.fine("add: adding '" + figureHolder.getFigureId() + "'");
203       }
204       return super.add(figure);
205    }
206 
207    synchronized public void bringToFront(Figure figure) {
208       if (log.isLoggable(Level.FINER)) this.log.finer("bringToFront");
209       if (this.doPublishEvent) {
210          ((StorableFigureHolder)this.figureTimestampTable.get(figure)).setToFront("true");
211       } 
212       super.bringToFront(figure);
213    }
214 
215    synchronized public Figure orphan(Figure figure) {
216       if (log.isLoggable(Level.FINER)) this.log.finer("orphan");
217       if (figure instanceof Drawing) return figure;
218       if (this.doPublishEvent) {
219          try {
220             erase(figure);
221          }
222          catch (XmlBlasterException ex) {
223             String figureId = ((StorableFigureHolder)this.figureTimestampTable.get(figure)).getFigureId();
224             log.severe("orphan '" + figureId + "' exception : " + ex.getMessage());
225             ex.printStackTrace();
226          }
227       }
228       return super.orphan(figure); // clean from my drawing area
229    }
230 
231    public synchronized void sendToBack(Figure figure) {
232       if (log.isLoggable(Level.FINER)) this.log.finer("sendToBack");
233       if (this.doPublishEvent) {
234          ((StorableFigureHolder)this.figureTimestampTable.get(figure)).setToFront("false");
235       } 
236       super.sendToBack(figure);
237    }
238 
239    public synchronized void sendToLayer(Figure figure, int layerNr) {
240       if (log.isLoggable(Level.FINER)) this.log.finer("sendToLayer");
241       super.sendToLayer(figure, layerNr);
242    }
243 
244    public synchronized void setTitle(java.lang.String name) {
245       if (log.isLoggable(Level.FINER)) this.log.finer("");
246       super.setTitle(name);
247    }
248 
249 
250    /**
251     * Invoked by the timer
252     */
253    public synchronized void timeout(Object userData) {
254       try {
255          swapIndex();
256          if (this.newChanged[this.publishIndex].size() == 0) return;
257          Iterator iter = this.newChanged[this.publishIndex].iterator();
258          while (iter.hasNext()) {
259             StorableFigureHolder figureHolder = (StorableFigureHolder)iter.next();
260             if (figureHolder.getFigure() instanceof Drawing) continue;
261             publish(figureHolder);
262          }
263          this.newChanged[this.publishIndex].clear();
264       }
265       catch (Throwable ex) {
266          log.warning("timeout: exception occured in timeout: " + ex.getMessage());
267          ex.printStackTrace();
268       }
269       finally {
270          this.timeout.addTimeoutListener(this, this.publishDelay, this);
271       }
272    }
273 
274    // publish -----------------------------------------------------------
275 
276    private void publish(StorableFigureHolder figureHolder) {
277       if (log.isLoggable(Level.FINER)) this.log.finer("publish");
278       // publish the message here ...
279       if (figureHolder == null || figureHolder.getFigure() instanceof Drawing) return;
280       try {
281          String figureId = figureHolder.getFigureId();
282          if (log.isLoggable(Level.FINE)) this.log.fine("publish '" + figureId + "'");
283          PublishKey pk = new PublishKey(this.global, figureId, "application/draw", "1.0");
284          pk.setClientTags("<drawingName>"+this.drawingName+"</drawingName>");
285          PublishQos pq = new PublishQos(this.global);
286 
287          MsgUnit msgUnit = new MsgUnit(pk, figureHolder.toBytes(), pq);
288          this.access.publish(msgUnit);
289       }
290       catch (IOException ex) {
291          log.severe("exception occured when publishing: " + ex.toString());
292       }
293       catch (XmlBlasterException ex) {
294          log.severe("exception occured when publishing: " + ex.getMessage());
295       }
296    }
297 
298    private void publishMetaInfo(String data) {
299       if (log.isLoggable(Level.FINER)) this.log.finer("publishMetaInfo");
300       // publish the message here ...
301       try {
302          PublishKey pk = new PublishKey(this.global, null, "text/plain", "1.0");
303          pk.setClientTags("<drawingMetadata>"+this.drawingName+"</drawingMetadata>");
304          PublishQos pq = new PublishQos(this.global);
305 
306          MsgUnit msgUnit = new MsgUnit(pk, data.getBytes(), pq);
307          this.access.publish(msgUnit);
308       }
309       catch (XmlBlasterException ex) {
310               log.severe("exception occured when publishing: " + ex.getMessage());
311       }
312    }
313 
314    // erase ---------------------------------------------------------------
315 
316    private void erase(Figure figure) throws XmlBlasterException {
317       if (log.isLoggable(Level.FINER)) this.log.finer("erase");
318       if (figure == null) return;
319       StorableFigureHolder figureHolder = (StorableFigureHolder)this.figureTimestampTable.get(figure);
320       if (figureHolder == null) return;
321       String figureId = figureHolder.getFigureId();
322       if (log.isLoggable(Level.FINE)) this.log.fine("erase '" + figureId + "'");
323       EraseKey ek = new EraseKey(this.global, figureId);
324       EraseQos eq = new EraseQos(this.global);
325       EraseReturnQos[] eraseArr = this.access.erase(ek, eq);
326       removeFromTable(figureHolder);
327    }
328 
329    // update ---------------------------------------------------------------
330 
331    private void currentFigureUpdate(StorableFigureHolder figureHolder) {
332       if (log.isLoggable(Level.FINER)) this.log.finer("currentFigureUpdate");
333       boolean sendToBack = false;
334       boolean sendToFront = false;
335       String toFront = figureHolder.getToFront(); 
336       if (toFront != null) {
337          boolean val = "true".equalsIgnoreCase(toFront);
338          log.info("update: the attribute 'TO_FRONT' is set to '" + val + "'"); 
339          if (val == true) sendToFront = true;
340          else sendToBack = true;                         
341          // setFigureAttribute(fig, TO_FRONT, null);
342       }
343       String figureId = figureHolder.getFigureId();
344       if (log.isLoggable(Level.FINE)) this.log.fine("currentFigureUpdate figureId='" + figureId + "'");
345       if (figureId == null) return;
346       // String figureId = msgContent.getFigureId();
347       log.info("update figure: '" + figureId  + "' changed or added");
348       StorableFigureHolder oldFigureHolder = (StorableFigureHolder)this.timestampFigureTable.get(figureId);
349 
350       Figure fig = figureHolder.getFigure();
351       if (oldFigureHolder == null) {
352          addToTable(figureHolder);
353          super.add(fig);
354       }
355       else {
356          Figure oldFigure = oldFigureHolder.getFigure();
357          super.replace(oldFigure, fig);
358          removeFromTable(oldFigureHolder);
359          addToTable(figureHolder);
360          FigureChangeEvent ev = new FigureChangeEvent(oldFigure);
361          figureRequestUpdate(ev);
362          if (sendToFront) {
363             if (log.isLoggable(Level.FINE)) this.log.fine("update: send to front");
364             bringToFront(fig);
365          } 
366          else if (sendToBack) {
367             if (log.isLoggable(Level.FINE)) this.log.fine("update: send to back");
368             sendToBack(fig);
369          } 
370       }
371       FigureChangeEvent ev1 = new FigureChangeEvent(fig);
372       figureRequestUpdate(ev1);
373    }
374 
375    public synchronized String update(String cbSessionId, UpdateKey updateKey, byte[] content, UpdateQos updateQos) {
376       log.info("update for '" + cbSessionId + "', '" + updateKey.getOid() + "' length of msg is '" + content.length + "'");
377       try {
378          lock();
379          this.doPublishEvent = false;
380               
381          if (updateQos.isErased()) {
382             log.info("Message '" + updateKey.getOid() + "' is erased");
383             String figureId = updateKey.getOid();
384             StorableFigureHolder figHolder = (StorableFigureHolder)this.timestampFigureTable.get(figureId);
385             if (figHolder != null) {
386                removeFromTable(figHolder);
387                Figure fig = figHolder.getFigure();
388                Figure figToRelease = super.orphan(fig);
389                FigureChangeEvent ev = new FigureChangeEvent(fig);
390                figureRequestUpdate(ev);
391                figToRelease.release();
392             }
393             return "OK";
394          }
395 
396          StorableFigureHolder figureHolder = StorableFigureHolder.fromBytes(content);
397          currentFigureUpdate(figureHolder);
398       }
399       catch (IOException ex) {
400          log.severe("update: an IOException occured when reconstructing the content: " + ex.getMessage());
401       }
402       catch (Throwable ex) {
403          log.severe("update: a Throwable occured when reconstructing the content (acknowledge anyway): " + ex.getMessage());
404          ex.printStackTrace();
405       }
406       finally {
407          this.doPublishEvent = true;
408          unlock();
409       }
410       return "OK";
411    }
412 
413    public void release() {
414       if (log.isLoggable(Level.FINER)) this.log.finer("release");
415       shutdown();
416       super.release();
417    }
418 
419 
420 
421    /*
422       public void figureInvalidated(FigureChangeEvent e) {
423          super.figureInvalidated(e);
424          if (log.isLoggable(Level.FINER)) this.log.finer("figureInvalidated " + e.getFigure());
425       }
426 
427       public void figureRemoved(FigureChangeEvent e) {
428          super.figureRemoved(e);
429          if (log.isLoggable(Level.FINER)) this.log.finer("figureRemoved " + e.getFigure());
430       }
431 
432       public void figureRequestRemove(FigureChangeEvent e) {
433          super.figureRequestRemove(e);
434          if (log.isLoggable(Level.FINER)) this.log.finer("figureRequestRemove " + e.getFigure());
435       }
436 
437       public void figureRequestUpdate(FigureChangeEvent e) {
438          super.figureRequestUpdate(e);
439          if (log.isLoggable(Level.FINER)) this.log.finer("figureRequestUpdate");
440       }
441 
442       public void addAll(FigureEnumeration fe) {
443          if (log.isLoggable(Level.FINER)) this.log.finer("addAll");
444          super.addAll(fe);
445       }
446 
447       public void init(java.awt.Rectangle viewRectangle) {
448          if (log.isLoggable(Level.FINER)) this.log.finer("init");
449          super.init(viewRectangle);
450       }
451 
452       public void orphanAll(FigureEnumeration fe) {
453          if (log.isLoggable(Level.FINER)) this.log.finer("orphanAll");
454          super.orphanAll(fe);
455       }
456 
457       public Figure remove(Figure figure) {
458          if (log.isLoggable(Level.FINER)) this.log.finer("remove");
459          return super.remove(figure);
460       }
461 
462       public void removeAll(FigureEnumeration fe) {
463          if (log.isLoggable(Level.FINER)) this.log.finer("removeAll");
464          super.removeAll(fe);
465       }
466 
467       public Figure replace(Figure figure, Figure replacement) {
468          if (log.isLoggable(Level.FINER)) this.log.finer("replace");
469          return super.replace(figure, replacement);
470       }
471    */
472 }


syntax highlighted by Code2HTML, v. 0.9.1