001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.Dimension;
008import java.awt.event.ActionEvent;
009import java.awt.event.KeyEvent;
010import java.lang.management.ManagementFactory;
011import java.util.ArrayList;
012import java.util.Arrays;
013import java.util.HashSet;
014import java.util.List;
015import java.util.ListIterator;
016import java.util.Map;
017import java.util.Map.Entry;
018import java.util.Set;
019
020import javax.swing.JScrollPane;
021
022import org.openstreetmap.josm.Main;
023import org.openstreetmap.josm.data.Preferences.Setting;
024import org.openstreetmap.josm.data.Version;
025import org.openstreetmap.josm.data.osm.DataSet;
026import org.openstreetmap.josm.data.osm.DatasetConsistencyTest;
027import org.openstreetmap.josm.gui.ExtendedDialog;
028import org.openstreetmap.josm.gui.widgets.JosmTextArea;
029import org.openstreetmap.josm.plugins.PluginHandler;
030import org.openstreetmap.josm.tools.BugReportExceptionHandler;
031import org.openstreetmap.josm.tools.OpenBrowser;
032import org.openstreetmap.josm.tools.PlatformHookUnixoid;
033import org.openstreetmap.josm.tools.Shortcut;
034import org.openstreetmap.josm.tools.Utils;
035
036/**
037 * @author xeen
038 *
039 * Opens a dialog with useful status information like version numbers for Java, JOSM and plugins
040 * Also includes preferences with stripped username and password
041 */
042public final class ShowStatusReportAction extends JosmAction {
043
044    /**
045     * Constructs a new {@code ShowStatusReportAction}
046     */
047    public ShowStatusReportAction() {
048        super(
049                tr("Show Status Report"),
050                "clock",
051                tr("Show status report with useful information that can be attached to bugs"),
052                Shortcut.registerShortcut("help:showstatusreport", tr("Help: {0}",
053                        tr("Show Status Report")), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE), false);
054
055        putValue("help", ht("/Action/ShowStatusReport"));
056        putValue("toolbar", "help/showstatusreport");
057        Main.toolbar.register(this);
058    }
059
060    private static void shortenParam(ListIterator<String> it, String[] param, String source, String target) {
061        if (source != null && target.length() < source.length() && param[1].startsWith(source)) {
062            it.set(param[0] + "=" + param[1].replace(source, target));
063        }
064    }
065
066    /**
067     * Replies the report header (software and system info)
068     * @return The report header (software and system info)
069     */
070    public static String getReportHeader() {
071        StringBuilder text = new StringBuilder();
072        text.append(Version.getInstance().getReleaseAttributes());
073        text.append("\n");
074        text.append("Identification: " + Version.getInstance().getAgentString());
075        text.append("\n");
076        text.append("Memory Usage: ");
077        text.append(Runtime.getRuntime().totalMemory()/1024/1024);
078        text.append(" MB / ");
079        text.append(Runtime.getRuntime().maxMemory()/1024/1024);
080        text.append(" MB (");
081        text.append(Runtime.getRuntime().freeMemory()/1024/1024);
082        text.append(" MB allocated, but free)");
083        text.append("\n");
084        text.append("Java version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor") + ", " + System.getProperty("java.vm.name"));
085        text.append("\n");
086        if (Main.platform.getClass() == PlatformHookUnixoid.class) {
087            // Add Java package details
088            String packageDetails = ((PlatformHookUnixoid) Main.platform).getJavaPackageDetails();
089            if (packageDetails != null) {
090                text.append("Java package: ");
091                text.append(packageDetails);
092                text.append("\n");
093            }
094            // Add WebStart package details if run from JNLP
095            if (Package.getPackage("javax.jnlp") != null) {
096                String webStartDetails = ((PlatformHookUnixoid) Main.platform).getWebStartPackageDetails();
097                if (webStartDetails != null) {
098                    text.append("WebStart package: ");
099                    text.append(webStartDetails);
100                    text.append("\n");
101                }
102            }
103        }
104        try {
105            final String envJavaHome = System.getenv("JAVA_HOME");
106            final String envJavaHomeAlt = Main.isPlatformWindows() ? "%JAVA_HOME%" : "${JAVA_HOME}";
107            final String propJavaHome = System.getProperty("java.home");
108            final String propJavaHomeAlt = "<java.home>";
109            // Build a new list of VM parameters to modify it below if needed (default implementation returns an UnmodifiableList instance)
110            List<String> vmArguments = new ArrayList<>(ManagementFactory.getRuntimeMXBean().getInputArguments());
111            for (ListIterator<String> it = vmArguments.listIterator(); it.hasNext(); ) {
112                String value = it.next();
113                if (value.contains("=")) {
114                    String[] param = value.split("=");
115                    // Hide some parameters for privacy concerns
116                    if (param[0].toLowerCase().startsWith("-dproxy")) {
117                        it.set(param[0]+"=xxx");
118                    // Shorten some parameters for readability concerns
119                    } else {
120                        shortenParam(it, param, envJavaHome, envJavaHomeAlt);
121                        shortenParam(it, param, propJavaHome, propJavaHomeAlt);
122                    }
123                }
124            }
125            if (!vmArguments.isEmpty()) {
126                text.append("VM arguments: "+ vmArguments.toString().replace("\\\\", "\\"));
127                text.append("\n");
128            }
129        } catch (SecurityException e) {
130            // Ignore exception
131        }
132        if (Main.commandLineArgs.length > 0) {
133            text.append("Program arguments: "+ Arrays.toString(Main.commandLineArgs));
134            text.append("\n");
135        }
136        if (Main.main != null) {
137            DataSet dataset = Main.main.getCurrentDataSet();
138            if (dataset != null) {
139                String result = DatasetConsistencyTest.runTests(dataset);
140                if (result.length() == 0) {
141                    text.append("Dataset consistency test: No problems found\n");
142                } else {
143                    text.append("\nDataset consistency test:\n"+result+"\n");
144                }
145            }
146        }
147        text.append("\n");
148        text.append(PluginHandler.getBugReportText());
149        text.append("\n");
150
151        return text.toString();
152    }
153
154    @Override
155    public void actionPerformed(ActionEvent e) {
156        StringBuilder text = new StringBuilder();
157        String reportHeader = getReportHeader();
158        text.append(reportHeader);
159        try {
160            Map<String, Setting<?>> settings = Main.pref.getAllSettings();
161            Set<String> keys = new HashSet<>(settings.keySet());
162            for (String key : keys) {
163                // Remove sensitive information from status report
164                if (key.startsWith("marker.show") || key.contains("username") || key.contains("password") || key.contains("access-token")) {
165                    settings.remove(key);
166                }
167            }
168            for (Entry<String, Setting<?>> entry : settings.entrySet()) {
169                text.append(entry.getKey()).append("=").append(entry.getValue().getValue().toString()).append("\n");
170            }
171        } catch (Exception x) {
172            Main.error(x);
173        }
174
175        JosmTextArea ta = new JosmTextArea(text.toString());
176        ta.setWrapStyleWord(true);
177        ta.setLineWrap(true);
178        ta.setEditable(false);
179        JScrollPane sp = new JScrollPane(ta);
180
181        ExtendedDialog ed = new ExtendedDialog(Main.parent,
182                tr("Status Report"),
183                new String[] {tr("Copy to clipboard and close"), tr("Report bug"), tr("Close") });
184        ed.setButtonIcons(new String[] {"copy.png", "bug.png", "cancel.png" });
185        ed.setContent(sp, false);
186        ed.setMinimumSize(new Dimension(380, 200));
187        ed.setPreferredSize(new Dimension(700, Main.parent.getHeight()-50));
188
189        switch (ed.showDialog().getValue()) {
190            case 1: Utils.copyToClipboard(text.toString()); break;
191            case 2: OpenBrowser.displayUrl(BugReportExceptionHandler.getBugReportUrl(
192                        Utils.strip(reportHeader)).toExternalForm()) ; break;
193        }
194    }
195}