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.FileNotFoundException; 027import java.io.PrintWriter; 028import java.io.UnsupportedEncodingException; 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; 039 040/** 041 * The application entity in the deploy hierarchy. 042 */ 043public class Application { 044 045 /** the log, for logging stuff instead of displaying them directly. */ 046 private static final Logger log = LoggerFactory.getLogger(Application.class); 047 048 /** the root-branch for this application in the XML tree. */ 049 private Element applicationRoot; 050 /** The specific settings for this instance, inherited and overwritten. */ 051 private XmlStructure settings; 052 /** parameters. */ 053 private Parameters machineParameters; 054 /** Name of this instance. */ 055 private String name; 056 /** The total name of this instance. */ 057 private String nameWithNamePath; 058 /** application instance id (optional, used when two application has same name). */ 059 private String applicationInstanceId; 060 061 /** The encoding to use when writing files. */ 062 private final String targetEncoding; 063 064 /** 065 * A application is the program to be run on a machine. 066 * 067 * @param subTreeRoot The root of this instance in the XML document. 068 * @param parentSettings The setting inherited by the parent. 069 * @param param The machine parameters inherited by the parent. 070 * @param targetEncoding the encoding to use when writing files. 071 */ 072 public Application(Element subTreeRoot, XmlStructure parentSettings, Parameters param, String targetEncoding) { 073 ArgumentNotValid.checkNotNull(subTreeRoot, "Element e"); 074 ArgumentNotValid.checkNotNull(parentSettings, "XmlStructure parentSettings"); 075 ArgumentNotValid.checkNotNull(param, "Parameters param"); 076 ArgumentNotValid.checkNotNullOrEmpty(targetEncoding, "targetEncoding"); 077 this.targetEncoding = targetEncoding; 078 settings = new XmlStructure(parentSettings.getRoot()); 079 applicationRoot = subTreeRoot; 080 machineParameters = new Parameters(param); 081 // retrieve the specific settings for this instance 082 Element tmpSet = applicationRoot.element(Constants.COMPLETE_SETTINGS_BRANCH); 083 // Generate the specific settings by combining the general settings 084 // and the specific, (only if this instance has specific settings) 085 if (tmpSet != null) { 086 settings.overWrite(tmpSet); 087 } 088 // check if new machine parameters 089 machineParameters.newParameters(applicationRoot); 090 // Retrieve the variables for this instance. 091 extractVariables(); 092 } 093 094 /** 095 * Extract the local variables from the root. 096 * <p> 097 * Currently, this is the name and the optional applicationId. 098 */ 099 private void extractVariables() { 100 try { 101 // retrieve name 102 Attribute at = applicationRoot.attribute(Constants.APPLICATION_NAME_ATTRIBUTE); 103 if (at != null) { 104 // the name is actually the classpath, so the specific class is 105 // set as the name. It is the last element in the classpath. 106 nameWithNamePath = at.getText().trim(); 107 // the classpath is is separated by '.' 108 String[] stlist = nameWithNamePath.split(Constants.REGEX_DOT_CHARACTER); 109 // take the last part of the application class path as name. 110 // e.g. 111 // dk.netarkivet.archive.bitarhcive.BitarchiveMonitorApplication 112 // gets the name BitarchiveMonitorApplication. 113 name = stlist[stlist.length - 1]; 114 115 // overwriting the name, if it exists already; 116 // otherwise it is inserted. 117 String xmlName = XmlStructure.pathAndContentToXML(nameWithNamePath, 118 Constants.COMPLETE_APPLICATION_NAME_LEAF); 119 Element appXmlName = XmlStructure.makeElementFromString(xmlName); 120 settings.overWrite(appXmlName); 121 } else { 122 log.warn("Application has no name!"); 123 throw new IllegalState("Application has no name!"); 124 } 125 // look for the optional application instance id 126 Element elem = settings.getSubChild(Constants.SETTINGS_APPLICATION_INSTANCE_ID_LEAF); 127 if (elem != null && !elem.getText().trim().isEmpty()) { 128 applicationInstanceId = elem.getText().trim(); 129 } 130 } catch (Exception e) { 131 log.debug("Application variables not extractable.", e); 132 throw new IOFailure("Application variables not extractable.", e); 133 } 134 } 135 136 /** 137 * Uses the name and the optional applicationId to create an unique identification for this application. 138 * 139 * @return The unique identification of this application. 140 */ 141 public String getIdentification() { 142 StringBuilder res = new StringBuilder(name); 143 // use only applicationInstanceId if it exists and has content 144 if (applicationInstanceId != null && !applicationInstanceId.isEmpty()) { 145 res.append(Constants.UNDERSCORE); 146 res.append(applicationInstanceId); 147 } 148 return res.toString(); 149 } 150 151 /** 152 * @return the total name with directory path. 153 */ 154 public String getTotalName() { 155 return nameWithNamePath; 156 } 157 158 /** 159 * Creates the settings file for this application. This is extracted from the XMLStructure and put into a specific 160 * file. The name of the settings file for this application is: "settings_" + identification + ".xml". 161 * 162 * @param directory The directory where the settings file should be placed. 163 */ 164 public void createSettingsFile(File directory) { 165 ArgumentNotValid.checkNotNull(directory, "File directory"); 166 167 // make file 168 File settingsFile = new File(directory, Constants.PREFIX_SETTINGS + getIdentification() 169 + Constants.EXTENSION_XML_FILES); 170 try { 171 // initiate writer 172 PrintWriter pw = new PrintWriter(settingsFile, targetEncoding); 173 try { 174 // Extract the XML content of the branch for this application 175 pw.println(settings.getXML()); 176 } finally { 177 pw.close(); 178 } 179 } catch (FileNotFoundException e) { 180 log.debug("Cannot create settings file for an application.", e); 181 throw new IOFailure("Cannot create settings file for an application.", e); 182 } catch (UnsupportedEncodingException e) { 183 log.debug("Unsupported encoding '{}'", targetEncoding, e); 184 throw new IOFailure("Unsupported encoding '" + targetEncoding + "'", e); 185 } 186 } 187 188 /** 189 * Makes the install path with linux syntax. 190 * 191 * @return The path in linux syntax. 192 */ 193 public String installPathLinux() { 194 return machineParameters.getInstallDirValue() + Constants.SLASH 195 + settings.getSubChildValue(Constants.SETTINGS_ENVIRONMENT_NAME_LEAF); 196 } 197 198 /** 199 * Makes the install path with windows syntax. 200 * 201 * @return The path with windows syntax. 202 */ 203 public String installPathWindows() { 204 return machineParameters.getInstallDirValue() + Constants.BACKSLASH 205 + settings.getSubChildValue(Constants.SETTINGS_ENVIRONMENT_NAME_LEAF); 206 } 207 208 /** 209 * For acquiring the machine parameter variable. 210 * 211 * @return The machine parameter variable. 212 */ 213 public Parameters getMachineParameters() { 214 return machineParameters; 215 } 216 217 /** 218 * For acquiring all the values of the leafs at the end of the path. 219 * 220 * @param path The path to the branches. 221 * @return The values of the leafs. If no values were found, then an empty collection of strings are returned. 222 */ 223 public String[] getSettingsValues(String[] path) { 224 ArgumentNotValid.checkNotNull(path, "String[] path"); 225 ArgumentNotValid.checkNotNegative(path.length, "Length of String[] path"); 226 return settings.getLeafValues(path); 227 } 228 229 /** 230 * Returns the settings XML subtree for the application. 231 * @return the settings XML subtree for the application 232 */ 233 public XmlStructure getSettings() { 234 return settings; 235 } 236 237 /** 238 * Detects whether this is a Harvester app, which requires a harvester bundle to be deployed. 239 * @return <code>true if the is a harvester requiring a harvester bundle, else <code>false</code>.</code> 240 */ 241 public boolean isBundledHarvester() { 242 List<Element> classPaths = getMachineParameters().getClassPaths(); 243 for (Element classPathElement : classPaths) { 244 if (classPathElement.getText().contains("heritrix3")) { 245 return true; 246 } 247 } 248 return false; 249 } 250 251}