/* Copyright (2005-2012) Schibsted ASA * This file is part of Possom. * Possom is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Possom 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Possom. If not, see <http://www.gnu.org/licenses/>. * * UrlResourceLoader.java * * Created on 20 January 2006, 10:24 * */ package no.sesat.search.site.config; import com.opensymphony.oscache.base.NeedsRefreshException; import com.opensymphony.oscache.general.GeneralCacheAdministrator; import java.io.IOException; import java.io.InputStream; import java.net.SocketTimeoutException; import java.net.URL; import java.net.MalformedURLException; import java.util.Properties; import javax.xml.parsers.DocumentBuilder; import no.sesat.search.http.HTTPClient; import no.sesat.search.site.Site; import no.sesat.search.site.SiteContext; import org.apache.log4j.Logger; /** Loads resources through URL references. * * @version $Id$ * */ public class UrlResourceLoader extends AbstractResourceLoader { // Constants ----------------------------------------------------- private static final GeneralCacheAdministrator PRESENCE_CACHE = new GeneralCacheAdministrator(); private static final int REFRESH_PERIOD = 60; // one minute private static final Logger LOG = Logger.getLogger(UrlResourceLoader.class); private static final String DEBUG_CHECKING_EXISTANCE_OF = "Checking existance of "; private static final int CACHE_CAPACITY = 1000; /** An application using the URLResourceLoader can make use this. **/ public static final Site.Context SITE_CONTEXT = new Site.Context(){ @Override public String getParentSiteName(final SiteContext siteContext) { // we have to do this manually instead of using SiteConfiguration, // because SiteConfiguration relies on the parent site that we haven't get initialised. // That is, the PARENT_SITE_KEY property MUST be explicit in the site's configuration.properties. final Properties props = new Properties(); final PropertiesLoader loader = UrlResourceLoader.newPropertiesLoader(siteContext, Site.CONFIGURATION_FILE, props); loader.abut(); final String parentName = props.getProperty(Site.PARENT_SITE_KEY); if(null == parentName && 0 == props.size()){ throw new IllegalArgumentException("Invalid site " + siteContext.getSite()); } return parentName; } }; // Attributes ---------------------------------------------------- static{ PRESENCE_CACHE.setCacheCapacity(CACHE_CAPACITY); } // Static -------------------------------------------------------- /** Creates a SiteConfiguration.Context based off the given site and delegating all Resource handling to this. * The returned object also implements ResourceContext **/ public static SiteConfiguration.Context newSiteConfigurationContext(final Site site){ return new SiteConfiguration.Context(){ @Override public PropertiesLoader newPropertiesLoader( final SiteContext siteCxt, final String resource, final Properties properties) { return UrlResourceLoader.newPropertiesLoader(siteCxt, resource, properties); } public DocumentLoader newDocumentLoader( final SiteContext siteCxt, final String resource, final DocumentBuilder builder) { return UrlResourceLoader.newDocumentLoader(siteCxt, resource, builder); } public BytecodeLoader newBytecodeLoader(SiteContext context, String className, final String jar) { return UrlResourceLoader.newBytecodeLoader(context, className, jar); } @Override public Site getSite() { return site; } }; } /** Create a new PropertiesLoader for the given resource name/path and load it into the given properties. * @param siteCxt the SiteContext that will tell us which site we are dealing with. * @param resource the resource name/path. * @param properties the properties to hold the individual properties loaded. * @return the new PropertiesLoader to use. **/ public static PropertiesLoader newPropertiesLoader( final SiteContext siteCxt, final String resource, final Properties properties) { final PropertiesLoader pl = new UrlResourceLoader(siteCxt); pl.init(resource, properties); return pl; } /** Create a new DocumentLoader for the given resource name/path and load it with the given DocumentBuilder. * @param siteCxt the SiteContext that will tell us which site we are dealing with. * @param resource the resource name/path. * @param builder the DocumentBuilder to build the DOM resource with. * @return the new DocumentLoader to use. **/ public static DocumentLoader newDocumentLoader( final SiteContext siteCxt, final String resource, final DocumentBuilder builder) { final DocumentLoader dl = new UrlResourceLoader(siteCxt); builder.setEntityResolver(new LocalEntityResolver()); dl.init(resource, builder); return dl; } /** * Creates new BytecodeLoader for the given site and resource. * * @param siteCxt context telling us which site to use. * @param resource the class to load bytecode for. * @return a bytecode loader for resource. */ public static BytecodeLoader newBytecodeLoader( final SiteContext siteCxt, final String resource, final String jar) { final BytecodeLoader bcLoader = new UrlResourceLoader(siteCxt); bcLoader.initBytecodeLoader(resource, jar); return bcLoader; } /** Calls doesUrlExist(url, "localhost") **/ public static boolean doesUrlExist(final URL url){ return doesUrlExist(url, "localhost"); } /** Checks that the url (requested with the given host header) exists. * * @param url the url whom's existence is checked * @param hostHeader the host header to use on the request (typically localhost) * @return true if in existence **/ public static boolean doesUrlExist(final URL url, final String hostHeader){ boolean success = false; try{ success = (Boolean)PRESENCE_CACHE.getFromCache(url.toString(), REFRESH_PERIOD); }catch(NeedsRefreshException nre){ boolean updatedCache = false; try { success = (null != hostHeader ? HTTPClient.instance(url, hostHeader) : HTTPClient.instance(url) ) .exists(""); LOG.trace(DEBUG_CHECKING_EXISTANCE_OF + url + " is " + success); PRESENCE_CACHE.putInCache(url.toString(), success); updatedCache = true; } catch (NullPointerException e) { LOG.debug(url.toString(), e); } catch (SocketTimeoutException ste) { LOG.debug(url.toString() + '\n' + ste); } catch (IOException e) { LOG.warn(url.toString(), e); } finally { if(!updatedCache){ PRESENCE_CACHE.cancelUpdate(url.toString()); } } } return success; } // Constructors -------------------------------------------------- /** */ protected UrlResourceLoader(final SiteContext cxt) { super(cxt); } // Public -------------------------------------------------------- @Override public boolean urlExists(final URL url) { return doesUrlExist(url); } // Z implementation ---------------------------------------------- // Y overrides --------------------------------------------------- // Package protected --------------------------------------------- // Protected ----------------------------------------------------- @Override protected URL getResource(final Site site) { return getURL(getResource(), site); } private static String getResourceDirectory(final String resource) { if (resource.contains("jar!")) { return "lib/"; } else if (resource.endsWith(".class")) { return "classes/"; } else if (resource.endsWith(".jsp")) { return "jsp/"; } else { return "conf/"; } } public static URL getURL(final String resource, final Site site) { final String jarScheme = resource.contains("jar!") ? "jar:" : ""; try { return new URL(jarScheme + "http://" + site.getName() + site.getConfigContext() + getResourceDirectory(resource) + resource); } catch (MalformedURLException ex) { throw new ResourceLoadException("Read Configuration from " + resource, ex); } } @Override protected InputStream getInputStreamFor(final URL url) { HTTPClient client = null; try { client = HTTPClient.instance(url, "localhost"); return client.getBufferedStream(""); }catch (IOException ex) { throw new ResourceLoadException(readResourceDebug(url), client.interceptIOException(ex)); } } public static String getHostHeader(final String resource){ return resource.substring(7,resource.indexOf('/',8)); } @Override protected final String readResourceDebug(final URL url){ return "Read Configuration from " + url; } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- }