ImportDataProcessAction.java

/*
 * This software was designed and created by Jason Carroll.
 * Copyright (c) 2002, 2003, 2004 Jason Carroll.
 * The author can be reached at jcarroll@cowsultants.com
 * ITracker website: http://www.cowsultants.com
 * ITracker forums: http://www.cowsultants.com/phpBB/index.php
 *
 * This program is free software; you can redistribute it and/or modify
 * it only under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.itracker.web.actions.admin.configuration;

import org.apache.log4j.Logger;
import org.apache.struts.action.*;
import org.itracker.ProjectException;
import org.itracker.UserException;
import org.itracker.core.resources.ITrackerResources;
import org.itracker.model.*;
import org.itracker.model.util.CustomFieldUtilities;
import org.itracker.model.util.SystemConfigurationUtilities;
import org.itracker.model.util.UserUtilities;
import org.itracker.services.ConfigurationService;
import org.itracker.services.IssueService;
import org.itracker.services.ProjectService;
import org.itracker.services.UserService;
import org.itracker.PasswordException;
import org.itracker.web.actions.base.ItrackerBaseAction;
import org.itracker.web.util.Constants;
import org.itracker.web.util.ImportExportUtilities;
import org.itracker.web.util.LoginUtilities;
import org.itracker.web.util.ServletContextUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;


public class ImportDataProcessAction extends ItrackerBaseAction {
    private static final Logger log = Logger.getLogger(ImportDataProcessAction.class);

    private static final class ActionException extends Exception {

    }

    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ActionMessages errors = new ActionMessages();


        if (!LoginUtilities.hasPermission(UserUtilities.PERMISSION_USER_ADMIN, request, response)) {
            return mapping.findForward("unauthorized");
        }

        try {

            HttpSession session = request.getSession(false);
            User importer = LoginUtilities.getCurrentUser(request);
            if (importer == null || !importer.isSuperUser()) {
                return mapping.findForward("unauthorized");
            }

            ImportDataModel model = (ImportDataModel) session.getAttribute(Constants.IMPORT_DATA_KEY);
            if (null == model) {
                errors.add(ActionMessages.GLOBAL_MESSAGE,
                        new ActionMessage("itracker.web.error.system.message", "No model in session.", "Request"));
            }
            checkErrors(errors);
            log.debug("Importing configuration data.");
            createConfig(model, importer, errors);

            checkErrors(errors);
            log.debug("Importing user data.");
            createUsers(model, importer, errors);

            checkErrors(errors);
            log.debug("Importing project data.");
            createProjects(model, importer, errors);

            checkErrors(errors);
            log.debug("Importing issue data.");
            createIssues(model, importer, errors);

            checkErrors(errors);
            log.debug("Import complete.");

        } catch (ActionException e) {
            if (errors.isEmpty()) {
                log.error("failed with empty errors", e);
                errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage(), "Unexpected"));
            }
            return handleErrors(errors, request, mapping);
        } catch (Exception e) {
            log.error("Exception while importing data.", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage(), "Unexpected"));
            return handleErrors(errors, request, mapping);
        }

        errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.importexport.importcomplete"));
        saveMessages(request, errors);
        // reset import models
        request.getSession().removeAttribute(Constants.IMPORT_DATA_KEY);
        return mapping.findForward("adminindex");
    }

    private void checkErrors(ActionMessages errors) throws ActionException{
        if (!errors.isEmpty()) {
            throw new ActionException();
        }
    }

    private ActionForward handleErrors(ActionMessages errors, HttpServletRequest request, ActionMapping mapping) {

        if (!errors.isEmpty()) {
            saveErrors(request, errors);
            return mapping.findForward("error");
        }
        return null;

    }
    private boolean createConfig(ImportDataModel model, User importer, ActionMessages errors) {
        try {
            ConfigurationService configurationService = ServletContextUtils.getItrackerServices().getConfigurationService();

            AbstractEntity[] importData = model.getData();
            for (int i = 0; i < importData.length; i++) {
                if (importData[i] instanceof Configuration && null == model.getExistingModel(i)) {
                    Configuration configItem = (Configuration) importData[i];
                    Configuration newConfigItem = configurationService.createConfigurationItem(configItem);
                    configItem.setId(newConfigItem.getId());

                    // Now add a new language key
                    String key = SystemConfigurationUtilities.getLanguageKey(configItem);
                    configurationService.updateLanguageItem(new Language(ImportExportUtilities.EXPORT_LOCALE_STRING, key, configItem.getName()));
                    ITrackerResources.clearKeyFromBundles(key, true);
                } else if (importData[i] instanceof CustomField && null == model.getExistingModel(i)) {
                    CustomField customField = (CustomField) importData[i];
                    CustomField newCustomField = configurationService.createCustomField(customField);
                    customField.setId(newCustomField.getId());

                    // Now add new language keys.  One for the field and then add one for for
                    // each option that exists.
                    String key = CustomFieldUtilities.getCustomFieldLabelKey(customField.getId());
                    // TODO, removed name attribute, so it's defaulted to the key
                    configurationService.updateLanguageItem(new Language(ImportExportUtilities.EXPORT_LOCALE_STRING, key, key));
                    ITrackerResources.clearKeyFromBundles(key, true);
                    if (customField.getFieldType() == CustomField.Type.LIST) {
                        // TODO, removed name attribute, so it's defaulted to the key
                        for (int j = 0; j < customField.getOptions().size(); j++) {
                            String optionKey = CustomFieldUtilities.getCustomFieldOptionLabelKey(customField.getId(), customField.getOptions().get(j).getId());
                            configurationService.updateLanguageItem(new Language(ImportExportUtilities.EXPORT_LOCALE_STRING, optionKey, optionKey));
                            ITrackerResources.clearKeyFromBundles(optionKey, true);
                        }
                    }
                }
            }
            configurationService.resetConfigurationCache();
        } catch (RuntimeException e) {
            log.error("failed to import", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        }

        return true;
    }

    private boolean createUsers(ImportDataModel model, User importer, ActionMessages errors) {
        try {
            UserService userService = ServletContextUtils.getItrackerServices().getUserService();

            AbstractEntity[] importData = model.getData();
            for (int i = 0; i < importData.length; i++) {
                if (importData[i] instanceof User && null == model.getExistingModel(i)) {
                    User user = (User) importData[i];
                    user.setRegistrationType(UserUtilities.REGISTRATION_TYPE_IMPORT);
                    if (model.getCreatePasswords()) {
                        user.setPassword(UserUtilities.encryptPassword(user.getLogin()));
                    }
                    user.setLogin(user.getLogin());
                    int status = user.getStatus();
                    User newUser = userService.createUser(user);
                    user.setId(newUser.getId());
                    newUser.setStatus(status);
                    userService.updateUser(newUser);
                }
            }
        } catch (RuntimeException e) {
            log.error("failed to import", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        } catch (PasswordException e) {
            log.error("failed to import", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        } catch (UserException e) {
            log.error("failed to import", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        }

        return true;
    }

    private boolean createProjects(ImportDataModel model, User importer, ActionMessages errors) {
        try {
            ProjectService projectService = ServletContextUtils.getItrackerServices().getProjectService();

            AbstractEntity[] importData = model.getData();
            for (int i = 0; i < importData.length; i++) {
                if (importData[i] instanceof Project && null == model.getExistingModel(i)) {
                    final Project project = (Project) importData[i];

                    Project importProject = new Project(project.getName());
                    importProject.setStatus(project.getStatus());
                    importProject.setDescription(project.getDescription());

                    importProject = projectService.createProject(importProject, importer.getId());
                    project.setId(importProject.getId());

                    HashSet<Integer> setOfOwnerIDs = new HashSet<Integer>();
                    for (int j = 0; j < project.getOwners().size(); j++) {
                        setOfOwnerIDs.add(project.getOwners().get(j).getId());
                    }
                    projectService.setProjectOwners(importProject, setOfOwnerIDs);

                    HashSet<Integer> setOfFieldIds = new HashSet<Integer>();
                    for (CustomField field: project.getCustomFields()) {
                        setOfFieldIds.add(field.getId());
                    }
                    projectService.setProjectFields(importProject, setOfFieldIds);

                    List<Component> components = project.getComponents();
                    for (Component component: components) {
                        Component newComponent = projectService.addProjectComponent(importProject.getId(), component);
                        component.setId(newComponent.getId());
                    }

                    List<Version> versions = project.getVersions();
                    for (Version version:  versions) {
                        Version newVersion = projectService.addProjectVersion(importProject.getId(), version);
                        version.setId(newVersion.getId());
                    }
                }
            }
        } catch (RuntimeException e) {
            log.error("createProjects: import failed.", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        }

        return true;
    }

    private boolean createIssues(ImportDataModel model, User importer, ActionMessages errors) {
        try {
            IssueService issueService = ServletContextUtils.getItrackerServices().getIssueService();

            AbstractEntity[] importData = model.getData();
            for (int i = 0; i < importData.length; i++) {
                if (importData[i] instanceof Issue && null == model.getExistingModel(i)) {

                    Issue issue = (Issue) importData[i];
                    if (issue.getProject().getStatus() != Status.ACTIVE) {
                        log.warn("createIssues: could not create issue for NON-Active project: " + issue);
                        continue;
                    }

                    final List<IssueHistory> history = issue.getHistory();
                    issue.setHistory(new ArrayList<IssueHistory>());

                    List<Component> componentsList = issue.getComponents();
                    issue.setComponents(new ArrayList<Component>());

                    List<Version> versionsList = issue.getVersions();
                    issue.setVersions(new ArrayList<Version>());

                    List<IssueAttachment> attachments = issue.getAttachments();
                    issue.setAttachments(new ArrayList<IssueAttachment>());


                    Issue newIssue = issueService.createIssue(issue,
                            issue.getProject().getId(), issue.getCreator().getId(), importer.getId());
                    issue.setId(newIssue.getId());

                    // Assign the issue
                    if (issue.getOwner() != null) {
                        issueService.assignIssue(issue.getId(), issue.getOwner().getId(), importer.getId());
                    }

                    // Now set Issue Custom Fields
                    // TODO:?
                    List<IssueField> fields = issue.getFields();
                    if (!fields.isEmpty()) {
                        for (IssueField f : fields) {
                            f.setIssue(issue);
                        }
                        issueService.setIssueFields(issue.getId(), issue.getFields());
                    }


                    // Now add all the issue history
                    for (IssueHistory h: history) {
                        h.setIssue(newIssue);
                        issueService.addIssueHistory(h);
                     // needed?   issueService.updateIssue(newIssue, importer.getId());
                    }


                    // Now add components and versions
                    HashSet<Integer> components = new HashSet<Integer>();
                    for (Component c: componentsList) {
                        components.add(c.getId());
                    }
                    if (!components.isEmpty()) {
                        issueService.setIssueComponents(newIssue.getId(), components, importer.getId());
                    }

                    HashSet<Integer> versions = new HashSet<Integer>();
                    for (Version v: versionsList) {
                        versions.add(v.getId());
                    }
                    if (!versions.isEmpty()) {
                        issueService.setIssueVersions(newIssue.getId(), versions, importer.getId());
                    }


                    // Now add any attachments
                    for (IssueAttachment a : attachments) {
                        a.setIssue(newIssue);
                        issueService.addIssueAttachment(a, null);
                    }
                }
            }
        } catch (ProjectException e) {
            log.error("createIssues: import failed.", e);
            errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("itracker.web.error.system.message", e.getMessage()));
            return false;
        }

        return true;
    }

}