//Gruppe 01 (Übung 4) //Aufgabe 4-3 package uebung04.aufgabe03; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.HashSet; import javax.swing.text.MutableAttributeSet; import javax.swing.text.html.HTML; import javax.swing.text.html.HTML.Tag; import javax.swing.text.html.HTMLEditorKit.ParserCallback; import javax.swing.text.html.parser.ParserDelegator; /** * Sucht in einer HTML-Ressource nach eingebetten Objekten und summiert die Dateigrößen auf, * wobei mehrfach verlinkte Objekte nur einmal gezählt werden. * Bei Framesets werden sämtliche Frames ausgewertet, in mehreren Frames dargestellte Seiten * werden nur einfach gezählt. * * @author Tilman Walther * @version 2.1 */ public class SiteSize extends ParserCallback { private URL url; // die URL der HTML-Ressource private URLConnection connection; // Verbindung zur HTML-Ressource private HashSet objects = null; // speichert URLs der bereits gezählten Objekten private long size = 0; // die Größe aller gezählten Objekte private ParserDelegator parser = new ParserDelegator(); // parst die Ressource und ruft handleSimpleTag() auf /** * Parses the resource behind the given URL and measures the size. * If the URL points to a non-HTML resource the size will be zero. * @param url the URL for the resource to parse * @return the size of the website including all embedded objects * @throws IOException if an I/O error occurs */ public long getSize(URL url) throws IOException { /* Für Testzwecke: Durchleitungsproxy einstellen / java.util.Properties props = System.getProperties(); props.put("proxySet", "true"); props.put("proxyHost", "127.0.0.1"); props.put("proxyPort", "10000"); /* --------------------------------------------- */ System.out.println("\nparsing "+url); this.url = url; connection = url.openConnection(); // Daten vom Server im "identity"-Format anfordern connection.addRequestProperty("Accept-Encoding", "identity"); // TODO für Testzwecke: //connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; de-de) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12"); connection.connect(); //System.out.println("Transfer-Encoding: "+connection.getHeaderField("transfer-encoding")+"\n"); // falls objects nicht bereits durch den privaten Konstruktor // inititalisiert wurde, neues HashSet erzeugen if (objects == null) objects = new HashSet(); // auf HTML-Inhalt prüfen if (connection.getContentType().startsWith("text/html")) { // Größe der Seite zur Gesamtgröße addieren addSize(url); // eingebettete Objekte mit Parser finden/addieren BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); parser.parse(br, this, true); } // Gesamtgröße zurückgeben return size; } /** * This private method is used for recursive processing of framesets. * In order not to count linked objects twice, the HashSet 'objects' is passed. * @param url the URL for the actual frame * @param objects the HashSet containing all objects that have already been counted * @return the size if the subframe * @throws IOException if an I/O error occurs */ private long getSize(URL url, HashSet objects) throws IOException { this.objects = objects; return getSize(url); } /** * Overrides javax.swing.text.html.HTMLEditorKit.ParserCallback.handleSimpleTag() which is called by the ParserDelegator * @see javax.swing.text.html.HTMLEditorKit.ParserCallback#handleSimpleTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, int) */ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) { String objLink = null; try { if (t == HTML.Tag.FRAME) { // leider trifft man öfter auf dummy-frames (Dreamweaver!), deswegen // fehlt ab und zu die Quelle if (a.getAttribute(HTML.Attribute.SRC) != null) { // absuluten Pfad zur Ressource nachvollziehen, um mehrfache Zählung zu vermeiden URL frameUrl = generateURL(a.getAttribute(HTML.Attribute.SRC).toString()); // Frame von neuem SiteSize-Objekt parsen lassen SiteSize frameSize = new SiteSize(); size += frameSize.getSize(frameUrl, objects); } } else { if (t == HTML.Tag.APPLET) { // Applets können direkt via CODE-Attribut oder als Archiv verlinkt sein if (a.getAttribute(HTML.Attribute.ARCHIVE) != null) { objLink = a.getAttribute(HTML.Attribute.ARCHIVE).toString(); } else { objLink = a.getAttribute(HTML.Attribute.CODE).toString(); } } else if (t == HTML.Tag.IMG) { objLink = a.getAttribute(HTML.Attribute.SRC).toString(); } else if (t == HTML.Tag.OBJECT) { objLink = a.getAttribute(HTML.Attribute.DATA).toString(); } else if (t == HTML.Tag.LINK) { objLink = a.getAttribute(HTML.Attribute.HREF).toString(); } if (objLink != null) { try { // absuluten Pfad zur Ressource nachvollziehen, um mehrfache Zählung zu vermeiden addSize(generateURL(objLink)); } catch (MalformedURLException mue) { System.out.println("MalformedURLException on position " + pos + ": '" + objLink + "'"); } } } } catch (IOException ioe) { System.out.println("IOException (" + pos + "): Could not connect to linked object. ("+objLink+")"); } } /** * Overrides javax.swing.text.html.HTMLEditorKit.ParserCallback.handleStartTag() which is called by the ParserDelegator. * @see javax.swing.text.html.HTMLEditorKit.ParserCallback#handleStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, int) */ public void handleStartTag(Tag t, MutableAttributeSet a, int pos) { // Da APPLET- und OBJECT-Tags auch als StartTags vorkommen können, wird das der Aufruf // in diesem Fall an handleSimpleTag() weitergegeben if ((t == HTML.Tag.APPLET) || (t == HTML.Tag.OBJECT)) handleSimpleTag(t, a, pos); } /** * Adds the size of the resource that the given URLConnection points to if it has not already been counted * @param connection an URLConnection to a resource * @throws IOException if an I/O error occurs */ private void addSize(URL url) throws IOException { URLConnection resConn = url.openConnection(); // TODO für Testzwecke: //resConn.addRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; de-DE; rv:1.7) Gecko/20040803 Firefox/0.9.3"); resConn.connect(); String resUrlStr = url.toString(); if (!objects.contains(resUrlStr)) { objects.add(resUrlStr); int resLength = resConn.getContentLength(); if (resLength >= 0) { size += resLength; } else { // da der Content-Length Header nicht übermittelt wurde, muss zur // Messung die Ressource übertragen werden int bytesRead; byte[] buffer = new byte[1024]; BufferedInputStream input = new BufferedInputStream(resConn.getInputStream()); resLength = 0; while ((bytesRead = input.read(buffer)) >= 0) { resLength += bytesRead; } size += resLength; } System.out.println("added '" + resUrlStr + "' (" + resLength + " bytes)"); } } /** * Generates the URL for a (relative) link extracted from a tag attribute * @param link a resource link from a tag attribute * @return the URL to the resource * @throws MalformedURLException */ // TODO wahrscheinlich lässt sich diese Methode (zumindest teilw.) durch URI.relativize() ersetzen private URL generateURL(String link) throws MalformedURLException { if (link.startsWith("//")) link = "http:" + link; String resUrlStr = null; if (link.indexOf("://") > 0) { resUrlStr = new String(link); } else if (link.startsWith("/")) { resUrlStr = new String(url.getProtocol() + "://" + url.getAuthority() + link); } else { String path = url.getPath(); if (path.length() > 0) path = path.substring(0, path.lastIndexOf("/")); resUrlStr= new String(url.getProtocol() + "://" + url.getAuthority() + path + "/" + link); } int i, j; while ( ((i = resUrlStr.indexOf("../")) > -1) && ((j = resUrlStr.indexOf("/", i+3)) > -1) ){ resUrlStr = resUrlStr.substring(0,i) + resUrlStr.substring(j+1); } //new URL(URLEncoder.encode(resUrlStr, "UTF-8")); return new URL(resUrlStr); } public static void main(String[] args) { try { String urlString; if (args.length == 0) { // URL von der Kommandozeile lesen BufferedReader din = new BufferedReader(new InputStreamReader(System.in)); System.out.print("URL: "); urlString = din.readLine(); } else { urlString = args[0]; } // URL erzeugen URL url = new URL(urlString); // SiteSize-Objekt erzeugen SiteSize siteSize = new SiteSize(); // Brutto-Größe messen und ausgeben System.out.println("\nSize of content: " + siteSize.getSize(url) + " bytes"); } catch (Exception e) { e.printStackTrace(); } } }