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