diff --git java/org/apache/catalina/realm/MemoryRealm.java java/org/apache/catalina/realm/MemoryRealm.java index b8776de..0baf646 100644 --- java/org/apache/catalina/realm/MemoryRealm.java +++ java/org/apache/catalina/realm/MemoryRealm.java @@ -19,17 +19,18 @@ package org.apache.catalina.realm; -import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import org.apache.catalina.Globals; import org.apache.catalina.LifecycleException; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.digester.Digester; +import org.apache.tomcat.util.file.ConfigFileLoader; /** @@ -282,30 +283,42 @@ public class MemoryRealm extends RealmBase { @Override protected void startInternal() throws LifecycleException { - // Validate the existence of our database file - File file = new File(pathname); - if (!file.isAbsolute()) - file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), pathname); - if (!file.exists() || !file.canRead()) - throw new LifecycleException - (sm.getString("memoryRealm.loadExist", - file.getAbsolutePath())); - - // Load the contents of the database file - if (log.isDebugEnabled()) - log.debug(sm.getString("memoryRealm.loadPath", - file.getAbsolutePath())); - Digester digester = getDigester(); + String pathName = getPathname(); + InputStream is = null; + try { - synchronized (digester) { - digester.push(this); - digester.parse(file); + is = ConfigFileLoader.getInputStream(pathName); + + // Load the contents of the database file + if (log.isDebugEnabled()) { + log.debug(sm.getString("memoryRealm.loadPath", pathName)); } - } catch (Exception e) { - throw new LifecycleException - (sm.getString("memoryRealm.readXml"), e); + + Digester digester = getDigester(); + try { + synchronized (digester) { + digester.push(this); + digester.parse(is); + } + } catch (Exception e) { + throw new LifecycleException + (sm.getString("memoryRealm.readXml"), e); + } finally { + digester.reset(); + } + + } catch (IOException ioe) { + throw new LifecycleException(sm.getString("memoryRealm.loadExist", + pathName), ioe); + } finally { - digester.reset(); + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // ignore + } + } } super.startInternal(); diff --git java/org/apache/catalina/users/MemoryUserDatabase.java java/org/apache/catalina/users/MemoryUserDatabase.java index 4395e36..7ca6d78 100644 --- java/org/apache/catalina/users/MemoryUserDatabase.java +++ java/org/apache/catalina/users/MemoryUserDatabase.java @@ -17,9 +17,9 @@ package org.apache.catalina.users; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.HashMap; @@ -34,6 +34,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.digester.AbstractObjectCreationFactory; import org.apache.tomcat.util.digester.Digester; +import org.apache.tomcat.util.file.ConfigFileLoader; import org.apache.tomcat.util.res.StringManager; import org.xml.sax.Attributes; @@ -394,52 +395,44 @@ public class MemoryUserDatabase implements UserDatabase { groups.clear(); roles.clear(); - // Construct a reader for the XML input file (if it exists) - File file = new File(pathname); - if (!file.isAbsolute()) { - file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), - pathname); - } - if (!file.exists()) { - log.error(sm.getString("memoryUserDatabase.fileNotFound", - file.getAbsolutePath())); - return; - } + String pathName = getPathname(); + InputStream is = null; - // Construct a digester to read the XML input file - Digester digester = new Digester(); - try { - digester.setFeature( - "http://apache.org/xml/features/allow-java-encodings", - true); - } catch (Exception e) { - log.warn(sm.getString("memoryUserDatabase.xmlFeatureEncoding"), e); - } - digester.addFactoryCreate - ("tomcat-users/group", - new MemoryGroupCreationFactory(this), true); - digester.addFactoryCreate - ("tomcat-users/role", - new MemoryRoleCreationFactory(this), true); - digester.addFactoryCreate - ("tomcat-users/user", - new MemoryUserCreationFactory(this), true); - - // Parse the XML input file to load this database - FileInputStream fis = null; try { - fis = new FileInputStream(file); - digester.parse(fis); + is = ConfigFileLoader.getInputStream(pathName); + + // Construct a digester to read the XML input file + Digester digester = new Digester(); + try { + digester.setFeature( + "http://apache.org/xml/features/allow-java-encodings", + true); + } catch (Exception e) { + log.warn(sm.getString("memoryUserDatabase.xmlFeatureEncoding"), e); + } + digester.addFactoryCreate + ("tomcat-users/group", + new MemoryGroupCreationFactory(this), true); + digester.addFactoryCreate + ("tomcat-users/role", + new MemoryRoleCreationFactory(this), true); + digester.addFactoryCreate + ("tomcat-users/user", + new MemoryUserCreationFactory(this), true); + + // Parse the XML input to load this database + digester.parse(is); + } catch (IOException ioe) { + log.error(sm.getString("memoryUserDatabase.fileNotFound", pathName)); } finally { - if (fis != null) { + if (is != null) { try { - fis.close(); + is.close(); } catch (IOException ioe) { // Ignore } } } - } } diff --git java/org/apache/naming/resources/ClasspathURLStreamHandler.java java/org/apache/naming/resources/ClasspathURLStreamHandler.java new file mode 100644 index 0000000..25b56f9 --- /dev/null +++ java/org/apache/naming/resources/ClasspathURLStreamHandler.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.naming.resources; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import org.apache.tomcat.util.res.StringManager; + +public class ClasspathURLStreamHandler extends URLStreamHandler { + + private static final StringManager sm = + StringManager.getManager(Constants.Package); + + + @Override + protected URLConnection openConnection(URL u) throws IOException { + String path = u.getPath(); + + // Thread context class loader first + URL classpathUrl = Thread.currentThread().getContextClassLoader().getResource(path); + if (classpathUrl == null) { + // This class's class loader if no joy with the tccl + classpathUrl = ClasspathURLStreamHandler.class.getResource(path); + } + + if (classpathUrl == null) { + throw new FileNotFoundException(sm.getString("classpathUrlStreamHandler.notFound", u)); + } + + return classpathUrl.openConnection(); + } +} diff --git java/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java java/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java index bf41555..14b36c3 100644 --- java/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java +++ java/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java @@ -23,8 +23,9 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** - * Factory for Stream handlers to a JNDI directory context that also supports - * users specifying additional stream handler. + * Factory for Stream handlers to a JNDI directory context, + * or for Stream handlers to a classpath url, + * which also supports users specifying additional stream handler. * * @author Remy Maucherat */ @@ -64,6 +65,8 @@ public class DirContextURLStreamHandlerFactory public URLStreamHandler createURLStreamHandler(String protocol) { if (protocol.equals("jndi")) { return new DirContextURLStreamHandler(); + } else if (protocol.equals("classpath")) { + return new ClasspathURLStreamHandler(); } else { for (URLStreamHandlerFactory factory : userFactories) { URLStreamHandler handler = diff --git java/org/apache/naming/resources/LocalStrings.properties java/org/apache/naming/resources/LocalStrings.properties index 4cc7cff..e18f90c 100644 --- java/org/apache/naming/resources/LocalStrings.properties +++ java/org/apache/naming/resources/LocalStrings.properties @@ -43,3 +43,4 @@ standardResources.exists=File base {0} does not exist standardResources.notStarted=Resources has not yet been started standardResources.null=Document base cannot be null standardResources.slash=Document base {0} must not end with a slash +classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class's class loader diff --git java/org/apache/tomcat/util/file/ConfigFileLoader.java java/org/apache/tomcat/util/file/ConfigFileLoader.java new file mode 100644 index 0000000..5b64d8b --- /dev/null +++ java/org/apache/tomcat/util/file/ConfigFileLoader.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tomcat.util.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * This class is used to obtain {@link InputStream}s for configuration files + * from a given location String. This allows greater flexibility than these + * files having to be loaded directly from a file system. + */ +public class ConfigFileLoader { + + private static final File CATALINA_BASE_FILE; + private static final URI CATALINA_BASE_URI; + + static { + CATALINA_BASE_FILE = new File(System.getProperty("catalina.base")); + CATALINA_BASE_URI = CATALINA_BASE_FILE.toURI(); + } + + private ConfigFileLoader() { + // Utility class. Hide the default constructor. + } + + + /** + * Load the resource from the specified location. + * + * @param location The location for the resource of interest. The location + * may be a URL or a file path. Relative paths will be + * resolved against CATALINA_BASE. + * + * @return The InputStream for the given resource. The caller is responsible + * for closing this stream when it is no longer used. + * + * @throws IOException If an InputStream cannot be created using the + * provided location + */ + public static InputStream getInputStream(String location) throws IOException { + // Absolute URIs will be left alone + // Relative files will be resolved relative to catalina base + // Absolute files will be converted to URIs + + // Location was originally always a file before URI support was added so + // try file first. + + // First guess, an absolute file path + File file = new File(location); + + if (!file.isAbsolute()) { + // Second guess, a file path relative to CATALINA_BASE + file = new File(CATALINA_BASE_FILE, location); + } + + if (file.isFile()) { + return new FileInputStream(file); + } + + // Third and final guess, a URI + URI uri = CATALINA_BASE_URI.resolve(location); + return uri.toURL().openStream(); + } + + +} \ No newline at end of file diff --git java/org/apache/tomcat/util/net/AbstractEndpoint.java java/org/apache/tomcat/util/net/AbstractEndpoint.java index c4ad4ce..9f0349b 100644 --- java/org/apache/tomcat/util/net/AbstractEndpoint.java +++ java/org/apache/tomcat/util/net/AbstractEndpoint.java @@ -16,7 +16,6 @@ */ package org.apache.tomcat.util.net; -import java.io.File; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -728,25 +727,6 @@ public abstract class AbstractEndpoint { } } - - public String adjustRelativePath(String path, String relativeTo) { - // Empty or null path can't point to anything useful. The assumption is - // that the value is deliberately empty / null so leave it that way. - if (path == null || path.length() == 0) { - return path; - } - String newPath = path; - File f = new File(newPath); - if ( !f.isAbsolute()) { - newPath = relativeTo + File.separator + newPath; - f = new File(newPath); - } - if (!f.exists()) { - getLog().warn("configured file:["+newPath+"] does not exist."); - } - return newPath; - } - protected abstract Log getLog(); // Flags to indicate optional feature support // Some of these are always hard-coded, some are hard-coded to false (i.e. @@ -833,8 +813,7 @@ public abstract class AbstractEndpoint { private String keystoreFile = System.getProperty("user.home")+"/.keystore"; public String getKeystoreFile() { return keystoreFile;} public void setKeystoreFile(String s ) { - keystoreFile = adjustRelativePath(s, - System.getProperty(Constants.CATALINA_BASE_PROP)); + keystoreFile = s; } private String keystorePass = null; @@ -874,8 +853,7 @@ public abstract class AbstractEndpoint { private String truststoreFile = System.getProperty("javax.net.ssl.trustStore"); public String getTruststoreFile() {return truststoreFile;} public void setTruststoreFile(String s) { - truststoreFile = adjustRelativePath(s, - System.getProperty(Constants.CATALINA_BASE_PROP)); + truststoreFile = s; } private String truststorePass = diff --git java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java index 7a4ade6..7cab87f 100644 --- java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java +++ java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java @@ -17,8 +17,6 @@ package org.apache.tomcat.util.net.jsse; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -63,6 +61,7 @@ import javax.net.ssl.X509KeyManager; import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.compat.JreVendor; +import org.apache.tomcat.util.file.ConfigFileLoader; import org.apache.tomcat.util.net.AbstractEndpoint; import org.apache.tomcat.util.net.Constants; import org.apache.tomcat.util.net.SSLUtil; @@ -431,12 +430,7 @@ public class JSSESocketFactory implements ServerSocketFactory, SSLUtil { } if(!("PKCS11".equalsIgnoreCase(type) || "".equalsIgnoreCase(path))) { - File keyStoreFile = new File(path); - if (!keyStoreFile.isAbsolute()) { - keyStoreFile = new File(System.getProperty( - Constants.CATALINA_BASE_PROP), path); - } - istream = new FileInputStream(keyStoreFile); + istream = ConfigFileLoader.getInputStream(path); } char[] storePass = null; @@ -718,16 +712,11 @@ public class JSSESocketFactory implements ServerSocketFactory, SSLUtil { protected Collection getCRLs(String crlf) throws IOException, CRLException, CertificateException { - File crlFile = new File(crlf); - if( !crlFile.isAbsolute() ) { - crlFile = new File( - System.getProperty(Constants.CATALINA_BASE_PROP), crlf); - } Collection crls = null; InputStream is = null; try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); - is = new FileInputStream(crlFile); + is = ConfigFileLoader.getInputStream(crlf); crls = cf.generateCRLs(is); } catch(IOException iex) { throw iex; diff --git test/org/apache/naming/resources/TestClasspathUrlStreamHandler.java test/org/apache/naming/resources/TestClasspathUrlStreamHandler.java new file mode 100644 index 0000000..5752658 --- /dev/null +++ test/org/apache/naming/resources/TestClasspathUrlStreamHandler.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.naming.resources; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestClasspathUrlStreamHandler { + + @BeforeClass + public static void setup() { + URL.setURLStreamHandlerFactory(DirContextURLStreamHandlerFactory.getInstance()); + } + + @Test + public void testClasspathURL01() throws IOException { + URL u = new URL("classpath:/org/apache/naming/resources/LocalStrings.properties"); + InputStream is = u.openStream(); + Properties p = new Properties(); + p.load(is); + String msg = (String) p.get("resources.null"); + Assert.assertEquals("Document base cannot be null", msg); + } +} \ No newline at end of file diff --git test/org/apache/tomcat/util/file/TestConfigFileLoader.java test/org/apache/tomcat/util/file/TestConfigFileLoader.java new file mode 100644 index 0000000..cf64fe5 --- /dev/null +++ test/org/apache/tomcat/util/file/TestConfigFileLoader.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tomcat.util.file; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.apache.naming.resources.DirContextURLStreamHandlerFactory; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestConfigFileLoader { + + @BeforeClass + public static void setup() { + URL.setURLStreamHandlerFactory( + DirContextURLStreamHandlerFactory.getInstance()); + File buildDir = new File( + System.getProperty("tomcat.test.tomcatbuild", "output/build")); + System.setProperty("catalina.base", buildDir.getAbsolutePath()); + } + + @Test + public void test01() throws IOException { + doTest("classpath:org/apache/catalina/mbeans-descriptors.xml"); + } + + @Test(expected=FileNotFoundException.class) + public void test02() throws IOException { + doTest("classpath:org/apache/catalina/foo"); + } + + @Test + public void test03() throws IOException { + doTest("conf/server.xml"); + } + + @Test(expected=FileNotFoundException.class) + public void test04() throws IOException { + doTest("conf/unknown"); + } + + @Test + public void testAbsolutePath() throws IOException { + File test = new File(System.getProperty("java.io.tmpdir"), "testAbsolutePath"); + if (test.exists()) { + FileUtils.forceDelete(test); + } + test.createNewFile(); + doTest(test.getAbsolutePath()); + } + + private void doTest(String path) throws IOException { + InputStream is = null; + try { + is = ConfigFileLoader.getInputStream(path); + Assert.assertNotNull(is); + } finally { + if (is != null) { + is.close(); + } + } + } +} \ No newline at end of file