001/* 002 * #%L 003 * Netarchivesuite - harvester 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 */ 023 024package dk.netarkivet.harvester.webinterface; 025 026import java.util.ArrayList; 027import java.util.Date; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Map; 031 032import javax.servlet.ServletRequest; 033import javax.servlet.jsp.PageContext; 034 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import dk.netarkivet.common.exceptions.ArgumentNotValid; 039import dk.netarkivet.common.exceptions.ForwardedToErrorPage; 040import dk.netarkivet.common.utils.DomainUtils; 041import dk.netarkivet.common.utils.I18n; 042import dk.netarkivet.common.webinterface.HTMLUtils; 043import dk.netarkivet.harvester.datamodel.Domain; 044import dk.netarkivet.harvester.datamodel.DomainConfiguration; 045import dk.netarkivet.harvester.datamodel.DomainDAO; 046import dk.netarkivet.harvester.datamodel.HarvestDefinitionDAO; 047import dk.netarkivet.harvester.datamodel.PartialHarvest; 048import dk.netarkivet.harvester.datamodel.Schedule; 049import dk.netarkivet.harvester.datamodel.ScheduleDAO; 050import dk.netarkivet.harvester.datamodel.SparseDomainConfiguration; 051import dk.netarkivet.harvester.datamodel.SparsePartialHarvest; 052import dk.netarkivet.harvester.datamodel.extendedfield.ExtendedFieldTypes; 053 054/** 055 * This class contains the methods for updating data for selective harvests. 056 */ 057@SuppressWarnings({"unchecked"}) 058public final class SelectiveHarvestUtil { 059 060 static final Logger log = LoggerFactory.getLogger(SelectiveHarvestUtil.class); 061 062 /** 063 * Utility class. No instances. 064 */ 065 private SelectiveHarvestUtil() { 066 } 067 068 069 /** 070 * Update or create a partial harvest definition. 071 * 072 * @param context JSP context of this call. Contains parameters as described in 073 * Definitions-edit-selective-harvest.jsp 074 * @param i18n Translation information. 075 * @param unknownDomains List to which unknown legal domains are added. 076 * @param illegalDomains List to which illegal domains are added, 077 */ 078 public static void processRequest(PageContext context, I18n i18n, List<String> unknownDomains, 079 List<String> illegalDomains) { 080 ArgumentNotValid.checkNotNull(context, "PageContext context"); 081 ArgumentNotValid.checkNotNull(i18n, "I18n i18n"); 082 ArgumentNotValid.checkNotNull(unknownDomains, "List unknownDomains"); 083 ArgumentNotValid.checkNotNull(illegalDomains, "List illegalDomains"); 084 log.info("Starting method processRequest"); 085 // Was the set next date button pressed? 086 boolean setNextDateOnly = HTMLUtils.parseOptionalBoolean(context, Constants.NEXTDATE_SUBMIT, false); 087 if (setNextDateOnly) { 088 HTMLUtils.forwardOnEmptyParameter(context, Constants.NEXTDATE_PARAM, Constants.NEXTDATE_PARAM); 089 HTMLUtils.forwardOnEmptyParameter(context, Constants.HARVEST_ID, Constants.HARVEST_ID); 090 091 // If the override date is set, parse it and set the override date. 092 Date date = HTMLUtils.parseOptionalDate(context, Constants.NEXTDATE_PARAM, I18n.getString( 093 dk.netarkivet.harvester.Constants.TRANSLATIONS_BUNDLE, context.getResponse().getLocale(), 094 "harvestdefinition.schedule.edit.timeformat"), null); 095 long harvestId = HTMLUtils.parseOptionalLong(context, Constants.HARVEST_ID, -1L); 096 097 HarvestDefinitionDAO.getInstance().updateNextdate(harvestId, date); 098 099 return; // nothin' more to do! 100 } 101 102 ServletRequest request = context.getRequest(); 103 if (request.getParameter(Constants.UPDATE_PARAM) == null) { 104 return; // nothing to do. 105 } 106 107 String deleteConfig = request.getParameter(Constants.DELETECONFIG_PARAM); 108 // Case where we are removing a configuration 109 // In this we make the delete, and then return; 110 if (deleteConfig != null) { 111 HTMLUtils.forwardOnEmptyParameter(context, Constants.DELETECONFIG_PARAM); 112 deleteConfig(context, i18n, deleteConfig); 113 return; 114 } 115 116 PartialHarvest hdd = updateHarvestDefinition(context, i18n, unknownDomains, illegalDomains); 117 118 ExtendedFieldValueDefinition.processRequest(context, i18n, hdd, ExtendedFieldTypes.HARVESTDEFINITION); 119 HarvestDefinitionDAO.getInstance().update(hdd); 120 121 boolean changed = false; 122 123 // If the override date is set, parse it and set the override date. 124 Date date = HTMLUtils.parseOptionalDate(context, Constants.NEXTDATE_PARAM, I18n.getString( 125 dk.netarkivet.harvester.Constants.TRANSLATIONS_BUNDLE, context.getResponse().getLocale(), 126 "harvestdefinition.schedule.edit.timeformat"), null); 127 if (date != null) { 128 hdd.setNextDate(date); 129 changed |= true; 130 } 131 132 // Case where we are adding domains that didn't exist before 133 // This uses two parameters because it has an input field and a submit 134 // button. 135 if (request.getParameter(Constants.ADDDOMAINS_PARAM) != null) { 136 HTMLUtils.forwardOnMissingParameter(context, Constants.UNKNOWN_DOMAINS_PARAM); 137 changed |= addDomainsToHarvest(hdd, request.getParameter(Constants.UNKNOWN_DOMAINS_PARAM)); 138 } 139 140 if (changed) { 141 HarvestDefinitionDAO.getInstance().update(hdd); 142 } 143 144 } 145 146 /** 147 * Updates the harvest definition with posted values. 148 * 149 * @param context The context that the web request processing happens in 150 * @param i18n Translation information for this site section. 151 * @param unknownDomains List to add unknown but legal domains to. 152 * @param illegalDomains List to add illegal domains to. 153 * @return The updated harvest definition. This object holds an edition that is legal to use for further updates 154 * (adding or deleting domains) 155 */ 156 private static PartialHarvest updateHarvestDefinition(PageContext context, I18n i18n, List<String> unknownDomains, 157 List<String> illegalDomains) { 158 ServletRequest request = context.getRequest(); 159 HTMLUtils.forwardOnEmptyParameter(context, Constants.HARVEST_PARAM, Constants.SCHEDULE_PARAM); 160 String name = request.getParameter(Constants.HARVEST_PARAM); 161 String oldname = request.getParameter(Constants.HARVEST_OLD_PARAM); 162 if (oldname == null) { 163 oldname = ""; 164 } 165 166 HTMLUtils.forwardOnMissingParameter(context, Constants.COMMENTS_PARAM, Constants.DOMAINLIST_PARAM, 167 Constants.AUDIENCE_PARAM); 168 String scheduleName = request.getParameter(Constants.SCHEDULE_PARAM); 169 Schedule sched = ScheduleDAO.getInstance().read(scheduleName); 170 if (sched == null) { 171 HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;unknown.schedule.0", scheduleName); 172 throw new ForwardedToErrorPage("Schedule '" + scheduleName + "' not found"); 173 } 174 175 String comments = request.getParameter(Constants.COMMENTS_PARAM); 176 String audience = request.getParameter(Constants.AUDIENCE_PARAM); 177 178 List<DomainConfiguration> dc = getDomainConfigurations(request.getParameterMap()); 179 addDomainsToConfigurations(dc, request.getParameter(Constants.DOMAINLIST_PARAM), unknownDomains, illegalDomains); 180 181 // If necessary create harvest from scratch 182 HarvestDefinitionDAO hddao = HarvestDefinitionDAO.getInstance(); 183 if ((request.getParameter(Constants.CREATENEW_PARAM) != null)) { 184 if (hddao.exists(name)) { 185 HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;harvest.definition.0.already.exists", name); 186 throw new ForwardedToErrorPage("A harvest definition " + "called '" + name + "' already exists"); 187 } 188 PartialHarvest hdd = new PartialHarvest(dc, sched, name, comments, audience); 189 hdd.setActive(false); 190 hddao.create(hdd); 191 return hdd; 192 } else { 193 long edition = HTMLUtils.parseOptionalLong(context, Constants.EDITION_PARAM, Constants.NO_EDITION); 194 PartialHarvest hdd; 195 if (oldname.equals(name)) { 196 hdd = (PartialHarvest) hddao.getHarvestDefinition(name); 197 } else { 198 if (hddao.exists(name)) { 199 HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;harvest.definition.0.already.exists", name); 200 throw new ForwardedToErrorPage("A harvest definition " + "called '" + name + "' already exists"); 201 } else { 202 hdd = (PartialHarvest) hddao.getHarvestDefinition(oldname); 203 hdd.setName(name); 204 } 205 } 206 if (hdd.getEdition() != edition) { 207 HTMLUtils.forwardWithRawErrorMessage(context, i18n, "errormsg;harvest.definition.changed.0.retry.1", 208 "<br/><a href=\"Definitions-edit-selective-harvest.jsp?" + Constants.HARVEST_PARAM + "=" 209 + HTMLUtils.encodeAndEscapeHTML(name) + "\">", "</a>"); 210 throw new ForwardedToErrorPage("Harvest definition '" + name + "' has changed"); 211 } 212 // update the harvest definition 213 hdd.setDomainConfigurations(dc); 214 hdd.setSchedule(sched); 215 hdd.setComments(comments); 216 hdd.setAudience(audience); 217 hddao.update(hdd); 218 return hdd; 219 } 220 } 221 222 /** 223 * Delete a domain configuration from a harvestdefinition. 224 * 225 * @param context The web server context for the JSP page. 226 * @param i18n Translation information for this site section. 227 * @param deleteConfig the configuration to delete, in the form of a domain name, a colon, a configuration name. 228 */ 229 private static void deleteConfig(PageContext context, I18n i18n, String deleteConfig) { 230 HTMLUtils.forwardOnEmptyParameter(context, Constants.HARVEST_PARAM, Constants.SCHEDULE_PARAM); 231 ServletRequest request = context.getRequest(); 232 String name = request.getParameter(Constants.HARVEST_PARAM); 233 HarvestDefinitionDAO hddao = HarvestDefinitionDAO.getInstance(); 234 if (!hddao.exists(name)) { 235 HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;harvestdefinition.0.does.not.exist", name); 236 throw new ForwardedToErrorPage("Harvestdefinition '" + name + "' does not exist"); 237 } 238 SparsePartialHarvest sph = hddao.getSparsePartialHarvest(name); 239 240 String[] domainConfigPair = deleteConfig.split(":", 2); 241 if (domainConfigPair.length < 2) { 242 HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;malformed.domain.config.pair.0", deleteConfig); 243 throw new ForwardedToErrorPage("Malformed domain-config pair " + deleteConfig); 244 } 245 String domainName = domainConfigPair[0]; 246 String configName = domainConfigPair[1]; 247 SparseDomainConfiguration key = new SparseDomainConfiguration(domainName, configName); 248 249 hddao.removeDomainConfiguration(sph.getOid(), key); 250 } 251 252 /** 253 * Extract domain configuration list from a map of parameters. All key that starts with Constants.DOMAIN_IDENTIFIER 254 * are treated as a concatenation of : DOMAIN_IDENTIFIER + domain name. The corresponding value in the map is 255 * treated as the configuration name Entries that do not match this pattern are ignored. 256 * 257 * @param configurations a mapping (domain to its configurations) 258 * @return a list of domain configurations 259 */ 260 private static List<DomainConfiguration> getDomainConfigurations(Map<String, String[]> configurations) { 261 List<DomainConfiguration> dcList = new ArrayList<DomainConfiguration>(); 262 263 for (Map.Entry<String, String[]> param : configurations.entrySet()) { 264 if (param.getKey().startsWith(Constants.DOMAIN_IDENTIFIER)) { 265 String domainName = param.getKey().substring(Constants.DOMAIN_IDENTIFIER.length()); 266 Domain domain = DomainDAO.getInstance().read(domainName); 267 for (String configurationName : param.getValue()) { 268 dcList.add(domain.getConfiguration(configurationName)); 269 } 270 } 271 } 272 return dcList; 273 } 274 275 /** 276 * Given a list of domain configurations and a list of domains, add the default configurations for the domains to 277 * the configuration list. If any of the domains are unknown, their names are instead appended to the argument 278 * unknownDomains (with newline separation) 279 * 280 * @param dcList the initial list of configurations 281 * @param extraDomains the domains to be added to dcList with default configurations 282 * @param unknownDomains a list to add unknown, legal domains to 283 * @param illegalDomains a list to add illegal domains to 284 */ 285 private static void addDomainsToConfigurations(List<DomainConfiguration> dcList, String extraDomains, 286 List<String> unknownDomains, List<String> illegalDomains) { 287 String[] domains = extraDomains.split("\\s+"); 288 DomainDAO ddao = DomainDAO.getInstance(); 289 for (String domain : domains) { 290 domain = domain.trim(); 291 if (domain.length() > 0) { 292 if (ddao.exists(domain)) { 293 Domain d = ddao.read(domain); 294 if (!dcList.contains(d.getDefaultConfiguration())) { 295 dcList.add(d.getDefaultConfiguration()); 296 } 297 } else { 298 if (DomainUtils.isValidDomainName(domain)) { 299 unknownDomains.add(domain); 300 } else { 301 illegalDomains.add(domain); 302 } 303 } 304 } 305 } 306 } 307 308 /** 309 * Given a harvest and list of domains, this method creates all the specified domains and adds them to the harvest 310 * with their default configuration. 311 * 312 * @param hdd The harvest definition to change. 313 * @param domains a whitespace-separated list of domains to create and add to harvest 314 * @return True if changes were made to hdd. 315 */ 316 private static boolean addDomainsToHarvest(PartialHarvest hdd, String domains) { 317 String[] domainsS = domains.split("\\s"); 318 List<DomainConfiguration> configurations = new ArrayList<DomainConfiguration>(); 319 for (String domainName : domainsS) { 320 if (domainName != null && !domainName.isEmpty()) { 321 if (DomainUtils.isValidDomainName(domainName)) { 322 Domain domain = Domain.getDefaultDomain(domainName); 323 DomainDAO.getInstance().create(domain); 324 configurations.add(domain.getDefaultConfiguration()); 325 } 326 } 327 } 328 if (configurations.size() > 0) { 329 Iterator<DomainConfiguration> existingConfigurations = hdd.getDomainConfigurations(); 330 while (existingConfigurations.hasNext()) { 331 configurations.add(existingConfigurations.next()); 332 } 333 hdd.setDomainConfigurations(configurations); 334 return true; 335 } else { 336 return false; 337 } 338 } 339}