vamsas connects and updates are threaded with a progress bar.
[jalview.git] / src / jalview / gui / VamsasApplication.java
1 /**
2  *
3  */
4 package jalview.gui;
5
6 import jalview.bin.Cache;
7 import jalview.datamodel.SequenceI;
8 import jalview.io.VamsasAppDatastore;
9 import jalview.structure.StructureSelectionManager;
10 import jalview.structure.VamsasListener;
11
12 import java.beans.PropertyChangeEvent;
13 import java.beans.PropertyChangeListener;
14 import java.io.File;
15 import java.io.IOException;
16 import java.util.Hashtable;
17 import java.util.IdentityHashMap;
18
19 import javax.swing.JInternalFrame;
20 import javax.swing.JOptionPane;
21
22 import uk.ac.vamsas.client.ClientHandle;
23 import uk.ac.vamsas.client.IClient;
24 import uk.ac.vamsas.client.IClientDocument;
25 import uk.ac.vamsas.client.InvalidSessionDocumentException;
26 import uk.ac.vamsas.client.NoDefaultSessionException;
27 import uk.ac.vamsas.client.UserHandle;
28 import uk.ac.vamsas.client.VorbaId;
29 import uk.ac.vamsas.client.picking.IMessageHandler;
30 import uk.ac.vamsas.client.picking.IPickManager;
31 import uk.ac.vamsas.client.picking.Message;
32 import uk.ac.vamsas.client.picking.MouseOverMessage;
33 import uk.ac.vamsas.objects.core.Entry;
34
35 /**
36  * @author jimp
37  * 
38  */
39 public class VamsasApplication
40 {
41   IClient vclient = null;
42
43   ClientHandle app = null;
44
45   UserHandle user = null;
46
47   Desktop jdesktop = null; // our jalview desktop reference
48
49   // Cache.preferences for vamsas client session arena
50   // preferences for check for default session at startup.
51   // user and organisation stuff.
52   public VamsasApplication(Desktop jdesktop, File sessionPath)
53   {
54     // JBPNote:
55     // we should create a session URI from the sessionPath and pass it to
56     // the clientFactory - but the vamsas api doesn't cope with that yet.
57     this.jdesktop = jdesktop;
58     initClientSession(null, sessionPath);
59   }
60
61   private static uk.ac.vamsas.client.IClientFactory getClientFactory()
62           throws IOException
63   {
64     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
65   }
66
67   /**
68    * Start a new vamsas session
69    * 
70    * @param jdesktop
71    */
72   public VamsasApplication(Desktop jdesktop)
73   {
74     this.jdesktop = jdesktop;
75     initClientSession(null, null);
76   }
77
78   /**
79    * init a connection to the session at the given url
80    * 
81    * @param jdesktop
82    * @param sessionUrl
83    */
84   public VamsasApplication(Desktop jdesktop, String sessionUrl)
85   {
86     this.jdesktop = jdesktop;
87     initClientSession(sessionUrl, null);
88   }
89
90   /**
91    * @throws IOException
92    *                 or other if clientfactory instantiation failed.
93    * @return list of current sessions or null if no session exists.
94    */
95   public static String[] getSessionList() throws Exception
96   {
97     return getClientFactory().getCurrentSessions();
98   }
99
100   /**
101    * initialise, possibly with either a valid session url or a file for a new
102    * session
103    * 
104    * @param sess
105    *                null or a valid session url
106    * @param vamsasDocument
107    *                null or a valid vamsas document file
108    * @return false if no vamsas connection was made
109    */
110   private boolean initClientSession(String sess, File vamsasDocument)
111   {
112     try
113     {
114       // Only need to tell the library what the application is here
115       app = getJalviewHandle();
116       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
117       if (vamsasDocument != null)
118       {
119         if (sess != null)
120         {
121           throw new Error(
122                   "Implementation Error - cannot import existing vamsas document into an existing session, Yet!");
123         }
124         try
125         {
126           vclient = clientfactory.openAsNewSessionIClient(app,
127                   vamsasDocument);
128         } catch (InvalidSessionDocumentException e)
129         {
130           JOptionPane
131                   .showInternalMessageDialog(
132                           Desktop.desktop,
133
134                           "VAMSAS Document could not be opened as a new session - please choose another",
135                           "VAMSAS Document Import Failed",
136                           JOptionPane.ERROR_MESSAGE);
137
138         }
139       }
140       else
141       {
142         // join existing or create a new session
143         if (sess == null)
144         {
145           vclient = clientfactory.getNewSessionIClient(app);
146         }
147         else
148         {
149           vclient = clientfactory.getIClient(app, sess);
150         }
151       }
152       // set some properties for our VAMSAS interaction
153       setVclientConfig();
154       user = vclient.getUserHandle();
155
156     } catch (Exception e)
157     {
158       jalview.bin.Cache.log
159               .error("Couldn't instantiate vamsas client !", e);
160       return false;
161     }
162     return true;
163   }
164
165   private void setVclientConfig()
166   {
167     if (vclient==null)
168     {
169       return;
170     }
171     try {
172       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
173       {
174         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient)  vclient).getSimpleClientConfig();
175         cfg._validatemergedroots = false;
176         cfg._validateupdatedroots= true; //we may write rubbish otherwise. 
177       }
178     }
179     catch (Error e)
180     {
181       Cache.log.warn("Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",e);
182     }
183     catch (Exception e)
184     {
185       Cache.log.warn("Probable VAMSAS client incompatibility - carrying on regardless",e);
186     }
187   }
188
189   /**
190    * make the appHandle for Jalview
191    * 
192    * @return
193    */
194   private ClientHandle getJalviewHandle()
195   {
196     return new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache
197             .getProperty("VERSION"));
198   }
199
200   /**
201    * 
202    * @return true if we are registered in a vamsas session
203    */
204   public boolean inSession()
205   {
206     return (vclient != null);
207   }
208
209   /**
210    * called to connect to session inits handlers, does an initial document
211    * update.
212    */
213   public void initial_update()
214   {
215     if (!inSession())
216     {
217       throw new Error(
218               "Impementation error! Vamsas Operations when client not initialised and connected.");
219     }
220     addDocumentUpdateHandler();
221     addStoreDocumentHandler();
222     startSession();
223     Cache.log
224             .debug("Jalview loading the Vamsas Session for the first time.");
225     dealWithDocumentUpdate(false); // we don't push an update out to the
226                                     // document yet.
227     Cache.log.debug("... finished update for the first time.");
228   }
229
230   /**
231    * Update all windows after a vamsas datamodel change. this could go on the
232    * desktop object!
233    * 
234    */
235   protected void updateJalviewGui()
236   {
237     JInternalFrame[] frames = jdesktop.getAllFrames();
238
239     if (frames == null)
240     {
241       return;
242     }
243
244     try
245     {
246       // REVERSE ORDER
247       for (int i = frames.length - 1; i > -1; i--)
248       {
249         if (frames[i] instanceof AlignFrame)
250         {
251           AlignFrame af = (AlignFrame) frames[i];
252           af.alignPanel.alignmentChanged();
253         }
254       }
255     } catch (Exception e)
256     {
257       Cache.log
258               .warn(
259                       "Exception whilst refreshing jalview windows after a vamsas document update.",
260                       e);
261     }
262   }
263
264   public void push_update()
265   {
266     Thread udthread = new Thread(new Runnable() {
267
268       public void run()
269       {
270         Cache.log.info("Jalview updating to the Vamsas Session.");
271     
272         dealWithDocumentUpdate(true);
273         /*
274          * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); }
275          * catch (Exception e) { Cache.log.error("Failed to get client document for
276          * update."); // RAISE A WARNING DIALOG disableGui(false); return; }
277          * updateVamsasDocument(cdoc); updateJalviewGui();
278          * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags
279          * back vclient.updateDocument(cdoc);
280          */
281         Cache.log.info("Jalview finished updating to the Vamsas Session.");
282         // TODO Auto-generated method stub
283       }
284       
285     });
286     udthread.start();
287   }
288
289   public void end_session()
290   {
291     if (!inSession())
292       throw new Error("Jalview not connected to Vamsas session.");
293     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
294     try
295     {
296       if (joinedSession)
297       {
298         vclient.finalizeClient();
299         Cache.log.info("Jalview has left the session.");
300       }
301       else
302       {
303         Cache.log
304                 .warn("JV Client leaving a session that's its not joined yet.");
305       }
306       joinedSession = false;
307       vclient = null;
308       app = null;
309       user = null;
310       jv2vobj = null;
311       vobj2jv = null;
312     } catch (Exception e)
313     {
314       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
315     }
316   }
317
318   public void updateJalview(IClientDocument cdoc)
319   {
320     Cache.log.debug("Jalview updating from sesion document ..");
321     ensureJvVamsas();
322     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
323             baseProvEntry(), alRedoState);
324     vds.updateToJalview();
325     Cache.log.debug(".. finished updating from sesion document.");
326
327   }
328
329   private void ensureJvVamsas()
330   {
331     if (jv2vobj == null)
332     {
333       jv2vobj = new IdentityHashMap();
334       vobj2jv = new Hashtable();
335       alRedoState = new Hashtable();
336     }
337   }
338
339   /**
340    * jalview object binding to VorbaIds
341    */
342   IdentityHashMap jv2vobj = null;
343
344   Hashtable vobj2jv = null;
345   Hashtable alRedoState = null;
346   public void updateVamsasDocument(IClientDocument doc)
347   {
348     ensureJvVamsas();
349     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
350             baseProvEntry(), alRedoState);
351     // wander through frames
352     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
353
354     if (frames == null)
355     {
356       return;
357     }
358
359     try
360     {
361       // REVERSE ORDER
362       for (int i = frames.length - 1; i > -1; i--)
363       {
364         if (frames[i] instanceof AlignFrame)
365         {
366           AlignFrame af = (AlignFrame) frames[i];
367
368           // update alignment and root from frame.
369           vds.storeVAMSAS(af.getViewport(), af.getTitle());
370         }
371       }
372       // REVERSE ORDER
373       for (int i = frames.length - 1; i > -1; i--)
374       {
375         if (frames[i] instanceof AlignFrame)
376         {
377           AlignFrame af = (AlignFrame) frames[i];
378
379           // add any AlignedCodonFrame mappings on this alignment to any other.
380           vds.storeSequenceMappings(af.getViewport(), af.getTitle());
381         }
382       }
383     } catch (Exception e)
384     {
385       Cache.log.error("Vamsas Document store exception", e);
386     }
387   }
388
389   private Entry baseProvEntry()
390   {
391     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
392     pentry.setUser(user.getFullName());
393     pentry.setApp(app.getClientUrn());
394     pentry.setDate(new java.util.Date());
395     pentry.setAction("created");
396     return pentry;
397   }
398
399   /**
400    * do a vamsas document update or update jalview from the vamsas document
401    * 
402    * @param fromJalview
403    *                true to update from jalview to the vamsas document
404    */
405   protected void dealWithDocumentUpdate(boolean fromJalview)
406   {
407     // called by update handler for document update.
408     Cache.log.debug("Updating jalview from changed vamsas document.");
409     disableGui(true);
410     try
411     {
412       long time = System.currentTimeMillis();
413       IClientDocument cdoc = vclient.getClientDocument();
414       if (Cache.log.isDebugEnabled())
415       {
416         Cache.log.debug("Time taken to get ClientDocument = "
417                 + (System.currentTimeMillis() - time));
418         time = System.currentTimeMillis();
419       }
420       if (fromJalview)
421       {
422         this.updateVamsasDocument(cdoc);
423         if (Cache.log.isDebugEnabled())
424         {
425           Cache.log
426                   .debug("Time taken to update Vamsas Document from jalview\t= "
427                           + (System.currentTimeMillis() - time));
428           time = System.currentTimeMillis();
429         }
430         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
431         if (Cache.log.isDebugEnabled())
432         {
433           Cache.log.debug("Time taken to set Document Roots\t\t= "
434                   + (System.currentTimeMillis() - time));
435           time = System.currentTimeMillis();
436         }
437       }
438       else
439       {
440         updateJalview(cdoc);
441         if (Cache.log.isDebugEnabled())
442         {
443           Cache.log
444                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
445                           + (System.currentTimeMillis() - time));
446           time = System.currentTimeMillis();
447         }
448
449       }
450       vclient.updateDocument(cdoc);
451       if (Cache.log.isDebugEnabled())
452       {
453         Cache.log.debug("Time taken to update Session Document\t= "
454                 + (System.currentTimeMillis() - time));
455         time = System.currentTimeMillis();
456       }
457       cdoc = null;
458     } catch (Exception ee)
459     {
460       System.err.println("Exception whilst updating :");
461       ee.printStackTrace(System.err);
462     }
463     Cache.log.debug("Finished updating from document change.");
464     disableGui(false);
465   }
466
467   private void addDocumentUpdateHandler()
468   {
469     final VamsasApplication client = this;
470     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
471     {
472       public void propertyChange(PropertyChangeEvent evt)
473       {
474         Cache.log.debug("Dealing with document update event.");
475         client.dealWithDocumentUpdate(false);
476         Cache.log.debug("finished dealing with event.");
477       }
478     });
479     Cache.log.debug("Added Jalview handler for vamsas document updates.");
480   }
481
482   private void addStoreDocumentHandler()
483   {
484     final VamsasApplication client = this;
485     vclient.addVorbaEventHandler(uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE, new PropertyChangeListener()
486     {
487       public void propertyChange(PropertyChangeEvent evt)
488       {
489         Cache.log.debug("Asking user if the vamsas session should be stored.");
490         int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
491                 "The current VAMSAS session has unsaved data - do you want to save it ?",
492             "VAMSAS Session Shutdown",
493             JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
494         
495             if(reply==JOptionPane.YES_OPTION)
496             {
497               Cache.log.debug("Prompting for vamsas store filename.");
498               Desktop.instance.vamsasSave_actionPerformed(null);
499               Cache.log.debug("Finished attempt at storing document.");
500             } 
501             Cache.log.debug("finished dealing with REQUESTTOCLOSE event.");
502       }
503     });
504     Cache.log.debug("Added Jalview handler for vamsas document updates.");
505   }
506   public void disableGui(boolean b)
507   {
508     Desktop.instance.setVamsasUpdate(b);
509   }
510
511   private boolean joinedSession = false;
512
513   private VamsasListener picker = null;
514
515   private void startSession()
516   {
517     if (inSession())
518     {
519       try
520       {
521         vclient.joinSession();
522         joinedSession = true;
523       } catch (Exception e)
524       {
525         // Complain to GUI
526         Cache.log.error("Failed to join vamsas session.", e);
527         vclient = null;
528       }
529       try
530       {
531         final IPickManager pm = vclient.getPickManager();
532         final StructureSelectionManager ssm = StructureSelectionManager
533                 .getStructureSelectionManager();
534         pm.registerMessageHandler(new IMessageHandler()
535         {
536           String last = null;
537
538           public void handleMessage(Message message)
539           {
540             if (message instanceof MouseOverMessage && vobj2jv != null)
541             {
542               MouseOverMessage mm = (MouseOverMessage) message;
543               String mstring = mm.getVorbaID() + " " + mm.getPosition();
544               if (last != null && mstring.equals(last))
545               {
546                 return;
547               }
548               // if (Cache.log.isDebugEnabled())
549               // {
550               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
551               // "+mm.getPosition());
552               // }
553               Object jvobj = vobj2jv.get(mm.getVorbaID());
554               if (jvobj != null && jvobj instanceof SequenceI)
555               {
556                 last = mstring;
557                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
558                 // bound to "+jvobj+" at "+mm.getPosition());
559                 // position is in sequence or in aligned sequence ???????
560                 ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
561                         .getPosition());
562               }
563             }
564           }
565         });
566         picker = new VamsasListener()
567         {
568           SequenceI last = null;
569
570           int i = -1;
571
572           public void mouseOver(SequenceI seq, int index)
573           {
574             if (jv2vobj == null)
575               return;
576             if (seq != last || i != index)
577             {
578               VorbaId v = (VorbaId) jv2vobj.get(seq);
579               if (v != null)
580               {
581                 Cache.log.debug("Mouse over " + v.getId() + " bound to "
582                         + seq + " at " + index);
583                 last = seq;
584                 i = index;
585                 MouseOverMessage message = new MouseOverMessage(v.getId(),
586                         index);
587                 pm.sendMessage(message);
588               }
589             }
590           }
591         };
592         ssm.addStructureViewerListener(picker); // better method here
593       } catch (Exception e)
594       {
595         Cache.log.error("Failed to init Vamsas Picking", e);
596       }
597     }
598   }
599 }