Clover icon

Coverage Report

  1. Project Clover database Wed Nov 5 2025 13:15:40 GMT
  2. Package jalview.util

File LaunchUtils.java

 

Coverage histogram

../../img/srcFileCovDistChart3.png
52% of files have more coverage

Code metrics

130
258
19
1
759
599
114
0.44
13.58
19
6

Classes

Class Line # Actions
LaunchUtils 44 258 114
0.2751842727.5%
 

Contributing tests

This file is covered by 100 tests. .

Source view

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.util;
22   
23    import java.io.File;
24    import java.io.FileInputStream;
25    import java.io.FileNotFoundException;
26    import java.io.IOException;
27    import java.io.InputStream;
28    import java.lang.management.ManagementFactory;
29    import java.net.MalformedURLException;
30    import java.net.URISyntaxException;
31    import java.net.URL;
32    import java.util.ArrayList;
33    import java.util.Arrays;
34    import java.util.HashSet;
35    import java.util.List;
36    import java.util.Locale;
37    import java.util.Properties;
38    import java.util.Set;
39    import java.util.concurrent.TimeUnit;
40    import java.util.jar.Attributes;
41    import java.util.jar.JarInputStream;
42    import java.util.jar.Manifest;
43   
 
44    public class LaunchUtils
45    {
46    // setting these is LaunchUtils so don't need to import Platform
47    public final static boolean isMac = System.getProperty("os.name")
48    .indexOf("Mac") > -1;
49   
50    public final static boolean isWindows = System.getProperty("os.name")
51    .indexOf("Win") > -1;
52   
53    private static boolean isJS = /** @j2sNative true || */
54    false;
55   
56    public static final String LOGFILE_HANDOVER = "LOGFILE_HANDOVER";
57   
 
58  0 toggle public static void loadChannelProps(File dir)
59    {
60  0 ChannelProperties.loadProps(dir);
61    }
62   
63    private static Properties userPreferences = null;
64   
 
65  0 toggle public static String getUserPreference(String key)
66    {
67  0 if (userPreferences == null)
68    {
69  0 String channelPrefsFilename = ChannelProperties
70    .getProperty("preferences.filename");
71  0 if (channelPrefsFilename == null)
72    {
73  0 return null;
74    }
75  0 File propertiesFile = new File(System.getProperty("user.home"),
76    channelPrefsFilename);
77  0 if (!propertiesFile.exists())
78    {
79  0 return null;
80    }
81  0 try
82    {
83  0 userPreferences = new Properties();
84  0 userPreferences.load(new FileInputStream(propertiesFile));
85    } catch (FileNotFoundException e)
86    {
87    // didn't find user preferences file
88  0 return null;
89    } catch (IOException e)
90    {
91  0 ErrorLog.errPrintln(e.getMessage());
92  0 return null;
93    }
94    }
95  0 return userPreferences.getProperty(key);
96    }
97   
 
98  0 toggle public static boolean getBooleanUserPreference(String key)
99    {
100  0 return Boolean.parseBoolean(getUserPreference(key));
101    }
102   
103    public static int JAVA_COMPILE_VERSION = 0;
104   
 
105  117 toggle public static int getJavaCompileVersion()
106    {
107  117 if (LaunchUtils.isJS)
108    {
109  0 return -1;
110    }
111  117 else if (JAVA_COMPILE_VERSION > 0)
112    {
113  0 return JAVA_COMPILE_VERSION;
114    }
115  117 String buildDetails = "jar:".concat(LaunchUtils.class
116    .getProtectionDomain().getCodeSource().getLocation().toString()
117    .concat("!" + "/.build_properties"));
118  117 try
119    {
120  117 URL localFileURL = new URL(buildDetails);
121  117 InputStream in = HttpUtils.openStream(localFileURL);
122  0 Properties buildProperties = new Properties();
123  0 buildProperties.load(in);
124  0 in.close();
125  0 String JCV = buildProperties.getProperty("JAVA_COMPILE_VERSION",
126    null);
127  0 if (JCV == null)
128    {
129  0 ErrorLog.errPrintln(
130    "Could not obtain JAVA_COMPILE_VERSION for comparison");
131  0 return -2;
132    }
133  0 JAVA_COMPILE_VERSION = Integer.parseInt(JCV);
134    } catch (MalformedURLException e)
135    {
136  0 ErrorLog.errPrintln("Could not find " + buildDetails);
137  0 return -3;
138    } catch (IOException e)
139    {
140  117 ErrorLog.errPrintln("Could not load " + buildDetails);
141  117 return -4;
142    } catch (NumberFormatException e)
143    {
144  0 ErrorLog.errPrintln("Could not parse JAVA_COMPILE_VERSION");
145  0 return -5;
146    }
147   
148  0 return JAVA_COMPILE_VERSION;
149    }
150   
151    public static int JAVA_VERSION = 0;
152   
 
153  581 toggle public static int getJavaVersion()
154    {
155  581 if (LaunchUtils.isJS)
156    {
157  0 return -1;
158    }
159  581 else if (JAVA_VERSION > 0)
160    {
161  527 return JAVA_VERSION;
162    }
163  54 try
164    {
165  54 String JV = System.getProperty("java.version");
166  54 if (JV == null)
167    {
168  0 ErrorLog.errPrintln("Could not obtain java.version for comparison");
169  0 return -2;
170    }
171  54 if (JV.startsWith("1."))
172    {
173  0 JV = JV.substring(2);
174    }
175  54 JAVA_VERSION = JV.indexOf(".") == -1 ? Integer.parseInt(JV)
176    : Integer.parseInt(JV.substring(0, JV.indexOf(".")));
177    } catch (NumberFormatException e)
178    {
179  0 ErrorLog.errPrintln("Could not parse java.version");
180  0 return -3;
181    }
182  54 return JAVA_VERSION;
183    }
184   
 
185  0 toggle public static String getJarPath(Class c)
186    {
187  0 try
188    {
189  0 return c.getProtectionDomain().getCodeSource().getLocation().toURI()
190    .getPath();
191    } catch (URISyntaxException e)
192    {
193  0 ErrorLog.errPrintln("Problem with class source location");
194  0 return null;
195    }
196    }
197   
 
198  117 toggle public static boolean checkJavaVersion()
199    {
200  117 if (LaunchUtils.isJS)
201    {
202  0 return true;
203    }
204  117 String buildDetails = "jar:".concat(LaunchUtils.class
205    .getProtectionDomain().getCodeSource().getLocation().toString()
206    .concat("!" + "/.build_properties"));
207   
208  117 int java_compile_version = getJavaCompileVersion();
209  117 int java_version = getJavaVersion();
210   
211  117 if (java_compile_version <= 0 || java_version <= 0)
212    {
213  117 ErrorLog.errPrintln("Could not make Java version check");
214  117 return true;
215    }
216    // Warn if these java.version and JAVA_COMPILE_VERSION conditions exist
217    // Usually this means a Java 11 compiled JAR being run by a Java 11 JVM
218  0 if (java_version >= 11 && java_compile_version < 11)
219    {
220  0 return false;
221    }
222   
223  0 return true;
224    }
225   
 
226  0 toggle public static String findJavaBin(boolean winConsole)
227    {
228  0 return findJavaBin(System.getProperty("java.home"), winConsole, true,
229    true);
230    }
231   
 
232  0 toggle public static String findJavaBin(boolean winConsole,
233    boolean applicationName, boolean generic)
234    {
235  0 return findJavaBin(System.getProperty("java.home"), winConsole,
236    applicationName, generic);
237    }
238   
239    /*
240    * Returns a string path to the most likely java binary wanted to run this
241    * installation of Jalview.
242    *
243    * @param javaHome Try this javaHome dir (defaults to the running java.home).
244    * @param winConsole whether to use java.exe (console) in preference to javaw.exe
245    * (only affects Windows).
246    * @param applicationName Look to see if the Jalview application name symbolic link is present and use it.
247    * @param generic Return a generic java command if not found.
248    */
 
249  0 toggle public static String findJavaBin(String javaHome, boolean winConsole,
250    boolean applicationName, boolean generic)
251    {
252  0 String javaBin = null;
253  0 final String javaExe = winConsole ? "java.exe" : "javaw.exe";
254  0 final String java = "java";
255   
256  0 if (javaHome != null)
257    {
258  0 String propertyAppName = null;
259  0 String appName = null;
260    // property "channel.app_name" is set by install4j when launching getdown
261  0 if (applicationName)
262    {
263  0 propertyAppName = System.getProperty("channel.app_name");
264  0 appName = (propertyAppName != null && propertyAppName.length() > 0)
265    ? propertyAppName
266    : ChannelProperties.getProperty("app_name");
267    }
268   
269  0 final String javaBinDir = javaHome + File.separator + "bin"
270    + File.separator;
271   
272    // appName and "Jalview" will not point to javaw.exe or java.exe but in
273    // this case that's okay because the taskbar display name problem doesn't
274    // manifest in Windows. See JAL-3820, JAL-4189.
275  0 List<String> potentialJavaBin = new ArrayList<>();
276  0 if (applicationName)
277    {
278  0 if (appName != null)
279    {
280  0 potentialJavaBin.add(appName);
281    }
282  0 if (ChannelProperties.FALLBACK_APPNAME != null)
283    {
284  0 potentialJavaBin.add(ChannelProperties.FALLBACK_APPNAME);
285    }
286    }
287  0 potentialJavaBin.add(java);
288  0 potentialJavaBin.add(javaExe);
289  0 for (String name : potentialJavaBin)
290    {
291  0 if (name == null)
292    {
293  0 continue;
294    }
295  0 if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole))
296    {
297  0 javaBin = javaBinDir + name;
298  0 break;
299    }
300    }
301    }
302   
303  0 if (javaBin == null && generic)
304    {
305  0 javaBin = LaunchUtils.isWindows ? javaExe : java;
306    }
307   
308  0 return javaBin;
309    }
310   
311    /*
312    * checkJVMSymlink returns true if the path in testBin *is* a java binary, or
313    * points to a java binary.
314    * @param testBin The binary or symbolic link to check
315    * @param winConsole whether we are in/want a Windows console (only relevant for Windows,
316    * determines whether we use java.exe or javaw.exe)
317    */
 
318  0 toggle private static boolean checkJVMSymlink(String testBin, boolean winConsole)
319    {
320  0 File testBinFile = new File(testBin);
321  0 if (!testBinFile.exists())
322    {
323  0 return false;
324    }
325  0 File targetFile = null;
326  0 try
327    {
328  0 targetFile = testBinFile.getCanonicalFile();
329    } catch (IOException e)
330    {
331  0 return false;
332    }
333  0 final String javaExe = winConsole ? "java.exe" : "javaw.exe";
334  0 if (targetFile != null && ("java".equals(targetFile.getName())
335    || javaExe.equals(targetFile.getName())))
336    {
337  0 return true;
338    }
339  0 return false;
340    }
341   
342    /**
343    * Create a java command that matches the currently running java process and
344    * optionally remove/add some JVM and application parameters.
345    *
346    * @param String
347    * javaBinary The java binary to use. null uses the same as current
348    * process.
349    * @param String[]
350    * removeJvmArguments The (startsWith) JVM arguments to remove.
351    * @param String[]
352    * addJvmArguments JVM arguments to add.
353    * @param String[]
354    * prependToClasspath Add these dirs to the start of the classpath
355    * @param String[]
356    * appendToClasspath Add these dirs to the end of the classpath
357    * @param String[]
358    * deleteFromClasspath Remove these dirs from the existing classpath
359    * @param String
360    * startClass The name of the start class if different. null if the
361    * same.
362    * @param String[]
363    * removeAppArguments The (startsWith) application arguments to
364    * remove from appArguments.
365    * @param String[]
366    * addAppArguments Application arguments to add.
367    * @param String[]
368    * appArguments The initial list of application arguments.
369    * @param boolean
370    * terminate Flag to terminate this process after starting new
371    * process.
372    */
 
373  0 toggle public static int startNewJvm(String javaBinary,
374    List<String> removeJvmArguments, List<String> addJvmArguments,
375    List<String> prependToClasspath, List<String> appendToClasspath,
376    List<String> removeFromClasspath, String startClass,
377    List<String> removeAppArguments, List<String> addAppArguments,
378    List<String> appArguments, boolean launcherprint,
379    boolean launcherwait, boolean launcherstop, boolean debug,
380    boolean quiet)
381    {
382  0 if (javaBinary == null)
383    {
384  0 javaBinary = findJavaBin(false, true, true);
385    }
386   
387  0 List<String> classpathDirs = new ArrayList<>();
388  0 if (prependToClasspath != null)
389    {
390  0 classpathDirs.addAll(prependToClasspath);
391    }
392   
393  0 String classpath = ManagementFactory.getRuntimeMXBean().getClassPath();
394  0 if (removeFromClasspath != null)
395    {
396  0 Set<String> removeCp = new HashSet<>();
397  0 for (String dcp : removeFromClasspath)
398    {
399  0 try
400    {
401  0 String canPath = new File(dcp).getCanonicalPath();
402  0 removeCp.add(canPath);
403    } catch (IOException e)
404    {
405  0 ErrorLog.errPrintln(
406    "Problem getting canonical path. " + e.getMessage());
407    }
408    }
409  0 for (String cp : classpath.split(File.pathSeparator))
410    {
411  0 try
412    {
413  0 String canPath = new File(cp).getCanonicalPath();
414  0 if (!removeCp.contains(canPath))
415    {
416  0 classpathDirs.add(cp);
417    }
418    } catch (IOException e)
419    {
420  0 ErrorLog.errPrintln(
421    "Problem getting canonical path. " + e.getMessage());
422    }
423    }
424    }
425    else
426    {
427  0 classpathDirs
428    .addAll(Arrays.asList(classpath.split(File.pathSeparator)));
429    }
430  0 if (appendToClasspath != null)
431    {
432  0 classpathDirs.addAll(appendToClasspath);
433    }
434   
435  0 List<String> jvmArguments = new ArrayList<>();
436  0 List<String> originalJvmArguments = ManagementFactory.getRuntimeMXBean()
437    .getInputArguments();
438  0 if (removeJvmArguments != null && removeJvmArguments.size() > 0)
439    {
440  0 for (String jvmArg : originalJvmArguments)
441    {
442  0 boolean addArg = true;
443  0 for (String rmArg : removeJvmArguments)
444    {
445  0 if (jvmArg.startsWith(rmArg))
446    {
447  0 addArg = false;
448  0 break;
449    }
450    }
451  0 if (addArg)
452    {
453  0 jvmArguments.add(jvmArg);
454    }
455    }
456    }
457    else
458    {
459  0 jvmArguments.addAll(originalJvmArguments);
460    }
461  0 if (addJvmArguments != null)
462    {
463  0 jvmArguments.addAll(addJvmArguments);
464    }
465   
466  0 if (startClass == null)
467    {
468    // this isn't always reliable
469  0 startClass = System.getProperty("sun.java.command");
470    }
471   
472  0 List<String> applicationArguments = new ArrayList<>();
473  0 if (removeAppArguments != null && removeAppArguments.size() > 0)
474    {
475  0 Set<String> removeArgs = new HashSet<>(removeAppArguments);
476  0 for (String appArg : appArguments)
477    {
478  0 if (!removeArgs.contains(appArg))
479    {
480  0 applicationArguments.add(appArg);
481    }
482    }
483    }
484    else
485    {
486  0 applicationArguments.addAll(appArguments);
487    }
488  0 if (addAppArguments != null)
489    {
490  0 applicationArguments.addAll(addAppArguments);
491    }
492   
493  0 List<String> command = new ArrayList<>();
494    // java command
495  0 command.add(javaBinary);
496   
497    // classpath
498  0 command.add("-cp");
499  0 command.add(String.join(File.pathSeparator, classpathDirs));
500   
501    // jvm args
502  0 command.addAll(jvmArguments);
503   
504    // start class
505  0 command.add(startClass);
506   
507    // application args
508  0 command.addAll(applicationArguments);
509   
510  0 return runProcess(command, launcherprint, launcherwait, launcherstop,
511    debug, quiet);
512    }
513   
 
514  0 toggle private static int runProcess(List<String> command, boolean launcherprint,
515    boolean launcherwait, boolean launcherstop, boolean debug,
516    boolean quiet)
517    {
518  0 final ProcessBuilder builder = new ProcessBuilder(command);
519  0 Process p = null;
520  0 int exitValue = -1;
521   
522  0 if (Boolean.parseBoolean(System.getProperty("launcherprint", "false"))
523    || launcherprint)
524    {
525  0 syserr(debug, quiet, "COMMAND : \""
526    + String.join("\" \"", builder.command()) + "\"");
527    }
528   
529  0 if (Boolean.parseBoolean(System.getProperty("launcherstop", "false"))
530    || (debug && launcherstop))
531    {
532  0 syserr(debug, quiet,
533    "System property 'launcherstop' is set and not 'false'. Exiting.");
534  0 System.exit(0);
535    }
536  0 try
537    {
538  0 builder.inheritIO();
539  0 Process process = builder.start();
540  0 p = process;
541  0 if (launcherwait)
542    {
543  0 syserr(debug, quiet, "Launching application process");
544  0 exitValue = process.waitFor();
545  0 syserr(debug, quiet,
546    "Application process return with value " + exitValue);
547    }
548    else
549    {
550  0 int waitInt = 0;
551  0 syserr(debug, quiet,
552    "Wait time for application process is " + waitInt + "ms");
553  0 if (process.waitFor(waitInt, TimeUnit.MILLISECONDS))
554    {
555  0 exitValue = process.exitValue();
556    }
557    else
558    {
559  0 exitValue = -2;
560    }
561    }
562  0 syserr(debug, quiet, "Launcher process ending");
563    } catch (IOException e)
564    {
565  0 if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory"))
566    {
567  0 syserr(true, quiet, "Caught a memory exception: " + e.getMessage());
568    // Probably the "Cannot allocate memory" error, try without the memory
569    // setting
570  0 ArrayList<String> commandNoMem = new ArrayList<>();
571  0 for (int i = 0; i < command.size(); i++)
572    {
573  0 if (!command.get(i).startsWith("-Xmx"))
574    {
575  0 commandNoMem.add(command.get(i));
576    }
577    }
578  0 final ProcessBuilder builderNoMem = new ProcessBuilder(
579    commandNoMem);
580  0 syserr(true, quiet, "Command without memory setting: "
581    + String.join(" ", builderNoMem.command()));
582  0 try
583    {
584  0 builderNoMem.inheritIO();
585  0 Process processNoMem = builderNoMem.start();
586  0 p = processNoMem;
587  0 exitValue = processNoMem.waitFor();
588    } catch (Exception ex)
589    {
590  0 ex.printStackTrace();
591    }
592    }
593    else
594    {
595  0 e.printStackTrace();
596    }
597    } catch (Exception e)
598    {
599  0 e.printStackTrace();
600    }
601  0 try
602    {
603  0 p.getOutputStream().flush();
604    } catch (IOException e)
605    {
606    }
607  0 return exitValue;
608    }
609   
610    /**
611    * Look for Implementation-Version in two jar manifests and compare according
612    * to the getdown-launcher version spec (1.8.3-1.4.0_JVL or _FJVL) returns -1
613    * if f0 is newer (or more "valid"), +1 if f1 is newer (or more "valid"), 0 if
614    * the same or both equally "invalid"
615    *
616    * @param f0
617    * @param f1
618    * @return int
619    */
 
620  12 toggle public static int compareGetdownLauncherJarVersions(File f0, File f1)
621    {
622  12 String v0 = f0.exists() ? getJarImplementationVersion(f0) : null;
623  12 return compareGetdownLauncherJarVersions(v0, f1);
624    }
625   
626    // Include a (String, File) comparison to allow comparison of jar version with
627    // built-in version
 
628  12 toggle public static int compareGetdownLauncherJarVersions(String v0, File f1)
629    {
630  12 String v1 = f1.exists() ? getJarImplementationVersion(f1) : null;
631  12 return compareGetdownLauncherJarVersions(v0, v1);
632    }
633   
 
634  26 toggle public static int compareGetdownLauncherJarVersions(String v0, String v1)
635    {
636  26 syserr(v0 != null && !v0.equals(v1), false,
637    "Got launcher versions '" + v0 + "' and '" + v1 + "'");
638   
639  26 if (v0 == null && v1 == null)
640    {
641  0 return 0;
642    }
643  26 if (v0 == null)
644    {
645  0 return -1;
646    }
647  26 if (v1 == null)
648    {
649  0 return 1;
650    }
651   
652    // remove the subscript
653  26 if (v0.endsWith("JVL"))
654    {
655  14 v0 = v0.substring(0, v0.lastIndexOf('_'));
656    }
657  26 if (v1.endsWith("JVL"))
658    {
659  14 v1 = v1.substring(0, v1.lastIndexOf('_'));
660    }
661   
662  26 String[] v0parts = v0.split("-");
663  26 String[] v1parts = v1.split("-");
664   
665  26 int compare = 0;
666  49 for (int j = 0; j < Math.min(v0parts.length, v1parts.length); j++)
667    {
668  40 compare = compareVersions(v0parts[j], v1parts[j]);
669  40 if (compare != 0)
670    {
671  17 return compare;
672    }
673    }
674   
675  9 return v0parts.length - v1parts.length;
676    }
677   
678    /**
679    * comparing versions numbers of the form 1.2.3.4...n ONLY returns 0 if v0 and
680    * v1 are the same, a negative number if v0 < v1 and a positive number if v0 >
681    * v1. The number returned does NOT show how far apart the version numbers
682    * are.
683    */
 
684  49 toggle public static int compareVersions(String v0, String v1)
685    {
686  49 if (v0 == null && v1 == null)
687    {
688  0 return 0;
689    }
690  49 if (v0 == null)
691    {
692  0 return -1;
693    }
694  49 if (v1 == null)
695    {
696  0 return 1;
697    }
698  49 String[] v0dots = v0.split("\\.");
699  49 String[] v1dots = v1.split("\\.");
700  49 int compare = 0;
701  159 for (int i = 0; i < Math.min(v0dots.length, v1dots.length); i++)
702    {
703  129 if (!v0dots[i].equals(v1dots[i])) // avoids unnecessary
704    // NumberFormatException
705    {
706  19 try
707    {
708  19 compare = Integer.parseInt(v0dots[i])
709    - Integer.parseInt(v1dots[i]);
710    } catch (NumberFormatException e)
711    {
712  4 syserr(true, false, "Couldn't parse one of '" + v0dots[i]
713    + "' or '" + v1dots[i] + "': " + e.getMessage());
714  4 syserr(true, false, "Comparing as strings.");
715  4 compare = v0dots[i].compareTo(v1dots[i]);
716    }
717  19 if (compare != 0)
718    {
719  19 return compare;
720    }
721    }
722    }
723    // all numbers match up to min length. If one has more dots, assume it's
724    // a greater version (e.g. 1.3.2 > 1.3)
725  30 return v0dots.length - v1dots.length;
726    }
727   
 
728  28 toggle public static String getJarImplementationVersion(File jarFile)
729    {
730  28 String implementationVersion = null;
731  28 try
732    {
733  28 JarInputStream j0 = new JarInputStream(new FileInputStream(jarFile));
734  28 Manifest m0 = j0.getManifest();
735  28 if (m0 == null)
736    {
737  0 System.err.println("No manifest in " + jarFile.getAbsolutePath());
738    }
739    else
740    {
741  28 implementationVersion = m0.getMainAttributes()
742    .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
743    }
744    } catch (IOException e)
745    {
746  0 System.err.println("Exception opening " + jarFile.getAbsolutePath()
747    + " to check version: " + e.getMessage());
748    }
749  28 return implementationVersion;
750    }
751   
 
752  48 toggle public static void syserr(boolean debug, boolean quiet, String message)
753    {
754  48 if (debug && !quiet)
755    {
756  26 ErrorLog.errPrintln("DEBUG - " + message);
757    }
758    }
759    }