001/*
002 * #%L
003 * Netarchivesuite - deploy
004 * %%
005 * Copyright (C) 2005 - 2018 The Royal Danish 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.ArrayList;
029import java.util.List;
030
031import org.dom4j.Attribute;
032import org.dom4j.Element;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import dk.netarkivet.common.exceptions.ArgumentNotValid;
037import dk.netarkivet.common.exceptions.IOFailure;
038import dk.netarkivet.common.exceptions.IllegalState;
039import dk.netarkivet.common.utils.FileUtils;
040
041/**
042 * Machine defines an abstract representation of a physical computer at a physical location. The actual instances are
043 * depending on the operation system: LinuxMachine and WindowsMachine. All non-OS specific methods are implemented this
044 * machine class.
045 */
046public abstract class Machine {
047
048    /** the log, for logging stuff instead of displaying them directly. */
049    protected static final Logger log = LoggerFactory.getLogger(Machine.class);
050    /** The root-branch for this machine in the XML tree. */
051    protected Element machineRoot;
052    /** The settings, inherited from parent and overwritten. */
053    protected XmlStructure settings;
054    /** The machine parameters. */
055    protected Parameters machineParameters;
056    /** The list of the application on this machine. */
057    protected List<Application> applications;
058    /** The name of this machine. */
059    protected String hostname;
060    /** The operating system on this machine: 'windows' or 'linux'. */
061    protected String operatingSystem;
062    /** The extension on the script files (specified by operating system). */
063    protected String scriptExtension;
064    /** The name of the NetarchiveSuite.zip file. */
065    protected String netarchiveSuiteFileName;
066    /** The inherited SLF4J config file. */
067    protected File inheritedSlf4jConfigFile;
068    /** The inherited security.policy file. */
069    protected File inheritedSecurityPolicyFile;
070    /** The inherited database file name. */
071    protected File databaseFile;
072    /** The inherited archive database file name. */
073    protected File arcDatabaseFile;
074    /** The directory for this machine. */
075    protected File machineDirectory;
076    /** Whether the temp dir should be cleaned. */
077    protected boolean resetTempDir;
078    /** The folder containing the external jar library files. */
079    protected File jarFolder;
080    /** The encoding to use when writing files. */
081    protected String targetEncoding;
082    /** user specific logo png file */
083    protected File logoFile;
084    /** user specific menulogo png file */
085    protected File menulogoFile;
086
087    /**
088     * A machine is referring to an actual computer at a physical location, which can have independent applications from
089     * the other machines at the same location.
090     *
091     * @param subTreeRoot The root of this instance in the XML document.
092     * @param parentSettings The setting inherited by the parent.
093     * @param param The machine parameters inherited by the parent.
094     * @param netarchiveSuiteSource The name of the NetarchiveSuite package file.
095     * @param securityPolicy The security policy file.
096     * @param dbFileName The name of the database file.
097     * @param archiveDbFileName The name of the archive database file.
098     * @param resetDir Whether the temporary directory should be reset.
099     * @param externalJarFolder The folder containing the external jar library files.
100     * @throws ArgumentNotValid If one of the following arguments are null: subTreeRoot, parentSettings, param,
101     * netarchiveSuiteSource, logProp, securityPolicy.
102     */
103    public Machine(Element subTreeRoot, XmlStructure parentSettings, Parameters param, String netarchiveSuiteSource,
104            File slf4JConfig, File securityPolicy, File dbFileName, File archiveDbFileName,
105            boolean resetDir, File externalJarFolder, File aLogoFile, File aMenulogoFile) throws ArgumentNotValid {
106        ArgumentNotValid.checkNotNull(subTreeRoot, "Element subTreeRoot");
107        ArgumentNotValid.checkNotNull(parentSettings, "XmlStructure parentSettings");
108        ArgumentNotValid.checkNotNull(param, "Parameters param");
109        ArgumentNotValid.checkNotNull(netarchiveSuiteSource, "String netarchiveSuiteSource");
110        ArgumentNotValid.checkNotNull(slf4JConfig, "File slf4JConfig");
111        ArgumentNotValid.checkNotNull(securityPolicy, "File securityPolicy");
112
113        settings = new XmlStructure(parentSettings.getRoot());
114        machineRoot = subTreeRoot;
115        machineParameters = new Parameters(param);
116        netarchiveSuiteFileName = netarchiveSuiteSource;
117        inheritedSlf4jConfigFile = slf4JConfig;
118        inheritedSecurityPolicyFile = securityPolicy;
119        databaseFile = dbFileName;
120        arcDatabaseFile = archiveDbFileName;
121        resetTempDir = resetDir;
122        jarFolder = externalJarFolder;
123        logoFile = aLogoFile;
124        menulogoFile = aMenulogoFile;
125
126        // Retrieve machine encoding
127        targetEncoding = machineRoot.attributeValue(Constants.MACHINE_ENCODING_ATTRIBUTE);
128        String msgTail = "";
129        if (targetEncoding == null || targetEncoding.isEmpty()) {
130            targetEncoding = Constants.MACHINE_ENCODING_DEFAULT;
131            msgTail = " (defaulted)";
132        }
133        System.out.println("Machine '" + machineRoot.attributeValue(Constants.MACHINE_NAME_ATTRIBUTE)
134                + "' configured with encoding '" + targetEncoding + "'" + msgTail);
135
136        // retrieve the specific settings for this instance
137        Element tmpSet = machineRoot.element(Constants.COMPLETE_SETTINGS_BRANCH);
138        // Generate the specific settings by combining the general settings
139        // and the specific, (only if this instance has specific settings)
140        if (tmpSet != null) {
141            settings.overWrite(tmpSet);
142        }
143
144        // check if new machine parameters
145        machineParameters.newParameters(machineRoot);
146        // Retrieve the variables for this instance.
147        extractVariables();
148        // generate the machines on this instance
149        extractApplications();
150    }
151
152    /**
153     * Extract the local variables from the root. Currently, this is the name and the operating system.
154     */
155    private void extractVariables() {
156        // retrieve name
157        Attribute at = machineRoot.attribute(Constants.MACHINE_NAME_ATTRIBUTE);
158        if (at != null) {
159            hostname = at.getText().trim();
160        } else {
161            throw new IllegalState("A Machine instance has no name!");
162        }
163    }
164
165    /**
166     * Extracts the XML for the applications from the root, creates the applications and puts them into the list.
167     */
168    @SuppressWarnings("unchecked")
169    private void extractApplications() {
170        applications = new ArrayList<Application>();
171        List<Element> le = machineRoot.elements(Constants.DEPLOY_APPLICATION_NAME);
172        for (Element e : le) {
173            applications.add(new Application(e, settings, machineParameters, targetEncoding));
174        }
175    }
176
177    /**
178     * Create the directory for the specific configurations of this machine and call the functions for creating all the
179     * scripts in this directory.
180     *
181     * @param parentDirectory The directory where to write the files.
182     * @throws ArgumentNotValid If the parenteDirectory is null.
183     */
184    public void write(File parentDirectory) throws ArgumentNotValid {
185        ArgumentNotValid.checkNotNull(parentDirectory, "File parentDirectory");
186
187        // create the directory for this machine
188        machineDirectory = new File(parentDirectory, hostname);
189        FileUtils.createDir(machineDirectory);
190
191        //
192        // create the content in the directory
193        //
194
195        // Create kill scripts
196        createApplicationKillScripts(machineDirectory);
197        createOSLocalKillAllScript(machineDirectory);
198        createHarvestDatabaseKillScript(machineDirectory);
199        createArchiveDatabaseKillScript(machineDirectory);
200        // create start scripts
201        createApplicationStartScripts(machineDirectory);
202        createOSLocalStartAllScript(machineDirectory);
203        createHarvestDatabaseStartScript(machineDirectory);
204        createArchiveDatabaseStartScript(machineDirectory);
205        createHarvestDatabaseUpdateScript(machineDirectory, false);
206        // create restart script
207        createRestartScript(machineDirectory);
208        // copy the security policy file
209        createSecurityPolicyFile(machineDirectory);
210        // create the SLF4J property files
211        createSlf4jConfigFiles(machineDirectory);
212        // create the jmx remote files
213        createJmxRemotePasswordFile(machineDirectory);
214        createJmxRemoteAccessFile(machineDirectory);
215        // create the installCreateDir script
216        createInstallDirScript(parentDirectory);
217
218        // write the settings for all application at this machine
219        for (Application app : applications) {
220            app.createSettingsFile(machineDirectory);
221        }
222    }
223
224    /**
225     * Make the script for killing this machine. This is put into the entire kill all script for the physical location.
226     *
227     * @return The script to kill this machine.
228     */
229    public String writeToGlobalKillScript() {
230        StringBuilder res = new StringBuilder();
231        res.append(ScriptConstants.writeKillMachineHeader(machineUserLogin()));
232        // write the operating system dependent part of the kill script
233        res.append(osKillScript());
234        return res.toString();
235    }
236
237    /**
238     * Make the script for installing this machine. This is put into the entire install script for the physical
239     * location.
240     *
241     * @return The script to make the installation on this machine
242     */
243    public String writeToGlobalInstallScript() {
244        StringBuilder res = new StringBuilder();
245        res.append(ScriptConstants.writeInstallMachineHeader(machineUserLogin()));
246        // write the operating system dependent part of the install script
247        res.append(osInstallScript());
248        return res.toString();
249    }
250
251    /**
252     * Make the script for starting this machine. This is put into the entire startall script for the physical location.
253     *
254     * @return The script to start this machine.
255     */
256    public String writeToGlobalStartScript() {
257        StringBuilder res = new StringBuilder();
258        res.append(ScriptConstants.writeStartMachineHeader(machineUserLogin()));
259        // write the operating system dependent part of the start script
260        res.append(osStartScript());
261        return res.toString();
262    }
263
264    /**
265     * Copy inherited securityPolicyFile to local directory.
266     *
267     * @param directory The local directory for this machine.
268     * @throws IOFailure If an error occurred during the creation of the security policy file.
269     */
270    protected void createSecurityPolicyFile(File directory) throws IOFailure {
271        // make file
272        File secPolFile = new File(directory, Constants.SECURITY_POLICY_FILE_NAME);
273        try {
274            // init writer
275            PrintWriter secPrinter = new PrintWriter(secPolFile, getTargetEncoding());
276            try {
277                // read the inherited security policy file.
278                String prop = FileUtils.readFile(inheritedSecurityPolicyFile);
279
280                // change the jmx monitor role (if defined in settings)
281                String monitorRole = settings.getLeafValue(Constants.SETTINGS_MONITOR_JMX_NAME_LEAF);
282                if (monitorRole != null) {
283                    prop = prop.replace(Constants.SECURITY_JMX_PRINCIPAL_NAME_TAG, monitorRole);
284                }
285
286                // Change the common temp dir (if defined in settings)
287                String ctd = settings.getLeafValue(Constants.SETTINGS_TEMPDIR_LEAF);
288                if (ctd != null) {
289                    prop = prop.replace(Constants.SECURITY_COMMON_TEMP_DIR_TAG, ctd);
290                }
291
292                // write to file.
293                secPrinter.write(prop);
294
295                // initialise list of directories to add
296                List<String> dirs = new ArrayList<String>();
297
298                // get all directories to add and put them into the list
299                for (Application app : applications) {
300                    // get archive.fileDir directory.
301                    String[] tmpDirs = app.getSettingsValues(Constants.SETTINGS_BITARCHIVE_BASEFILEDIR_LEAF);
302                    if (tmpDirs != null && tmpDirs.length > 0) {
303                        for (String st : tmpDirs) {
304                            dirs.add(st);
305                        }
306                    }
307                }
308
309                // append file directories
310                if (!dirs.isEmpty()) {
311                    secPrinter.write("grant {" + "\n");
312                    for (String dir : dirs) {
313                        secPrinter.write(ScriptConstants
314                                .writeSecurityPolicyDirPermission(changeFileDirPathForSecurity(dir)));
315                    }
316                    secPrinter.write("};");
317                }
318            } finally {
319                secPrinter.close();
320            }
321        } catch (IOException e) {
322            log.warn("IOException while creating security policy file: ", e);
323            throw new IOFailure("Cannot create security policy file.", e);
324        }
325    }
326
327    /**
328     * Creates a the SLF4J config file for every application. This is done by taking the inherited log file and changing
329     * "APPID" in the file into the identification of the application.
330     *
331     * @param directory The local directory for this machine
332     * @throws IOFailure If an error occurred during the creationg of the log property file.
333     */
334    protected void createSlf4jConfigFiles(File directory) throws IOFailure {
335        // make config file for every application
336        for (Application app : applications) {
337            // make file
338            File logProp = new File(directory, Constants.SLF4J_CONFIG_APPLICATION_PREFIX + app.getIdentification()
339                    + Constants.SLF4J_CONFIG_APPLICATION_SUFFIX);
340            try {
341                // init writer
342                PrintWriter logPrinter = new PrintWriter(logProp, getTargetEncoding());
343
344                try {
345                    // read the inherited log property file.
346                    String prop = FileUtils.readFile(inheritedSlf4jConfigFile);
347
348                    // append stuff!
349                    prop = prop.replace(Constants.LOG_PROPERTY_APPLICATION_ID_TAG, app.getIdentification());
350                    prop = modifyLogProperties(prop);
351                    // write to file.
352                    logPrinter.write(prop);
353                } finally {
354                    logPrinter.close();
355                }
356            } catch (IOException e) {
357                log.warn("IOException while creating SLF4J config file:", e);
358                throw new IOFailure("Cannot create SLF4J config file.", e);
359            }
360        }
361    }
362
363    /**
364     * Make any OS-specific modifications to logging properties. The default makes no modifications, but this can be
365     * overridden in subclasses.
366     *
367     * @param logProperties the contents of the logging properties file.
368     * @return the contents of the logging properties file with any desired modifications.
369     */
370    protected String modifyLogProperties(String logProperties) {
371        return logProperties;
372    }
373
374    /**
375     * Creates the jmxremote.password file, based on the settings.
376     *
377     * @param directory The local directory for this machine
378     * @throws IOFailure If an error occurred during the creation of the jmx remote password file.
379     */
380    protected void createJmxRemotePasswordFile(File directory) throws IOFailure {
381        // make file
382        File jmxFile = new File(directory, Constants.JMX_PASSWORD_FILE_NAME);
383        try {
384            // init writer
385            PrintWriter jw = new PrintWriter(jmxFile, getTargetEncoding());
386            try {
387                // Write the header of the jmxremote.password file.
388                jw.print(ScriptConstants.JMXREMOTE_PASSWORD_HEADER);
389
390                // Get the username and password for monitor and heritrix.
391                StringBuilder logins = new StringBuilder();
392
393                // Append the jmx logins for monitor and heritrix.
394                logins.append(getMonitorLogin());
395                logins.append(getHeritrixLogin());
396
397                jw.print(logins.toString());
398            } finally {
399                jw.close();
400            }
401        } catch (IOException e) {
402            log.trace("IOException while creating jmxremote.password:", e);
403            throw new IOFailure("Cannot create jmxremote.password.", e);
404        }
405    }
406
407    /**
408     * Creates the jmxremote.password file, based on the settings.
409     *
410     * @param directory The local directory for this machine
411     * @throws IOFailure If an error occurred during the creation of the jmx remote access file.
412     */
413    protected void createJmxRemoteAccessFile(File directory) throws IOFailure {
414        // make file
415        File jmxFile = new File(directory, Constants.JMX_ACCESS_FILE_NAME);
416        try {
417            // init writer
418            PrintWriter jw = new PrintWriter(jmxFile, getTargetEncoding());
419            try {
420                // Write the header of the jmxremote.password file.
421                jw.print(ScriptConstants.JMXREMOTE_ACCESS_HEADER);
422
423                // Get the username and password for monitor and heritrix.
424                StringBuilder logins = new StringBuilder();
425
426                // Append the jmx logins for monitor and heritrix.
427                logins.append(getMonitorUsername());
428                logins.append(getHeritrixUsername());
429
430                jw.print(logins.toString());
431            } finally {
432                jw.close();
433            }
434        } catch (IOException e) {
435            log.trace("IOException while creating jmxremote.access file:", e);
436            throw new IOFailure("Cannot create jmxremote.access file.", e);
437        }
438    }
439
440    /**
441     * For finding the jmxUsernames and jmxPasswords under the monitor branch in the settings. Goes through all
442     * applications, which all must have the same username and the same passwords.
443     *
444     * @return The string to add to the jmxremote.password file.
445     * @throws IllegalState If there is a different amount of usernames and passwords, or if two application has
446     * different values for their username or passwords (applications without values are ignored).
447     */
448    protected String getMonitorLogin() throws IllegalState {
449        StringBuilder res = new StringBuilder();
450        // initialise list of usernames and passwords to add
451        List<String> usernames = new ArrayList<String>();
452        List<String> passwords = new ArrayList<String>();
453        String[] tmpVals;
454
455        // get values from applications and put them into the lists
456        for (Application app : applications) {
457            // get monitor.jmxUsername
458            tmpVals = app.getSettingsValues(Constants.SETTINGS_MONITOR_JMX_NAME_LEAF);
459            if (tmpVals != null && tmpVals.length > 0) {
460                for (String st : tmpVals) {
461                    usernames.add(st);
462                }
463            }
464            // get monitor.jmxPassword
465            tmpVals = app.getSettingsValues(Constants.SETTINGS_MONITOR_JMX_PASSWORD_LEAF);
466            if (tmpVals != null && tmpVals.length > 0) {
467                for (String st : tmpVals) {
468                    passwords.add(st);
469                }
470            }
471        }
472
473        // if different amount of usernames and passwords => DIE
474        if (usernames.size() != passwords.size()) {
475            String msg = "Different amount of usernames and passwords in monitor under applications on machine: '"
476                    + hostname + "'";
477            log.warn(msg);
478            throw new IllegalState(msg);
479        }
480
481        // warn if no usernames for monitor.
482        if (usernames.size() == 0) {
483            log.warn("No usernames or passwords for monitor on machine: '{}'", hostname);
484        }
485
486        // check if the usernames and passwords are the same.
487        for (int i = 1; i < usernames.size(); i++) {
488            if (!usernames.get(0).equals(usernames.get(i)) || !passwords.get(0).equals(passwords.get(i))) {
489                String msg = "Different usernames or passwords under monitor on the same machine: '" + hostname + "'";
490                log.warn(msg);
491                throw new IllegalState(msg);
492            }
493        }
494
495        // make the resulting string
496        if (usernames.size() > 0) {
497            res.append(usernames.get(0));
498            res.append(Constants.SPACE);
499            res.append(passwords.get(0));
500            res.append(Constants.NEWLINE);
501        }
502        return res.toString();
503    }
504
505    /**
506     * For retrieving the monitor username for the jmxremote.access file. This will have the rights 'readonly'.
507     *
508     * @return The string for the jmxremote.access file for allowing the monitor user to readonly.
509     * @throws IllegalState If different applications on the machine have different user names.
510     */
511    protected String getMonitorUsername() throws IllegalState {
512        StringBuilder res = new StringBuilder();
513        // initialise list of usernames and passwords to add
514        List<String> usernames = new ArrayList<String>();
515        String[] tmpVals;
516
517        // get values from applications and put them into the lists
518        for (Application app : applications) {
519            // get monitor.jmxUsername
520            tmpVals = app.getSettingsValues(Constants.SETTINGS_MONITOR_JMX_NAME_LEAF);
521            if (tmpVals != null && tmpVals.length > 0) {
522                for (String st : tmpVals) {
523                    usernames.add(st);
524                }
525            }
526        }
527
528        // check if the usernames and passwords are the same.
529        for (int i = 1; i < usernames.size(); i++) {
530            if (!usernames.get(0).equals(usernames.get(i))) {
531                String msg = "Different usernames for the monitor on the same machine: '" + hostname + "'";
532                log.warn(msg);
533                throw new IllegalState(msg);
534            }
535        }
536
537        // make the resulting string
538        if (usernames.size() > 0) {
539            res.append(usernames.get(0));
540            res.append(Constants.SPACE);
541            res.append(ScriptConstants.JMXREMOTE_MONITOR_PRIVILEGES);
542            res.append(Constants.NEWLINE);
543        }
544        return res.toString();
545    }
546
547    /**
548     * For finding the jmxUsernames and jmxPasswords under the harvest.harvesting.heritrix branch under in the settings.
549     * Goes through all applications, which all must have the same username and the same passwords.
550     *
551     * @return The string to add to the jmxremote.password file.
552     * @throws IllegalState If there is a different amount of usernames and passwords, or if two application has
553     * different values for their username or passwords (applications without values are ignored).
554     */
555    protected String getHeritrixLogin() throws IllegalState {
556        StringBuilder res = new StringBuilder();
557        // initialise list of usernames and passwords to add
558        List<String> usernames = new ArrayList<String>();
559        List<String> passwords = new ArrayList<String>();
560        String[] tmpVals;
561
562        // get values from applications and put them into the lists
563        for (Application app : applications) {
564            // get heritrix.jmxUsername
565            tmpVals = app.getSettingsValues(Constants.SETTINGS_HERITRIX_JMX_USERNAME_LEAF);
566            if (tmpVals != null && tmpVals.length > 0) {
567                for (String st : tmpVals) {
568                    usernames.add(st);
569                }
570            }
571            // get heritrix.jmxPassword
572            tmpVals = app.getSettingsValues(Constants.SETTINGS_HERITRIX_JMX_PASSWORD_LEAF);
573            if (tmpVals != null && tmpVals.length > 0) {
574                for (String st : tmpVals) {
575                    passwords.add(st);
576                }
577            }
578        }
579
580        // if different amount of usernames and passwords. DIE
581        if (usernames.size() != passwords.size()) {
582            String msg = "Different amount of usernames and passwords in heritrix under applications on machine: '"
583                    + hostname + "'";
584            log.warn(msg);
585            throw new IllegalState(msg);
586        }
587
588        // if no usernames, and thus no passwords, finish!
589        if (usernames.size() == 0) {
590            return "";
591        }
592
593        // check if the usernames and passwords are the same.
594        for (int i = 1; i < usernames.size(); i++) {
595            if (!usernames.get(0).equals(usernames.get(i)) || !passwords.get(0).equals(passwords.get(i))) {
596                String msg = "Different usernames or passwords " + "under heritrix on machine: '" + hostname + "'";
597                log.warn(msg);
598                throw new IllegalState(msg);
599            }
600        }
601
602        // make the resulting string
603        if (usernames.size() > 0) {
604            res.append(usernames.get(0));
605            res.append(Constants.SPACE);
606            res.append(passwords.get(0));
607            res.append(Constants.NEWLINE);
608        }
609        return res.toString();
610    }
611
612    /**
613     * For retrieving the Heritrix username for the jmxremote.access file. This will have the rights 'readwrite'.
614     *
615     * @return The string for the jmxremote.access file for allowing the heritrix user to readonly.
616     */
617    protected String getHeritrixUsername() {
618        StringBuilder res = new StringBuilder();
619        // initialise list of usernames and passwords to add
620        List<String> usernames = new ArrayList<String>();
621        String[] tmpVals;
622
623        // get values from applications and put them into the lists
624        for (Application app : applications) {
625            // get heritrix.jmxUsername
626            tmpVals = app.getSettingsValues(Constants.SETTINGS_HERITRIX_JMX_USERNAME_LEAF);
627            if (tmpVals != null && tmpVals.length > 0) {
628                for (String st : tmpVals) {
629                    usernames.add(st);
630                }
631            }
632        }
633
634        // check if the usernames and passwords are the same.
635        for (int i = 1; i < usernames.size(); i++) {
636            if (!usernames.get(0).equals(usernames.get(i))) {
637                String msg = "Different usernames for Heritrix on the same machine: '" + hostname + "'";
638                log.warn(msg);
639                throw new IllegalState(msg);
640            }
641        }
642
643        // make the resulting string
644        if (usernames.size() > 0) {
645            res.append(usernames.get(0));
646            res.append(Constants.SPACE);
647            res.append(ScriptConstants.JMXREMOTE_HERITRIX_PRIVILEGES);
648            res.append(Constants.NEWLINE);
649        }
650        return res.toString();
651    }
652    
653    protected StringBuilder updateLogofileInWarFiles(StringBuilder aRes, File aLogofile, String aDestinationFilename) {
654        aRes.append("scp " + aLogofile.getAbsolutePath() + " " + machineUserLogin() + ":" + getInstallDirPath() + "/" + Constants.WEBPAGESDIR + "/" + aDestinationFilename + Constants.NEWLINE);
655
656        for (String war : Constants.WARFILENAMES) {
657                aRes.append("ssh " + machineUserLogin() + "  \"cd " + getInstallDirPath() + "/" + Constants.WEBPAGESDIR + "; zip -r " + war + " " + aDestinationFilename + "\"" + Constants.NEWLINE);
658        }
659        
660        aRes.append("ssh " + machineUserLogin() + " \"rm " + getInstallDirPath() + "/" + Constants.WEBPAGESDIR + "/" + aDestinationFilename + "\"" + Constants.NEWLINE);
661        
662        return aRes;
663    }
664
665    /**
666     * The string for accessing this machine through SSH.
667     *
668     * @return The access through SSH to the machine
669     */
670    protected String machineUserLogin() {
671        return machineParameters.getMachineUserName().getStringValue().trim() + Constants.AT + hostname;
672    }
673
674    /**
675     * For retrieving the environment name variable.
676     *
677     * @return The environment name.
678     */
679    protected String getEnvironmentName() {
680        return settings.getSubChildValue(Constants.SETTINGS_ENVIRONMENT_NAME_LEAF);
681    }
682
683    /**
684     * Creates the kill scripts for all the applications.
685     *
686     * @param directory The directory for this machine (use global variable?).
687     */
688    protected abstract void createApplicationKillScripts(File directory);
689
690    /**
691     * Creates the start scripts for all the applications.
692     *
693     * @param directory The directory for this machine (use global variable?).
694     */
695    protected abstract void createApplicationStartScripts(File directory);
696
697    /**
698     * This function creates the script to start all applications on this machine. The scripts calls all the start
699     * script for each application.
700     *
701     * @param directory The directory for this machine (use global variable?).
702     */
703    protected abstract void createOSLocalStartAllScript(File directory);
704
705    /**
706     * This function creates the script to kill all applications on this machine. The scripts calls all the kill script
707     * for each application.
708     *
709     * @param directory The directory for this machine (use global variable?).
710     */
711    protected abstract void createOSLocalKillAllScript(File directory);
712
713    /**
714     * The operation system specific path to the installation directory.
715     *
716     * @return Install path.
717     */
718    protected abstract String getInstallDirPath();
719
720    /**
721     * The operation system specific path to the conf directory.
722     *
723     * @return Conf path.
724     */
725    protected abstract String getConfDirPath();
726
727    /**
728     * The operation system specific path to the lib directory.
729     *
730     * @return Lib path.
731     */
732    protected abstract String getLibDirPath();
733
734    /**
735     * Creates the operation system specific killing script for this machine.
736     *
737     * @return Operation system specific part of the killscript.
738     */
739    protected abstract String osKillScript();
740
741    /**
742     * Creates the operation system specific installation script for this machine.
743     *
744     * @return Operation system specific part of the installscript.
745     */
746    protected abstract String osInstallScript();
747
748    /**
749     * Creates the specified directories in the deploy-configuration file.
750     *
751     * @return The script for creating the directories.
752     */
753    protected abstract String osInstallScriptCreateDir();
754
755    /**
756     * Updates user specific Logos in all war files.
757     *
758     * @return The script for updating logos in all war files.
759     */
760    protected abstract String osUpdateLogos();
761        
762    /**
763     * Creates the operation system specific starting script for this machine.
764     *
765     * @return Operation system specific part of the startscript.
766     */
767    protected abstract String osStartScript();
768
769    /**
770     * Makes all the class paths into the operation system specific syntax, and puts them into a string where they are
771     * separated by the operation system specific separator (':' for linux, ';' for windows).
772     *
773     * @param app The application which has the class paths.
774     * @return The class paths in operation system specific syntax.
775     */
776    protected abstract String osGetClassPath(Application app);
777
778    /**
779     * Checks if a specific directory for the database is given in the settings, and thus if the database should be
780     * installed on this machine.
781     * <p>
782     * If no specific database is given as deploy argument (databaseFileName = null) then use the standard database
783     * extracted from NetarchiveSuite.zip. Else send the given new database to the standard database location.
784     * <p>
785     * Extract the database in the standard database location to the specified database directory.
786     *
787     * @return The script for installing the database (if needed).
788     */
789    protected abstract String osInstallDatabase();
790
791    /**
792     * Checks if a specific directory for the archive database is given in the settings, and thus if the archive
793     * database should be installed on this machine.
794     * <p>
795     * If not specific database is given (adminDatabaseFileName = null) then use the default in the NetarchiveSuite.zip
796     * package. Else send the new archive database to the standard database location, and extract it to the given
797     * location.
798     *
799     * @return The script for installing the archive database (if needed).
800     */
801    protected abstract String osInstallArchiveDatabase();
802
803    /**
804     * This function makes the part of the install script for installing the external jar files from within the
805     * jarFolder. If the jarFolder is null, then no action will be performed.
806     *
807     * @return The script for installing the external jar files (if needed).
808     */
809    protected abstract String osInstallExternalJarFiles();
810
811    /**
812     * This functions makes the script for creating the new directories.
813     * <p>
814     * Linux creates directories directly through ssh. Windows creates an install a script file for installing the
815     * directories, which has to be sent to the machine, then executed and finally deleted.
816     *
817     * @param dir The name of the directory to create.
818     * @param clean Whether the directory should be cleaned\reset.
819     * @return The lines of code for creating the directories.
820     * @see #createInstallDirScript(File)
821     */
822    protected abstract String scriptCreateDir(String dir, boolean clean);
823
824    /**
825     * Creates the script for creating the application specified directories. Also creates the directories along the
826     * path to the directories.
827     *
828     * @return The script for creating the application specified directories.
829     */
830    protected abstract String getAppDirectories();
831
832    /**
833     * This method does the following: Retrieves the path to the jmxremote.access and jmxremote.password files. Moves
834     * these files, if they are different from standard. Makes the jmxremote.access and jmxremote.password files
835     * readonly.
836     *
837     * @return The commands for handling the jmxremote files.
838     */
839    protected abstract String getJMXremoteFilesCommand();
840
841    /**
842     * Function to create the script which installs the new directories. This is only used for windows machines!
843     *
844     * @param dir The directory to put the file
845     */
846    protected abstract void createInstallDirScript(File dir);
847
848    /**
849     * Creates a script for restating all the applications on a given machine.
850     *
851     * @param dir The directory where the script will be placed.
852     */
853    protected abstract void createRestartScript(File dir);
854
855    /**
856     * Creates a script for starting the archive database on a given machine. This is only created if the
857     * &lt;globalArchiveDatabaseDir&gt; parameter is defined on the machine level.
858     *
859     * @param dir The directory where the script will be placed.
860     */
861    protected abstract void createArchiveDatabaseStartScript(File dir);
862
863    /**
864     * Creates a script for killing the archive database on a given machine. This is only created if the
865     * &lt;globalArchiveDatabaseDir&gt; parameter is defined on the machine level.
866     *
867     * @param dir The directory where the script will be placed.
868     */
869    protected abstract void createArchiveDatabaseKillScript(File dir);
870
871    /**
872     * Creates a script for starting the harvest database on a given machine. This is only created if the
873     * &lt;deployHarvestDatabaseDir&gt; parameter is defined on the machine level.
874     *
875     * @param dir The directory where the script will be placed.
876     */
877    protected abstract void createHarvestDatabaseStartScript(File dir);
878
879    /**
880     * Creates a script for killing the harvest database on a given machine. This is only created if the
881     * &lt;globalHarvestDatabaseDir&gt; parameter is defined on the machine level.
882     *
883     * @param dir The directory where the script will be placed.
884     */
885    protected abstract void createHarvestDatabaseKillScript(File dir);
886
887    /**
888     * Changes the file directory path to the format used in the security policy.
889     *
890     * @param path The current path.
891     * @return The formatted path.
892     */
893    protected abstract String changeFileDirPathForSecurity(String path);
894
895    /**
896     * create a harvestDatabaseUpdatescript in the given machineDirectory.
897     *
898     * @param machineDirectory a given MachineDirectory.
899     * @param forceCreate
900     */
901    protected abstract void createHarvestDatabaseUpdateScript(File machineDirectory, boolean forceCreate);
902
903    protected String getTargetEncoding() {
904        return targetEncoding;
905    }
906
907}