View Javadoc

1   /*
2    * This software was designed and created by Jason Carroll.
3    * Copyright (c) 2002, 2003, 2004 Jason Carroll.
4    * The author can be reached at jcarroll@cowsultants.com
5    * ITracker website: http://www.cowsultants.com
6    * ITracker forums: http://www.cowsultants.com/phpBB/index.php
7    *
8    * This program is free software; you can redistribute it and/or modify
9    * it only under the terms of the GNU General Public License as published by
10   * the Free Software Foundation; either version 2 of the License, or
11   * (at your option) any later version.
12   *
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   * GNU General Public License for more details.
17   */
18  
19  package org.itracker.web.actions.admin.configuration;
20  
21  import java.io.IOException;
22  import java.util.List;
23  
24  import javax.naming.InitialContext;
25  import javax.servlet.ServletException;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  import javax.servlet.http.HttpSession;
29  
30  import org.apache.commons.beanutils.PropertyUtils;
31  import org.apache.log4j.Logger;
32  import org.apache.struts.action.ActionForm;
33  import org.apache.struts.action.ActionForward;
34  import org.apache.struts.action.ActionMapping;
35  import org.apache.struts.action.ActionMessage;
36  import org.apache.struts.action.ActionMessages;
37  import org.apache.struts.upload.FormFile;
38  import org.itracker.core.resources.ITrackerResources;
39  import org.itracker.model.AbstractEntity;
40  import org.itracker.model.Configuration;
41  import org.itracker.model.CustomField;
42  import org.itracker.model.ImportDataModel;
43  import org.itracker.model.Issue;
44  import org.itracker.model.Project;
45  import org.itracker.model.User;
46  import org.itracker.services.ConfigurationService;
47  import org.itracker.services.ProjectService;
48  import org.itracker.services.UserService;
49  import org.itracker.services.exceptions.ImportExportException;
50  import org.itracker.services.util.ImportExportUtilities;
51  import org.itracker.services.util.SystemConfigurationUtilities;
52  import org.itracker.services.util.UserUtilities;
53  import org.itracker.web.actions.base.ItrackerBaseAction;
54  import org.itracker.web.util.Constants;
55  
56  
57  
58  /**
59    * Performs a verification on the import data to ensure that it contains no errors,
60    * applies any import options, and also updates the data to reuse any current system
61    * data if needed.  It also collects statistics on the import data to display to the user
62    * before the import is actually processed.
63    * <br><br>
64    * When reusing existing system data.  The following criteria is used to determine if
65    * a piece of data matches an existing system resource:<br>
66    * User - the login<br>
67    * Project - the project name<br>
68    * Status, Severity, Resolution - the name of the item as defined in the language root/base locale<br>
69    * Custom Fields - the label name of the custom field as defined in the language root/base locale<br>
70    */
71  public class ImportDataVerifyAction extends ItrackerBaseAction {
72  	private static final Logger log = Logger.getLogger(ImportDataVerifyAction.class);
73  	
74      private static final int UPDATE_STATUS = 1;
75      private static final int UPDATE_SEVERITY = 2;
76      private static final int UPDATE_RESOLUTION = 3;
77  
78      public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
79          ActionMessages errors = new ActionMessages();
80  
81  
82          if(! hasPermission(UserUtilities.PERMISSION_USER_ADMIN, request, response)) {
83              return mapping.findForward("unauthorized");
84          }
85  
86          try {
87              FormFile file = (FormFile) PropertyUtils.getSimpleProperty(form, "importFile");
88              String xmlData = new String(file.getFileData());
89  
90              ImportDataModel model = new ImportDataModel();
91              AbstractEntity[] importData = ImportExportUtilities.importIssues(xmlData);
92              boolean[] existingModel = new boolean[importData.length];
93  
94              model.setReuseUsers((Boolean) PropertyUtils.getSimpleProperty(form, "optionreuseusers"));
95              model.setReuseProjects((Boolean) PropertyUtils.getSimpleProperty(form, "optionreuseprojects"));
96              model.setReuseConfig((Boolean) PropertyUtils.getSimpleProperty(form, "optionreuseconfig"));
97              model.setCreatePasswords((Boolean) PropertyUtils.getSimpleProperty(form, "optioncreatepasswords"));
98              model.setData(importData, existingModel);
99  
100             InitialContext ic = new InitialContext();
101             checkConfig(model, ic);
102             log.debug(model.toString());
103             checkUsers(model, ic);
104             log.debug(model.toString());
105             checkProjects(model, ic);
106             log.debug(model.toString());
107             checkIssues(model, ic);
108             log.debug(model.toString());
109 
110             HttpSession session = request.getSession(true);
111             session.setAttribute(Constants.IMPORT_DATA_KEY, model);
112         } catch(ImportExportException iee) {
113             if(iee.getType() == ImportExportException.TYPE_INVALID_LOGINS) {
114                 log.error("Invalid logins found while verifying import data.");
115                 errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.importexport.invalidlogins", iee.getMessage()));
116             } else if(iee.getType() == ImportExportException.TYPE_INVALID_STATUS) {
117                 log.error("Invalid status found while verifying import data.");
118                 errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.importexport.invalidstatus", iee.getMessage()));
119             } else {
120                 log.error("Exception while verifying import data.", iee);
121                 errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system"));
122             }
123         } catch(Exception e) {
124             log.error("Exception while verifying import data.", e);
125             errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system"));
126         }
127 
128         if(! errors.isEmpty()) {
129             saveErrors(request, errors);
130             return mapping.getInputForward();
131         }
132 
133         return mapping.findForward("importdataverify");
134     }
135 
136     private void checkConfig(ImportDataModel model, InitialContext ic) throws ImportExportException {
137         try {
138             int maxSeverityValue = 1;
139             int maxResolutionValue = 1;
140 
141             ConfigurationService configurationService = getITrackerServices().getConfigurationService();
142 
143             List<Configuration> statuses = configurationService.getConfigurationItemsByType(SystemConfigurationUtilities.TYPE_STATUS, ImportExportUtilities.EXPORT_LOCALE);
144             List<Configuration> severities = configurationService.getConfigurationItemsByType(SystemConfigurationUtilities.TYPE_SEVERITY, ImportExportUtilities.EXPORT_LOCALE);
145             List<Configuration> resolutions = configurationService.getConfigurationItemsByType(SystemConfigurationUtilities.TYPE_RESOLUTION, ImportExportUtilities.EXPORT_LOCALE);
146             List<CustomField> fields = configurationService.getCustomFields(ImportExportUtilities.EXPORT_LOCALE);
147 
148             for(int i = 0; i < severities.size(); i++) {
149                 maxSeverityValue = Math.max(maxSeverityValue, Integer.parseInt(severities.get(i).getValue()));
150             }
151             for(int i = 0; i < resolutions.size(); i++) {
152                 maxResolutionValue = Math.max(maxResolutionValue, Integer.parseInt(resolutions.get(i).getValue()));
153             }
154 
155             AbstractEntity[] importData = model.getData();
156             for(int i = 0; i < importData.length; i++) {
157                 if(importData[i] instanceof Configuration) {
158                     // Need to check to see if it finds a matching name.  If so change value.
159                     // For status, if it finds a matching value but not name, this is an error.
160                     // Otherwise, just change the value for the resolution and severity.  Then iterate
161                     // through the issues and update the old value to the new one since they are all stored
162                     // as strings/ints, not the id to the config item.
163                     Configuration configItem = (Configuration) importData[i];
164                     if(configItem.getType() == SystemConfigurationUtilities.TYPE_STATUS) {
165                         boolean found = false;
166                         for(int j = 0; j < statuses.size(); j++) {
167                             if(model.getReuseConfig() && statuses.get(j).getName().equalsIgnoreCase(configItem.getName())) {
168                                 // Matching status, update issues
169                                 updateIssues(importData, UPDATE_STATUS, configItem.getValue(), statuses.get(j).getValue());
170                                 model.setExistingModel(i, true);
171                                 model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_STATUSES, ImportExportUtilities.IMPORT_STAT_REUSED);
172                                 found = true;
173                                 break;
174                             } else if(statuses.get(j).getValue().equalsIgnoreCase(configItem.getValue())) {
175                                 // Found a matching status value, and the name didn't match
176                                 throw new ImportExportException(configItem.getValue(), ImportExportException.TYPE_INVALID_STATUS);
177                             }
178                         }
179                         if(! found) {
180                             model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_STATUSES, ImportExportUtilities.IMPORT_STAT_NEW);
181                         }
182                     } else if(configItem.getType() == SystemConfigurationUtilities.TYPE_SEVERITY) {
183                         boolean found = false;
184                         if(model.getReuseConfig()) {
185                             for(int j = 0; j < severities.size(); j++) {
186                                 if(severities.get(j).getName().equalsIgnoreCase(configItem.getName())) {
187                                     // Matching severity, update issues
188                                     updateIssues(importData, UPDATE_SEVERITY, configItem.getValue(), severities.get(j).getValue());
189                                     model.setExistingModel(i, true);
190                                     model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_SEVERITIES, ImportExportUtilities.IMPORT_STAT_REUSED);
191                                     found = true;
192                                     break;
193                                 }
194                             }
195                         }
196                         if(! found) {
197                             updateIssues(importData, UPDATE_SEVERITY, configItem.getValue(), Integer.toString(++maxSeverityValue));
198                             configItem.setValue(Integer.toString(maxSeverityValue));
199                             model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_SEVERITIES, ImportExportUtilities.IMPORT_STAT_NEW);
200                         }
201                     } else if(configItem.getType() == SystemConfigurationUtilities.TYPE_RESOLUTION) {
202                         boolean found = false;
203                         if(model.getReuseConfig()) {
204                             for(int j = 0; j < resolutions.size(); j++) {
205                                 if(resolutions.get(j).getName().equalsIgnoreCase(configItem.getName())) {
206                                     // Matching resolution, update issues
207                                     updateIssues(importData, UPDATE_RESOLUTION, configItem.getValue(), resolutions.get(j).getValue());
208                                     model.setExistingModel(i, true);
209                                     model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_RESOLUTIONS, ImportExportUtilities.IMPORT_STAT_REUSED);
210                                     found = true;
211                                     break;
212                                 }
213                             }
214                         }
215                         if(! found) {
216                             updateIssues(importData, UPDATE_RESOLUTION, configItem.getValue(), Integer.toString(++maxResolutionValue));
217                             configItem.setValue(Integer.toString(maxResolutionValue));
218                             model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_RESOLUTIONS, ImportExportUtilities.IMPORT_STAT_NEW);
219                         }
220                     }
221                 } else if(importData[i] instanceof CustomField) {
222                     boolean found = false;
223                     CustomField field = (CustomField) importData[i];
224                     if(model.getReuseFields()) {
225                         for(int j = 0; j < fields.size(); j++) {
226                             if(fields.get(j).getFieldType() == field.getFieldType() /*&& fields.get(j).getName().equalsIgnoreCase(field.getName())*/) {
227                                 // Matching custom field.  Set id, but don't need to update issues
228                                 // since it contains the customfield model
229                                 field.setId(fields.get(j).getId());
230                                 model.setExistingModel(i, true);
231                                 model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_FIELDS, ImportExportUtilities.IMPORT_STAT_REUSED);
232                                 found = true;
233                                 break;
234                             }
235                         }
236                     }
237                     if(! found) {
238                         model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_FIELDS, ImportExportUtilities.IMPORT_STAT_NEW);
239                     }
240                 }
241             }
242         } catch(ImportExportException iee) {
243             throw iee;
244         } catch(Exception e) {
245             log.error("Error verifiying import data.", e);
246             throw new ImportExportException(e.getMessage());
247         }
248     }
249 
250     private void checkUsers(ImportDataModel model, InitialContext ic) throws ImportExportException {
251         String invalidLogins = null;
252 
253         try {
254             UserService userService = getITrackerServices().getUserService();
255 
256             AbstractEntity[] importData = model.getData();
257 
258             for(int i = 0; i < importData.length; i++) {
259                 if(importData[i] instanceof User) {
260                     User user = (User) importData[i];
261                     User existingUser = userService.getUserByLogin(user.getLogin());
262                     if(existingUser != null) {
263                         if(model.getReuseUsers()) {
264                             user.setId(existingUser.getId());
265                             model.setExistingModel(i, true);
266                             model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_USERS, ImportExportUtilities.IMPORT_STAT_REUSED);
267                             log.debug("Reusing existing user " + user.getLogin() + "(" + user.getId() + ") during import.");
268                         } else {
269                             log.debug("Existing user " + existingUser.getLogin() + "(" + existingUser.getId() + ") during import.  Adding to invalid login list.");
270                             invalidLogins = (invalidLogins == null ? 
271                             		existingUser.getLogin() : 
272                             	new StringBuffer(invalidLogins).append(", ").append(existingUser.getLogin()).toString());
273                         }
274                     } else {
275                         model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_USERS, ImportExportUtilities.IMPORT_STAT_NEW);
276                     }
277                 }
278             }
279         } catch(Exception e) {
280             log.error("Error verifiying import data.", e);
281             throw new ImportExportException(e.getMessage());
282         }
283 
284         if(invalidLogins != null) {
285             throw new ImportExportException(invalidLogins, ImportExportException.TYPE_INVALID_LOGINS);
286         }
287     }
288 
289     private void checkProjects(ImportDataModel model, InitialContext ic) throws ImportExportException {
290         try {
291             ProjectService projectService = getITrackerServices().getProjectService();
292 
293             List<Project> existingProjects = projectService.getAllProjects();
294             if(existingProjects.size() == 0) {
295                 return;
296             }
297 
298             AbstractEntity[] importData = model.getData();
299 
300             for(int i = 0; i < importData.length; i++) {
301                 if(importData[i] instanceof Project) {
302                     if(! model.getReuseProjects()) {
303                         model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_PROJECTS, ImportExportUtilities.IMPORT_STAT_NEW);
304                         continue;
305                     }
306 
307                     Project project = (Project) importData[i];
308                     boolean found = false;
309                     for(int j = 0; j < existingProjects.size(); j++) {
310                         log.debug("Project Name: " + project.getName() + "  Existing Project: " + existingProjects.get(j).getName());
311                         log.debug("Project Name: " + ITrackerResources.escapeUnicodeString(project.getName(), false) + "  Existing Project: " +  ITrackerResources.escapeUnicodeString(existingProjects.get(j).getName(), false));
312                         if(project.getName() != null && project.getName().equalsIgnoreCase(existingProjects.get(j).getName())) {
313                             project.setId(existingProjects.get(j).getId());
314                             model.setExistingModel(i, true);
315                             model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_PROJECTS, ImportExportUtilities.IMPORT_STAT_REUSED);
316                             found = true;
317                             log.debug("Reusing existing project " + project.getName() + "(" + project.getId() + ") during import.");
318                             break;
319                         }
320                     }
321                     if(! found) {
322                         model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_PROJECTS, ImportExportUtilities.IMPORT_STAT_NEW);
323                     }
324                 }
325             }
326         } catch(Exception e) {
327             log.error("Error verifiying import data.", e);
328             throw new ImportExportException(e.getMessage());
329         }
330     }
331 
332     private void checkIssues(ImportDataModel model, InitialContext ic) throws ImportExportException {
333         AbstractEntity[] importData = model.getData();
334 
335         for(int i = 0; i < importData.length; i++) {
336             if(importData[i] instanceof Issue) {
337                 model.addVerifyStatistic(ImportExportUtilities.IMPORT_STAT_ISSUES, ImportExportUtilities.IMPORT_STAT_NEW);
338             }
339         }
340     }
341 
342     private void updateIssues(AbstractEntity[] models, int updateType, String currentValue, String newValue) throws ImportExportException {
343         if(models == null || currentValue == null || newValue == null) {
344             return;
345         }
346 
347         try {
348             for(int i = 0; i < models.length; i++) {
349                 if(models[i] instanceof Issue) {
350                     Issue issue = (Issue) models[i];
351                     if(updateType == UPDATE_STATUS && issue.getStatus() == Integer.parseInt(currentValue)) {
352                         issue.setStatus(Integer.parseInt(newValue));
353                     } else if(updateType == UPDATE_SEVERITY && issue.getSeverity() == Integer.parseInt(currentValue)) {
354                         issue.setSeverity(Integer.parseInt(newValue));
355                     } else if(updateType == UPDATE_RESOLUTION && currentValue.equalsIgnoreCase(issue.getResolution())) {
356                         issue.setResolution(newValue);
357                     }
358                 }
359             }
360         } catch(Exception e) {
361             log.debug("Unable to update configuration data in issues.", e);
362             throw new ImportExportException("Unable to update configuration data in issues: " + e.getMessage());
363         }
364     }
365 }
366