/* * Copyright 2012 Anchialas and Mykolas. * Copyright 2015 Matthias Bläsing * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.kenai.redminenb.repository; import com.kenai.redminenb.Redmine; import com.kenai.redminenb.project.RedmineProjectPanel; import com.kenai.redminenb.ui.Defaults; import com.kenai.redminenb.util.ListComboBoxModel; import com.kenai.redminenb.util.RedmineUtil; import com.kenai.redminenb.api.AuthMode; import com.kenai.redminenb.util.NestedProject; import com.taskadapter.redmineapi.RedmineException; import com.taskadapter.redmineapi.RedmineManager; import com.taskadapter.redmineapi.RedmineManagerFactory; import com.taskadapter.redmineapi.bean.Project; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.RuntimeErrorException; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.jsoup.Jsoup; import org.netbeans.modules.bugtracking.spi.RepositoryController; import org.netbeans.modules.bugtracking.spi.RepositoryInfo; import org.openide.util.*; import org.openide.util.NbBundle.Messages; /** * Redmine repository parameter controller. * * @author Mykolas * @author Anchialas <anchialas@gmail.com> */ @Messages({ "MSG_MissingName=Missing Name", "MSG_WrongUrl=Wrong URL format", "MSG_MissingUrl=Missing URL", "MSG_MissingUsername=Missing Username", "MSG_MissingPassword=Missing Password", "MSG_MissingAccessKey=Missing Access Key", "MSG_MissingProject=No Project is selected", "MSG_TrackerAlreadyExists=Issue Tracker with the same name already exists", "MSG_RepositoryAlreadyExists=The same Issue Tracker already exists", "MSG_AuthSuccessful=Successfully authenticated", "MSG_Unchanged=Unchanged" }) public class RedmineRepositoryController implements RepositoryController, DocumentListener, ActionListener, ItemListener { private static final Logger LOG = Logger.getLogger(RedmineRepositoryController.class.getName()); private final RedmineRepository repository; private final RedmineRepositoryPanel panel; private String errorMessage; private final ChangeSupport support = new ChangeSupport(this); @SuppressWarnings("LeakingThisInConstructor") public RedmineRepositoryController(RedmineRepository repository) { this.repository = repository; panel = new RedmineRepositoryPanel(this); panel.nameTextField.getDocument().addDocumentListener(this); panel.urlTextField.getDocument().addDocumentListener(this); panel.accessKeyTextField.getDocument().addDocumentListener(this); panel.userField.getDocument().addDocumentListener(this); panel.pwdField.getDocument().addDocumentListener(this); panel.featureWatchers.addActionListener(this); panel.featureDeleteAttachments.addActionListener(this); panel.projectComboBox.addItemListener(this); panel.connectButton.addActionListener(this); panel.createNewProjectButton.addActionListener(this); panel.rbAccessKey.addActionListener(this); panel.rbCredentials.addActionListener(this); panel.httpAuthEnabled.addActionListener(this); panel.httpPwdField.getDocument().addDocumentListener(this); panel.httpUserField.getDocument().addDocumentListener(this); } @Override public JComponent getComponent() { return panel; } private String getUrl() { String url = panel.urlTextField.getText().trim(); return url.endsWith("/") ? url.substring(0, url.length() - 1) : url; // NOI18N } private String getName() { return panel.nameTextField.getText().trim(); } private String getUser() { return panel.userField.getText(); } private char[] getPassword() { return panel.pwdField.getPassword(); } public AuthMode getAuthMode() { return panel.getAuthMode(); } private String getAccessKey() { return panel.accessKeyTextField.getText().trim(); } private ProjectId getProject() { return (ProjectId) panel.projectComboBox.getSelectedItem(); } private boolean isFeatureWatchers() { return panel.featureWatchers.isSelected(); } private boolean isFeatureDeleteAttachments() { return panel.featureDeleteAttachments.isSelected(); } private String getHttpUser() { return panel.httpUserField.getText(); } private char[] getHttpPassword() { return panel.httpPwdField.getPassword(); } @Override @SuppressWarnings("ResultOfObjectAllocationIgnored") public boolean isValid() { errorMessage = null; // check url String url = getUrl(); if (getUrl().trim().isEmpty()) { // NOI18N errorMessage = Bundle.MSG_MissingUrl(); return false; } try { new URL(url); // check this first even if URL is an URI new URI(url); } catch (MalformedURLException | URISyntaxException ex) { errorMessage = Bundle.MSG_WrongUrl(); Redmine.LOG.log(Level.FINE, errorMessage, ex); return false; } // username and password required if not access key authentication if (getAuthMode() == AuthMode.Credentials) { if (StringUtils.isBlank(getUser())) { errorMessage = Bundle.MSG_MissingUsername(); return false; } else if (ArrayUtils.isEmpty(getPassword())) { errorMessage = Bundle.MSG_MissingPassword(); return false; } } else { if (StringUtils.isBlank(getAccessKey())) { errorMessage = Bundle.MSG_MissingAccessKey(); return false; } } if (getName().trim().isEmpty()) { errorMessage = Bundle.MSG_MissingName(); return false; } return true; } @Override public HelpCtx getHelpCtx() { return new HelpCtx(getClass().getName()); } @Override public String getErrorMessage() { return errorMessage; } @Override public void applyChanges() { String httpUser = null; char[] httpPassword = null; if(panel.httpAuthEnabled.isSelected()) { httpUser = getHttpUser(); httpPassword = getHttpPassword(); } repository.setInfoValues(getName(), getUrl(), getUser(), getPassword(), getAccessKey(), getAuthMode(), getProject() == null ? null : getProject().getId(), isFeatureWatchers(), httpUser, httpPassword, isFeatureDeleteAttachments()); } @Override public final void populate() { assert SwingUtilities.isEventDispatchThread(); panel.progressIcon.setIcon(null); panel.progressTextPane.setText(""); panel.progressPanel.setVisible(true); panel.nameTextField.setText(repository.getDisplayName()); panel.urlTextField.setText(repository.getUrl()); panel.setAuthMode(repository.getAuthMode()); panel.accessKeyTextField.setText(repository.getAccessKey()); panel.userField.setText(repository.getUsername()); panel.pwdField.setText(repository.getPassword() == null ? "" : String.valueOf(repository.getPassword())); RepositoryInfo info = repository.getInfo(); if( info != null && info.getHttpUsername() != null && (! info.getHttpUsername().isEmpty()) && info.getHttpPassword() != null && info.getHttpPassword().length > 0) { panel.httpAuthEnabled.setSelected(true); panel.httpUserField.setText(info.getHttpUsername()); panel.httpPwdField.setText(new String(info.getHttpPassword())); } else { panel.httpAuthEnabled.setSelected(false); panel.httpUserField.setText(""); panel.httpPwdField.setText(""); } List<ProjectId> initList = new ArrayList<>(); initList.add(null); if(repository.getProjectID() != null) { ProjectId project = new ProjectId(repository.getProjectID(), Bundle.MSG_Unchanged()); initList.add(project); panel.projectComboBox.setModel(new ListComboBoxModel<>(initList)); panel.projectComboBox.setSelectedItem(project); } else { panel.projectComboBox.setModel(new ListComboBoxModel<>(initList)); } panel.featureWatchers.setSelected(repository.isFeatureWatchers()); panel.featureDeleteAttachments.setSelected(repository.isFeatureDeleteAttachments()); panel.setFieldsEnabled(true); } @Override public void insertUpdate(DocumentEvent e) { changedUpdate(e); } @Override public void removeUpdate(DocumentEvent e) { changedUpdate(e); } @Override public void changedUpdate(DocumentEvent e) { fireChange(); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == panel.connectButton) { onConnect(); } else if (e.getSource() == panel.projectComboBox) { onProjectSelected(); } else if (e.getSource() == panel.createNewProjectButton) { onCreateNewProject(); } } private void onConnect() { panel.setFieldsEnabled(false); new SwingWorker<List<ProjectId>,Object>() { @Override protected List<ProjectId> doInBackground() throws Exception { RedmineManager rm = null; try { rm = getManager(); rm.getUserManager().getCurrentUser(); List<NestedProject> projectList = new ArrayList<>( RedmineRepository .convertProjectList(rm.getProjectManager().getProjects()) .values()); Collections.sort(projectList); List<ProjectId> result = new ArrayList<>(projectList.size() + 1); result.add(null); for (NestedProject np : projectList) { result.add(new ProjectId(np.getProject().getId(), np.toString())); } return result; } catch (Throwable ex) { throw new RuntimeException("Failed to connect; " + ex.getMessage(), ex); } } @Override protected void done() { panel.progressIcon.setIcon(null); panel.progressTextPane.setText(""); panel.progressPanel.setVisible(true); try { List<ProjectId> projects = get(); panel.progressIcon.setIcon(Defaults.getIcon("info.png")); panel.progressTextPane.setText(Bundle.MSG_AuthSuccessful()); Object item = panel.projectComboBox.getSelectedItem(); panel.projectComboBox.setModel(new ListComboBoxModel<>(projects)); panel.projectComboBox.setSelectedItem(item); } catch (ExecutionException ex) { Throwable cause = ex.getCause(); String errorMessage = null; if (cause instanceof RedmineException) { errorMessage = Redmine.getMessage("MSG_REDMINE_ERROR", Jsoup.parse(ex.getLocalizedMessage()).text()); Redmine.LOG.log(Level.INFO, errorMessage, ex); } else if (cause instanceof Exception) { errorMessage = Redmine.getMessage("MSG_CONNECTION_ERROR", ex.getLocalizedMessage()); Redmine.LOG.log(Level.INFO, errorMessage, ex); } if (errorMessage != null) { panel.progressIcon.setIcon(Defaults.getIcon("warning.png")); panel.progressTextPane.setText(errorMessage); } } catch (InterruptedException ex) { } panel.setFieldsEnabled(true); } }.execute(); } private void onProjectSelected() { ProjectId project = getProject(); if (project != null && StringUtils.isEmpty(getName())) { panel.nameTextField.setText(project.getName()); } fireChange(); } private void onCreateNewProject() { RedmineManager rm = null; try { rm = getManager(); ProjectId selectedProject = (ProjectId) panel.projectComboBox.getSelectedItem(); RedmineProjectPanel projectPanel = new RedmineProjectPanel(rm); if (RedmineUtil.show(projectPanel, "New Redmine project", "OK")) { List<NestedProject> projectList = new ArrayList<>( RedmineRepository .convertProjectList(rm.getProjectManager().getProjects()) .values()); List<ProjectId> projectIdList = new ArrayList<>(projectList.size()); Collections.sort(projectList); projectIdList.add(null); for (NestedProject np : projectList) { Project p = np.getProject(); ProjectId id = new ProjectId(p.getId(), np.toString()); projectIdList.add(id); if (p.getIdentifier().equals(projectPanel.getIdentifier())) { selectedProject = id; break; } } panel.projectComboBox.setModel(new ListComboBoxModel<>(projectIdList)); panel.projectComboBox.setSelectedItem(selectedProject); } fireChange(); } catch (RedmineException ex) { errorMessage = NbBundle.getMessage(Redmine.class, "MSG_REDMINE_ERROR", Jsoup.parse(ex.getLocalizedMessage()).text()); Redmine.LOG.log(Level.INFO, errorMessage, ex); } } @Override public void addChangeListener(ChangeListener l) { support.addChangeListener(l); } @Override public void removeChangeListener(ChangeListener l) { support.removeChangeListener(l); } protected void fireChange() { support.fireChange(); } @Override public void cancelChanges() { } @Override public void itemStateChanged(ItemEvent e) { fireChange(); } private RedmineManager getManager() { RedmineManager manager; if (getAuthMode() == AuthMode.AccessKey) { manager = RedmineManagerFactory.createWithApiKey( getUrl() , getAccessKey() , RedmineManagerFactoryHelper.getTransportConfig() ); if(panel.httpAuthEnabled.isSelected()) { RedmineManagerFactoryHelper.getTransportFromManager(manager) .setCredentials(getHttpUser(), new String(getHttpPassword())); } } else { manager = RedmineManagerFactory.createWithUserAuth( getUrl() , getUser() , new String(getPassword()) , RedmineManagerFactoryHelper.getTransportConfig() ); } return manager; } }