private void postPlugin(final boolean isPing) throws IOException {
		// The plugin's description file containg all of the plugin data such as name, version, author, etc
		final PluginDescriptionFile description = plugin.getDescription();
		
		// Construct the post data
		final StringBuilder data = new StringBuilder();
		data.append(encode("guid")).append('=').append(encode(guid));
		encodeDataPair(data, "version", description.getVersion());
		encodeDataPair(data, "server", Bukkit.getVersion());
		encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().length));
		encodeDataPair(data, "revision", String.valueOf(REVISION));
		
		// If we're pinging, append it
		if (isPing) {
			encodeDataPair(data, "ping", "true");
		}
		
		// Acquire a lock on the graphs, which lets us make the assumption we also lock everything
		// inside of the graph (e.g plotters)
		synchronized (graphs) {
			final Iterator<Graph> iter = graphs.iterator();
			
			while (iter.hasNext()) {
				final Graph graph = iter.next();
				
				// Because we have a lock on the graphs set already, it is reasonable to assume
				// that our lock transcends down to the individual plotters in the graphs also.
				// Because our methods are private, no one but us can reasonably access this list
				// without reflection so this is a safe assumption without adding more code.
				for (Plotter plotter : graph.getPlotters()) {
					// The key name to send to the metrics server
					// The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top
					// Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME
					final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName());
					
					// The value to send, which for the foreseeable future is just the string
					// value of plotter.getValue()
					final String value = Integer.toString(plotter.getValue());
					
					// Add it to the http post data :)
					encodeDataPair(data, key, value);
				}
			}
		}
		
		// Create the url
		URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName())));
		
		// Connect to the website
		URLConnection connection;
		
		// Mineshafter creates a socks proxy, so we can safely bypass it
		// It does not reroute POST requests so we need to go around it
		if (isMineshafterPresent()) {
			connection = url.openConnection(Proxy.NO_PROXY);
		} else {
			connection = url.openConnection();
		}
		
		connection.setDoOutput(true);
		
		// Write the data
		final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
		writer.write(data.toString());
		writer.flush();
		
		// Now read the response
		final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		final String response = reader.readLine();
		
		// close resources
		writer.close();
		reader.close();
		
		if (response == null || response.startsWith("ERR")) {
			throw new IOException(response); // Throw the exception
		} else {
			// Is this the first update this hour?
			if (response.contains("OK This is your first update this hour")) {
				synchronized (graphs) {
					final Iterator<Graph> iter = graphs.iterator();
					
					while (iter.hasNext()) {
						final Graph graph = iter.next();
						
						for (Plotter plotter : graph.getPlotters()) {
							plotter.reset();
						}
					}
				}
			}
		}
		// if (response.startsWith("OK")) - We should get "OK" followed by an optional description if everything goes right
	}