Java tutorial
/* * 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 3 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, see <http://www.gnu.org/licenses/>. */ package com.frostwire.gui.updates; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.util.OSUtils; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import com.limegroup.gnutella.gui.search.SearchEngine; import com.limegroup.gnutella.settings.ApplicationSettings; import com.limegroup.gnutella.settings.ChatSettings; import com.limegroup.gnutella.util.FrostWireUtils; /** * SAX Parser and more. Its responsible for creating UpdateMessages The * UpdateManager will ask this object if it has announcements or an update * message available. */ public final class UpdateMessageReader implements ContentHandler { private static final Log LOG = LogFactory.getLog(UpdateMessageReader.class); public HashSet<UpdateMessage> _announcements = null; public UpdateMessage _bufferMessage = null; public boolean _introloaded = false; public boolean _otherloaded = false; public LinkedList<UpdateMessage> _overlays = null; public UpdateMessage _updateMessage = null; // public void UpdateMessageReader() {} public String _updateURL = "http://update.frostwire.com"; public String _updateServer = ""; // FTA: Used to connect using NIO // (non-blocking). I've found that // update.frostwire.com was down causing // part of the abnormal behaviour. /** * Only ads Announcements that have not expired * * @param msg */ public void addAnnouncement(UpdateMessage msg) { if (_announcements == null) { _announcements = new HashSet<UpdateMessage>(); } if (msg.getMessageType().equals("announcement") && !msg.hasExpired()) { _announcements.add(msg); } } /** * As overlay messages are interpreted, this method will add them to * _overlays. It will replace old intro messages for newer, and old * afterSearch messages for newer. * * When I say 'old' I mean, a message that was received just a while ago as * we were parsing the document. * * The idea is that update.frostwire.com defines first the more generic * update overlays, and further down in the document the more specific * overlays, so that we can ignore generic configuration if we are fit for a * more specific overlay * * e.g. An intro for a specific frostwire version, language. * * which message to keep for the client. * * @see * UpdateManager.updateOverlays(HashSet<UpdateMessage>,UpdateMessageReader * aka:me) * @param msg */ public void addOverlay(UpdateMessage msg) { if (msg != null && msg.getMessageType().equals("overlay")) { if (_overlays == null) _overlays = new LinkedList<UpdateMessage>(); // Replace newly found intro, or aftersearch // for previously added message of same nature. if (!_overlays.isEmpty()) { Iterator<UpdateMessage> it = _overlays.iterator(); while (it.hasNext()) { UpdateMessage m = it.next(); // Find another intro or after search, and replace it if (m.isIntro() == msg.isIntro()) { _overlays.remove(m); _overlays.add(msg); return; } } // while to find what to replace } // if we can iterate // if I make it here, then I need to add a new overlay _overlays.add(msg); } // if we actually receive an overlay else { System.out.println("UpdateManager.addOverlay() - The message given wasn't good."); } } public void characters(char[] ch, int start, int length) throws SAXException { } public void endDocument() throws SAXException { } /** * When the tag closes, if we have a _buffer message, we check what type it * is and set it as the Update Message (if its for this client/os) or if its * an announcement, we add it to the announcements collection. * * This function will make use of 'isMessageForMe', to try to discard the * message in case its addressed to another OS different than the one where * this client is running on top of. * * If its an update message and no update message has been spotted so far, * the UpdateReader sets it as the update message object available * * If its an announcement we attempt adding it as another announcement. That * method will only add it if the message has not expired. * */ public void endElement(String uri, String name, String qName) throws SAXException { // discard buffer message if its not meant for me right away. if (!isMessageForMe(_bufferMessage)) { // System.out.println("Discarding message - " + _bufferMessage); _bufferMessage = null; return; } if (_bufferMessage != null && name.equalsIgnoreCase("message")) { if (_bufferMessage.getMessageType().equalsIgnoreCase("update")) { setUpdateMessage(_bufferMessage); } else if (_bufferMessage.getMessageType().equalsIgnoreCase("announcement")) { addAnnouncement(_bufferMessage); } else if (_bufferMessage.getMessageType().equalsIgnoreCase("overlay")) { // System.out.println("UpdateMessageReader.endElement() - addOverlay"); addOverlay(_bufferMessage); } _bufferMessage = null; } } // endElement public void endPrefixMapping(String arg0) throws SAXException { } public HashSet<UpdateMessage> getAnnouncements() { return _announcements; } public List<UpdateMessage> getOverlays() { return _overlays; } public UpdateMessage getUpdateMessage() { return _updateMessage; } public String getUpdateURL() { return _updateURL; } public boolean hasAnnouncements() { return _announcements != null && _announcements.size() > 0; } public boolean hasOverlays() { return _overlays != null && _overlays.size() > 0; } public boolean hasUpdateMessage() { return _updateMessage != null; } public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException { } public boolean isIntroLoaded() { return _introloaded; // checks if intro is loaded for the current // language } /** * Checks if we should show this message for the language the user is using. * If no language is specified in the message, then it returns true right * away the message is meant for everyone, unless there's a more specific * message ahead. * * * If you want a full blown validation use isMessageForMe() * * @param msg * @return */ private boolean isMessageEligibleForMyLang(UpdateMessage msg) { String langinmsg = msg.getLanguage(); // current language in message if (langinmsg == null || langinmsg.equals("*")) return true; String langinapp = ApplicationSettings.getLanguage().toLowerCase(); if (langinmsg.length() == 2) return langinapp.toLowerCase().startsWith(langinmsg.toLowerCase()); if (langinmsg.endsWith("*")) { langinapp = ApplicationSettings.getLanguage().substring(0, 2); langinmsg = langinmsg.substring(0, langinmsg.indexOf("*")); // removes // last // char } return langinmsg.equalsIgnoreCase(langinapp); } /** * Checks if this message should be shown for the OS on which this FrostWire * is runnning on. * * If you want a full blown validation use isMessageForMe() * * @param msg * @return */ private boolean isMessageEligibleForMyOs(UpdateMessage msg) { if (msg.getOs() == null) return true; boolean im_mac_msg_for_me = msg.getOs().equals("mac") && OSUtils.isMacOSX(); boolean im_windows_msg_for_me = msg.getOs().equals("windows") && (OSUtils.isWindows() || OSUtils.isWindowsXP() || OSUtils.isWindowsNT() || OSUtils.isWindows98() || OSUtils.isWindows95() || OSUtils.isWindowsMe() || OSUtils.isWindowsVista()); boolean im_linux_msg_for_me = msg.getOs().equals("linux") && OSUtils.isLinux(); return im_mac_msg_for_me || im_windows_msg_for_me || im_linux_msg_for_me; } /** * Checks if FrostWire isn't too old for this message. If the message has no * version info, it doesn't matter then, it should be eligible for all * versions. * * When it's an update message: If the message is an update message then it * doesn't matter what version it's sent, it should be valid because we need * to use the version in it to see if we have to update or not. * * If you want a full blown validation use isMessageForMe() * * @param msg * @return */ private boolean isMessageEligibleForMyVersion(UpdateMessage msg) { if (msg.getVersion() == null || msg.getMessageType().equalsIgnoreCase("update")) return true; return !UpdateManager.isFrostWireOld(msg.getVersion()); } /** * Tells me if I'm supposed to keep the given update message. Compares the * message's OS string against the current operating system. * * Compares: - version (If message matches all versions before me [not exact * match]) * * If the message is an announcement, it cares about the version number not * being outdated. * * @param msg * @return */ private boolean isMessageForMe(UpdateMessage msg) { if (msg == null) { // System.out.println("UpdateManager.isMessageForMe() - Message was null"); return false; } /* * System.out.println("UpdateManager.isMessageForMe() - isMessageEligibleForMyOs - " * + isMessageEligibleForMyOs(msg));System.out.println( * "UpdateManager.isMessageForMe() - isMessageEligibleForMyLang - " + * isMessageEligibleForMyLang(msg));System.out.println( * "UpdateManager.isMessageForMe() - isMessageEligibleForMyVersion - " + * isMessageEligibleForMyVersion(msg)); */ return isMessageEligibleForMyOs(msg) && isMessageEligibleForMyLang(msg) && isMessageEligibleForMyVersion(msg); } // isMessageForMe public boolean isOtherLoaded() { return _otherloaded; // check if overlay is loaded for the current // language } public void processingInstruction(String arg0, String arg1) throws SAXException { } public void readUpdateFile() { HttpURLConnection connection = null; InputSource src = null; try { String userAgent = "FrostWire/" + OSUtils.getOS() + "-" + OSUtils.getArchitecture() + "/" + FrostWireUtils.getFrostWireVersion(); connection = (HttpURLConnection) (new URL(getUpdateURL())).openConnection(); String url = getUpdateURL(); System.out.println("Reading update file from " + url); connection.setRequestProperty("User-Agent", userAgent); connection.setRequestProperty("Connection", "close"); connection.setReadTimeout(10000); // 10 secs timeout if (connection.getResponseCode() >= 400) { // invalid URL for sure connection.disconnect(); return; } src = new InputSource(connection.getInputStream()); XMLReader rdr = XMLReaderFactory .createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser"); rdr.setContentHandler(this); rdr.parse(src); connection.getInputStream().close(); connection.disconnect(); } catch (java.net.SocketTimeoutException e3) { System.out.println("UpdateMessageReadre.readUpdateFile() Socket Timeout Exeception " + e3.toString()); } catch (IOException e) { System.out.println("UpdateMessageReader.readUpdateFile() IO exception " + e.toString()); } catch (SAXException e2) { System.out.println("UpdateMessageReader.readUpdateFile() SAX exception " + e2.toString()); } } public void setDocumentLocator(Locator arg0) { } /** * Sets only the first update message it finds. Make sure to put only have a * single update message everytime on the server, If you plan to leave old * messages there, keep the newest one at the beginning of the file. * * @param msg */ public void setUpdateMessage(UpdateMessage msg) { if (_updateMessage == null && msg != null && msg.getMessageType().equals("update")) { _updateMessage = msg; } } public void setUpdateURL(String updateURL) { if (updateURL == null) _updateURL = "http://update.frostwire.com"; else _updateURL = updateURL; } public void skippedEntity(String arg0) throws SAXException { } public void startDocument() throws SAXException { } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { // deal with the opening tag if (localName.equalsIgnoreCase("update")) { UpdateManager.getInstance().setServerTime(atts.getValue("time")); if (atts.getValue("torrentDetailsUrl") != null && atts.getValue("torrentDetailsUrl").length() > 0) { String torrentDetailsUrl = atts.getValue("torrentDetailsUrl"); List<SearchEngine> searchEngines = SearchEngine.getSearchEngines(); for (SearchEngine searchEngine : searchEngines) { // not doing it for ClearBits on purpose. if (!searchEngine.equals(SearchEngine.CLEARBITS)) { searchEngine.redirectUrl = torrentDetailsUrl; } } } } else if (localName.equalsIgnoreCase("message")) { String type = atts.getValue("type"); String message = atts.getValue("value"); String url = atts.getValue("url"); String torrent = atts.getValue("torrent"); String installerUrl = atts.getValue("installerUrl"); String os = atts.getValue("os"); String showOnce = atts.getValue("showOnce"); String version = atts.getValue("version"); String src = atts.getValue("src"); _bufferMessage = new UpdateMessage(type, message); _bufferMessage.setUrl(url); _bufferMessage.setTorrent(torrent); _bufferMessage.setInstallerUrl(installerUrl); _bufferMessage.setOs(os); _bufferMessage.setShowOnce(showOnce); _bufferMessage.setVersion(version); if (atts.getValue("md5") != null) { _bufferMessage.setRemoteMD5(atts.getValue("md5")); LOG.debug("UpdateMessageReader.startElement overlay md5=" + atts.getValue("md5")); } // language properties available only inside overlay if (atts.getValue("language") != null) { _bufferMessage.setLanguage(atts.getValue("language")); // System.out.println("UpdateMessageReader.startElement overlay language=" // + atts.getValue("lang")); } if (atts.getValue("valueInstallerReady") != null) { _bufferMessage.setMessageInstallerReady(atts.getValue("valueInstallerReady")); // System.out.println("UpdateMessageReader.startElement overlay md5=" // + atts.getValue("md5")); } if (_bufferMessage.getMessageType().equalsIgnoreCase("announcement")) { _bufferMessage.setExpiration(atts.getValue("expiration")); } if (_bufferMessage.getMessageType().equalsIgnoreCase("chat_server")) { ChatSettings.CHAT_SERVER.setValue(message); ChatSettings.instance().save(); } // deal with overlay messages specific properties if (_bufferMessage.getMessageType().equalsIgnoreCase("overlay")) { // System.out.println("UpdateMessageReader.startElement overlay msg found"); _bufferMessage.setSrc(src); if (atts.getValue("intro") != null && (atts.getValue("intro").equals("1") || atts.getValue("intro").equalsIgnoreCase("true") || atts.getValue("intro").equalsIgnoreCase("yes"))) { _bufferMessage.setIntro(true); // System.out.println("UpdateMessageReader.startElement overlay intro=true"); } else { _bufferMessage.setIntro(false); // System.out.println("UpdateMessageReader.startElement overlay intro=false"); } } // overlays } } public void startPrefixMapping(String arg0, String arg1) throws SAXException { } } // UpdateMessageReader class