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