Source Code Cross Referenced for ResourceLoader.java in  » Database-ORM » MMBase » org » mmbase » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » MMBase » org.mmbase.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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 &lt;!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 &lt;?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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.