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.FileWriter;
027import java.io.IOException;
028import java.nio.charset.Charset;
029import java.util.List;
030
031import org.dom4j.Element;
032
033import dk.netarkivet.common.exceptions.ArgumentNotValid;
034
035/**
036 * This class applies the test variables.
037 * <p>
038 * It creates a new instance of the settings where the variables are changed, and then writes it out as a new
039 * deploy-configuration file (does not overwrite the original, but creates a new in same directory).
040 * <p>
041 * This class is prompted by our need to be able to install and run multiple instances the NetarchiveSuite in our
042 * test-environment simultaneously.
043 */
044public class CreateTestInstance {
045    /** The source configuration file. */
046    private File source;
047    /**
048     * The configuration instance. Loaded from the source file, changed and saved.
049     */
050    private XmlStructure deployConfiguration;
051    /** The string value of the calculated offset. */
052    private String offsetVal;
053    /** The paths to where the positions the offset are to be used. */
054    private OffsetSystem[] offsetPaths;
055    /** The new value for the HTTP port. */
056    private String httpPortVal;
057    /** The path to the HTTP port. */
058    private String[] httpPortPath;
059    /** The new value for the environment name. */
060    private String environmentNameVal;
061    /** The path to the environment name. */
062    private String[] environmentNamePath;
063    /** The new value of the mail receiver. */
064    private String mailReceiverVal;
065    /** The path to the mail receiver. */
066    private String[] mailReceiverPath;
067
068    /**
069     * The constructor.
070     *
071     * @param configSource The source configuration file.
072     */
073    public CreateTestInstance(File configSource) {
074        ArgumentNotValid.checkNotNull(configSource, "File configSource");
075
076        source = configSource;
077        deployConfiguration = new XmlStructure(source, Charset.defaultCharset().name());
078
079        offsetPaths = new OffsetSystem[0];
080        offsetVal = "";
081    }
082
083    /**
084     * Function to apply the variables.
085     *
086     * @param offset The input offset value (1-9 below httpPort).
087     * @param httpPort The new value for the HTTP port.
088     * @param environmentName The new value for the environment name.
089     * @param mailReceiver The new value for the mailReceiver.
090     */
091    public void applyTestArguments(String offset, String httpPort, String environmentName, String mailReceiver) {
092        ArgumentNotValid.checkNotNullOrEmpty(offset, "String offset");
093        ArgumentNotValid.checkNotNullOrEmpty(httpPort, "String httpPort");
094        ArgumentNotValid.checkNotNullOrEmpty(environmentName, "String environmentName");
095        ArgumentNotValid.checkNotNullOrEmpty(mailReceiver, "String mailReceiver");
096
097        // calculate offset
098        int offsetInt = (new Integer(httpPort)).intValue() - (new Integer(offset)).intValue();
099
100        if (offsetInt > Constants.TEST_OFFSET_INTEGER_MAXIMUM_VALUE || offsetInt < 0) {
101            System.err.print(Constants.MSG_ERROR_TEST_OFFSET);
102            System.out.println();
103            System.exit(1);
104        }
105        // change integer to string (easiest way to change integer to String)
106        offsetVal = Integer.toString(offsetInt);
107
108        // vaildate the environment name.
109        if (!Constants.validEnvironmentName(environmentName)) {
110            System.err.print(Constants.MSG_ERROR_INVALID_ENVIRONMENT_NAME + environmentName);
111            System.out.println();
112            System.exit(1);
113        }
114
115        // Get values
116        httpPortVal = httpPort;
117        environmentNameVal = environmentName;
118        mailReceiverVal = mailReceiver;
119
120        // make paths
121        httpPortPath = Constants.COMPLETE_HTTP_PORT_LEAF;
122        environmentNamePath = Constants.COMPLETE_ENVIRONMENT_NAME_LEAF;
123        mailReceiverPath = Constants.SETTINGS_NOTIFICATION_RECEIVER_PATH;
124
125        // make offset paths
126        offsetPaths = new OffsetSystem[] {
127                new OffsetSystem(Constants.TEST_OFFSET_MONITOR_JMX_PORT, Constants.COMPLETE_JMX_PORT_PATH),
128                new OffsetSystem(Constants.TEST_OFFSET_MONITOR_RMI_PORT, Constants.COMPLETE_JMX_RMIPORT_PATH),
129                new OffsetSystem(Constants.TEST_OFFSET_HERITRIX_GUI_PORT,
130                        Constants.COMPLETE_HARVEST_HERITRIX_GUI_PORT_PATH),
131                new OffsetSystem(Constants.TEST_OFFSET_HERITRIX_JMX_PORT, Constants.COMPLETE_HARVEST_HERITRIX_JMX_PORT),
132        // new OffsetSystem(Constants.TEST_OFFSET_ARCHIVE_DB_URL_PORT,
133        // Constants.COMPLETE_ARCHIVE_DATABASE_PORT),
134        // new OffsetSystem(Constants.TEST_OFFSET_HARVEST_DB_URL_PORT,
135        // Constants.COMPLETE_HARVEST_DATABASE_PORT)
136        };
137
138        // apply the arguments
139        apply();
140    }
141
142    /**
143     * Applies the new variables. Goes through all element instances and applies the variables.
144     */
145    @SuppressWarnings("unchecked")
146    private void apply() {
147        // apply on root
148        applyOnElement(deployConfiguration.getRoot());
149
150        List<Element> physLocs = deployConfiguration.getChildren(Constants.DEPLOY_PHYSICAL_LOCATION);
151
152        for (Element pl : physLocs) {
153            // apply on every physical location
154            applyOnElement(pl);
155
156            List<Element> machines = pl.elements(Constants.DEPLOY_MACHINE);
157            for (Element mac : machines) {
158                // apply on every machine
159                applyOnElement(mac);
160
161                List<Element> applications = mac.elements(Constants.DEPLOY_APPLICATION_NAME);
162                for (Element app : applications) {
163                    // apply on every application
164                    applyOnElement(app);
165
166                    applyEnvironmentNameOnBaseFileDir(app);
167                }
168            }
169        }
170    }
171
172    /**
173     * Applies the new variables on a specific element.
174     *
175     * @param e The element where the variables are to be applied.
176     */
177    private void applyOnElement(Element e) {
178        // Check argument valid
179        ArgumentNotValid.checkNotNull(e, "Element e");
180
181        // Check the following!
182        deployConfiguration.overWriteOnly(e, httpPortVal, httpPortPath);
183        deployConfiguration.overWriteOnly(e, environmentNameVal, environmentNamePath);
184        deployConfiguration.overWriteOnly(e, mailReceiverVal, mailReceiverPath);
185
186        for (OffsetSystem ofs : offsetPaths) {
187            deployConfiguration.overWriteOnlyInt(e, ofs.getIndex(), offsetVal.charAt(0), ofs.getPath());
188        }
189    }
190
191    /**
192     * Applies the environment name on the name of the file-directories. Thus: fileDir -> fileDir/environmentName
193     *
194     * @param app The application where this has to be applied.
195     */
196    private void applyEnvironmentNameOnBaseFileDir(Element app) {
197        ArgumentNotValid.checkNotNull(app, "Element app");
198
199        // Get the list of leaf elements along the path to the base_file_dir
200        List<Element> elems = XmlStructure.getAllChildrenAlongPath(app.element(Constants.COMPLETE_SETTINGS_BRANCH),
201                Constants.SETTINGS_BITARCHIVE_BASEFILEDIR_LEAF);
202        // append the environment name as sub directory to these leafs.
203        for (Element el : elems) {
204            StringBuilder content = new StringBuilder(el.getText().trim());
205            // check if windows format has been used (if the index of the
206            // windows directory separator is different from -1).
207            if (content.indexOf(Constants.BACKSLASH) > -1) {
208                content.append(Constants.BACKSLASH + environmentNameVal);
209            } else {
210                content.append(Constants.SLASH + environmentNameVal);
211            }
212            // then set new value.
213            el.setText(content.toString());
214        }
215    }
216
217    /**
218     * Creates a file containing the new configuration instance.
219     *
220     * @param filename The name of the file to be written.
221     * @throws IOException If anything goes wrong.
222     */
223    public void createConfigurationFile(String filename) throws IOException {
224        ArgumentNotValid.checkNotNullOrEmpty(filename, "String filename");
225        File f = new File(filename);
226
227        FileWriter fw = new FileWriter(f);
228        try {
229            fw.write(deployConfiguration.getXML());
230        } finally {
231            if (fw != null) {
232                fw.close();
233            }
234        }
235    }
236
237    /**
238     * Structure for handling where to apply the new offset value.
239     */
240    private static class OffsetSystem {
241        /** The index of the decimal to be replaced by the offset. */
242        private int index;
243        /** The path to the leaf where the offset are to be applied. */
244        private String[] path;
245
246        /**
247         * The constructor.
248         *
249         * @param i The index variable.
250         * @param p The path variable.
251         */
252        public OffsetSystem(int i, String[] p) {
253            index = i;
254            path = p;
255        }
256
257        /**
258         * For retrieving the index.
259         *
260         * @return The index where the offset should be applied.
261         */
262        public int getIndex() {
263            return index;
264        }
265
266        /**
267         * For retrieving the path.
268         *
269         * @return The path in the xml-structure to the element which should have a character changed.
270         */
271        public String[] getPath() {
272            return path;
273        }
274    }
275}