/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.xmlrpc.client.XmlRpcClient;
import com.rapidminer.Process;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.RapidMinerGUI;
import com.rapidminer.tools.plugin.Plugin;
/**
* A bug report can be send by the user. It should only be used in cases where
* an exception does not occur due to a user error.
*
* @author Simon Fischer, Ingo Mierswa, Marco Boeck
*/
public class BugReport {
private static final int BUFFER_SIZE = 1024;
private static void getSystemProperties(String prefix, StringBuffer string) {
string.append(prefix + " properties:" + Tools.getLineSeparator());
Enumeration keys = System.getProperties().propertyNames();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
if (key.startsWith(prefix)) {
string.append(" " + key + "\t= " + System.getProperty(key) + Tools.getLineSeparator());
}
}
}
private static void getRapidMinerParameters(StringBuffer string) {
string.append("RapidMiner Parameters:" + Tools.getLineSeparator());
for (String key: ParameterService.getParameterKeys()) {
string.append(" " + key + "\t= " + ParameterService.getParameterValue(key) + Tools.getLineSeparator());
}
}
public static String getProperties() {
StringBuffer string = new StringBuffer();
string.append("System properties:" + Tools.getLineSeparator());
string.append("------------" + Tools.getLineSeparator() + Tools.getLineSeparator());
getSystemProperties("os", string);
getSystemProperties("java", string);
getRapidMinerParameters(string);
return string.toString();
}
public static String getStackTrace(Throwable throwable) {
StringBuffer string = new StringBuffer();
string.append("Stack trace:" + Tools.getLineSeparator());
string.append("------------" + Tools.getLineSeparator() + Tools.getLineSeparator());
while (throwable != null) {
string.append("Exception:\t" + throwable.getClass().getName() + Tools.getLineSeparator());
string.append("Message:\t" + throwable.getMessage() + Tools.getLineSeparator());
string.append("Stack trace:"+ Tools.getLineSeparator());
StackTraceElement[] ste = throwable.getStackTrace();
for (StackTraceElement element : ste) {
string.append(" " + element + Tools.getLineSeparator());
}
string.append(Tools.getLineSeparator());
throwable = throwable.getCause();
if (throwable != null) {
string.append("");
string.append("Cause:");
}
}
return string.toString();
}
public static void createBugReport(File reportFile, Throwable exception, String userMessage, Process process, String logMessage, File[] attachments) throws IOException {
ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(reportFile));
zipOut.setComment("RapidMiner bug report - generated " + new Date());
write("message.txt", "User message", userMessage, zipOut);
write("_process.xml", "Process as in memory.", process.getRootOperator().getXML(false), zipOut);
if (process.getProcessLocation() != null) {
try {
String contents = process.getProcessLocation().getRawXML();
write(process.getProcessLocation().getShortName(), "Raw process file in repository.", contents, zipOut);
} catch (Throwable t) {
write(process.getProcessLocation().getShortName(), "Raw process file in repository.", "could not read: " + t, zipOut);
}
}
write("_log.txt", "Log message", logMessage, zipOut);
write("_properties.txt", "System properties, information about java version and operating system", getProperties(), zipOut);
write("_exception.txt", "Exception stack trace", getStackTrace(exception), zipOut);
for (File attachment : attachments)
writeFile(attachment, zipOut);
zipOut.close();
}
/**
* Creates the BugZilla bugreport.
* @param client the logged in BugZilla client
* @param exception the exception which was thrown by the bug
* @param userSummary summary of the bug
* @param userDescription description of the bug
* @param process the currently active process
* @param logMessage the RM log
* @param attachments optional attachements
* @param attachProcess if the process xml should be attached
* @param sendSystemProps if the system properties should be included
* @throws Exception
*/
public static void createBugZillaReport(XmlRpcClient client, Throwable exception, String userSummary,
String completeDescription, String component, String version, String severity, String platform,
String os, Process process, String logMessage, File[] attachments, boolean attachProcess, boolean attachSystemProps) throws Exception {
// create temp files with all the data we need
// attach process if user agreed via checkbox
File processFile = File.createTempFile("_process", ".xml");
processFile.deleteOnExit();
String xmlProcess;
if (RapidMinerGUI.getMainFrame().getProcess().getProcessLocation() != null) {
try {
xmlProcess = RapidMinerGUI.getMainFrame().getProcess().getProcessLocation().getRawXML();
} catch (Throwable t) {
xmlProcess = "could not read: " + t;
}
} else {
xmlProcess = "no process available";
}
writeFile(processFile, xmlProcess);
// attach system properties if user agreed via checkbox
File propertiesFile = File.createTempFile("_properties", ".txt");
propertiesFile.deleteOnExit();
writeFile(propertiesFile, getProperties());
// append the RM version to the description
StringBuffer buffer = new StringBuffer(completeDescription);
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append(getStackTrace(exception));
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append("RapidMiner: ");
buffer.append(RapidMiner.getVersion());
buffer.append(Tools.getLineSeparator());
for (Plugin plugin : Plugin.getAllPlugins()) {
buffer.append(plugin.getName());
buffer.append(": ");
buffer.append(plugin.getVersion());
buffer.append(Tools.getLineSeparator());
}
completeDescription = buffer.toString();
// call BugZilla via xml-rpc
XmlRpcClient rpcClient = client;
Map<String, String> bugMap = new HashMap<String, String>();
bugMap.put("product", "RapidMiner");
bugMap.put("component", component);
bugMap.put("summary", userSummary);
bugMap.put("description", completeDescription);
bugMap.put("version", version);
bugMap.put("op_sys", os);
bugMap.put("platform", platform);
bugMap.put("severity", severity);
bugMap.put("status", "NEW");
Map createResult = (Map)rpcClient.execute("Bug.create", new Object[]{ bugMap });
LogService.getRoot().fine("Bug submitted successfully. Bug ID: " + createResult.get("id"));
String id = String.valueOf(createResult.get("id"));
Map<String, Object> attachmentMap = new HashMap<String, Object>();
// add process xml file attachment if selected
if (attachProcess) {
attachmentMap.put("ids", new String[]{ id });
// BugZilla API states Base64 encoded string is needed, but it does not work
// attachmentMap.put("data", Base64.encodeFromFile(processFile.getPath()));
FileInputStream fileInputStream = new FileInputStream(processFile);
byte[] data = new byte[(int)processFile.length()];
fileInputStream.read(data);
attachmentMap.put("data", data);
attachmentMap.put("file_name", "process.xml");
attachmentMap.put("summary", "process.xml");
attachmentMap.put("content_type", "application/xml");
createResult = (Map)rpcClient.execute("Bug.add_attachment", new Object[]{ attachmentMap });
attachmentMap.clear();
}
// add process xml file attachment if selected
if (attachSystemProps) {
attachmentMap.put("ids", new String[]{ id });
// BugZilla API states Base64 encoded string is needed, but it does not work
// attachmentMap.put("data", Base64.encodeFromFile(propertiesFile.getPath()));
FileInputStream fileInputStream = new FileInputStream(propertiesFile);
byte[] data = new byte[(int)propertiesFile.length()];
fileInputStream.read(data);
attachmentMap.put("data", data);
attachmentMap.put("file_name", "system-properties.txt");
attachmentMap.put("summary", "system-properties.txt");
attachmentMap.put("content_type", "text/plain");
createResult = (Map)rpcClient.execute("Bug.add_attachment", new Object[]{ attachmentMap });
attachmentMap.clear();
}
// add attachments by user
for (File file : attachments) {
attachmentMap.put("ids", new String[]{ id });
// BugZilla API states Base64 encoded string is needed, but it does not work
// attachmentMap.put("data", Base64.encodeFromFile(file.getPath()));
FileInputStream fileInputStream = new FileInputStream(file);
byte[] data = new byte[(int)file.length()];
fileInputStream.read(data);
attachmentMap.put("data", data);
attachmentMap.put("file_name", file.getName());
attachmentMap.put("summary", file.getName());
attachmentMap.put("content_type", "application/data");
createResult = (Map)rpcClient.execute("Bug.add_attachment", new Object[]{ attachmentMap });
attachmentMap.clear();
}
}
/**
* Creates the complete description of the bug including user description, exception stack trace,
* system properties and RM and plugin versions.
* @param userDescription the description the user entered
* @param exception the {@link Throwable} on which the bug report is based upon
* @param attachProcess if true, will attach the process xml
* @param attachSystemProps if true, will attach the system properties
* @return the human readable complete bug report
*/
public static String createCompleteBugDescription(String userDescription, Throwable exception, boolean attachProcess, boolean attachSystemProps) {
StringBuffer buffer = new StringBuffer();
// append the user description
buffer.append(userDescription);
// append RapidMiner and plugin versions
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append("RapidMiner: ");
buffer.append(RapidMiner.getVersion());
buffer.append(Tools.getLineSeparator());
for (Plugin plugin : Plugin.getAllPlugins()) {
buffer.append(plugin.getName());
buffer.append(": ");
buffer.append(plugin.getVersion());
buffer.append(Tools.getLineSeparator());
}
// append stack trace
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append(getStackTrace(exception));
// if user selected it, attach process xml
if (attachProcess) {
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append("Process:");
buffer.append(Tools.getLineSeparator());
buffer.append("------------");
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
String xmlProcess;
if (RapidMinerGUI.getMainFrame().getProcess()!= null) {
try {
xmlProcess = RapidMinerGUI.getMainFrame().getProcess().getRootOperator().getXML(false);
} catch (Throwable t) {
xmlProcess = "could not read: " + t;
}
} else {
xmlProcess = "no process available";
}
buffer.append(xmlProcess);
}
// if user agreed to it, attach system properties
if (attachSystemProps) {
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append(Tools.getLineSeparator());
buffer.append(getProperties());
}
return buffer.toString();
}
private static void writeFile(File file, ZipOutputStream out) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(file);
out.putNextEntry(new ZipEntry(file.getName()));
byte[] buffer = new byte[BUFFER_SIZE];
int read = -1;
do {
read = in.read(buffer);
if (read > -1) {
out.write(buffer, 0, read);
}
} while (read > -1);
out.closeEntry();
} catch (IOException e) {
throw e;
} finally {
if (in != null)
in.close();
}
}
private static void write(String name, String comment, String string, ZipOutputStream out) throws IOException {
ZipEntry entry = new ZipEntry(name);
entry.setComment(comment);
out.putNextEntry(entry);
PrintStream print = new PrintStream(out);
print.println(string);
print.flush();
out.closeEntry();
}
private static void writeFile(File file, String contents) throws IOException {
FileWriter writer = new FileWriter(file);
writer.write(contents);
writer.close();
}
}