/** * Copyright 2011 meltmedia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xchain.framework.net; import java.net.URL; import java.net.URLConnection; import java.net.JarURLConnection; import java.util.jar.JarEntry; import java.util.Collections; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.Map; import java.io.IOException; import org.xchain.framework.net.protocol.resource.ResourceNotFoundException; import org.xchain.framework.net.strategy.JarUrlExistsStrategy; import org.xchain.framework.net.strategy.FileUrlExistsStrategy; import org.xchain.framework.net.strategy.ResourceUrlExistsStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A utility class for Url related methods. * * @author Christian Trimble * @author Mike Moulton * @author Jason Rose * @author Devon Tackett * @author Josh Kennedy */ public class UrlUtil { /** The log for the url util. */ public static Logger log = LoggerFactory.getLogger( UrlUtil.class ); public static UrlUtil instance = new UrlUtil(); public static UrlUtil getInstance() { return instance; } public Map<String, UrlExistsStrategy> existsStrategyMap = Collections.synchronizedMap(new HashMap<String, UrlExistsStrategy>()); /** * Register the given UrlExistsStrategy. UrlExistsStrategies must be registered before a URL can be checked on that protocol. * * @param strategy The strategy to register. */ private void registerExistsStrategy(UrlExistsStrategy strategy ) { existsStrategyMap.put( strategy.getProtocol(), strategy ); } static { // Register UrlExistsStrategies. UrlUtil.getInstance().registerExistsStrategy(new FileUrlExistsStrategy()); UrlUtil.getInstance().registerExistsStrategy(new JarUrlExistsStrategy()); UrlUtil.getInstance().registerExistsStrategy(new ResourceUrlExistsStrategy()); } /** * Check if the given systemId references something that exists. * * @param systemId The systemId to check. */ public boolean exists( String systemId ) throws Exception { return exists(UrlFactory.getInstance().newUrl(systemId)); } /** * Check if the given URL exists. This uses UrlExistsStrategy implementations to determine if the given URL exists. * * @param url The URL to check. * * @return True if the given URL exists. */ public boolean exists( URL url ) throws Exception { // get the protocol. String protocol = url.getProtocol(); // get the strategy for this protocol. UrlExistsStrategy strategy = (UrlExistsStrategy)existsStrategyMap.get(protocol); if( log.isDebugEnabled() ) { log.debug("Checking if '"+url.toExternalForm()+"' is exists ("+ protocol+")."); } if( strategy != null ) { return strategy.exists(url); } else { URLConnection connection = null; try { connection = url.openConnection(); connection.connect(); String statusCode = connection.getHeaderField("Status-Code"); if( statusCode == null ) { if( log.isDebugEnabled() ) { log.debug("The status code for '"+url.toExternalForm()+"' is null."); } return false; } else { return statusCode.matches("2\\d\\d"); } } catch (ResourceNotFoundException rnfe) { return false; } finally { closeConnection(connection); } } } /** * @return The last modified date for the url. */ public long lastModified( URL url ) throws IOException { URLConnection connection = null; try { connection = url.openConnection(); return lastModified(connection); } finally { closeConnection(connection); } } /** * Returns the last modified date for this url connection. */ public long lastModified( URLConnection connection ) { long lastModified = 0; // if this url connection is a jar url, then we need to try and get the date from the entry. try { if( connection instanceof JarURLConnection ) { try { JarURLConnection jarConnection = (JarURLConnection)connection; JarEntry jarEntry = jarConnection.getJarEntry(); if( jarEntry != null ) { lastModified = jarEntry.getTime(); } } catch( Exception e ) { if( log.isWarnEnabled() ) { log.warn("Could not get date from jar entry.", e); } } } // if the last modified date was not set, then return the standard last modified date. if( lastModified <= 0 ) { lastModified = connection.getLastModified(); } } finally { closeConnection(connection); } return lastModified; } /** * Determine if a given url was last modified before another given url. * * @param url The url to check if it was last modified before. * @param otherUrl The url to check against. * * @return True if the url was last modified before the otherUrl. */ public boolean lastModifiedBefore( URL url, URL otherUrl ) throws IOException { URLConnection connection = null; URLConnection otherConnection = null; try { connection = url.openConnection(); long lastModified = lastModified(connection); if( log.isDebugEnabled() ) { log.debug("Url '"+url+"' has last modified date "+lastModified+"."); } otherConnection = otherUrl.openConnection(); long otherLastModified = lastModified(otherConnection); if( log.isDebugEnabled() ) { log.debug("Url '"+otherUrl+"' has last modified date "+otherLastModified+"."); } return lastModified < otherLastModified; } finally { closeConnection(connection); closeConnection(otherConnection); } /* UrlModifiedDateStrategy urlModifiedDateStrategy = (UrlModifiedDateStrategy)modifiedDateStrategyMap.get(url.getProtocol()); UrlModifiedDateStrategy otherUrlModifiedDateStrategy = (UrlModifiedDateStrategy)modifiedDateStrategyMap.get(otherUrl.getProtocol()); // either is null, then set the default. if( urlModifiedDateStrategy == null ) { urlModifiedDateStrategy = defaultUrlModifiedDateStrategy; } if( otherUrlModifiedDateStrategy == null ) { otherUrlModifiedDateStrategy = defaultUrlModifiedDateStrategy; } return urlModifiedDateStrategy.getLongDate( url ) > */ } /** * Determine if the given url was last modified before the given set of urls. * * @param url The url to check with. * @param urlSet The set of urls to check against. * * @return True if the given url was last modified before any of the urls in the given set. */ public boolean lastModifiedBefore( URL url, Set<URL> urlSet ) throws IOException { boolean result = false; URLConnection connection = null; URLConnection otherConnection = null; try { // Open the connection. connection = url.openConnection(); // Get the last modified date. long lastModified = lastModified(connection); if( log.isDebugEnabled() ) { log.debug("Url '"+url+"' has last modified date "+lastModified+"."); } // Iterate over the given set of URLs. Iterator<URL> urlIterator = urlSet.iterator(); while( urlIterator.hasNext() && !result ) { URL otherUrl = urlIterator.next(); // Open the other connection. otherConnection = otherUrl.openConnection(); // Get the last modified date. long otherLastModified = lastModified(otherConnection); if( log.isDebugEnabled() ) { log.debug("Url '"+otherUrl+"' has last modified date "+otherLastModified+"."); } // Close the other connection. closeConnection(otherConnection); result = lastModified < otherLastModified; } } finally { // Make sure both connections are closed. closeConnection(connection); closeConnection(otherConnection); } return result; } /** * Check if the given URL has been modified after the given date. * * @param url The URL to check. * @param lastModified The last modified date to check on. * * @return True if the given URL has been modified after the given date. */ public boolean lastModifiedAfter( URL url, long lastModified ) throws IOException { // get the load time for the url. long urlLastModified = lastModified(url); // if the url last modified is after the load date, then a reload is required. return lastModified < urlLastModified; } /** * Check if the given collection of URLs has been modified after the given date. * * @param urlCollection The collection of URLs to check. * @param lastModified The last modified date to check on. * * @return True if any of the given URLs has been modified after the given date. */ public boolean lastModifiedAfter( Collection<URL> urlCollection, long lastModified ) throws IOException { boolean result = false; // if the load date is before any of the urls in the dependency set, then we need to reload. Iterator<URL> urlIterator = urlCollection.iterator(); while( urlIterator.hasNext() && !result ) { result = lastModifiedAfter(urlIterator.next(), lastModified); } return result; } /** * Unwrap any level of wrapped UrlConnectionWrappers. * * @param connection The URLConnection to unwrap. * * @return A URLConnection that is not wrapped by a UrlConnectionWrapper. */ public URLConnection unwrapConnection( URLConnection connection ) { while( connection instanceof UrlConnectionWrapper ) { connection = ((UrlConnectionWrapper)connection).getWrapped(); } return connection; } /** * Close the input and output streams for the given URLConnection. * * @param connection The connection to close. */ public void closeConnection( URLConnection connection ) { if( connection != null ) { closeInputStream(connection); closeOutputStream(connection); } } /** * Closes the input stream for a url connection. */ public static void closeInputStream( URLConnection urlConnection ) { if( urlConnection != null && urlConnection.getDoInput() ) { try { urlConnection.getInputStream().close(); } catch( Exception ioe ) { // do nothing. if( log.isDebugEnabled() ) { log.debug("Exception thrown while closing connection.", ioe); } } } } /** * Closes the output stream for a url connection. */ public static void closeOutputStream( URLConnection urlConnection ) { if( urlConnection != null && urlConnection.getDoOutput() ) { try { urlConnection.getInputStream().close(); } catch( Exception ioe ) { // do nothing. if( log.isDebugEnabled() ) { log.debug("Exception thrown while closing connection.", ioe); } } } } }