Java tutorial
/* * TV-Browser * Copyright (C) 04-2003 Martin Oberhauser (darras@users.sourceforge.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * CVS information: * $RCSfile$ * $Source$ * $Date: 2010-01-30 08:42:35 +0100 (Sat, 30 Jan 2010) $ * $Author: bananeweizen $ * $Revision: 6424 $ */ package util.io; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.StringTokenizer; import java.util.logging.Logger; import java.util.zip.GZIPOutputStream; import org.apache.commons.lang.math.RandomUtils; import util.exc.TvBrowserException; import devplugin.Date; import devplugin.ProgressMonitor; /** * * * @author Til Schneider, www.murfman.de */ public class Mirror { /** The localizer for this class. */ public static final util.ui.Localizer mLocalizer = util.ui.Localizer.getLocalizerFor(Mirror.class); private static final int MAX_UP_TO_DATE_CHECKS = 10; private static final int MAX_LAST_UPDATE_DAYS = 5; /** The name extension of mirror files */ public static final String MIRROR_LIST_FILE_NAME = "mirrorlist.gz"; private static final Logger mLog = Logger.getLogger(Mirror.class.getName()); /** The default weight of a mirror */ public static final int DEFAULT_WEIGHT = 100; private String mUrl; private int mWeight; /** List of blocked Servers */ private static ArrayList<String> BLOCKEDSERVERS = new ArrayList<String>(); /** Mirror-Download Running?*/ private static boolean mMirrorDownloadRunning = true; /** Exception on downloading in Thread */ private static boolean mDownloadException = false; /** Data of Mirror-Download*/ private static byte[] mMirrorDownloadData = null; /** * @param url * @param weight */ public Mirror(String url, int weight) { // Escape spaces in the URL mUrl = IOUtilities.replace(url, " ", "%20"); mWeight = weight; } /** * Creates an instance with the given URL * and the default weight for this mirror. * * @param url The URL of the mirror. */ public Mirror(String url) { this(url, DEFAULT_WEIGHT); } /** * Gets the URL of this Mirror. * * @return The URL of this Mirror. */ public String getUrl() { return mUrl; } /** * Gets the weight of this Mirror. * * @return The weight of this Mirror. */ public int getWeight() { return mWeight; } /** * Sets the weight of this Mirror. * * @param weight The new weight of this Mirror. */ public void setWeight(int weight) { mWeight = weight; } /** * Reads the mirrors from the given stream. * * @param stream * The stream to read the mirrors from. * @return The mirror array read from the stream. * @throws IOException * Thrown if something went wrong. * @throws FileFormatException * Thrown if something went wrong. */ private static Mirror[] readMirrorListFromStream(InputStream stream) throws IOException, FileFormatException { InputStream gIn = IOUtilities.openSaveGZipInputStream(stream); BufferedReader reader = new BufferedReader(new InputStreamReader(gIn)); ArrayList<Mirror> list = new ArrayList<Mirror>(); String line; int lineCount = 1; while ((line = reader.readLine()) != null) { line = line.trim(); if (line.length() > 0) { // This is not an empty line -> read it StringTokenizer tokenizer = new StringTokenizer(line, ";"); if (tokenizer.countTokens() < 2) { throw new FileFormatException( "Syntax error in mirror file line " + lineCount + ": '" + line + "'"); } String url = tokenizer.nextToken(); String weightAsString = tokenizer.nextToken(); int weight; try { weight = Integer.parseInt(weightAsString); } catch (Exception exc) { throw new FileFormatException("Syntax error in mirror file line " + lineCount + ": weight is not a number: '" + weightAsString + "'"); } list.add(new Mirror(url, weight)); } lineCount++; } gIn.close(); Mirror[] mirrorArr = new Mirror[list.size()]; list.toArray(mirrorArr); return mirrorArr; } /** * Reads the mirrors in the given file. * * @param file * The file to read the mirrors from. * @return The mirror array read from the file. * @throws IOException * Thrown if something went wrong. * @throws FileFormatException * Thrown if something went wrong. */ public static Mirror[] readMirrorListFromFile(File file) throws IOException, FileFormatException { BufferedInputStream stream = null; try { stream = new BufferedInputStream(new FileInputStream(file), 0x2000); return readMirrorListFromStream(stream); } finally { if (stream != null) { try { stream.close(); } catch (IOException exc) { } } } } /** * Write the mirror array to the given stream. * * @param stream The stream to write the mirror array to. * @param mirrorArr The mirror array to write. * @throws IOException Thrown if something went wrong. */ private static void writeMirrorListToStream(OutputStream stream, Mirror[] mirrorArr) throws IOException { GZIPOutputStream gOut = new GZIPOutputStream(stream); PrintWriter writer = new PrintWriter(gOut); for (Mirror mirror : mirrorArr) { writer.print(mirror.getUrl()); writer.print(";"); writer.println(String.valueOf(mirror.getWeight())); } writer.close(); gOut.close(); } /** * * * @param file The file to write the mirror array to. * @param mirrorArr The mirror array to write. * @throws IOException Thrown if something went wrong. */ public static void writeMirrorListToFile(File file, Mirror[] mirrorArr) throws IOException { // NOTE: We need two try blocks to ensure that the file is closed in the // outer block. try { FileOutputStream stream = null; try { stream = new FileOutputStream(file); writeMirrorListToStream(stream, mirrorArr); } finally { // Close the file in every case if (stream != null) { try { stream.close(); } catch (IOException exc) { } } } } catch (IOException exc) { file.delete(); throw exc; } } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + ((mUrl == null) ? 0 : mUrl.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Mirror other = (Mirror) obj; if (mUrl == null) { if (other.mUrl != null) { return false; } } else if (!mUrl.equals(other.mUrl)) { return false; } return true; } /** * Loads the mirror lists from the given file * and the given server defined mirror array. * * @param file The file to load the mirrors from. * @param mirrorUrlArr The array with the current mirrors urls. * @param serverDefindedMirros The array with the server definded mirrors * @return The load mirror array. */ public static Mirror[] loadMirrorList(File file, String[] mirrorUrlArr, Mirror[] serverDefindedMirros) { try { ArrayList<Mirror> mirrorList = new ArrayList<Mirror>( Arrays.asList(Mirror.readMirrorListFromFile(file))); for (int i = 0; i < mirrorUrlArr.length; i++) { Mirror basemirror = mirrorList.get(i); if (!mirrorList.contains(basemirror)) { mirrorList.add(basemirror); } } if (serverDefindedMirros != null) { for (int i = 0; i < serverDefindedMirros.length; i++) { if (!mirrorList.contains(serverDefindedMirros[i])) { mirrorList.add(serverDefindedMirros[i]); } } } return mirrorList.toArray(new Mirror[mirrorList.size()]); } catch (Exception exc) { ArrayList<Mirror> mirrorList = new ArrayList<Mirror>(); for (int i = 0; i < mirrorUrlArr.length; i++) { if (!BLOCKEDSERVERS.contains(getServerBase(mirrorUrlArr[i])) && mirrorUrlArr[i] != null) { mirrorList.add(new Mirror(mirrorUrlArr[i])); } } return mirrorList.toArray(new Mirror[mirrorList.size()]); } } /** * Get the Server-Domain of the Url * @param url Url to fetch the Server-Domain from * @return Server-Domain */ private static String getServerBase(String url) { if (url.startsWith("http://")) { url = url.substring(7); } if (url.indexOf('/') >= 0) { url = url.substring(0, url.indexOf('/')); } return url; } private static Mirror chooseMirror(Mirror[] mirrorArr, Mirror oldMirror, String name, Class caller) throws TvBrowserException { Mirror[] oldMirrorArr = mirrorArr; /* remove the old mirror from the mirrorlist */ if (oldMirror != null) { ArrayList<Mirror> mirrors = new ArrayList<Mirror>(); for (Mirror mirror : mirrorArr) { if (oldMirror != mirror) { mirrors.add(mirror); } } mirrorArr = new Mirror[mirrors.size()]; mirrors.toArray(mirrorArr); } // Get the total weight int totalWeight = 0; for (Mirror mirror : mirrorArr) { totalWeight += mirror.getWeight(); } // Choose a weight int chosenWeight = RandomUtils.nextInt(totalWeight); // Find the chosen mirror int currWeight = 0; for (Mirror mirror : mirrorArr) { currWeight += mirror.getWeight(); if (currWeight > chosenWeight) { // Check whether this is the old mirror or Mirror is Blocked if (((mirror == oldMirror) || BLOCKEDSERVERS.contains(getServerBase(mirror.getUrl()))) && (mirrorArr.length > 1)) { // We chose the old mirror -> chose another one ArrayList<Mirror> oldList = new ArrayList<Mirror>(oldMirrorArr.length); for (Mirror m : oldMirrorArr) { oldList.add(m); } ArrayList<Mirror> currentList = new ArrayList<Mirror>(mirrorArr.length); for (Mirror m : mirrorArr) { currentList.add(m); } Comparator<Mirror> comp = new Comparator<Mirror>() { @Override public int compare(Mirror m1, Mirror m2) { return m1.getUrl().compareTo(m2.getUrl()); } }; Collections.sort(oldList, comp); Collections.sort(currentList, comp); if (oldList.equals(currentList)) { // avoid stack overflow return mirror; } return chooseMirror(mirrorArr, oldMirror, name, caller); } else { return mirror; } } } // We didn't find a mirror? This should not happen -> throw exception StringBuilder buf = new StringBuilder(); for (Mirror mirror : oldMirrorArr) { buf.append(mirror.getUrl()).append('\n'); } throw new TvBrowserException(caller, "error.2", "No mirror found\ntried following mirrors: ", name, buf.toString()); } /** * Chooses a up to date mirror. * * @param mirrorArr The mirror array to check. * @param monitor The progress monitor to use. * @param name The name of the file to check. * @param id The id of the file to check. * @param caller The caller class. * @param additionalErrorMsg An additional error message value. * @return The choosen mirror or <code>null</code>, if no up to date mirror was found or something went wrong. * @throws TvBrowserException */ public static Mirror chooseUpToDateMirror(Mirror[] mirrorArr, ProgressMonitor monitor, String name, String id, Class caller, String additionalErrorMsg) throws TvBrowserException { boolean isUpToDate = false; // Choose a random Mirror Mirror mirror = chooseMirror(mirrorArr, null, name, caller); if (monitor != null) { monitor.setMessage(mLocalizer.msg("info.3", "Try to connect to mirror {0}", mirror.getUrl())); } // Check whether the mirror is up to date and available for (int i = 0; i < MAX_UP_TO_DATE_CHECKS; i++) { try { if (mirrorIsUpToDate(mirror, id)) { isUpToDate = true; break; } else { // This one is not up to date -> choose another one Mirror oldMirror = mirror; mirror = chooseMirror(mirrorArr, mirror, name, caller); mLog.info("Mirror " + oldMirror.getUrl() + " is out of date or down. Choosing " + mirror.getUrl() + " instead."); if (monitor != null) { monitor.setMessage( mLocalizer.msg("info.4", "Mirror {0} is out of date or down. Choosing {1}", oldMirror.getUrl(), mirror.getUrl())); } } } catch (TvBrowserException exc) { String blockedServer = getServerBase(mirror.getUrl()); BLOCKEDSERVERS.add(blockedServer); mLog.info("Server blocked : " + blockedServer); if (mirrorArr.length == 1 && mirrorArr[0].equals(mirror)) { return null; } // This one is not available -> choose another one Mirror oldMirror = mirror; mirror = chooseMirror(mirrorArr, mirror, name, caller); mLog.info("Mirror " + oldMirror.getUrl() + " is not available. Choosing " + mirror.getUrl() + " instead."); if (monitor != null) { monitor.setMessage(mLocalizer.msg("info.5", "Mirror {0} is not available. Choosing {1}", oldMirror.getUrl(), mirror.getUrl())); } } } // Return the mirror if (isUpToDate) { return mirror; } return null; } private static boolean mirrorIsUpToDate(Mirror mirror, String id) throws TvBrowserException { // Load the lastupdate file and parse it final String url = mirror.getUrl() + (mirror.getUrl().endsWith("/") ? "" : "/") + id + "_lastupdate"; Date lastupdated; mMirrorDownloadRunning = true; mMirrorDownloadData = null; mDownloadException = false; mLog.info("Loading MirrorDate from " + url); new Thread(new Runnable() { public void run() { try { mMirrorDownloadData = IOUtilities.loadFileFromHttpServer(new URL(url), 60000); } catch (Exception e) { mDownloadException = true; } mMirrorDownloadRunning = false; }; }, "Load mirror date from " + url).start(); int num = 0; // Wait till second Thread is finished or 15000 ms reached while ((mMirrorDownloadRunning) && (num < 150)) { num++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } if (mMirrorDownloadRunning || mMirrorDownloadData == null || mDownloadException) { mLog.info("Server " + url + " is down!"); return false; } try { // Parse is. E.g.: '2003-10-09 11:48:45' String asString = new String(mMirrorDownloadData); if (asString.length() > 10) { int year = Integer.parseInt(asString.substring(0, 4)); int month = Integer.parseInt(asString.substring(5, 7)); int day = Integer.parseInt(asString.substring(8, 10)); lastupdated = new Date(year, month, day); mLog.info("Done !"); return lastupdated.compareTo(new Date().addDays(-MAX_LAST_UPDATE_DAYS)) >= 0; } } catch (NumberFormatException parseException) { mLog.info("The file on the server has the wrong format!"); } return false; } /** * Reset the List of banned Servers */ public static void resetBannedServers() { BLOCKEDSERVERS.clear(); } }