0001: /*
0002:
0003: This software is OSI Certified Open Source Software.
0004: OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006: The license (Mozilla version 1.0) can be read at the MMBase site.
0007: See http://www.MMBase.org/license
0008:
0009: */
0010: package org.mmbase.util;
0011:
0012: // general
0013: import java.io.*;
0014: import java.util.*;
0015: import java.util.regex.Pattern;
0016: import java.net.*;
0017:
0018: // used for resolving in servlet-environment
0019: import javax.servlet.ServletContext;
0020: import javax.servlet.http.HttpServletRequest;
0021:
0022: // used for resolving in MMBase database
0023: import org.mmbase.bridge.*;
0024: import org.mmbase.bridge.util.Queries;
0025: import org.mmbase.storage.search.implementation.*;
0026: import org.mmbase.storage.search.*;
0027:
0028: // XML stuff
0029: import org.w3c.dom.Document;
0030: import org.w3c.dom.DocumentType;
0031: import org.xml.sax.InputSource;
0032: import javax.xml.transform.*;
0033: import javax.xml.transform.Transformer;
0034: import javax.xml.parsers.DocumentBuilder;
0035: import javax.xml.transform.stream.StreamResult;
0036: import javax.xml.transform.dom.DOMSource;
0037:
0038: // used for Unicode Escaping when editing property files
0039: import org.mmbase.util.transformers.*;
0040:
0041: import org.mmbase.util.logging.Logger;
0042: import org.mmbase.util.logging.Logging;
0043:
0044: /**
0045: * MMBase resource loader, for loading config-files and those kind of things. It knows about MMBase config file locations.
0046: *
0047: * I read <a href="http://www.javaworld.com/javaqa/2003-08/02-qa-0822-urls.html">http://www.javaworld.com/javaqa/2003-08/02-qa-0822-urls.html</a>.
0048: *
0049: * Programmers should do something like this if they need a configuration file:
0050: <pre>
0051: InputStream configStream = ResourceLoader.getConfigurationRoot().getResourceAsStream("modules/myconfiguration.xml");
0052: </pre>
0053: or
0054: <pre>
0055: InputSource config = ResourceLoader.getConfigurationRoot().getInputSource("modules/myconfiguration.xml");
0056: </pre>
0057: of if you need a list of all resources:
0058: <pre>
0059: ResourceLoader builderLoader = new ResourceLoader("builders");
0060: List list = builderLoader.getResourcePaths(ResourceLoader.XML_PATTERN, true)
0061: </pre>
0062:
0063: When you want to place a configuration file then you have several options, wich are in order of preference:
0064: <ol>
0065: <li>Place it as on object in 'resources' builder (if such a builder is present)</li>
0066: <li>Place it in the directory identified by the 'mmbase.config' setting (A system property or web.xml setting).</li>
0067: <li>Place it in the directory WEB-INF/config. If this is a real directory (you are not in a war), then the resource will also be returned by {@link #getFiles}.</li>
0068: <li>
0069: Place it in the class-loader path of your app-server, below the 'org.mmbase.config' package.
0070: For tomcat this boils down to the following list (Taken from <a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/class-loader-howto.html">tomcat 5 class-loader howto</a>)
0071: <ol>
0072: <li>Bootstrap classes of your JVM</li>
0073: <li>System class loader classses</li>
0074: <li>/WEB-INF/classes of your web application. If this is a real directory (you are not in a war), then the resource will also be returned by {@link #getFiles}.</li>
0075: <li>/WEB-INF/lib/*.jar of your web application</li>
0076: <li>$CATALINA_HOME/common/classes</li>
0077: <li>$CATALINA_HOME/common/endorsed/*.jar</li>
0078: <li>$CATALINA_HOME/common/lib/*.jar</li>
0079: <li>$CATALINA_BASE/shared/classes</li>
0080: <li>$CATALINA_BASE/shared/lib/*.jar</li>
0081: </ol>
0082: </li>
0083: </ol>
0084: * <p>
0085: * Resources which do not reside in the MMBase configuration repository, can also be handled. Those can be resolved relatively to the web root, using {@link #getWebRoot()}.
0086: * </p>
0087: *
0088: * <p>Resources can programmaticly created or changed by the use of {@link #createResourceAsStream}, or something like {@link #getWriter}.</p>
0089: *
0090: * <p>If you want to check beforehand if a resource can be changed, then something like <code>resourceLoader.getResource().openConnection().getDoOutput()</code> can be used.</p>
0091: * <p>That is also valid if you want to check for existance. <code>resourceLoader.getResource().openConnection().getDoInput()</code>.</p>
0092: * <p>If you want to remove a resource, you must write <code>null</code> to all URL's returned by {@link #findResources} (Do for every URL:<code>url.openConnection().getOutputStream().write(null);</code>)</p>
0093: * <h3>Encodings</h3>
0094: * <p>ResourceLoader is well aware of encodings. You can open XML's as Reader, and this will be done using the encoding specified in the XML itself. When saving an XML using a Writer, this will also be done using the encoding specified in the XML.</p>
0095: * <p>For property-files, the java-unicode-escaping is undone on loading, and applied on saving, so there is no need to think of that.</p>
0096: * @author Michiel Meeuwissen
0097: * @since MMBase-1.8
0098: * @version $Id: ResourceLoader.java,v 1.52 2007/12/17 11:36:05 michiel Exp $
0099: */
0100: public class ResourceLoader extends ClassLoader {
0101:
0102: private static Logger log = Logging
0103: .getLoggerInstance(ResourceLoader.class);
0104:
0105: public static void initLogging() {
0106: log = Logging.getLoggerInstance(ResourceLoader.class);
0107: }
0108:
0109: /**
0110: * Protocol prefix used by URL objects in this class.
0111: */
0112: protected static final String PROTOCOL = "mm";
0113:
0114: /**
0115: * Used for files, and servlet resources.
0116: */
0117: protected static final String RESOURCE_ROOT = "/WEB-INF/config";
0118:
0119: /**
0120: * Used when getting resources with normal class-loader.
0121: */
0122: protected static final String CLASSLOADER_ROOT = "/org/mmbase/config";
0123:
0124: /**
0125: * Protocol prefix used by URL objects in this class.
0126: */
0127: public static final URL NODE_URL_CONTEXT;
0128:
0129: static {
0130: URL temp = null;
0131: try {
0132: temp = new URL("http", "localhost", "/node/");
0133: } catch (MalformedURLException mfue) {
0134: assert false : mfue;
0135: }
0136: NODE_URL_CONTEXT = temp;
0137: }
0138:
0139: /**
0140: * Used when using getResourcePaths for normal class-loaders.
0141: */
0142: protected static final String INDEX = "INDEX";
0143:
0144: private static ResourceLoader configRoot = null;
0145: private static ResourceLoader webRoot = null;
0146: private static ResourceLoader systemRoot = null;
0147: private static ServletContext servletContext = null;
0148:
0149: // these should perhaps be configurable:
0150: public static final String RESOURCENAME_FIELD = "name";
0151: public static final String TYPE_FIELD = "type";
0152: public static final String FILENAME_FIELD = "filename";
0153: public static final String HANDLE_FIELD = "handle";
0154: public static final String LASTMODIFIED_FIELD = "lastmodified";
0155: public static final String DEFAULT_CONTEXT = "admin";
0156:
0157: public static final int TYPE_CONFIG = 0;
0158: public static final int TYPE_WEB = 1;
0159:
0160: // This should perhaps be a member (too) to allow for better authorisation support.
0161: static NodeManager resourceBuilder = null;
0162:
0163: /**
0164: * The URLStreamHandler for 'mm' URL's.
0165: */
0166:
0167: private final MMURLStreamHandler mmStreamHandler = new MMURLStreamHandler();
0168:
0169: /**
0170: * Creates a new URL object, which is used to load resources. First a normal java.net.URL is
0171: * instantiated, if that fails, we check for the 'mmbase' protocol. If so, a URL is instantiated
0172: * with a URLStreamHandler which can handle that.
0173: *
0174: * If that too fails, it should actually already be a MalformedURLException, but we try
0175: * supposing it is some existing file and return a file: URL. If no such file, only then a
0176: * MalformedURLException is thrown.
0177: */
0178: protected URL newURL(final String url) throws MalformedURLException {
0179: // Try already installed protocols first:
0180: try {
0181: return new URL(url);
0182: } catch (MalformedURLException ignore) {
0183: // Ignore: try our own handler next.
0184: }
0185:
0186: final int firstColon = url.indexOf(':');
0187: if (firstColon <= 0) {
0188: if (new File(url).exists())
0189: return new URL("file:" + url); // try it as a simply file
0190: throw new MalformedURLException("No protocol specified: "
0191: + url);
0192: } else {
0193:
0194: final String protocol = url.substring(0, firstColon);
0195: if (protocol.equals(PROTOCOL)) {
0196: return new URL(null/* no context */, url,
0197: mmStreamHandler);
0198: } else {
0199: if (new File(url).exists())
0200: return new URL("file:" + url);
0201: throw new MalformedURLException("Unknown protocol: "
0202: + protocol);
0203: }
0204: }
0205: }
0206:
0207: static {
0208: // make sure it works a bit before servlet-startup.
0209: init(null);
0210: }
0211:
0212: /**
0213: * Initializes the Resourceloader using a servlet-context (makes resolving relatively to WEB-INF/config possible).
0214: * @param sc The ServletContext used for determining the mmbase configuration directory. Or <code>null</code>.
0215: */
0216: public static synchronized void init(ServletContext sc) {
0217: servletContext = sc;
0218: // reset both roots, they will be redetermined using servletContext.
0219: configRoot = null;
0220: webRoot = null;
0221: }
0222:
0223: /**
0224: * Sets the MMBase builder which must be used for resource.
0225: * The builder must have an URL and a HANDLE field.
0226: * This method can be called only once.
0227: * @param b An MMObjectBuilder (this may be <code>null</code> if no such builder available)
0228: * @throws RuntimeException if builder was set already.
0229: */
0230: public static void setResourceBuilder(NodeManager b) {
0231: if (ResourceWatcher.resourceWatchers == null) {
0232: throw new RuntimeException(
0233: "A resource builder was set already: "
0234: + resourceBuilder);
0235: }
0236: resourceBuilder = b;
0237: // must be informed to existing ResourceWatchers.
0238: ResourceWatcher.setResourceBuilder(); // this will also set ResourceWatcher.resourceWatchers to null.
0239: log.info("The resources builder '" + b.getName()
0240: + "' is available. (user: " + b.getCloud().getUser()
0241: + ")");
0242: }
0243:
0244: /**
0245: * Utility method to return the name part of a resource-name (removed directory and 'extension').
0246: * Used e.g. when loading builders in MMBase.
0247: */
0248: public static String getName(String path) {
0249: //avoid NullPointerException in util method
0250: if (path == null) {
0251: return null;
0252: }
0253: int i = path.lastIndexOf('/');
0254: path = path.substring(i + 1);
0255:
0256: i = path.lastIndexOf('.');
0257: if (i > 0) {
0258: path = path.substring(0, i);
0259: }
0260: return path;
0261: }
0262:
0263: /**
0264: * Utility method to return the 'directory' part of a resource-name.
0265: * Used e.g. when loading builders in MMBase.
0266: */
0267: public static String getDirectory(String path) {
0268: //avoid NullPointerException in util method
0269: if (path == null) {
0270: return null;
0271: }
0272: int i = path.lastIndexOf('/');
0273: if (i > 0) {
0274: path = path.substring(0, i);
0275: } else {
0276: path = "";
0277: }
0278: return path;
0279: }
0280:
0281: /**
0282: * Utility method to return the name of the directory of a resource-name. This does not include
0283: * any /-chars any more.
0284: * @since MMBase-1.8.2
0285: */
0286: public static String getDirectoryName(String path) {
0287: if (path == null) {
0288: return null;
0289: }
0290: if (path.length() > 0 && path.charAt(path.length() - 1) == '/')
0291: path = path.substring(0, path.length() - 1);
0292:
0293: int i = path.lastIndexOf('/');
0294: path = path.substring(i + 1);
0295: if (path.length() > 0 && path.charAt(0) == '/')
0296: path = path.substring(1);
0297: return path;
0298: }
0299:
0300: /**
0301: * Singleton that returns the ResourceLoader for loading mmbase configuration
0302: */
0303: public static synchronized ResourceLoader getConfigurationRoot() {
0304: if (configRoot == null) {
0305:
0306: configRoot = new ResourceLoader();
0307:
0308: //adds a resource that can load from nodes
0309: configRoot.roots.add(configRoot.new NodeURLStreamHandler(
0310: TYPE_CONFIG));
0311:
0312: // mmbase.config settings
0313: String configPath = null;
0314: if (servletContext != null) {
0315: configPath = servletContext
0316: .getInitParameter("mmbase.config");
0317: log
0318: .debug("found the mmbase config path parameter using the mmbase.config servlet context parameter");
0319: }
0320: if (configPath == null) {
0321: configPath = System.getProperty("mmbase.config");
0322: if (configPath != null) {
0323: log
0324: .debug("found the mmbase.config path parameter using the mmbase.config system property");
0325: }
0326: } else if (System.getProperty("mmbase.config") != null) {
0327: //if the configPath at this point was not null and the mmbase.config system property is defined
0328: //this deserves a warning message since the setting is masked
0329: log
0330: .warn("mmbase.config system property is masked by mmbase.config servlet context parameter");
0331: }
0332:
0333: if (configPath != null) {
0334: if (servletContext != null) {
0335: // take into account that configpath can start at webrootdir
0336: if (configPath.startsWith("$WEBROOT")) {
0337: configPath = servletContext
0338: .getRealPath(configPath.substring(8));
0339: }
0340: }
0341: log.debug("Adding " + configPath);
0342: configRoot.roots
0343: .add(configRoot.new FileURLStreamHandler(
0344: new File(configPath), true));
0345: }
0346:
0347: if (servletContext != null) {
0348: String s = servletContext.getRealPath(RESOURCE_ROOT);
0349: if (s != null) {
0350: PathURLStreamHandler h = configRoot.new FileURLStreamHandler(
0351: new File(s), true);
0352: if (!configRoot.roots.contains(h)) {
0353: configRoot.roots.add(h);
0354: }
0355: } else {
0356: configRoot.roots
0357: .add(configRoot.new ServletResourceURLStreamHandler(
0358: RESOURCE_ROOT));
0359: }
0360: }
0361:
0362: if (servletContext != null) {
0363: String s = servletContext
0364: .getRealPath("/WEB-INF/classes"
0365: + CLASSLOADER_ROOT); // prefer opening as a files.
0366: if (s != null) {
0367: configRoot.roots
0368: .add(configRoot.new FileURLStreamHandler(
0369: new File(s), false));
0370: }
0371: }
0372:
0373: configRoot.roots
0374: .add(configRoot.new ClassLoaderURLStreamHandler(
0375: CLASSLOADER_ROOT));
0376:
0377: //last fall back: fully qualified class-name
0378: configRoot.roots
0379: .add(configRoot.new ClassLoaderURLStreamHandler("/"));
0380:
0381: }
0382: return configRoot;
0383: }
0384:
0385: /**
0386: * Singleton that returns the ResourceLoader for loading from the file-system, relative from
0387: * current working directory and falling back to the file system roots. This can be used in
0388: * command-line tools and such. In a servlet environment you should use either
0389: * {@link #getConfigurationRoot} or {@link #getWebRoot}.
0390: */
0391: public static synchronized ResourceLoader getSystemRoot() {
0392: if (systemRoot == null) {
0393: systemRoot = new ResourceLoader();
0394: systemRoot.roots.add(systemRoot.new FileURLStreamHandler(
0395: new File(System.getProperty("user.dir")), true));
0396: File[] roots = File.listRoots();
0397: for (File element : roots) {
0398: systemRoot.roots
0399: .add(systemRoot.new FileURLStreamHandler(
0400: element, true));
0401: }
0402:
0403: }
0404: return systemRoot;
0405: }
0406:
0407: /**
0408: * Singleton that returns the ResourceLoader for witch the base path is the web root
0409: */
0410: public static synchronized ResourceLoader getWebRoot() {
0411: if (webRoot == null) {
0412: webRoot = new ResourceLoader();
0413:
0414: //webRoot.roots.add(webRoot.new NodeURLStreamHandler(Resource.TYPE_WEB));
0415:
0416: String htmlRoot = null;
0417: if (servletContext != null) {
0418: htmlRoot = servletContext
0419: .getInitParameter("mmbase.htmlroot");
0420: }
0421:
0422: if (htmlRoot == null) {
0423: htmlRoot = System.getProperty("mmbase.htmlroot");
0424: }
0425: if (htmlRoot != null) {
0426: webRoot.roots.add(webRoot.new FileURLStreamHandler(
0427: new File(htmlRoot), true));
0428: }
0429:
0430: if (servletContext != null) {
0431: String s = servletContext.getRealPath("/");
0432: if (s != null) {
0433: webRoot.roots.add(webRoot.new FileURLStreamHandler(
0434: new File(s), true));
0435: }
0436: webRoot.roots
0437: .add(webRoot.new ServletResourceURLStreamHandler(
0438: "/"));
0439: }
0440: }
0441:
0442: return webRoot;
0443: }
0444:
0445: /**
0446: * Returns the resource loader associated with the directory of the given request
0447: */
0448: public static synchronized ResourceLoader getWebDirectory(
0449: HttpServletRequest request) {
0450: return ResourceLoader.getWebRoot().getChildResourceLoader(
0451: request.getServletPath()).getParentResourceLoader();
0452: }
0453:
0454: /**
0455: * The URL relative to which this class-loader resolves. Cannot be <code>null</code>.
0456: */
0457: private final URL context;
0458:
0459: /**
0460: * Child resourceloaders have a parent.
0461: */
0462: private final ResourceLoader parent;
0463:
0464: private final List<PathURLStreamHandler> roots;
0465:
0466: /**
0467: * This constructor instantiates a new root resource-loader. This constructor is protected (so you may use it in an extension), but normally use:
0468: * {@link #getConfigurationRoot} or {@link #getWebRoot}.
0469: */
0470: protected ResourceLoader() {
0471: super ();
0472: roots = new ArrayList<PathURLStreamHandler>();
0473: parent = null;
0474: try {
0475: context = newURL(PROTOCOL + ":/");
0476: } catch (MalformedURLException mue) {
0477: throw new RuntimeException(mue);
0478: }
0479: }
0480:
0481: /**
0482: * Instantiates a ResourceLoader for a 'sub directory' of given ResourceLoader. Used by {@link #getChildResourceLoader}.
0483: */
0484: protected ResourceLoader(final ResourceLoader cl,
0485: final String context) {
0486: super (ResourceLoader.class.getClassLoader());
0487: this .context = cl.findResource(context + "/");
0488: roots = new ArrayList<PathURLStreamHandler>();
0489: for (PathURLStreamHandler o : cl.roots) {
0490: // hmm, don't like this code, but don't know how else to copy the inner object.
0491: if (o instanceof FileURLStreamHandler) {
0492: roots.add(new FileURLStreamHandler(
0493: (FileURLStreamHandler) o));
0494: } else if (o instanceof NodeURLStreamHandler) {
0495: roots.add(new NodeURLStreamHandler(
0496: (NodeURLStreamHandler) o));
0497: } else if (o instanceof ServletResourceURLStreamHandler) {
0498: roots.add(new ServletResourceURLStreamHandler(
0499: (ServletResourceURLStreamHandler) o));
0500: } else if (o instanceof ClassLoaderURLStreamHandler) {
0501: roots.add(new ClassLoaderURLStreamHandler(
0502: (ClassLoaderURLStreamHandler) o));
0503: } else {
0504: assert false;
0505: }
0506: }
0507: parent = cl;
0508: }
0509:
0510: /**
0511: * If name starts with '/' or 'mm:/' the 'parent' resourceloader is used.
0512: *
0513: * Otherwise the name is resolved relatively. (For the root ResourceLoader that is the same as starting with /)
0514: *
0515: * {@inheritDoc}
0516: */
0517: protected URL findResource(final String name) {
0518: try {
0519: if (name.startsWith("/")) {
0520: return newURL(PROTOCOL + ":" + name);
0521: } else if (name.startsWith(PROTOCOL + ":")) {
0522: return newURL(name);
0523: } else {
0524: return new URL(context, name);
0525: }
0526: } catch (MalformedURLException mfue) {
0527: log.info(mfue + Logging.stackTrace(mfue));
0528: return null;
0529: }
0530: }
0531:
0532: /**
0533: * {@inheritDoc}
0534: * @see #getResourceList
0535: */
0536: protected Enumeration<URL> findResources(final String name)
0537: throws IOException {
0538: final Iterator<PathURLStreamHandler> i = roots.iterator();
0539: return new Enumeration<URL>() {
0540: private Enumeration<URL> current = null;
0541: private Enumeration<URL> next;
0542: {
0543: current = getNext();
0544: next = getNext();
0545: }
0546:
0547: protected Enumeration<URL> getNext() {
0548: if (i.hasNext()) {
0549: try {
0550: PathURLStreamHandler ush = i.next();
0551: Enumeration<URL> e = ush.getResources(name);
0552: return e;
0553: } catch (IOException io) {
0554: log.warn(io);
0555: return current;
0556: }
0557: } else {
0558: return current;
0559: }
0560: }
0561:
0562: public boolean hasMoreElements() {
0563: return current.hasMoreElements()
0564: || next.hasMoreElements();
0565: }
0566:
0567: public URL nextElement() {
0568: if (!current.hasMoreElements()) {
0569: current = next;
0570: next = getNext();
0571: }
0572: URL n = current.nextElement();
0573: return n;
0574: }
0575:
0576: };
0577: }
0578:
0579: /**
0580: * Returns a List, containing all URL's which may represent the
0581: * given resource. This can be used to show what resource whould be loaded and what resource
0582: * whould be masked, or one can also simply somehow 'merge' all these resources.
0583: */
0584: public List<URL> getResourceList(final String name) {
0585: try {
0586: return Collections.list(getResources(name));
0587: } catch (IOException io) {
0588: log.warn(io);
0589: return Collections.emptyList();
0590: }
0591: }
0592:
0593: /**
0594: * Can be used as an argument for {@link #getResourcePaths(Pattern, boolean)}. MMBase works mainly
0595: * with xml configuration files, so this comes in handy.
0596: */
0597: public static final Pattern XML_PATTERN = Pattern
0598: .compile(".*\\.xml$");
0599:
0600: /**
0601: * Returns the 'context' for the ResourceLoader (an URL).
0602: */
0603: public URL getContext() {
0604: return context;
0605: }
0606:
0607: /**
0608: * Returns the 'parent' ResourceLoader. Or <code>null</code> if this ClassLoader has no
0609: * parent. You can create a ResourceLoader with a parent by {@link
0610: * #getChildResourceLoader(String)}.
0611: */
0612: public ResourceLoader getParentResourceLoader() {
0613: return parent;
0614: }
0615:
0616: /**
0617: *
0618: *
0619: * @param context a context relative to the current resource loader
0620: * @return a new 'child' ResourceLoader or the parent ResourceLoader if the context is ".."
0621: * @see #ResourceLoader(ResourceLoader, String)
0622: */
0623: public ResourceLoader getChildResourceLoader(final String context) {
0624: if (context.equals("..")) { // should be made a bit smarter, (also recognizing "../..", "/" and those kind of things).
0625: return getParentResourceLoader();
0626: }
0627: String[] dirs = context.split("/");
0628: ResourceLoader rl = this ;
0629: for (String element : dirs) {
0630: rl = new ResourceLoader(rl, element);
0631: }
0632: return rl;
0633:
0634: }
0635:
0636: /**
0637: * Returns a set of 'sub resources' (read: 'files in the same directory'), which can succesfully be loaded by the ResourceLoader.
0638: *
0639: * @param pattern A Regular expression pattern to which the file-name must match, or <code>null</code> if no restrictions apply
0640: * @param recursive If true, then also subdirectories are searched.
0641: * @return A Set of Strings which can be successfully loaded with the resourceloader.
0642: */
0643: public Set<String> getResourcePaths(final Pattern pattern,
0644: final boolean recursive) {
0645: return getResourcePaths(pattern, recursive, false);
0646: }
0647:
0648: /**
0649: * Returns a set of context strings which can be used to instantiated new ResourceLoaders (resource loaders for directories)
0650: * (see {@link #getChildResourceLoader(String)}).
0651: * @param pattern A Regular expression pattern to which the file-name must match, or <code>null</code> if no restrictions apply
0652: * @param recursive If true, then also subdirectories are searched.
0653: */
0654: public Set<String> getChildContexts(final Pattern pattern,
0655: final boolean recursive) {
0656: return getResourcePaths(pattern, recursive, true);
0657: }
0658:
0659: /**
0660: * Used by {@link #getResourcePaths(Pattern, boolean)} and {@link #getChildContexts(Pattern, boolean)}
0661: * @param pattern A Regular expression pattern to which the file-name must match, or <code>null</code> if no restrictions apply
0662: * @param recursive If true, then also subdirectories are searched.
0663: * @param directories {@link #getChildContexts(Pattern, boolean)} supplies <code>true</code> {@link #getResourcePaths(Pattern, boolean)} supplies <code>false</code>
0664: */
0665: protected Set<String> getResourcePaths(final Pattern pattern,
0666: final boolean recursive, final boolean directories) {
0667: Set<String> results = new TreeSet<String>(); // a set with fixed iteration order
0668: for (PathURLStreamHandler cf : roots) {
0669: cf.getPaths(results, pattern, recursive, directories);
0670: }
0671: return results;
0672: }
0673:
0674: /**
0675: * If you want to change a resource, or create one, then this method can be used. Specify the
0676: * desired resource-name and you get an OutputStream back, to which you must write.
0677: *
0678: * This is a shortcut to <code>findResource(name).openConnection().getOutputStream()</code>
0679: *
0680: * If the given resource already existed, it will be overwritten, or shadowed, if it was not
0681: * writeable.
0682: *
0683: * @throws IOException If the Resource for some reason could not be created.
0684: */
0685: public OutputStream createResourceAsStream(String name)
0686: throws IOException {
0687: if (name == null || name.equals("")) {
0688: throw new IOException(
0689: "You cannot create a resource with an empty name");
0690: }
0691: URL resource = findResource(name);
0692: URLConnection connection = resource.openConnection();
0693: return connection.getOutputStream();
0694: }
0695:
0696: /**
0697: * Returns the givens resource as an InputSource (XML streams). ResourceLoader is often used for
0698: * XML.
0699: * The System ID is set, otherwise you could as wel do new InputSource(r.getResourceAsStream());
0700: * @param name The name of the resource to be loaded
0701: * @return The InputSource if succesfull, <code>null</code> otherwise.
0702: */
0703: public InputSource getInputSource(final String name)
0704: throws IOException {
0705: return getInputSource(findResource(name));
0706: }
0707:
0708: /**
0709: * Static version of {@link #getInputSource(String)}, can e.g. be used in combination with {@link #getResourceList(String)}
0710: */
0711: public static InputSource getInputSource(final URL url)
0712: throws IOException {
0713: try {
0714: InputStream stream = url.openStream();
0715: if (stream == null)
0716: return null;
0717: InputSource is = new InputSource(stream);
0718: //is.setCharacterStream(new InputStreamReader(stream));
0719: is.setSystemId(url.toExternalForm());
0720: return is;
0721: } catch (MalformedURLException mfue) {
0722: log.info(mfue);
0723: return null;
0724: }
0725: }
0726:
0727: /**
0728: * Returns the givens resource as a Document (parsed XML). This can come in handly, because most
0729: * configuration is in XML.
0730: *
0731: * @param name The name of the resource to be loaded
0732: * @return The Document if succesful, <code>null</code> if there is no such resource.
0733: * @throws SAXException If the resource does not present parseable XML.
0734: * @throws IOException
0735: */
0736: public Document getDocument(String name)
0737: throws org.xml.sax.SAXException, IOException {
0738: return getDocument(name, true, null);
0739: }
0740:
0741: /**
0742: * Returns the givens resource as a Document (parsed XML). This can come in handly, because most
0743: * configuration is in XML.
0744: *
0745: * @param name The name of the resource to be loaded
0746: * @param validation If <code>true</code>, validate the xml. By dtd if one of the first lines starts with <!DOCTYPE, by XSD otherwise
0747: * @param baseClass If validation is <code>true</code>, the base class to search for the validating xsd or dtd
0748: * @return The Document if succesful, <code>null</code> if there is no such resource.
0749: * @throws SAXException If the resource does not present parseable XML.
0750: * @throws IOException
0751: */
0752: public Document getDocument(String name, boolean validation,
0753: Class<?> baseClass) throws org.xml.sax.SAXException,
0754: IOException {
0755: return getDocument(getResource(name), validation, baseClass);
0756: }
0757:
0758: /**
0759: * Static version of {@link #getDocument(String, boolean, Class)}, can e.g. be used in combination with {@link #getResourceList(String)}
0760: */
0761: public static Document getDocument(URL url, boolean validation,
0762: Class<?> baseClass) throws org.xml.sax.SAXException,
0763: IOException {
0764: boolean xsd = validation;
0765: if (validation) {
0766: // determin whether this XML perhaps must be validated by DTD (specified 'DOCTYPE')
0767: Reader r = new InputStreamReader(url.openStream());
0768: if (r == null)
0769: return null;
0770: BufferedReader reader = new BufferedReader(r);
0771: String line = reader.readLine();
0772: int lineNumber = 0;
0773: while (lineNumber < 2 && line != null) {
0774: if (line.startsWith("<!DOCTYPE")) {
0775: log.debug("Using DTD to validate '" + url + "'");
0776: xsd = false;
0777: }
0778: line = reader.readLine();
0779: lineNumber++;
0780: }
0781: reader.close();
0782: }
0783:
0784: InputSource source = getInputSource(url);
0785: if (source == null)
0786: return null;
0787: XMLEntityResolver resolver = new XMLEntityResolver(validation,
0788: baseClass);
0789: DocumentBuilder dbuilder = org.mmbase.util.xml.DocumentReader
0790: .getDocumentBuilder(validation, xsd,
0791: null/* default error handler */, resolver);
0792: if (dbuilder == null)
0793: throw new RuntimeException(
0794: "failure retrieving document builder");
0795: if (log.isDebugEnabled())
0796: log.debug("Reading " + source.getSystemId());
0797: try {
0798: Document doc = dbuilder.parse(source);
0799: return doc;
0800: } catch (IOException ioe) { // dtd or so not found?
0801: if (validation) {
0802: log.error(ioe);
0803: // try without validating too.
0804: return getDocument(url, false, baseClass);
0805: } else {
0806: throw ioe;
0807: }
0808: }
0809:
0810: }
0811:
0812: /**
0813: * Creates a resource with given name for given Source.
0814: *
0815: * @see #createResourceAsStream(String)
0816: * @param docType Document which must be used, or <code>null</code> if none.
0817: */
0818: public void storeSource(String name, Source source,
0819: DocumentType docType) throws IOException {
0820: try {
0821: log.service("Storing source " + name + " for " + this );
0822: OutputStream stream = createResourceAsStream(name);
0823: StreamResult streamResult = new StreamResult(stream);
0824: TransformerFactory tf = TransformerFactory.newInstance();
0825: Transformer serializer = tf.newTransformer();
0826: serializer.setOutputProperty(OutputKeys.INDENT, "yes");
0827: if (docType != null) {
0828: serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
0829: docType.getPublicId());
0830: serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
0831: docType.getSystemId());
0832: }
0833: // Indenting not very nice in all xslt-engines, but well, its better then depending
0834: // on a real xslt or lots of code.
0835: serializer.transform(source, streamResult);
0836: stream.close();
0837: } catch (final TransformerException te) {
0838: IOException io = new IOException(te.getMessage());
0839: io.initCause(te);
0840: throw io;
0841: }
0842: }
0843:
0844: /**
0845: * Creates a resource for a given Document.
0846: * @param name Name of the resource.
0847: * @param doc The xml document which must be stored.
0848: * @see #createResourceAsStream(String)
0849: */
0850: public void storeDocument(String name, Document doc)
0851: throws IOException {
0852: storeSource(name, new DOMSource(doc), doc.getDoctype());
0853: }
0854:
0855: /**
0856: * Returns a reader for a given resource. This performs the tricky task of finding the encoding.
0857: * Resources are actually InputStreams (byte arrays), but often they are quite text-oriented
0858: * (like e.g. XML's or property-files), so this method may be useful.
0859: * A resource is supposed to be a property-file if it's name ends in ".properties", it is
0860: * supposed to be XML if it's content starts with <?xml.
0861: * @see #getResourceAsStream(String)
0862: */
0863: public Reader getReader(String name) throws IOException {
0864: try {
0865: InputStream is = getResourceAsStream(name);
0866: if (is == null)
0867: return null;
0868: if (name.endsWith(".properties")) {
0869: // todo \ u escapes must be escaped to decent Character's.
0870: return new TransformingReader(new InputStreamReader(is,
0871: "UTF-8"), new InverseCharTransformer(
0872: new UnicodeEscaper()));
0873: }
0874: byte b[] = new byte[100];
0875: if (is.markSupported()) {
0876: is.mark(101);
0877: }
0878: try {
0879: is.read(b, 0, 100);
0880: if (is.markSupported()) {
0881: is.reset();
0882: } else {
0883: is = getResourceAsStream(name);
0884: }
0885: } catch (IOException ioe) {
0886: is = getResourceAsStream(name);
0887: }
0888:
0889: String encoding = GenericResponseWrapper.getXMLEncoding(b);
0890: if (encoding != null) {
0891: return new InputStreamReader(is, encoding);
0892: }
0893:
0894: // all other things, default to UTF-8
0895: return new InputStreamReader(is, "UTF-8");
0896: } catch (UnsupportedEncodingException uee) {
0897: // could not happen
0898: return null;
0899: }
0900: }
0901:
0902: /**
0903: * Returns a writer for a given resource, so that you can overwrite or create it. This performs
0904: * the tricky task of serializing to the right encoding. It supports the same tricks as {@link
0905: * #getReader}, but then inversed.
0906: * @see #getReader(String)
0907: * @see #createResourceAsStream(String)
0908: */
0909: public Writer getWriter(String name) throws IOException {
0910: OutputStream os = createResourceAsStream(name);
0911: try {
0912: if (os == null)
0913: return null;
0914: if (name.endsWith(".properties")) {
0915: // performs \ u escaping.
0916: return new TransformingWriter(new OutputStreamWriter(
0917: os, "UTF-8"), new UnicodeEscaper());
0918: }
0919: } catch (UnsupportedEncodingException uee) {
0920: log.error("uee " + uee);
0921: }
0922: return new EncodingDetectingOutputStreamWriter(os);
0923: }
0924:
0925: /**
0926: * Returns an abstract URL for a resource with given name, <code>findResource(name).toString()</code> would give an 'external' form.
0927: */
0928: public String toInternalForm(String name) {
0929: return toInternalForm(findResource(name));
0930:
0931: }
0932:
0933: public static String toInternalForm(URL u) {
0934: return u.getProtocol() + ":" + u.getPath();
0935: }
0936:
0937: /**
0938: * Used by {@link ResourceWatcher}. And by some deprecated code that wants to produce File objects.
0939: * @return A List of all files associated with the resource.
0940: */
0941: public List<File> getFiles(String name) {
0942: List<File> result = new ArrayList<File>();
0943: if (name.startsWith("file://")) {
0944: try {
0945: result.add(new File(new URL(name).getFile()));
0946: return result;
0947: } catch (MalformedURLException mfue) {
0948: log.warn(mfue);
0949: }
0950: }
0951: for (PathURLStreamHandler o : roots) {
0952: if (o instanceof FileURLStreamHandler) {
0953: result.add(((FileURLStreamHandler) o).getFile(name));
0954: }
0955: }
0956: return result;
0957:
0958: }
0959:
0960: /**
0961: * @return A Node associated with the resource.
0962: * Used by {@link ResourceWatcher}.
0963: */
0964: Node getResourceNode(String name) {
0965: for (PathURLStreamHandler o : roots) {
0966: if (o instanceof NodeURLStreamHandler) {
0967: return ((NodeConnection) o.openConnection(name))
0968: .getResourceNode();
0969: }
0970: }
0971: return null;
0972: }
0973:
0974: /**
0975: * Logs warning if 'newer' resources are shadowed by older ones.
0976: */
0977: void checkShadowedNewerResources(String name) {
0978: long lastModified = -1;
0979: URL usedUrl = null;
0980:
0981: for (PathURLStreamHandler cf : roots) {
0982: URLConnection con = cf.openConnection(name);
0983: if (con.getDoInput()) {
0984: long lm = con.getLastModified();
0985: if (lm > 0 && usedUrl != null && lastModified > 0
0986: && lm > lastModified) {
0987: log.warn("File " + con.getURL() + " is newer ("
0988: + new Date(lm) + ") then " + usedUrl + "("
0989: + new Date(lastModified)
0990: + ") but shadowed by it");
0991: log.debug("Checked because "
0992: + Logging.stackTrace(15));
0993: }
0994: if (usedUrl == null && lm > 0) {
0995: usedUrl = con.getURL();
0996: lastModified = lm;
0997: }
0998: }
0999: }
1000: }
1001:
1002: /**
1003: * Determine wether File f is shadowed.
1004: * @param name Check for resource with this name
1005: * @param file The file to check for this resource.
1006: * @return The URL for the shadowing resource, or <code>null</code> if not shadowed.
1007: * @throws IllegalArgumentException if <code>file</code> is not a file associated with the resource with given name.
1008: */
1009: URL shadowed(File f, String name) {
1010: for (PathURLStreamHandler cf : roots) {
1011: if (cf instanceof NodeURLStreamHandler) {
1012: URLConnection con = cf.openConnection(name);
1013: if (con.getDoInput()) {
1014: return con.getURL();
1015: }
1016: } else if (cf instanceof FileURLStreamHandler) {
1017: FileConnection con = (FileConnection) cf
1018: .openConnection(name);
1019: File file = con.getFile();
1020: if (file.equals(f)) {
1021: return null; // ok, not shadowed.
1022: } else {
1023: if (file.exists()) {
1024: try {
1025: return file.toURL(); // f is shadowed!
1026: } catch (MalformedURLException mfue) {
1027: assert false : mfue;
1028: }
1029: }
1030: }
1031: }
1032: }
1033: for (File file : getFiles(name)) {
1034: if (file.equals(f)) {
1035: return null;
1036: } else {
1037: if (file.exists()) {
1038: try {
1039: return file.toURL(); // f is shadowed!
1040: } catch (MalformedURLException mfue) {
1041: assert false : mfue;
1042: }
1043: }
1044: }
1045: }
1046: // did not find f as a file for this resource
1047: throw new IllegalArgumentException("File " + f
1048: + " is not a file for resource " + name);
1049: }
1050:
1051: /**
1052: * @see java.lang.Object#toString()
1053: */
1054: public String toString() {
1055: return "" + context.getPath() + " resolving in " + roots;
1056: }
1057:
1058: /**
1059: * @see java.lang.Object#equals(java.lang.Object)
1060: */
1061: public boolean equals(Object o) {
1062: if (this == o)
1063: return true;
1064: // if this is a 'root' loader, then the only equal object should be the object itself!
1065: if (parent == null)
1066: return false;
1067: if (o instanceof ResourceLoader) {
1068: ResourceLoader rl = (ResourceLoader) o;
1069: return rl.parent == parent && rl.context.sameFile(context);
1070: } else {
1071: return false;
1072: }
1073: }
1074:
1075: /**
1076: * @see java.lang.Object#hashCode()
1077: */
1078: public int hashCode() {
1079: int result = 0;
1080: result = HashCodeUtil.hashCode(result, parent);
1081: result = HashCodeUtil.hashCode(result, context);
1082: return result;
1083: }
1084:
1085: /**
1086: * ================================================================================
1087: * INNER CLASSES, all private, protected
1088: * ================================================================================
1089: */
1090:
1091: /**
1092: * Extension URLStreamHandler, used for the 'sub' Handlers, entries of 'roots' in ResourceLoader are of this type.
1093: */
1094: protected abstract class PathURLStreamHandler extends
1095: URLStreamHandler {
1096: /**
1097: * We need an openConnection by name only, and public.
1098: */
1099: abstract public URLConnection openConnection(String name);
1100:
1101: /**
1102: * When a URL has been created, in {@link #openConnection(String)}, this method can make a 'name' of it again.
1103: */
1104: abstract protected String getName(URL u);
1105:
1106: /**
1107: * Returns a List of URL's with the same name. Defaults to one URL.
1108: */
1109: Enumeration<URL> getResources(final String name)
1110: throws IOException {
1111: return new Enumeration<URL>() {
1112: private boolean hasMore = true;
1113:
1114: public boolean hasMoreElements() {
1115: return hasMore;
1116: };
1117:
1118: public URL nextElement() {
1119: hasMore = false;
1120: return openConnection(name).getURL();
1121: }
1122:
1123: };
1124: }
1125:
1126: protected URLConnection openConnection(URL u)
1127: throws IOException {
1128: return openConnection(getName(u));
1129: }
1130:
1131: abstract Set<String> getPaths(Set<String> results,
1132: Pattern pattern, boolean recursive, boolean directories);
1133: }
1134:
1135: protected class FileURLStreamHandler extends PathURLStreamHandler {
1136: private final File fileRoot;
1137: private final boolean writeable;
1138:
1139: FileURLStreamHandler(File root, boolean w) {
1140: fileRoot = root;
1141: writeable = w;
1142:
1143: }
1144:
1145: FileURLStreamHandler(FileURLStreamHandler f) {
1146: fileRoot = f.fileRoot;
1147: writeable = f.writeable;
1148: }
1149:
1150: public File getFile(String name) {
1151: if (name != null && name.startsWith("file:")) {
1152: try {
1153: return new File(new URI(name)); // hff, how cumbersome, to translate an URL to a File
1154: } catch (URISyntaxException use) {
1155: log.warn(use);
1156: }
1157: }
1158: String fileName = fileRoot
1159: + ResourceLoader.this .context.getPath()
1160: + (name == null ? "" : name);
1161: if (!File.separator.equals("/")) { // windows compatibility
1162: fileName = fileName.replace('/', File.separator
1163: .charAt(0)); // er
1164: }
1165: return new File(fileName);
1166: }
1167:
1168: public String getName(URL u) {
1169: int l = (fileRoot + ResourceLoader.this .context.getPath())
1170: .length();
1171: String path = u.getPath();
1172: return l < path.length() ? path.substring(l) : path;
1173: }
1174:
1175: public URLConnection openConnection(String name) {
1176: URL u;
1177: try {
1178: if (name.startsWith("file:")) {
1179: u = new URL(null, name, this );
1180: } else {
1181: u = new URL(null, "file:" + getFile(name), this );
1182: }
1183: } catch (MalformedURLException mfue) {
1184: throw new AssertionError(mfue.getMessage());
1185: }
1186: return new FileConnection(u, getFile(name), writeable);
1187: }
1188:
1189: public Set<String> getPaths(final Set<String> results,
1190: final Pattern pattern, final boolean recursive,
1191: final boolean directories) {
1192: return getPaths(results, pattern, recursive ? "" : null,
1193: directories);
1194: }
1195:
1196: private Set<String> getPaths(final Set<String> results,
1197: final Pattern pattern, final String recursive,
1198: final boolean directories) {
1199: FilenameFilter filter = new FilenameFilter() {
1200: public boolean accept(File dir, String name) {
1201: File f = new File(dir, name);
1202: return pattern == null
1203: || (f.isDirectory() && recursive != null)
1204: || pattern.matcher(f.toString()).matches();
1205: }
1206: };
1207: File f = getFile(recursive);
1208:
1209: if (f.isDirectory()) { // should always be true
1210: File[] files = f.listFiles(filter);
1211: if (files == null)
1212: return results;
1213: for (File element : files) {
1214: if (element.getName().equals(""))
1215: continue;
1216: if (recursive != null && element.isDirectory()) {
1217: getPaths(results, pattern, recursive
1218: + element.getName() + "/", directories);
1219: }
1220: if (element.canRead()
1221: && (directories == element.isDirectory())) {
1222: results
1223: .add((recursive == null ? ""
1224: : recursive)
1225: + element.getName());
1226: }
1227:
1228: }
1229: }
1230:
1231: return results;
1232: }
1233:
1234: public String toString() {
1235: return fileRoot.toString();
1236: }
1237:
1238: public boolean equals(Object o) {
1239: if (o instanceof FileURLStreamHandler) {
1240: FileURLStreamHandler of = (FileURLStreamHandler) o;
1241: return of.fileRoot.equals(fileRoot);
1242: } else {
1243: return false;
1244: }
1245: }
1246:
1247: }
1248:
1249: /**
1250: * A URLConnection for connecting to a File. Of course SUN ships an implementation as well
1251: * (File.getURL), but Sun's implementation sucks. You can't use it for writing a file, and
1252: * getDoInput always gives true, even if the file does not even exist. This version supports
1253: * checking by <code>getDoInput()</code> (read rights) and <code>getDoOutput()</code> (write
1254: * rights) and deleting by <code>getOutputStream().write(null)</code>
1255: */
1256: private class FileConnection extends URLConnection {
1257: private final File file;
1258: private final boolean writeable;
1259:
1260: FileConnection(URL u, File f, boolean w) {
1261: super (u);
1262: this .file = f;
1263: this .writeable = w;
1264: }
1265:
1266: public void connect() throws IOException {
1267: connected = true;
1268: }
1269:
1270: public File getFile() {
1271: return file;
1272: }
1273:
1274: public boolean getDoInput() {
1275: return file.canRead();
1276: }
1277:
1278: public boolean getDoOutput() {
1279: if (!writeable)
1280: return false;
1281: File f = file;
1282: while (f != null) {
1283: if (f.exists()) {
1284: return f.canWrite();
1285: } else {
1286: f = f.getParentFile();
1287: }
1288: }
1289: return false;
1290: }
1291:
1292: public InputStream getInputStream() throws IOException {
1293: if (!connected)
1294: connect();
1295: return new FileInputStream(file);
1296: }
1297:
1298: public OutputStream getOutputStream() throws IOException {
1299: if (!connected)
1300: connect();
1301: if (!writeable) {
1302: throw new UnknownServiceException(
1303: "This file-connection does not allow writing.");
1304: }
1305: // create directory if necessary.
1306: File parent = file.getParentFile();
1307: if (parent != null) {
1308: if (!parent.exists()) {
1309: log.info("Creating subdirs for " + file);
1310: }
1311: parent.mkdirs();
1312: if (!parent.exists()) {
1313: log.warn("Could not create directory for " + file
1314: + ": " + parent);
1315: }
1316: } else {
1317: log.warn("Parent of " + file + " is null ?!");
1318: }
1319: if (file.isDirectory()) {
1320: final File directory = file;
1321: return new OutputStream() {
1322: public void write(byte[] b) throws IOException {
1323: if (b == null) {
1324: directory.delete();
1325: } else {
1326: super .write(b);
1327: }
1328: }
1329:
1330: public void write(int b) throws IOException {
1331: throw new UnsupportedOperationException(
1332: "Cannot write bytes to a directory outputstream");
1333: }
1334: };
1335:
1336: } else {
1337: return new FileOutputStream(file) {
1338: public void write(byte[] b) throws IOException {
1339: if (b == null) {
1340: file.delete();
1341: } else {
1342: super .write(b);
1343: }
1344: }
1345: };
1346: }
1347: }
1348:
1349: public long getLastModified() {
1350: return file.lastModified();
1351: }
1352:
1353: public String toString() {
1354: return "FileConnection " + file.toString();
1355: }
1356:
1357: }
1358:
1359: /**
1360: * URLStreamHandler for NodeConnections.
1361: */
1362: protected class NodeURLStreamHandler extends PathURLStreamHandler {
1363: private final int type;
1364:
1365: NodeURLStreamHandler(int type) {
1366: this .type = type;
1367: }
1368:
1369: NodeURLStreamHandler(NodeURLStreamHandler nf) {
1370: this .type = nf.type;
1371: }
1372:
1373: protected String getName(URL u) {
1374: return u.getPath().substring(
1375: NODE_URL_CONTEXT.getPath().length());
1376: }
1377:
1378: public URLConnection openConnection(String name) {
1379: URL u;
1380: try {
1381: u = new URL(NODE_URL_CONTEXT, name, this );
1382: } catch (MalformedURLException mfue) {
1383: throw new AssertionError(mfue.getMessage());
1384: }
1385: return new NodeConnection(u, name, type);
1386: }
1387:
1388: public Set<String> getPaths(final Set<String> results,
1389: final Pattern pattern, final boolean recursive,
1390: final boolean directories) {
1391: if (ResourceLoader.resourceBuilder != null) {
1392: try {
1393: NodeQuery query = ResourceLoader.resourceBuilder
1394: .createQuery();
1395: Constraint typeConstraint = Queries
1396: .createConstraint(query, TYPE_FIELD,
1397: Queries.getOperator("="), type);
1398: Constraint nameConstraint = Queries
1399: .createConstraint(query,
1400: RESOURCENAME_FIELD, Queries
1401: .getOperator("LIKE"),
1402: ResourceLoader.this .context
1403: .getPath().substring(1)
1404: + "%");
1405:
1406: BasicCompositeConstraint constraint = new BasicCompositeConstraint(
1407: CompositeConstraint.LOGICAL_AND);
1408:
1409: constraint.addChild(typeConstraint).addChild(
1410: nameConstraint);
1411:
1412: query.setConstraint(constraint);
1413: for (Node node : resourceBuilder.getList(query)) {
1414: String url = node
1415: .getStringValue(RESOURCENAME_FIELD);
1416: String subUrl = url
1417: .substring(ResourceLoader.this .context
1418: .getPath().length() - 1);
1419: int pos = subUrl.indexOf('/');
1420:
1421: if (directories) {
1422: if (pos < 0)
1423: continue; // not a directory
1424: do {
1425: String u = subUrl.substring(0, pos);
1426: if (pattern != null
1427: && !pattern.matcher(u)
1428: .matches()) {
1429: continue;
1430: }
1431: results.add(u);
1432: pos = subUrl.indexOf('/', pos + 1);
1433: } while (pos > 0 && recursive);
1434: } else {
1435: if (pos > 0 && !recursive)
1436: continue;
1437: if (pattern != null
1438: && !pattern.matcher(subUrl)
1439: .matches()) {
1440: continue;
1441: }
1442: results.add(subUrl);
1443: }
1444:
1445: }
1446: } catch (BridgeException sqe) {
1447: log.warn(sqe);
1448: }
1449: }
1450: return results;
1451: }
1452:
1453: public String toString() {
1454: return "nodes of type " + type;
1455: }
1456:
1457: }
1458:
1459: /**
1460: * A URLConnection based on an MMBase node.
1461: * @see FileConnection
1462: */
1463: private class NodeConnection extends URLConnection {
1464: Node node;
1465: final String name;
1466: final int type;
1467:
1468: NodeConnection(URL url, String name, int t) {
1469: super (url);
1470: this .name = name;
1471: this .type = t;
1472: }
1473:
1474: public void connect() throws IOException {
1475: if (ResourceLoader.resourceBuilder == null) {
1476: throw new IOException("No resources builder available.");
1477: }
1478: connected = true;
1479: }
1480:
1481: /**
1482: * Gets the Node associated with this URL if there is one.
1483: * @return MMObjectNode or <code>null</code>
1484: */
1485: public Node getResourceNode() {
1486: if (node != null)
1487: return node;
1488: if (name.equals(""))
1489: return null;
1490: String realName = (ResourceLoader.this .context.getPath() + name)
1491: .substring(1);
1492: if (ResourceLoader.resourceBuilder != null) {
1493: try {
1494: NodeQuery query = resourceBuilder.createQuery();
1495: Constraint constraint1 = Queries.createConstraint(
1496: query, RESOURCENAME_FIELD, Queries
1497: .getOperator("="), realName);
1498: Constraint constraint2 = Queries.createConstraint(
1499: query, TYPE_FIELD,
1500: Queries.getOperator("="), type);
1501:
1502: BasicCompositeConstraint constraint = new BasicCompositeConstraint(
1503: CompositeConstraint.LOGICAL_AND);
1504: constraint.addChild(constraint1);
1505: constraint.addChild(constraint2);
1506:
1507: query.setConstraint(constraint);
1508: Iterator<Node> i = resourceBuilder.getList(query)
1509: .iterator();
1510: if (i.hasNext()) {
1511: node = i.next();
1512: return node;
1513: }
1514: } catch (BridgeException sqe) {
1515: log.warn(sqe);
1516: }
1517: }
1518: return null;
1519: }
1520:
1521: public boolean getDoInput() {
1522: return getResourceNode() != null;
1523: }
1524:
1525: public boolean getDoOutput() {
1526: getResourceNode();
1527: return (node != null && node.mayWrite())
1528: || (ResourceLoader.resourceBuilder != null && ResourceLoader.resourceBuilder
1529: .mayCreateNode());
1530: }
1531:
1532: public InputStream getInputStream() throws IOException {
1533: getResourceNode();
1534: if (node != null) {
1535: return node.getInputStreamValue(HANDLE_FIELD);
1536: } else {
1537: throw new IOException("No such (node) resource for "
1538: + name);
1539: }
1540: }
1541:
1542: public OutputStream getOutputStream() throws IOException {
1543: if (getResourceNode() == null) {
1544: if (ResourceLoader.resourceBuilder == null)
1545: return null;
1546:
1547: node = ResourceLoader.resourceBuilder.createNode();
1548: node.setContext(DEFAULT_CONTEXT);
1549: String resourceName = (ResourceLoader.this .context
1550: .getPath() + name).substring(1);
1551: node.setStringValue(RESOURCENAME_FIELD, resourceName);
1552: node.setIntValue(TYPE_FIELD, type);
1553: log.info("Creating node " + resourceName + " " + name
1554: + " " + type);
1555: node.commit();
1556: }
1557: return new OutputStream() {
1558: ByteArrayOutputStream bytes = new ByteArrayOutputStream();
1559:
1560: public void close() throws IOException {
1561: byte[] b = bytes.toByteArray();
1562: node.setValue(HANDLE_FIELD, b);
1563: String mimeType = URLConnection
1564: .guessContentTypeFromStream(new ByteArrayInputStream(
1565: b));
1566: if (mimeType == null) {
1567: URLConnection.guessContentTypeFromName(name);
1568: }
1569: node.setValue("mimetype", mimeType);
1570: node.commit();
1571: }
1572:
1573: public void write(int b) {
1574: bytes.write(b);
1575: }
1576:
1577: public void write(byte[] b) throws IOException {
1578: if (b == null) {
1579: node.delete();
1580: node = null;
1581: } else {
1582: super .write(b);
1583: }
1584: }
1585: };
1586: }
1587:
1588: public long getLastModified() {
1589: getResourceNode();
1590: if (node != null) {
1591: Date lm = node.getDateValue(LASTMODIFIED_FIELD);
1592: if (lm != null) {
1593: return lm.getTime();
1594: }
1595: }
1596: return -1;
1597: }
1598:
1599: public String toString() {
1600: return "NodeConnection " + node;
1601: }
1602:
1603: }
1604:
1605: /**
1606: * If running in a servlet 2.3 environment the ServletResourceURLStreamHandler is not fully
1607: * functional. A warning about that is logged, but only once.
1608: */
1609: private static boolean warned23 = false;
1610:
1611: /**
1612: * URLStreamHandler based on the servletContext object of ResourceLoader
1613: */
1614: protected class ServletResourceURLStreamHandler extends
1615: PathURLStreamHandler {
1616: private String root;
1617:
1618: ServletResourceURLStreamHandler(String r) {
1619: root = r;
1620: }
1621:
1622: ServletResourceURLStreamHandler(
1623: ServletResourceURLStreamHandler f) {
1624: root = f.root;
1625: }
1626:
1627: protected String getName(URL u) {
1628: return u.getPath().substring(root.length());
1629: }
1630:
1631: public URLConnection openConnection(String name) {
1632: try {
1633: URL u = ResourceLoader.servletContext.getResource(root
1634: + ResourceLoader.this .context.getPath() + name);
1635: if (u == null)
1636: return NOT_AVAILABLE_URLSTREAM_HANDLER
1637: .openConnection(name);
1638: return u.openConnection();
1639: } catch (IOException ioe) {
1640: return NOT_AVAILABLE_URLSTREAM_HANDLER
1641: .openConnection(name);
1642: }
1643: }
1644:
1645: public Set<String> getPaths(final Set<String> results,
1646: final Pattern pattern, final boolean recursive,
1647: final boolean directories) {
1648: if (log.isDebugEnabled()) {
1649: log.debug("Getting "
1650: + (directories ? "directories" : "files")
1651: + " matching '" + pattern + "' in '" + root
1652: + "'");
1653: }
1654: return getPaths(results, pattern, recursive ? "" : null,
1655: directories);
1656: }
1657:
1658: private Set<String> getPaths(final Set<String> results,
1659: final Pattern pattern, final String recursive,
1660: final boolean directories) {
1661: if (servletContext != null) {
1662: try {
1663: final String currentRoot = root
1664: + (root.equals("/") ? "" : "/")
1665: + ResourceLoader.this .context.getPath();
1666: final String resourcePath = currentRoot
1667: + (recursive == null ? "" : recursive);
1668: final Collection<String> c = servletContext
1669: .getResourcePaths(resourcePath);
1670:
1671: if (log.isDebugEnabled()) {
1672: log.debug("CurrentRoot '" + currentRoot
1673: + "' resourcePath '" + resourcePath
1674: + "' c: " + c);
1675: }
1676:
1677: if (c == null)
1678: return results;
1679:
1680: for (String res : c) {
1681: log.trace(res);
1682: if (res.equals(resourcePath + "/")) {
1683: // I think this is a bug in Jetty (according to javadoc this should not happen, but it does!)
1684: continue;
1685: }
1686:
1687: String newResourcePath = res
1688: .substring(currentRoot.length());
1689: final boolean isDir = newResourcePath
1690: .endsWith("/");
1691: if (isDir) {
1692: newResourcePath = newResourcePath
1693: .substring(0, newResourcePath
1694: .length() - 1);
1695: // subdirs
1696: if (recursive != null) {
1697: getPaths(results, pattern,
1698: newResourcePath, directories);
1699: }
1700: if (newResourcePath.equals(""))
1701: continue;
1702: }
1703: if ((pattern == null || pattern.matcher(
1704: "/" + newResourcePath).matches())
1705: && (directories == isDir)) {
1706: log.debug("Adding " + newResourcePath);
1707: results.add(newResourcePath);
1708: } else {
1709: log.debug("/" + newResourcePath
1710: + " does not match " + pattern);
1711: }
1712: }
1713: } catch (NoSuchMethodError nsme) {
1714: if (!warned23) {
1715: log.warn("Servet 2.3 feature not supported! "
1716: + nsme.getMessage());
1717: warned23 = true;
1718: }
1719: // servletContext.getResourcePaths is only a servlet 2.3 feature.
1720:
1721: // old app-server (orion 1.5.4: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getResourcePaths(Ljava/lang/String;)Ljava/util/Set;)
1722: // simply ignore, running on war will not work in such app-servers
1723: } catch (Throwable t) {
1724: log.error(t.getMessage(), t);
1725: // ignore
1726: }
1727: }
1728: if (log.isTraceEnabled()) {
1729: log.trace("Returning " + results);
1730: }
1731: return results;
1732: }
1733:
1734: public String toString() {
1735: return "ServletResource " + root;
1736: }
1737: }
1738:
1739: protected class ClassLoaderURLStreamHandler extends
1740: PathURLStreamHandler {
1741: private String root;
1742:
1743: // Some arrangment to remember wich subdirs were possible
1744: //private Set subDirs = new HashSet();
1745:
1746: ClassLoaderURLStreamHandler(String r) {
1747: root = r;
1748: }
1749:
1750: ClassLoaderURLStreamHandler(ClassLoaderURLStreamHandler f) {
1751: root = f.root;
1752: }
1753:
1754: private ClassLoader getClassLoader() {
1755: ClassLoader cl = ResourceLoader.class.getClassLoader();
1756: if (cl == null) {
1757: // A system class.
1758: return ClassLoader.getSystemClassLoader();
1759: } else {
1760: return cl;
1761: }
1762: }
1763:
1764: protected String getName(URL u) {
1765: return u.getPath().substring(
1766: (root + ResourceLoader.this .context.getPath())
1767: .length());
1768: }
1769:
1770: private String getClassResourceName(final String name)
1771: throws MalformedURLException {
1772: String res = root
1773: + new URL(ResourceLoader.this .context, name)
1774: .getPath();
1775: while (res.startsWith("/")) {
1776: res = res.substring(1);
1777: }
1778: if (log.isDebugEnabled()) {
1779: log.debug("Name " + name + " is resource " + res);
1780: }
1781: return res;
1782: }
1783:
1784: Enumeration<URL> getResources(final String name)
1785: throws IOException {
1786: try {
1787: return getClassLoader().getResources(
1788: getClassResourceName(name));
1789: } catch (IOException ioe) {
1790: throw ioe;
1791: } catch (Throwable t) {
1792: log.warn(t);
1793: return Collections.enumeration(Collections.EMPTY_LIST);
1794: }
1795: }
1796:
1797: public URLConnection openConnection(String name) {
1798: try {
1799: URL u = getClassLoader().getResource(
1800: getClassResourceName(name));
1801: if (u == null) {
1802: return NOT_AVAILABLE_URLSTREAM_HANDLER
1803: .openConnection(name);
1804: }
1805: //subDirs.add(ResourceLoader.getDirectory(name));
1806: return u.openConnection();
1807: } catch (IOException ioe) {
1808: return NOT_AVAILABLE_URLSTREAM_HANDLER
1809: .openConnection(name);
1810: }
1811: }
1812:
1813: public Set<String> getPaths(final Set<String> results,
1814: final Pattern pattern, final boolean recursive,
1815: final boolean directories) {
1816: return getPaths(results, pattern, recursive, directories,
1817: "", null);
1818: }
1819:
1820: private Set<String> getPaths(final Set<String> results,
1821: final Pattern pattern, final boolean recursive,
1822: final boolean directories, String resourceDir,
1823: String searchUp) {
1824: try {
1825: List<String> subDirs = new ArrayList<String>();
1826: Enumeration<URL> e = getResources(""
1827: .equals(resourceDir) ? INDEX : resourceDir
1828: + INDEX);
1829: if (searchUp != null && resourceDir.startsWith(".."))
1830: resourceDir = "";
1831: while (e.hasMoreElements()) {
1832: URL u = e.nextElement();
1833: InputStream inputStream = u.openStream();
1834: if (inputStream != null) {
1835: BufferedReader reader = new BufferedReader(
1836: new InputStreamReader(inputStream));
1837: try {
1838: while (true) {
1839: String line = reader.readLine();
1840: if (line == null)
1841: break;
1842: if (line.startsWith("#"))
1843: continue; // support for comments
1844: line = line.trim();
1845:
1846: if (line.startsWith("./"))
1847: line = line.substring(2);
1848:
1849: if (searchUp != null) {
1850: if (line.startsWith(searchUp)) {
1851: line = line.substring(searchUp
1852: .length());
1853: } else {
1854: continue;
1855: }
1856: }
1857:
1858: if (directories) {
1859: line = getDirectory(line);
1860: }
1861: if (line.equals(""))
1862: continue; // support for empty lines
1863:
1864: int firstSlash = line.indexOf('/');
1865: if (firstSlash > 0
1866: && firstSlash < line.length()
1867: && !recursive)
1868: continue;
1869:
1870: if (pattern == null
1871: || pattern.matcher(line)
1872: .matches()) {
1873: results
1874: .add("".equals(resourceDir) ? line
1875: : resourceDir
1876: + line);
1877: }
1878: if (line.endsWith("/")) {
1879: subDirs
1880: .add("".equals(resourceDir) ? line
1881: : resourceDir
1882: + line);
1883: }
1884: }
1885: } catch (IOException ioe) {
1886: } finally {
1887: reader.close();
1888: }
1889: }
1890: }
1891: if (recursive) {
1892: for (String dir : subDirs) {
1893: String newDir = "".equals(resourceDir) ? dir
1894: : resourceDir + dir;
1895: getPaths(results, pattern, recursive,
1896: directories, newDir, null);
1897: }
1898: }
1899: if (searchUp == null) {
1900: searchUp = ResourceLoader
1901: .getDirectoryName(ResourceLoader.this .context
1902: .getFile()) + '/';
1903: ResourceLoader p = ResourceLoader.this .parent;
1904: String rd = "../";
1905: while (p != null) {
1906: getPaths(results, pattern, recursive,
1907: directories, rd, searchUp);
1908: searchUp = ResourceLoader
1909: .getDirectoryName(p.context.getFile())
1910: + '/' + searchUp;
1911: p = p.getParentResourceLoader();
1912: rd = "../" + rd;
1913:
1914: }
1915: }
1916: } catch (IOException ioe) {
1917: }
1918: /*
1919: if (directories) {
1920: Iterator i = subDirs.iterator();
1921: while (i.hasNext()) {
1922: String dir = (String) i.next();
1923: if (pattern == null || pattern.matcher(dir).matches()) {
1924: results.add(dir);
1925: }
1926: }
1927: }
1928: */
1929: if (log.isDebugEnabled()) {
1930: log.debug("Returning " + results);
1931: }
1932: return results;
1933: }
1934:
1935: public String toString() {
1936: return "ClassLoader " + root;
1937: }
1938: }
1939:
1940: private static String NOT_FOUND = "/localhost/NOTFOUND/";
1941:
1942: /**
1943: * URLStreamHandler for URL's which can do neither input, nor output. Such an URL can be
1944: * returned by other PathURLStreamHandlers too.
1945: */
1946: private PathURLStreamHandler NOT_AVAILABLE_URLSTREAM_HANDLER = new PathURLStreamHandler() {
1947:
1948: protected String getName(URL u) {
1949: String path = u.getPath();
1950: return path.substring("/NOTFOUND/".length());
1951: }
1952:
1953: public URLConnection openConnection(String name) {
1954: URL u;
1955: try {
1956: u = new URL(null, "http:/" + NOT_FOUND + name, this );
1957: } catch (MalformedURLException mfue) {
1958: throw new AssertionError(mfue.getMessage());
1959: }
1960: return new NotAvailableConnection(u, name);
1961: }
1962:
1963: public Set<String> getPaths(final Set<String> results,
1964: final Pattern pattern, final boolean recursive,
1965: final boolean directories) {
1966: return new HashSet<String>();
1967: }
1968: };
1969:
1970: /**
1971: * A connection which can neither do input, nor output.
1972: */
1973: private class NotAvailableConnection extends URLConnection {
1974:
1975: private final String name;
1976:
1977: private NotAvailableConnection(URL u, String n) {
1978: super (u);
1979: name = n;
1980: }
1981:
1982: public void connect() throws IOException {
1983: throw new IOException("No such resource " + name);
1984: };
1985:
1986: public boolean getDoInput() {
1987: return false;
1988: }
1989:
1990: public boolean getDoOutput() {
1991: return false;
1992: }
1993:
1994: public InputStream getInputStream() throws IOException {
1995: connect();
1996: return null;
1997: }
1998:
1999: public OutputStream getOutputStream() throws IOException {
2000: connect();
2001: return null;
2002: }
2003:
2004: public String toString() {
2005: return "NOTAVAILABLECONNECTION " + name;
2006: }
2007: };
2008:
2009: /**
2010: * The MMURLStreamHandler is a StreamHandler for the protocol 'mm' (which is only for internal
2011: * use). It combines the Connection types implented here above.
2012: */
2013:
2014: private class MMURLStreamHandler extends URLStreamHandler implements
2015: java.io.Serializable {
2016:
2017: MMURLStreamHandler() {
2018: super ();
2019: }
2020:
2021: protected URLConnection openConnection(URL u)
2022: throws IOException {
2023: return new MMURLConnection(u);
2024: }
2025:
2026: /**
2027: * ExternalForms are mainly used in entity-resolving and URL.toString()
2028: * {@inheritDoc}
2029: */
2030: protected String toExternalForm(URL u) {
2031: return new MMURLConnection(u).getInputConnection().getURL()
2032: .toExternalForm();
2033: }
2034: }
2035:
2036: /**
2037: * Implements the logic for our MM protocol. This logic consists of iterating in <code>ResourceLoader.this.roots</code>.
2038: */
2039: private class MMURLConnection extends URLConnection {
2040:
2041: URLConnection inputConnection = null;
2042: URLConnection outputConnection = null;
2043: final String name;
2044:
2045: MMURLConnection(URL u) {
2046: super (u);
2047: name = url.getPath().substring(1);
2048: //log.debug("Connection to " + url + Logging.stackTrace(new Throwable()));
2049: if (!url.getProtocol().equals(PROTOCOL)) {
2050: throw new RuntimeException(
2051: "Only supporting URL's with protocol "
2052: + PROTOCOL);
2053: }
2054: }
2055:
2056: /**
2057: * {@inheritDoc}
2058: */
2059: public void connect() {
2060: connected = true;
2061: }
2062:
2063: /**
2064: * Returns first possible connection which can be read.
2065: */
2066: protected URLConnection getInputConnection() {
2067: if (inputConnection != null) {
2068: return inputConnection;
2069: }
2070: for (PathURLStreamHandler cf : ResourceLoader.this .roots) {
2071: URLConnection c = cf.openConnection(name);
2072: if (c.getDoInput()) {
2073: inputConnection = c;
2074: break;
2075: }
2076: }
2077: if (inputConnection == null) {
2078: setDoInput(false);
2079: inputConnection = NOT_AVAILABLE_URLSTREAM_HANDLER
2080: .openConnection(name);
2081: } else {
2082: setDoInput(true);
2083: }
2084: connect();
2085: return inputConnection;
2086: }
2087:
2088: /**
2089: * Returns <code>true</true> if you can successfully use getInputStream();
2090: */
2091: public boolean getDoInput() {
2092: return getInputConnection().getDoInput();
2093: }
2094:
2095: /**
2096: * {@inheritDoc}
2097: */
2098: public InputStream getInputStream() throws IOException {
2099: return getInputConnection().getInputStream();
2100: }
2101:
2102: /**
2103: * Returns last URL which can be written, and which is still earlier the the first URL which can be read (or the same URL).
2104: * This ensures that when used for writing, it will then be the prefered one for reading.
2105: */
2106: protected URLConnection getOutputConnection() {
2107: if (outputConnection != null) {
2108: return outputConnection;
2109: }
2110:
2111: // search connection which will be used for reading, and check if it can be used for writing
2112: ListIterator<PathURLStreamHandler> i = ResourceLoader.this .roots
2113: .listIterator();
2114: while (i.hasNext()) {
2115: PathURLStreamHandler cf = i.next();
2116: URLConnection c = cf.openConnection(name);
2117: if (c.getDoInput()) {
2118: if (c.getDoOutput()) { // prefer the currently read one.
2119: outputConnection = c;
2120: }
2121: break;
2122: }
2123: }
2124: if (outputConnection == null) {
2125: // the URL used for reading, could not be written.
2126: // Now iterate backwards, and search one which can be.
2127: while (i.hasPrevious()) {
2128: PathURLStreamHandler cf = i.previous();
2129: URLConnection c = cf.openConnection(name);
2130: if (c.getDoOutput()) {
2131: outputConnection = c;
2132: break;
2133: }
2134: }
2135: }
2136:
2137: if (outputConnection == null) {
2138: setDoOutput(false);
2139: outputConnection = NOT_AVAILABLE_URLSTREAM_HANDLER
2140: .openConnection(name);
2141: } else {
2142: setDoOutput(true);
2143: }
2144: connect();
2145: return outputConnection;
2146: }
2147:
2148: /**
2149: * Returns <code>true</true> if you can successfully use getOutputStream();
2150: */
2151: public boolean getDoOutput() {
2152: return getOutputConnection().getDoOutput();
2153: }
2154:
2155: /**
2156: * {@inheritDoc}
2157: */
2158: public OutputStream getOutputStream() throws IOException {
2159: try {
2160: OutputStream os = getOutputConnection()
2161: .getOutputStream();
2162: if (os == null) {
2163: // Can find no place to store this resource. Giving up.
2164: throw new IOException(
2165: "Cannot create an OutputStream for "
2166: + url
2167: + ", it can no way written, and no resource-node could be created)");
2168: } else {
2169: return os;
2170: }
2171: } catch (Exception ioe) {
2172: IOException i = new IOException(
2173: "Cannot create an OutputStream for " + url
2174: + " " + ioe.getMessage());
2175: i.initCause(ioe);
2176: throw i;
2177: }
2178: }
2179:
2180: /**
2181: * {@inheritDoc}
2182: */
2183: public long getLastModified() {
2184: return getInputConnection().getLastModified();
2185: }
2186:
2187: }
2188:
2189: // ================================================================================
2190: /**
2191: * For testing purposes only
2192: */
2193: public static void main(String[] argv) {
2194: ResourceLoader resourceLoader;
2195:
2196: if (System.getProperty("mmbase.htmlroot") != null) {
2197: resourceLoader = getWebRoot();
2198: } else if (System.getProperty("mmbase.config") != null) {
2199: resourceLoader = getConfigurationRoot();
2200: } else {
2201: resourceLoader = getSystemRoot();
2202: }
2203: try {
2204: if (argv.length == 0) {
2205: System.err
2206: .println("useage: java [-Dmmbase.config=<config dir>|-Dmmbase.htmlroot=<some other dir>] "
2207: + ResourceLoader.class.getName()
2208: + " [<sub directory>] [<resource-name>|*|**]");
2209: return;
2210: }
2211: String arg = argv[0];
2212: if (argv.length > 1) {
2213: resourceLoader = getConfigurationRoot()
2214: .getChildResourceLoader(argv[0]);
2215: arg = argv[1];
2216: }
2217: if (arg.equals("*") || arg.equals("**")) {
2218: for (String s : resourceLoader.getResourcePaths(Pattern
2219: .compile(".*"), arg.equals("**"))) {
2220: System.out.println("" + s);
2221: }
2222: } else {
2223: InputStream resource = resourceLoader
2224: .getResourceAsStream(arg);
2225: if (resource == null) {
2226: System.out.println("No such resource " + arg
2227: + " for " + resourceLoader.getResource(arg)
2228: + ". Creating now.");
2229: PrintWriter writer = new PrintWriter(resourceLoader
2230: .getWriter(arg));
2231: writer.println("TEST");
2232: writer.close();
2233: return;
2234: }
2235: System.out.println("-------------------- resolved "
2236: + resourceLoader.getResourceList(arg)
2237: + " with " + resourceLoader + ": ");
2238:
2239: BufferedReader reader = new BufferedReader(
2240: new InputStreamReader(resource));
2241:
2242: while (true) {
2243: String line = reader.readLine();
2244: if (line == null)
2245: break;
2246: System.out.println(line);
2247: }
2248: }
2249:
2250: } catch (Exception mfeu) {
2251: System.err.println(mfeu.getMessage()
2252: + Logging.stackTrace(mfeu));
2253: }
2254: }
2255:
2256: }
|