package edu.umd.cs.findbugs; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; import java.util.regex.Matcher; import java.util.regex.Pattern; import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput; import edu.umd.cs.findbugs.xml.XMLOutput; public class UsageTracker { private static final Logger LOGGER = Logger.getLogger(UsageTracker.class.getName()); public void trackUsage(final URI trackerUrl, final Set<Plugin> plugins) { if (trackerUrl == null) { LOGGER.info("Not submitting usage tracking for plugins with blank URL: " + getPluginNames(plugins)); return; } final String entryPoint = getEntryPoint(); Thread thread = new Thread(new Runnable() { public void run() { try { actuallyTrackUsage(trackerUrl, plugins, entryPoint); } catch (Exception e) { LOGGER.log(Level.WARNING, "Error submitting usage tracking data to " + trackerUrl, e); } } }); thread.setDaemon(true); thread.start(); } private void actuallyTrackUsage(URI trackerUrl, Set<Plugin> plugins, String entryPoint) throws IOException { System.out.println("Submitting anonymous usage tracking info to " + trackerUrl + " for " + getPluginNames(plugins)); HttpURLConnection conn = (HttpURLConnection) trackerUrl.toURL().openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.connect(); OutputStream out = conn.getOutputStream(); XMLOutput xmlOutput = new OutputStreamXMLOutput(out); xmlOutput.beginDocument(); xmlOutput.startTag("findbugs-invocation"); xmlOutput.addAttribute("version", Version.RELEASE); xmlOutput.addAttribute("app-name", Version.getApplicationName()); xmlOutput.addAttribute("app-version", Version.getApplicationVersion()); xmlOutput.addAttribute("entry-point", entryPoint); xmlOutput.addAttribute("os", SystemProperties.getProperty("os.name", "")); xmlOutput.addAttribute("java-version", getMajorJavaVersion()); xmlOutput.addAttribute("uuid", getUuid()); xmlOutput.stopTag(false); for (Plugin plugin : plugins) { xmlOutput.startTag("plugin"); xmlOutput.addAttribute("id", plugin.getPluginId()); xmlOutput.addAttribute("name", plugin.getShortDescription()); xmlOutput.addAttribute("version", plugin.getVersion()); xmlOutput.stopTag(true); } xmlOutput.closeTag("findbugs-invocation"); xmlOutput.finish(); out.close(); int responseCode = conn.getResponseCode(); if (responseCode != 200) { LOGGER.warning("Error while submitting anonymous usage data: " + responseCode + " - " + conn.getResponseMessage()); } } private String getPluginNames(Set<Plugin> plugins) { String text = ""; boolean first = true; for (Plugin plugin : plugins) { text = (first ? "" : ", ") + plugin.getShortDescription(); first = false; } return text; } private String getEntryPoint() { String lastFbClass = "<UNKNOWN>"; for (StackTraceElement s : Thread.currentThread().getStackTrace()) { String cls = s.getClassName(); if (cls.startsWith("edu.umd.cs.findbugs.")) { lastFbClass = cls; } } return lastFbClass; } private static synchronized String getUuid() { Preferences prefs = Preferences.userNodeForPackage(UsageTracker.class); long uuid = prefs.getLong("uuid", 0); if (uuid == 0) { uuid = new Random().nextLong(); prefs.putLong("uuid", uuid); } return Long.toString(uuid, 16); } private String getMajorJavaVersion() { String ver = SystemProperties.getProperty("java.version", ""); Matcher m = Pattern.compile("^\\d+\\.\\d+").matcher(ver); if (m.find()) { return m.group(); } return ""; } }