/* * StateObject.java * * * ==================================================== Professional Data Security (PDS) http://crypto.brettlee.com ==================================================== Copyright (c) 2009-2011, Brett Lee All rights reserved. Portions Copyright (C) 1995-2008, Sun Microsystems, Inc Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ORGANIZATION nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================================================================= */ package com.brettlee.crypto; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JOptionPane; import java.util.Iterator; import java.util.Random; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * StateObject :: Maintain configuration state of the PDS application, * both in memory and as a serialized object on "disk." * * * Displaying file system paths properly on both Windows and *nix-based * machines requires both knowledge of the host OS and methods that can * return the appropriate path format for the current host. * * Terminology: * * DOS-WIN: The path that DOS/Windows most commonly uses. * C:\My Documents\Myname\ * PDS-DOS: The PDS path format that all DOS/Windows paths are saved in. * /C:/My Documents/Myname/ * * DOS-WIN paths in the StateObject are always in PDS-DOS format. * * On a DOS-WIN machine, PDS-DOS paths are displayed as DOS-WIN paths. * Thus, the "getters" and "setters" of this class need to convert * between the two formats, and do this via two "pathTo...()" methods. * */ class StateObject implements Serializable { private static final long serialVersionUID = 0000000000000000001L; // Formatting String newline = System.getProperty("line.separator"); String fileSeparator = java.io.File.separator; // Our pseudo random number generator static transient Random random = new Random(); // Frequently used patterns static Pattern beginsWithSeparator = Pattern.compile("([/])(.*)$"); static Pattern endsInSeparator = Pattern.compile("(.*)([/\\\\])+$"); static Pattern pdsDosPattern = Pattern.compile("/[A-Z]:(.*)", Pattern.CASE_INSENSITIVE); // Our variables // Don't do what I did in beta 9, and that is change the name of one // of these without providing a way to get the old name out of prior instances. :( // String defaultKeyStore; // Name of default KeyStore String defaultKeyAlias; // Name of default Key String defaultEncryptedFilePath; // Default dir for encrypted files String defaultKeyStoreDir; // Default dir for KeyStores Vector keyStoreSearchPath; // Paths to search for a KeyStore, if // not found via PDS file metadata // Variables with default values // boolean initialInit = false; // First time PDS is started? boolean defaultKeyDefined = false; // User has a default Key/KeyStore? // OptionsDialog transient boolean newDefaultFilePath = false; // Has a new Default File path been defined transient boolean newDefaultKeyStorePath = false; // Has a new Default KeyStore path been defined transient boolean keyStoreDirAdded = false; // Has a KeyStore Dir been added transient boolean keyStoreDirRemoved = false; // Has a KeyStore Dir been removed // Configuration state, action flags, etc. // static transient String runTimePath = null; // Base Directory static transient boolean dosWindows = false; // Running on Windows? static transient boolean fileChooserCancel = false; // Was Cancel option selected static transient boolean authenticationCancel = false; // Was Cancel selected from the Auth Class static transient boolean returnFromKSP = false; // Candidate for refactoring. static transient JEditorPane editorInFocus; // When multiple editor panes are open transient Vector> cryptoState = new Vector>(); /* * This Vector will hold references to all of the CryptoFile objects * that are running in a CryptoEngine thread. * * If an exception were to occur in one of those threads, that exception * will be added to this Vector via the CryptoState class and subsequently * accessed via the reference. * * The framework for this was added shortly after support after each * each CryptoEngine instance was started in a unique Thread, which * allows for multiple CryptoEngine instances to occur simultaneously. * Currently, exceptions in the CryptoEngine threads drop on the floor. * * This Vector, along with the CryptoState class, could be used to * provide an option to cancel an executing thread. * */ // Path conversions - Lightweight Apache::Commons IO // See PDS-DOS notes above for details // static String pathToState( String path ) { return pathToState( path, true ); } static String pathToState( String path, Boolean isDir ) { // System.out.println("pathToState In: " + path); if ( path == null ) return path; Matcher matcher; matcher = beginsWithSeparator.matcher( path ); if ( ! matcher.matches() ) { path = "/" + path; } if ( isDir ) { matcher = endsInSeparator.matcher( path ); if ( ! matcher.matches() ) { path = path + File.separator; } } path = path.replaceAll("\\\\", "/"); // System.out.println("pathToState Out: " + path); return path; } static String pathToDisplay( String path ) { return pathToDisplay( path, true ); } static String pathToDisplay( String path, Boolean isDir ) { // System.out.println("pathToDisplay In: " + path); if ( path == null ) return path; Matcher matcher; Pattern endsInSeparator = Pattern.compile("(.*)([/\\\\])+$"); matcher = endsInSeparator.matcher( path ); if ( ! matcher.matches() ) { if ( isDir ) { path = path + File.separator; } } if ( dosWindows ) { matcher = pdsDosPattern.matcher( path ); if ( matcher.matches() ) { path = path.replaceFirst("/", "").replaceAll("/", "\\\\"); } else { path = path.replaceAll("/", "\\\\"); } } // System.out.println("pathToDisplay Out: " + path); return path; } /* * Our getters and setters */ /* * Running on DOS Windows? * * Used to determine whether "paths" returned by other * getters need to be massaged from PDS-DOS to DOS-WIN. */ static boolean getDosWindows() { return dosWindows; } void setDosWindows( boolean truth ) { dosWindows = truth; } // Initial Initialization? // // Used to determine certain actions that only happen // when the application is "initially" initialized. // ----------------------------------------------------- boolean getInitialInit() { return initialInit; } void setInitialInit( boolean truth ) { initialInit = truth; } // Default KeyStore & Key defined? // // Set to true if a default KeyStore and Key have been defined. // ------------------------------------------------------------ boolean getHaveDefaults() { return defaultKeyDefined; } void setHaveDefaults( boolean truth ) { defaultKeyDefined = truth; } // Default KeyStore - defaultKeyStore // ------------------------------------ String getDefaultKeyStore() { return defaultKeyStore; } void setDefaultKeyStore( String name ) { defaultKeyStore = name; } // Default Key Alias - defaultKeyAlias // ------------------------------------- String getDefaultKeyAlias() { return defaultKeyAlias; } void setDefaultKeyAlias( String alias ) { defaultKeyAlias = alias; } // Default Directory for Encrypted Files - defaultEncDir // // Everybody gets one of these. Created by the application if // not defined. User can redefine. // -------------------------------------------------------------- String getDefaultEncryptedFilePath() { return pathToDisplay( defaultEncryptedFilePath ); } void setDefaultEncryptedFilePath( String location ) { defaultEncryptedFilePath = pathToState( location ); } // Default Directory for KeyStore Files - defaultKeyStoreDir // // Everybody gets one of these. Created by the application if // not defined. User can redefine. // -------------------------------------------------------------- String getDefaultKeyStoreDir() { return pathToDisplay( defaultKeyStoreDir ); } void setDefaultKeyStoreDir( String location ) { defaultKeyStoreDir = pathToState( location ).replaceAll("/^", ""); } // KeyStore Search Path (KSP) - keyStoreSearchPath // // Everybody gets one of these. Created by the application if // not defined. User can redefine. // ------------------------------------------------- Vector getKeyStoreSearchPath() { if ( keyStoreSearchPath == null ) { // System.out.println("Have null keyStoreSearchPath."); keyStoreSearchPath = new Vector(); keyStoreSearchPath.add( getDefaultKeyStoreDir() ); return keyStoreSearchPath; } else { if (! dosWindows ) { return keyStoreSearchPath; } else { Iterator i = keyStoreSearchPath.iterator(); Vector ksPaths = new Vector(); while ( i.hasNext() ) { ksPaths.add ( pathToDisplay( (String) i.next() )); } return ksPaths; } } } void setKeyStoreSearchPath( Vector argVector ) { Vector stateVector = new Vector(); Iterator i = argVector.iterator(); while ( i.hasNext() ) { stateVector.add( pathToState( i.next() )); } /* // Verify System.out.println("Verification Follows:"); i = argStringVector.iterator(); while ( i.hasNext() ) { System.out.println("From the Arg: " + (String) i.next()); } i = newStringVector.iterator(); while ( i.hasNext() ) { System.out.println("From the New: " + (String) i.next()); } */ keyStoreSearchPath = stateVector; } void addPathToKeyStoreSearchPath( String pathToAdd ) { pathToAdd = pathToState( pathToAdd ); boolean match = false; Iterator i = keyStoreSearchPath.iterator(); while ( i.hasNext() ) { if ( ( (String) i.next() ).equals( pathToAdd ) ) { match = true; } } if ( ! match ) { keyStoreSearchPath.add( pathToAdd ); } } // Transient Variables Follow //////////////////////////////////////////////////// // File Chooser Cancel Action // ------------------------------------------------- static void setFileChooserCancel( boolean truth ) { fileChooserCancel = truth; } static boolean getFileChooserCancel() { return fileChooserCancel; } // Authentication Cancel Action // ------------------------------------------------- static void setAuthenticationCancel( boolean truth ) { authenticationCancel = truth; } static boolean getAuthenticationCancel() { return authenticationCancel; } // Key Store Search Path - Success ? // ------------------------------------------------- static void setReturnFromKSP( boolean truth ) { returnFromKSP = truth; } static boolean getReturnFromKSP() { return returnFromKSP; } // Current editor in focus (for UndoManager) // ------------------------------------------------- static void setEditorInFocus( JEditorPane editor ) { editorInFocus = editor; } static JEditorPane getEditorInFocus() { return editorInFocus; } // Run Time path // ------------------------------------------------- static void setRunTimePath( String runTimePathPassed ) { runTimePath = runTimePathPassed; } static String getRunTimePath() { return runTimePath; } // Options Dialog // ------------------------------------------------- void setNewDefaultFilePath( boolean truth ) { newDefaultFilePath = truth; } boolean getNewDefaultFilePath() { return newDefaultFilePath; } void setNewDefaultKeyStorePath( boolean truth ) { newDefaultKeyStorePath = truth; } boolean getNewDefaultKeyStorePath() { return newDefaultKeyStorePath; } void setKeyStoreDirAdded( boolean truth ) { keyStoreDirAdded = truth; } boolean getKeyStoreDirAdded() { return keyStoreDirAdded; } void setKeyStoreDirRemoved( boolean truth ) { keyStoreDirRemoved = truth; } boolean getKeyStoreDirRemoved() { return keyStoreDirRemoved; } // Save State // // Save the in-memory instance of this object to a // serialized object on persistent storage. // ------------------------------------------------- boolean saveState() { /* All paths in the "in memory" StateObject are in PDS-DOS format: /data/project/files/file.PDS -or- /C:/project/files/file.PDS All of the "setters" ensure it. All of the "getters" convert for the view as needed. This method simply writes the StateObject out as a serialized object. */ // System.out.println("Writing to storage: " + runTimePath + "pdsState.serObj"); ObjectOutputStream oos = null; try { // - write object serialized oos = new ObjectOutputStream ( new FileOutputStream( runTimePath + "pdsState.serObj") ); oos.writeObject ( this ); // System.out.println("Success saving current state. "); } catch (Exception ex) { System.out.println("Error - Unable to save the current state. " + ex); System.out.println("Error saving to persistent storage."); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to save state to persistent storage.", "Error", JOptionPane.ERROR_MESSAGE); return false; } finally { try { oos.flush(); oos.close(); } catch (Exception e) { return false; } } return true; } // StateObject Constructor - The Beast // The Constructor continues to the end of this file. // ------------------------------------------------------ public StateObject() { StateObject stateObject = this; // Initialization has been a real PITA. The latest goal is: // // ** For ZIP file installations, the StateObject will be in the same // directory as the pds.jar file. This will allow inserting a // thumb drive into any USB slot and finding the StateObject. // Prior to 1.0beta8, the state file was kept in a "mydata" dir // relative to the pds.jar file. If found there, it is now moved. // // ** For JWS installations, the state file will be in user.home. // Prior to 1.0beta8, the state file *was* kept in: // user.home/Desktop/mydata/ // but sometimes it ended up in: // C:\mydata // If found in either of these, it is moved. // What if both exist? Inverse race condition; the last one wins... :( // // // Better open a debug file to trace the activity... // // FileWriter outDebug = null; // try { // outDebug = new FileWriter( "C:\\pdsDebug.txt" ); // outDebug = new FileWriter( "/tmp/pdsDebug.txt" ); // } catch ( Exception exDebug ){ // System.out.println("Unable to open debug file: " + exDebug); // } // Where are we? if ( new java.io.File("C:\\\\").exists() ) { setDosWindows( true ); } // Find path of the executable Jar. // String pathToExecutableJar = getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); // System.out.println("Raw Java path to executable jar: " + pathToExecutableJar); // Strip off "/pds.jar" (or on CDROM/USB type encoding, "/PDS.JAR") // Pattern pattern = Pattern.compile("(.*)/pds.jar", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher( pathToExecutableJar ); if(matcher.matches()) { // If it matches, then assume this is a ZIP file installation. // System.out.println("Running from manual installation."); // try { // outDebug.write( "Debug- Match on '/pds.jar'" + newline); // outDebug.write( "Debug- Path to /jar is:" + matcher.group(1) + ":" + newline); // outDebug.flush(); // } catch ( Exception exDebug ){ // } // System.out.println("Runtime : " + pathToExecutableJar); runTimePath = matcher.group(1); // System.out.println("Runtime path: " + runTimePath); // Replace spaces with "%20" String regex = "%20"; runTimePath = runTimePath.replaceAll(regex," "); // System.out.println("Runtime path: " + runTimePath); } // try { // outDebug.write( "Debug- Path of jar:" + pathToExecutableJar + ":" + newline); // outDebug.write( "Debug- Run time:" + runTimePath + ":" + newline); // outDebug.flush(); // } catch ( Exception exDebug ){ // } if (( runTimePath == null ) || ( runTimePath.length() == 0 )){ // Java Web Start installation may have a NULL or empty run time path. // In that case, assume Java Web Start and set runTimePath to user.home. // // System.out.println("Running from Java Web Start installation."); runTimePath = System.getProperty("user.home"); // System.out.println("Runtime path: " + runTimePath); // try { // outDebug.write( "Debug- Null/empty runTimePath modified to:" // + runTimePath + ":" + newline); // outDebug.flush(); // } catch ( Exception exDebug ){ // } } // Both JWS and ZIP installations need a file separator appended // runTimePath = runTimePath + fileSeparator; // try { // outDebug.write( "Running with: " + runTimePath + newline); // outDebug.flush(); // outDebug.close(); // } catch ( Exception exDebug ){ // } // /////////////////////////////////////// // We now have the run time path! // /////////////////////////////////////// // Determine if the StateObjects exists as a file - first in the default location: // if ( new java.io.File(runTimePath + "pdsState.serObj").exists() ) { initialInit = false; // System.out.println("StateObject Exists in default location."); // Then in a secondary location. // Yes, this will eventually go away. Once "everyone" has upgraded to 1.0b8x. :) } else { // System.out.println("StateObject was *NOT* found in the default location."); // Track if an **OLD** state file was found // int oldStateFound = 0; String pathToOldState = null; // Check to see if the StateObject exists from a previous execution // // JWS if ( new java.io.File(runTimePath + "Desktop/mydata/pdsState.serObj").exists() ) { oldStateFound = 1; pathToOldState = runTimePath + "Desktop/mydata/pdsState.serObj"; } if ( new java.io.File( "/mydata/pdsState.serObj").exists() ) { oldStateFound = 1; pathToOldState = "/mydata/pdsState.serObj"; } if ( new java.io.File( "/C:/mydata/pdsState.serObj").exists() ) { oldStateFound = 1; pathToOldState = "/C:/mydata/pdsState.serObj"; } // ZIP if ( new java.io.File(runTimePath + "/mydata/pdsState.serObj").exists() ) { oldStateFound = 1; pathToOldState = runTimePath + "/mydata/pdsState.serObj"; } // If file found in the **OLD** location: // Move the file to the new location. // Eliminate the dependency on the "mydata" directory - as of v1.0b8. // if ( oldStateFound == 1 ) { // System.out.println("Found StateObject in alternate."); // Not an initial initialization initialInit = false; // Move (copy + delete) try { // Copy InputStream in = new FileInputStream( pathToOldState ); OutputStream out = new FileOutputStream( runTimePath + "pdsState.serObj" ); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); // Delete the original new File( pathToOldState ).delete(); } catch (Exception e) { System.out.println("Error moving pdsState.serObj to " + runTimePath + newline + e ); } } else { // This is an initial initialization initialInit = true; } } // Is there a state file to work with? // if ( new java.io.File(runTimePath + "pdsState.serObj").exists() ) { // System.out.println("Initial state FILE exists..."); try { ObjectInputStream ois = new ObjectInputStream ( new FileInputStream(runTimePath + "pdsState.serObj") ); Object oisObject = ois.readObject(); ois.close(); if (oisObject instanceof StateObject) { // System.out.println("OIS is a StateObject..."); // Populate the in-memory instance of the StateObject // StateObject oisStateObject = (StateObject) oisObject; // Read in the Default Location for Encrypted Files // if (oisStateObject.getDefaultEncryptedFilePath() instanceof String) { stateObject.setDefaultEncryptedFilePath( pathToState( oisStateObject.getDefaultEncryptedFilePath() )); // System.out.println("Default Dir for Encrypted files: " + stateObject.getDefaultEncryptedFilePath() ); } // Read in the Default Location for KeyStores // if (oisStateObject.getDefaultKeyStoreDir() instanceof String) { stateObject.setDefaultKeyStoreDir( pathToState( oisStateObject.getDefaultKeyStoreDir() )); // System.out.println("Default Dir for KeyStore files: " + stateObject.getDefaultKeyStoreDir() ); } // Read in the Default KeyStore and Key, if appropriate // if ( oisStateObject.getHaveDefaults() ) { // KeyStore Name stateObject.setDefaultKeyStore( oisStateObject.getDefaultKeyStore() ); // System.out.println("Default KeyStore: " + stateObject.getDefaultKeyStore() ); // Key Alias stateObject.setDefaultKeyAlias( oisStateObject.getDefaultKeyAlias() ); // System.out.println("Default Key Alias: " + stateObject.getDefaultKeyAlias() ); // Have Defaults stateObject.setHaveDefaults( oisStateObject.getHaveDefaults() ); // System.out.println("Have a Default Key: " + stateObject.getHaveDefaults() ); } // Read in the Default Search Path // if ( oisStateObject.getKeyStoreSearchPath() instanceof Vector ) { // System.out.println("KeyStore Search Path exists."); stateObject.setKeyStoreSearchPath( oisStateObject.getKeyStoreSearchPath() ); } } } catch (Exception e) { System.out.println("Error loading previous state: " + e); } } /* At this point there *is* a stateObject. * It has either been initialized via a discovered serialized StateObject, * or it is a shiny new StateObject. */ boolean changesMade = false; // Check the Default Location for Encrypted Files // if ( ! ( stateObject.getDefaultEncryptedFilePath() instanceof String )) { // System.out.println("Default Dir for Encrypted files is not defined."); // System.out.println("Defining Default Dir for Encrypted files..."); stateObject.setDefaultEncryptedFilePath( runTimePath + "pdsData" + fileSeparator + "myfiles" + fileSeparator ); // System.out.println("Encrypted files Dir now: " + stateObject.getDefaultEncDir()); changesMade = true; } // Check the Default Location for KeyStores // if ( ! ( stateObject.getDefaultKeyStoreDir() instanceof String )) { // System.out.println("Default Dir for KeyStore files is not defined."); // System.out.println("Defining Default Dir for KeyStore files..."); stateObject.setDefaultKeyStoreDir( runTimePath + "pdsData" + fileSeparator + "mykeys" + fileSeparator ); // System.out.println("Default KeyStore Dir now: " + stateObject.getDefaultKeyStoreDir()); changesMade = true; } // Check the Default Search Path // if ( ! ( stateObject.getKeyStoreSearchPath() instanceof Vector )) { // System.out.println("KeyStore Search Path does not exist."); // System.out.println("Defining KeyStore Search Path..."); Vector myList = new Vector(); myList.add( runTimePath + "pdsData" + fileSeparator + "mykeys" + fileSeparator ); stateObject.setKeyStoreSearchPath(myList); // System.out.println("KeyStore Search Path now: " + stateObject.getKeyStoreSearchPath()); changesMade = true; } // Ensure that the necessary directory structure exists. // Defaults are: // (runTimePath | user.home | user.defined)/pdsData/mykeys // (runTimePath | user.home | user.defined)/pdsData/myfiles // // Create default file directory // if (! ((new File( stateObject.getDefaultEncryptedFilePath() )).mkdirs()) ) { boolean isDir = (new File( stateObject.getDefaultEncryptedFilePath() )).isDirectory(); if ( ! isDir ) { if ( initialInit ) { System.out.println("Cannot create default file directory:" + newline + stateObject.getDefaultEncryptedFilePath() + newline + "Exiting."); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to create the default file directory:" + newline + newline + stateObject.getDefaultEncryptedFilePath() + newline + newline + "Exiting.", "Error", JOptionPane.ERROR_MESSAGE); System.exit(1); } } } // Create default KeyStore directory // if (! ((new File( stateObject.getDefaultKeyStoreDir() )).mkdirs()) ) { boolean isDir = (new File( stateObject.getDefaultKeyStoreDir() )).isDirectory(); if ( ! isDir ) { if ( initialInit ) { System.out.println("Cannot create default KeyStore directory:" + newline + stateObject.getDefaultKeyStoreDir() + newline + "Exiting."); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to create the default KeyStore directory." + newline + newline + stateObject.getDefaultKeyStoreDir() + newline + newline + "Exiting.", "Error", JOptionPane.ERROR_MESSAGE); System.exit(1); } } } // Save any in-memory changes to permanent storage // changesMade = true; // "Temporary" Override. State configuration from previous // releases is (always) suspect. Update and save the new... if ( changesMade ) { // System.out.println("Saving stateObject to storage..."); // Save stateObject if ( ! saveState() ) { System.out.println("Error saving current state to persistent storage:"); System.out.println( runTimePath ); JFrame frame = new JFrame(); JOptionPane.showMessageDialog( frame, "Unable to save the application state." + newline + newline + runTimePath + "pdsState.obj" + newline + newline + "If this message persists, try moving the file above." + newline + newline + "Please report this error and provide console output." + newline + newline, "Error", JOptionPane.ERROR_MESSAGE); if ( initialInit ) { System.exit(1); } } } } }