/** * Copyright (C) 2010 Orbeon, Inc. * * This program 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 * 2.1 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 Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.resources; import org.orbeon.dom.Document; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.xml.XMLParsing; import org.orbeon.oxf.xml.XMLReceiver; import org.w3c.dom.Node; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.lang.reflect.Constructor; import java.util.*; /** * The priority resource manager delegates to two or more resource managers the loading of documents. For example, if a * flat file resource manager doesn't contain a document, a second classloader resource manager might load it. * * This is an important feature that allows an application developer to bundle a resource, and still allow the user to * override it easily. */ public class PriorityResourceManagerImpl implements ResourceManager { private final List<ResourceManager> resourceManagers = new ArrayList<ResourceManager>(); public PriorityResourceManagerImpl(Map<String, String> props) { // Map an order number to a Map of local properties final Map<Integer, Map<String, String>> orderToPropertyNames = new TreeMap<Integer, Map<String, String>>(); for (final String key: props.keySet()) { if (key.startsWith(PriorityResourceManagerFactory.PRIORITY_PROPERTY)) { final String substring = key.substring(PriorityResourceManagerFactory.PRIORITY_PROPERTY.length()); final int dotIndex = substring.indexOf('.'); final int position; final String localPropertyName; if (dotIndex == -1) { position = new Integer(substring); localPropertyName = null; } else { position = new Integer(substring.substring(0, dotIndex)); localPropertyName = substring.substring(dotIndex + 1); } if (orderToPropertyNames.get(position) == null) orderToPropertyNames.put(position, new HashMap<String, String>()); final Map<String, String> localProperties = orderToPropertyNames.get(position); localProperties.put(localPropertyName, props.get(key)); } } // Create resource managers in order for (final Map.Entry<Integer, Map<String, String>> entry: orderToPropertyNames.entrySet()) { final int position = entry.getKey(); final Map<String, String> localProperties = entry.getValue(); try { // Create instance final Class<ResourceManagerFactory> clazz = (Class<ResourceManagerFactory>) Class.forName((String) props.get(PriorityResourceManagerFactory.PRIORITY_PROPERTY + position)); final Constructor<ResourceManagerFactory> constructor = clazz.getConstructor(Map.class); final Map<String, String> allProps = new HashMap<String, String>(props); allProps.putAll(localProperties); final ResourceManagerFactory factory = constructor.newInstance(allProps); final ResourceManager instance = factory.makeInstance(); resourceManagers.add(instance); } catch (Exception e) { throw new OXFException(e); } } } public Node getContentAsDOM(final String key) { return (Node) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.getContentAsDOM(key); } }); } public Document getContentAsDOM4J(final String key) { return (Document) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.getContentAsDOM4J(key); } }); } public Document getContentAsDOM4J(final String key, final XMLParsing.ParserConfiguration parserConfiguration, final boolean handleLexical) { return (Document) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.getContentAsDOM4J(key, parserConfiguration, handleLexical); } }); } public void getContentAsSAX(final String key, final XMLReceiver xmlReceiver) { delegate(new Operation() { public Object run(ResourceManager resourceManager) { resourceManager.getContentAsSAX(key, xmlReceiver); return null; } }); } public void getContentAsSAX(final String key, final XMLReceiver xmlReceiver, final XMLParsing.ParserConfiguration parserConfiguration, final boolean handleLexical) { delegate(new Operation() { public Object run(ResourceManager resourceManager) { resourceManager.getContentAsSAX(key, xmlReceiver, parserConfiguration, handleLexical); return null; } }); } public InputStream getContentAsStream(final String key) { return (InputStream) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.getContentAsStream(key); } }); } public long lastModified(final String key, boolean doNotThrowResourceNotFound) { for (ResourceManager resourceManager: resourceManagers) { long lastModified = resourceManager.lastModified(key, true); if (lastModified != -1) return lastModified; } throw new ResourceNotFoundException(key); } public int length(final String key) { return (Integer) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.length(key); } }); } public boolean canWrite(final String key) { final boolean[] result = new boolean[1]; delegate(new Operation() { public Object run(ResourceManager resourceManager) { // Logical "or" result[0] |= resourceManager.canWrite(key); return null; } }); return result[0]; } public OutputStream getOutputStream(final String key) { return (OutputStream) delegate(new Operation() { public Object run(ResourceManager resourceManager) { if (!resourceManager.canWrite(key)) throw new ResourceNotFoundException(key); return resourceManager.getOutputStream(key); } }); } public Writer getWriter(final String key) { return (Writer) delegate(new Operation() { public Object run(ResourceManager resourceManager) { if (!resourceManager.canWrite(key)) throw new ResourceNotFoundException(key); return resourceManager.getWriter(key); } }); } public String getRealPath(final String key) { return (String) delegate(new Operation() { public Object run(ResourceManager resourceManager) { return resourceManager.getRealPath(key); // Need an option for this as some callers call this for non-existing files // final String realPath = resourceManager.getRealPath(key); // // Here consider null as not found // // The semantic is a bit different from the underlying managers, which have: // // // // - null: unsupported // // - ResourceNotFoundException: supported by not found // if (realPath == null) // throw new ResourceNotFoundException("Cannot read from file " + key); // else // return realPath; } }); } public XMLReceiver getWriteContentHandler(final String key) { return (XMLReceiver) delegate(new Operation() { public Object run(ResourceManager resourceManager) { if (!resourceManager.canWrite(key)) throw new ResourceNotFoundException(key); return resourceManager.getWriteContentHandler(key); } }); } private static interface Operation { public Object run(ResourceManager resourceManager); } private Object delegate(Operation operation) { Exception firstException = null; for (ResourceManager resourceManager: resourceManagers) { try { return operation.run(resourceManager); } catch (Exception e) { if(!(e instanceof ResourceNotFoundException)) { // this is serious, not just a file not found // we should abort and throw the exception right away if(e instanceof OXFException) throw (OXFException)e; else throw new OXFException(e); } if (firstException == null) firstException = e; } } if (firstException instanceof ResourceNotFoundException) throw ((ResourceNotFoundException) firstException); else throw new OXFException(firstException); } public boolean exists(String key) { for (ResourceManager resourceManager: resourceManagers) { if (resourceManager.exists(key)) return true; } return false; } }