/* Copyright (2007-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/>. */ package no.sesat.search.view.velocity; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.io.InputStream; import java.util.regex.Matcher; import no.sesat.search.http.HTTPClient; import no.sesat.search.site.config.UrlResourceLoader; import no.sesat.search.site.Site; import org.apache.log4j.Logger; import org.apache.velocity.runtime.resource.Resource; import org.apache.velocity.runtime.resource.loader.ResourceLoader; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.commons.collections.ExtendedProperties; /** * This is a simple URL-based loader. * ORIGINAL FROM http://svn.apache.org/repos/asf/jakarta/velocity/engine/trunk/whiteboard/geir/URLResourceLoader.java * * original version Id: URLResourceLoader.java,v 1.3 2004/03/19 17:13:40 dlr Exp * * * * MODIFIED TO SUIT SCHIBSTEDSØK's NEEDS. * There was a choice here to implement all the URL handling stuff from scratch or to plug into the existing * functionality found in no.schibstedsøk.front.search.configuration.loader * Since this class is hidden between the velocity API it made more sense to go from scratch to best * meet velocity's requirements... * * * @version $Id: URLResourceLoader.java,v 1.3 2004/03/19 17:13:40 dlr Exp $ */ public class URLResourceLoader extends ResourceLoader { public interface Context{ boolean doesUrlExist(final URL url); URL getURL(final String resource, final Site site); } // Constants ----------------------------------------------------- private static final Logger LOG = Logger.getLogger(URLResourceLoader.class); private static final String ERR_RESOURCE_NOT_FOUND = "Cannot find resource "; private static final String DEBUG_LOOKING_FOR = "Looking for "; private static final String DEBUG_EXISTS = "Positive HEAD on "; private static final String DEBUG_FULL_URL_IS = "Real URL is "; private static final String DEBUG_HOST_HEADER_IS = "URL's host-header is "; private static final String DEBUG_DOESNT_EXIST = "Using fallback URL"; // Attributes ---------------------------------------------------- private Site site; // Static -------------------------------------------------------- private static Context context = new DefaultContext(); // Allows the tests to switch the Velocity ResourceLoader over to a file based one. static void setContext(final Context context){ URLResourceLoader.context = context; } // Constructors -------------------------------------------------- public URLResourceLoader(){} public URLResourceLoader(final Site site){ this.site= site; } // Public -------------------------------------------------------- /** {@inheritDoc} */ public void init(final ExtendedProperties configuration) { // the engine's properties actually come from the RuntimeServices *not* the ExtendedProperties site = (Site)rsvc.getProperty(Site.NAME_KEY); } /** * Get an InputStream so that the Runtime can build a * template with it. * * @param url url of template to fetch bytestream of * @return InputStream containing the template * @throws ResourceNotFoundException if template not found * in the file template path. */ public /*synchronized*/ InputStream getResourceStream(final String url) throws ResourceNotFoundException{ LOG.trace("start getResourceStream( " + url + " )"); try{ synchronized( url.intern() ){ return getStream(findUrl(url, site)); } }catch( IOException e ){ throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url ); } } /** {@inheritDoc} */ public boolean isSourceModified(Resource resource){ final boolean result = getLastModified(resource) > resource.getLastModified(); LOG.debug("isSourceModified( "+resource.getName()+" ): "+result); return result; } /** {@inheritDoc} */ public long getLastModified(Resource resource){ try{ final URL url = findUrl(resource.getName(), site); final HTTPClient client = HTTPClient.instance(url, "localhost"); return client.getLastModified(""); }catch( ResourceNotFoundException e ){ LOG.error( ERR_RESOURCE_NOT_FOUND + resource.getName() ); }catch( IOException e ){ LOG.error( ERR_RESOURCE_NOT_FOUND + resource.getName() ); } return 0; } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- private static URL findUrl(final String url, final Site currentSite) throws ResourceNotFoundException{ try{ LOG.trace(DEBUG_LOOKING_FOR + url ); return findUrlImpl(url, currentSite); }catch( IOException e ){ throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url ); } } private static URL findUrlImpl(final String url, final Site currentSite) throws IOException, ResourceNotFoundException { final URL u = context.getURL(url, currentSite); if (context.doesUrlExist(u)) { return u; } else { final Site parent = currentSite.getParent(); if (null == parent) { throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url ); } if (LOG.isTraceEnabled()) { LOG.trace(DEBUG_DOESNT_EXIST + parent.getName()); } // Recursively look for the resource in ancestor sites. return findUrlImpl(getFallbackURL(url, currentSite, parent), parent); } } private static String getFallbackURL(final String url, final Site currSite, final Site ancestorSite) { final String oldUrl = currSite.getName() + currSite.getConfigContext(); final String newUrl = ancestorSite.getName() + ancestorSite.getConfigContext(); return url.replaceFirst(Matcher.quoteReplacement(oldUrl), newUrl); } private static InputStream getStream(final URL url) throws IOException{ final HTTPClient client = HTTPClient.instance(url, "localhost"); try{ return client.getBufferedStream(""); }catch(IOException ioe){ throw client.interceptIOException(ioe); } } // Inner classes ------------------------------------------------- private static final class DefaultContext implements Context{ public boolean doesUrlExist(final URL url) { return UrlResourceLoader.doesUrlExist(url); } public URL getURL(final String resource, final Site site) { try { return new URL(resource); } catch (MalformedURLException e) { throw new ResourceNotFoundException(e); } } } }