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