/* 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/>. * * FileResourceLoader.java * * Created on 20 January 2006, 10:24 * */ package no.sesat.search.site.config; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; import javax.xml.parsers.DocumentBuilder; import no.sesat.search.site.Site; import no.sesat.search.site.SiteContext; import org.apache.log4j.Logger; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; /** Loads resource through ClassLoader resources. * * @version $Id: FileResourceLoader.java 3361 2006-08-03 13:44:54Z mickw $ * */ public class FileResourceLoader extends AbstractResourceLoader { private static final Logger LOG = Logger.getLogger(FileResourceLoader.class); /** 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 FileResourceLoader(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 FileResourceLoader(siteCxt); builder.setEntityResolver(new LocalTestEntityResolver()); 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. * @param jar * @return a bytecode loader for resource. */ public static BytecodeLoader newBytecodeLoader(final SiteContext siteCxt, final String resource, final String jar) { final BytecodeLoader bcLoader = new FileResourceLoader(siteCxt); bcLoader.initBytecodeLoader(resource, jar); return bcLoader; } protected FileResourceLoader(final SiteContext cxt) { super(cxt); } @Override public boolean urlExists(final URL url) { return null != url && new File(url.getFile()).exists(); } protected final String getProjectName(final String siteName){ // XXX Very hacky and awful! desparately needs attention. One idea is to always use "generic.sesam" as skin name String projectName = siteName .replaceAll("(localhost|(alpha|nuclei|beta|electron|gamma).test.sesam)", "sesam") .replaceAll("generic.(sesam.no)", "generic.sesam"); if( projectName.indexOf(':') > 0 ){ projectName = projectName.substring(0, projectName.indexOf(':')); } if( !projectName.endsWith("/")){ projectName = projectName + '/'; } if( projectName.endsWith("sesam/") && !"generic.sesam/".equals(projectName) ){ projectName = projectName.substring(0, projectName.length() - 1) + ".no/"; }else if( !projectName.contains(".") ){ projectName = projectName.substring(0, projectName.length() - 1) + ".sesam.no/"; } return projectName.replace('/', File.separatorChar); } // Not a doubt in my mind someone could write this a shit load better. i'll buy a beer to them. @Override protected final URL getResource(final Site site) { LOG.debug("getResource(" + site + ')'); final String suffix = "target" + File.separatorChar + getResourceDirectory(); return getResource(suffix, getResource(), site, false); } protected final URL getResource(final String directory, final String resource, final Site site, final boolean forceUrl){ LOG.debug("getResource(" + directory + ", " + resource + ", " + site + ")"); final String project = getProjectName(site.getName()); int genericSesamLoop = 0; try{ if(resource.contains("jar!") || resource.endsWith(".class")){ final String classname = resource.substring(resource.indexOf("jar!") + 4); // this will actually fail. but the class is loaded eventually cause everything is in one classloader. return FileResourceLoader.class.getClassLoader().getResource(classname); } String basedir = System.getProperty("basedir") + File.separatorChar; LOG.debug("project " + project); while(true){ String basedirNormalised = new File(basedir).toURI().normalize().toString() .replaceFirst("file:", "") .replace('/', File.separatorChar); if(!basedirNormalised.endsWith(File.separator)){ basedirNormalised = basedirNormalised + File.separatorChar; } LOG.debug("basedirNormalised " + basedirNormalised); if((File.separatorChar + "war" + File.separatorChar).equals(basedirNormalised) || (File.separatorChar + "generic.sesam" + File.separatorChar + "war" + File.separatorChar).equals(basedirNormalised) || (File.separatorChar + "sesat-kernel" + File.separatorChar + "generic.sesam" + File.separatorChar + "war" + File.separatorChar).equals(basedirNormalised)){ LOG.warn("At root of filesystem! looking for " + directory + resource + " Current requirement of tests is that sesat-kernel is checked out, and named such," + " in any parent folder from here." + " I've searched all the way to the root of the filesystem"); return null; } if(basedirNormalised.endsWith(project) || basedirNormalised.endsWith(project + "war" + File.separatorChar)){ LOG.debug("looking in " + basedirNormalised + directory); final File f = new File(basedirNormalised + directory + resource); if(f.exists() || forceUrl){ return f.toURI().normalize().toURL(); } } if(("generic.sesam" + File.separatorChar).equals(project)){ ++genericSesamLoop; basedir = System.getProperty("basedir") + File.separatorChar; for(int i = 0; i < genericSesamLoop; ++i){ basedir = basedir + ".." + File.separatorChar; } if(new File(basedir + "generic.sesam").exists()){ // we are already inside sesat-kernel basedir = basedir + "generic.sesam" + File.separatorChar + "war" + File.separatorChar; }else{ basedir = basedir + "sesat-kernel" + File.separatorChar + "generic.sesam" + File.separatorChar + "war" + File.separatorChar; } }else{ basedir = basedir + (basedir.endsWith("war"+ File.separatorChar) ? ".." + File.separatorChar : "") + ".." + File.separatorChar + "war" + File.separatorChar; } } }catch (MalformedURLException ex) { throw new ResourceLoadException(ex.getMessage(), ex); } } private String getResourceDirectory() { return "classes" + File.separatorChar; } protected String getUrlFor(final String resource) { return resource; } @Override protected InputStream getInputStreamFor(URL url) { try { return new FileInputStream(new File(url.getFile())); } catch (IOException e) { throw new ResourceLoadException(readResourceDebug(url), e); } } private static class LocalTestEntityResolver implements EntityResolver { private static final Logger LOG = Logger.getLogger(LocalTestEntityResolver.class); private static final String INFO_LOADING_DTD = "Loading local DTD "; public InputSource resolveEntity(final String publicId, final String systemId) { // the latter is only for development purposes when dtds have't been published to production yet if (systemId.startsWith("http://sesam.no/dtds/") || systemId.startsWith("http://localhost")) { final String suffix = "war" + File.separatorChar + "src" + File.separatorChar + "main" + File.separatorChar + "webapp" + File.separatorChar + "dtds" + systemId.substring(systemId.lastIndexOf('/')); String basedir = System.getProperty("basedir") + File.separatorChar; int genericSesamLoop = 0; while(true){ final String basedirNormalised = new File(basedir).toURI().normalize().toString() .replaceFirst("file:", "") .replace('/', File.separatorChar); if("/".equals(basedirNormalised)){ throw new IllegalStateException("At root of filesystem! looking for " + suffix + " . Current requirement of tests is that sesat-kernel is checked out, and named such," + " in any parent folder from here." + "I've searched all the way to the root of the filesystem"); } LOG.debug("looking in " + basedirNormalised + suffix); final File f = new File(basedirNormalised + suffix); if(f.exists()){ try{ return new InputSource(new FileInputStream(f)); }catch (FileNotFoundException ex) { throw new ResourceLoadException(ex.getMessage(), ex); } } ++genericSesamLoop; basedir = System.getProperty("basedir") + File.separatorChar; for(int i = 0; i < genericSesamLoop; ++i){ basedir = basedir + ".." + File.separatorChar; } } } else { // use the default behaviour return null; } } } }