Author: markt Date: Fri Sep 14 12:36:58 2018 New Revision: 1840917 URL: http://svn.apache.org/viewvc?rev=1840917&view=rev Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=58590 Monitor the source for a MemoryUserdatabase (typically tomcat-users.xml) and re-load it it a change is detected.
Modified: tomcat/trunk/java/org/apache/catalina/users/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabase.java tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/jndi-resources-howto.xml Modified: tomcat/trunk/java/org/apache/catalina/users/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/users/LocalStrings.properties?rev=1840917&r1=1840916&r2=1840917&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/users/LocalStrings.properties [UTF-8] (original) +++ tomcat/trunk/java/org/apache/catalina/users/LocalStrings.properties [UTF-8] Fri Sep 14 12:36:58 2018 @@ -20,6 +20,8 @@ memoryUserDatabase.nullGroup=Null or zer memoryUserDatabase.nullRole=Null or zero length role name specified. The role will be ignored. memoryUserDatabase.nullUser=Null or zero length user name specified. The user will be ignored. memoryUserDatabase.readOnly=User database has been configured to be read only. Changes cannot be saved +memoryUserDatabase.reload=Reloading memory user database [{0}] from updated source [{1}] +memoryUserDatabase.reloadError=Error reloading memory user database [{0}] from updated source [{1}] memoryUserDatabase.renameOld=Cannot rename original file to [{0}] memoryUserDatabase.renameNew=Cannot rename new file to [{0}] memoryUserDatabase.restoreOrig=Cannot restore [{0} to original file Modified: tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabase.java?rev=1840917&r1=1840916&r2=1840917&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabase.java (original) +++ tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabase.java Fri Sep 14 12:36:58 2018 @@ -22,6 +22,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; @@ -148,6 +151,9 @@ public class MemoryUserDatabase implemen private final Lock readLock = dbLock.readLock(); private final Lock writeLock = dbLock.writeLock(); + private volatile long lastModified = 0; + private boolean watchSource = true; + // ------------------------------------------------------------- Properties @@ -165,6 +171,7 @@ public class MemoryUserDatabase implemen } + /** * @return the unique global identifier of this user database. */ @@ -212,6 +219,17 @@ public class MemoryUserDatabase implemen } + public boolean getWatchSource() { + return watchSource; + } + + + + public void setWatchSource(boolean watchSource) { + this.watchSource = watchSource; + } + + /** * @return the set of {@link Role}s defined in this user database. */ @@ -406,7 +424,13 @@ public class MemoryUserDatabase implemen roles.clear(); String pathName = getPathname(); - try (InputStream is = ConfigFileLoader.getInputStream(getPathname())) { + URI uri = ConfigFileLoader.getURI(pathName); + URL url = uri.toURL(); + URLConnection uConn = url.openConnection(); + + try (InputStream is = uConn.getInputStream()) { + this.lastModified = uConn.getLastModified(); + // Construct a digester to read the XML input file Digester digester = new Digester(); try { @@ -538,21 +562,21 @@ public class MemoryUserDatabase implemen fileNew = new File(System.getProperty(Globals.CATALINA_BASE_PROP), pathnameNew); } - try (FileOutputStream fos = new FileOutputStream(fileNew); - OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8"); - PrintWriter writer = new PrintWriter(osw)) { - - // Print the file prolog - writer.println("<?xml version='1.0' encoding='utf-8'?>"); - writer.println("<tomcat-users xmlns=\"http://tomcat.apache.org/xml\""); - writer.print(" "); - writer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); - writer.print(" "); - writer.println("xsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\""); - writer.println(" version=\"1.0\">"); + writeLock.lock(); + try { + try (FileOutputStream fos = new FileOutputStream(fileNew); + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8"); + PrintWriter writer = new PrintWriter(osw)) { + + // Print the file prolog + writer.println("<?xml version='1.0' encoding='utf-8'?>"); + writer.println("<tomcat-users xmlns=\"http://tomcat.apache.org/xml\""); + writer.print(" "); + writer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); + writer.print(" "); + writer.println("xsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\""); + writer.println(" version=\"1.0\">"); - writeLock.lock(); - try { // Print entries for each defined role, group, and user Iterator<?> values = null; values = getRoles(); @@ -570,23 +594,24 @@ public class MemoryUserDatabase implemen writer.print(" "); writer.println(((MemoryUser) values.next()).toXml()); } - } finally { - writeLock.unlock(); - } - // Print the file epilog - writer.println("</tomcat-users>"); + // Print the file epilog + writer.println("</tomcat-users>"); - // Check for errors that occurred while printing - if (writer.checkError()) { - throw new IOException(sm.getString("memoryUserDatabase.writeException", - fileNew.getAbsolutePath())); - } - } catch (IOException e) { - if (fileNew.exists() && !fileNew.delete()) { - log.warn(sm.getString("memoryUserDatabase.fileDelete", fileNew)); + // Check for errors that occurred while printing + if (writer.checkError()) { + throw new IOException(sm.getString("memoryUserDatabase.writeException", + fileNew.getAbsolutePath())); + } + } catch (IOException e) { + if (fileNew.exists() && !fileNew.delete()) { + log.warn(sm.getString("memoryUserDatabase.fileDelete", fileNew)); + } + throw e; } - throw e; + this.lastModified = fileNew.lastModified(); + } finally { + writeLock.unlock(); } // Perform the required renames to permanently save this file @@ -621,6 +646,34 @@ public class MemoryUserDatabase implemen } } + + @Override + public void backgroundProcess() { + if (!watchSource) { + return; + } + + URI uri = ConfigFileLoader.getURI(getPathname()); + try { + URL url = uri.toURL(); + URLConnection uConn = url.openConnection(); + + if (this.lastModified != uConn.getLastModified()) { + writeLock.lock(); + try { + if (this.lastModified != uConn.getLastModified()) { + log.info(sm.getString("memoryUserDatabase.reload", id, uri)); + open(); + } + } finally { + writeLock.unlock(); + } + } + } catch (Exception ioe) { + log.error(sm.getString("memoryUserDatabase.reloadError", id, uri), ioe); + } + } + /** * Return a String representation of this UserDatabase. Modified: tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java?rev=1840917&r1=1840916&r2=1840917&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java (original) +++ tomcat/trunk/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java Fri Sep 14 12:36:58 2018 @@ -98,6 +98,11 @@ public class MemoryUserDatabaseFactory i database.setReadonly(Boolean.parseBoolean(ra.getContent().toString())); } + ra = ref.get("watchSource"); + if (ra != null) { + database.setWatchSource(Boolean.parseBoolean(ra.getContent().toString())); + } + // Return the configured database instance database.open(); // Don't try something we know won't work Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1840917&r1=1840916&r2=1840917&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Fri Sep 14 12:36:58 2018 @@ -48,6 +48,13 @@ <subsection name="Catalina"> <changelog> <add> + <bug>58590</bug>: Add the ability for a UserDatabase to monitor the + backing XML file for changes and reload the source file if a change in + the last modified time is detected. This is enabled by default meaning + that changes to <code>$CATALINA_BASE/conf/tomcat-users.xml</code> will + now take effect a short time after the file is saved. (markt) + </add> + <add> <bug>61692</bug>: Add the ability to control which HTTP methods are handled by the CGI Servlet via a new initialization parameter <code>cgiMethods</code>. (markt) Modified: tomcat/trunk/webapps/docs/jndi-resources-howto.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/jndi-resources-howto.xml?rev=1840917&r1=1840916&r2=1840917&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/jndi-resources-howto.xml (original) +++ tomcat/trunk/webapps/docs/jndi-resources-howto.xml Fri Sep 14 12:36:58 2018 @@ -482,6 +482,12 @@ public class MyBean2 { is running as. Ensure that these are appropriate to maintain the security of your installation.</p> + <p>If referenced in a Realm, the UserDatabse will, by default, monitor + <code>pathname</code> for changes and reload the file if a change in the + last modified time is observed. This can be disabled by setting the + <code>watchSource</code> attribute to <code>false</code>. + </p> + <h5>3. Configure the Realm</h5> <p>Configure a UserDatabase Realm to use this resource as described in the --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org