001/*
002 * #%L
003 * Netarchivesuite - deploy
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023package dk.netarkivet.deploy;
024
025import java.io.File;
026import java.io.IOException;
027import java.io.PrintWriter;
028import java.util.HashMap;
029import java.util.Map;
030
031import org.dom4j.Element;
032
033import dk.netarkivet.common.exceptions.IOFailure;
034
035/**
036 * A WindowsMachine is the instance of the abstract machine class, which runs the operating system Windows. This class
037 * only contains the operating system specific functions.
038 */
039public class WindowsMachine extends Machine {
040
041    /**
042     * The constructor. Starts by initializing the parent abstract class, then sets the operating system dependent
043     * variables.
044     *
045     * @param root The XML root element.
046     * @param parentSettings The Settings to be inherited from the PhysicalLocation, where this machine is placed.
047     * @param param The machine parameters to be inherited from the PhysicalLocation.
048     * @param netarchiveSuiteSource The name of the NetarchiveSuite package file. Must be '.zip'.
049     * @param securityPolicy The security policy file, to be copied into machine directory.
050     * @param dbFile The harvest definition database file.
051     * @param arcdbFile The archive database.
052     * @param resetDir Whether the temporary directory should be reset.
053     * @param externalJarFolder The folder containing the external jar library files.
054     */
055    public WindowsMachine(Element root, XmlStructure parentSettings, Parameters param, String netarchiveSuiteSource,
056            File slf4JConfig, File securityPolicy, File dbFile, File arcdbFile, boolean resetDir,
057            File externalJarFolder) {
058        super(root, parentSettings, param, netarchiveSuiteSource, slf4JConfig, securityPolicy, dbFile,
059                arcdbFile, resetDir, externalJarFolder);
060        // set operating system
061        operatingSystem = Constants.OPERATING_SYSTEM_WINDOWS_ATTRIBUTE;
062        scriptExtension = Constants.SCRIPT_EXTENSION_WINDOWS;
063    }
064
065    /**
066     * On windows machines console output can cause problems so any uses of ConsoleHandler are
067     * removed.
068     *
069     * @param logProperties the original contents of the logging properties file.
070     * @return logging properties with the ConsoleHandler removed
071     */
072    /* Is this necessaray with the logback?
073     @Override
074     protected String modifyLogProperties(String logProperties) {
075        String output;
076        // First delete any instances followed by a comma - ie
077        // not as last handler
078        output = logProperties.replaceAll("java.util.logging.ConsoleHandler\\s*,", "");
079        // Now delete any instances where ConsoleHandler is declared
080        // last
081        output = output.replaceAll(",\\s*java.util.logging.ConsoleHandler", "");
082        return output;
083    } */
084
085    /**
086     * Creates the operation system specific installation script for this machine.
087     * <p>
088     * Pseudo code: - echo copying 'NetarchiveSuite.zip' to: 'machine' - scp 'NetarchiveSuite.zip' 'login'@'machine': -
089     * echo unzipping 'NetarchiveSuite.zip' at: 'machine' - ssh 'login'@'machine' 'unzip' 'environmentName' -o
090     * 'NetarchiveSuite.zip - $'create directories'. - echo preparing for copying of settings and scripts - if [ $( ssh
091     * 'login'@'machine' cmd /c if exist 'environmentName'\\conf\\jmxremote.password echo 1 ) ]; then echo Y | ssh
092     * 'login'@'machine' cmd /c cacls 'environmentName'\\conf\\jmxremote.password /P BITARKIV\\'login':F; fi - if [ $(
093     * ssh 'login'@'machine' cmd /c if exist 'environmentName'\\conf\\jmxremote.access echo 1 ) ]; then echo Y | ssh
094     * 'login'@'machine' cmd /c cacls 'environmentName'\\conf\\jmxremote.access /P BITARKIV\\'login':F; fi - echo
095     * copying settings and scripts - scp -r 'machine'/* 'login'@'machine':'environmentName'\\conf\\ - $'apply database
096     * script' - echo make password files readonly * if 'jmxremote-password-path' != 'jmxremote-password-defaultpath' -
097     * ssh 'login'@'machine' move /Y 'jmxremote-password-defaultpath' 'jmxremote-password-path' * if
098     * 'jmxremote-access-path' != 'jmxremote-access-defaultpath' - ssh 'login'@'machine' move /Y
099     * 'jmxremote-access-defaultpath' 'jmxremote-access-path' - echo Y | ssh 'login'@'machine' cmd /c cacls
100     * 'environmentName'\\'jmxremote-password-path' /P BITARKIV\\'login':R - echo Y | ssh 'login'@'machine' cmd /c cacls
101     * 'environmentName'\\'jmxremote-access-path' /P BITARKIV\\'login':R
102     * <p>
103     * variables: 'NetarchiveSuite.zip' = The NetarchiveSuitePackage with '.zip' extension. 'machine' = The machine
104     * name. 'login' = The username for the machine. 'unzip' = The command for unzipping. 'environmentName' = the
105     * environmentName from the configuration file.
106     * <p>
107     * $'...' = call other function.
108     *
109     * @return Operation system specific part of the installscript
110     */
111    @Override
112    protected String osInstallScript() {
113        StringBuilder res = new StringBuilder();
114        // - echo copying 'NetarchiveSuite.zip' to: 'machine'
115        res.append(ScriptConstants.ECHO_COPYING + Constants.SPACE);
116        res.append(netarchiveSuiteFileName);
117        res.append(Constants.SPACE + ScriptConstants.TO + Constants.COLON + Constants.SPACE);
118        res.append(hostname);
119        res.append(Constants.NEWLINE);
120        // - scp 'NetarchiveSuite.zip' 'login'@'machine':
121        res.append(ScriptConstants.SCP + Constants.SPACE);
122        res.append(netarchiveSuiteFileName);
123        res.append(Constants.SPACE);
124        res.append(machineUserLogin());
125        res.append(Constants.COLON);
126        res.append(Constants.NEWLINE);
127        // echo removing old libraries if they exist.
128        res.append(ScriptConstants.ECHO_DELETING_OLD_LIBRARIES);
129        res.append(Constants.NEWLINE);
130
131        // ssh 'login'@'machine' cmd /c if exist
132        // 'environmentName'\\'lib' del /Q 'environmentName'\\'lib'
133
134        res.append(ScriptConstants.SSH + Constants.SPACE);
135        res.append(machineUserLogin());
136        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.IF
137                + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE);
138        res.append(ScriptConstants.doubleBackslashes(getLocalLibDirPath()) + Constants.SPACE + "DEL /Q"
139                + Constants.SPACE);
140        res.append(ScriptConstants.doubleBackslashes(getLocalLibDirPath()));
141        res.append(Constants.SPACE + Constants.NEWLINE);
142
143        // - echo unzipping 'NetarchiveSuite.zip' at: 'machine'
144        res.append(ScriptConstants.ECHO_UNZIPPING + Constants.SPACE);
145        res.append(netarchiveSuiteFileName);
146        res.append(Constants.SPACE + ScriptConstants.AT + Constants.COLON + Constants.SPACE);
147        res.append(hostname);
148        res.append(Constants.NEWLINE);
149        // - ssh 'login'@'machine' 'unzip' 'environmentName' -o
150        // 'NetarchiveSuite.zip
151        res.append(ScriptConstants.SSH + Constants.SPACE);
152        res.append(machineUserLogin());
153        res.append(Constants.SPACE + ScriptConstants.WINDOWS_UNZIP_COMMAND + Constants.SPACE);
154        res.append(getEnvironmentName());
155        res.append(Constants.SPACE + ScriptConstants.SCRIPT_OUTPUT + Constants.SPACE);
156        res.append(netarchiveSuiteFileName);
157        res.append(Constants.NEWLINE);
158        // - $'create directories'.
159        res.append(osInstallScriptCreateDir());
160        // - echo preparing for copying of settings and scripts
161        res.append(ScriptConstants.ECHO_PREPARING_FOR_COPY);
162        res.append(Constants.NEWLINE);
163        // - if [ $( ssh 'login'@'machine' cmd /c if exist
164        // 'environmentName'\\'jmxremote.password' echo 1 ) ]; then echo Y
165        // | ssh 'login'@'machine' cmd /c cacls
166        // 'environmentName'\\'jmxremote.password' /P BITARKIV\\'login':F; fi
167        res.append(ScriptConstants.IF + Constants.SPACE + Constants.SQUARE_BRACKET_BEGIN + Constants.SPACE
168                + Constants.DOLLAR_SIGN + Constants.BRACKET_BEGIN + Constants.SPACE + ScriptConstants.SSH
169                + Constants.SPACE);
170        res.append(machineUserLogin());
171        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.IF
172                + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE);
173        res.append(ScriptConstants.doubleBackslashes(getLocalConfDirPath()));
174        res.append(Constants.JMX_PASSWORD_FILE_NAME);
175        res.append(Constants.SPACE + ScriptConstants.ECHO_ONE);
176        res.append(Constants.SPACE + Constants.BRACKET_END + Constants.SPACE + Constants.SQUARE_BRACKET_END
177                + Constants.SEMICOLON + Constants.SPACE + ScriptConstants.THEN + Constants.SPACE);
178        res.append(ScriptConstants.ECHO_Y + Constants.SPACE + Constants.SEPARATOR + Constants.SPACE
179                + ScriptConstants.SSH + Constants.SPACE);
180        res.append(machineUserLogin());
181        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.CACLS
182                + Constants.SPACE);
183        res.append(ScriptConstants.doubleBackslashes(getLocalConfDirPath()));
184        res.append(Constants.JMX_PASSWORD_FILE_NAME + Constants.SPACE + ScriptConstants.SLASH_P + Constants.SPACE
185                + ScriptConstants.BITARKIV_BACKSLASH_BACKSLASH);
186        res.append(machineParameters.getMachineUserName().getText().trim());
187        res.append(ScriptConstants.COLON_F + Constants.SEMICOLON + Constants.SPACE + ScriptConstants.FI
188                + Constants.SEMICOLON);
189        res.append(Constants.NEWLINE);
190        // - if [ $( ssh 'login'@'machine' cmd /c if exist
191        // 'environmentName'\\'jmxremote.access' echo 1 ) ]; then echo Y
192        // | ssh 'login'@'machine' cmd /c cacls
193        // 'environmentName'\\'jmxremote.access' /P BITARKIV\\'login':F; fi
194        res.append(ScriptConstants.IF + Constants.SPACE + Constants.SQUARE_BRACKET_BEGIN + Constants.SPACE
195                + Constants.DOLLAR_SIGN + Constants.BRACKET_BEGIN + Constants.SPACE + ScriptConstants.SSH
196                + Constants.SPACE);
197        res.append(machineUserLogin());
198        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.IF
199                + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE);
200        res.append(ScriptConstants.doubleBackslashes(getLocalConfDirPath()));
201        res.append(Constants.JMX_ACCESS_FILE_NAME);
202        res.append(Constants.SPACE + ScriptConstants.ECHO_ONE);
203        res.append(Constants.SPACE + Constants.BRACKET_END + Constants.SPACE + Constants.SQUARE_BRACKET_END
204                + Constants.SEMICOLON + Constants.SPACE + ScriptConstants.THEN + Constants.SPACE
205                + ScriptConstants.ECHO_Y + Constants.SPACE);
206        res.append(Constants.SEPARATOR + Constants.SPACE + ScriptConstants.SSH + Constants.SPACE);
207        res.append(machineUserLogin());
208        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.CACLS
209                + Constants.SPACE);
210        res.append(ScriptConstants.doubleBackslashes(getLocalConfDirPath()));
211        res.append(Constants.JMX_ACCESS_FILE_NAME + Constants.SPACE + ScriptConstants.SLASH_P + Constants.SPACE
212                + ScriptConstants.BITARKIV_BACKSLASH_BACKSLASH);
213        res.append(machineParameters.getMachineUserName().getText().trim());
214        res.append(ScriptConstants.COLON_F + Constants.SEMICOLON + Constants.SPACE + ScriptConstants.FI
215                + Constants.SEMICOLON);
216        res.append(Constants.NEWLINE);
217        // - echo copying settings and scripts
218        res.append(ScriptConstants.ECHO_COPY_SETTINGS_AND_SCRIPTS);
219        res.append(Constants.NEWLINE);
220        // - scp -r 'machine'/* 'login'@'machine':'environmentName'\\conf\\
221        res.append(ScriptConstants.SCP + Constants.SPACE + ScriptConstants.DASH_R + Constants.SPACE);
222        res.append(hostname);
223        res.append(Constants.SLASH + Constants.STAR + Constants.SPACE);
224        res.append(machineUserLogin());
225        res.append(Constants.COLON);
226        res.append(ScriptConstants.doubleBackslashes(getLocalConfDirPath()));
227        res.append(Constants.NEWLINE);
228        // INSTALL EXTERNAL JAR FILES.
229        res.append(osInstallExternalJarFiles());
230        // APPLY DATABASE!
231        res.append(osInstallDatabase());
232        // APPLY ARCHIVE DATABASE!
233        res.append(osInstallArchiveDatabase());
234        // HANDLE JMXREMOTE PASSWORD AND ACCESS FILE.
235        res.append(getJMXremoteFilesCommand());
236        // END OF SCRIPT
237        return res.toString();
238    }
239
240    /**
241     * Creates the operation system specific killing script for this machine.
242     * <p>
243     * pseudocode: - ssh 'login'@'machine' cmd /c 'environmentName'\\conf\\killall.bat
244     * <p>
245     * variables: 'login' = machine user name 'machine' = name of the machine 'environmentName' = the environmentName
246     * from configuration.
247     *
248     * @return Operation system specific part of the killscript.
249     */
250    @Override
251    protected String osKillScript() {
252        StringBuilder res = new StringBuilder();
253        res.append(ScriptConstants.SSH + Constants.SPACE);
254        res.append(machineUserLogin());
255        res.append(Constants.SPACE + Constants.QUOTE_MARK + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE
256                + Constants.SPACE);
257        res.append(getLocalConfDirPath());
258        res.append(Constants.SCRIPT_NAME_KILL_ALL);
259        res.append(scriptExtension);
260        res.append(Constants.SPACE + Constants.QUOTE_MARK + Constants.SPACE);
261        res.append(Constants.NEWLINE);
262        return res.toString();
263    }
264
265    /**
266     * Creates the operation system specific starting script for this machine.
267     * <p>
268     * pseudocode: - ssh 'login'@'machine' cmd /c 'environmentName'\\conf\\startall.bat - sleep 5 - ssh
269     * 'login'@'machine' "more 'environmentName'\\start_APP.log
270     * <p>
271     * variables: 'login' = machine user name 'machine' = name of the machine 'environmentName' = the environmentName
272     * from configuration.
273     *
274     * @return Operation system specific part of the startscript.
275     */
276    @Override
277    protected String osStartScript() {
278        StringBuilder res = new StringBuilder();
279        // - ssh 'login'@'machine' cmd /c 'environmentName'\\conf\\startall.bat
280        res.append(ScriptConstants.SSH + Constants.SPACE);
281        res.append(machineUserLogin());
282        res.append(Constants.SPACE + Constants.QUOTE_MARK + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE
283                + Constants.SPACE);
284        res.append(getLocalConfDirPath());
285        res.append(Constants.SCRIPT_NAME_START_ALL);
286        res.append(scriptExtension);
287        res.append(Constants.SPACE + Constants.QUOTE_MARK + Constants.SPACE);
288        res.append(Constants.NEWLINE);
289        // - sleep 5
290        res.append(ScriptConstants.SLEEP_5);
291        res.append(Constants.NEWLINE);
292        // - ssh 'login'@'machine' "more 'environmentName'\\start_APP.log"
293        for (Application app : applications) {
294            res.append(ScriptConstants.SSH + Constants.SPACE);
295            res.append(machineUserLogin());
296            res.append(Constants.SPACE + Constants.QUOTE_MARK + ScriptConstants.WINDOWS_COMMAND_TYPE + Constants.SPACE);
297            res.append(getEnvironmentName() + Constants.BACKSLASH + Constants.SCRIPT_NAME_LOCAL_START
298                    + app.getIdentification() + Constants.EXTENSION_LOG_FILES);
299            res.append(Constants.QUOTE_MARK + Constants.SPACE);
300            res.append(Constants.NEWLINE);
301        }
302
303        return res.toString();
304    }
305
306    @Override
307    protected String getInstallDirPath() {
308        return machineParameters.getInstallDirValue() + Constants.BACKSLASH + getEnvironmentName();
309    }
310
311    @Override
312    protected String getConfDirPath() {
313        return getInstallDirPath() + Constants.CONF_DIR_WINDOWS;
314    }
315
316    @Override
317    protected String getLibDirPath() {
318        return getInstallDirPath() + Constants.LIB_DIR_WINDOWS;
319    }
320
321    /**
322     * Creates the local path to the conf dir.
323     *
324     * @return The path to the conf dir for ssh.
325     */
326    protected String getLocalConfDirPath() {
327        return getEnvironmentName() + Constants.CONF_DIR_WINDOWS;
328    }
329
330    /**
331     * Creates the local path to the lib dir.
332     *
333     * @return The path to the lib dir for ssh.
334     */
335    protected String getLocalLibDirPath() {
336        return getEnvironmentName() + Constants.LIB_DIR_WINDOWS;
337    }
338
339    /**
340     * Creates the local path to the installation directory.
341     *
342     * @return The path to the installation directory for ssh.
343     */
344    protected String getLocalInstallDirPath() {
345        return getEnvironmentName() + Constants.BACKSLASH;
346    }
347
348    /**
349     * This function creates the script to kill all applications on this machine. The scripts calls all the kill script
350     * for each application.
351     *
352     * @param directory The directory for this machine (use global variable?).
353     * @throws IOFailure If an error occurred during the creation of the local killall script.
354     */
355    @Override
356    protected void createOSLocalKillAllScript(File directory) throws IOFailure {
357        // create the kill all script file
358        File killAllScript = new File(directory, Constants.SCRIPT_NAME_KILL_ALL + scriptExtension);
359        try {
360            // Initialise script
361            PrintWriter killPrinter = new PrintWriter(killAllScript, getTargetEncoding());
362            try {
363                killPrinter.println(ScriptConstants.ECHO_KILL_ALL_APPS + Constants.COLON + Constants.SPACE
364                        + Constants.APOSTROPHE + hostname + Constants.APOSTROPHE);
365                killPrinter.println(ScriptConstants.CD + Constants.SPACE + Constants.QUOTE_MARK + getConfDirPath()
366                        + Constants.QUOTE_MARK);
367                // insert path to kill script for all applications
368                for (Application app : applications) {
369                    // make name of file
370                    String appScript = Constants.SCRIPT_NAME_LOCAL_KILL + app.getIdentification() + scriptExtension;
371                    killPrinter.print(ScriptConstants.OPERATING_SYSTEM_WINDOWS_RUN_BATCH_FILE);
372                    killPrinter.print(Constants.QUOTE_MARK);
373                    killPrinter.print(appScript);
374                    killPrinter.print(Constants.QUOTE_MARK);
375                    killPrinter.println();
376                }
377                // Add 10 seconds timeout to allow the shutdown to complete before exiting the ssh connection
378                killPrinter.print(ScriptConstants.OPERATING_SYSTEM_WINDOWS_10_SECONDS_WAIT);
379                killPrinter.println();
380                    } finally {
381                // close script
382                killPrinter.close();
383            }
384        } catch (IOException e) {
385            String msg = "Problems creating local kill all script. ";
386            log.trace(msg, e);
387            throw new IOFailure(msg, e);
388        }
389    }
390
391    /**
392     * This function creates the script to start all applications on this machine. The scripts calls all the start
393     * script for each application.
394     *
395     * @param directory The directory for this machine (use global variable?).
396     * @throws IOFailure If an error occurred during the creation of the local startall script.
397     */
398    @Override
399    protected void createOSLocalStartAllScript(File directory) throws IOFailure {
400        // create the start all script file
401        File startAllScript = new File(directory, Constants.SCRIPT_NAME_START_ALL + scriptExtension);
402        try {
403            // Initialise script
404            PrintWriter startPrinter = new PrintWriter(startAllScript, getTargetEncoding());
405            try {
406                startPrinter.println(ScriptConstants.ECHO_START_ALL_APPS + Constants.COLON + Constants.SPACE
407                        + Constants.APOSTROPHE + hostname + Constants.APOSTROPHE);
408                startPrinter.println(ScriptConstants.CD + Constants.SPACE + Constants.QUOTE_MARK + getConfDirPath()
409                        + Constants.QUOTE_MARK);
410                // insert path to kill script for all applications
411                for (Application app : applications) {
412                    // make name of file
413                    String appScript = Constants.SCRIPT_NAME_LOCAL_START + app.getIdentification() + scriptExtension;
414                    startPrinter.print(ScriptConstants.OPERATING_SYSTEM_WINDOWS_RUN_BATCH_FILE);
415                    startPrinter.print(Constants.QUOTE_MARK);
416                    startPrinter.print(appScript);
417                    startPrinter.print(Constants.QUOTE_MARK);
418                    startPrinter.println();
419                }
420                // Add 10 seconds timeout to allow the Starting to complete before exiting the ssh connection
421                startPrinter.print(ScriptConstants.OPERATING_SYSTEM_WINDOWS_10_SECONDS_WAIT);
422                startPrinter.println();
423                    } finally {
424                // close script
425                startPrinter.close();
426            }
427        } catch (IOException e) {
428            String msg = "Problems during creation of the local start " + "all script.";
429            log.trace(msg, e);
430            throw new IOFailure(msg, e);
431        }
432    }
433
434    /**
435     * Creates the kill scripts for all the applications. Two script files are created: kill_app.bat and
436     * kill_ps_app.bat.
437     * <p>
438     * kill_ps_app.bat kills the process of the application. kill_app.bat runs kill_ps_app.bat if the application is
439     * running. run_app tells if the application is running. It is deleted during kill.
440     * <p>
441     * The kill_app.bat should have the following structure:
442     * <p>
443     * - ECHO Killing application : app - CD "path" - IF EXIST run_app GOTO KILL - GOTO NOKILL - - :KILL - cmdrun
444     * kill_ps_app.bat - DEL run_app - GOTO DONE - - :NOKILL - ECHO Cannot kill application. Already running. - - :DONE
445     * <p>
446     * where: app = application name. path = the path to the ./conf directory. cmdrun = the windows command to run other
447     * batch programs.
448     * <p>
449     * The kill_ps_app.bat is empty upon creation. When the application is started, the command to kill the process of
450     * he application is written to this file as the only content. It will look something like this:
451     * <p>
452     * - taskkill /F /PID id
453     * <p>
454     * where: id = the process identification number of the running application.
455     * <p>
456     * TODO kill the potential heritrix process, created by a harvester. Just like on Linux/Unix. If we in the future
457     * add the possibility of running heritrix on Windows.
458     *
459     * @param directory The directory for this machine (use global variable?).
460     * @throws IOFailure If an error occurred during the creation of the application kill script file.
461     */
462    @Override
463    protected void createApplicationKillScripts(File directory) throws IOFailure {
464        // go through all applications and create their kill script
465        for (Application app : applications) {
466            String id = app.getIdentification();
467            String killPsName = Constants.SCRIPT_KILL_PS + id + scriptExtension;
468            File appKillScript = new File(directory, Constants.SCRIPT_NAME_LOCAL_KILL + id + scriptExtension);
469            File appKillPsScript = new File(directory, killPsName);
470            try {
471                // make print writer for writing to file
472                PrintWriter appPrint = new PrintWriter(appKillScript, getTargetEncoding());
473                try {
474                    // initiate variables
475                    String tmpRunApp = Constants.FILE_TEMPORARY_RUN_WINDOWS_NAME + id;
476                    // get the content for the kill script of
477                    // this application
478                    // #echo kill windows application
479                    appPrint.println(ScriptConstants.ECHO_KILL_WINDOWS_APPLICATION + Constants.COLON + Constants.SPACE
480                            + id);
481                    // cd "path"
482                    appPrint.println(ScriptConstants.CD + Constants.SPACE + Constants.QUOTE_MARK
483                            + app.installPathWindows() + Constants.CONF_DIR_WINDOWS + Constants.QUOTE_MARK);
484                    // if exist run_app.txt GOTO KILL
485                    appPrint.println(ScriptConstants.IF + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE
486                            + tmpRunApp + Constants.SPACE + ScriptConstants.GOTO + Constants.SPACE
487                            + ScriptConstants.LABEL_KILL);
488                    // GOTO NOKILL
489                    appPrint.println(ScriptConstants.GOTO + Constants.SPACE + ScriptConstants.LABEL_NOKILL);
490                    //
491                    appPrint.println();
492                    // :KILL
493                    appPrint.println(Constants.COLON + ScriptConstants.LABEL_KILL);
494                    // cmdrun kill_ps_app.bat
495                    appPrint.println(ScriptConstants.OPERATING_SYSTEM_WINDOWS_RUN_BATCH_FILE + Constants.QUOTE_MARK
496                            + killPsName + Constants.QUOTE_MARK);
497                    // del run_app.txt
498                    appPrint.println(ScriptConstants.DEL + Constants.SPACE + tmpRunApp);
499                    // GOTO DONE
500                    appPrint.println(ScriptConstants.GOTO + Constants.SPACE + ScriptConstants.LABEL_DONE);
501                    //
502                    appPrint.println();
503                    // :NOKILL
504                    appPrint.println(Constants.COLON + ScriptConstants.LABEL_NOKILL);
505                    // echo Cannot kill application. Already running.
506                    appPrint.println(ScriptConstants.ECHO_CANNOT_KILL_APP);
507                    //
508                    appPrint.println();
509                    // :DONE
510                    appPrint.println(Constants.COLON + ScriptConstants.LABEL_DONE);
511                } finally {
512                    // close file
513                    appPrint.close();
514                }
515                // Printer for making the kill process file.
516                PrintWriter appPsPrint = new PrintWriter(appKillPsScript, getTargetEncoding());
517                try {
518                    // write dummy line in kill script.
519                    appPsPrint.println("ECHO Not started!");
520                } finally {
521                    // close file
522                    appPsPrint.close();
523                }
524            } catch (IOException e) {
525                String msg = "Cannot create the kill script for " + "application: " + app.getIdentification()
526                        + ", at machine: '" + hostname + "'";
527                log.trace(msg, e);
528                throw new IOFailure(msg, e);
529            }
530        }
531    }
532
533    /**
534     * Creates the start scripts for all the applications.
535     * <p>
536     * It creates the batch and the VBscript for starting the application, which are called start_app.bat and
537     * start_app.vbs respectively. These files are created in each of their own function.
538     *
539     * @param directory The directory for this machine (use global variable?).
540     * @see #windowsStartBatScript(Application, File)
541     * @see #windowsStartVbsScript(Application, File)
542     */
543    @Override
544    protected void createApplicationStartScripts(File directory) {
545        // go through all applications and create their start script
546        for (Application app : applications) {
547            windowsStartBatScript(app, directory);
548            windowsStartVbsScript(app, directory);
549        }
550    }
551
552    @Override
553    protected String osGetClassPath(Application app) {
554        StringBuilder res = new StringBuilder();
555        // get all the classpaths (change from '\' to '\\')
556        for (Element cp : app.getMachineParameters().getClassPaths()) {
557            // insert the path to the install directory
558            res.append(ScriptConstants.doubleBackslashes(getInstallDirPath()));
559            res.append(Constants.BACKSLASH + Constants.BACKSLASH);
560            // Then insert the class path.
561            res.append(ScriptConstants.replaceWindowsDirSeparators(cp.getText()));
562            res.append(Constants.SEMICOLON);
563        }
564        return res.toString();
565    }
566
567    /**
568     * Creates the batch script for starting the application. It checks if the application is running and starts the
569     * application if it is not the case. The application is started by calling the start_app.vbs.
570     * <p>
571     * The script should be the following:
572     * <p>
573     * - echo Starting windows application : app - cd "path" - if exist ".\conf\run_app.txt" goto NOSTART - goto START -
574     * - :START - cscript .\conf\start_app.vbs - goto DONE - - :NOSTART - echo Application already running. - - :DONE
575     * <p>
576     * where: app = the name of the application. path = the path to the installation directory.
577     *
578     * @param app The application to start.
579     * @param directory The directory where the script should be placed.
580     * @throws IOFailure If an error occurred during the creation of the windows start bat script.
581     */
582    protected void windowsStartBatScript(Application app, File directory) throws IOFailure {
583        File appStartScript = new File(directory, Constants.SCRIPT_NAME_LOCAL_START + app.getIdentification()
584                + scriptExtension);
585        try {
586            // make print writer for writing to file
587            PrintWriter appPrint = new PrintWriter(appStartScript, getTargetEncoding());
588            try {
589                // initiate variables
590                String id = app.getIdentification();
591                String tmpRunApp = Constants.FILE_TEMPORARY_RUN_WINDOWS_NAME + id;
592
593                // cd "path"
594                appPrint.println(ScriptConstants.CD + Constants.SPACE + Constants.QUOTE_MARK + app.installPathWindows()
595                        + Constants.QUOTE_MARK);
596                // if exist .\conf\run_app.txt GOTO NOSTART
597                appPrint.println(ScriptConstants.IF + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE
598                        + Constants.DOT + Constants.CONF_DIR_WINDOWS + tmpRunApp + Constants.SPACE
599                        + ScriptConstants.GOTO + Constants.SPACE + ScriptConstants.LABEL_NOSTART);
600                // GOTO START
601                appPrint.println(ScriptConstants.GOTO + Constants.SPACE + ScriptConstants.LABEL_START);
602                //
603                appPrint.println();
604                // :START
605                appPrint.println(Constants.COLON + ScriptConstants.LABEL_START);
606                // cscript .\conf\start_app.vbs
607                appPrint.println(ScriptConstants.CSCRIPT + Constants.SPACE + Constants.DOT + Constants.CONF_DIR_WINDOWS
608                        + Constants.SCRIPT_NAME_LOCAL_START + id + Constants.EXTENSION_VBS_FILES);
609                // GOTO DONE
610                appPrint.println(ScriptConstants.GOTO + Constants.SPACE + ScriptConstants.LABEL_DONE);
611                //
612                appPrint.println();
613                // :NOSTART
614                appPrint.println(Constants.COLON + ScriptConstants.LABEL_NOSTART);
615                // echo Cannot start. Application already running.
616                appPrint.println(ScriptConstants.ECHO_CANNOT_START_APP);
617                //
618                appPrint.println();
619                // :DONE
620                appPrint.println(Constants.COLON + ScriptConstants.LABEL_DONE);
621            } finally {
622                // close file
623                appPrint.close();
624            }
625        } catch (IOException e) {
626            String msg = "Cannot create the start script for application: " + app.getIdentification()
627                    + ", at machine: '" + hostname + "'";
628            log.trace(msg, e);
629            throw new IOFailure(msg, e);
630        }
631    }
632
633    protected static final class windowsStartVbsScriptTpl {
634        protected static final String[] mainScript = {
635                "Set WshShell= CreateObject(\"WScript.Shell\")",
636                "javahome = WshShell.ExpandEnvironmentStrings(\"%JAVA_HOME%\")",
637                "WScript.Echo \"JAVA_HOME=\" & javahome",
638                "If javahome = \"%JAVA_HOME%\" Then",
639                "  java = \"java\"",
640                "Else",
641                "  java = javahome & \"\\bin\\java\"",
642                "End If",
643                "Set oExec = WshShell.exec(\"\"\"java\"\"\" & \" ${machineparameters} -classpath \"\"${classpath}\"\""
644                        + " -Ddk.netarkivet.settings.file=\"\"${confdirpath}settings_${id}.xml\"\""
645                        + "${slf4jlogger}" + "${securityManagement}" + " ${appname}" + "\")",
646                "Set fso= CreateObject(\"Scripting.FileSystemObject\")",
647                "Set f=fso.OpenTextFile(\".\\conf\\${killpsname}\",2,True)",
648                "f.WriteLine \"taskkill /F /PID \" & oExec.ProcessID",
649                "f.close",
650                "Set tf=fso.OpenTextFile(\".\\conf\\${tmprunpsname}\",8,True)",
651                "tf.WriteLine \"running process: \" & oExec.ProcessID",
652                "tf.close",
653                "'Create a new start-log for the application",
654                "CreateObject(\"Scripting.FileSystemObject\").OpenTextFile(\"${startlogname}\", 2, True).close",
655                "Do While oExec.Status = 0 Or oExec.StdOut.AtEndOfStream <> True Or oExec.StdErr.AtEndOfStream <> True",
656                "  WScript.Sleep 1000",
657                "  Do While oExec.StdOut.AtEndOfStream <> True",
658                "    Set outFile = CreateObject(\"Scripting.FileSystemObject\").OpenTextFile(\"${startlogname}\", 8, True)",
659                "    outFile.WriteLine oExec.StdOut.ReadLine",
660                "    outFile.close",
661                "  Loop",
662                "  Do While oExec.StdErr.AtEndOfStream <> True",
663                "    Set outFile = CreateObject(\"Scripting.FileSystemObject\").OpenTextFile(\"${startlogname}\", 8, True)",
664                "    outFile.WriteLine oExec.StdErr.ReadLine", "    outFile.close", "  Loop", "Loop"};
665        protected static final String slf4jLogger = " -Dlogback.configurationFile=\"\"${confdirpath}logback_${id}.xml\"\"";
666        protected static final String securityManagement = " -Djava.security.manager -Djava.security.policy=\"\"${confdirpath}security.policy\"\"";
667    }
668
669    /**
670     * This function creates the VBscript to start the application. It calls a command for executing the java
671     * application, then it writes the way to kill the process in the kill_ps_app.bat and finally it creates the
672     * run-file.
673     * <p>
674     * It should have the following content: - set WshShell = CreateObject("WScript.Shell") - set oExec = WshShell.exec(
675     * JAVA ) - set fso = CreateObject("Scripting.FileSystemObject") - set f =
676     * fso.OpenTextFile(".\conf\kill_ps_app.bat", 2, True) - f.WriteLine "taskkill /F /PID " & oExec.ProcessID - f.close
677     * - 'Create a new start-log for the application - CreateObject("Scripting.FileSystemObject").OpenTextFile("
678     * start_APP.log", 2, True).close - Do While oExec.Status = 0 - WScript.Sleep 1000 - Do While
679     * oExec.StdOut.AtEndOfStream <> True - Set outFile = CreateObject("Scripting.FileSystemObject")
680     * .OpenTextFile("start_APP.log", 8, True) - outFile.WriteLine oExec.StdOut.ReadLine - outFile.close - Loop - Do
681     * While oExec.StdErr.AtEndOfStream <> True - Set outFile = CreateObject("Scripting.FileSystemObject")
682     * .OpenTextFile("start_APP.log", 8, True) - outFile.WriteLine oExec.StdErr.ReadLine - outFile.close - Loop - Loop
683     * <p>
684     * where: JAVA = the command for starting the java application (very long). app = the name of the application.
685     *
686     * @param app The application to start.
687     * @param directory The directory where the script should be placed.
688     * @throws IOFailure If an error occurred during the creation of the windows vb script.
689     */
690    protected void windowsStartVbsScript(Application app, File directory) throws IOFailure {
691        File appStartSupportScript = new File(directory, Constants.SCRIPT_NAME_LOCAL_START + app.getIdentification()
692                + Constants.EXTENSION_VBS_FILES);
693        try {
694            // make print writer for writing to file
695            PrintWriter vbsPrint = new PrintWriter(appStartSupportScript, getTargetEncoding());
696            try {
697                // initiate variables
698                String id = app.getIdentification();
699                String killPsName = Constants.SCRIPT_KILL_PS + id + scriptExtension;
700                String tmpRunPsName = Constants.FILE_TEMPORARY_RUN_WINDOWS_NAME + id;
701                String startLogName = Constants.SCRIPT_NAME_LOCAL_START + id + Constants.EXTENSION_LOG_FILES;
702
703                Map<String, String> env = new HashMap<String, String>();
704                env.put("machineparameters", app.getMachineParameters().writeJavaOptions());
705                env.put("classpath", osGetClassPath(app));
706                env.put("confdirpath", ScriptConstants.doubleBackslashes(getConfDirPath()));
707                env.put("id", id);
708                env.put("appname", app.getTotalName());
709                env.put("killpsname", killPsName);
710                env.put("tmprunpsname", tmpRunPsName);
711                env.put("startlogname", startLogName);
712                if (inheritedSlf4jConfigFile != null) {
713                    env.put("slf4jlogger", Template.untemplate(windowsStartVbsScriptTpl.slf4jLogger, env, true));
714                } else {
715                    env.put("slf4jlogger", "");
716                }
717                if (app.getTotalName().contains(ScriptConstants.BITARCHIVE_APPLICATION_NAME)) {
718                    env.put("securityManagement",
719                            Template.untemplate(windowsStartVbsScriptTpl.securityManagement, env, true));
720                } else {
721                    env.put("securityManagement", "");
722                }
723                String str = Template.untemplate(windowsStartVbsScriptTpl.mainScript, env, true, "\r\n");
724                vbsPrint.print(str);
725            } finally {
726                // close file
727                vbsPrint.close();
728            }
729        } catch (IOException e) {
730            String msg = "Cannot create the start script for application: " + app.getIdentification()
731                    + ", at machine: '" + hostname + "'";
732            log.trace(msg, e);
733            throw new IOFailure(msg, e);
734        }
735    }
736
737    /**
738     * THIS HAS NOT BEEN IMPLEMENTED FOR WINDOWS YET - ONLY LINUX!
739     * <p>
740     * Checks if a specific directory for the database is given in the settings, and thus if the database should be
741     * installed on this machine.
742     * <p>
743     * If no specific database is given (databaseFileName = null) then use the standard database extracted from
744     * NetarchiveSuite.zip. Else send the given new database to the standard database location.
745     * <p>
746     * Extract the database in the standard database location to the specified database directory.
747     *
748     * @return The script for installing the database (if needed).
749     */
750    @Override
751    protected String osInstallDatabase() {
752        String databaseDir = machineParameters.getHarvestDatabaseDirValue();
753        // Do not install if no proper database directory.
754        if (databaseDir == null || databaseDir.isEmpty()) {
755            return Constants.EMPTY;
756        }
757
758        StringBuilder res = new StringBuilder(ScriptConstants.ECHO_WINDOWS_DATABASE);
759        res.append(Constants.NEWLINE);
760
761        return res.toString();
762    }
763
764    /**
765     * THIS HAS NOT BEEN IMPLEMENTED FOR WINDOWS YET - ONLY LINUX!
766     * <p>
767     * Checks if a specific directory for the archive database is given in the settings, and thus if the archive
768     * database should be installed on this machine.
769     * <p>
770     * If not specific database is given (archiveDatabaseFileName = null) then use the default in the
771     * NetarchiveSuite.zip package. Else send the new archive database to the standard database location, and extract it
772     * to the given location.
773     *
774     * @return The script for installing the archive database (if needed).
775     */
776    @Override
777    protected String osInstallArchiveDatabase() {
778        String bpDatabaseDir = machineParameters.getArchiveDatabaseDirValue();
779
780        // Do not install if no proper archive database directory.
781        if (bpDatabaseDir == null || bpDatabaseDir.isEmpty()) {
782            return Constants.EMPTY;
783        }
784
785        StringBuilder res = new StringBuilder(ScriptConstants.ECHO_WINDOWS_DATABASE);
786        res.append(Constants.NEWLINE);
787
788        return res.toString();
789    }
790
791    @Override
792    protected String osInstallExternalJarFiles() {
793        if (jarFolder == null) {
794            return Constants.EMPTY;
795        }
796
797        StringBuilder res = new StringBuilder();
798
799        // Comment about copying files.
800        res.append(ScriptConstants.ECHO_INSTALLING_EXTERNAL_JAR_FILES);
801        res.append(Constants.NEWLINE);
802        // if [ -d folder ]; then ssh machine "md installdir/external";
803        // scp folder/* machine:installdir/external; fi;
804        res.append(ScriptConstants.LINUX_IF_DIR_EXIST + Constants.SPACE);
805        res.append(jarFolder.getPath());
806        res.append(Constants.SPACE + ScriptConstants.LINUX_THEN + Constants.SPACE);
807        res.append(ScriptConstants.SSH + Constants.SPACE);
808        res.append(machineUserLogin());
809        res.append(Constants.SPACE + ScriptConstants.MD + Constants.SPACE + Constants.QUOTE_MARK);
810        res.append(ScriptConstants.doubleBackslashes(getInstallDirPath()));
811        res.append(Constants.BACKSLASH + Constants.BACKSLASH + Constants.EXTERNAL_JAR_DIRECTORY + Constants.QUOTE_MARK
812                + Constants.SEMICOLON);
813        res.append(Constants.SPACE + ScriptConstants.SCP + Constants.SPACE + ScriptConstants.DASH_R + Constants.SPACE);
814        res.append(jarFolder.getPath());
815        res.append(Constants.SLASH + Constants.STAR);
816        res.append(Constants.SPACE);
817        res.append(machineUserLogin());
818        res.append(Constants.COLON + Constants.QUOTE_MARK);
819        res.append(ScriptConstants.doubleBackslashes(getInstallDirPath()));
820        res.append(Constants.BACKSLASH + Constants.BACKSLASH + Constants.EXTERNAL_JAR_DIRECTORY + Constants.QUOTE_MARK);
821        res.append(Constants.SEMICOLON + Constants.SPACE + ScriptConstants.FI + Constants.SEMICOLON);
822        res.append(Constants.NEWLINE);
823
824        return res.toString();
825    }
826
827    /**
828     * Creates the specified directories in the deploy config file.
829     *
830     * @return The script for creating the directories.
831     */
832    @Override
833    protected String osInstallScriptCreateDir() {
834        StringBuilder res = new StringBuilder();
835
836        res.append(ScriptConstants.ECHO_CREATING_DIRECTORIES);
837        res.append(Constants.NEWLINE);
838
839        // send script to machine
840        res.append(ScriptConstants.SCP + Constants.SPACE);
841        res.append(getMakeDirectoryName());
842        res.append(Constants.SPACE);
843        res.append(machineUserLogin());
844        res.append(Constants.COLON);
845        res.append(Constants.NEWLINE);
846
847        // run script
848        res.append(ScriptConstants.SSH + Constants.SPACE + Constants.SPACE);
849        res.append(machineUserLogin());
850        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE);
851        res.append(getMakeDirectoryName());
852        res.append(Constants.NEWLINE);
853
854        // delete script
855        res.append(ScriptConstants.SSH + Constants.SPACE + Constants.SPACE);
856        res.append(machineUserLogin());
857        res.append(Constants.SPACE + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE + ScriptConstants.DEL
858                + Constants.SPACE);
859        res.append(getMakeDirectoryName());
860        res.append(Constants.NEWLINE);
861
862        return res.toString();
863    }
864
865    @Override
866    protected String scriptCreateDir(String dir, boolean clean) {
867        StringBuilder res = new StringBuilder();
868
869        if (clean) {
870            res.append(ScriptConstants.IF + Constants.SPACE + ScriptConstants.EXIST + Constants.SPACE);
871            res.append(dir);
872            res.append(Constants.SPACE + ScriptConstants.RD + Constants.SPACE);
873            res.append(dir);
874            res.append(Constants.NEWLINE);
875        }
876        res.append(ScriptConstants.IF + Constants.SPACE + ScriptConstants.NOT + Constants.SPACE + ScriptConstants.EXIST
877                + Constants.SPACE);
878        res.append(dir);
879        res.append(Constants.SPACE + ScriptConstants.MD + Constants.SPACE);
880        res.append(dir);
881
882        res.append(Constants.NEWLINE);
883
884        return res.toString();
885    }
886
887    /**
888     * Function for creating the directories along the path until the end directory. Does not create the end directory.
889     *
890     * @param dir The path to the directory.
891     * @return The script for creating the directory.
892     */
893    protected String createPathToDir(String dir) {
894        StringBuilder res = new StringBuilder();
895
896        String[] pathDirs = dir.split(Constants.REGEX_BACKSLASH_CHARACTER);
897        StringBuilder path = new StringBuilder();
898
899        // only make directories along path to last directory,
900        // don't create end directory.
901        for (int i = 0; i < pathDirs.length - 1; i++) {
902            // don't make directory of empty path.
903            if (!pathDirs[i].isEmpty()) {
904                path.append(pathDirs[i]);
905                if (!path.substring(path.length() - 1).endsWith(Constants.COLON)) {
906                    res.append(scriptCreateDir(path.toString(), false));
907                }
908            }
909            path.append(Constants.BACKSLASH);
910        }
911
912        return res.toString();
913    }
914
915    @Override
916    protected String getAppDirectories() {
917        StringBuilder res = new StringBuilder();
918        String[] dirs;
919
920        for (Application app : applications) {
921            // get archive.fileDir directories.
922            dirs = app.getSettingsValues(Constants.SETTINGS_BITARCHIVE_BASEFILEDIR_LEAF);
923            if (dirs != null && dirs.length > 0) {
924                for (String dir : dirs) {
925                    res.append(createPathToDir(dir));
926                    res.append(scriptCreateDir(dir, false));
927                    for (String subdir : Constants.BASEFILEDIR_SUBDIRECTORIES) {
928                        res.append(scriptCreateDir(dir + Constants.BACKSLASH + subdir, false));
929                    }
930                }
931            }
932
933            // get harvester.harvesting.serverDir directories.
934            dirs = app.getSettingsValues(Constants.SETTINGS_HARVEST_SERVERDIR_LEAF);
935            if (dirs != null && dirs.length > 0) {
936                for (String dir : dirs) {
937                    res.append(createPathToDir(dir));
938                    res.append(scriptCreateDir(dir, false));
939                }
940            }
941
942            // get the viewerproxy.baseDir directories.
943            dirs = app.getSettingsValues(Constants.SETTINGS_VIEWERPROXY_BASEDIR_LEAF);
944            if (dirs != null && dirs.length > 0) {
945                for (String dir : dirs) {
946                    res.append(createPathToDir(dir));
947                    res.append(scriptCreateDir(dir, false));
948                }
949            }
950
951            // get the common.tempDir directories. But only those,
952            // which are not the same as the machine common.tempDir.
953            dirs = app.getSettingsValues(Constants.SETTINGS_TEMPDIR_LEAF);
954            if (dirs != null && dirs.length > 0) {
955                String machineDir = settings.getLeafValue(Constants.SETTINGS_TEMPDIR_LEAF);
956                for (String dir : dirs) {
957                    // Don't make machine temp dir twice.
958                    if (!dir.equals(machineDir)) {
959                        res.append(createPathToDir(dir));
960                        res.append(scriptCreateDir(dir, resetTempDir));
961                    }
962                }
963            }
964        }
965
966        return res.toString();
967    }
968
969    /**
970     * Creates the name for the make dir script.
971     *
972     * @return The name of the script for creating the directories.
973     */
974    protected String getMakeDirectoryName() {
975        return Constants.WINDOWS_DIR_CREATE_PREFIX + hostname + scriptExtension;
976    }
977
978    /**
979     * Function to create the script which installs the new directories. This is only used for windows machines!
980     *
981     * @param directory The directory to put the file.
982     * @throws IOFailure If an error occurred during the creation of the install-dir script.
983     */
984    @Override
985    protected void createInstallDirScript(File directory) throws IOFailure {
986        File dirScript = new File(directory, getMakeDirectoryName());
987        try {
988            // make print writer for writing to file
989            PrintWriter dirPrint = new PrintWriter(dirScript, getTargetEncoding());
990            try {
991                // go to correct directory
992                dirPrint.print(ScriptConstants.CD + Constants.SPACE);
993                dirPrint.print(getInstallDirPath());
994                dirPrint.print(Constants.NEWLINE);
995
996                // go through all directories.
997                String dir;
998
999                // get archive.bitpresevation.baseDir directory.
1000                dir = settings.getLeafValue(Constants.SETTINGS_ARCHIVE_BP_BASEDIR_LEAF);
1001                if (dir != null && !dir.isEmpty() && !dir.equalsIgnoreCase(Constants.DOT)) {
1002                    dirPrint.print(createPathToDir(dir));
1003                    dirPrint.print(scriptCreateDir(dir, false));
1004                }
1005
1006                // get archive.arcrepository.baseDir directory.
1007                dir = settings.getLeafValue(Constants.SETTINGS_ARCHIVE_ARC_BASEDIR_LEAF);
1008                if (dir != null && !dir.isEmpty() && !dir.equalsIgnoreCase(Constants.DOT)) {
1009                    dirPrint.print(createPathToDir(dir));
1010                    dirPrint.print(scriptCreateDir(dir, false));
1011                }
1012
1013                dirPrint.print(getAppDirectories());
1014
1015                // get tempDir directory.
1016                dir = settings.getLeafValue(Constants.SETTINGS_TEMPDIR_LEAF);
1017                if (dir != null && !dir.isEmpty() && !dir.equalsIgnoreCase(Constants.DOT)) {
1018                    dirPrint.print(createPathToDir(dir));
1019                    dirPrint.print(scriptCreateDir(dir, resetTempDir));
1020                }
1021            } finally {
1022                // close file
1023                dirPrint.close();
1024            }
1025        } catch (IOException e) {
1026            String msg = "Problems creating install directory script. ";
1027            log.trace(msg, e);
1028            throw new IOFailure(msg, e);
1029        }
1030    }
1031
1032    @Override
1033    protected String changeFileDirPathForSecurity(String path) {
1034        path += Constants.BACKSLASH + Constants.SECURITY_FILE_DIR_TAG + Constants.BACKSLASH;
1035        return path.replace(Constants.BACKSLASH, ScriptConstants.SECURITY_DIR_SEPARATOR);
1036    }
1037
1038    /**
1039     * This method does the following:
1040     * <p>
1041     * Retrieves the path to the jmxremote.access and jmxremote.password files.
1042     * <p>
1043     * Moves these files, if they are different from standard. This has to be a force move (command 'move /Y').
1044     * <p>
1045     * Makes the jmxremote.access and jmxremote.password files readonly.
1046     *
1047     * @return The commands for handling the jmxremote files.
1048     */
1049    @Override
1050    protected String getJMXremoteFilesCommand() {
1051        String accessFilePath;
1052        String passwordFilePath;
1053        String[] options;
1054
1055        // retrieve the access file path.
1056        options = settings.getLeafValues(Constants.SETTINGS_COMMON_JMX_ACCESSFILE);
1057
1058        // extract the path, if any. Else set default.
1059        if (options.length == 0) {
1060            accessFilePath = Constants.JMX_ACCESS_FILE_PATH_DEFAULT;
1061        } else {
1062            accessFilePath = options[0];
1063            // warn if more than one access file is defined.
1064            if (options.length > 1) {
1065                log.debug(Constants.MSG_WARN_TOO_MANY_JMXREMOTE_FILE_PATHS);
1066            }
1067        }
1068
1069        // retrieve the password file path.
1070        options = settings.getLeafValues(Constants.SETTINGS_COMMON_JMX_PASSWORDFILE);
1071
1072        // extract the path, if any. Else set default.
1073        if (options.length == 0) {
1074            passwordFilePath = Constants.JMX_PASSWORD_FILE_PATH_DEFAULT;
1075        } else {
1076            passwordFilePath = options[0];
1077            // warn if more than one access file is defined.
1078            if (options.length > 1) {
1079                log.debug(Constants.MSG_WARN_TOO_MANY_JMXREMOTE_FILE_PATHS);
1080            }
1081        }
1082
1083        // change the path to windows syntax.
1084        accessFilePath = ScriptConstants.replaceWindowsDirSeparators(accessFilePath);
1085        passwordFilePath = ScriptConstants.replaceWindowsDirSeparators(passwordFilePath);
1086
1087        // initialise the resulting command string.
1088        StringBuilder res = new StringBuilder();
1089
1090        // - echo make password files readonly
1091        res.append(ScriptConstants.ECHO_MAKE_PASSWORD_FILES);
1092        res.append(Constants.NEWLINE);
1093
1094        // IF NOT DEFAULT PATHS, THEN MAKE SCRIPT TO MOVE THE FILES.
1095        if (!accessFilePath.equals(ScriptConstants.replaceWindowsDirSeparators(Constants.JMX_ACCESS_FILE_PATH_DEFAULT))) {
1096            // ssh dev@kb-test-adm-001.kb.dk "mv
1097            // installpath/conf/jmxremote.access installpath/accessFilePath"
1098            res.append(ScriptConstants.SSH + Constants.SPACE);
1099            res.append(machineUserLogin());
1100            res.append(Constants.SPACE + Constants.QUOTE_MARK);
1101            res.append(ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE);
1102            res.append(ScriptConstants.WINDOWS_FORCE_MOVE);
1103            res.append(Constants.SPACE);
1104            res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1105            res.append(ScriptConstants.replaceWindowsDirSeparators(Constants.JMX_ACCESS_FILE_PATH_DEFAULT));
1106            res.append(Constants.SPACE);
1107            res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1108            res.append(accessFilePath);
1109            res.append(Constants.QUOTE_MARK);
1110            res.append(Constants.NEWLINE);
1111        }
1112
1113        if (!passwordFilePath.equals(ScriptConstants
1114                .replaceWindowsDirSeparators(Constants.JMX_PASSWORD_FILE_PATH_DEFAULT))) {
1115            // ssh dev@kb-test-adm-001.kb.dk "mv
1116            // installpath/conf/jmxremote.access installpath/accessFilePath"
1117            res.append(ScriptConstants.SSH + Constants.SPACE);
1118            res.append(machineUserLogin());
1119            res.append(Constants.SPACE + Constants.QUOTE_MARK);
1120            res.append(ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE);
1121            res.append(ScriptConstants.WINDOWS_FORCE_MOVE);
1122            res.append(Constants.SPACE);
1123            res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1124            res.append(ScriptConstants.replaceWindowsDirSeparators(Constants.JMX_PASSWORD_FILE_PATH_DEFAULT));
1125            res.append(Constants.SPACE);
1126            res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1127            res.append(passwordFilePath);
1128            res.append(Constants.QUOTE_MARK);
1129            res.append(Constants.NEWLINE);
1130        }
1131
1132        // - echo Y | ssh 'login'@'machine' cmd /c cacls
1133        // 'environmentName'\\conf\\jmxremote.password /P BITARKIV\\'login':R
1134        res.append(ScriptConstants.ECHO_Y + Constants.SPACE + Constants.SEPARATOR + Constants.SPACE
1135                + ScriptConstants.SSH + Constants.SPACE);
1136        res.append(machineUserLogin());
1137        res.append(Constants.SPACE + Constants.QUOTE_MARK + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE
1138                + ScriptConstants.CACLS + Constants.SPACE);
1139        res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1140        res.append(passwordFilePath);
1141        res.append(Constants.SPACE + ScriptConstants.SLASH_P + Constants.SPACE
1142                + ScriptConstants.BITARKIV_BACKSLASH_BACKSLASH);
1143        res.append(machineParameters.getMachineUserName().getText().trim());
1144        res.append(ScriptConstants.COLON_R + Constants.QUOTE_MARK);
1145        res.append(Constants.NEWLINE);
1146
1147        // - echo Y | ssh 'login'@'machine' cmd /c cacls
1148        // 'environmentName'\\conf\\jmxremote.access /P BITARKIV\\'login':R
1149        res.append(ScriptConstants.ECHO_Y + Constants.SPACE + Constants.SEPARATOR + Constants.SPACE
1150                + ScriptConstants.SSH + Constants.SPACE);
1151        res.append(machineUserLogin());
1152        res.append(Constants.SPACE + Constants.QUOTE_MARK + ScriptConstants.WINDOWS_COMMAND_RUN + Constants.SPACE
1153                + ScriptConstants.CACLS + Constants.SPACE);
1154        res.append(ScriptConstants.doubleBackslashes(getLocalInstallDirPath()));
1155        res.append(accessFilePath);
1156        res.append(Constants.SPACE + ScriptConstants.SLASH_P + Constants.SPACE
1157                + ScriptConstants.BITARKIV_BACKSLASH_BACKSLASH);
1158        res.append(machineParameters.getMachineUserName().getText().trim());
1159        res.append(ScriptConstants.COLON_R + Constants.QUOTE_MARK);
1160        res.append(Constants.NEWLINE);
1161
1162        return res.toString();
1163    }
1164
1165    /**
1166     * Creates scripts for restarting all the applications on a machine. This script should start by killing all the
1167     * existing processes, and then starting them again.
1168     * <p>
1169     * First the killall scripts is called, then wait for 5 seconds for the applications to be fully terminated, and
1170     * finally call the startall script.
1171     * <p>
1172     * The createWaitScript is called through this script to create the wait script which is used by the restart script.
1173     *
1174     * @param dir The directory where the script file will be placed.
1175     * @throws IOFailure If the restart script cannot be created, or if the wait script cannot be created.
1176     */
1177    @Override
1178    protected void createRestartScript(File dir) throws IOFailure {
1179        // Start by creating the wait script.
1180        createWaitScript(dir);
1181
1182        try {
1183            // initialise the script file.
1184            File restartScript = new File(dir, Constants.SCRIPT_NAME_RESTART + scriptExtension);
1185
1186            // make print writer for writing to file
1187            PrintWriter restartPrint = new PrintWriter(restartScript, getTargetEncoding());
1188            try {
1189                restartPrint.println(ScriptConstants.CD + Constants.SPACE + Constants.QUOTE_MARK + getConfDirPath()
1190                        + Constants.QUOTE_MARK);
1191
1192                // call killall script.
1193                restartPrint.print(ScriptConstants.WINDOWS_COMMAND_RUN);
1194                restartPrint.print(Constants.SPACE);
1195                restartPrint.print(Constants.SCRIPT_NAME_KILL_ALL + scriptExtension);
1196                restartPrint.print(Constants.NEWLINE);
1197
1198                // call wait script.
1199                restartPrint.print(ScriptConstants.CSCRIPT);
1200                restartPrint.print(Constants.SPACE);
1201                restartPrint.print(Constants.SCRIPT_NAME_WAIT + Constants.EXTENSION_VBS_FILES);
1202                restartPrint.print(Constants.NEWLINE);
1203
1204                // call startall script.
1205                restartPrint.print(ScriptConstants.WINDOWS_COMMAND_RUN);
1206                restartPrint.print(Constants.SPACE);
1207                restartPrint.print(Constants.SCRIPT_NAME_START_ALL + scriptExtension);
1208                restartPrint.print(Constants.NEWLINE);
1209            } finally {
1210                // close file
1211                restartPrint.close();
1212            }
1213        } catch (IOException e) {
1214            // Log the error and throw an IOFailure.
1215            log.trace(Constants.MSG_ERROR_RESTART_FILE, e);
1216            throw new IOFailure(Constants.MSG_ERROR_RESTART_FILE, e);
1217        }
1218    }
1219
1220    /**
1221     * Creates the script for waiting during restart.
1222     *
1223     * @param dir The directory where the script should be placed.
1224     * @throws IOFailure If the method fails in creating the wait script.
1225     */
1226    protected void createWaitScript(File dir) throws IOFailure {
1227        try {
1228            // initialise the script file.
1229            File waitScript = new File(dir, Constants.SCRIPT_NAME_WAIT + Constants.EXTENSION_VBS_FILES);
1230
1231            // make print writer for writing to file
1232            PrintWriter waitPrint = new PrintWriter(waitScript, getTargetEncoding());
1233            try {
1234                // Create the wait script.
1235                waitPrint.print(ScriptConstants.VB_WRITE_WAIT + Constants.SPACE
1236                        + (Constants.WAIT_TIME_DURING_RESTART * Constants.TIME_SECOND_IN_MILLISECONDS));
1237                waitPrint.print(Constants.NEWLINE);
1238            } finally {
1239                // close file
1240                waitPrint.close();
1241            }
1242        } catch (IOException e) {
1243            // log error and throw a IOFailure.
1244            log.trace(Constants.MSG_ERROR_WAIT_FILE, e);
1245            throw new IOFailure(Constants.MSG_ERROR_WAIT_FILE, e);
1246        }
1247    }
1248
1249    @Override
1250    protected void createArchiveDatabaseStartScript(File dir) {
1251        // Ignore if no archive database directory has been defined.
1252        String dbDir = machineParameters.getArchiveDatabaseDirValue();
1253        if (dbDir.isEmpty()) {
1254            return;
1255        }
1256
1257        // TODO NOT SUPPORTED!
1258        System.err.println("An Admin Database is not supported on a windows "
1259                + "machine. Please fix your deploy configuration.");
1260    }
1261
1262    @Override
1263    protected void createArchiveDatabaseKillScript(File dir) {
1264        // Ignore if no archive database directory has been defined.
1265        String dbDir = machineParameters.getArchiveDatabaseDirValue();
1266        if (dbDir.isEmpty()) {
1267            return;
1268        }
1269
1270        // TODO NOT SUPPORTED!
1271        System.err.println("An Admin Database is not supported on a windows "
1272                + "machine. Please fix your deploy configuration.");
1273    }
1274
1275    @Override
1276    protected void createHarvestDatabaseStartScript(File dir) {
1277        // Ignore if no harvest database directory has been defined.
1278        String dbDir = machineParameters.getHarvestDatabaseDirValue();
1279        if (dbDir.isEmpty()) {
1280            return;
1281        }
1282
1283        // TODO NOT SUPPORTED!
1284        System.err.println("An Harvest Database is not supported on a windows "
1285                + "machine. Please fix your deploy configuration.");
1286    }
1287
1288    @Override
1289    protected void createHarvestDatabaseKillScript(File dir) {
1290        // Ignore if no harvest database directory has been defined.
1291        String dbDir = machineParameters.getHarvestDatabaseDirValue();
1292        if (dbDir.isEmpty()) {
1293            return;
1294        }
1295
1296        // TODO NOT SUPPORTED!
1297        System.err.println("An Harvest Database is not supported on a windows "
1298                + "machine. Please fix your deploy configuration.");
1299    }
1300
1301    @Override
1302    protected void createHarvestDatabaseUpdateScript(File machineDirectory, boolean forceCreate) {
1303        // Ignore if no harvest database directory has been defined.
1304        String dbDir = machineParameters.getHarvestDatabaseDirValue();
1305        if (dbDir.isEmpty()) {
1306            return;
1307        }
1308
1309        System.err.println("An Harvest Database is not supported on a windows "
1310                + "machine. Please fix your deploy configuration.");
1311    }
1312
1313}