package org.openflexo.br.view;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Window;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.Deflater;
import org.openflexo.AdvancedPrefs;
import org.openflexo.ApplicationVersion;
import org.openflexo.Flexo;
import org.openflexo.br.BugReportManager;
import org.openflexo.components.ProgressWindow;
import org.openflexo.fib.controller.FIBController.Status;
import org.openflexo.fib.controller.FIBDialog;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.module.FlexoModule;
import org.openflexo.module.Module;
import org.openflexo.module.Modules;
import org.openflexo.swing.ImageUtils;
import org.openflexo.swing.ImageUtils.ImageType;
import org.openflexo.toolbox.FileResource;
import org.openflexo.toolbox.FileUtils;
import org.openflexo.toolbox.FlexoVersion;
import org.openflexo.toolbox.IProgress;
import org.openflexo.toolbox.ToolBox;
import org.openflexo.toolbox.ZipUtils;
import org.openflexo.view.FlexoDialog;
import org.openflexo.view.FlexoFrame;
import org.openflexo.view.controller.FlexoController;
import org.openflexo.ws.jira.JIRAClient;
import org.openflexo.ws.jira.JIRAClient.Method;
import org.openflexo.ws.jira.JIRAClient.Progress;
import org.openflexo.ws.jira.UnauthorizedJIRAAccessException;
import org.openflexo.ws.jira.action.SubmitIssue;
import org.openflexo.ws.jira.model.JIRAComponent;
import org.openflexo.ws.jira.model.JIRAIssue;
import org.openflexo.ws.jira.model.JIRAObject;
import org.openflexo.ws.jira.model.JIRAPriority;
import org.openflexo.ws.jira.model.JIRAProject;
import org.openflexo.ws.jira.model.JIRAVersion;
import org.openflexo.ws.jira.result.JIRAResult;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
public class JIRAIssueReportDialog {
private final class ReportProgress implements Progress {
int count = 0;
@Override
public void setProgress(double percentage) {
int steps = (int) (percentage * 100);
steps -= count;
for (int i = 0; i < steps; i++) {
ProgressWindow.instance().setSecondaryProgress("");
}
count += steps;
}
public void resetCount() {
ProgressWindow.instance().resetSecondaryProgress(100);
count = 0;
}
}
public static class SubmitIssueReport {
private String issueLink;
private List<String> errors;
private List<String> warnings;
public SubmitIssueReport() {
errors = new ArrayList<String>();
warnings = new ArrayList<String>();
}
public boolean hasErrors() {
return errors.size() > 0;
}
public boolean hasWarnings() {
return warnings.size() > 0;
}
public String getIssueLink() {
return issueLink;
}
public void setIssueLink(String issueLink) {
this.issueLink = issueLink;
}
public List<String> getErrors() {
return errors;
}
public List<String> getWarnings() {
return warnings;
}
public void addToErrors(String error) {
errors.add(error);
}
public void addToWarning(String warning) {
warnings.add(warning);
}
public String errorsToString() {
StringBuilder sb = new StringBuilder();
for (String e : errors) {
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(e);
}
return sb.toString();
}
public String warningsToString() {
StringBuilder sb = new StringBuilder();
for (String w : warnings) {
if (sb.length() > 0) {
sb.append('\n');
}
sb.append(w);
}
return sb.toString();
}
public String issueLinkHyperlink() {
return "<html><a href=\"" + issueLink + "\">" + issueLink + "</a></href>";
}
public void openIssueLink() {
ToolBox.openURL(getIssueLink());
}
}
private static final Logger logger = FlexoLogger.getLogger(JIRAIssueReportDialog.class.getPackage().getName());
public static final File FIB_FILE = new FileResource("Fib/JIRAIssueReportDialog.fib");
public static final File REPORT_FIB_FILE = new FileResource("Fib/JIRASubmitIssueReportDialog.fib");
private static final List<JIRAComponent> EMPTY_LIST = new ArrayList<JIRAComponent>(0);
private JIRAIssue issue;
private JIRAProject project;
private boolean sendLogs;
private boolean sendScreenshots;
private boolean sendProject;
private boolean sendSystemProperties;
private File attachFile;
private FlexoProject flexoProject;
public static void newBugReport(FlexoModule module, FlexoProject project) {
newBugReport(null, module, project);
}
public static void newBugReport(Exception e, FlexoModule module, FlexoProject project) {
try {
JIRAIssueReportDialog report = new JIRAIssueReportDialog(e);
report.setFlexoProject(project);
if (module != null) {
if (report.getIssue().getIssuetype().getComponentField() != null
&& report.getIssue().getIssuetype().getComponentField().getAllowedValues() != null) {
for (JIRAComponent comp : report.getIssue().getIssuetype().getComponentField().getAllowedValues()) {
if (comp.getId().equals(module.getModule().getJiraComponentID())) {
report.getIssue().setComponent(comp);
break;
}
}
}
}
FIBDialog<JIRAIssueReportDialog> dialog = FIBDialog.instanciateAndShowDialog(FIB_FILE, report, FlexoFrame.getActiveFrame(),
true, FlexoLocalization.getMainLocalizer());
boolean ok = false;
while (!ok) {
if (dialog.getStatus() == Status.VALIDATED) {
try {
while (AdvancedPrefs.getBugReportUser() == null || AdvancedPrefs.getBugReportUser().trim().length() == 0
|| AdvancedPrefs.getBugReportPassword() == null
|| AdvancedPrefs.getBugReportPassword().trim().length() == 0) {
if (!JIRAURLCredentialsDialog.askLoginPassword()) {
break;
}
}
ok = dialog.getData().send();
} catch (MalformedURLException e1) {
FlexoController.showError(FlexoLocalization.localizedForKey("could_not_send_bug_report") + " " + e.getMessage());
} catch (UnauthorizedJIRAAccessException e1) {
if (JIRAURLCredentialsDialog.askLoginPassword()) {
continue;
} else {
break;
}
} catch (IOException e1) {
e1.printStackTrace();
}
} else {
break;
}
if (!ok) {
dialog.setVisible(true);
}
}
} catch (JsonSyntaxException e1) {
e1.printStackTrace();
FlexoController.showError(FlexoLocalization.localizedForKey("cannot_read_JIRA_project_file"));
} catch (JsonIOException e1) {
e1.printStackTrace();
FlexoController.showError(FlexoLocalization.localizedForKey("cannot_read_JIRA_project_file"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
FlexoController.showError(FlexoLocalization.localizedForKey("cannot_read_JIRA_project_file"));
}
}
private void setFlexoProject(FlexoProject flexoProject) {
this.flexoProject = flexoProject;
}
public JIRAIssueReportDialog() throws JsonSyntaxException, JsonIOException, FileNotFoundException {
this(null);
}
public JIRAIssueReportDialog(Exception e) throws JsonSyntaxException, JsonIOException, FileNotFoundException {
this.project = BugReportManager.getInstance().getOpenFlexoProject();
issue = new JIRAIssue();
issue.setIssuetype(project.getIssuetypes().get(0));
issue.setProject(project);
if (issue.getIssuetype().getPriorityField() != null && issue.getIssuetype().getPriorityField().getAllowedValues() != null) {
JIRAPriority major = null;
for (JIRAPriority p : issue.getIssuetype().getPriorityField().getAllowedValues()) {
if ("Major".equals(p.getName())) {
major = p;
break;
}
if ("3".equals(p.getId())) {
major = p;
}
}
issue.setPriority(major);
}
sendSystemProperties = false;
sendScreenshots = false;
sendLogs = true;
if (e != null) {
issue.setStacktrace(e.getClass().getName() + ": " + e.getMessage() + "\n" + ToolBox.getStackTraceAsString(e));
}
}
public JIRAProject getProject() {
return project;
}
public JIRAIssue getIssue() {
return issue;
}
public void setIssue(JIRAIssue issue) {
this.issue = issue;
}
public boolean send() throws IOException {
JIRAClient client = new JIRAClient(AdvancedPrefs.getBugReportUrl(), AdvancedPrefs.getBugReportUser(),
AdvancedPrefs.getBugReportPassword());
final SubmitIssueReport report = new SubmitIssueReport();
SubmitIssueToJIRA target = new SubmitIssueToJIRA(client, report);
int steps = target.getNumberOfSteps();
ProgressWindow.showProgressWindow(FlexoLocalization.localizedForKey("submitting_bug_report"), steps);
try {
boolean submit = true;
while (submit) {
target.run();
if (target.getException() != null) {
if (target.getException() instanceof SocketTimeoutException) {
submit = FlexoController.confirm(FlexoLocalization.localizedForKey("could_not_send_incident_so_far_keep_trying")
+ "? ");
if (submit) {
client.setTimeout(client.getTimeout() * 2);// Let's increase time out
}
} else {
throw target.getException();
}
} else {
submit = false;
}
}
} finally {
ProgressWindow.hideProgressWindow();
}
FIBDialog
.instanciateAndShowDialog(REPORT_FIB_FILE, report, FlexoFrame.getActiveFrame(), true, FlexoLocalization.getMainLocalizer());
return !report.hasErrors();
}
private class SubmitIssueToJIRA implements Runnable {
private SubmitIssueReport report;
private IOException exception;
private final JIRAClient client;
protected SubmitIssueToJIRA(JIRAClient client, SubmitIssueReport report) {
super();
this.client = client;
this.report = report;
}
public int getNumberOfSteps() {
int steps = 1;
if (sendProject) {
steps += 2;
}
if (sendLogs) {
steps++;
}
if (attachFile != null) {
steps++;
}
if (sendScreenshots) {
for (int i = 0; i < Frame.getFrames().length; i++) {
Frame frame = Frame.getFrames()[i];
if (frame instanceof FlexoFrame) {
steps++;
for (Window w : frame.getOwnedWindows()) {
if (w instanceof FlexoDialog || w instanceof FIBDialog) {
steps++;
}
}
}
}
}
return steps;
}
@Override
public void run() {
ReportProgress progressAdapter = new ReportProgress();
String buildid = "build.id = " + ApplicationVersion.BUILD_ID + "\n";
String commitID = "commit.id = " + ApplicationVersion.COMMIT_ID + "\n";
if (sendSystemProperties) {
issue.setSystemProperties(buildid + commitID + ToolBox.getSystemProperties(true));
} else {
issue.setSystemProperties(buildid + commitID);
}
if (getIssue().getIssuetype().getVersionField() != null) {
List<JIRAVersion> allowedValues = getIssue().getIssuetype().getVersionField().getAllowedValues();
FlexoVersion flexoVersion = new FlexoVersion(ApplicationVersion.BUSINESS_APPLICATION_VERSION);
FlexoVersion simpleVersion = new FlexoVersion(flexoVersion.major, flexoVersion.minor, flexoVersion.patch, -1, false, false);
JIRAVersion selected = null;
for (JIRAVersion version : allowedValues) {
if (flexoVersion.equals(version.getName())) {
selected = version;
break;
}
}
if (selected == null) {
for (JIRAVersion version : allowedValues) {
if (simpleVersion.equals(version.getName())) {
selected = version;
break;
}
}
}
getIssue().setVersion(selected);
} else {
getIssue().setVersion(null);
}
// Always call make valid before replacing by identity members
getIssue().makeValid();
getIssue().<JIRAObject> replaceMembersByIdentityMembers();
try {
ProgressWindow.instance().setProgress(FlexoLocalization.localizedForKey("creating_issue"));
progressAdapter.resetCount();
JIRAResult submit = client.submit(new SubmitIssue(getIssue()), Method.POST, progressAdapter);
if (submit.getErrorMessages() != null && submit.getErrorMessages().size() > 0) {
for (String error : submit.getErrorMessages()) {
report.addToErrors(error);
}
}
if (submit.getKey() != null) {
JIRAIssue result = new JIRAIssue();
result.setKey(submit.getKey());
report.setIssueLink(AdvancedPrefs.getBugReportUrl() + "/browse/" + submit.getKey());
if (sendLogs) {
ProgressWindow.instance().setProgress(FlexoLocalization.localizedForKey("sending_logs"));
progressAdapter.resetCount();
try {
client.attachFilesToIssue(result, progressAdapter, Flexo.getErrLogFile());
} catch (IOException e) {
report.addToErrors(FlexoLocalization.localizedForKey("could_not_attach_file") + " "
+ Flexo.getErrLogFile().getAbsolutePath() + "\n\t" + e.getMessage());
}
}
if (attachFile != null) {
ProgressWindow.instance().setProgress(
FlexoLocalization.localizedForKey("sending_file") + " " + attachFile.getAbsolutePath());
progressAdapter.resetCount();
try {
client.attachFilesToIssue(result, progressAdapter, attachFile);
} catch (IOException e) {
report.addToErrors(FlexoLocalization.localizedForKey("could_not_attach_file") + " "
+ attachFile.getAbsolutePath() + "\n\t" + e.getMessage());
}
}
if (sendProject) {
if (flexoProject != null) {
File projectDirectory = flexoProject.getProjectDirectory();
String directoryName = projectDirectory.getName();
File zipFile = new File(System.getProperty("java.io.tmpdir"), directoryName.substring(0,
directoryName.length() - 4) + ".zip");
FileFilter filter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return !pathname.getName().endsWith("~");
}
};
ProgressWindow.instance().setProgress(FlexoLocalization.localizedForKey("compressing_project"));
ProgressWindow.instance().resetSecondaryProgress(
FileUtils.countFilesInDirectory(projectDirectory, true, filter));
try {
ZipUtils.makeZip(zipFile, projectDirectory, new IProgress() {
@Override
public void setSecondaryProgress(String stepName) {
ProgressWindow.instance().setSecondaryProgress(stepName);
}
@Override
public void setProgress(String stepName) {
}
@Override
public void resetSecondaryProgress(int steps) {
}
@Override
public void hideWindow() {
}
}, filter, Deflater.BEST_COMPRESSION);
try {
ProgressWindow.instance().setProgress(FlexoLocalization.localizedForKey("sending_project"));
progressAdapter.resetCount();
client.attachFilesToIssue(result, progressAdapter, zipFile);
} catch (IOException e) {
report.addToErrors(FlexoLocalization.localizedForKey("could_not_attach_project") + " " + e.getMessage());
}
} catch (IOException e) {
report.addToErrors(FlexoLocalization.localizedForKey("could_not_zip_project") + " " + e.getMessage());
}
}
}
if (sendScreenshots) {
for (int i = 0; i < Frame.getFrames().length; i++) {
Frame frame = Frame.getFrames()[i];
if (frame instanceof FlexoFrame) {
ProgressWindow.instance().setProgress(
FlexoLocalization.localizedForKey("sending_screenshot") + " " + frame.getTitle());
progressAdapter.resetCount();
attachScreenshotToIssue(client, result, frame, frame.getTitle(), progressAdapter, report);
for (Window w : frame.getOwnedWindows()) {
if (w instanceof FlexoDialog || w instanceof FIBDialog) {
ProgressWindow.instance().setProgress(
FlexoLocalization.localizedForKey("sending_screenshot") + " " + ((Dialog) w).getTitle());
progressAdapter.resetCount();
attachScreenshotToIssue(client, result, w, ((Dialog) w).getTitle(), progressAdapter, report);
}
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
this.exception = e;
} finally {
getIssue().<JIRAObject> replaceMembersByOriginalMembers();
}
}
public IOException getException() {
return exception;
}
}
private void attachScreenshotToIssue(JIRAClient client, JIRAIssue result, Window window, String title, Progress progress,
SubmitIssueReport report) {
if (window.isVisible() && window.getSize().getWidth() > 0 && window.getSize().getHeight() > 0) {
try {
File file = new File(System.getProperty("java.io.tmpdir"), FileUtils.getValidFileName(title + ".png"));
ImageUtils.saveImageToFile(ImageUtils.createImageFromComponent(window), file, ImageType.PNG);
client.attachFilesToIssue(result, progress, file);
} catch (Exception e) {
report.addToErrors(FlexoLocalization.localizedForKey("could_not_attach_screenshot") + " " + title + "\n\t" + e.getMessage());
logger.log(Level.SEVERE, "Error when trying to send screenshot: " + title, e);
}
}
}
public List<JIRAComponent> getAvailableModules() {
if (getIssue().getIssuetype() == null) {
return EMPTY_LIST;
}
List<JIRAComponent> availableModules = new ArrayList<JIRAComponent>();
for (Module module : Modules.getInstance().getAvailableModules()) {
for (JIRAComponent component : getIssue().getIssuetype().getComponentField().getAllowedValues()) {
if (module.getJiraComponentID().equals(component.getId())) {
availableModules.add(component);
break;
}
}
}
return availableModules;
}
public boolean isSendLogs() {
return sendLogs;
}
public void setSendLogs(boolean sendLogs) {
this.sendLogs = sendLogs;
}
public boolean isSendScreenshots() {
return sendScreenshots;
}
public void setSendScreenshots(boolean sendScreenshots) {
this.sendScreenshots = sendScreenshots;
}
public boolean isSendSystemProperties() {
return sendSystemProperties;
}
public void setSendSystemProperties(boolean sendSystemProperties) {
this.sendSystemProperties = sendSystemProperties;
}
public File getAttachFile() {
return attachFile;
}
public void setAttachFile(File attachFile) {
this.attachFile = attachFile;
}
public boolean isSendProject() {
return sendProject;
}
public void setSendProject(boolean sendProject) {
this.sendProject = sendProject;
}
}