This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 81bb56eb7b Drop JDBCAccessLogValve
81bb56eb7b is described below
commit 81bb56eb7b6d9939c50fed2d01d7004ec61af49f
Author: remm <[email protected]>
AuthorDate: Tue Dec 3 16:50:23 2024 +0100
Drop JDBCAccessLogValve
Will see if it is eventually replaced by a DataSourceAccessLog valve
with proper functionality and scalability. So far it seems there's no
demand for it.
---
.../apache/catalina/valves/JDBCAccessLogValve.java | 657 ---------------------
.../catalina/valves/TestJDBCAccessLogValve.java | 143 -----
webapps/docs/changelog.xml | 6 +
3 files changed, 6 insertions(+), 800 deletions(-)
diff --git a/java/org/apache/catalina/valves/JDBCAccessLogValve.java
b/java/org/apache/catalina/valves/JDBCAccessLogValve.java
deleted file mode 100644
index c598759874..0000000000
--- a/java/org/apache/catalina/valves/JDBCAccessLogValve.java
+++ /dev/null
@@ -1,657 +0,0 @@
-/*
- * 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.catalina.valves;
-
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Properties;
-
-import jakarta.servlet.ServletException;
-
-import org.apache.catalina.AccessLog;
-import org.apache.catalina.LifecycleException;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
-import org.apache.tomcat.util.ExceptionUtils;
-
-/**
- * <p>
- * This Tomcat extension logs server access directly to a database, and can be
used instead of the regular file-based
- * access log implemented in AccessLogValve. To use, copy into the
server/classes directory of the Tomcat installation
- * and configure in server.xml as:
- * </p>
- *
- * <pre>
- * <Valve className="org.apache.catalina.valves.JDBCAccessLogValve"
- * driverName="<i>your_jdbc_driver</i>"
- * connectionURL="<i>your_jdbc_url</i>"
- * pattern="combined" resolveHosts="false"
- * />
- * </pre>
- * <p>
- * Many parameters can be configured, such as the database connection (with
<code>driverName</code> and
- * <code>connectionURL</code>), the table name (<code>tableName</code>) and
the field names (corresponding to the
- * get/set method names). The same options as AccessLogValve are supported,
such as <code>resolveHosts</code> and
- * <code>pattern</code> ("common" or "combined" only).
- * </p>
- * <p>
- * When Tomcat is started, a database connection is created and used for all
the log activity. When Tomcat is shutdown,
- * the database connection is closed. This logger can be used at the level of
the Engine context (being shared by all
- * the defined hosts) or the Host context (one instance of the logger per
host, possibly using different databases).
- * </p>
- * <p>
- * The database table can be created with the following command:
- * </p>
- *
- * <pre>
- * CREATE TABLE access (
- * id INT UNSIGNED AUTO_INCREMENT NOT NULL,
- * remoteHost CHAR(15) NOT NULL,
- * userName CHAR(15),
- * timestamp TIMESTAMP NOT NULL,
- * query VARCHAR(255) NOT NULL,
- * status SMALLINT UNSIGNED NOT NULL,
- * bytes INT UNSIGNED NOT NULL,
- * virtualHost VARCHAR(64) NOT NULL,
- * method VARCHAR(8) NOT NULL,
- * referer VARCHAR(128),
- * userAgent VARCHAR(128),
- * PRIMARY KEY (id),
- * INDEX (timestamp),
- * INDEX (remoteHost),
- * INDEX (virtualHost),
- * INDEX (query),
- * INDEX (userAgent)
- * );
- * </pre>
- * <p>
- * Set JDBCAccessLogValve attribute useLongContentLength="true" as you have
more then 4GB outputs. Please, use long SQL
- * datatype at access.bytes attribute. The datatype of bytes at oracle is
<i>number</i> and other databases use <i>bytes
- * BIGINT NOT NULL</i>.
- * </p>
- * <p>
- * If the table is created as above, its name and the field names don't need
to be defined.
- * </p>
- * <p>
- * If the request method is "common", only these fields are used:
- * <code>remoteHost, user, timeStamp, query, status, bytes</code>
- * </p>
- *
- * @author Andre de Jesus
- * @author Peter Rossbach
- * @deprecated Non scalable design, and not documented. Will be removed in
Tomcat 12.
- */
-@Deprecated
-public class JDBCAccessLogValve extends ValveBase implements AccessLog {
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Class constructor. Initializes the fields with the default values. The
defaults are:
- *
- * <pre>
- * driverName = null;
- * connectionURL = null;
- * tableName = "access";
- * remoteHostField = "remoteHost";
- * userField = "userName";
- * timestampField = "timestamp";
- * virtualHostField = "virtualHost";
- * methodField = "method";
- * queryField = "query";
- * statusField = "status";
- * bytesField = "bytes";
- * refererField = "referer";
- * userAgentField = "userAgent";
- * pattern = "common";
- * resolveHosts = false;
- * </pre>
- */
- public JDBCAccessLogValve() {
- super(true);
- driverName = null;
- connectionURL = null;
- tableName = "access";
- remoteHostField = "remoteHost";
- userField = "userName";
- timestampField = "timestamp";
- virtualHostField = "virtualHost";
- methodField = "method";
- queryField = "query";
- statusField = "status";
- bytesField = "bytes";
- refererField = "referer";
- userAgentField = "userAgent";
- pattern = "common";
- resolveHosts = false;
- conn = null;
- ps = null;
- currentTimeMillis = new java.util.Date().getTime();
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * Use long contentLength as you have more 4 GB output.
- *
- * @since 6.0.15
- */
- protected boolean useLongContentLength = false;
-
- /**
- * The connection username to use when trying to connect to the database.
- */
- protected String connectionName = null;
-
-
- /**
- * The connection URL to use when trying to connect to the database.
- */
- protected String connectionPassword = null;
-
- /**
- * Instance of the JDBC Driver class we use as a connection factory.
- */
- protected Driver driver = null;
-
-
- protected String driverName;
- protected String connectionURL;
- protected String tableName;
- protected String remoteHostField;
- protected String userField;
- protected String timestampField;
- protected String virtualHostField;
- protected String methodField;
- protected String queryField;
- protected String statusField;
- protected String bytesField;
- protected String refererField;
- protected String userAgentField;
- protected String pattern;
- protected boolean resolveHosts;
-
-
- protected Connection conn;
- protected PreparedStatement ps;
-
-
- protected long currentTimeMillis;
-
- /**
- * Should this valve set request attributes for IP address, hostname,
protocol and port used for the request.
- * Default is <code>true</code>.
- *
- * @see #setRequestAttributesEnabled(boolean)
- */
- protected boolean requestAttributesEnabled = true;
-
-
- // ------------------------------------------------------------- Properties
-
- /**
- * {@inheritDoc} Default is <code>true</code>.
- */
- @Override
- public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
- this.requestAttributesEnabled = requestAttributesEnabled;
- }
-
- @Override
- public boolean getRequestAttributesEnabled() {
- return requestAttributesEnabled;
- }
-
- /**
- * @return the username to use to connect to the database.
- */
- public String getConnectionName() {
- return connectionName;
- }
-
- /**
- * Set the username to use to connect to the database.
- *
- * @param connectionName Username
- */
- public void setConnectionName(String connectionName) {
- this.connectionName = connectionName;
- }
-
- /**
- * Sets the database driver name.
- *
- * @param driverName The complete name of the database driver class.
- */
- public void setDriverName(String driverName) {
- this.driverName = driverName;
- }
-
- /**
- * @return the password to use to connect to the database.
- */
- public String getConnectionPassword() {
- return connectionPassword;
- }
-
- /**
- * Set the password to use to connect to the database.
- *
- * @param connectionPassword User password
- */
- public void setConnectionPassword(String connectionPassword) {
- this.connectionPassword = connectionPassword;
- }
-
- /**
- * Sets the JDBC URL for the database where the log is stored.
- *
- * @param connectionURL The JDBC URL of the database.
- */
- public void setConnectionURL(String connectionURL) {
- this.connectionURL = connectionURL;
- }
-
-
- /**
- * Sets the name of the table where the logs are stored.
- *
- * @param tableName The name of the table.
- */
- public void setTableName(String tableName) {
- this.tableName = tableName;
- }
-
-
- /**
- * Sets the name of the field containing the remote host.
- *
- * @param remoteHostField The name of the remote host field.
- */
- public void setRemoteHostField(String remoteHostField) {
- this.remoteHostField = remoteHostField;
- }
-
-
- /**
- * Sets the name of the field containing the remote user name.
- *
- * @param userField The name of the remote user field.
- */
- public void setUserField(String userField) {
- this.userField = userField;
- }
-
-
- /**
- * Sets the name of the field containing the server-determined timestamp.
- *
- * @param timestampField The name of the server-determined timestamp field.
- */
- public void setTimestampField(String timestampField) {
- this.timestampField = timestampField;
- }
-
-
- /**
- * Sets the name of the field containing the virtual host information
(this is in fact the server name).
- *
- * @param virtualHostField The name of the virtual host field.
- */
- public void setVirtualHostField(String virtualHostField) {
- this.virtualHostField = virtualHostField;
- }
-
-
- /**
- * Sets the name of the field containing the HTTP request method.
- *
- * @param methodField The name of the HTTP request method field.
- */
- public void setMethodField(String methodField) {
- this.methodField = methodField;
- }
-
-
- /**
- * Sets the name of the field containing the URL part of the HTTP query.
- *
- * @param queryField The name of the field containing the URL part of the
HTTP query.
- */
- public void setQueryField(String queryField) {
- this.queryField = queryField;
- }
-
-
- /**
- * Sets the name of the field containing the HTTP response status code.
- *
- * @param statusField The name of the HTTP response status code field.
- */
- public void setStatusField(String statusField) {
- this.statusField = statusField;
- }
-
-
- /**
- * Sets the name of the field containing the number of bytes returned.
- *
- * @param bytesField The name of the returned bytes field.
- */
- public void setBytesField(String bytesField) {
- this.bytesField = bytesField;
- }
-
-
- /**
- * Sets the name of the field containing the referer.
- *
- * @param refererField The referer field name.
- */
- public void setRefererField(String refererField) {
- this.refererField = refererField;
- }
-
-
- /**
- * Sets the name of the field containing the user agent.
- *
- * @param userAgentField The name of the user agent field.
- */
- public void setUserAgentField(String userAgentField) {
- this.userAgentField = userAgentField;
- }
-
-
- /**
- * Sets the logging pattern. The patterns supported correspond to the
file-based "common" and "combined". These are
- * translated into the use of tables containing either set of fields.
- * <P>
- * <I>TO DO: more flexible field choices.</I>
- * </P>
- *
- * @param pattern The name of the logging pattern.
- */
- public void setPattern(String pattern) {
- this.pattern = pattern;
- }
-
-
- /**
- * Determines whether IP host name resolution is done.
- *
- * @param resolveHosts "true" or "false", if host IP resolution is desired
or not.
- */
- public void setResolveHosts(String resolveHosts) {
- this.resolveHosts = Boolean.parseBoolean(resolveHosts);
- }
-
- /**
- * @return <code>true</code> if content length should be considered a long
rather than an int, defaults to
- * <code>false</code>
- */
- public boolean getUseLongContentLength() {
- return this.useLongContentLength;
- }
-
- /**
- * @param useLongContentLength the useLongContentLength to set
- */
- public void setUseLongContentLength(boolean useLongContentLength) {
- this.useLongContentLength = useLongContentLength;
- }
-
- // --------------------------------------------------------- Public Methods
-
-
- @Override
- public void invoke(Request request, Response response) throws IOException,
ServletException {
- getNext().invoke(request, response);
- }
-
-
- @Override
- public void log(Request request, Response response, long time) {
- if (!getState().isAvailable()) {
- return;
- }
-
- final String EMPTY = "";
-
- String remoteHost;
- if (resolveHosts) {
- if (requestAttributesEnabled) {
- Object host = request.getAttribute(REMOTE_HOST_ATTRIBUTE);
- if (host == null) {
- remoteHost = request.getRemoteHost();
- } else {
- remoteHost = (String) host;
- }
- } else {
- remoteHost = request.getRemoteHost();
- }
- } else {
- if (requestAttributesEnabled) {
- Object addr = request.getAttribute(REMOTE_ADDR_ATTRIBUTE);
- if (addr == null) {
- remoteHost = request.getRemoteAddr();
- } else {
- remoteHost = (String) addr;
- }
- } else {
- remoteHost = request.getRemoteAddr();
- }
- }
- String user = request.getRemoteUser();
- String query = request.getRequestURI();
-
- long bytes = response.getBytesWritten(true);
- if (bytes < 0) {
- bytes = 0;
- }
- int status = response.getStatus();
- String virtualHost = EMPTY;
- String method = EMPTY;
- String referer = EMPTY;
- String userAgent = EMPTY;
- String logPattern = pattern;
- if (logPattern.equals("combined")) {
- virtualHost = request.getServerName();
- method = request.getMethod();
- referer = request.getHeader("referer");
- userAgent = request.getHeader("user-agent");
- }
- synchronized (this) {
- int numberOfTries = 2;
- while (numberOfTries > 0) {
- try {
- open();
-
- ps.setString(1, remoteHost);
- ps.setString(2, user);
- ps.setTimestamp(3, new Timestamp(getCurrentTimeMillis()));
- ps.setString(4, query);
- ps.setInt(5, status);
-
- if (useLongContentLength) {
- ps.setLong(6, bytes);
- } else {
- if (bytes > Integer.MAX_VALUE) {
- bytes = -1;
- }
- ps.setInt(6, (int) bytes);
- }
- if (logPattern.equals("combined")) {
- ps.setString(7, virtualHost);
- ps.setString(8, method);
- ps.setString(9, referer);
- ps.setString(10, userAgent);
- }
- ps.executeUpdate();
- return;
- } catch (SQLException e) {
- // Log the problem for posterity
-
container.getLogger().error(sm.getString("jdbcAccessLogValve.exception"), e);
-
- // Close the connection so that it gets reopened next time
- if (conn != null) {
- close();
- }
- }
- numberOfTries--;
- }
- }
-
- }
-
-
- /**
- * Open (if necessary) and return a database connection for use by this
AccessLogValve.
- *
- * @exception SQLException if a database error occurs
- */
- protected void open() throws SQLException {
-
- // Do nothing if there is a database connection already open
- if (conn != null) {
- return;
- }
-
- // Instantiate our database driver if necessary
- if (driver == null) {
- try {
- Class<?> clazz = Class.forName(driverName);
- driver = (Driver) clazz.getConstructor().newInstance();
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- throw new SQLException(e.getMessage(), e);
- }
- }
-
- // Open a new connection
- Properties props = new Properties();
- if (connectionName != null) {
- props.put("user", connectionName);
- }
- if (connectionPassword != null) {
- props.put("password", connectionPassword);
- }
- conn = driver.connect(connectionURL, props);
- conn.setAutoCommit(true);
- prepare();
- String logPattern = pattern;
- if (logPattern.equals("common")) {
- ps = conn.prepareStatement(
- "INSERT INTO " + tableName + " (" + remoteHostField + ", "
+ userField + ", " + timestampField +
- ", " + queryField + ", " + statusField + ", " +
bytesField + ") VALUES(?, ?, ?, ?, ?, ?)");
- } else if (logPattern.equals("combined")) {
- ps = conn.prepareStatement("INSERT INTO " + tableName + " (" +
remoteHostField + ", " + userField + ", " +
- timestampField + ", " + queryField + ", " + statusField +
", " + bytesField + ", " +
- virtualHostField + ", " + methodField + ", " +
refererField + ", " + userAgentField +
- ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
- }
- }
-
-
- /**
- * Prepare tables for processing. Used by subclasses for testing.
- * @throws SQLException if an exception occurs
- */
- protected void prepare() throws SQLException {
- }
-
-
- /**
- * Close the specified database connection.
- */
- protected void close() {
-
- // Do nothing if the database connection is already closed
- if (conn == null) {
- return;
- }
-
- // Close our prepared statements (if any)
- try {
- ps.close();
- } catch (Throwable f) {
- ExceptionUtils.handleThrowable(f);
- }
- this.ps = null;
-
-
- // Close this database connection, and log any errors
- try {
- conn.close();
- } catch (SQLException e) {
-
container.getLogger().error(sm.getString("jdbcAccessLogValve.close"), e); //
Just log it here
- } finally {
- this.conn = null;
- }
-
- }
-
-
- /**
- * Start this component and implement the requirements of
- * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
that prevents this component from being
- * used
- */
- @Override
- protected void startInternal() throws LifecycleException {
- try {
- open();
- } catch (SQLException e) {
- throw new LifecycleException(e);
- }
- super.startInternal();
- }
-
-
- /**
- * Stop this component and implement the requirements of
- * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
that prevents this component from being
- * used
- */
- @Override
- protected void stopInternal() throws LifecycleException {
- super.stopInternal();
- close();
- }
-
-
- public long getCurrentTimeMillis() {
- long systime = System.currentTimeMillis();
- if ((systime - currentTimeMillis) > 1000) {
- currentTimeMillis = new java.util.Date(systime).getTime();
- }
- return currentTimeMillis;
- }
-
-}
diff --git a/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java
b/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java
deleted file mode 100644
index dc2597316f..0000000000
--- a/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.catalina.valves;
-
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.tomcat.util.buf.ByteChunk;
-
-@RunWith(Parameterized.class)
-public class TestJDBCAccessLogValve extends TomcatBaseTest {
-
- public static final String SCHEMA =
- "CREATE TABLE access (\n" +
- " id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY(Start with 1,
Increment by 1),\n" +
- " remoteHost CHAR(15) NOT NULL,\n" +
- " userName CHAR(15),\n" +
- " timestamp TIMESTAMP NOT NULL,\n" +
- " query VARCHAR(255),\n" +
- " status SMALLINT NOT NULL,\n" +
- " bytes INT NOT NULL,\n" +
- " virtualHost VARCHAR(64),\n" +
- " method VARCHAR(8),\n" +
- " referer VARCHAR(128),\n" +
- " userAgent VARCHAR(128)\n" +
- ")";
-
- @Parameterized.Parameters(name = "{index}: logPattern[{0}]")
- public static Collection<Object[]> parameters() {
- List<Object[]> parameterSets = new ArrayList<>();
-
- parameterSets.add(new Object[] {"common"});
- parameterSets.add(new Object[] {"combined"});
-
- return parameterSets;
- }
-
- @Parameter(0)
- public String logPattern;
-
- @Test
- public void testValve() throws Exception {
-
- Tomcat tomcat = getTomcatInstance();
- // No file system docBase required
- getTomcatInstanceTestWebapp(false, false);
- Context ctx = (Context) tomcat.getHost().findChildren()[0];
-
- CustomJDBCAccessLogValve accessLogValve = new
CustomJDBCAccessLogValve();
- accessLogValve.setDriverName("org.apache.derby.jdbc.EmbeddedDriver");
- accessLogValve.setConnectionURL("jdbc:derby:" +
getTemporaryDirectory().getAbsolutePath()
- + "/accesslog-" + logPattern + ";create=true");
- accessLogValve.setPattern(logPattern);
- ctx.getParent().getPipeline().addValve(accessLogValve);
-
- tomcat.start();
-
- ByteChunk result = new ByteChunk();
- int rc = getUrl("http://localhost:" + getPort() + "/test/index.html",
result, null);
- Assert.assertEquals(HttpServletResponse.SC_OK, rc);
- result.recycle();
-
- rc = getUrl("http://localhost:" + getPort() +
"/test/404.html?foo=bar", result, null);
- Assert.assertEquals(HttpServletResponse.SC_OK, rc);
-
- accessLogValve.verify();
-
- tomcat.stop();
-
- }
-
- protected class CustomJDBCAccessLogValve extends JDBCAccessLogValve {
- @Override
- protected void prepare() throws SQLException {
- try (Statement statement = conn.createStatement()) {
- statement.execute(SCHEMA);
- }
- }
-
- public void verify() throws Exception {
- /*
- * The access log is written after the response. The client may
have processed the response faster than the
- * server was able to write the access log. Give the server up to
5 seconds to write the access log.
- */
- int rowCount = 0;
- int timerCount = 0;
- while (timerCount < 500) {
- try (Statement statement = conn.createStatement()) {
- statement.execute("SELECT COUNT(*) FROM access");
- ResultSet rs = statement.getResultSet();
- Assert.assertTrue(rs.next());
- rowCount = rs.getInt(1);
- if (rowCount > 1) {
- break;
- }
- Thread.sleep(10);
- timerCount++;
- }
- }
- try (Statement statement = conn.createStatement()) {
- statement.execute("SELECT * FROM access");
- ResultSet rs = statement.getResultSet();
- Assert.assertTrue(rs.next());
- Assert.assertEquals(HttpServletResponse.SC_OK,
rs.getInt("status"));
- Assert.assertEquals("/test/index.html", rs.getString("query"));
- Assert.assertTrue(rs.getLong("bytes") == 934);
- Assert.assertTrue(rs.next());
- Assert.assertEquals(HttpServletResponse.SC_OK,
rs.getInt("status"));
- Assert.assertEquals("/test/404.html", rs.getString("query"));
- }
- }
- }
-}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c768d9d1a5..ff62723297 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -122,6 +122,12 @@
Add support for the new Servlet API method
<code>HttpServletResponse.sendEarlyHints()</code>. (markt)
</add>
+ <update>
+ Remove <code>JDBCAccessLogValve</code> which provides limited
+ functionality compared to what is expected from an
+ <code>AccessLog</code> and was not implemented in a scalable way.
+ (remm)
+ </update>
<!-- Entries for backport and removal before 12.0.0-M1 below this line
-->
<fix>
Add special handling for the <code>protocols</code> attribute of
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]