82036639a791c013d5c42ddb26f0d2982a1d404f
[jalview.git] / src / jalview / bin / Jalview.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import jalview.ext.so.SequenceOntology;
24 import jalview.gui.AlignFrame;
25 import jalview.gui.Desktop;
26 import jalview.gui.PromptUserConfig;
27 import jalview.io.AppletFormatAdapter;
28 import jalview.io.BioJsHTMLOutput;
29 import jalview.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileLoader;
34 import jalview.io.HtmlSvgOutput;
35 import jalview.io.IdentifyFile;
36 import jalview.io.NewickFile;
37 import jalview.io.gff.SequenceOntologyFactory;
38 import jalview.schemes.ColourSchemeI;
39 import jalview.schemes.ColourSchemeProperty;
40 import jalview.util.MessageManager;
41 import jalview.util.Platform;
42 import jalview.util.Worker;
43 import jalview.ws.jws2.Jws2Discoverer;
44
45 import java.io.BufferedReader;
46 import java.io.File;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.InputStreamReader;
50 import java.io.OutputStreamWriter;
51 import java.io.PrintWriter;
52 import java.net.MalformedURLException;
53 import java.net.URI;
54 import java.net.URISyntaxException;
55 import java.net.URL;
56 import java.security.AllPermission;
57 import java.security.CodeSource;
58 import java.security.PermissionCollection;
59 import java.security.Permissions;
60 import java.security.Policy;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Vector;
67
68 import javax.swing.LookAndFeel;
69 import javax.swing.UIManager;
70
71 import groovy.lang.Binding;
72 import groovy.util.GroovyScriptEngine;
73
74 /**
75  * Main class for Jalview Application <br>
76  * <br>
77  * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \
78  * jalview.bin.Jalview
79  * 
80  * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \
81  * jalview.bin.Jalview jalview.bin.Jalview
82  * 
83  * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not
84  * embellish '*' to e.g. '*.jar')
85  * 
86  * @author $author$
87  * @version $Revision$
88  */
89 public class Jalview
90 {
91   /*
92    * singleton instance of this class
93    */
94   private static Jalview instance;
95
96   private Desktop desktop;
97
98   public static AlignFrame currentAlignFrame;
99
100   static
101   {
102     // grab all the rights we can the JVM
103     Policy.setPolicy(new Policy()
104     {
105       @Override
106       public PermissionCollection getPermissions(CodeSource codesource)
107       {
108         Permissions perms = new Permissions();
109         perms.add(new AllPermission());
110         return (perms);
111       }
112
113       @Override
114       public void refresh()
115       {
116       }
117     });
118   }
119
120   /**
121    * keep track of feature fetching tasks.
122    * 
123    * @author JimP
124    * 
125    */
126   class FeatureFetcher
127   {
128     /*
129      * TODO: generalise to track all jalview events to orchestrate batch
130      * processing events.
131      */
132
133     private int queued = 0;
134
135     private int running = 0;
136
137     public FeatureFetcher()
138     {
139
140     }
141
142     public void addFetcher(final AlignFrame af,
143             final Vector<String> dasSources)
144     {
145       final long id = System.currentTimeMillis();
146       queued++;
147       final FeatureFetcher us = this;
148       new Thread(new Runnable()
149       {
150
151         @Override
152         public void run()
153         {
154           synchronized (us)
155           {
156             queued--;
157             running++;
158           }
159
160           af.setProgressBar(MessageManager
161                   .getString("status.das_features_being_retrived"), id);
162           af.featureSettings_actionPerformed(null);
163           af.setProgressBar(null, id);
164           synchronized (us)
165           {
166             running--;
167           }
168         }
169       }).start();
170     }
171
172     public synchronized boolean allFinished()
173     {
174       return queued == 0 && running == 0;
175     }
176
177   }
178
179   public static Jalview getInstance()
180   {
181     return instance;
182   }
183
184   /**
185    * main class for Jalview application
186    * 
187    * @param args
188    *          open <em>filename</em>
189    */
190   public static void main(String[] args)
191   {
192     float ONE_MB = 1048576f;
193     Runtime runtime = Runtime.getRuntime();
194     float maxMemory = runtime.maxMemory() / ONE_MB;
195     boolean is9or10 = System.getProperty("java.version").startsWith("10.")
196             || System.getProperty("java.version").startsWith("9.")
197             || System.getProperty("java.version").startsWith("10.")
198             || System.getProperty("java.version").startsWith("1.10.")
199             || System.getProperty("java.version").startsWith("1.9.");
200     if (maxMemory < 1024 * 14)
201     {
202       ArrayList<String> cmd = new ArrayList();
203       
204       boolean dontspawn=false;
205       for (String r : args)
206       {
207         cmd.add(r);
208         if (r.indexOf("-Xmx")>-1) {
209           dontspawn=true;
210           break;
211         }
212       }
213       if (!dontspawn)
214       {
215         cmd.add("-Xmx15G");
216         String[] javargs=new String[1];
217         if (is9or10) {
218           javargs = new String[2];
219           javargs[1] = "--add-modules=java.se.ee";
220         }
221         javargs[0]="-Xmx15G";
222         
223         System.err.print("relaunching with 15G: ");
224         for (String r : cmd)
225         {
226           System.err.print(r + " ");
227         }
228         System.err.println("");
229         Worker worker = Worker.jalviewDesktopRunner(false,
230                 cmd.toArray(new String[0]), 0, javargs);
231         worker.doEcho();
232         try
233         {
234         while (worker.getExitValue() == null)
235         {
236           try
237           {
238           Thread.sleep(2000);
239           } catch (Exception q)
240           {
241           }
242           ;
243         }
244         } catch (Exception q)
245         {
246         }
247         ;
248
249         System.exit(worker.getExitValue());
250       }
251     }
252
253     List<String> rgs = new ArrayList();
254     rgs.addAll(Arrays.asList(args));
255     int i = 0;
256     while (i < rgs.size())
257     {
258       if (rgs.get(i).indexOf("-Xmx") > -1)
259       {
260         rgs.remove(i);
261       }
262       else
263       {
264         i++;
265       }
266     }
267     instance = new Jalview();
268     instance.doMain(args);
269   }
270
271   /**
272    * @param args
273    */
274   void doMain(String[] args)
275   {
276     System.setSecurityManager(null);
277     System.out
278             .println("Java version: " + System.getProperty("java.version"));
279     System.out.println(System.getProperty("os.arch") + " "
280             + System.getProperty("os.name") + " "
281             + System.getProperty("os.version"));
282
283     ArgsParser aparser = new ArgsParser(args);
284     boolean headless = false;
285
286     if (aparser.contains("help") || aparser.contains("h"))
287     {
288       showUsage();
289       System.exit(0);
290     }
291     if (aparser.contains("nodisplay") || aparser.contains("nogui")
292             || aparser.contains("headless"))
293     {
294       System.setProperty("java.awt.headless", "true");
295       headless = true;
296     }
297     String usrPropsFile = aparser.getValue("props");
298     Cache.loadProperties(usrPropsFile); // must do this before
299     if (usrPropsFile != null)
300     {
301       System.out.println(
302               "CMD [-props " + usrPropsFile + "] executed successfully!");
303     }
304
305     // anything else!
306
307     final String jabawsUrl = aparser.getValue("jabaws");
308     if (jabawsUrl != null)
309     {
310       try
311       {
312         Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
313         System.out.println(
314                 "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
315       } catch (MalformedURLException e)
316       {
317         System.err.println(
318                 "Invalid jabaws parameter: " + jabawsUrl + " ignored");
319       }
320     }
321
322     String defs = aparser.getValue("setprop");
323     while (defs != null)
324     {
325       int p = defs.indexOf('=');
326       if (p == -1)
327       {
328         System.err.println("Ignoring invalid setprop argument : " + defs);
329       }
330       else
331       {
332         System.out.println("Executing setprop argument: " + defs);
333         // DISABLED FOR SECURITY REASONS
334         // TODO: add a property to allow properties to be overriden by cli args
335         // Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
336       }
337       defs = aparser.getValue("setprop");
338     }
339     if (System.getProperty("java.awt.headless") != null
340             && System.getProperty("java.awt.headless").equals("true"))
341     {
342       headless = true;
343     }
344     System.setProperty("http.agent",
345             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
346     try
347     {
348       Cache.initLogger();
349     } catch (NoClassDefFoundError error)
350     {
351       error.printStackTrace();
352       System.out.println("\nEssential logging libraries not found."
353               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
354       System.exit(0);
355     }
356
357     desktop = null;
358
359     try
360     {
361       UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
362     } catch (Exception ex)
363     {
364       System.err.println("Unexpected Look and Feel Exception");
365       ex.printStackTrace();
366     }
367     if (Platform.isAMac())
368     {
369
370       LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
371               .getLookAndFeel();
372       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
373               "Jalview");
374       System.setProperty("apple.laf.useScreenMenuBar", "true");
375       if (lookAndFeel != null)
376       {
377         try
378         {
379           UIManager.setLookAndFeel(lookAndFeel);
380         } catch (Throwable e)
381         {
382           System.err.println(
383                   "Failed to set QuaQua look and feel: " + e.toString());
384         }
385       }
386       if (lookAndFeel == null || !(lookAndFeel.getClass()
387               .isAssignableFrom(UIManager.getLookAndFeel().getClass()))
388               || !UIManager.getLookAndFeel().getClass().toString()
389                       .toLowerCase().contains("quaqua"))
390       {
391         try
392         {
393           System.err.println(
394                   "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
395           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
396         } catch (Throwable e)
397         {
398           System.err.println(
399                   "Failed to reset look and feel: " + e.toString());
400         }
401       }
402     }
403
404     /*
405      * configure 'full' SO model if preferences say to, 
406      * else use the default (SO Lite)
407      */
408     if (Cache.getDefault("USE_FULL_SO", false))
409     {
410       SequenceOntologyFactory.setInstance(new SequenceOntology());
411     }
412
413     if (!headless)
414     {
415       desktop = new Desktop();
416       desktop.setInBatchMode(true); // indicate we are starting up
417       desktop.setVisible(true);
418       desktop.startServiceDiscovery();
419       if (!aparser.contains("nousagestats"))
420       {
421         startUsageStats(desktop);
422       }
423       else
424       {
425         System.err.println("CMD [-nousagestats] executed successfully!");
426       }
427
428       if (!aparser.contains("noquestionnaire"))
429       {
430         String url = aparser.getValue("questionnaire");
431         if (url != null)
432         {
433           // Start the desktop questionnaire prompter with the specified
434           // questionnaire
435           Cache.log.debug("Starting questionnaire url at " + url);
436           desktop.checkForQuestionnaire(url);
437           System.out.println(
438                   "CMD questionnaire[-" + url + "] executed successfully!");
439         }
440         else
441         {
442           if (Cache.getProperty("NOQUESTIONNAIRES") == null)
443           {
444             // Start the desktop questionnaire prompter with the specified
445             // questionnaire
446             // String defurl =
447             // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
448             // //
449             String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
450             Cache.log.debug(
451                     "Starting questionnaire with default url: " + defurl);
452             desktop.checkForQuestionnaire(defurl);
453           }
454         }
455       }
456       else
457       {
458         System.err.println("CMD [-noquestionnaire] executed successfully!");
459       }
460
461       if (!aparser.contains("nonews"))
462       {
463         desktop.checkForNews();
464       }
465
466       BioJsHTMLOutput.updateBioJS();
467     }
468
469     String file = null, data = null;
470     FileFormatI format = null;
471     DataSourceType protocol = null;
472     FileLoader fileLoader = new FileLoader(!headless);
473     Vector<String> getFeatures = null; // vector of das source nicknames to
474                                        // fetch
475     // features from
476     // loading is done.
477     String groovyscript = null; // script to execute after all loading is
478     // completed one way or another
479     // extract groovy argument and execute if necessary
480     groovyscript = aparser.getValue("groovy", true);
481     file = aparser.getValue("open", true);
482
483     if (file == null && desktop == null)
484     {
485       System.out.println("No files to open!");
486       System.exit(1);
487     }
488     String vamsasImport = aparser.getValue("vdoc");
489     String vamsasSession = aparser.getValue("vsess");
490     if (vamsasImport != null || vamsasSession != null)
491     {
492       if (desktop == null || headless)
493       {
494         System.out.println(
495                 "Headless vamsas sessions not yet supported. Sorry.");
496         System.exit(1);
497       }
498       // if we have a file, start a new session and import it.
499       boolean inSession = false;
500       if (vamsasImport != null)
501       {
502         try
503         {
504           DataSourceType viprotocol = AppletFormatAdapter
505                   .checkProtocol(vamsasImport);
506           if (viprotocol == DataSourceType.FILE)
507           {
508             inSession = desktop.vamsasImport(new File(vamsasImport));
509           }
510           else if (viprotocol == DataSourceType.URL)
511           {
512             inSession = desktop.vamsasImport(new URL(vamsasImport));
513           }
514
515         } catch (Exception e)
516         {
517           System.err.println("Exeption when importing " + vamsasImport
518                   + " as a vamsas document.");
519           e.printStackTrace();
520         }
521         if (!inSession)
522         {
523           System.err.println("Failed to import " + vamsasImport
524                   + " as a vamsas document.");
525         }
526         else
527         {
528           System.out.println("Imported Successfully into new session "
529                   + desktop.getVamsasApplication().getCurrentSession());
530         }
531       }
532       if (vamsasSession != null)
533       {
534         if (vamsasImport != null)
535         {
536           // close the newly imported session and import the Jalview specific
537           // remnants into the new session later on.
538           desktop.vamsasStop_actionPerformed(null);
539         }
540         // now join the new session
541         try
542         {
543           if (desktop.joinVamsasSession(vamsasSession))
544           {
545             System.out.println(
546                     "Successfully joined vamsas session " + vamsasSession);
547           }
548           else
549           {
550             System.err.println("WARNING: Failed to join vamsas session "
551                     + vamsasSession);
552           }
553         } catch (Exception e)
554         {
555           System.err.println(
556                   "ERROR: Failed to join vamsas session " + vamsasSession);
557           e.printStackTrace();
558         }
559         if (vamsasImport != null)
560         {
561           // the Jalview specific remnants can now be imported into the new
562           // session at the user's leisure.
563           Cache.log.info(
564                   "Skipping Push for import of data into existing vamsas session."); // TODO:
565           // enable
566           // this
567           // when
568           // debugged
569           // desktop.getVamsasApplication().push_update();
570         }
571       }
572     }
573     long progress = -1;
574     // Finally, deal with the remaining input data.
575     if (file != null)
576     {
577       if (!headless)
578       {
579         desktop.setProgressBar(
580                 MessageManager
581                         .getString("status.processing_commandline_args"),
582                 progress = System.currentTimeMillis());
583       }
584       System.out.println("CMD [-open " + file + "] executed successfully!");
585
586       if (!file.startsWith("http://"))
587       {
588         if (!(new File(file)).exists())
589         {
590           System.out.println("Can't find " + file);
591           if (headless)
592           {
593             System.exit(1);
594           }
595         }
596       }
597
598       protocol = AppletFormatAdapter.checkProtocol(file);
599
600       try
601       {
602         format = new IdentifyFile().identify(file, protocol);
603       } catch (FileFormatException e1)
604       {
605         // TODO ?
606       }
607
608       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
609               format);
610       if (af == null)
611       {
612         System.out.println("error");
613       }
614       else
615       {
616         setCurrentAlignFrame(af);
617         data = aparser.getValue("colour", true);
618         if (data != null)
619         {
620           data.replaceAll("%20", " ");
621
622           ColourSchemeI cs = ColourSchemeProperty
623                   .getColourScheme(af.getViewport().getAlignment(), data);
624
625           if (cs != null)
626           {
627             System.out.println(
628                     "CMD [-color " + data + "] executed successfully!");
629           }
630           af.changeColour(cs);
631         }
632
633         // Must maintain ability to use the groups flag
634         data = aparser.getValue("groups", true);
635         if (data != null)
636         {
637           af.parseFeaturesFile(data,
638                   AppletFormatAdapter.checkProtocol(data));
639           // System.out.println("Added " + data);
640           System.out.println(
641                   "CMD groups[-" + data + "]  executed successfully!");
642         }
643         data = aparser.getValue("features", true);
644         if (data != null)
645         {
646           af.parseFeaturesFile(data,
647                   AppletFormatAdapter.checkProtocol(data));
648           // System.out.println("Added " + data);
649           System.out.println(
650                   "CMD [-features " + data + "]  executed successfully!");
651         }
652
653         data = aparser.getValue("annotations", true);
654         if (data != null)
655         {
656           af.loadJalviewDataFile(data, null, null, null);
657           // System.out.println("Added " + data);
658           System.out.println(
659                   "CMD [-annotations " + data + "] executed successfully!");
660         }
661         // set or clear the sortbytree flag.
662         if (aparser.contains("sortbytree"))
663         {
664           af.getViewport().setSortByTree(true);
665           if (af.getViewport().getSortByTree())
666           {
667             System.out.println("CMD [-sortbytree] executed successfully!");
668           }
669         }
670         if (aparser.contains("no-annotation"))
671         {
672           af.getViewport().setShowAnnotation(false);
673           if (!af.getViewport().isShowAnnotation())
674           {
675             System.out.println("CMD no-annotation executed successfully!");
676           }
677         }
678         if (aparser.contains("nosortbytree"))
679         {
680           af.getViewport().setSortByTree(false);
681           if (!af.getViewport().getSortByTree())
682           {
683             System.out
684                     .println("CMD [-nosortbytree] executed successfully!");
685           }
686         }
687         data = aparser.getValue("tree", true);
688         if (data != null)
689         {
690           try
691           {
692             System.out.println(
693                     "CMD [-tree " + data + "] executed successfully!");
694             NewickFile nf = new NewickFile(data,
695                     AppletFormatAdapter.checkProtocol(data));
696             af.getViewport()
697                     .setCurrentTree(af.showNewickTree(nf, data).getTree());
698           } catch (IOException ex)
699           {
700             System.err.println("Couldn't add tree " + data);
701             ex.printStackTrace(System.err);
702           }
703         }
704         // TODO - load PDB structure(s) to alignment JAL-629
705         // (associate with identical sequence in alignment, or a specified
706         // sequence)
707
708         getFeatures = checkDasArguments(aparser);
709         if (af != null && getFeatures != null)
710         {
711           FeatureFetcher ff = startFeatureFetching(getFeatures);
712           if (ff != null)
713           {
714             while (!ff.allFinished() || af.operationInProgress())
715             {
716               // wait around until fetching is finished.
717               try
718               {
719                 Thread.sleep(100);
720               } catch (Exception e)
721               {
722
723               }
724             }
725           }
726           getFeatures = null; // have retrieved features - forget them now.
727         }
728         if (groovyscript != null)
729         {
730           // Execute the groovy script after we've done all the rendering stuff
731           // and before any images or figures are generated.
732           System.out.println("Executing script " + groovyscript);
733           executeGroovyScript(groovyscript, af);
734           System.out.println("CMD groovy[" + groovyscript
735                   + "] executed successfully!");
736           groovyscript = null;
737         }
738         String imageName = "unnamed.png";
739         while (aparser.getSize() > 1)
740         {
741           String outputFormat = aparser.nextValue();
742           file = aparser.nextValue();
743
744           if (outputFormat.equalsIgnoreCase("png"))
745           {
746             af.createPNG(new File(file));
747             imageName = (new File(file)).getName();
748             System.out.println("Creating PNG image: " + file);
749             continue;
750           }
751           else if (outputFormat.equalsIgnoreCase("svg"))
752           {
753             File imageFile = new File(file);
754             imageName = imageFile.getName();
755             af.createSVG(imageFile);
756             System.out.println("Creating SVG image: " + file);
757             continue;
758           }
759           else if (outputFormat.equalsIgnoreCase("html"))
760           {
761             File imageFile = new File(file);
762             imageName = imageFile.getName();
763             HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
764             htmlSVG.exportHTML(file);
765
766             System.out.println("Creating HTML image: " + file);
767             continue;
768           }
769           else if (outputFormat.equalsIgnoreCase("biojsmsa"))
770           {
771             if (file == null)
772             {
773               System.err.println("The output html file must not be null");
774               return;
775             }
776             try
777             {
778               BioJsHTMLOutput.refreshVersionInfo(
779                       BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
780             } catch (URISyntaxException e)
781             {
782               e.printStackTrace();
783             }
784             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
785             bjs.exportHTML(file);
786             System.out
787                     .println("Creating BioJS MSA Viwer HTML file: " + file);
788             continue;
789           }
790           else if (outputFormat.equalsIgnoreCase("imgMap"))
791           {
792             af.createImageMap(new File(file), imageName);
793             System.out.println("Creating image map: " + file);
794             continue;
795           }
796           else if (outputFormat.equalsIgnoreCase("eps"))
797           {
798             File outputFile = new File(file);
799             System.out.println(
800                     "Creating EPS file: " + outputFile.getAbsolutePath());
801             af.createEPS(outputFile);
802             continue;
803           }
804
805           if (af.saveAlignment(file, format))
806           {
807             System.out.println("Written alignment in " + format
808                     + " format to " + file);
809           }
810           else
811           {
812             System.out.println("Error writing file " + file + " in "
813                     + format + " format!!");
814           }
815
816         }
817
818         while (aparser.getSize() > 0)
819         {
820           System.out.println("Unknown arg: " + aparser.nextValue());
821         }
822       }
823     }
824     AlignFrame startUpAlframe = null;
825     // We'll only open the default file if the desktop is visible.
826     // And the user
827     // ////////////////////
828
829     if (!headless && file == null && vamsasImport == null
830             && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
831     {
832       file = jalview.bin.Cache.getDefault("STARTUP_FILE",
833               jalview.bin.Cache.getDefault("www.jalview.org",
834                       "http://www.jalview.org")
835                       + "/examples/exampleFile_2_7.jar");
836       if (file.equals(
837               "http://www.jalview.org/examples/exampleFile_2_3.jar"))
838       {
839         // hardwire upgrade of the startup file
840         file.replace("_2_3.jar", "_2_7.jar");
841         // and remove the stale setting
842         jalview.bin.Cache.removeProperty("STARTUP_FILE");
843       }
844
845       protocol = DataSourceType.FILE;
846
847       if (file.indexOf("http:") > -1)
848       {
849         protocol = DataSourceType.URL;
850       }
851
852       if (file.endsWith(".jar"))
853       {
854         format = FileFormat.Jalview;
855       }
856       else
857       {
858         try
859         {
860           format = new IdentifyFile().identify(file, protocol);
861         } catch (FileFormatException e)
862         {
863           // TODO what?
864         }
865       }
866
867       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
868               format);
869       getFeatures = checkDasArguments(aparser);
870       // extract groovy arguments before anything else.
871     }
872     // If the user has specified features to be retrieved,
873     // or a groovy script to be executed, do them if they
874     // haven't been done already
875     // fetch features for the default alignment
876     if (getFeatures != null)
877     {
878       if (startUpAlframe != null)
879       {
880         startFeatureFetching(getFeatures);
881       }
882     }
883     // Once all other stuff is done, execute any groovy scripts (in order)
884     if (groovyscript != null)
885     {
886       if (Cache.groovyJarsPresent())
887       {
888         System.out.println("Executing script " + groovyscript);
889         executeGroovyScript(groovyscript, startUpAlframe);
890       }
891       else
892       {
893         System.err.println(
894                 "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
895                         + groovyscript);
896       }
897     }
898     // and finally, turn off batch mode indicator - if the desktop still exists
899     if (desktop != null)
900     {
901       if (progress != -1)
902       {
903         desktop.setProgressBar(null, progress);
904       }
905       desktop.setInBatchMode(false);
906     }
907   }
908
909   private static void showUsage()
910   {
911     System.out.println(
912             "Usage: jalview -open [FILE] [OUTPUT_FORMAT] [OUTPUT_FILE]\n\n"
913                     + "-nodisplay\tRun Jalview without User Interface.\n"
914                     + "-props FILE\tUse the given Jalview properties file instead of users default.\n"
915                     + "-colour COLOURSCHEME\tThe colourscheme to be applied to the alignment\n"
916                     + "-annotations FILE\tAdd precalculated annotations to the alignment.\n"
917                     + "-tree FILE\tLoad the given newick format tree file onto the alignment\n"
918                     + "-features FILE\tUse the given file to mark features on the alignment.\n"
919                     + "-fasta FILE\tCreate alignment file FILE in Fasta format.\n"
920                     + "-clustal FILE\tCreate alignment file FILE in Clustal format.\n"
921                     + "-pfam FILE\tCreate alignment file FILE in PFAM format.\n"
922                     + "-msf FILE\tCreate alignment file FILE in MSF format.\n"
923                     + "-pileup FILE\tCreate alignment file FILE in Pileup format\n"
924                     + "-pir FILE\tCreate alignment file FILE in PIR format.\n"
925                     + "-blc FILE\tCreate alignment file FILE in BLC format.\n"
926                     + "-json FILE\tCreate alignment file FILE in JSON format.\n"
927                     + "-jalview FILE\tCreate alignment file FILE in Jalview format.\n"
928                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
929                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
930                     + "-html FILE\tCreate HTML file from alignment.\n"
931                     + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
932                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
933                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
934                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
935                     + "-noquestionnaire\tTurn off questionnaire check.\n"
936                     + "-nonews\tTurn off check for Jalview news.\n"
937                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
938                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
939                     // +
940                     // "-setprop PROPERTY=VALUE\tSet the given Jalview property,
941                     // after all other properties files have been read\n\t
942                     // (quote the 'PROPERTY=VALUE' pair to ensure spaces are
943                     // passed in correctly)"
944                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
945                     + "-dasserver nickname=URL\tAdd and enable a das server with given nickname\n\t\t\t(alphanumeric or underscores only) for retrieval of features for all alignments.\n"
946                     + "\t\t\tSources that also support the sequence command may be specified by prepending the URL with sequence:\n"
947                     + "\t\t\t e.g. sequence:http://localdas.somewhere.org/das/source)\n"
948                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
949                     // +
950                     // "-vdoc vamsas-document\tImport vamsas document into new
951                     // session or join existing session with same URN\n"
952                     // + "-vses vamsas-session\tJoin session with given URN\n"
953                     + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
954                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
955   }
956
957   private static void startUsageStats(final Desktop desktop)
958   {
959     /**
960      * start a User Config prompt asking if we can log usage statistics.
961      */
962     PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
963             "USAGESTATS", "Jalview Usage Statistics",
964             "Do you want to help make Jalview better by enabling "
965                     + "the collection of usage statistics with Google Analytics ?"
966                     + "\n\n(you can enable or disable usage tracking in the preferences)",
967             new Runnable()
968             {
969               @Override
970               public void run()
971               {
972                 Cache.log.debug(
973                         "Initialising googletracker for usage stats.");
974                 Cache.initGoogleTracker();
975                 Cache.log.debug("Tracking enabled.");
976               }
977             }, new Runnable()
978             {
979               @Override
980               public void run()
981               {
982                 Cache.log.debug("Not enabling Google Tracking.");
983               }
984             }, null, true);
985     desktop.addDialogThread(prompter);
986   }
987
988   /**
989    * Locate the given string as a file and pass it to the groovy interpreter.
990    * 
991    * @param groovyscript
992    *          the script to execute
993    * @param jalviewContext
994    *          the Jalview Desktop object passed in to the groovy binding as the
995    *          'Jalview' object.
996    */
997   private void executeGroovyScript(String groovyscript, AlignFrame af)
998   {
999     /**
1000      * for scripts contained in files
1001      */
1002     File tfile = null;
1003     /**
1004      * script's URI
1005      */
1006     URL sfile = null;
1007     if (groovyscript.trim().equals("STDIN"))
1008     {
1009       // read from stdin into a tempfile and execute it
1010       try
1011       {
1012         tfile = File.createTempFile("jalview", "groovy");
1013         PrintWriter outfile = new PrintWriter(
1014                 new OutputStreamWriter(new FileOutputStream(tfile)));
1015         BufferedReader br = new BufferedReader(
1016                 new InputStreamReader(System.in));
1017         String line = null;
1018         while ((line = br.readLine()) != null)
1019         {
1020           outfile.write(line + "\n");
1021         }
1022         br.close();
1023         outfile.flush();
1024         outfile.close();
1025
1026       } catch (Exception ex)
1027       {
1028         System.err.println("Failed to read from STDIN into tempfile "
1029                 + ((tfile == null) ? "(tempfile wasn't created)"
1030                         : tfile.toString()));
1031         ex.printStackTrace();
1032         return;
1033       }
1034       try
1035       {
1036         sfile = tfile.toURI().toURL();
1037       } catch (Exception x)
1038       {
1039         System.err.println(
1040                 "Unexpected Malformed URL Exception for temporary file created from STDIN: "
1041                         + tfile.toURI());
1042         x.printStackTrace();
1043         return;
1044       }
1045     }
1046     else
1047     {
1048       try
1049       {
1050         sfile = new URI(groovyscript).toURL();
1051       } catch (Exception x)
1052       {
1053         tfile = new File(groovyscript);
1054         if (!tfile.exists())
1055         {
1056           System.err.println("File '" + groovyscript + "' does not exist.");
1057           return;
1058         }
1059         if (!tfile.canRead())
1060         {
1061           System.err.println("File '" + groovyscript + "' cannot be read.");
1062           return;
1063         }
1064         if (tfile.length() < 1)
1065         {
1066           System.err.println("File '" + groovyscript + "' is empty.");
1067           return;
1068         }
1069         try
1070         {
1071           sfile = tfile.getAbsoluteFile().toURI().toURL();
1072         } catch (Exception ex)
1073         {
1074           System.err.println("Failed to create a file URL for "
1075                   + tfile.getAbsoluteFile());
1076           return;
1077         }
1078       }
1079     }
1080     try
1081     {
1082       Map<String, Object> vbinding = new HashMap<>();
1083       vbinding.put("Jalview", this);
1084       if (af != null)
1085       {
1086         vbinding.put("currentAlFrame", af);
1087       }
1088       Binding gbinding = new Binding(vbinding);
1089       GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
1090       gse.run(sfile.toString(), gbinding);
1091       if ("STDIN".equals(groovyscript))
1092       {
1093         // delete temp file that we made -
1094         // only if it was successfully executed
1095         tfile.delete();
1096       }
1097     } catch (Exception e)
1098     {
1099       System.err.println("Exception Whilst trying to execute file " + sfile
1100               + " as a groovy script.");
1101       e.printStackTrace(System.err);
1102
1103     }
1104   }
1105
1106   /**
1107    * Check commandline for any das server definitions or any fetchfrom switches
1108    * 
1109    * @return vector of DAS source nicknames to retrieve from
1110    */
1111   private static Vector<String> checkDasArguments(ArgsParser aparser)
1112   {
1113     Vector<String> source = null;
1114     String data;
1115     String locsources = Cache.getProperty(Cache.DAS_LOCAL_SOURCE);
1116     while ((data = aparser.getValue("dasserver", true)) != null)
1117     {
1118       String nickname = null;
1119       String url = null;
1120       int pos = data.indexOf('=');
1121       // determine capabilities
1122       if (pos > 0)
1123       {
1124         nickname = data.substring(0, pos);
1125       }
1126       url = data.substring(pos + 1);
1127       if (url != null && (url.startsWith("http:")
1128               || url.startsWith("sequence:http:")))
1129       {
1130         if (nickname == null)
1131         {
1132           nickname = url;
1133         }
1134         if (locsources == null)
1135         {
1136           locsources = "";
1137         }
1138         else
1139         {
1140           locsources += "\t";
1141         }
1142         locsources = locsources + nickname + "|" + url;
1143         System.err.println(
1144                 "NOTE! dasserver parameter not yet really supported (got args of "
1145                         + nickname + "|" + url);
1146         if (source == null)
1147         {
1148           source = new Vector<>();
1149         }
1150         source.addElement(nickname);
1151       }
1152       System.out.println(
1153               "CMD [-dasserver " + data + "] executed successfully!");
1154     } // loop until no more server entries are found.
1155     if (locsources != null && locsources.indexOf('|') > -1)
1156     {
1157       Cache.log.debug("Setting local source list in properties file to:\n"
1158               + locsources);
1159       Cache.setProperty(Cache.DAS_LOCAL_SOURCE, locsources);
1160     }
1161     while ((data = aparser.getValue("fetchfrom", true)) != null)
1162     {
1163       System.out.println("adding source '" + data + "'");
1164       if (source == null)
1165       {
1166         source = new Vector<>();
1167       }
1168       source.addElement(data);
1169     }
1170     return source;
1171   }
1172
1173   /**
1174    * start a feature fetcher for every alignment frame
1175    * 
1176    * @param dasSources
1177    */
1178   private FeatureFetcher startFeatureFetching(
1179           final Vector<String> dasSources)
1180   {
1181     FeatureFetcher ff = new FeatureFetcher();
1182     AlignFrame afs[] = Desktop.getAlignFrames();
1183     if (afs == null || afs.length == 0)
1184     {
1185       return null;
1186     }
1187     for (int i = 0; i < afs.length; i++)
1188     {
1189       ff.addFetcher(afs[i], dasSources);
1190     }
1191     return ff;
1192   }
1193
1194   public static boolean isHeadlessMode()
1195   {
1196     String isheadless = System.getProperty("java.awt.headless");
1197     if (isheadless != null && isheadless.equalsIgnoreCase("true"))
1198     {
1199       return true;
1200     }
1201     return false;
1202   }
1203
1204   public AlignFrame[] getAlignFrames()
1205   {
1206     return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
1207             : Desktop.getAlignFrames();
1208
1209   }
1210
1211   /**
1212    * Quit method delegates to Desktop.quit - unless running in headless mode
1213    * when it just ends the JVM
1214    */
1215   public void quit()
1216   {
1217     if (desktop != null)
1218     {
1219       desktop.quit();
1220     }
1221     else
1222     {
1223       System.exit(0);
1224     }
1225   }
1226
1227   public static AlignFrame getCurrentAlignFrame()
1228   {
1229     return Jalview.currentAlignFrame;
1230   }
1231
1232   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
1233   {
1234     Jalview.currentAlignFrame = currentAlignFrame;
1235   }
1236 }