/* This software is distributed under the Open Source Definition, which may be found at http://www.opensource.org/docs/definition.php . In particular, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met. * Redistribution of source code must retain this notice, this list of conditions and the following disclaimer. * Redistributions in binary form must provide access to this notice, this list of conditions and the following disclaimer, and the underlying source code. * All modifications to this software must be clearly documented, and are solely the responsibility of the agent making the modifications. * If significant modifications or enhancements are made to this software, the ESRL/GSD Software Policy Manager (softwaremgr.fsl@noaa.gov ) should be notified. This software and its documentation are in the public domain and are furnished "as is". The United Stated Government, its instrumentalities, officers, employees, and agents make no warranty, express or implied, as to the usefulness of the software and documentation for any purpose. They assume no responsibility (1) for the use of the software and documentation; or (2) to provide technical support to users. */ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; /** * Program parses a web access log for WRF Portal, WRF Domain Wizard, and Workflow Manager downloads, * builds a list of unique IP addresses, does a web lookup for the location of each IP address, and * builds a KML file to display these download locations (IP address locations) in Google Earth. * * more detailed ip information (location and org name) can be found here: * http://www.ip-adress.com/ipaddresstolocation/ * (they do limit you to a certain number of lookups per day, however) * * Note: web server must have correct MIME type for Google Earth in order to open Google Earth when .kml link is selected. * For Apache, add these lines to the httpd.conf file: * AddType application/vnd.google-earth.kml+xml .kml * AddType application/vnd.google-earth.kmz .kmz * * @author jssmith */ public class AccessCounter { private String webAccessLogFilePath; public AccessCounter(String webAccessLogFilePath) { this.webAccessLogFilePath = webAccessLogFilePath; } /** * Inner class models a geo "location" of a download */ class Location { String lat; String lon; String city; String country; String name; Location () { } } /** * I use the hostip.info site to geo-code each IP address. * See http://www.hostip.info/use.html for more information * @param ip * @return */ private Location getLocation(String ip) { try { URL url = new URL("http://api.hostip.info/get_html.php?ip=" + ip + "&position=true"); URLConnection conn = url.openConnection(); Location location = new Location(); // Get the response BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = rd.readLine()) != null) { if (line.toUpperCase().contains("COUNTRY")) location.country = line.substring(line.indexOf(":") + 1).trim(); if (line.toUpperCase().contains("CITY")) location.city = line.substring(line.indexOf(":") + 1).trim(); if (line.toUpperCase().contains("LATITUDE")) location.lat = line.substring(line.indexOf(":") + 1).trim(); if (line.toUpperCase().contains("LONGITUDE")) location.lon = line.substring(line.indexOf(":") + 1).trim(); } rd.close(); if (location.lat.length() < 1) { if (location.country.toUpperCase().contains("CHINA")) { location.lat = "40"; location.lon = "116"; } else if (location.country.toUpperCase().contains("SPAIN")) { location.lat = "40"; location.lon = "-4"; } else if (location.country.toUpperCase().contains("GERMANY")) { location.lat = "50"; location.lon = "8"; } else if (location.country.toUpperCase().contains("UNITED STATES")) { location.lat = "38"; location.lon = "-100"; } else if (location.country.toUpperCase().contains("KOREA")) { location.lat = "37.5"; location.lon = "127"; } else if (location.country.toUpperCase().contains("EUROPEAN UNION")) { location.lat = "45"; location.lon = "11"; } else if (location.country.toUpperCase().contains("(IR)")) { location.lat = "35.44"; location.lon = "51.30"; } else if (location.country.toUpperCase().contains("CANADA")) { location.lat = "43.39"; location.lon = "-79"; } else if (location.country.toUpperCase().contains("TAIWAN")) { location.lat = "24.8"; location.lon = "121.4"; } else if (location.country.toUpperCase().contains("SOUTH AFRICA")) { location.lat = "-26"; location.lon = "28"; } else if (location.country.toUpperCase().contains("JAPAN")) { location.lat = "35.6"; location.lon = "139.0"; } else if (location.country.toUpperCase().contains("FRANCE")) { location.lat = "48.75"; location.lon = "2.3"; } else if (location.country.toUpperCase().contains("CHILE")) { location.lat = "-33.5"; location.lon = "-70.75"; } else if (location.country.toUpperCase().contains("ITALY")) { location.lat = "42"; location.lon = "12.52"; } else if (location.country.toUpperCase().contains("AUSTRALIA")) { location.lat = "-34"; location.lon = "150"; } else if (location.country.toUpperCase().contains("PORTUGAL")) { location.lat = "38.7"; location.lon = "-9.1"; } else if (location.country.toUpperCase().contains("SWITZERLAND")) { location.lat = "46.2"; location.lon = "6.05"; } else if (location.country.toUpperCase().contains("MACAO")) { location.lat = "22.21"; location.lon = "113.5"; } else if (location.country.toUpperCase().contains("INDIA")) { location.lat = "20"; location.lon = "77.1"; } else if (location.country.toUpperCase().contains("AUSTRIA")) { location.lat = "47.3"; location.lon = "13.3"; } else if (location.country.toUpperCase().contains("NETHERLANDS")) { location.lat = "52.3"; location.lon = "4.8"; } else if (location.country.toUpperCase().contains("NEW ZEALAND")) { location.lat = "-36.8"; location.lon = "174.6"; } else if (location.country.toUpperCase().contains("GREECE")) { location.lat = "38"; location.lon = "23.66"; } else if (location.country.toUpperCase().contains("COSTA RICA")) { location.lat = "10"; location.lon = "-84"; } else if (location.country.toUpperCase().contains("SWEDEN")) { location.lat = "59.2"; location.lon = "18.04"; } else if (location.country.toUpperCase().contains("MEXICO")) { location.lat = "19.5"; location.lon = "-99.1"; } else if (location.country.toUpperCase().contains("BELGIUM")) { location.lat = "50.8"; location.lon = "4.3"; } else if (location.country.toUpperCase().contains("UKRAINE")) { location.lat = "49"; location.lon = "32"; } else if (location.country.toUpperCase().contains("VENEZUELA")) { location.lat = "10.5"; location.lon = "-66.9"; } else if (location.country.toUpperCase().contains("HONG KONG")) { location.lat = "22.3"; location.lon = "114.2"; } else if (location.country.toUpperCase().contains("UNITED KINGDOM")) { location.lat = "51.5"; location.lon = "0"; } else if (location.country.toUpperCase().contains("CROATIA")) { location.lat = "45.2"; location.lon = "15.5"; } else if (location.country.toUpperCase().contains("RUSSIAN FEDERATION")) { location.lat = "55.7"; location.lon = "37.6"; } else if (location.country.toUpperCase().contains("BRAZIL")) { location.lat = "-22.5"; location.lon = "-46.6"; } else if (location.country.toUpperCase().contains("UNKNOWN")) { location.lat = "31"; location.lon = "-70"; } else { System.out.println(location.country); return(null); } //since these are default lat/lon for countries, randomize the location by 2 degrees location.lat = "" + (Double.parseDouble(location.lat) + (Double)(-1 + 2*Math.random())); location.lon = "" + (Double.parseDouble(location.lon) + (Double)(-1 + 2* Math.random())); } //trim off the country code -- e.g., "(US)" location.country = location.country.substring(0, location.country.length()-4).trim(); //abbreviate UNITED STATES to USA if (location.country.contains("UNITED STATES")) location.country = "USA"; if (location.country.toUpperCase().contains("UNKNOWN")) location.name = "Location Unknown"; else if (location.city.toUpperCase().contains("UNKNOWN")) location.name = location.country; else location.name = location.city + " " + location.country; return(location); } catch (Exception e) { e.printStackTrace(); return(null); } } /** * Writes a text file to the local/network file system * @param text String * @param filepath String * @throws IOException */ public void writeTextFile(String text, String filepath) throws IOException { BufferedWriter writer = new BufferedWriter(new FileWriter(filepath)); try { writer.write(text); writer.flush(); } finally { if (writer != null) writer.close(); } } /** * Reads a text file from the local/network file system and returns it as a String * @param filepath String * @return String * @throws IOException */ private ArrayList readTextFile() throws IOException { BufferedReader reader = null; ArrayList lines = new ArrayList(); reader = new BufferedReader(new FileReader(webAccessLogFilePath)); try { String s = null; do { s = reader.readLine(); if (s != null) lines.add(s); } while (s != null); } finally { if (reader != null) reader.close(); } return (lines); } /** * Builds a list of unique IP addresses found in the access_log text file that correspond to the * given three resourceName(s). resourceName2 and resourceName3 can be null * @param resourceName1 * @param resourceName2 * @param resourceName3 * @return * @throws java.io.IOException */ private ArrayList getUniqueIPsThatAccessedResource(String resourceName1, String resourceName2, String resourceName3) throws IOException { ArrayList uniqueIPs = new ArrayList(); ArrayList lines = readTextFile(); for (String line : lines) { if (line.contains(resourceName1) || (resourceName2 != null && line.contains(resourceName2)) || (resourceName3 != null && line.contains(resourceName3))) { int ipEndIndex = line.indexOf(" -"); String ip = line.substring(0, ipEndIndex); if (!uniqueIPs.contains(ip)) { uniqueIPs.add(ip); // System.out.println(ip); } } } System.out.println(uniqueIPs.size() + " instances of '" + resourceName1 + "' found in " + webAccessLogFilePath); return(uniqueIPs); } /** * Append the header tags to the given kml StringBuffer. * @param kml */ private void appendHeaderKML(StringBuffer kml) { kml.append("\n"); kml.append("\n"); kml.append("\n"); //custom icon for WRF Portal locations on Google Earth map kml.append(" \n"); //custom icon for WRF Domain Wizard (WDW) locations on Google Earth map kml.append(" \n"); //custom icon for External Workflow Manager locations on Google Earth map kml.append(" \n"); } /** * Append tailing tags to kml document * @param kml */ private void appendTailKML(StringBuffer kml) { kml.append("\n"); kml.append("\n"); } /** * For each unique IP address, lookup location, and use this location information to generate * a Placemark tag in the given kml. * @param kml * @param uniqueIPs * @param iconName * @param descFilename * @throws java.io.IOException */ private void appendPlacemarksKML(StringBuffer kml, ArrayListuniqueIPs, String iconName, String descFilename) throws IOException { ArrayListuniqueCountries = new ArrayList(); for (String ip : uniqueIPs) { Location location = getLocation(ip); if (location == null) { System.out.println("location is null for " + ip); continue; } if (!uniqueCountries.contains(location.country)) uniqueCountries.add(location.country); kml.append(" \n"); kml.append(" " + location.name + "\n"); kml.append(" #" + iconName + "\n"); //when user clicks on Placemark icon, the html "description" below will pop up in a Google Earth window kml.append(" ]]>\n"); kml.append(" \n"); kml.append(" " + location.lon + "," + location.lat + ",0\n"); kml.append(" \n"); kml.append(" \n"); } System.out.println("For " + descFilename + ", " + uniqueCountries.size() + " unique countries were found in the web access log for the given IP addresses."); } /** * Run program * @param args * @throws java.io.IOException */ public static void main(String[] args) throws IOException { /* The web server access log file path. Here is a sample line for a DomainWizard.jnlp download from the web server access log: 58.97.8.58 - - [01/Jun/2008:04:11:53 -0400] "GET /domainwizard/DomainWizard.jnlp HTTP/1.0" 200 953 "http://www.wrfportal.org/DomainWizard.html" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031030" */ String accessLogFilePath = "C:\\Java\\WebAccessLogCounter\\data\\access_log.txt"; //output file path of the kml file we will build String kmlFilePath = "C:\\Java\\WebAccessLogCounter\\data\\workflowmgr.kml"; AccessCounter ac = new AccessCounter(accessLogFilePath); StringBuffer kml = new StringBuffer(); ac.appendHeaderKML(kml); ArrayListuniqueIPs; uniqueIPs = ac.getUniqueIPsThatAccessedResource("workflowmgr.zip", null, null); ac.appendPlacemarksKML(kml, uniqueIPs, "workflowmgrIcon", "workflowmgr-installation.png"); // uniqueIPs = ac.getUniqueIPsThatAccessedResource("WRFDomainWizard102.zip", "WRFDomainWizard.zip", "DomainWizard.jnlp"); // ac.appendPlacemarksKML(kml, uniqueIPs, "wdwIcon", "wdw-installation.png"); // uniqueIPs = ac.getUniqueIPsThatAccessedResource("portal.jnlp", "wrf-portal.zip", null); // ac.appendPlacemarksKML(kml, uniqueIPs, "portalIcon", "portal-installation.png"); ac.appendTailKML(kml); ac.writeTextFile(kml.toString(), kmlFilePath); System.out.println(kml.toString().length() + " bytes written to " + kmlFilePath); } }