This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
/*
* 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;
public interface ContextBind {
/**
* Change the current thread context class loader to the web application
* class loader. If no web application class loader is defined, or if the
* current thread is already using the web application class loader then no
* change will be made. If the class loader is changed and a
* {@link org.apache.catalina.ThreadBindingListener} is configured then
* {@link org.apache.catalina.ThreadBindingListener#bind()} will be called
* after the change has been made.
*
* @param usePrivilegedAction
* Should a {@link java.security.PrivilegedAction} be used when
* obtaining the current thread context class loader and setting
* the new one?
* @param originalClassLoader
* The current class loader if known to save this method having to
* look it up
*
* @return If the class loader has been changed by the method it will return
* the thread context class loader in use when the method was
* called. If no change was made then this method returns null.
*/
ClassLoader bind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
/**
* Restore the current thread context class loader to the original class
* loader in used before {@link #bind(boolean, ClassLoader)} was called. If
* no original class loader is passed to this method then no change will be
* made. If the class loader is changed and a
* {@link org.apache.catalina.ThreadBindingListener} is configured then
* {@link org.apache.catalina.ThreadBindingListener#unbind()} will be called
* before the change is made.
*
* @param usePrivilegedAction
* Should a {@link java.security.PrivilegedAction} be used when
* setting the current thread context class loader?
* @param originalClassLoader
* The class loader to restore as the thread context class loader
*/
void unbind(boolean usePrivilegedAction, ClassLoader originalClassLoader);
}

View File

@@ -0,0 +1,42 @@
/*
* 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;
import java.lang.reflect.InvocationTargetException;
import javax.naming.NamingException;
public interface InstanceManager {
Object newInstance(Class<?> clazz) throws IllegalAccessException, InvocationTargetException,
NamingException, InstantiationException, IllegalArgumentException,
NoSuchMethodException, SecurityException;
Object newInstance(String className) throws IllegalAccessException, InvocationTargetException,
NamingException, InstantiationException, ClassNotFoundException,
IllegalArgumentException, NoSuchMethodException, SecurityException;
Object newInstance(String fqcn, ClassLoader classLoader) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, IllegalArgumentException, NoSuchMethodException,
SecurityException;
void newInstance(Object o)
throws IllegalAccessException, InvocationTargetException, NamingException;
void destroyInstance(Object o) throws IllegalAccessException, InvocationTargetException;
}

View File

@@ -0,0 +1,35 @@
/*
* 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;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class InstanceManagerBindings {
private static final Map<ClassLoader, InstanceManager> bindings = new ConcurrentHashMap<>();
public static final void bind(ClassLoader classLoader, InstanceManager instanceManager) {
bindings.put(classLoader, instanceManager);
}
public static final void unbind(ClassLoader classLoader) {
bindings.remove(classLoader);
}
public static final InstanceManager get(ClassLoader classLoader) {
return bindings.get(classLoader);
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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;
import java.lang.instrument.ClassFileTransformer;
/**
* Specifies a class loader capable of being decorated with
* {@link ClassFileTransformer}s. These transformers can instrument
* (or weave) the byte code of classes loaded through this class loader
* to alter their behavior. Currently only
* {@link org.apache.catalina.loader.WebappClassLoaderBase} implements this
* interface. This allows web application frameworks or JPA providers
* bundled with a web application to instrument web application classes
* as necessary.
* <p>
* You should always program against the methods of this interface
* (whether using reflection or otherwise). The methods in
* {@code WebappClassLoaderBase} are protected by the default security
* manager if one is in use.
*
* @since 8.0, 7.0.64
*/
public interface InstrumentableClassLoader {
/**
* Adds the specified class file transformer to this class loader. The
* transformer will then be able to instrument the bytecode of any
* classes loaded by this class loader after the invocation of this
* method.
*
* @param transformer The transformer to add to the class loader
* @throws IllegalArgumentException if the {@literal transformer} is null.
*/
void addTransformer(ClassFileTransformer transformer);
/**
* Removes the specified class file transformer from this class loader.
* It will no longer be able to instrument the byte code of any classes
* loaded by the class loader after the invocation of this method.
* However, any classes already instrumented by this transformer before
* this method call will remain in their instrumented state.
*
* @param transformer The transformer to remove
*/
void removeTransformer(ClassFileTransformer transformer);
/**
* Returns a copy of this class loader without any class file
* transformers. This is a tool often used by Java Persistence API
* providers to inspect entity classes in the absence of any
* instrumentation, something that can't be guaranteed within the
* context of a {@link ClassFileTransformer}'s
* {@link ClassFileTransformer#transform(ClassLoader, String, Class,
* java.security.ProtectionDomain, byte[]) transform} method.
* <p>
* The returned class loader's resource cache will have been cleared
* so that classes already instrumented will not be retained or
* returned.
*
* @return the transformer-free copy of this class loader.
*/
ClassLoader copyWithoutTransformers();
}

View File

@@ -0,0 +1,151 @@
/*
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.jar.Manifest;
/**
* Provides an abstraction for use by the various classes that need to scan
* JARs. The classes provided by the JRE for accessing JARs
* ({@link java.util.jar.JarFile} and {@link java.util.jar.JarInputStream}) have
* significantly different performance characteristics depending on the form of
* the URL used to access the JAR. For file based JAR {@link java.net.URL}s,
* {@link java.util.jar.JarFile} is faster but for non-file based
* {@link java.net.URL}s, {@link java.util.jar.JarFile} creates a copy of the
* JAR in the temporary directory so {@link java.util.jar.JarInputStream} is
* faster.
*/
public interface Jar extends AutoCloseable {
/**
* @return The URL for accessing the JAR file.
*/
URL getJarFileURL();
/**
* Determines if a specific entry exists within the JAR.
*
* @param name Entry to look for
* @return Implementations will always return {@code false}
*
* @throws IOException if an I/O error occurs while processing the JAR file
* entries
*
* @deprecated Unused. This will be removed in Tomcat 9 onwards.
*/
@Deprecated
boolean entryExists(String name) throws IOException;
/**
* Obtain an {@link InputStream} for a given entry in a JAR. The caller is
* responsible for closing the stream.
*
* @param name Entry to obtain an {@link InputStream} for
* @return An {@link InputStream} for the specified entry or null if
* the entry does not exist
*
* @throws IOException if an I/O error occurs while processing the JAR file
*/
InputStream getInputStream(String name) throws IOException;
/**
* Obtain the last modified time for the given resource in the JAR.
*
* @param name Entry to obtain the modification time for
*
* @return The time (in the same format as
* {@link System#currentTimeMillis()} that the resource was last
* modified. Returns -1 if the entry does not exist
*
* @throws IOException if an I/O error occurs while processing the JAR file
*/
long getLastModified(String name) throws IOException;
/**
* Determine if the given resource in present in the JAR.
*
* @param name Entry to look for
*
* @return {@code true} if the entry is present in the JAR, otherwise
* {@code false}
*
* @throws IOException if an I/O error occurs while processing the JAR file
*/
boolean exists(String name) throws IOException;
/**
* Close any resources associated with this JAR.
*/
@Override
void close();
/**
* Moves the internal pointer to the next entry in the JAR.
*/
void nextEntry();
/**
* Obtains the name of the current entry.
*
* @return The entry name
*/
String getEntryName();
/**
* Obtains the input stream for the current entry.
*
* @return The input stream
* @throws IOException If the stream cannot be obtained
*/
InputStream getEntryInputStream() throws IOException;
/**
* Obtain, in String form, the URL for an entry in this JAR. Note that for
* JARs nested in WAR files, the Tomcat specific war:file:... form will not
* be used, rather the jar:jar:file:... form (that the JRE does not
* understand will be used). Note that this means that any code using these
* URLs will need to understand the jar:jar:file:... form and use the
* {@link org.apache.tomcat.util.scan.JarFactory} to ensure resources are
* accessed correctly.
*
* @param entry The entry to generate the URL for
*
* @return a URL for the specified entry in the JAR
*/
String getURL(String entry);
/**
* Obtain the manifest for the JAR file.
*
* @return The manifest for this JAR file.
*
* @throws IOException If an I/O error occurs trying to obtain the manifest
*/
Manifest getManifest() throws IOException;
/**
* Resets the internal pointer used to track JAR entries to the beginning of
* the JAR.
*
* @throws IOException If the pointer cannot be reset
*/
void reset() throws IOException;
}

View File

@@ -0,0 +1,31 @@
/*
* 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;
public interface JarScanFilter {
/**
*
* @param jarScanType The type of JAR scan currently being performed
* @param jarName The name of the JAR file (without any path
* information) to be checked to see if it should
* be included in the results or not
* @return <code>true</code> if the JAR should be returned in the results,
* <code>false</code> if it should be excluded
*/
boolean check(JarScanType jarScanType, String jarName);
}

View File

@@ -0,0 +1,23 @@
/*
* 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;
public enum JarScanType {
TLD,
PLUGGABILITY,
OTHER
}

View File

@@ -0,0 +1,45 @@
/*
* 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;
import javax.servlet.ServletContext;
/**
* Scans a web application and classloader hierarchy for JAR files. Uses
* include TLD scanning and web-fragment.xml scanning. Uses a call-back
* mechanism so the caller can process each JAR found.
*/
public interface JarScanner {
/**
* Scan the provided ServletContext and classloader for JAR files. Each JAR
* file found will be passed to the callback handler to be processed.
*
* @param scanType The type of JAR scan to perform. This is passed to
* the filter which uses it to determine how to
* filter the results
* @param context The ServletContext - used to locate and access
* WEB-INF/lib
* @param callback The handler to process any JARs found
*/
public void scan(JarScanType scanType, ServletContext context,
JarScannerCallback callback);
public JarScanFilter getJarScanFilter();
public void setJarScanFilter(JarScanFilter jarScanFilter);
}

View File

@@ -0,0 +1,67 @@
/*
* 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;
import java.io.File;
import java.io.IOException;
/**
* This interface is implemented by clients of the {@link JarScanner} to enable
* them to receive notification of a discovered JAR.
*/
public interface JarScannerCallback {
/**
* A JAR was found and may be accessed for further processing via the
* provided URL connection. The caller is responsible for closing the JAR.
*
* @param jar The JAR to process
* @param webappPath The path, if any, to the JAR within the web application
* @param isWebapp Indicates if the JAR was found within a web
* application. If <code>false</code> the JAR should
* be treated as being provided by the container
*
* @throws IOException if an I/O error occurs while scanning the JAR
*/
public void scan(Jar jar, String webappPath, boolean isWebapp)
throws IOException;
/**
* A directory was found that is to be treated as an unpacked JAR. The
* directory may be accessed for further processing via the provided file.
*
* @param file The directory containing the unpacked JAR.
* @param webappPath The path, if any, to the file within the web
* application
* @param isWebapp Indicates if the JAR was found within a web
* application. If <code>false</code> the JAR should
* be treated as being provided by the container
*
* @throws IOException if an I/O error occurs while scanning the JAR
*/
public void scan(File file, String webappPath, boolean isWebapp) throws IOException;
/**
* A directory structure was found within the web application at
* /WEB-INF/classes that should be handled as an unpacked JAR. Note that all
* resource access must be via the ServletContext to ensure that any
* additional resources are visible.
*
* @throws IOException if an I/O error occurs while scanning WEB-INF/classes
*/
public void scanWebInfClasses() throws IOException;
}

View File

@@ -0,0 +1,25 @@
/*
* 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;
public interface PeriodicEventListener {
/**
* Execute a periodic task, such as reloading, etc.
*/
public void periodicEvent();
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
import java.lang.reflect.InvocationTargetException;
import javax.naming.NamingException;
/**
* SimpleInstanceManager
*
* Implement the org.apache.tomcat.InstanceManager interface.
*/
public class SimpleInstanceManager implements InstanceManager {
public SimpleInstanceManager() {
}
@Override
public Object newInstance(Class<?> clazz) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException, NoSuchMethodException {
return prepareInstance(clazz.getConstructor().newInstance());
}
@Override
public Object newInstance(String className) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
return prepareInstance(clazz.getConstructor().newInstance());
}
@Override
public Object newInstance(String fqcn, ClassLoader classLoader) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = classLoader.loadClass(fqcn);
return prepareInstance(clazz.getConstructor().newInstance());
}
@Override
public void newInstance(Object o) throws IllegalAccessException, InvocationTargetException,
NamingException {
// NO-OP
}
@Override
public void destroyInstance(Object o) throws IllegalAccessException, InvocationTargetException {
}
private Object prepareInstance(Object o) {
return o;
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.buildutil;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
/**
* Ant task that checks that all the files in the given fileset have end-of-line
* delimiters that are appropriate for the current OS.
*
* <p>
* The goal is to check whether we have problems with svn:eol-style property
* when files are committed on one OS and then checked on another one.
*/
public class CheckEol extends Task {
/** The files to be checked */
private final List<FileSet> filesets = new LinkedList<>();
/**
* Sets the files to be checked
*
* @param fs The fileset to be checked.
*/
public void addFileset( FileSet fs ) {
filesets.add( fs );
}
/**
* Perform the check
*
* @throws BuildException if an error occurs during execution of
* this task.
*/
@Override
public void execute() throws BuildException {
Mode mode = null;
if ("\n".equals(System.lineSeparator())) {
mode = Mode.LF;
} else if ("\r\n".equals(System.lineSeparator())) {
mode = Mode.CRLF;
} else {
log("Line ends check skipped, because OS line ends setting is neither LF nor CRLF.",
Project.MSG_VERBOSE);
return;
}
int count = 0;
List<CheckFailure> errors = new ArrayList<>();
// Step through each file and check.
for (FileSet fs : filesets) {
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
File basedir = ds.getBasedir();
String[] files = ds.getIncludedFiles();
if (files.length > 0) {
log("Checking line ends in " + files.length + " file(s)");
for (int i = 0; i < files.length; i++) {
File file = new File(basedir, files[i]);
log("Checking file '" + file + "' for correct line ends",
Project.MSG_DEBUG);
try {
check(file, errors, mode);
} catch (IOException e) {
throw new BuildException("Could not check file '"
+ file.getAbsolutePath() + "'", e);
}
count++;
}
}
}
if (count > 0) {
log("Done line ends check in " + count + " file(s), "
+ errors.size() + " error(s) found.");
}
if (errors.size() > 0) {
String message = "The following files have wrong line ends: "
+ errors;
// We need to explicitly write the message to the log, because
// long BuildException messages may be trimmed. E.g. I observed
// this problem with Eclipse IDE 3.7.
log(message, Project.MSG_ERR);
throw new BuildException(message);
}
}
private enum Mode {
LF, CRLF
}
private static class CheckFailure {
private final File file;
private final int line;
private final String value;
public CheckFailure(File file, int line, String value) {
this.file = file;
this.line = line;
this.value = value;
}
@Override
public String toString() {
return System.lineSeparator() + file + ": uses " + value + " on line " + line;
}
}
private void check(File file, List<CheckFailure> errors, Mode mode) throws IOException {
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream is = new BufferedInputStream(fis)) {
int line = 1;
int prev = -1;
int ch;
while ((ch = is.read()) != -1) {
if (ch == '\n') {
if (mode == Mode.LF && prev == '\r') {
errors.add(new CheckFailure(file, line, "CRLF"));
return;
} else if (mode == Mode.CRLF && prev != '\r') {
errors.add(new CheckFailure(file, line, "LF"));
return;
}
line++;
} else if (prev == '\r') {
errors.add(new CheckFailure(file, line, "CR"));
return;
}
prev = ch;
}
}
}
}

View File

@@ -0,0 +1,413 @@
/*
* 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.buildutil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Ant task that submits a file to the Digicert (formally Symantec) code-signing
* service. The service is defined by the published
* <a href="https://api.ws.digicert.com/webtrust/SigningService?wsdl">WSDL</a>.
* Note that while the service has migrated to a Digicert domain, the namespace
* continues to use a Symantec domain.
*/
public class SignCode extends Task {
private static final URL SIGNING_SERVICE_URL;
private static final String NS = "cod";
private static final MessageFactory SOAP_MSG_FACTORY;
static {
try {
SIGNING_SERVICE_URL = new URL(
"https://api-appsec.pki.digicert.com/webtrust/SigningService");
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
try {
SOAP_MSG_FACTORY = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
} catch (SOAPException e) {
throw new IllegalArgumentException(e);
}
}
private final List<FileSet> filesets = new ArrayList<>();
private String userName;
private String password;
private String partnerCode;
private String keyStore;
private String keyStorePassword;
private String applicationName;
private String applicationVersion;
private String signingService;
private boolean debug;
public void addFileset(FileSet fileset) {
filesets.add(fileset);
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
public void setPartnerCode(String partnerCode) {
this.partnerCode = partnerCode;
}
public void setKeyStore(String keyStore) {
this.keyStore = keyStore;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public void setApplicationVersion(String applicationVersion) {
this.applicationVersion = applicationVersion;
}
public void setSigningService(String signingService) {
this.signingService = signingService;
}
public void setDebug(String debug) {
this.debug = Boolean.parseBoolean(debug);
}
@Override
public void execute() throws BuildException {
List<File> filesToSign = new ArrayList<>();
// Process the filesets and populate the list of files that need to be
// signed.
for (FileSet fileset : filesets) {
DirectoryScanner ds = fileset.getDirectoryScanner(getProject());
File basedir = ds.getBasedir();
String[] files = ds.getIncludedFiles();
if (files.length > 0) {
for (int i = 0; i < files.length; i++) {
File file = new File(basedir, files[i]);
filesToSign.add(file);
}
}
}
// Set up the TLS client
System.setProperty("javax.net.ssl.keyStore", keyStore);
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
try {
String signingSetID = makeSigningRequest(filesToSign);
downloadSignedFiles(filesToSign, signingSetID);
} catch (SOAPException | IOException e) {
throw new BuildException(e);
}
}
private String makeSigningRequest(List<File> filesToSign) throws SOAPException, IOException {
log("Constructing the code signing request");
SOAPMessage message = SOAP_MSG_FACTORY.createMessage();
SOAPBody body = populateEnvelope(message, NS);
SOAPElement requestSigning = body.addChildElement("requestSigning", NS);
SOAPElement requestSigningRequest =
requestSigning.addChildElement("requestSigningRequest", NS);
addCredentials(requestSigningRequest, this.userName, this.password, this.partnerCode);
SOAPElement applicationName =
requestSigningRequest.addChildElement("applicationName", NS);
applicationName.addTextNode(this.applicationName);
SOAPElement applicationVersion =
requestSigningRequest.addChildElement("applicationVersion", NS);
applicationVersion.addTextNode(this.applicationVersion);
SOAPElement signingServiceName =
requestSigningRequest.addChildElement("signingServiceName", NS);
signingServiceName.addTextNode(this.signingService);
List<String> fileNames = getFileNames(filesToSign);
SOAPElement commaDelimitedFileNames =
requestSigningRequest.addChildElement("commaDelimitedFileNames", NS);
commaDelimitedFileNames.addTextNode(StringUtils.join(fileNames));
SOAPElement application =
requestSigningRequest.addChildElement("application", NS);
application.addTextNode(getApplicationString(fileNames, filesToSign));
// Send the message
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = soapConnectionFactory.createConnection();
log("Sending signing request to server and waiting for response");
SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL);
if (debug) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 * 1024);
response.writeTo(baos);
log(baos.toString("UTF-8"));
}
log("Processing response");
SOAPElement responseBody = response.getSOAPBody();
// Should come back signed
NodeList bodyNodes = responseBody.getChildNodes();
NodeList requestSigningResponseNodes = bodyNodes.item(0).getChildNodes();
NodeList returnNodes = requestSigningResponseNodes.item(0).getChildNodes();
String signingSetID = null;
String signingSetStatus = null;
for (int i = 0; i < returnNodes.getLength(); i++) {
Node returnNode = returnNodes.item(i);
if (returnNode.getLocalName().equals("signingSetID")) {
signingSetID = returnNode.getTextContent();
} else if (returnNode.getLocalName().equals("signingSetStatus")) {
signingSetStatus = returnNode.getTextContent();
}
}
if (!signingService.contains("TEST") && !"SIGNED".equals(signingSetStatus) ||
signingService.contains("TEST") && !"INITIALIZED".equals(signingSetStatus) ) {
throw new BuildException("Signing failed. Status was: " + signingSetStatus);
}
return signingSetID;
}
private void downloadSignedFiles(List<File> filesToSign, String id)
throws SOAPException, IOException {
log("Downloading signed files. The signing set ID is: " + id);
SOAPMessage message = SOAP_MSG_FACTORY.createMessage();
SOAPBody body = populateEnvelope(message, NS);
SOAPElement getSigningSetDetails = body.addChildElement("getSigningSetDetails", NS);
SOAPElement getSigningSetDetailsRequest =
getSigningSetDetails.addChildElement("getSigningSetDetailsRequest", NS);
addCredentials(getSigningSetDetailsRequest, this.userName, this.password, this.partnerCode);
SOAPElement signingSetID =
getSigningSetDetailsRequest.addChildElement("signingSetID", NS);
signingSetID.addTextNode(id);
SOAPElement returnApplication =
getSigningSetDetailsRequest.addChildElement("returnApplication", NS);
returnApplication.addTextNode("true");
// Send the message
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = soapConnectionFactory.createConnection();
log("Requesting signed files from server and waiting for response");
SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL);
log("Processing response");
SOAPElement responseBody = response.getSOAPBody();
// Check for success
// Extract the signed file(s) from the ZIP
NodeList bodyNodes = responseBody.getChildNodes();
NodeList getSigningSetDetailsResponseNodes = bodyNodes.item(0).getChildNodes();
NodeList returnNodes = getSigningSetDetailsResponseNodes.item(0).getChildNodes();
String result = null;
String data = null;
for (int i = 0; i < returnNodes.getLength(); i++) {
Node returnNode = returnNodes.item(i);
if (returnNode.getLocalName().equals("result")) {
result = returnNode.getChildNodes().item(0).getTextContent();
} else if (returnNode.getLocalName().equals("signingSet")) {
data = returnNode.getChildNodes().item(1).getTextContent();
}
}
if (!"0".equals(result)) {
throw new BuildException("Download failed. Result code was: " + result);
}
extractFilesFromApplicationString(data, filesToSign);
}
private static SOAPBody populateEnvelope(SOAPMessage message, String namespace)
throws SOAPException {
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration(
"soapenv","http://schemas.xmlsoap.org/soap/envelope/");
envelope.addNamespaceDeclaration(
namespace,"http://api.ws.symantec.com/webtrust/codesigningservice");
return envelope.getBody();
}
private static void addCredentials(SOAPElement requestSigningRequest,
String user, String pwd, String code) throws SOAPException {
SOAPElement authToken = requestSigningRequest.addChildElement("authToken", NS);
SOAPElement userName = authToken.addChildElement("userName", NS);
userName.addTextNode(user);
SOAPElement password = authToken.addChildElement("password", NS);
password.addTextNode(pwd);
SOAPElement partnerCode = authToken.addChildElement("partnerCode", NS);
partnerCode.addTextNode(code);
}
/**
* Signing service requires unique files names. Since files will be returned
* in order, use dummy names that we know are unique but retain the file
* extension since the signing service appears to use it to figure out what
* to sign and how to sign it.
*/
private static List<String> getFileNames(List<File> filesToSign) {
List<String> result = new ArrayList<>(filesToSign.size());
for (int i = 0; i < filesToSign.size(); i++) {
File f = filesToSign.get(i);
String fileName = f.getName();
int extIndex = fileName.lastIndexOf('.');
String newName;
if (extIndex < 0) {
newName = Integer.toString(i);
} else {
newName = Integer.toString(i) + fileName.substring(extIndex);
}
result.add(newName);
}
return result;
}
/**
* Zips the files, base 64 encodes the resulting zip and then returns the
* string. It would be far more efficient to stream this directly to the
* signing server but the files that need to be signed are relatively small
* and this simpler to write.
*
* @param fileNames Modified names of files
* @param files Files to be signed
*/
private static String getApplicationString(List<String> fileNames, List<File> files)
throws IOException {
// 16 MB should be more than enough for Tomcat
// TODO: Refactoring this entire class so it uses streaming rather than
// buffering the entire set of files in memory would make it more
// widely useful.
ByteArrayOutputStream baos = new ByteArrayOutputStream(16 * 1024 * 1024);
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
byte[] buf = new byte[32 * 1024];
for (int i = 0; i < files.size(); i++) {
try (FileInputStream fis = new FileInputStream(files.get(i))) {
ZipEntry zipEntry = new ZipEntry(fileNames.get(i));
zos.putNextEntry(zipEntry);
int numRead;
while ( (numRead = fis.read(buf)) >= 0) {
zos.write(buf, 0, numRead);
}
}
}
}
return Base64.encodeBase64String(baos.toByteArray());
}
/**
* Removes base64 encoding, unzips the files and writes the new files over
* the top of the old ones.
*/
private static void extractFilesFromApplicationString(String data, List<File> files)
throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(data));
try (ZipInputStream zis = new ZipInputStream(bais)) {
byte[] buf = new byte[32 * 1024];
for (int i = 0; i < files.size(); i ++) {
try (FileOutputStream fos = new FileOutputStream(files.get(i))) {
zis.getNextEntry();
int numRead;
while ( (numRead = zis.read(buf)) >= 0) {
fos.write(buf, 0 , numRead);
}
}
}
}
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.buildutil;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
/**
* Ant task to convert a given set of files from Text to HTML.
* Inserts an HTML header including pre tags and replaces special characters
* with their HTML escaped equivalents.
*
* <p>This task is currently used by the ant script to build our examples</p>
*
* @author Mark Roth
*/
public class Txt2Html
extends Task
{
/** The directory to contain the resulting files */
private File todir;
/** The file to be converted into HTML */
private final List<FileSet> filesets = new LinkedList<>();
/**
* The encoding of the source files (.java and .jsp). Once they use
* UTF-8, this will need to be updated.
*/
private static final String SOURCE_ENCODING = "ISO-8859-1";
/**
* Line terminator to be used for separating lines of the generated
* HTML page, to be independent from "line.separator" system property.
*/
private static final String LINE_SEPARATOR = "\r\n";
/**
* Sets the directory to contain the resulting files
*
* @param todir The directory
*/
public void setTodir( File todir ) {
this.todir = todir;
}
/**
* Sets the files to be converted into HTML
*
* @param fs The fileset to be converted.
*/
public void addFileset( FileSet fs ) {
filesets.add( fs );
}
/**
* Perform the conversion
*
* @throws BuildException if an error occurs during execution of
* this task.
*/
@Override
public void execute()
throws BuildException
{
int count = 0;
// Step through each file and convert.
for (FileSet fs : filesets) {
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
File basedir = ds.getBasedir();
String[] files = ds.getIncludedFiles();
for( int i = 0; i < files.length; i++ ) {
File from = new File( basedir, files[i] );
File to = new File( todir, files[i] + ".html" );
if( !to.exists() ||
(from.lastModified() > to.lastModified()) )
{
log( "Converting file '" + from.getAbsolutePath() +
"' to '" + to.getAbsolutePath(), Project.MSG_VERBOSE );
try {
convert( from, to );
}
catch( IOException e ) {
throw new BuildException( "Could not convert '" +
from.getAbsolutePath() + "' to '" +
to.getAbsolutePath() + "'", e );
}
count++;
}
}
if( count > 0 ) {
log( "Converted " + count + " file" + (count > 1 ? "s" : "") +
" to " + todir.getAbsolutePath() );
}
}
}
/**
* Perform the actual copy and conversion
*
* @param from The input file
* @param to The output file
* @throws IOException Thrown if an error occurs during the conversion
*/
private void convert( File from, File to )
throws IOException
{
// Open files:
try (BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(from), SOURCE_ENCODING))) {
try (PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(to), "UTF-8"))) {
// Output header:
out.print("<!DOCTYPE html><html><head><meta charset=\"UTF-8\" />"
+ "<title>Source Code</title></head><body><pre>" );
// Convert, line-by-line:
String line;
while( (line = in.readLine()) != null ) {
StringBuilder result = new StringBuilder();
int len = line.length();
for( int i = 0; i < len; i++ ) {
char c = line.charAt( i );
switch( c ) {
case '&':
result.append( "&amp;" );
break;
case '<':
result.append( "&lt;" );
break;
default:
result.append( c );
}
}
out.print( result.toString() + LINE_SEPARATOR );
}
// Output footer:
out.print( "</pre></body></html>" );
}
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.buildutil.translate;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Base class providing common implementation for back-port utilities.
*/
public abstract class BackportBase {
protected final Map<String,Properties> sourceTranslations = new HashMap<>();
protected final Map<String,Properties> targetTranslations = new HashMap<>();
protected final File targetRoot;
protected final Properties sourceEnglish;
protected final Properties targetEnglish;
protected final File storageDir;
protected BackportBase(String... args) throws IOException {
if (args.length != 1) {
throw new IllegalArgumentException("Missing back-port target");
}
targetRoot = new File(args[0]);
if (!targetRoot.isDirectory()) {
throw new IllegalArgumentException("Back-port target not a directory");
}
File sourceRoot = new File(".");
for (String dir : Constants.SEARCH_DIRS) {
File directory = new File(dir);
Utils.processDirectory(sourceRoot, directory, sourceTranslations);
}
for (String dir : Constants.SEARCH_DIRS) {
File directory = new File(targetRoot, dir);
Utils.processDirectory(targetRoot, directory, targetTranslations);
}
sourceEnglish = sourceTranslations.get("");
targetEnglish = targetTranslations.get("");
storageDir = new File(targetRoot, Constants.STORAGE_DIR);
}
protected abstract void execute() throws IOException;
}

View File

@@ -0,0 +1,51 @@
/*
* 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.buildutil.translate;
import java.io.IOException;
/**
* Generates a set of English property files to back-port updates to a previous
* version. Where a key exists in the source and target versions the value is
* copied from the source to the target, overwriting the value in the target.
* The expectation is that the changes will be manually reviewed before
* committing them.
*/
public class BackportEnglish extends BackportBase {
public static void main(String... args) throws IOException {
BackportEnglish backport = new BackportEnglish(args);
backport.execute();
}
protected BackportEnglish(String[] args) throws IOException {
super(args);
}
@Override
protected void execute() throws IOException {
for (Object key : sourceEnglish.keySet()) {
if (targetEnglish.containsKey(key)) {
targetEnglish.put(key, sourceEnglish.get(key));
}
}
Utils.export("", targetEnglish, storageDir);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.buildutil.translate;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* Generates a set of translated property files to back-port updates to a
* previous version. If the source and target use the same value for the English
* key then any translated value for that key is copied from the source to the
* target.
*/
public class BackportTranslations extends BackportBase {
public static void main(String... args) throws IOException {
BackportTranslations backport = new BackportTranslations(args);
backport.execute();
}
protected BackportTranslations(String[] args) throws IOException {
super(args);
}
@Override
protected void execute() throws IOException {
for (String langauge : targetTranslations.keySet()) {
// Skip source
if (langauge.length() == 0) {
continue;
}
Properties sourceTranslated = sourceTranslations.get(langauge);
Properties targetTranslated = targetTranslations.get(langauge);
if (targetTranslated == null) {
targetTranslated = new Properties();
targetTranslations.put(langauge, targetTranslated);
}
for (Object key : targetEnglish.keySet()) {
if (sourceTranslated.containsKey(key) &&
targetEnglish.get(key).equals(sourceEnglish.get(key))) {
targetTranslated.put(key, sourceTranslated.get(key));
}
}
// Remove translated values for keys that have been removed
Iterator<Map.Entry<Object,Object>> iter = targetTranslated.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Object,Object> entry = iter.next();
if (!targetEnglish.containsKey(entry.getKey())) {
iter.remove();
}
}
Utils.export(langauge, targetTranslated, storageDir);
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.buildutil.translate;
public class Constants {
public static final String L10N_PREFIX = "LocalStrings";
public static final String L10N_SUFFIX = ".properties";
public static final String[] SEARCH_DIRS = new String[] { "java", "webapps" };
public static final String STORAGE_DIR = ".settings/translations";
public static final String END_PACKAGE_MARKER = ".zzz.";
}

View File

@@ -0,0 +1,133 @@
/*
* 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.buildutil.translate;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Properties;
public class Import {
public static void main(String... args) throws IOException {
File root = new File(Constants.STORAGE_DIR);
for (File f : root.listFiles()) {
// Not robust but good enough
if (f.isFile() && f.getName().startsWith(Constants.L10N_PREFIX)) {
processFile(f);
}
}
}
@SuppressWarnings("null")
private static void processFile(File f) throws IOException {
String language = Utils.getLanguage(f.getName());
// Unlike the master branch, don't skip the original so we can import
// updates to the English translations
Properties props = Utils.load(f);
Object[] objKeys = props.keySet().toArray();
Arrays.sort(objKeys);
String currentPkg = null;
Writer w = null;
String currentGroup = "zzz";
for (Object objKey : objKeys) {
String key = (String) objKey;
CompositeKey cKey = new CompositeKey(key);
if (!cKey.pkg.equals(currentPkg)) {
currentPkg = cKey.pkg;
if (w != null) {
w.close();
}
File outFile = new File(currentPkg.replace('.', File.separatorChar), Constants.L10N_PREFIX + language + Constants.L10N_SUFFIX);
FileOutputStream fos = new FileOutputStream(outFile);
w = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
insertLicense(w);
}
if (!currentGroup.equals(cKey.group)) {
currentGroup = cKey.group;
w.write(System.lineSeparator());
}
w.write(cKey.key + "=" + Utils.formatValue(props.getProperty(key)));
w.write(System.lineSeparator());
}
if (w != null) {
w.close();
}
}
private static void insertLicense(Writer w) throws IOException {
w.write("# Licensed to the Apache Software Foundation (ASF) under one or more");
w.write(System.lineSeparator());
w.write("# contributor license agreements. See the NOTICE file distributed with");
w.write(System.lineSeparator());
w.write("# this work for additional information regarding copyright ownership.");
w.write(System.lineSeparator());
w.write("# The ASF licenses this file to You under the Apache License, Version 2.0");
w.write(System.lineSeparator());
w.write("# (the \"License\"); you may not use this file except in compliance with");
w.write(System.lineSeparator());
w.write("# the License. You may obtain a copy of the License at");
w.write(System.lineSeparator());
w.write("#");
w.write(System.lineSeparator());
w.write("# http://www.apache.org/licenses/LICENSE-2.0");
w.write(System.lineSeparator());
w.write("#");
w.write(System.lineSeparator());
w.write("# Unless required by applicable law or agreed to in writing, software");
w.write(System.lineSeparator());
w.write("# distributed under the License is distributed on an \"AS IS\" BASIS,");
w.write(System.lineSeparator());
w.write("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
w.write(System.lineSeparator());
w.write("# See the License for the specific language governing permissions and");
w.write(System.lineSeparator());
w.write("# limitations under the License.");
w.write(System.lineSeparator());
}
private static class CompositeKey {
public final String pkg;
public final String key;
public final String group;
public CompositeKey(String in) {
int posPkg = in.indexOf(Constants.END_PACKAGE_MARKER);
pkg = in.substring(0, posPkg);
key = in.substring(posPkg + Constants.END_PACKAGE_MARKER.length());
int posGroup = key.indexOf('.');
if (posGroup == -1) {
group = "";
} else {
group = key.substring(0, posGroup);
}
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.buildutil.translate;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
public class Utils {
private static final Pattern ADD_CONTINUATION = Pattern.compile("\\n", Pattern.MULTILINE);
private static final Pattern ESCAPE_LEADING_SPACE = Pattern.compile("^(\\s)", Pattern.MULTILINE);
private static final Pattern FIX_SINGLE_QUOTE = Pattern.compile("(?<!')'(?!')", Pattern.MULTILINE);
private Utils() {
// Utility class. Hide default constructor.
}
static String getLanguage(String name) {
return name.substring(Constants.L10N_PREFIX.length(), name.length() - Constants.L10N_SUFFIX.length());
}
static Properties load(File f) {
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream(f);
Reader r = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
props.load(r);
} catch (IOException e) {
e.printStackTrace();
}
return props;
}
static String formatValue(String in) {
String result = ADD_CONTINUATION.matcher(in).replaceAll("\\\\n\\\\\n");
if (result.endsWith("\\\n")) {
result = result.substring(0, result.length() - 2);
}
result = ESCAPE_LEADING_SPACE.matcher(result).replaceAll("\\\\$1");
if (result.contains("\n\\\t")) {
result = result.replace("\n\\\t", "\n\\t");
}
if (result.contains("[{0}]")) {
result = FIX_SINGLE_QUOTE.matcher(result).replaceAll("''");
}
return result;
}
static void processDirectory(File root, File dir, Map<String,Properties> translations) throws IOException {
File[] files = dir.listFiles();
if (files == null) {
throw new IllegalArgumentException("Not a directory [" + dir.getAbsolutePath() + "]");
}
for (File f : files) {
if (f.isDirectory()) {
processDirectory(root, f, translations);
} else if (f.isFile()) {
processFile(root, f, translations);
}
}
}
static void processFile(File root, File f, Map<String,Properties> translations) throws IOException {
String name = f.getName();
// non-l10n files
if (!name.startsWith(Constants.L10N_PREFIX)) {
return;
}
// Determine language
String language = Utils.getLanguage(name);
String keyPrefix = getKeyPrefix(root, f);
Properties props = Utils.load(f);
// Create a Map for the language if one does not exist.
Properties translation = translations.get(language);
if (translation == null) {
translation = new Properties();
translations.put(language, translation);
}
// Add the properties from this file to the combined file, prefixing the
// key with the package name to ensure uniqueness.
for (Object obj : props.keySet()) {
String key = (String) obj;
String value = props.getProperty(key);
translation.put(keyPrefix + key, value);
}
}
static String getKeyPrefix(File root, File f) throws IOException {
String prefix = f.getParentFile().getCanonicalPath();
prefix = prefix.substring(root.getCanonicalPath().length() + 1);
prefix = prefix.replace(File.separatorChar, '.');
prefix = prefix + Constants.END_PACKAGE_MARKER;
return prefix;
}
static void export(String language, Properties translation, File storageDir) {
File out = new File(storageDir, Constants.L10N_PREFIX + language + Constants.L10N_SUFFIX);
try (FileOutputStream fos = new FileOutputStream(out);
Writer w = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
String[] keys = translation.keySet().toArray(new String[0]);
Arrays.sort(keys);
for (Object key : keys) {
w.write(key + "=" + Utils.formatValue(translation.getProperty((String) key)) + "\n");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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.dbcp.dbcp2;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.TrackedUse;
/**
* Tracks db connection usage for recovering and reporting abandoned db connections.
* <p>
* The JDBC Connection, Statement, and ResultSet classes extend this class.
* </p>
*
* @since 2.0
*/
public class AbandonedTrace implements TrackedUse {
/** A list of objects created by children of this object. */
private final List<WeakReference<AbandonedTrace>> traceList = new ArrayList<>();
/** Last time this connection was used. */
private volatile long lastUsedMillis = 0;
/**
* Creates a new AbandonedTrace without config and without doing abandoned tracing.
*/
public AbandonedTrace() {
init(null);
}
/**
* Constructs a new AbandonedTrace with a parent object.
*
* @param parent
* AbandonedTrace parent object.
*/
public AbandonedTrace(final AbandonedTrace parent) {
init(parent);
}
/**
* Adds an object to the list of objects being traced.
*
* @param trace
* AbandonedTrace object to add.
*/
protected void addTrace(final AbandonedTrace trace) {
synchronized (this.traceList) {
this.traceList.add(new WeakReference<>(trace));
}
setLastUsed();
}
/**
* Clears the list of objects being traced by this object.
*/
protected void clearTrace() {
synchronized (this.traceList) {
this.traceList.clear();
}
}
/**
* Gets the last time this object was used in milliseconds.
*
* @return long time in milliseconds.
*/
@Override
public long getLastUsed() {
return lastUsedMillis;
}
/**
* Gets a list of objects being traced by this object.
*
* @return List of objects.
*/
protected List<AbandonedTrace> getTrace() {
final int size = traceList.size();
if (size == 0) {
return Collections.emptyList();
}
final ArrayList<AbandonedTrace> result = new ArrayList<>(size);
synchronized (this.traceList) {
final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
while (iter.hasNext()) {
final AbandonedTrace trace = iter.next().get();
if (trace == null) {
// Clean-up since we are here anyway
iter.remove();
} else {
result.add(trace);
}
}
}
return result;
}
/**
* Initializes abandoned tracing for this object.
*
* @param parent
* AbandonedTrace parent object.
*/
private void init(final AbandonedTrace parent) {
if (parent != null) {
parent.addTrace(this);
}
}
/**
* Removes this object the source object is tracing.
*
* @param source The object tracing
* @since 2.7.0
*/
protected void removeThisTrace(final Object source) {
if (source instanceof AbandonedTrace) {
AbandonedTrace.class.cast(source).removeTrace(this);
}
}
/**
* Removes a child object this object is tracing.
*
* @param trace
* AbandonedTrace object to remove.
*/
protected void removeTrace(final AbandonedTrace trace) {
synchronized (this.traceList) {
final Iterator<WeakReference<AbandonedTrace>> iter = traceList.iterator();
while (iter.hasNext()) {
final AbandonedTrace traceInList = iter.next().get();
if (trace != null && trace.equals(traceInList)) {
iter.remove();
break;
} else if (traceInList == null) {
// Clean-up since we are here anyway
iter.remove();
}
}
}
}
/**
* Sets the time this object was last used to the current time in milliseconds.
*/
protected void setLastUsed() {
lastUsedMillis = System.currentTimeMillis();
}
/**
* Sets the time in milliseconds this object was last used.
*
* @param lastUsedMillis
* time in milliseconds.
*/
protected void setLastUsed(final long lastUsedMillis) {
this.lastUsedMillis = lastUsedMillis;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,602 @@
/*
* 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.dbcp.dbcp2;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.impl.BaseObjectPoolConfig;
import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPoolConfig;
/**
* <p>
* JNDI object factory that creates an instance of <code>BasicDataSource</code> that has been configured based on the
* <code>RefAddr</code> values of the specified <code>Reference</code>, which must match the names and data types of the
* <code>BasicDataSource</code> bean properties with the following exceptions:
* </p>
* <ul>
* <li><code>connectionInitSqls</code> must be passed to this factory as a single String using semi-colon to delimit the
* statements whereas <code>BasicDataSource</code> requires a collection of Strings.</li>
* </ul>
*
* @since 2.0
*/
public class BasicDataSourceFactory implements ObjectFactory {
private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
private static final String PROP_DEFAULT_AUTO_COMMIT = "defaultAutoCommit";
private static final String PROP_DEFAULT_READ_ONLY = "defaultReadOnly";
private static final String PROP_DEFAULT_TRANSACTION_ISOLATION = "defaultTransactionIsolation";
private static final String PROP_DEFAULT_CATALOG = "defaultCatalog";
private static final String PROP_DEFAULT_SCHEMA = "defaultSchema";
private static final String PROP_CACHE_STATE = "cacheState";
private static final String PROP_DRIVER_CLASS_NAME = "driverClassName";
private static final String PROP_LIFO = "lifo";
private static final String PROP_MAX_TOTAL = "maxTotal";
private static final String PROP_MAX_IDLE = "maxIdle";
private static final String PROP_MIN_IDLE = "minIdle";
private static final String PROP_INITIAL_SIZE = "initialSize";
private static final String PROP_MAX_WAIT_MILLIS = "maxWaitMillis";
private static final String PROP_TEST_ON_CREATE = "testOnCreate";
private static final String PROP_TEST_ON_BORROW = "testOnBorrow";
private static final String PROP_TEST_ON_RETURN = "testOnReturn";
private static final String PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis";
private static final String PROP_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
private static final String PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis";
private static final String PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "softMinEvictableIdleTimeMillis";
private static final String PROP_EVICTION_POLICY_CLASS_NAME = "evictionPolicyClassName";
private static final String PROP_TEST_WHILE_IDLE = "testWhileIdle";
private static final String PROP_PASSWORD = "password";
private static final String PROP_URL = "url";
private static final String PROP_USER_NAME = "username";
private static final String PROP_VALIDATION_QUERY = "validationQuery";
private static final String PROP_VALIDATION_QUERY_TIMEOUT = "validationQueryTimeout";
private static final String PROP_JMX_NAME = "jmxName";
private static final String PROP_CONNECTION_FACTORY_CLASS_NAME = "connectionFactoryClassName";
/**
* The property name for connectionInitSqls. The associated value String must be of the form [query;]*
*/
private static final String PROP_CONNECTION_INIT_SQLS = "connectionInitSqls";
private static final String PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED = "accessToUnderlyingConnectionAllowed";
private static final String PROP_REMOVE_ABANDONED_ON_BORROW = "removeAbandonedOnBorrow";
private static final String PROP_REMOVE_ABANDONED_ON_MAINTENANCE = "removeAbandonedOnMaintenance";
private static final String PROP_REMOVE_ABANDONED_TIMEOUT = "removeAbandonedTimeout";
private static final String PROP_LOG_ABANDONED = "logAbandoned";
private static final String PROP_ABANDONED_USAGE_TRACKING = "abandonedUsageTracking";
private static final String PROP_POOL_PREPARED_STATEMENTS = "poolPreparedStatements";
private static final String PROP_MAX_OPEN_PREPARED_STATEMENTS = "maxOpenPreparedStatements";
private static final String PROP_CONNECTION_PROPERTIES = "connectionProperties";
private static final String PROP_MAX_CONN_LIFETIME_MILLIS = "maxConnLifetimeMillis";
private static final String PROP_LOG_EXPIRED_CONNECTIONS = "logExpiredConnections";
private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
private static final String PROP_ENABLE_AUTO_COMMIT_ON_RETURN = "enableAutoCommitOnReturn";
private static final String PROP_DEFAULT_QUERY_TIMEOUT = "defaultQueryTimeout";
private static final String PROP_FAST_FAIL_VALIDATION = "fastFailValidation";
/**
* Value string must be of the form [STATE_CODE,]*
*/
private static final String PROP_DISCONNECTION_SQL_CODES = "disconnectionSqlCodes";
/*
* Block with obsolete properties from DBCP 1.x. Warn users that these are ignored and they should use the 2.x
* properties.
*/
private static final String NUPROP_MAX_ACTIVE = "maxActive";
private static final String NUPROP_REMOVE_ABANDONED = "removeAbandoned";
private static final String NUPROP_MAXWAIT = "maxWait";
/*
* Block with properties expected in a DataSource This props will not be listed as ignored - we know that they may
* appear in Resource, and not listing them as ignored.
*/
private static final String SILENT_PROP_FACTORY = "factory";
private static final String SILENT_PROP_SCOPE = "scope";
private static final String SILENT_PROP_SINGLETON = "singleton";
private static final String SILENT_PROP_AUTH = "auth";
private static final String[] ALL_PROPERTIES = {PROP_DEFAULT_AUTO_COMMIT, PROP_DEFAULT_READ_ONLY,
PROP_DEFAULT_TRANSACTION_ISOLATION, PROP_DEFAULT_CATALOG, PROP_DEFAULT_SCHEMA, PROP_CACHE_STATE,
PROP_DRIVER_CLASS_NAME, PROP_LIFO, PROP_MAX_TOTAL, PROP_MAX_IDLE, PROP_MIN_IDLE, PROP_INITIAL_SIZE,
PROP_MAX_WAIT_MILLIS, PROP_TEST_ON_CREATE, PROP_TEST_ON_BORROW, PROP_TEST_ON_RETURN,
PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS, PROP_NUM_TESTS_PER_EVICTION_RUN, PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS,
PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, PROP_EVICTION_POLICY_CLASS_NAME, PROP_TEST_WHILE_IDLE, PROP_PASSWORD,
PROP_URL, PROP_USER_NAME, PROP_VALIDATION_QUERY, PROP_VALIDATION_QUERY_TIMEOUT, PROP_CONNECTION_INIT_SQLS,
PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED, PROP_REMOVE_ABANDONED_ON_BORROW, PROP_REMOVE_ABANDONED_ON_MAINTENANCE,
PROP_REMOVE_ABANDONED_TIMEOUT, PROP_LOG_ABANDONED, PROP_ABANDONED_USAGE_TRACKING, PROP_POOL_PREPARED_STATEMENTS,
PROP_MAX_OPEN_PREPARED_STATEMENTS, PROP_CONNECTION_PROPERTIES, PROP_MAX_CONN_LIFETIME_MILLIS,
PROP_LOG_EXPIRED_CONNECTIONS, PROP_ROLLBACK_ON_RETURN, PROP_ENABLE_AUTO_COMMIT_ON_RETURN,
PROP_DEFAULT_QUERY_TIMEOUT, PROP_FAST_FAIL_VALIDATION, PROP_DISCONNECTION_SQL_CODES, PROP_JMX_NAME,
PROP_CONNECTION_FACTORY_CLASS_NAME };
/**
* Obsolete properties from DBCP 1.x. with warning strings suggesting new properties. LinkedHashMap will guarantee
* that properties will be listed to output in order of insertion into map.
*/
private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
static {
NUPROP_WARNTEXT.put(NUPROP_MAX_ACTIVE,
"Property " + NUPROP_MAX_ACTIVE + " is not used in DBCP2, use " + PROP_MAX_TOTAL + " instead. "
+ PROP_MAX_TOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + ".");
NUPROP_WARNTEXT.put(NUPROP_REMOVE_ABANDONED,
"Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2," + " use one or both of "
+ PROP_REMOVE_ABANDONED_ON_BORROW + " or " + PROP_REMOVE_ABANDONED_ON_MAINTENANCE + " instead. "
+ "Both have default value set to false.");
NUPROP_WARNTEXT.put(NUPROP_MAXWAIT,
"Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAX_WAIT_MILLIS + " instead. "
+ PROP_MAX_WAIT_MILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS
+ ".");
}
/**
* Silent Properties. These properties will not be listed as ignored - we know that they may appear in JDBC Resource
* references, and we will not list them as ignored.
*/
private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
static {
SILENT_PROPERTIES.add(SILENT_PROP_FACTORY);
SILENT_PROPERTIES.add(SILENT_PROP_SCOPE);
SILENT_PROPERTIES.add(SILENT_PROP_SINGLETON);
SILENT_PROPERTIES.add(SILENT_PROP_AUTH);
}
// -------------------------------------------------- ObjectFactory Methods
/**
* <p>
* Create and return a new <code>BasicDataSource</code> instance. If no instance can be created, return
* <code>null</code> instead.
* </p>
*
* @param obj
* The possibly null object containing location or reference information that can be used in creating an
* object
* @param name
* The name of this object relative to <code>nameCtx</code>
* @param nameCtx
* The context relative to which the <code>name</code> parameter is specified, or <code>null</code> if
* <code>name</code> is relative to the default initial context
* @param environment
* The possibly null environment that is used in creating this object
*
* @throws Exception
* if an exception occurs creating the instance
*/
@Override
public Object getObjectInstance(final Object obj, final Name name, final Context nameCtx,
final Hashtable<?, ?> environment) throws Exception {
// We only know how to deal with <code>javax.naming.Reference</code>s
// that specify a class name of "javax.sql.DataSource"
if (obj == null || !(obj instanceof Reference)) {
return null;
}
final Reference ref = (Reference) obj;
if (!"javax.sql.DataSource".equals(ref.getClassName())) {
return null;
}
// Check property names and log warnings about obsolete and / or unknown properties
final List<String> warnings = new ArrayList<>();
final List<String> infoMessages = new ArrayList<>();
validatePropertyNames(ref, name, warnings, infoMessages);
for (final String warning : warnings) {
log.warn(warning);
}
for (final String infoMessage : infoMessages) {
log.info(infoMessage);
}
final Properties properties = new Properties();
for (final String propertyName : ALL_PROPERTIES) {
final RefAddr ra = ref.get(propertyName);
if (ra != null) {
final String propertyValue = ra.getContent().toString();
properties.setProperty(propertyName, propertyValue);
}
}
return createDataSource(properties);
}
/**
* Collects warnings and info messages. Warnings are generated when an obsolete property is set. Unknown properties
* generate info messages.
*
* @param ref
* Reference to check properties of
* @param name
* Name provided to getObject
* @param warnings
* container for warning messages
* @param infoMessages
* container for info messages
*/
private void validatePropertyNames(final Reference ref, final Name name, final List<String> warnings,
final List<String> infoMessages) {
final List<String> allPropsAsList = Arrays.asList(ALL_PROPERTIES);
final String nameString = name != null ? "Name = " + name.toString() + " " : "";
if (NUPROP_WARNTEXT != null && !NUPROP_WARNTEXT.keySet().isEmpty()) {
for (final String propertyName : NUPROP_WARNTEXT.keySet()) {
final RefAddr ra = ref.get(propertyName);
if (ra != null && !allPropsAsList.contains(ra.getType())) {
final StringBuilder stringBuilder = new StringBuilder(nameString);
final String propertyValue = ra.getContent().toString();
stringBuilder.append(NUPROP_WARNTEXT.get(propertyName)).append(" You have set value of \"")
.append(propertyValue).append("\" for \"").append(propertyName)
.append("\" property, which is being ignored.");
warnings.add(stringBuilder.toString());
}
}
}
final Enumeration<RefAddr> allRefAddrs = ref.getAll();
while (allRefAddrs.hasMoreElements()) {
final RefAddr ra = allRefAddrs.nextElement();
final String propertyName = ra.getType();
// If property name is not in the properties list, we haven't warned on it
// and it is not in the "silent" list, tell user we are ignoring it.
if (!(allPropsAsList.contains(propertyName) || NUPROP_WARNTEXT.keySet().contains(propertyName)
|| SILENT_PROPERTIES.contains(propertyName))) {
final String propertyValue = ra.getContent().toString();
final StringBuilder stringBuilder = new StringBuilder(nameString);
stringBuilder.append("Ignoring unknown property: ").append("value of \"").append(propertyValue)
.append("\" for \"").append(propertyName).append("\" property");
infoMessages.add(stringBuilder.toString());
}
}
}
/**
* Creates and configures a {@link BasicDataSource} instance based on the given properties.
*
* @param properties
* The data source configuration properties.
* @return A new a {@link BasicDataSource} instance based on the given properties.
* @throws Exception
* Thrown when an error occurs creating the data source.
*/
public static BasicDataSource createDataSource(final Properties properties) throws Exception {
final BasicDataSource dataSource = new BasicDataSource();
String value = null;
value = properties.getProperty(PROP_DEFAULT_AUTO_COMMIT);
if (value != null) {
dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULT_READ_ONLY);
if (value != null) {
dataSource.setDefaultReadOnly(Boolean.valueOf(value));
}
value = properties.getProperty(PROP_DEFAULT_TRANSACTION_ISOLATION);
if (value != null) {
int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
if ("NONE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_NONE;
} else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_COMMITTED;
} else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_READ_UNCOMMITTED;
} else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_REPEATABLE_READ;
} else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
level = Connection.TRANSACTION_SERIALIZABLE;
} else {
try {
level = Integer.parseInt(value);
} catch (final NumberFormatException e) {
System.err.println("Could not parse defaultTransactionIsolation: " + value);
System.err.println("WARNING: defaultTransactionIsolation not set");
System.err.println("using default value of database driver");
level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
}
}
dataSource.setDefaultTransactionIsolation(level);
}
value = properties.getProperty(PROP_DEFAULT_CATALOG);
if (value != null) {
dataSource.setDefaultCatalog(value);
}
value = properties.getProperty(PROP_DEFAULT_SCHEMA);
if (value != null) {
dataSource.setDefaultSchema(value);
}
value = properties.getProperty(PROP_CACHE_STATE);
if (value != null) {
dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DRIVER_CLASS_NAME);
if (value != null) {
dataSource.setDriverClassName(value);
}
value = properties.getProperty(PROP_LIFO);
if (value != null) {
dataSource.setLifo(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_MAX_TOTAL);
if (value != null) {
dataSource.setMaxTotal(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAX_IDLE);
if (value != null) {
dataSource.setMaxIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MIN_IDLE);
if (value != null) {
dataSource.setMinIdle(Integer.parseInt(value));
}
value = properties.getProperty(PROP_INITIAL_SIZE);
if (value != null) {
dataSource.setInitialSize(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MAX_WAIT_MILLIS);
if (value != null) {
dataSource.setMaxWaitMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_TEST_ON_CREATE);
if (value != null) {
dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TEST_ON_BORROW);
if (value != null) {
dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TEST_ON_RETURN);
if (value != null) {
dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
if (value != null) {
dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_NUM_TESTS_PER_EVICTION_RUN);
if (value != null) {
dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
}
value = properties.getProperty(PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS);
if (value != null) {
dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
if (value != null) {
dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_EVICTION_POLICY_CLASS_NAME);
if (value != null) {
dataSource.setEvictionPolicyClassName(value);
}
value = properties.getProperty(PROP_TEST_WHILE_IDLE);
if (value != null) {
dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_PASSWORD);
if (value != null) {
dataSource.setPassword(value);
}
value = properties.getProperty(PROP_URL);
if (value != null) {
dataSource.setUrl(value);
}
value = properties.getProperty(PROP_USER_NAME);
if (value != null) {
dataSource.setUsername(value);
}
value = properties.getProperty(PROP_VALIDATION_QUERY);
if (value != null) {
dataSource.setValidationQuery(value);
}
value = properties.getProperty(PROP_VALIDATION_QUERY_TIMEOUT);
if (value != null) {
dataSource.setValidationQueryTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
if (value != null) {
dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_BORROW);
if (value != null) {
dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_MAINTENANCE);
if (value != null) {
dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_REMOVE_ABANDONED_TIMEOUT);
if (value != null) {
dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
}
value = properties.getProperty(PROP_LOG_ABANDONED);
if (value != null) {
dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_ABANDONED_USAGE_TRACKING);
if (value != null) {
dataSource.setAbandonedUsageTracking(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_POOL_PREPARED_STATEMENTS);
if (value != null) {
dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_MAX_OPEN_PREPARED_STATEMENTS);
if (value != null) {
dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
}
value = properties.getProperty(PROP_CONNECTION_INIT_SQLS);
if (value != null) {
dataSource.setConnectionInitSqls(parseList(value, ';'));
}
value = properties.getProperty(PROP_CONNECTION_PROPERTIES);
if (value != null) {
final Properties p = getProperties(value);
final Enumeration<?> e = p.propertyNames();
while (e.hasMoreElements()) {
final String propertyName = (String) e.nextElement();
dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
}
}
value = properties.getProperty(PROP_MAX_CONN_LIFETIME_MILLIS);
if (value != null) {
dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
}
value = properties.getProperty(PROP_LOG_EXPIRED_CONNECTIONS);
if (value != null) {
dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_JMX_NAME);
if (value != null) {
dataSource.setJmxName(value);
}
value = properties.getProperty(PROP_ENABLE_AUTO_COMMIT_ON_RETURN);
if (value != null) {
dataSource.setAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
if (value != null) {
dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DEFAULT_QUERY_TIMEOUT);
if (value != null) {
dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
}
value = properties.getProperty(PROP_FAST_FAIL_VALIDATION);
if (value != null) {
dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
}
value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
if (value != null) {
dataSource.setDisconnectionSqlCodes(parseList(value, ','));
}
value = properties.getProperty(PROP_CONNECTION_FACTORY_CLASS_NAME);
if (value != null) {
dataSource.setConnectionFactoryClassName(value);
}
// DBCP-215
// Trick to make sure that initialSize connections are created
if (dataSource.getInitialSize() > 0) {
dataSource.getLogWriter();
}
// Return the configured DataSource instance
return dataSource;
}
/**
* <p>
* Parse properties from the string. Format of the string must be [propertyName=property;]*
* <p>
*
* @param propText
* @return Properties
* @throws Exception
*/
private static Properties getProperties(final String propText) throws Exception {
final Properties p = new Properties();
if (propText != null) {
p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes(StandardCharsets.ISO_8859_1)));
}
return p;
}
/**
* Parse list of property values from a delimited string
*
* @param value
* delimited list of values
* @param delimiter
* character used to separate values in the list
* @return String Collection of values
*/
private static Collection<String> parseList(final String value, final char delimiter) {
final StringTokenizer tokenizer = new StringTokenizer(value, Character.toString(delimiter));
final Collection<String> tokens = new ArrayList<>(tokenizer.countTokens());
while (tokenizer.hasMoreTokens()) {
tokens.add(tokenizer.nextToken());
}
return tokens;
}
}

View File

@@ -0,0 +1,316 @@
/*
* 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.dbcp.dbcp2;
/**
* Defines the methods that will be made available via JMX.
*
* @since 2.0
*/
public interface BasicDataSourceMXBean {
/**
* See {@link BasicDataSource#getAbandonedUsageTracking()}
*
* @return {@link BasicDataSource#getAbandonedUsageTracking()}
*/
boolean getAbandonedUsageTracking();
/**
* See {@link BasicDataSource#getDefaultAutoCommit()}
*
* @return {@link BasicDataSource#getDefaultAutoCommit()}
*/
Boolean getDefaultAutoCommit();
/**
* See {@link BasicDataSource#getDefaultReadOnly()}
*
* @return {@link BasicDataSource#getDefaultReadOnly()}
*/
Boolean getDefaultReadOnly();
/**
* See {@link BasicDataSource#getDefaultTransactionIsolation()}
*
* @return {@link BasicDataSource#getDefaultTransactionIsolation()}
*/
int getDefaultTransactionIsolation();
/**
* See {@link BasicDataSource#getDefaultCatalog()}
*
* @return {@link BasicDataSource#getDefaultCatalog()}
*/
String getDefaultCatalog();
/**
* See {@link BasicDataSource#getDefaultSchema()}
*
* @return {@link BasicDataSource#getDefaultSchema()}
* @since 2.5.0
*/
String getDefaultSchema();
/**
* See {@link BasicDataSource#getCacheState()}
*
* @return {@link BasicDataSource#getCacheState()}
*/
boolean getCacheState();
/**
* See {@link BasicDataSource#getDriverClassName()}
*
* @return {@link BasicDataSource#getDriverClassName()}
*/
String getDriverClassName();
/**
* See {@link BasicDataSource#getLifo()}
*
* @return {@link BasicDataSource#getLifo()}
*/
boolean getLifo();
/**
* See {@link BasicDataSource#getMaxTotal()}
*
* @return {@link BasicDataSource#getMaxTotal()}
*/
int getMaxTotal();
/**
* See {@link BasicDataSource#getMaxIdle()}
*
* @return {@link BasicDataSource#getMaxIdle()}
*/
int getMaxIdle();
/**
* See {@link BasicDataSource#getMinIdle()}
*
* @return {@link BasicDataSource#getMinIdle()}
*/
int getMinIdle();
/**
* See {@link BasicDataSource#getInitialSize()}
*
* @return {@link BasicDataSource#getInitialSize()}
*/
int getInitialSize();
/**
* See {@link BasicDataSource#getMaxWaitMillis()}
*
* @return {@link BasicDataSource#getMaxWaitMillis()}
*/
long getMaxWaitMillis();
/**
* See {@link BasicDataSource#isPoolPreparedStatements()}
*
* @return {@link BasicDataSource#isPoolPreparedStatements()}
*/
boolean isPoolPreparedStatements();
/**
* See {@link BasicDataSource#getMaxOpenPreparedStatements()}
*
* @return {@link BasicDataSource#getMaxOpenPreparedStatements()}
*/
int getMaxOpenPreparedStatements();
/**
* See {@link BasicDataSource#getTestOnCreate()}
*
* @return {@link BasicDataSource#getTestOnCreate()}
*/
boolean getTestOnCreate();
/**
* See {@link BasicDataSource#getTestOnBorrow()}
*
* @return {@link BasicDataSource#getTestOnBorrow()}
*/
boolean getTestOnBorrow();
/**
* See {@link BasicDataSource#getTimeBetweenEvictionRunsMillis()}
*
* @return {@link BasicDataSource#getTimeBetweenEvictionRunsMillis()}
*/
long getTimeBetweenEvictionRunsMillis();
/**
* See {@link BasicDataSource#getNumTestsPerEvictionRun()}
*
* @return {@link BasicDataSource#getNumTestsPerEvictionRun()}
*/
int getNumTestsPerEvictionRun();
/**
* See {@link BasicDataSource#getMinEvictableIdleTimeMillis()}
*
* @return {@link BasicDataSource#getMinEvictableIdleTimeMillis()}
*/
long getMinEvictableIdleTimeMillis();
/**
* See {@link BasicDataSource#getSoftMinEvictableIdleTimeMillis()}
*
* @return {@link BasicDataSource#getSoftMinEvictableIdleTimeMillis()}
*/
long getSoftMinEvictableIdleTimeMillis();
/**
* See {@link BasicDataSource#getTestWhileIdle()}
*
* @return {@link BasicDataSource#getTestWhileIdle()}
*/
boolean getTestWhileIdle();
/**
* See {@link BasicDataSource#getNumActive()}
*
* @return {@link BasicDataSource#getNumActive()}
*/
int getNumActive();
/**
* See {@link BasicDataSource#getNumIdle()}
*
* @return {@link BasicDataSource#getNumIdle()}
*/
int getNumIdle();
/**
* See {@link BasicDataSource#getPassword()}
*
* @return {@link BasicDataSource#getPassword()}
*/
String getPassword();
/**
* See {@link BasicDataSource#getUrl()}
*
* @return {@link BasicDataSource#getUrl()}
*/
String getUrl();
/**
* See {@link BasicDataSource#getUsername()}
*
* @return {@link BasicDataSource#getUsername()}
*/
String getUsername();
/**
* See {@link BasicDataSource#getValidationQuery()}
*
* @return {@link BasicDataSource#getValidationQuery()}
*/
String getValidationQuery();
/**
* See {@link BasicDataSource#getValidationQueryTimeout()}
*
* @return {@link BasicDataSource#getValidationQueryTimeout()}
*/
int getValidationQueryTimeout();
/**
* See {@link BasicDataSource#getConnectionInitSqlsAsArray()}
*
* @return {@link BasicDataSource#getConnectionInitSqlsAsArray()}
*/
String[] getConnectionInitSqlsAsArray();
/**
* See {@link BasicDataSource#isAccessToUnderlyingConnectionAllowed()}
*
* @return {@link BasicDataSource#isAccessToUnderlyingConnectionAllowed()}
*/
boolean isAccessToUnderlyingConnectionAllowed();
/**
* See {@link BasicDataSource#getMaxConnLifetimeMillis()}
*
* @return {@link BasicDataSource#getMaxConnLifetimeMillis()}
*/
long getMaxConnLifetimeMillis();
/**
* See {@link BasicDataSource#getLogExpiredConnections()}
*
* @return {@link BasicDataSource#getLogExpiredConnections()}
* @since 2.1
*/
boolean getLogExpiredConnections();
/**
* See {@link BasicDataSource#getRemoveAbandonedOnBorrow()}
*
* @return {@link BasicDataSource#getRemoveAbandonedOnBorrow()}
*/
boolean getRemoveAbandonedOnBorrow();
/**
* See {@link BasicDataSource#getRemoveAbandonedOnMaintenance()}
*
* @return {@link BasicDataSource#getRemoveAbandonedOnMaintenance()}
*/
boolean getRemoveAbandonedOnMaintenance();
/**
* See {@link BasicDataSource#getRemoveAbandonedTimeout()}
*
* @return {@link BasicDataSource#getRemoveAbandonedTimeout()}
*/
int getRemoveAbandonedTimeout();
/**
* See {@link BasicDataSource#getLogAbandoned()}
*
* @return {@link BasicDataSource#getLogAbandoned()}
*/
boolean getLogAbandoned();
/**
* See {@link BasicDataSource#isClosed()}
*
* @return {@link BasicDataSource#isClosed()}
*/
boolean isClosed();
/**
* See {@link BasicDataSource#getFastFailValidation()}
*
* @return {@link BasicDataSource#getFastFailValidation()}
* @since 2.1
*/
boolean getFastFailValidation();
/**
* See {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
*
* @return {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
* @since 2.1
*/
String[] getDisconnectionSqlCodesAsArray();
}

View File

@@ -0,0 +1,36 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Abstract factory interface for creating {@link java.sql.Connection}s.
*
* @since 2.0
*/
public interface ConnectionFactory {
/**
* Create a new {@link java.sql.Connection} in an implementation specific fashion.
*
* @return a new {@link java.sql.Connection}
* @throws SQLException
* if a database error occurs creating the connection
*/
Connection createConnection() throws SQLException;
}

View File

@@ -0,0 +1,77 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
/*
* Creates {@link ConnectionFactory} instances.
*
* @since 2.7.0
*/
class ConnectionFactoryFactory {
/**
* Creates a new {@link DriverConnectionFactory} allowing for an override through
* {@link BasicDataSource#getDriverClassName()}.
*
* @param basicDataSource Configures creation.
* @param driver The JDBC driver.
* @return a new {@link DriverConnectionFactory} allowing for a {@link BasicDataSource#getDriverClassName()}
* override.
* @throws SQLException Thrown when instantiation fails.
*/
static ConnectionFactory createConnectionFactory(final BasicDataSource basicDataSource, final Driver driver)
throws SQLException {
final Properties connectionProperties = basicDataSource.getConnectionProperties();
final String url = basicDataSource.getUrl();
// Set up the driver connection factory we will use
final String user = basicDataSource.getUsername();
if (user != null) {
connectionProperties.put("user", user);
} else {
basicDataSource.log("DBCP DataSource configured without a 'username'");
}
final String pwd = basicDataSource.getPassword();
if (pwd != null) {
connectionProperties.put("password", pwd);
} else {
basicDataSource.log("DBCP DataSource configured without a 'password'");
}
final String connectionFactoryClassName = basicDataSource.getConnectionFactoryClassName();
if (connectionFactoryClassName != null) {
try {
final Class<?> connectionFactoryFromCCL = Class.forName(connectionFactoryClassName);
return (ConnectionFactory) connectionFactoryFromCCL
.getConstructor(Driver.class, String.class, Properties.class)
.newInstance(driver, url, connectionProperties);
} catch (final Exception t) {
final String message = "Cannot load ConnectionFactory implementation '" + connectionFactoryClassName
+ "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
// Defaults to DriverConnectionFactory
return new DriverConnectionFactory(driver, url, connectionProperties);
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.dbcp.dbcp2;
/**
* Constants for use with JMX.
*
* @since 2.0
*/
public class Constants {
public static final String JMX_CONNECTION_POOL_BASE_EXT = ",connectionpool=";
public static final String JMX_CONNECTION_POOL_PREFIX = "connections";
public static final String JMX_CONNECTION_BASE_EXT = JMX_CONNECTION_POOL_BASE_EXT + JMX_CONNECTION_POOL_PREFIX
+ ",connection=";
public static final String JMX_STATEMENT_POOL_BASE_EXT = JMX_CONNECTION_BASE_EXT;
public static final String JMX_STATEMENT_POOL_PREFIX = ",statementpool=statements";
}

View File

@@ -0,0 +1,111 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
/**
* A {@link DataSource}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DataSourceConnectionFactory implements ConnectionFactory {
private final DataSource dataSource;
private final String userName;
private final char[] userPassword;
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
*/
public DataSourceConnectionFactory(final DataSource dataSource) {
this(dataSource, null, (char[]) null);
}
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
* @param userName
* The user name.
* @param userPassword
* The user password.
* @since 2.4.0
*/
public DataSourceConnectionFactory(final DataSource dataSource, final String userName, final char[] userPassword) {
this.dataSource = dataSource;
this.userName = userName;
this.userPassword = Utils.clone(userPassword);
}
/**
* Constructs an instance for the given DataSource.
*
* @param dataSource
* The DataSource for this factory.
* @param userName
* The user name.
* @param password
* The user password.
*/
public DataSourceConnectionFactory(final DataSource dataSource, final String userName, final String password) {
this.dataSource = dataSource;
this.userName = userName;
this.userPassword = Utils.toCharArray(password);
}
@Override
public Connection createConnection() throws SQLException {
if (null == userName && null == userPassword) {
return dataSource.getConnection();
}
return dataSource.getConnection(userName, Utils.toString(userPassword));
}
/**
* @return The data source.
* @since 2.6.0
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* @return The user name.
* @since 2.6.0
*/
public String getUserName() {
return userName;
}
/**
* @return The user password.
* @since 2.6.0
*/
public char[] getUserPassword() {
return userPassword;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,654 @@
/*
* 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.dbcp.dbcp2;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
/**
* A base delegating implementation of {@link PreparedStatement}.
* <p>
* All of the methods from the {@link PreparedStatement} interface simply check to see that the
* {@link PreparedStatement} is active, and call the corresponding method on the "delegate" provided in my constructor.
* <p>
* Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
* Statement ensures that the Connection which created it can close any open Statement's on Connection close.
*
* @since 2.0
*/
public class DelegatingPreparedStatement extends DelegatingStatement implements PreparedStatement {
/**
* Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
* which created it.
*
* @param statement
* the {@link PreparedStatement} to delegate all calls to.
* @param connection
* the {@link DelegatingConnection} that created this statement.
*/
public DelegatingPreparedStatement(final DelegatingConnection<?> connection, final PreparedStatement statement) {
super(connection, statement);
}
@Override
public void addBatch() throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().addBatch();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void clearParameters() throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().clearParameters();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public boolean execute() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return getDelegatePreparedStatement().execute();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public ResultSet executeQuery() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return DelegatingResultSet.wrapResultSet(this, getDelegatePreparedStatement().executeQuery());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int executeUpdate() throws SQLException {
checkOpen();
if (getConnectionInternal() != null) {
getConnectionInternal().setLastUsed();
}
try {
return getDelegatePreparedStatement().executeUpdate();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
private PreparedStatement getDelegatePreparedStatement() {
return (PreparedStatement) getDelegate();
}
@Override
public ResultSetMetaData getMetaData() throws SQLException {
checkOpen();
try {
return getDelegatePreparedStatement().getMetaData();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public java.sql.ParameterMetaData getParameterMetaData() throws SQLException {
checkOpen();
try {
return getDelegatePreparedStatement().getParameterMetaData();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public void setArray(final int i, final Array x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setArray(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setAsciiStream(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setAsciiStream(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBigDecimal(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBinaryStream(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBinaryStream(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int i, final Blob x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(parameterIndex, inputStream);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBlob(final int parameterIndex, final InputStream inputStream, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBlob(parameterIndex, inputStream, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBoolean(final int parameterIndex, final boolean x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBoolean(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setByte(final int parameterIndex, final byte x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setByte(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setBytes(final int parameterIndex, final byte[] x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setBytes(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader, final int length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setCharacterStream(final int parameterIndex, final Reader reader, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setCharacterStream(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int i, final Clob x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setClob(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDate(final int parameterIndex, final Date x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDate(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDate(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setDouble(final int parameterIndex, final double x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setDouble(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFloat(final int parameterIndex, final float x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setFloat(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setInt(final int parameterIndex, final int x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setInt(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setLong(final int parameterIndex, final long x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setLong(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNCharacterStream(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNCharacterStream(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNCharacterStream(final int parameterIndex, final Reader value, final long length)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNCharacterStream(parameterIndex, value, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final NClob value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final Reader reader) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, reader);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNClob(parameterIndex, reader, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNString(final int parameterIndex, final String value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNString(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNull(final int parameterIndex, final int sqlType) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNull(parameterIndex, sqlType);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setNull(final int paramIndex, final int sqlType, final String typeName) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setNull(paramIndex, sqlType, typeName);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x, targetSqlType);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scale)
throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setObject(parameterIndex, x, targetSqlType, scale);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setRef(final int i, final Ref x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setRef(i, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setRowId(final int parameterIndex, final RowId value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setRowId(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setShort(final int parameterIndex, final short x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setShort(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setSQLXML(final int parameterIndex, final SQLXML value) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setSQLXML(parameterIndex, value);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setString(final int parameterIndex, final String x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setString(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTime(final int parameterIndex, final Time x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTime(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTime(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTimestamp(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setTimestamp(parameterIndex, x, cal);
} catch (final SQLException e) {
handleException(e);
}
}
/** @deprecated Use setAsciiStream(), setCharacterStream() or setNCharacterStream() */
@Deprecated
@Override
public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setUnicodeStream(parameterIndex, x, length);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setURL(final int parameterIndex, final java.net.URL x) throws SQLException {
checkOpen();
try {
getDelegatePreparedStatement().setURL(parameterIndex, x);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Returns a String representation of this object.
*
* @return String
*/
@SuppressWarnings("resource")
@Override
public synchronized String toString() {
final Statement statement = getDelegate();
return statement == null ? "NULL" : statement.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,710 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* A base delegating implementation of {@link Statement}.
* <p>
* All of the methods from the {@link Statement} interface simply check to see that the {@link Statement} is active, and
* call the corresponding method on the "delegate" provided in my constructor.
* <p>
* Extends AbandonedTrace to implement Statement tracking and logging of code which created the Statement. Tracking the
* Statement ensures that the Connection which created it can close any open Statement's on Connection close.
*
* @since 2.0
*/
public class DelegatingStatement extends AbandonedTrace implements Statement {
/** My delegate. */
private Statement statement;
/** The connection that created me. **/
private DelegatingConnection<?> connection;
private boolean closed = false;
/**
* Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code
* which created it.
*
* @param statement
* the {@link Statement} to delegate all calls to.
* @param connection
* the {@link DelegatingConnection} that created this statement.
*/
public DelegatingStatement(final DelegatingConnection<?> connection, final Statement statement) {
super(connection);
this.statement = statement;
this.connection = connection;
}
/**
*
* @throws SQLException
* thrown by the delegating statement.
* @since 2.4.0 made public, was protected in 2.3.0.
*/
public void activate() throws SQLException {
if (statement instanceof DelegatingStatement) {
((DelegatingStatement) statement).activate();
}
}
@Override
public void addBatch(final String sql) throws SQLException {
checkOpen();
try {
statement.addBatch(sql);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void cancel() throws SQLException {
checkOpen();
try {
statement.cancel();
} catch (final SQLException e) {
handleException(e);
}
}
protected void checkOpen() throws SQLException {
if (isClosed()) {
throw new SQLException(this.getClass().getName() + " with address: \"" + this.toString() + "\" is closed.");
}
}
@Override
public void clearBatch() throws SQLException {
checkOpen();
try {
statement.clearBatch();
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void clearWarnings() throws SQLException {
checkOpen();
try {
statement.clearWarnings();
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Close this DelegatingStatement, and close any ResultSets that were not explicitly closed.
*/
@Override
public void close() throws SQLException {
if (isClosed()) {
return;
}
final List<Exception> thrownList = new ArrayList<>();
try {
if (connection != null) {
connection.removeTrace(this);
connection = null;
}
// The JDBC spec requires that a statement close any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See bug 17301 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final int size = resultSetList.size();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[size]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
if (connection != null) {
// Does not rethrow e.
connection.handleExceptionNoThrow(e);
}
thrownList.add(e);
}
}
}
clearTrace();
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
if (connection != null) {
// Does not rethrow e.
connection.handleExceptionNoThrow(e);
}
thrownList.add(e);
}
}
} finally {
closed = true;
statement = null;
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
}
@Override
public void closeOnCompletion() throws SQLException {
checkOpen();
try {
Jdbc41Bridge.closeOnCompletion(statement);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public boolean execute(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, autoGeneratedKeys);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, columnIndexes);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean execute(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.execute(sql, columnNames);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public int[] executeBatch() throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeBatch();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public ResultSet executeQuery(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return DelegatingResultSet.wrapResultSet(this, statement.executeQuery(sql));
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int executeUpdate(final String sql) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, autoGeneratedKeys);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, columnIndexes);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int executeUpdate(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
setLastUsedInParent();
try {
return statement.executeUpdate(sql, columnNames);
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
protected void finalize() throws Throwable {
// This is required because of statement pooling. The poolable
// statements will always be strongly held by the statement pool. If the
// delegating statements that wrap the poolable statement are not
// strongly held they will be garbage collected but at that point the
// poolable statements need to be returned to the pool else there will
// be a leak of statements from the pool. Closing this statement will
// close all the wrapped statements and return any poolable statements
// to the pool.
close();
super.finalize();
}
@Override
public Connection getConnection() throws SQLException {
checkOpen();
return getConnectionInternal(); // return the delegating connection that created this
}
protected DelegatingConnection<?> getConnectionInternal() {
return connection;
}
/**
* Returns my underlying {@link Statement}.
*
* @return my underlying {@link Statement}.
* @see #getInnermostDelegate
*/
public Statement getDelegate() {
return statement;
}
@Override
public int getFetchDirection() throws SQLException {
checkOpen();
try {
return statement.getFetchDirection();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getFetchSize() throws SQLException {
checkOpen();
try {
return statement.getFetchSize();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public ResultSet getGeneratedKeys() throws SQLException {
checkOpen();
try {
return DelegatingResultSet.wrapResultSet(this, statement.getGeneratedKeys());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
/**
* If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively
* invokes this method on my delegate.
* <p>
* Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when
* no non-{@code DelegatingStatement} delegate can be found by traversing this chain.
* </p>
* <p>
* This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain
* a "genuine" {@link Statement}.
* </p>
*
* @return The innermost delegate.
*
* @see #getDelegate
*/
@SuppressWarnings("resource")
public Statement getInnermostDelegate() {
Statement s = statement;
while (s != null && s instanceof DelegatingStatement) {
s = ((DelegatingStatement) s).getDelegate();
if (this == s) {
return null;
}
}
return s;
}
@Override
public int getMaxFieldSize() throws SQLException {
checkOpen();
try {
return statement.getMaxFieldSize();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getMaxRows() throws SQLException {
checkOpen();
try {
return statement.getMaxRows();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public boolean getMoreResults() throws SQLException {
checkOpen();
try {
return statement.getMoreResults();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean getMoreResults(final int current) throws SQLException {
checkOpen();
try {
return statement.getMoreResults(current);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public int getQueryTimeout() throws SQLException {
checkOpen();
try {
return statement.getQueryTimeout();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public ResultSet getResultSet() throws SQLException {
checkOpen();
try {
return DelegatingResultSet.wrapResultSet(this, statement.getResultSet());
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
@Override
public int getResultSetConcurrency() throws SQLException {
checkOpen();
try {
return statement.getResultSetConcurrency();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getResultSetHoldability() throws SQLException {
checkOpen();
try {
return statement.getResultSetHoldability();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getResultSetType() throws SQLException {
checkOpen();
try {
return statement.getResultSetType();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public int getUpdateCount() throws SQLException {
checkOpen();
try {
return statement.getUpdateCount();
} catch (final SQLException e) {
handleException(e);
return 0;
}
}
@Override
public SQLWarning getWarnings() throws SQLException {
checkOpen();
try {
return statement.getWarnings();
} catch (final SQLException e) {
handleException(e);
throw new AssertionError();
}
}
protected void handleException(final SQLException e) throws SQLException {
if (connection != null) {
connection.handleException(e);
} else {
throw e;
}
}
/*
* Note: This method was protected prior to JDBC 4.
*/
@Override
public boolean isClosed() throws SQLException {
return closed;
}
protected boolean isClosedInternal() {
return closed;
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
checkOpen();
try {
return Jdbc41Bridge.isCloseOnCompletion(statement);
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean isPoolable() throws SQLException {
checkOpen();
try {
return statement.isPoolable();
} catch (final SQLException e) {
handleException(e);
return false;
}
}
@Override
public boolean isWrapperFor(final Class<?> iface) throws SQLException {
if (iface.isAssignableFrom(getClass())) {
return true;
} else if (iface.isAssignableFrom(statement.getClass())) {
return true;
} else {
return statement.isWrapperFor(iface);
}
}
/**
*
* @throws SQLException
* thrown by the delegating statement.
* @since 2.4.0 made public, was protected in 2.3.0.
*/
public void passivate() throws SQLException {
if (statement instanceof DelegatingStatement) {
((DelegatingStatement) statement).passivate();
}
}
protected void setClosedInternal(final boolean closed) {
this.closed = closed;
}
@Override
public void setCursorName(final String name) throws SQLException {
checkOpen();
try {
statement.setCursorName(name);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Sets my delegate.
*
* @param statement
* my delegate.
*/
public void setDelegate(final Statement statement) {
this.statement = statement;
}
@Override
public void setEscapeProcessing(final boolean enable) throws SQLException {
checkOpen();
try {
statement.setEscapeProcessing(enable);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFetchDirection(final int direction) throws SQLException {
checkOpen();
try {
statement.setFetchDirection(direction);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setFetchSize(final int rows) throws SQLException {
checkOpen();
try {
statement.setFetchSize(rows);
} catch (final SQLException e) {
handleException(e);
}
}
private void setLastUsedInParent() {
if (connection != null) {
connection.setLastUsed();
}
}
@Override
public void setMaxFieldSize(final int max) throws SQLException {
checkOpen();
try {
statement.setMaxFieldSize(max);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setMaxRows(final int max) throws SQLException {
checkOpen();
try {
statement.setMaxRows(max);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setPoolable(final boolean poolable) throws SQLException {
checkOpen();
try {
statement.setPoolable(poolable);
} catch (final SQLException e) {
handleException(e);
}
}
@Override
public void setQueryTimeout(final int seconds) throws SQLException {
checkOpen();
try {
statement.setQueryTimeout(seconds);
} catch (final SQLException e) {
handleException(e);
}
}
/**
* Returns a String representation of this object.
*
* @return String
*/
@Override
public synchronized String toString() {
return statement == null ? "NULL" : statement.toString();
}
@Override
public <T> T unwrap(final Class<T> iface) throws SQLException {
if (iface.isAssignableFrom(getClass())) {
return iface.cast(this);
} else if (iface.isAssignableFrom(statement.getClass())) {
return iface.cast(statement);
} else {
return statement.unwrap(iface);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
/**
* A {@link Driver}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DriverConnectionFactory implements ConnectionFactory {
private final String connectionString;
private final Driver driver;
private final Properties properties;
/**
* Constructs a connection factory for a given Driver.
*
* @param driver
* The Driver.
* @param connectString
* The connection string.
* @param properties
* The connection properties.
*/
public DriverConnectionFactory(final Driver driver, final String connectString, final Properties properties) {
this.driver = driver;
this.connectionString = connectString;
this.properties = properties;
}
@Override
public Connection createConnection() throws SQLException {
return driver.connect(connectionString, properties);
}
/**
* @return The connection String.
* @since 2.6.0
*/
public String getConnectionString() {
return connectionString;
}
/**
* @return The Driver.
* @since 2.6.0
*/
public Driver getDriver() {
return driver;
}
/**
* @return The Properties.
* @since 2.6.0
*/
public Properties getProperties() {
return properties;
}
@Override
public String toString() {
return this.getClass().getName() + " [" + String.valueOf(driver) + ";" + String.valueOf(connectionString) + ";"
+ String.valueOf(properties) + "]";
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
/*
* Creates {@link Driver} instances.
*
* @since 2.7.0
*/
class DriverFactory {
static Driver createDriver(final BasicDataSource basicDataSource) throws SQLException {
// Load the JDBC driver class
Driver driverToUse = basicDataSource.getDriver();
String driverClassName = basicDataSource.getDriverClassName();
ClassLoader driverClassLoader = basicDataSource.getDriverClassLoader();
String url = basicDataSource.getUrl();
if (driverToUse == null) {
Class<?> driverFromCCL = null;
if (driverClassName != null) {
try {
try {
if (driverClassLoader == null) {
driverFromCCL = Class.forName(driverClassName);
} else {
driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
}
} catch (final ClassNotFoundException cnfe) {
driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
}
} catch (final Exception t) {
final String message = "Cannot load JDBC driver class '" + driverClassName + "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
try {
if (driverFromCCL == null) {
driverToUse = DriverManager.getDriver(url);
} else {
// Usage of DriverManager is not possible, as it does not
// respect the ContextClassLoader
// N.B. This cast may cause ClassCastException which is
// handled below
driverToUse = (Driver) driverFromCCL.getConstructor().newInstance();
if (!driverToUse.acceptsURL(url)) {
throw new SQLException("No suitable driver", "08001");
}
}
} catch (final Exception t) {
final String message = "Cannot create JDBC driver of class '"
+ (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";
basicDataSource.log(message, t);
throw new SQLException(message, t);
}
}
return driverToUse;
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* A {@link DriverManager}-based implementation of {@link ConnectionFactory}.
*
* @since 2.0
*/
public class DriverManagerConnectionFactory implements ConnectionFactory {
static {
// Related to DBCP-212
// Driver manager does not sync loading of drivers that use the service
// provider interface. This will cause issues is multi-threaded
// environments. This hack makes sure the drivers are loaded before
// DBCP tries to use them.
DriverManager.getDrivers();
}
private final String connectionUri;
private final String userName;
private final char[] userPassword;
private final Properties properties;
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @since 2.2
*/
public DriverManagerConnectionFactory(final String connectionUri) {
this.connectionUri = connectionUri;
this.properties = new Properties();
this.userName = null;
this.userPassword = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code> jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param properties
* a list of arbitrary string tag/value pairs as connection arguments; normally at least a "user" and
* "password" property should be included.
*/
public DriverManagerConnectionFactory(final String connectionUri, final Properties properties) {
this.connectionUri = connectionUri;
this.properties = properties;
this.userName = null;
this.userPassword = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param userName
* the database user
* @param userPassword
* the user's password
*/
public DriverManagerConnectionFactory(final String connectionUri, final String userName,
final char[] userPassword) {
this.connectionUri = connectionUri;
this.userName = userName;
this.userPassword = Utils.clone(userPassword);
this.properties = null;
}
/**
* Constructor for DriverManagerConnectionFactory.
*
* @param connectionUri
* a database url of the form <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
* @param userName
* the database user
* @param userPassword
* the user's password
*/
public DriverManagerConnectionFactory(final String connectionUri, final String userName,
final String userPassword) {
this.connectionUri = connectionUri;
this.userName = userName;
this.userPassword = Utils.toCharArray(userPassword);
this.properties = null;
}
@Override
public Connection createConnection() throws SQLException {
if (null == properties) {
if (userName == null && userPassword == null) {
return DriverManager.getConnection(connectionUri);
}
return DriverManager.getConnection(connectionUri, userName, Utils.toString(userPassword));
}
return DriverManager.getConnection(connectionUri, properties);
}
/**
* @return The connection URI.
* @since 2.6.0
*/
public String getConnectionUri() {
return connectionUri;
}
/**
* @return The Properties.
* @since 2.6.0
*/
public Properties getProperties() {
return properties;
}
/**
* @return The user name.
* @since 2.6.0
*/
public String getUserName() {
return userName;
}
}

View File

@@ -0,0 +1,482 @@
/*
* 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.dbcp.dbcp2;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.sql.CommonDataSource;
/**
* Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
* {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
*
* @since 2.6.0
*/
public class Jdbc41Bridge {
/**
* Delegates to {@link Connection#abort(Executor)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
* </p>
*
* @param connection
* the receiver
* @param executor
* See {@link Connection#abort(Executor)}.
* @throws SQLException
* See {@link Connection#abort(Executor)}.
* @see Connection#abort(Executor)
*/
public static void abort(final Connection connection, final Executor executor) throws SQLException {
try {
connection.abort(executor);
} catch (final AbstractMethodError e) {
connection.close();
}
}
/**
* Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
* {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
* </p>
*
* @param databaseMetaData
* See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @throws SQLException
* See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
* @see DatabaseMetaData#generatedKeyAlwaysReturned()
*/
public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
try {
return databaseMetaData.generatedKeyAlwaysReturned();
} catch (final AbstractMethodError e) {
// do nothing
return false;
}
}
/**
* Delegates to {@link Connection#getNetworkTimeout()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
* </p>
*
* @param connection
* the receiver
* @return See {@link Connection#getNetworkTimeout()}
* @throws SQLException
* See {@link Connection#getNetworkTimeout()}
* @see Connection#getNetworkTimeout()
*/
public static int getNetworkTimeout(final Connection connection) throws SQLException {
try {
return connection.getNetworkTimeout();
} catch (final AbstractMethodError e) {
return 0;
}
}
/**
* Delegates to {@link ResultSet#getObject(int, Class)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
* </p>
*
* @param <T>
* See {@link ResultSet#getObject(int, Class)}
* @param resultSet
* See {@link ResultSet#getObject(int, Class)}
* @param columnIndex
* See {@link ResultSet#getObject(int, Class)}
* @param type
* See {@link ResultSet#getObject(int, Class)}
* @return See {@link ResultSet#getObject(int, Class)}
* @throws SQLException
* See {@link ResultSet#getObject(int, Class)}
* @see ResultSet#getObject(int, Class)
*/
@SuppressWarnings("unchecked")
public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
throws SQLException {
try {
return resultSet.getObject(columnIndex, type);
} catch (final AbstractMethodError e) {
if (type == String.class) {
return (T) resultSet.getString(columnIndex);
}
// Numbers
if (type == Integer.class) {
return (T) Integer.valueOf(resultSet.getInt(columnIndex));
}
if (type == Long.class) {
return (T) Long.valueOf(resultSet.getLong(columnIndex));
}
if (type == Double.class) {
return (T) Double.valueOf(resultSet.getDouble(columnIndex));
}
if (type == Float.class) {
return (T) Float.valueOf(resultSet.getFloat(columnIndex));
}
if (type == Short.class) {
return (T) Short.valueOf(resultSet.getShort(columnIndex));
}
if (type == BigDecimal.class) {
return (T) resultSet.getBigDecimal(columnIndex);
}
if (type == Byte.class) {
return (T) Byte.valueOf(resultSet.getByte(columnIndex));
}
// Dates
if (type == Date.class) {
return (T) resultSet.getDate(columnIndex);
}
if (type == Time.class) {
return (T) resultSet.getTime(columnIndex);
}
if (type == Timestamp.class) {
return (T) resultSet.getTimestamp(columnIndex);
}
// Streams
if (type == InputStream.class) {
return (T) resultSet.getBinaryStream(columnIndex);
}
if (type == Reader.class) {
return (T) resultSet.getCharacterStream(columnIndex);
}
// Other
if (type == Object.class) {
return (T) resultSet.getObject(columnIndex);
}
if (type == Boolean.class) {
return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
}
if (type == Array.class) {
return (T) resultSet.getArray(columnIndex);
}
if (type == Blob.class) {
return (T) resultSet.getBlob(columnIndex);
}
if (type == Clob.class) {
return (T) resultSet.getClob(columnIndex);
}
if (type == Ref.class) {
return (T) resultSet.getRef(columnIndex);
}
if (type == RowId.class) {
return (T) resultSet.getRowId(columnIndex);
}
if (type == SQLXML.class) {
return (T) resultSet.getSQLXML(columnIndex);
}
if (type == URL.class) {
return (T) resultSet.getURL(columnIndex);
}
throw new SQLFeatureNotSupportedException(
String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, Integer.valueOf(columnIndex), type));
}
}
/**
* Delegates to {@link ResultSet#getObject(String, Class)} without throwing a {@link AbstractMethodError}.
*
* @param <T>
* See {@link ResultSet#getObject(String, Class)}
* @param resultSet
* See {@link ResultSet#getObject(String, Class)}
* @param columnLabel
* See {@link ResultSet#getObject(String, Class)}
* @param type
* See {@link ResultSet#getObject(String, Class)}
* @return See {@link ResultSet#getObject(String, Class)}
* @throws SQLException
* See {@link ResultSet#getObject(String, Class)}
* @see ResultSet#getObject(int, Class)
*/
@SuppressWarnings("unchecked")
public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
throws SQLException {
try {
return resultSet.getObject(columnLabel, type);
} catch (final AbstractMethodError e) {
// Numbers
if (type == Integer.class) {
return (T) Integer.valueOf(resultSet.getInt(columnLabel));
}
if (type == Long.class) {
return (T) Long.valueOf(resultSet.getLong(columnLabel));
}
if (type == Double.class) {
return (T) Double.valueOf(resultSet.getDouble(columnLabel));
}
if (type == Float.class) {
return (T) Float.valueOf(resultSet.getFloat(columnLabel));
}
if (type == Short.class) {
return (T) Short.valueOf(resultSet.getShort(columnLabel));
}
if (type == BigDecimal.class) {
return (T) resultSet.getBigDecimal(columnLabel);
}
if (type == Byte.class) {
return (T) Byte.valueOf(resultSet.getByte(columnLabel));
}
// Dates
if (type == Date.class) {
return (T) resultSet.getDate(columnLabel);
}
if (type == Time.class) {
return (T) resultSet.getTime(columnLabel);
}
if (type == Timestamp.class) {
return (T) resultSet.getTimestamp(columnLabel);
}
// Streams
if (type == InputStream.class) {
return (T) resultSet.getBinaryStream(columnLabel);
}
if (type == Reader.class) {
return (T) resultSet.getCharacterStream(columnLabel);
}
// Other
if (type == Object.class) {
return (T) resultSet.getObject(columnLabel);
}
if (type == Boolean.class) {
return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
}
if (type == Array.class) {
return (T) resultSet.getArray(columnLabel);
}
if (type == Blob.class) {
return (T) resultSet.getBlob(columnLabel);
}
if (type == Clob.class) {
return (T) resultSet.getClob(columnLabel);
}
if (type == Ref.class) {
return (T) resultSet.getRef(columnLabel);
}
if (type == RowId.class) {
return (T) resultSet.getRowId(columnLabel);
}
if (type == SQLXML.class) {
return (T) resultSet.getSQLXML(columnLabel);
}
if (type == URL.class) {
return (T) resultSet.getURL(columnLabel);
}
throw new SQLFeatureNotSupportedException(
String.format("resultSet=%s, columnLabel=%s, type=%s", resultSet, columnLabel, type));
}
}
/**
* Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
* {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
* then return null.
* </p>
*
* @param databaseMetaData
* the receiver
* @param catalog
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param schemaPattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param tableNamePattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @param columnNamePattern
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @throws SQLException
* See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
* @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
*/
public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
throws SQLException {
try {
return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
} catch (final AbstractMethodError e) {
// do nothing
return null;
}
}
/**
* Delegates to {@link Connection#getSchema()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
* </p>
*
* @param connection
* the receiver
* @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
* @throws SQLException
* See {@link Connection#getSchema()}.
* @see Connection#getSchema()
*/
public static String getSchema(final Connection connection) throws SQLException {
try {
return connection.getSchema();
} catch (final AbstractMethodError e) {
// do nothing
return null;
}
}
/**
* Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
* </p>
*
* @param connection
* the receiver
* @param executor
* See {@link Connection#setNetworkTimeout(Executor, int)}
* @param milliseconds
* {@link Connection#setNetworkTimeout(Executor, int)}
* @throws SQLException
* {@link Connection#setNetworkTimeout(Executor, int)}
* @see Connection#setNetworkTimeout(Executor, int)
*/
public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
throws SQLException {
try {
connection.setNetworkTimeout(executor, milliseconds);
} catch (final AbstractMethodError e) {
// do nothing
}
}
/**
* Delegates to {@link Connection#setSchema(String)} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
* </p>
*
* @param connection
* the receiver
* @param schema
* See {@link Connection#setSchema(String)}.
* @throws SQLException
* See {@link Connection#setSchema(String)}.
* @see Connection#setSchema(String)
*/
public static void setSchema(final Connection connection, final String schema) throws SQLException {
try {
connection.setSchema(schema);
} catch (final AbstractMethodError e) {
// do nothing
}
}
/**
* Delegates to {@link Statement#closeOnCompletion()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
* is closed to then throw an SQLException.
* </p>
*
* @param statement
* See {@link Statement#closeOnCompletion()}
* @throws SQLException
* See {@link Statement#closeOnCompletion()}
* @see Statement#closeOnCompletion()
*/
public static void closeOnCompletion(final Statement statement) throws SQLException {
try {
statement.closeOnCompletion();
} catch (final AbstractMethodError e) {
if (statement.isClosed()) {
throw new SQLException("Statement closed");
}
}
}
/**
* Delegates to {@link Statement#isCloseOnCompletion()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
* connection is closed to then throw an SQLException.
* </p>
*
* @param statement
* See {@link Statement#isCloseOnCompletion()}
* @return See {@link Statement#isCloseOnCompletion()}
* @throws SQLException
* See {@link Statement#isCloseOnCompletion()}
* @see Statement#closeOnCompletion()
*/
public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
try {
return statement.isCloseOnCompletion();
} catch (final AbstractMethodError e) {
if (statement.isClosed()) {
throw new SQLException("Statement closed");
}
return false;
}
}
/**
* Delegates to {@link CommonDataSource#getParentLogger()} without throwing a {@link AbstractMethodError}.
* <p>
* If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
* </p>
*
* @param commonDataSource
* See {@link CommonDataSource#getParentLogger()}
* @return See {@link CommonDataSource#getParentLogger()}
* @throws SQLFeatureNotSupportedException
* See {@link CommonDataSource#getParentLogger()}
*/
public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
try {
return commonDataSource.getParentLogger();
} catch (final AbstractMethodError e) {
throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
}
}
}

View File

@@ -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.tomcat.dbcp.dbcp2;
/**
* Exception thrown when a connection's maximum lifetime has been exceeded.
*
* @since 2.1
*/
class LifetimeExceededException extends Exception {
private static final long serialVersionUID = -3783783104516492659L;
/**
* Create a LifetimeExceededException.
*/
public LifetimeExceededException() {
super();
}
/**
* Create a LifetimeExceededException with the given message.
*
* @param message
* The message with which to create the exception
*/
public LifetimeExceededException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.dbcp.dbcp2;
import java.util.List;
/**
* An exception wrapping a list of exceptions.
*
* @since 2.4.0
*/
public class ListException extends Exception {
private static final long serialVersionUID = 1L;
private final List<Throwable> exceptionList;
/**
* Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently
* be initialized by a call to {@link #initCause}.
*
* @param message
* the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}
* method.
* @param exceptionList
* a list of exceptions.
*/
public ListException(final String message, final List<Throwable> exceptionList) {
super(message);
this.exceptionList = exceptionList;
}
/**
* Gets the list of exceptions.
*
* @return the list of exceptions.
*/
public List<Throwable> getExceptionList() {
return exceptionList;
}
}

View File

@@ -0,0 +1,26 @@
# 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.
connectionFactory.lifetimeExceeded=The lifetime of the connection [{0}] milliseconds exceeds the maximum permitted value of [{1}] milliseconds
pool.close.fail=Cannot close connection pool.
poolableConnection.validate.fastFail=Fatal SQLException was thrown previously on this connection.
poolableConnectionFactory.validateObject.fail=Failed to validate a poolable connection.
poolingDataSource.factoryConfig=PoolableConnectionFactory not linked to pool. Calling setPool() to fix the configuration.
swallowedExceptionLogger.onSwallowedException=An internal object pool swallowed an Exception.

View File

@@ -0,0 +1,16 @@
# 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.
poolableConnection.validate.fastFail=Fatale SQLException wurde bereits vorher von dieser Verbindung geworfen

View File

@@ -0,0 +1,26 @@
# 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.
connectionFactory.lifetimeExceeded=La durée de vie de la connection de [{0}] millisecondes excède la valeur maximale permis de [{1}] millisecondes
pool.close.fail=Impossible de fermer le pool de connections
poolableConnection.validate.fastFail=Une exception fatale SQLException avait déjà été lancée pour cette connection
poolableConnectionFactory.validateObject.fail=Impossible de valider la connection poolable
poolingDataSource.factoryConfig=La PoolableConnectionFactory n'est pas liée au pool, il faut appeler setPool() pour y remédier et corriger la configuration
swallowedExceptionLogger.onSwallowedException=Un object interne du pool a avalé une exception

View File

@@ -0,0 +1,26 @@
# 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.
connectionFactory.lifetimeExceeded=コネクション[{0}]ミリ秒の有効期間が[{1}]ミリ秒の許容最大値を超えています
pool.close.fail=コネクションプールを停止できません。
poolableConnection.validate.fastFail=このコネクションは過去に致命的な SQLException を送出したことがあります。
poolableConnectionFactory.validateObject.fail=プール可能なコネクションを検証できません。
poolingDataSource.factoryConfig=PoolableConnectionFactory がコネクションプールに接続していません。構成を修復するには setPool() を呼び出してください。
swallowedExceptionLogger.onSwallowedException=内部オブジェクトプールが例外を飲み込みました。

View File

@@ -0,0 +1,26 @@
# 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.
connectionFactory.lifetimeExceeded=해당 연결의 존속시간 [{0}] 밀리초가, 최대 허용치인 [{1}] 밀리초를 초과합니다.
pool.close.fail=데이터베이스 연결 풀을 닫을 수 없습니다.
poolableConnection.validate.fastFail=이 연결에서, 심각한 SQLException이 이전에 발생했습니다.
poolableConnectionFactory.validateObject.fail=Poolable connection이 유효한지 확인하지 못했습니다.
poolingDataSource.factoryConfig=PoolableConnectionFactory가 풀에 연결되지 않았습니다. setPool()을 호출하여 이 설정 문제를 해결합니다.
swallowedExceptionLogger.onSwallowedException=내부 객체 풀이 예외 발생을 무시했습니다.

View File

@@ -0,0 +1,20 @@
# 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.
poolableConnection.validate.fastFail=此连接上预先抛出了致命的 SQLException。
poolingDataSource.factoryConfig=PoolableConnectionFactory 未连接到连接池。请调用 setPool() 修复此配置。
swallowedExceptionLogger.onSwallowedException=一个内部对象池吞并了一个异常。

View File

@@ -0,0 +1,105 @@
/*
* 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.dbcp.dbcp2;
import java.lang.management.ManagementFactory;
import java.util.Objects;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* Internal wrapper class that allows JMX to be a noop if absent or disabled.
*
* @since 2.2.1
*/
class ObjectNameWrapper {
private static final Log log = LogFactory.getLog(ObjectNameWrapper.class);
private static MBeanServer MBEAN_SERVER = getPlatformMBeanServer();
private static MBeanServer getPlatformMBeanServer() {
try {
return ManagementFactory.getPlatformMBeanServer();
} catch (LinkageError | Exception e) {
// ignore - JMX not available
log.debug("Failed to get platform MBeanServer", e);
return null;
}
}
public static ObjectName unwrap(final ObjectNameWrapper wrapper) {
return wrapper == null ? null : wrapper.unwrap();
}
public static ObjectNameWrapper wrap(final ObjectName objectName) {
return new ObjectNameWrapper(objectName);
}
public static ObjectNameWrapper wrap(final String name) throws MalformedObjectNameException {
return wrap(new ObjectName(name));
}
private final ObjectName objectName;
public ObjectNameWrapper(final ObjectName objectName) {
this.objectName = objectName;
}
public void registerMBean(final Object object) {
if (MBEAN_SERVER == null || objectName == null) {
return;
}
try {
MBEAN_SERVER.registerMBean(object, objectName);
} catch (LinkageError | Exception e) {
log.warn("Failed to complete JMX registration for " + objectName, e);
}
}
/**
* @since 2.7.0
*/
@Override
public String toString() {
return Objects.toString(objectName);
}
public void unregisterMBean() {
if (MBEAN_SERVER == null || objectName == null) {
return;
}
if (MBEAN_SERVER.isRegistered(objectName)) {
try {
MBEAN_SERVER.unregisterMBean(objectName);
} catch (LinkageError | Exception e) {
log.warn("Failed to complete JMX unregistration for " + objectName, e);
}
}
}
public ObjectName unwrap() {
return objectName;
}
}

View File

@@ -0,0 +1,998 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import org.apache.tomcat.dbcp.dbcp2.PoolingConnection.StatementType;
/**
* A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
*
* @since 2.0
*/
public class PStmtKey {
/**
* Builder for prepareCall(String sql).
*/
private class PreparedCallSQL implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql);
}
}
/**
* Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency).
*/
private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
}
}
/**
* Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
*/
private class PreparedCallWithResultSetHoldability implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
resultSetHoldability.intValue());
}
}
/**
* Builder for prepareStatement(String sql).
*/
private class PreparedStatementSQL implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql);
}
}
/**
* Builder for prepareStatement(String sql, int autoGeneratedKeys).
*/
private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, autoGeneratedKeys.intValue());
}
}
/**
* Builder for prepareStatement(String sql, int[] columnIndexes).
*/
private class PreparedStatementWithColumnIndexes implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, columnIndexes);
}
}
/**
* Builder for prepareStatement(String sql, String[] columnNames).
*/
private class PreparedStatementWithColumnNames implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, columnNames);
}
}
/**
* Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency).
*/
private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
}
}
/**
* Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
*/
private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
@Override
public Statement createStatement(final Connection connection) throws SQLException {
return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
resultSetHoldability.intValue());
}
}
/**
* Interface for Prepared or Callable Statement.
*/
private interface StatementBuilder {
Statement createStatement(Connection connection) throws SQLException;
}
/**
* SQL defining Prepared or Callable Statement
*/
private final String sql;
/**
* Result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>,
* or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
*/
private final Integer resultSetType;
/**
* Result set concurrency. A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
private final Integer resultSetConcurrency;
/**
* Result set holdability. One of the following <code>ResultSet</code> constants:
* <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*/
private final Integer resultSetHoldability;
/** Database catalog. */
private final String catalog;
/** Database schema. */
private final String schema;
/**
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*/
private final Integer autoGeneratedKeys;
/**
* An array of column indexes indicating the columns that should be returned from the inserted row or rows.
*/
private final int[] columnIndexes;
/**
* An array of column names indicating the columns that should be returned from the inserted row or rows.
*/
private final String[] columnNames;
/**
* Statement type, prepared or callable.
*/
private final StatementType statementType;
/** Statement builder */
private transient StatementBuilder builder;
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @deprecated Use {@link #PStmtKey(String, String, String)}.
*/
@Deprecated
public PStmtKey(final String sql) {
this(sql, null, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @deprecated Use {@link #PStmtKey(String, String, String)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog) {
this(sql, catalog, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @deprecated Use @link {@link #PStmtKey(String, String, String, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)}
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetHoldability();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetHoldability();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetConcurrency();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetConcurrency();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
* @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
this.builder = new PreparedStatementWithColumnIndexes();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param statementType
* The SQL statement type, prepared or callable.
* @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType)}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementSQL();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param statementType
* The SQL statement type, prepared or callable.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)}
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final StatementType statementType,
final Integer autoGeneratedKeys) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = statementType;
this.autoGeneratedKeys = autoGeneratedKeys;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithAutoGeneratedKeys();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema) {
this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) {
this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) {
this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetHoldability();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetHoldability();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.resultSetType = Integer.valueOf(resultSetType);
this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
this.resultSetHoldability = null;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithResultSetConcurrency();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallWithResultSetConcurrency();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
*/
public PStmtKey(final String sql, final String catalog, final String schema, final int[] columnIndexes) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
this.builder = new PreparedStatementWithColumnIndexes();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param statementType
* The SQL statement type, prepared or callable.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = statementType;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementSQL();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param statementType
* The SQL statement type, prepared or callable.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType,
final Integer autoGeneratedKeys) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = statementType;
this.autoGeneratedKeys = autoGeneratedKeys;
this.columnIndexes = null;
this.columnNames = null;
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
if (statementType == StatementType.PREPARED_STATEMENT) {
this.builder = new PreparedStatementWithAutoGeneratedKeys();
} else if (statementType == StatementType.CALLABLE_STATEMENT) {
this.builder = new PreparedCallSQL();
}
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param schema
* The schema.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @since 2.5.0
*/
public PStmtKey(final String sql, final String catalog, final String schema, final String[] columnNames) {
this.sql = sql;
this.catalog = catalog;
this.schema = schema;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
builder = new PreparedStatementWithColumnNames();
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param catalog
* The catalog.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
*/
@Deprecated
public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
this.sql = sql;
this.catalog = catalog;
this.schema = null;
this.statementType = StatementType.PREPARED_STATEMENT;
this.autoGeneratedKeys = null;
this.columnIndexes = null;
this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
this.resultSetType = null;
this.resultSetConcurrency = null;
this.resultSetHoldability = null;
// create builder
builder = new PreparedStatementWithColumnNames();
}
/**
* Creates a new Statement from the given Connection.
*
* @param connection
* The Connection to use to create the statement.
* @return The statement.
* @throws SQLException
* Thrown when there is a problem creating the statement.
*/
public Statement createStatement(final Connection connection) throws SQLException {
if (builder == null) {
throw new IllegalStateException("Prepared statement key is invalid.");
}
return builder.createStatement(connection);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PStmtKey other = (PStmtKey) obj;
if (autoGeneratedKeys == null) {
if (other.autoGeneratedKeys != null) {
return false;
}
} else if (!autoGeneratedKeys.equals(other.autoGeneratedKeys)) {
return false;
}
if (catalog == null) {
if (other.catalog != null) {
return false;
}
} else if (!catalog.equals(other.catalog)) {
return false;
}
if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
return false;
}
if (!Arrays.equals(columnNames, other.columnNames)) {
return false;
}
if (resultSetConcurrency == null) {
if (other.resultSetConcurrency != null) {
return false;
}
} else if (!resultSetConcurrency.equals(other.resultSetConcurrency)) {
return false;
}
if (resultSetHoldability == null) {
if (other.resultSetHoldability != null) {
return false;
}
} else if (!resultSetHoldability.equals(other.resultSetHoldability)) {
return false;
}
if (resultSetType == null) {
if (other.resultSetType != null) {
return false;
}
} else if (!resultSetType.equals(other.resultSetType)) {
return false;
}
if (schema == null) {
if (other.schema != null) {
return false;
}
} else if (!schema.equals(other.schema)) {
return false;
}
if (sql == null) {
if (other.sql != null) {
return false;
}
} else if (!sql.equals(other.sql)) {
return false;
}
if (statementType != other.statementType) {
return false;
}
return true;
}
/**
* Gets a flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*
* @return a flag indicating whether auto-generated keys should be returned.
*/
public Integer getAutoGeneratedKeys() {
return autoGeneratedKeys;
}
/**
* The catalog.
*
* @return The catalog.
*/
public String getCatalog() {
return catalog;
}
/**
* Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
*
* @return An array of column indexes.
*/
public int[] getColumnIndexes() {
return columnIndexes;
}
/**
* Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
*
* @return An array of column names.
*/
public String[] getColumnNames() {
return columnNames;
}
/**
* Gets the result set concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*
* @return The result set concurrency type.
*/
public Integer getResultSetConcurrency() {
return resultSetConcurrency;
}
/**
* Gets the result set holdability, one of the following <code>ResultSet</code> constants:
* <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*
* @return The result set holdability.
*/
public Integer getResultSetHoldability() {
return resultSetHoldability;
}
/**
* Gets the result set type, one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
*
* @return the result set type.
*/
public Integer getResultSetType() {
return resultSetType;
}
/**
* The schema.
*
* @return The catalog.
*/
public String getSchema() {
return schema;
}
/**
* Gets the SQL statement.
*
* @return the SQL statement.
*/
public String getSql() {
return sql;
}
/**
* The SQL statement type.
*
* @return The SQL statement type.
*/
public StatementType getStmtType() {
return statementType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((autoGeneratedKeys == null) ? 0 : autoGeneratedKeys.hashCode());
result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
result = prime * result + Arrays.hashCode(columnIndexes);
result = prime * result + Arrays.hashCode(columnNames);
result = prime * result + ((resultSetConcurrency == null) ? 0 : resultSetConcurrency.hashCode());
result = prime * result + ((resultSetHoldability == null) ? 0 : resultSetHoldability.hashCode());
result = prime * result + ((resultSetType == null) ? 0 : resultSetType.hashCode());
result = prime * result + ((schema == null) ? 0 : schema.hashCode());
result = prime * result + ((sql == null) ? 0 : sql.hashCode());
result = prime * result + ((statementType == null) ? 0 : statementType.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer buf = new StringBuffer();
buf.append("PStmtKey: sql=");
buf.append(sql);
buf.append(", catalog=");
buf.append(catalog);
buf.append(", schema=");
buf.append(schema);
buf.append(", resultSetType=");
buf.append(resultSetType);
buf.append(", resultSetConcurrency=");
buf.append(resultSetConcurrency);
buf.append(", resultSetHoldability=");
buf.append(resultSetHoldability);
buf.append(", autoGeneratedKeys=");
buf.append(autoGeneratedKeys);
buf.append(", columnIndexes=");
buf.append(Arrays.toString(columnIndexes));
buf.append(", columnNames=");
buf.append(Arrays.toString(columnNames));
buf.append(", statementType=");
buf.append(statementType);
return buf.toString();
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.dbcp.dbcp2;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
/**
* A {@link DelegatingCallableStatement} that cooperates with {@link PoolingConnection} to implement a pool of
* {@link CallableStatement}s.
* <p>
* The {@link #close} method returns this statement to its containing pool. (See {@link PoolingConnection}.)
*
* @see PoolingConnection
* @since 2.0
*/
public class PoolableCallableStatement extends DelegatingCallableStatement {
/**
* The {@link KeyedObjectPool} from which this CallableStatement was obtained.
*/
private final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool;
/**
* Key for this statement in the containing {@link KeyedObjectPool}.
*/
private final PStmtKey key;
/**
* Constructor.
*
* @param callableStatement
* the underlying {@link CallableStatement}
* @param key
* the key for this statement in the {@link KeyedObjectPool}
* @param pool
* the {@link KeyedObjectPool} from which this CallableStatement was obtained
* @param connection
* the {@link DelegatingConnection} that created this CallableStatement
*/
public PoolableCallableStatement(final CallableStatement callableStatement, final PStmtKey key,
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool,
final DelegatingConnection<Connection> connection) {
super(connection, callableStatement);
this.pool = pool;
this.key = key;
// Remove from trace now because this statement will be
// added by the activate method.
removeThisTrace(getConnectionInternal());
}
/**
* Returns the CallableStatement to the pool. If {{@link #isClosed()}, this is a No-op.
*/
@Override
public void close() throws SQLException {
// calling close twice should have no effect
if (!isClosed()) {
try {
pool.returnObject(key, this);
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close CallableStatement (return to pool failed)", e);
}
}
}
/**
* Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection that created
* it.
*
* @since 2.4.0 made public, was protected in 2.3.0.
*/
@Override
public void activate() throws SQLException {
setClosedInternal(false);
if (getConnectionInternal() != null) {
getConnectionInternal().addTrace(this);
}
super.activate();
}
/**
* Passivates to prepare for return to the pool. Removes the trace associated with this CallableStatement from the
* Connection that created it. Also closes any associated ResultSets.
*
* @since 2.4.0 made public, was protected in 2.3.0.
*/
@Override
public void passivate() throws SQLException {
setClosedInternal(true);
removeThisTrace(getConnectionInternal());
// The JDBC spec requires that a statement close any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See DBCP-10 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final List<Exception> thrownList = new ArrayList<>();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[resultSetList.size()]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
thrownList.add(e);
}
}
}
clearTrace();
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
super.passivate();
}
}

View File

@@ -0,0 +1,348 @@
/*
* 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.dbcp.dbcp2;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
/**
* A delegating connection that, rather than closing the underlying connection, returns itself to an {@link ObjectPool}
* when closed.
*
* @since 2.0
*/
public class PoolableConnection extends DelegatingConnection<Connection> implements PoolableConnectionMXBean {
private static MBeanServer MBEAN_SERVER;
static {
try {
MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();
} catch (NoClassDefFoundError | Exception ex) {
// ignore - JMX not available
}
}
/** The pool to which I should return. */
private final ObjectPool<PoolableConnection> pool;
private final ObjectNameWrapper jmxObjectName;
// Use a prepared statement for validation, retaining the last used SQL to
// check if the validation query has changed.
private PreparedStatement validationPreparedStatement;
private String lastValidationSql;
/**
* Indicate that unrecoverable SQLException was thrown when using this connection. Such a connection should be
* considered broken and not pass validation in the future.
*/
private boolean fatalSqlExceptionThrown = false;
/**
* SQL_STATE codes considered to signal fatal conditions. Overrides the defaults in
* {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}).
*/
private final Collection<String> disconnectionSqlCodes;
/** Whether or not to fast fail validation after fatal connection errors */
private final boolean fastFailValidation;
/**
*
* @param conn
* my underlying connection
* @param pool
* the pool to which I should return when closed
* @param jmxObjectName
* JMX name
* @param disconnectSqlCodes
* SQL_STATE codes considered fatal disconnection errors
* @param fastFailValidation
* true means fatal disconnection errors cause subsequent validations to fail immediately (no attempt to
* run query or isValid)
*/
public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
final ObjectName jmxObjectName, final Collection<String> disconnectSqlCodes,
final boolean fastFailValidation) {
super(conn);
this.pool = pool;
this.jmxObjectName = ObjectNameWrapper.wrap(jmxObjectName);
this.disconnectionSqlCodes = disconnectSqlCodes;
this.fastFailValidation = fastFailValidation;
if (jmxObjectName != null) {
try {
MBEAN_SERVER.registerMBean(this, jmxObjectName);
} catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
// For now, simply skip registration
}
}
}
/**
*
* @param conn
* my underlying connection
* @param pool
* the pool to which I should return when closed
* @param jmxName
* JMX name
*/
public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
final ObjectName jmxName) {
this(conn, pool, jmxName, null, true);
}
@Override
protected void passivate() throws SQLException {
super.passivate();
setClosedInternal(true);
}
/**
* {@inheritDoc}
* <p>
* This method should not be used by a client to determine whether or not a connection should be return to the
* connection pool (by calling {@link #close()}). Clients should always attempt to return a connection to the pool
* once it is no longer required.
*/
@Override
public boolean isClosed() throws SQLException {
if (isClosedInternal()) {
return true;
}
if (getDelegateInternal().isClosed()) {
// Something has gone wrong. The underlying connection has been
// closed without the connection being returned to the pool. Return
// it now.
close();
return true;
}
return false;
}
/**
* Returns me to my pool.
*/
@Override
public synchronized void close() throws SQLException {
if (isClosedInternal()) {
// already closed
return;
}
boolean isUnderlyingConnectionClosed;
try {
isUnderlyingConnectionClosed = getDelegateInternal().isClosed();
} catch (final SQLException e) {
try {
pool.invalidateObject(this);
} catch (final IllegalStateException ise) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final Exception ie) {
// DO NOTHING the original exception will be rethrown
}
throw new SQLException("Cannot close connection (isClosed check failed)", e);
}
/*
* Can't set close before this code block since the connection needs to be open when validation runs. Can't set
* close after this code block since by then the connection will have been returned to the pool and may have
* been borrowed by another thread. Therefore, the close flag is set in passivate().
*/
if (isUnderlyingConnectionClosed) {
// Abnormal close: underlying connection closed unexpectedly, so we
// must destroy this proxy
try {
pool.invalidateObject(this);
} catch (final IllegalStateException e) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final Exception e) {
throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
}
} else {
// Normal close: underlying connection is still open, so we
// simply need to return this proxy to the pool
try {
pool.returnObject(this);
} catch (final IllegalStateException e) {
// pool is closed, so close the connection
passivate();
getInnermostDelegate().close();
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection (return to pool failed)", e);
}
}
}
/**
* Actually close my underlying {@link Connection}.
*/
@Override
public void reallyClose() throws SQLException {
if (jmxObjectName != null) {
jmxObjectName.unregisterMBean();
}
if (validationPreparedStatement != null) {
try {
validationPreparedStatement.close();
} catch (final SQLException sqle) {
// Ignore
}
}
super.closeInternal();
}
/**
* Expose the {@link #toString()} method via a bean getter so it can be read as a property via JMX.
*/
@Override
public String getToString() {
return toString();
}
/**
* Validates the connection, using the following algorithm:
* <ol>
* <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
* thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
* <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
* returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
* <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
* least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
* </ol>
*
* @param sql
* The validation SQL query.
* @param timeoutSeconds
* The validation timeout in seconds.
* @throws SQLException
* Thrown when validation fails or an SQLException occurs during validation
*/
public void validate(final String sql, int timeoutSeconds) throws SQLException {
if (fastFailValidation && fatalSqlExceptionThrown) {
throw new SQLException(Utils.getMessage("poolableConnection.validate.fastFail"));
}
if (sql == null || sql.length() == 0) {
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
if (!isValid(timeoutSeconds)) {
throw new SQLException("isValid() returned false");
}
return;
}
if (!sql.equals(lastValidationSql)) {
lastValidationSql = sql;
// Has to be the innermost delegate else the prepared statement will
// be closed when the pooled connection is passivated.
validationPreparedStatement = getInnermostDelegateInternal().prepareStatement(sql);
}
if (timeoutSeconds > 0) {
validationPreparedStatement.setQueryTimeout(timeoutSeconds);
}
try (ResultSet rs = validationPreparedStatement.executeQuery()) {
if (!rs.next()) {
throw new SQLException("validationQuery didn't return a row");
}
} catch (final SQLException sqle) {
throw sqle;
}
}
/**
* Checks the SQLState of the input exception and any nested SQLExceptions it wraps.
* <p>
* If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the
* configured list of fatal exception codes. If this property is not set, codes are compared against the default
* codes in {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
* Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
* </p>
*
* @param e
* SQLException to be examined
* @return true if the exception signals a disconnection
*/
private boolean isDisconnectionSqlException(final SQLException e) {
boolean fatalException = false;
final String sqlState = e.getSQLState();
if (sqlState != null) {
fatalException = disconnectionSqlCodes == null
? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX)
|| Utils.DISCONNECTION_SQL_CODES.contains(sqlState)
: disconnectionSqlCodes.contains(sqlState);
if (!fatalException) {
final SQLException nextException = e.getNextException();
if (nextException != null && nextException != e) {
fatalException = isDisconnectionSqlException(e.getNextException());
}
}
}
return fatalException;
}
@Override
protected void handleException(final SQLException e) throws SQLException {
fatalSqlExceptionThrown |= isDisconnectionSqlException(e);
super.handleException(e);
}
/**
* @return The disconnection SQL codes.
* @since 2.6.0
*/
public Collection<String> getDisconnectionSqlCodes() {
return disconnectionSqlCodes;
}
/**
* @return Whether to fail-fast.
* @since 2.6.0
*/
public boolean isFastFailValidation() {
return fastFailValidation;
}
}

View File

@@ -0,0 +1,657 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.PooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* A {@link PooledObjectFactory} that creates {@link PoolableConnection}s.
*
* @since 2.0
*/
public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
/**
* Internal constant to indicate the level is not set.
*/
static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
private final ConnectionFactory connectionFactory;
private final ObjectName dataSourceJmxObjectName;
private volatile String validationQuery;
private volatile int validationQueryTimeoutSeconds = -1;
private Collection<String> connectionInitSqls;
private Collection<String> disconnectionSqlCodes;
private boolean fastFailValidation = true;
private volatile ObjectPool<PoolableConnection> pool;
private Boolean defaultReadOnly;
private Boolean defaultAutoCommit;
private boolean autoCommitOnReturn = true;
private boolean rollbackOnReturn = true;
private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
private String defaultCatalog;
private String defaultSchema;
private boolean cacheState;
private boolean poolStatements;
private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
private long maxConnLifetimeMillis = -1;
private final AtomicLong connectionIndex = new AtomicLong(0);
private Integer defaultQueryTimeoutSeconds;
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param connFactory
* the {@link ConnectionFactory} from which to obtain base {@link Connection}s
* @param dataSourceJmxObjectName
* The JMX object name, may be null.
*/
public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) {
this.connectionFactory = connFactory;
this.dataSourceJmxObjectName = dataSourceJmxObjectName;
}
@Override
public void activateObject(final PooledObject<PoolableConnection> p) throws Exception {
validateLifetime(p);
final PoolableConnection conn = p.getObject();
conn.activate();
if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) {
conn.setAutoCommit(defaultAutoCommit.booleanValue());
}
if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION
&& conn.getTransactionIsolation() != defaultTransactionIsolation) {
conn.setTransactionIsolation(defaultTransactionIsolation);
}
if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) {
conn.setReadOnly(defaultReadOnly.booleanValue());
}
if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) {
conn.setCatalog(defaultCatalog);
}
if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(conn))) {
Jdbc41Bridge.setSchema(conn, defaultSchema);
}
conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
}
@Override
public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
p.getObject().reallyClose();
}
/**
* @return The cache state.
* @since Made public in 2.6.0.
*/
public boolean getCacheState() {
return cacheState;
}
/**
* @return The connection factory.
* @since Made public in 2.6.0.
*/
public ConnectionFactory getConnectionFactory() {
return connectionFactory;
}
protected AtomicLong getConnectionIndex() {
return connectionIndex;
}
/**
* @return The collection of initialization SQL statements.
* @since 2.6.0
*/
public Collection<String> getConnectionInitSqls() {
return connectionInitSqls;
}
/**
* @return The data source JMX ObjectName
* @since Made public in 2.6.0.
*/
public ObjectName getDataSourceJmxName() {
return dataSourceJmxObjectName;
}
/**
* @return The data source JMS ObjectName.
* @since 2.6.0
*/
public ObjectName getDataSourceJmxObjectName() {
return dataSourceJmxObjectName;
}
/**
* @return Default auto-commit value.
* @since 2.6.0
*/
public Boolean getDefaultAutoCommit() {
return defaultAutoCommit;
}
/**
* @return Default catalog.
* @since 2.6.0
*/
public String getDefaultCatalog() {
return defaultCatalog;
}
/**
* @return Default query timeout in seconds.
*/
public Integer getDefaultQueryTimeout() {
return defaultQueryTimeoutSeconds;
}
/**
* @return Default query timeout in seconds.
* @since 2.6.0
*/
public Integer getDefaultQueryTimeoutSeconds() {
return defaultQueryTimeoutSeconds;
}
/**
* @return Default read-only-value.
* @since 2.6.0
*/
public Boolean getDefaultReadOnly() {
return defaultReadOnly;
}
/**
* @return Default schema.
* @since 2.6.0
*/
public String getDefaultSchema() {
return defaultSchema;
}
/**
* @return Default transaction isolation.
* @since 2.6.0
*/
public int getDefaultTransactionIsolation() {
return defaultTransactionIsolation;
}
/**
* SQL_STATE codes considered to signal fatal conditions.
* <p>
* Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with
* {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #isFastFailValidation()} is
* {@code true}, whenever connections created by this factory generate exceptions with SQL_STATE codes in this list,
* they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at isValid or
* validation query).
* </p>
* <p>
* If {@link #isFastFailValidation()} is {@code false} setting this property has no effect.
* </p>
*
* @return SQL_STATE codes overriding defaults
* @since 2.1
*/
public Collection<String> getDisconnectionSqlCodes() {
return disconnectionSqlCodes;
}
/**
* @return Maximum connection lifetime in milliseconds.
* @since 2.6.0
*/
public long getMaxConnLifetimeMillis() {
return maxConnLifetimeMillis;
}
protected int getMaxOpenPreparedStatements() {
return maxOpenPreparedStatements;
}
/**
* Returns the {@link ObjectPool} in which {@link Connection}s are pooled.
*
* @return the connection pool
*/
public synchronized ObjectPool<PoolableConnection> getPool() {
return pool;
}
/**
* @return Whether to pool statements.
* @since Made public in 2.6.0.
*/
public boolean getPoolStatements() {
return poolStatements;
}
/**
* @return Validation query.
* @since 2.6.0
*/
public String getValidationQuery() {
return validationQuery;
}
/**
* @return Validation query timeout in seconds.
* @since 2.6.0
*/
public int getValidationQueryTimeoutSeconds() {
return validationQueryTimeoutSeconds;
}
protected void initializeConnection(final Connection conn) throws SQLException {
final Collection<String> sqls = connectionInitSqls;
if (conn.isClosed()) {
throw new SQLException("initializeConnection: connection closed");
}
if (null != sqls) {
try (Statement stmt = conn.createStatement()) {
for (final String sql : sqls) {
Objects.requireNonNull(sql, "null connectionInitSqls element");
stmt.execute(sql);
}
}
}
}
/**
* @return Whether to auto-commit on return.
* @since 2.6.0
*/
public boolean isAutoCommitOnReturn() {
return autoCommitOnReturn;
}
/**
* @return Whether to auto-commit on return.
* @deprecated Use {@link #isAutoCommitOnReturn()}.
*/
@Deprecated
public boolean isEnableAutoCommitOnReturn() {
return autoCommitOnReturn;
}
/**
* True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
* SQL_STATE indicating fatal disconnection errors.
*
* @return true if connections created by this factory will fast fail validation.
* @see #setDisconnectionSqlCodes(Collection)
* @since 2.1
* @since 2.5.0 Defaults to true, previous versions defaulted to false.
*/
public boolean isFastFailValidation() {
return fastFailValidation;
}
/**
* @return Whether to rollback on return.
*/
public boolean isRollbackOnReturn() {
return rollbackOnReturn;
}
@Override
public PooledObject<PoolableConnection> makeObject() throws Exception {
Connection conn = connectionFactory.createConnection();
if (conn == null) {
throw new IllegalStateException("Connection factory returned null from createConnection");
}
try {
initializeConnection(conn);
} catch (final SQLException sqle) {
// Make sure the connection is closed
try {
conn.close();
} catch (final SQLException ignore) {
// ignore
}
// Rethrow original exception so it is visible to caller
throw sqle;
}
final long connIndex = connectionIndex.getAndIncrement();
if (poolStatements) {
conn = new PoolingConnection(conn);
final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
config.setMaxTotalPerKey(-1);
config.setBlockWhenExhausted(false);
config.setMaxWaitMillis(0);
config.setMaxIdlePerKey(1);
config.setMaxTotal(maxOpenPreparedStatements);
if (dataSourceJmxObjectName != null) {
final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString());
base.append(Constants.JMX_CONNECTION_BASE_EXT);
base.append(Long.toString(connIndex));
config.setJmxNameBase(base.toString());
config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
} else {
config.setJmxEnabled(false);
}
final PoolingConnection poolingConn = (PoolingConnection) conn;
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(
poolingConn, config);
poolingConn.setStatementPool(stmtPool);
poolingConn.setCacheState(cacheState);
}
// Register this connection with JMX
ObjectName connJmxName;
if (dataSourceJmxObjectName == null) {
connJmxName = null;
} else {
connJmxName = new ObjectName(
dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex);
}
final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName, disconnectionSqlCodes,
fastFailValidation);
pc.setCacheState(cacheState);
return new DefaultPooledObject<>(pc);
}
@Override
public void passivateObject(final PooledObject<PoolableConnection> p) throws Exception {
validateLifetime(p);
final PoolableConnection conn = p.getObject();
Boolean connAutoCommit = null;
if (rollbackOnReturn) {
connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
if (!connAutoCommit.booleanValue() && !conn.isReadOnly()) {
conn.rollback();
}
}
conn.clearWarnings();
// DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should
// have autoCommit enabled
if (autoCommitOnReturn) {
if (connAutoCommit == null) {
connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
}
if (!connAutoCommit.booleanValue()) {
conn.setAutoCommit(true);
}
}
conn.passivate();
}
public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
this.autoCommitOnReturn = autoCommitOnReturn;
}
public void setCacheState(final boolean cacheState) {
this.cacheState = cacheState;
}
/**
* Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off
* connection initialization.
*
* @param connectionInitSqls
* SQL statement to initialize {@link Connection}s.
*/
public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
this.connectionInitSqls = connectionInitSqls;
}
/**
* Sets the default "auto commit" setting for borrowed {@link Connection}s
*
* @param defaultAutoCommit
* the default "auto commit" setting for borrowed {@link Connection}s
*/
public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
this.defaultAutoCommit = defaultAutoCommit;
}
/**
* Sets the default "catalog" setting for borrowed {@link Connection}s
*
* @param defaultCatalog
* the default "catalog" setting for borrowed {@link Connection}s
*/
public void setDefaultCatalog(final String defaultCatalog) {
this.defaultCatalog = defaultCatalog;
}
public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
}
/**
* Sets the default "read only" setting for borrowed {@link Connection}s
*
* @param defaultReadOnly
* the default "read only" setting for borrowed {@link Connection}s
*/
public void setDefaultReadOnly(final Boolean defaultReadOnly) {
this.defaultReadOnly = defaultReadOnly;
}
/**
* Sets the default "schema" setting for borrowed {@link Connection}s
*
* @param defaultSchema
* the default "schema" setting for borrowed {@link Connection}s
* @since 2.5.0
*/
public void setDefaultSchema(final String defaultSchema) {
this.defaultSchema = defaultSchema;
}
/**
* Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s
*
* @param defaultTransactionIsolation
* the default "Transaction Isolation" setting for returned {@link Connection}s
*/
public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
this.defaultTransactionIsolation = defaultTransactionIsolation;
}
/**
* @param disconnectionSqlCodes
* The disconnection SQL codes.
* @see #getDisconnectionSqlCodes()
* @since 2.1
*/
public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
this.disconnectionSqlCodes = disconnectionSqlCodes;
}
/**
* @param autoCommitOnReturn Whether to auto-commit on return.
* @deprecated Use {@link #setAutoCommitOnReturn(boolean)}.
*/
@Deprecated
public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
this.autoCommitOnReturn = autoCommitOnReturn;
}
/**
* @see #isFastFailValidation()
* @param fastFailValidation
* true means connections created by this factory will fast fail validation
* @since 2.1
*/
public void setFastFailValidation(final boolean fastFailValidation) {
this.fastFailValidation = fastFailValidation;
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
*
* @param maxConnLifetimeMillis
* The maximum lifetime in milliseconds.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* Sets the maximum number of open prepared statements.
*
* @param maxOpenPreparedStatements
* The maximum number of open prepared statements.
*/
public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}
/**
* Deprecated due to typo in method name.
*
* @param maxOpenPreparedStatements
* The maximum number of open prepared statements.
* @deprecated Use {@link #setMaxOpenPreparedStatements(int)}.
*/
@Deprecated // Due to typo in method name.
public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
setMaxOpenPreparedStatements(maxOpenPreparedStatements);
}
/**
* Sets the {@link ObjectPool} in which to pool {@link Connection}s.
*
* @param pool
* the {@link ObjectPool} in which to pool those {@link Connection}s
*/
public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
if (null != this.pool && pool != this.pool) {
try {
this.pool.close();
} catch (final Exception e) {
// ignored !?!
}
}
this.pool = pool;
}
public void setPoolStatements(final boolean poolStatements) {
this.poolStatements = poolStatements;
}
public void setRollbackOnReturn(final boolean rollbackOnReturn) {
this.rollbackOnReturn = rollbackOnReturn;
}
/**
* Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If
* not specified, {@link Connection#isValid(int)} will be used to validate connections.
*
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s.
*/
public void setValidationQuery(final String validationQuery) {
this.validationQuery = validationQuery;
}
/**
* Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
* response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
*
* @param validationQueryTimeoutSeconds
* new validation query timeout value in seconds
*/
public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
}
public void validateConnection(final PoolableConnection conn) throws SQLException {
if (conn.isClosed()) {
throw new SQLException("validateConnection: connection closed");
}
conn.validate(validationQuery, validationQueryTimeoutSeconds);
}
private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded",
Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis)));
}
}
}
@Override
public boolean validateObject(final PooledObject<PoolableConnection> p) {
try {
validateLifetime(p);
validateConnection(p.getObject());
return true;
} catch (final Exception e) {
if (log.isDebugEnabled()) {
log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
}
return false;
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.dbcp.dbcp2;
import java.sql.SQLException;
/**
* Defines the attributes and methods that will be exposed via JMX for {@link PoolableConnection} instances.
*
* @since 2.0
*/
public interface PoolableConnectionMXBean {
// Read-only properties
boolean isClosed() throws SQLException;
// SQLWarning getWarnings() throws SQLException;
String getToString();
// Read-write properties
boolean getAutoCommit() throws SQLException;
void setAutoCommit(boolean autoCommit) throws SQLException;
boolean getCacheState();
void setCacheState(boolean cacheState);
String getCatalog() throws SQLException;
void setCatalog(String catalog) throws SQLException;
int getHoldability() throws SQLException;
void setHoldability(int holdability) throws SQLException;
boolean isReadOnly() throws SQLException;
void setReadOnly(boolean readOnly) throws SQLException;
String getSchema() throws SQLException;
void setSchema(String schema) throws SQLException;
int getTransactionIsolation() throws SQLException;
void setTransactionIsolation(int level) throws SQLException;
// Methods
void clearCachedState();
void clearWarnings() throws SQLException;
void close() throws SQLException;
void reallyClose() throws SQLException;
}

View File

@@ -0,0 +1,159 @@
/*
* 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.dbcp.dbcp2;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
/**
* A {@link DelegatingPreparedStatement} that cooperates with {@link PoolingConnection} to implement a pool of
* {@link PreparedStatement}s.
* <p>
* My {@link #close} method returns me to my containing pool. (See {@link PoolingConnection}.)
* </p>
*
* @param <K>
* the key type
*
* @see PoolingConnection
* @since 2.0
*/
public class PoolablePreparedStatement<K> extends DelegatingPreparedStatement {
/**
* The {@link KeyedObjectPool} from which I was obtained.
*/
private final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool;
/**
* My "key" as used by {@link KeyedObjectPool}.
*/
private final K key;
private volatile boolean batchAdded = false;
/**
* Constructor.
*
* @param stmt
* my underlying {@link PreparedStatement}
* @param key
* my key" as used by {@link KeyedObjectPool}
* @param pool
* the {@link KeyedObjectPool} from which I was obtained.
* @param conn
* the {@link java.sql.Connection Connection} from which I was created
*/
public PoolablePreparedStatement(final PreparedStatement stmt, final K key,
final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool, final DelegatingConnection<?> conn) {
super(conn, stmt);
this.pool = pool;
this.key = key;
// Remove from trace now because this statement will be
// added by the activate method.
removeThisTrace(getConnectionInternal());
}
/**
* Add batch.
*/
@Override
public void addBatch() throws SQLException {
super.addBatch();
batchAdded = true;
}
/**
* Clear Batch.
*/
@Override
public void clearBatch() throws SQLException {
batchAdded = false;
super.clearBatch();
}
/**
* Return me to my pool.
*/
@Override
public void close() throws SQLException {
// calling close twice should have no effect
if (!isClosed()) {
try {
pool.returnObject(key, this);
} catch (final SQLException e) {
throw e;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close preparedstatement (return to pool failed)", e);
}
}
}
@Override
public void activate() throws SQLException {
setClosedInternal(false);
if (getConnectionInternal() != null) {
getConnectionInternal().addTrace(this);
}
super.activate();
}
@Override
public void passivate() throws SQLException {
// DBCP-372. clearBatch with throw an exception if called when the
// connection is marked as closed.
if (batchAdded) {
clearBatch();
}
setClosedInternal(true);
removeThisTrace(getConnectionInternal());
// The JDBC spec requires that a statement closes any open
// ResultSet's when it is closed.
// FIXME The PreparedStatement we're wrapping should handle this for us.
// See bug 17301 for what could happen when ResultSets are closed twice.
final List<AbandonedTrace> resultSetList = getTrace();
if (resultSetList != null) {
final List<Exception> thrownList = new ArrayList<>();
final ResultSet[] resultSets = resultSetList.toArray(new ResultSet[resultSetList.size()]);
for (final ResultSet resultSet : resultSets) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
thrownList.add(e);
}
}
}
clearTrace();
if (!thrownList.isEmpty()) {
throw new SQLExceptionList(thrownList);
}
}
super.passivate();
}
}

View File

@@ -0,0 +1,602 @@
/*
* 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.dbcp.dbcp2;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.NoSuchElementException;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
* <p>
* The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each
* time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of
* the returned statement doesn't actually close the statement, but rather returns it to the pool. (See
* {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
* </p>
*
* @see PoolablePreparedStatement
* @since 2.0
*/
public class PoolingConnection extends DelegatingConnection<Connection>
implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
/**
* Statement types.
*
* @since 2.0 protected enum.
* @since 2.4.0 public enum.
*/
public enum StatementType {
/**
* Callable statement.
*/
CALLABLE_STATEMENT,
/**
* Prepared statement.
*/
PREPARED_STATEMENT
}
/** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pstmtPool;
/**
* Constructor.
*
* @param connection
* the underlying {@link Connection}.
*/
public PoolingConnection(final Connection connection) {
super(connection);
}
/**
* {@link KeyedPooledObjectFactory} method for activating pooled statements.
*
* @param key
* ignored
* @param pooledObject
* wrapped pooled statement to be activated
*/
@Override
public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().activate();
}
/**
* Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the
* underlying connection.
*/
@Override
public synchronized void close() throws SQLException {
try {
if (null != pstmtPool) {
final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldpool = pstmtPool;
pstmtPool = null;
try {
oldpool.close();
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection", e);
}
}
} finally {
try {
getDelegateInternal().close();
} finally {
setClosedInternal(true);
}
}
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param columnIndexes
* column indexes
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int columnIndexes[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
}
protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
resultSetHoldability);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
resultSetHoldability, statementType);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param statementType
* statement type
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null);
}
/**
* Creates a PStmtKey for the given arguments.
*
* @param sql
* the SQL string used to define the statement
* @param columnNames
* column names
*
* @return the PStmtKey created for the given arguments.
*/
protected PStmtKey createKey(final String sql, final String columnNames[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
}
/**
* {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements.
* Closes the underlying statement.
*
* @param key
* ignored
* @param pooledObject
* the wrapped pooled statement to be destroyed.
*/
@Override
public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().getInnermostDelegate().close();
}
private String getCatalogOrNull() {
String catalog = null;
try {
catalog = getCatalog();
} catch (final SQLException e) {
// Ignored
}
return catalog;
}
private String getSchemaOrNull() {
String catalog = null;
try {
catalog = getSchema();
} catch (final SQLException e) {
// Ignored
}
return catalog;
}
/**
* {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or
* {@link PoolableCallableStatement}s. The <code>stmtType</code> field in the key determines whether a
* PoolablePreparedStatement or PoolableCallableStatement is created.
*
* @param key
* the key for the {@link PreparedStatement} to be created
* @see #createKey(String, int, int, StatementType)
*/
@SuppressWarnings("resource")
@Override
public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
if (null == key) {
throw new IllegalArgumentException("Prepared statement key is null or invalid.");
}
if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
final PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate());
@SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pstmtPool, this);
return new DefaultPooledObject<DelegatingPreparedStatement>(pps);
}
final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate());
final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pstmtPool, this);
return new DefaultPooledObject<DelegatingPreparedStatement>(pcs);
}
/**
* Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
*
* @param sql The statement to be normalized.
*
* @return The canonical form of the supplied SQL statement.
*/
protected String normalizeSQL(final String sql) {
return sql.trim();
}
/**
* {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s.
* Invokes {@link PreparedStatement#clearParameters}.
*
* @param key
* ignored
* @param pooledObject
* a wrapped {@link PreparedStatement}
*/
@Override
public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
@SuppressWarnings("resource")
final DelegatingPreparedStatement dps = pooledObject.getObject();
dps.clearParameters();
dps.passivate();
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql) throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(
createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from the pool.
*
* @param sql
* the SQL string used to define the CallableStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @return a {@link PoolableCallableStatement}
* @throws SQLException
* Wraps an underlying exception.
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
try {
return (CallableStatement) pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, StatementType.CALLABLE_STATEMENT));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenCallableStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow callableStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param columnIndexes
* column indexes
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, columnIndexes));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param resultSetType
* result set type
* @param resultSetConcurrency
* result set concurrency
* @param resultSetHoldability
* result set holdability
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from the pool.
*
* @param sql
* the SQL string used to define the PreparedStatement
* @param columnNames
* column names
* @return a {@link PoolablePreparedStatement}
*/
@Override
public PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
if (null == pstmtPool) {
throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
}
try {
return pstmtPool.borrowObject(createKey(sql, columnNames));
} catch (final NoSuchElementException e) {
throw new SQLException("MaxOpenPreparedStatements limit reached", e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Sets the prepared statement pool.
*
* @param pool
* the prepared statement pool.
*/
public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) {
pstmtPool = pool;
}
@Override
public synchronized String toString() {
if (pstmtPool != null) {
return "PoolingConnection: " + pstmtPool.toString();
}
return "PoolingConnection: null";
}
/**
* {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently always returns true.
*
* @param key
* ignored
* @param pooledObject
* ignored
* @return {@code true}
*/
@Override
public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
return true;
}
}

View File

@@ -0,0 +1,257 @@
/*
* 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.dbcp.dbcp2;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool;
/**
* A simple {@link DataSource} implementation that obtains {@link Connection}s from the specified {@link ObjectPool}.
*
* @param <C>
* The connection type
*
* @since 2.0
*/
public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
private static final Log log = LogFactory.getLog(PoolingDataSource.class);
/** Controls access to the underlying connection */
private boolean accessToUnderlyingConnectionAllowed;
/**
* Constructs a new instance backed by the given connection pool.
*
* @param pool
* the given connection pool.
*/
public PoolingDataSource(final ObjectPool<C> pool) {
Objects.requireNonNull(pool, "Pool must not be null.");
this.pool = pool;
// Verify that pool's factory refers back to it. If not, log a warning and try to fix.
if (this.pool instanceof GenericObjectPool<?>) {
final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) this.pool)
.getFactory();
Objects.requireNonNull(pcf, "PoolableConnectionFactory must not be null.");
if (pcf.getPool() != this.pool) {
log.warn(Utils.getMessage("poolingDataSource.factoryConfig"));
@SuppressWarnings("unchecked") // PCF must have a pool of PCs
final ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) this.pool;
pcf.setPool(p);
}
}
}
/**
* Closes and free all {@link Connection}s from the pool.
*
* @since 2.1
*/
@Override
public void close() throws RuntimeException, SQLException {
try {
pool.close();
} catch (final RuntimeException rte) {
throw new RuntimeException(Utils.getMessage("pool.close.fail"), rte);
} catch (final Exception e) {
throw new SQLException(Utils.getMessage("pool.close.fail"), e);
}
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying {@link Connection} is allowed, false otherwise.
*/
public boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public boolean isWrapperFor(final Class<?> iface) throws SQLException {
return false;
}
@Override
public <T> T unwrap(final Class<T> iface) throws SQLException {
throw new SQLException("PoolingDataSource is not a wrapper.");
}
/* JDBC_4_ANT_KEY_END */
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
// --- DataSource methods -----------------------------------------
/**
* Returns a {@link java.sql.Connection} from my pool, according to the contract specified by
* {@link ObjectPool#borrowObject}.
*/
@Override
public Connection getConnection() throws SQLException {
try {
final C conn = pool.borrowObject();
if (conn == null) {
return null;
}
return new PoolGuardConnectionWrapper<>(conn);
} catch (final SQLException e) {
throw e;
} catch (final NoSuchElementException e) {
throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
} catch (final RuntimeException e) {
throw e;
} catch (final InterruptedException e) {
// Reset the interrupt status so it is visible to callers
Thread.currentThread().interrupt();
throw new SQLException("Cannot get a connection, general error", e);
} catch (final Exception e) {
throw new SQLException("Cannot get a connection, general error", e);
}
}
/**
* Throws {@link UnsupportedOperationException}
*
* @throws UnsupportedOperationException
* always thrown
*/
@Override
public Connection getConnection(final String uname, final String passwd) throws SQLException {
throw new UnsupportedOperationException();
}
/**
* Returns my log writer.
*
* @return my log writer
* @see DataSource#getLogWriter
*/
@Override
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Throws {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException
* As this implementation does not support this feature.
*/
@Override
public int getLoginTimeout() {
throw new UnsupportedOperationException("Login timeout is not supported.");
}
/**
* Throws {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException
* As this implementation does not support this feature.
*/
@Override
public void setLoginTimeout(final int seconds) {
throw new UnsupportedOperationException("Login timeout is not supported.");
}
/**
* Sets my log writer.
*
* @see DataSource#setLogWriter
*/
@Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
/** My log writer. */
private PrintWriter logWriter = null;
private final ObjectPool<C> pool;
protected ObjectPool<C> getPool() {
return pool;
}
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* @since 2.0
*/
private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
PoolGuardConnectionWrapper(final D delegate) {
super(delegate);
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getDelegate()
*/
@Override
public D getDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
@Override
public Connection getInnermostDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
}
@Override
public void close() throws SQLException {
if (getDelegateInternal() != null) {
super.close();
super.setDelegate(null);
}
}
@Override
public boolean isClosed() throws SQLException {
return getDelegateInternal() == null ? true : super.isClosed();
}
}
}

View File

@@ -0,0 +1,263 @@
/*
* 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.dbcp.dbcp2;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
/**
* A {@link Driver} implementation that obtains {@link Connection}s from a registered {@link ObjectPool}.
*
* @since 2.0
*/
public class PoolingDriver implements Driver {
/** Register myself with the {@link DriverManager}. */
static {
try {
DriverManager.registerDriver(new PoolingDriver());
} catch (final Exception e) {
// ignore
}
}
/** The map of registered pools. */
protected static final HashMap<String, ObjectPool<? extends Connection>> pools = new HashMap<>();
/** Controls access to the underlying connection */
private final boolean accessToUnderlyingConnectionAllowed;
/**
* Constructs a new driver with <code>accessToUnderlyingConnectionAllowed</code> enabled.
*/
public PoolingDriver() {
this(true);
}
/**
* For unit testing purposes.
*
* @param accessToUnderlyingConnectionAllowed
* Do {@link DelegatingConnection}s created by this driver permit access to the delegate?
*/
protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
protected boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
}
/**
* Gets the connection pool for the given name.
*
* @param name
* The pool name
* @return The pool
* @throws SQLException
* Thrown when the named pool is not registered.
*/
public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) throws SQLException {
final ObjectPool<? extends Connection> pool = pools.get(name);
if (null == pool) {
throw new SQLException("Pool not registered: " + name);
}
return pool;
}
/**
* Registers a named pool.
*
* @param name
* The pool name.
* @param pool
* The pool.
*/
public synchronized void registerPool(final String name, final ObjectPool<? extends Connection> pool) {
pools.put(name, pool);
}
/**
* Closes a named pool.
*
* @param name
* The pool name.
* @throws SQLException
* Thrown when a problem is caught closing the pool.
*/
public synchronized void closePool(final String name) throws SQLException {
@SuppressWarnings("resource")
final ObjectPool<? extends Connection> pool = pools.get(name);
if (pool != null) {
pools.remove(name);
try {
pool.close();
} catch (final Exception e) {
throw new SQLException("Error closing pool " + name, e);
}
}
}
/**
* Gets the pool names.
*
* @return the pool names.
*/
public synchronized String[] getPoolNames() {
final Set<String> names = pools.keySet();
return names.toArray(new String[names.size()]);
}
@Override
public boolean acceptsURL(final String url) throws SQLException {
return url == null ? false : url.startsWith(URL_PREFIX);
}
@Override
public Connection connect(final String url, final Properties info) throws SQLException {
if (acceptsURL(url)) {
final ObjectPool<? extends Connection> pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
try {
final Connection conn = pool.borrowObject();
if (conn == null) {
return null;
}
return new PoolGuardConnectionWrapper(pool, conn);
} catch (final SQLException e) {
throw e;
} catch (final NoSuchElementException e) {
throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e);
}
}
return null;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
/**
* Invalidates the given connection.
*
* @param conn
* connection to invalidate
* @throws SQLException
* if the connection is not a <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating
* the connection
*/
public void invalidateConnection(final Connection conn) throws SQLException {
if (conn instanceof PoolGuardConnectionWrapper) { // normal case
final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
@SuppressWarnings("unchecked")
final ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
try {
pool.invalidateObject(pgconn.getDelegateInternal());
} catch (final Exception e) {
// Ignore.
}
} else {
throw new SQLException("Invalid connection class");
}
}
@Override
public int getMajorVersion() {
return MAJOR_VERSION;
}
@Override
public int getMinorVersion() {
return MINOR_VERSION;
}
@Override
public boolean jdbcCompliant() {
return true;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {
return new DriverPropertyInfo[0];
}
/** My URL prefix */
public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
// version numbers
protected static final int MAJOR_VERSION = 1;
protected static final int MINOR_VERSION = 0;
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* @since 2.0
*/
private class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> {
private final ObjectPool<? extends Connection> pool;
PoolGuardConnectionWrapper(final ObjectPool<? extends Connection> pool, final Connection delegate) {
super(delegate);
this.pool = pool;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getDelegate()
*/
@Override
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getDelegate();
}
return null;
}
/**
* @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
@Override
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getInnermostDelegate();
}
return null;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.dbcp.dbcp2;
import java.sql.SQLException;
import java.util.List;
/**
* An SQLException based on a list of Throwable causes.
* <p>
* The first exception in the list is used as this exception's cause and is accessible with the usual
* {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
* </p>
*
* @since 2.7.0
*/
public class SQLExceptionList extends SQLException {
private static final long serialVersionUID = 1L;
private final List<? extends Throwable> causeList;
/**
* Creates a new exception caused by a list of exceptions.
*
* @param causeList a list of cause exceptions.
*/
public SQLExceptionList(List<? extends Throwable> causeList) {
super(String.format("%,d exceptions: %s", Integer.valueOf(causeList == null ? 0 : causeList.size()), causeList),
causeList == null ? null : causeList.get(0));
this.causeList = causeList;
}
/**
* Gets the cause list.
*
* @return The list of causes.
*/
public List<? extends Throwable> getCauseList() {
return causeList;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.dbcp.dbcp2;
import org.apache.juli.logging.Log;
import org.apache.tomcat.dbcp.pool2.SwallowedExceptionListener;
/**
* Class for logging swallowed exceptions.
*
* @since 2.0
*/
public class SwallowedExceptionLogger implements SwallowedExceptionListener {
private final Log log;
private final boolean logExpiredConnections;
/**
* Create a SwallowedExceptionLogger with the given logger. By default, expired connection logging is turned on.
*
* @param log
* logger
*/
public SwallowedExceptionLogger(final Log log) {
this(log, true);
}
/**
* Create a SwallowedExceptionLogger with the given logger and expired connection logging property.
*
* @param log
* logger
* @param logExpiredConnections
* false suppresses logging of expired connection events
*/
public SwallowedExceptionLogger(final Log log, final boolean logExpiredConnections) {
this.log = log;
this.logExpiredConnections = logExpiredConnections;
}
@Override
public void onSwallowException(final Exception e) {
if (logExpiredConnections || !(e instanceof LifetimeExceededException)) {
log.warn(Utils.getMessage("swallowedExceptionLogger.onSwallowedException"), e);
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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.dbcp.dbcp2;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Set;
/**
* Utility methods.
*
* @since 2.0
*/
public final class Utils {
private static final ResourceBundle messages = ResourceBundle
.getBundle(Utils.class.getPackage().getName() + ".LocalStrings");
/**
* Whether the security manager is enabled.
*/
public static final boolean IS_SECURITY_ENABLED = System.getSecurityManager() != null;
/** Any SQL_STATE starting with this value is considered a fatal disconnect */
public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
/**
* SQL codes of fatal connection errors.
* <ul>
* <li>57P01 (Admin shutdown)</li>
* <li>57P02 (Crash shutdown)</li>
* <li>57P03 (Cannot connect now)</li>
* <li>01002 (SQL92 disconnect error)</li>
* <li>JZ0C0 (Sybase disconnect error)</li>
* <li>JZ0C1 (Sybase disconnect error)</li>
* </ul>
*/
public static final Set<String> DISCONNECTION_SQL_CODES;
static {
DISCONNECTION_SQL_CODES = new HashSet<>();
DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
}
/**
* Clones the given char[] if not null.
*
* @param value
* may be null.
* @return a cloned char[] or null.
*/
public static char[] clone(final char[] value) {
return value == null ? null : value.clone();
}
/**
* Closes the AutoCloseable (which may be null).
*
* @param autoCloseable
* an AutoCloseable, may be {@code null}
* @since 2.6.0
*/
public static void closeQuietly(final AutoCloseable autoCloseable) {
if (autoCloseable != null) {
try {
autoCloseable.close();
} catch (final Exception e) {
// ignored
}
}
}
/**
* Gets the correct i18n message for the given key.
*
* @param key
* The key to look up an i18n message.
* @return The i18n message.
*/
public static String getMessage(final String key) {
return getMessage(key, (Object[]) null);
}
/**
* Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
*
* @param key
* A message key.
* @param args
* The message arguments.
* @return An i18n message.
*/
public static String getMessage(final String key, final Object... args) {
final String msg = messages.getString(key);
if (args == null || args.length == 0) {
return msg;
}
final MessageFormat mf = new MessageFormat(msg);
return mf.format(args, new StringBuffer(), null).toString();
}
/**
* Converts the given String to a char[].
*
* @param value
* may be null.
* @return a char[] or null.
*/
public static char[] toCharArray(final String value) {
return value != null ? value.toCharArray() : null;
}
/**
* Converts the given char[] to a String.
*
* @param value
* may be null.
* @return a String or null.
*/
public static String toString(final char[] value) {
return value == null ? null : String.valueOf(value);
}
private Utils() {
// not instantiable
}
}

View File

@@ -0,0 +1,308 @@
/*
* 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.dbcp.dbcp2.cpdsadapter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.tomcat.dbcp.dbcp2.DelegatingCallableStatement;
import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
/**
* This class is the <code>Connection</code> that will be returned from
* <code>PooledConnectionImpl.getConnection()</code>. Most methods are wrappers around the JDBC 1.x
* <code>Connection</code>. A few exceptions include preparedStatement and close. In accordance with the JDBC
* specification this Connection cannot be used after closed() is called. Any further usage will result in an
* SQLException.
* <p>
* ConnectionImpl extends DelegatingConnection to enable access to the underlying connection.
* </p>
*
* @since 2.0
*/
class ConnectionImpl extends DelegatingConnection<Connection> {
private final boolean accessToUnderlyingConnectionAllowed;
/** The object that instantiated this object */
private final PooledConnectionImpl pooledConnection;
/**
* Creates a <code>ConnectionImpl</code>.
*
* @param pooledConnection
* The PooledConnection that is calling the ctor.
* @param connection
* The JDBC 1.x Connection to wrap.
* @param accessToUnderlyingConnectionAllowed
* if true, then access is allowed to the underlying connection
*/
ConnectionImpl(final PooledConnectionImpl pooledConnection, final Connection connection,
final boolean accessToUnderlyingConnectionAllowed) {
super(connection);
this.pooledConnection = pooledConnection;
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
}
/**
* Marks the Connection as closed, and notifies the pool that the pooled connection is available.
* <p>
* In accordance with the JDBC specification this Connection cannot be used after closed() is called. Any further
* usage will result in an SQLException.
* </p>
*
* @throws SQLException
* The database connection couldn't be closed.
*/
@Override
public void close() throws SQLException {
if (!isClosedInternal()) {
try {
passivate();
} finally {
setClosedInternal(true);
pooledConnection.notifyListeners();
}
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
* specified using JDBC call escape syntax.
* @return a default <code>CallableStatement</code> object containing the pre-compiled SQL statement.
* @exception SQLException
* Thrown if a database access error occurs or this method is called on a closed connection.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql) throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this, pooledConnection.prepareCall(sql));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a <code>CallableStatement</code> object containing the pre-compiled SQL statement that will produce
* <code>ResultSet</code> objects with the given type and concurrency.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type and concurrency.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this,
pooledConnection.prepareCall(sql, resultSetType, resultSetConcurrency));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>CallableStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* one of the following <code>ResultSet</code> constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* one of the following <code>ResultSet</code> constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* one of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a new <code>CallableStatement</code> object, containing the pre-compiled SQL statement, that will
* generate <code>ResultSet</code> objects with the given type, concurrency, and holdability.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type, concurrency, and holdability.
* @since 2.4.0
*/
@Override
public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
checkOpen();
try {
return new DelegatingCallableStatement(this,
pooledConnection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>PreparedStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @param sql
* SQL statement to be prepared
* @return the prepared statement
* @throws SQLException
* if this connection is closed or an error occurs in the wrapped connection.
*/
@Override
public PreparedStatement prepareStatement(final String sql) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql));
} catch (final SQLException e) {
handleException(e); // Does not return
return null;
}
}
/**
* If pooling of <code>PreparedStatement</code>s is turned on in the {@link DriverAdapterCPDS}, a pooled object may
* be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}.
*
* @throws SQLException
* if this connection is closed or an error occurs in the wrapped connection.
*/
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this,
pooledConnection.prepareStatement(sql, resultSetType, resultSetConcurrency));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this,
pooledConnection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, autoGeneratedKeys));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, columnIndexes));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
@Override
public PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
checkOpen();
try {
return new DelegatingPreparedStatement(this, pooledConnection.prepareStatement(sql, columnNames));
} catch (final SQLException e) {
handleException(e);
return null;
}
}
//
// Methods for accessing the delegate connection
//
/**
* If false, getDelegate() and getInnermostDelegate() will return null.
*
* @return true if access is allowed to the underlying connection
* @see ConnectionImpl
*/
public boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
}
/**
* Get the delegated connection, if allowed.
*
* @return the internal connection, or null if access is not allowed.
* @see #isAccessToUnderlyingConnectionAllowed()
*/
@Override
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return getDelegateInternal();
}
return null;
}
/**
* Get the innermost connection, if allowed.
*
* @return the innermost internal connection, or null if access is not allowed.
* @see #isAccessToUnderlyingConnectionAllowed()
*/
@Override
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return super.getInnermostDelegateInternal();
}
return null;
}
}

View File

@@ -0,0 +1,779 @@
/*
* 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.dbcp.dbcp2.cpdsadapter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Hashtable;
import java.util.Properties;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.BaseObjectPoolConfig;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* <p>
* An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but
* still include a {@link java.sql.DriverManager} implementation. <code>ConnectionPoolDataSource</code>s are not used
* within general applications. They are used by <code>DataSource</code> implementations that pool
* <code>Connection</code>s, such as {@link org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource}. A J2EE container
* will normally provide some method of initializing the <code>ConnectionPoolDataSource</code> whose attributes are
* presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical
* connections to the database, when the pooling <code>DataSource</code> needs to create a new physical connection.
* </p>
* <p>
* Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any
* bean and then attached directly to a pooling <code>DataSource</code>. <code>Jdbc2PoolDataSource</code> can use the
* <code>ConnectionPoolDataSource</code> with or without the use of JNDI.
* </p>
* <p>
* The DriverAdapterCPDS also provides <code>PreparedStatement</code> pooling which is not generally available in jdbc2
* <code>ConnectionPoolDataSource</code> implementation, but is addressed within the jdbc3 specification. The
* <code>PreparedStatement</code> pool in DriverAdapterCPDS has been in the dbcp package for some time, but it has not
* undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled
* with the poolPreparedStatements attribute.
* </p>
* <p>
* The <a href="package-summary.html">package documentation</a> contains an example using catalina and JNDI. The
* <a href="../datasources/package-summary.html">datasources package documentation</a> shows how to use
* <code>DriverAdapterCPDS</code> as a source for <code>Jdbc2PoolDataSource</code> without the use of JNDI.
* </p>
*
* @since 2.0
*/
public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory {
private static final String KEY_USER = "user";
private static final String KEY_PASSWORD = "password";
private static final long serialVersionUID = -4820523787212147844L;
private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, "
+ "further initialization is not allowed.";
static {
// Attempt to prevent deadlocks - see DBCP - 272
DriverManager.getDrivers();
}
/** Description */
private String description;
/** Url name */
private String url;
/** User name */
private String userName;
/** User password */
private char[] userPassword;
/** Driver class name */
private String driver;
/** Login TimeOut in seconds */
private int loginTimeout;
/** Log stream. NOT USED */
private transient PrintWriter logWriter;
// PreparedStatement pool properties
private boolean poolPreparedStatements;
private int maxIdle = 10;
private long timeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
private int numTestsPerEvictionRun = -1;
private int minEvictableIdleTimeMillis = -1;
private int maxPreparedStatements = -1;
/** Whether or not getConnection has been called */
private volatile boolean getConnectionCalled;
/** Connection properties passed to JDBC Driver */
private Properties connectionProperties;
/**
* Controls access to the underlying connection
*/
private boolean accessToUnderlyingConnectionAllowed;
/**
* Default no-arg constructor for Serialization
*/
public DriverAdapterCPDS() {
}
/**
* Throws an IllegalStateException, if a PooledConnection has already been requested.
*/
private void assertInitializationAllowed() throws IllegalStateException {
if (getConnectionCalled) {
throw new IllegalStateException(GET_CONNECTION_CALLED);
}
}
private boolean getBooleanContentString(RefAddr ra) {
return Boolean.valueOf(getStringContent(ra)).booleanValue();
}
/**
* Gets the connection properties passed to the JDBC driver.
*
* @return the JDBC connection properties used when creating connections.
*/
public Properties getConnectionProperties() {
return connectionProperties;
}
/**
* Gets the value of description. This property is here for use by the code which will deploy this datasource. It is
* not used internally.
*
* @return value of description, may be null.
* @see #setDescription(String)
*/
public String getDescription() {
return description;
}
/**
* Gets the driver class name.
*
* @return value of driver.
*/
public String getDriver() {
return driver;
}
private int getIntegerStringContent(final RefAddr ra) {
return Integer.parseInt(getStringContent(ra));
}
/**
* Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT
* USED.
*/
@Override
public int getLoginTimeout() {
return loginTimeout;
}
/**
* Gets the log writer for this data source. NOT USED.
*/
@Override
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
* negative for no limit.
*
* @return the value of maxIdle
*/
public int getMaxIdle() {
return this.maxIdle;
}
/**
* Gets the maximum number of prepared statements.
*
* @return maxPrepartedStatements value
*/
public int getMaxPreparedStatements() {
return maxPreparedStatements;
}
/**
* Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
* idle object evictor (if any).
*
* @see #setMinEvictableIdleTimeMillis
* @see #setTimeBetweenEvictionRunsMillis
* @return the minimum amount of time a statement may sit idle in the pool.
*/
public int getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
/**
* Gets the number of statements to examine during each run of the idle object evictor thread (if any.)
*
* @see #setNumTestsPerEvictionRun
* @see #setTimeBetweenEvictionRunsMillis
* @return the number of statements to examine during each run of the idle object evictor thread (if any.)
*/
public int getNumTestsPerEvictionRun() {
return numTestsPerEvictionRun;
}
/**
* Implements {@link ObjectFactory} to create an instance of this class
*/
@Override
public Object getObjectInstance(final Object refObj, final Name name, final Context context,
final Hashtable<?, ?> env) throws Exception {
// The spec says to return null if we can't create an instance
// of the reference
DriverAdapterCPDS cpds = null;
if (refObj instanceof Reference) {
final Reference ref = (Reference) refObj;
if (ref.getClassName().equals(getClass().getName())) {
RefAddr ra = ref.get("description");
if (isNotEmpty(ra)) {
setDescription(getStringContent(ra));
}
ra = ref.get("driver");
if (isNotEmpty(ra)) {
setDriver(getStringContent(ra));
}
ra = ref.get("url");
if (isNotEmpty(ra)) {
setUrl(getStringContent(ra));
}
ra = ref.get(KEY_USER);
if (isNotEmpty(ra)) {
setUser(getStringContent(ra));
}
ra = ref.get(KEY_PASSWORD);
if (isNotEmpty(ra)) {
setPassword(getStringContent(ra));
}
ra = ref.get("poolPreparedStatements");
if (isNotEmpty(ra)) {
setPoolPreparedStatements(getBooleanContentString(ra));
}
ra = ref.get("maxIdle");
if (isNotEmpty(ra)) {
setMaxIdle(getIntegerStringContent(ra));
}
ra = ref.get("timeBetweenEvictionRunsMillis");
if (isNotEmpty(ra)) {
setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra));
}
ra = ref.get("numTestsPerEvictionRun");
if (isNotEmpty(ra)) {
setNumTestsPerEvictionRun(getIntegerStringContent(ra));
}
ra = ref.get("minEvictableIdleTimeMillis");
if (isNotEmpty(ra)) {
setMinEvictableIdleTimeMillis(getIntegerStringContent(ra));
}
ra = ref.get("maxPreparedStatements");
if (isNotEmpty(ra)) {
setMaxPreparedStatements(getIntegerStringContent(ra));
}
ra = ref.get("accessToUnderlyingConnectionAllowed");
if (isNotEmpty(ra)) {
setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra));
}
cpds = this;
}
}
return cpds;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
/**
* Gets the value of password for the default user.
*
* @return value of password.
*/
public String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password for the default user.
*
* @return value of password.
* @since 2.4.0
*/
public char[] getPasswordCharArray() {
return userPassword;
}
/**
* Attempts to establish a database connection using the default user and password.
*/
@Override
public PooledConnection getPooledConnection() throws SQLException {
return getPooledConnection(getUser(), getPassword());
}
/**
* Attempts to establish a database connection.
*
* @param pooledUserName
* name to be used for the connection
* @param pooledUserPassword
* password to be used fur the connection
*/
@Override
public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword)
throws SQLException {
getConnectionCalled = true;
PooledConnectionImpl pooledConnection = null;
// Workaround for buggy WebLogic 5.1 classloader - ignore the exception upon first invocation.
try {
if (connectionProperties != null) {
update(connectionProperties, KEY_USER, pooledUserName);
update(connectionProperties, KEY_PASSWORD, pooledUserPassword);
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), connectionProperties));
} else {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
}
pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
} catch (final ClassCircularityError e) {
if (connectionProperties != null) {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), connectionProperties));
} else {
pooledConnection = new PooledConnectionImpl(
DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
}
pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
}
KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null;
if (isPoolPreparedStatements()) {
final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
config.setMaxTotalPerKey(Integer.MAX_VALUE);
config.setBlockWhenExhausted(false);
config.setMaxWaitMillis(0);
config.setMaxIdlePerKey(getMaxIdle());
if (getMaxPreparedStatements() <= 0) {
// since there is no limit, create a prepared statement pool with an eviction thread;
// evictor settings are the same as the connection pool settings.
config.setTimeBetweenEvictionRunsMillis(getTimeBetweenEvictionRunsMillis());
config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
config.setMinEvictableIdleTimeMillis(getMinEvictableIdleTimeMillis());
} else {
// since there is a limit, create a prepared statement pool without an eviction thread;
// pool has LRU functionality so when the limit is reached, 15% of the pool is cleared.
// see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method
config.setMaxTotal(getMaxPreparedStatements());
config.setTimeBetweenEvictionRunsMillis(-1);
config.setNumTestsPerEvictionRun(0);
config.setMinEvictableIdleTimeMillis(0);
}
stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config);
pooledConnection.setStatementPool(stmtPool);
}
return pooledConnection;
}
/**
* Implements {@link Referenceable}.
*/
@Override
public Reference getReference() throws NamingException {
// this class implements its own factory
final String factory = getClass().getName();
final Reference ref = new Reference(getClass().getName(), factory, null);
ref.add(new StringRefAddr("description", getDescription()));
ref.add(new StringRefAddr("driver", getDriver()));
ref.add(new StringRefAddr("loginTimeout", String.valueOf(getLoginTimeout())));
ref.add(new StringRefAddr(KEY_PASSWORD, getPassword()));
ref.add(new StringRefAddr(KEY_USER, getUser()));
ref.add(new StringRefAddr("url", getUrl()));
ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements())));
ref.add(new StringRefAddr("maxIdle", String.valueOf(getMaxIdle())));
ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", String.valueOf(getTimeBetweenEvictionRunsMillis())));
ref.add(new StringRefAddr("numTestsPerEvictionRun", String.valueOf(getNumTestsPerEvictionRun())));
ref.add(new StringRefAddr("minEvictableIdleTimeMillis", String.valueOf(getMinEvictableIdleTimeMillis())));
ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements())));
return ref;
}
private String getStringContent(RefAddr ra) {
return ra.getContent().toString();
}
/**
* Gets the number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no
* idle object evictor thread will be run.
*
* @return the value of the evictor thread timer
* @see #setTimeBetweenEvictionRunsMillis(long)
*/
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
/**
* Gets the value of url used to locate the database for this datasource.
*
* @return value of url.
*/
public String getUrl() {
return url;
}
/**
* Gets the value of default user (login or user name).
*
* @return value of user.
*/
public String getUser() {
return userName;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
private boolean isNotEmpty(RefAddr ra) {
return ra != null && ra.getContent() != null;
}
/**
* Whether to toggle the pooling of <code>PreparedStatement</code>s
*
* @return value of poolPreparedStatements.
*/
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
/**
* Sets the connection properties passed to the JDBC driver.
* <p>
* If <code>props</code> contains "user" and/or "password" properties, the corresponding instance properties are
* set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()}
* when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when
* {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or
* {@link #setPassword(String)} overwrite the values of these properties if <code>connectionProperties</code> is not
* null.
* </p>
*
* @param props
* Connection properties to use when creating new connections.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setConnectionProperties(final Properties props) {
assertInitializationAllowed();
connectionProperties = props;
if (connectionProperties != null) {
if (connectionProperties.containsKey(KEY_USER)) {
setUser(connectionProperties.getProperty(KEY_USER));
}
if (connectionProperties.containsKey(KEY_PASSWORD)) {
setPassword(connectionProperties.getProperty(KEY_PASSWORD));
}
}
}
/**
* Sets the value of description. This property is here for use by the code which will deploy this datasource. It is
* not used internally.
*
* @param v
* Value to assign to description.
*/
public void setDescription(final String v) {
this.description = v;
}
/**
* Sets the driver class name. Setting the driver class name cause the driver to be registered with the
* DriverManager.
*
* @param v
* Value to assign to driver.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
* @throws ClassNotFoundException
* if the class cannot be located
*/
public void setDriver(final String v) throws ClassNotFoundException {
assertInitializationAllowed();
this.driver = v;
// make sure driver is registered
Class.forName(v);
}
/**
* Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. NOT
* USED.
*/
@Override
public void setLoginTimeout(final int seconds) {
loginTimeout = seconds;
}
/**
* Sets the log writer for this data source. NOT USED.
*/
@Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
/**
* Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
* negative for no limit.
*
* @param maxIdle
* The maximum number of statements that can remain idle
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setMaxIdle(final int maxIdle) {
assertInitializationAllowed();
this.maxIdle = maxIdle;
}
/**
* Sets the maximum number of prepared statements.
*
* @param maxPreparedStatements
* the new maximum number of prepared statements
*/
public void setMaxPreparedStatements(final int maxPreparedStatements) {
this.maxPreparedStatements = maxPreparedStatements;
}
/**
* Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
* idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone.
*
* @param minEvictableIdleTimeMillis
* minimum time to set (in ms)
* @see #getMinEvictableIdleTimeMillis()
* @see #setTimeBetweenEvictionRunsMillis(long)
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) {
assertInitializationAllowed();
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
/**
* Sets the number of statements to examine during each run of the idle object evictor thread (if any).
* <p>
* When a negative value is supplied, <code>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</code>
* tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the idle objects will be tested
* per run.
* </p>
*
* @param numTestsPerEvictionRun
* number of statements to examine per run
* @see #getNumTestsPerEvictionRun()
* @see #setTimeBetweenEvictionRunsMillis(long)
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
assertInitializationAllowed();
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
}
/**
* Sets the value of password for the default user.
*
* @param userPassword
* Value to assign to password.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPassword(final char[] userPassword) {
assertInitializationAllowed();
this.userPassword = Utils.clone(userPassword);
update(connectionProperties, KEY_PASSWORD, Utils.toString(this.userPassword));
}
/**
* Sets the value of password for the default user.
*
* @param userPassword
* Value to assign to password.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPassword(final String userPassword) {
assertInitializationAllowed();
this.userPassword = Utils.toCharArray(userPassword);
update(connectionProperties, KEY_PASSWORD, userPassword);
}
/**
* Whether to toggle the pooling of <code>PreparedStatement</code>s
*
* @param poolPreparedStatements
* true to pool statements.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setPoolPreparedStatements(final boolean poolPreparedStatements) {
assertInitializationAllowed();
this.poolPreparedStatements = poolPreparedStatements;
}
/**
* Sets the number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no
* idle object evictor thread will be run.
*
* @param timeBetweenEvictionRunsMillis
* The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,
* no idle object evictor thread will be run.
* @see #getTimeBetweenEvictionRunsMillis()
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
assertInitializationAllowed();
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
/**
* Sets the value of URL string used to locate the database for this datasource.
*
* @param v
* Value to assign to url.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setUrl(final String v) {
assertInitializationAllowed();
this.url = v;
}
/**
* Sets the value of default user (login or user name).
*
* @param v
* Value to assign to user.
* @throws IllegalStateException
* if {@link #getPooledConnection()} has been called
*/
public void setUser(final String v) {
assertInitializationAllowed();
this.userName = v;
update(connectionProperties, KEY_USER, v);
}
/**
* Does not print the userName and userPassword field nor the 'user' or 'password' in the connectionProperties.
*
* @since 2.6.0
*/
@Override
public synchronized String toString() {
final StringBuilder builder = new StringBuilder(super.toString());
builder.append("[description=");
builder.append(description);
builder.append(", url=");
// TODO What if the connection string contains a 'user' or 'password' query parameter but that connection string is not in a legal URL format?
builder.append(url);
builder.append(", driver=");
builder.append(driver);
builder.append(", loginTimeout=");
builder.append(loginTimeout);
builder.append(", poolPreparedStatements=");
builder.append(poolPreparedStatements);
builder.append(", maxIdle=");
builder.append(maxIdle);
builder.append(", timeBetweenEvictionRunsMillis=");
builder.append(timeBetweenEvictionRunsMillis);
builder.append(", numTestsPerEvictionRun=");
builder.append(numTestsPerEvictionRun);
builder.append(", minEvictableIdleTimeMillis=");
builder.append(minEvictableIdleTimeMillis);
builder.append(", maxPreparedStatements=");
builder.append(maxPreparedStatements);
builder.append(", getConnectionCalled=");
builder.append(getConnectionCalled);
builder.append(", connectionProperties=");
Properties tmpProps = connectionProperties;
final String pwdKey = "password";
if (connectionProperties != null && connectionProperties.contains(pwdKey)) {
tmpProps = (Properties) connectionProperties.clone();
tmpProps.remove(pwdKey);
}
builder.append(tmpProps);
builder.append(", accessToUnderlyingConnectionAllowed=");
builder.append(accessToUnderlyingConnectionAllowed);
builder.append("]");
return builder.toString();
}
private void update(final Properties properties, final String key, final String value) {
if (properties != null && key != null) {
if (value == null) {
properties.remove(key);
} else {
properties.setProperty(key, value);
}
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.dbcp.dbcp2.cpdsadapter;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
/**
* A key uniquely identifying a {@link java.sql.PreparedStatement PreparedStatement}.
*
* @since 2.0
* @deprecated Use {@link PStmtKey}.
*/
@Deprecated
public class PStmtKeyCPDS extends PStmtKey {
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
*/
public PStmtKeyCPDS(final String sql) {
super(sql);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
*/
public PStmtKeyCPDS(final String sql, final int autoGeneratedKeys) {
super(sql, null, autoGeneratedKeys);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*/
public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency) {
super(sql, resultSetType, resultSetConcurrency);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
*/
public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
super(sql, null, resultSetType, resultSetConcurrency, resultSetHoldability);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
*/
public PStmtKeyCPDS(final String sql, final int columnIndexes[]) {
super(sql, null, columnIndexes);
}
/**
* Constructs a key to uniquely identify a prepared statement.
*
* @param sql
* The SQL statement.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
*/
public PStmtKeyCPDS(final String sql, final String columnNames[]) {
super(sql, null, columnNames);
}
}

View File

@@ -0,0 +1,731 @@
/*
* 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.dbcp.dbcp2.cpdsadapter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Vector;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;
import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection;
import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.Jdbc41Bridge;
import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
import org.apache.tomcat.dbcp.dbcp2.PoolableCallableStatement;
import org.apache.tomcat.dbcp.dbcp2.PoolablePreparedStatement;
import org.apache.tomcat.dbcp.dbcp2.PoolingConnection.StatementType;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* Implementation of PooledConnection that is returned by PooledConnectionDataSource.
*
* @since 2.0
*/
class PooledConnectionImpl
implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
/**
* The JDBC database connection that represents the physical db connection.
*/
private Connection connection;
/**
* A DelegatingConnection used to create a PoolablePreparedStatementStub.
*/
private final DelegatingConnection<?> delegatingConnection;
/**
* The JDBC database logical connection.
*/
private Connection logicalConnection;
/**
* ConnectionEventListeners.
*/
private final Vector<ConnectionEventListener> eventListeners;
/**
* StatementEventListeners.
*/
private final Vector<StatementEventListener> statementEventListeners = new Vector<>();
/**
* Flag set to true, once {@link #close()} is called.
*/
private boolean closed;
/** My pool of {@link PreparedStatement}s. */
private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
/**
* Controls access to the underlying connection.
*/
private boolean accessToUnderlyingConnectionAllowed;
/**
* Wraps the real connection.
*
* @param connection
* the connection to be wrapped.
*/
PooledConnectionImpl(final Connection connection) {
this.connection = connection;
if (connection instanceof DelegatingConnection) {
this.delegatingConnection = (DelegatingConnection<?>) connection;
} else {
this.delegatingConnection = new DelegatingConnection<>(connection);
}
eventListeners = new Vector<>();
closed = false;
}
/**
* My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
*
* @param key
* Ignored.
* @param pooledObject
* Ignored.
*/
@Override
public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().activate();
}
/**
* {@inheritDoc}
*/
@Override
public void addConnectionEventListener(final ConnectionEventListener listener) {
if (!eventListeners.contains(listener)) {
eventListeners.add(listener);
}
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public void addStatementEventListener(final StatementEventListener listener) {
if (!statementEventListeners.contains(listener)) {
statementEventListeners.add(listener);
}
}
/* JDBC_4_ANT_KEY_END */
/**
* Throws an SQLException, if isClosed is true
*/
private void assertOpen() throws SQLException {
if (closed) {
throw new SQLException(CLOSED);
}
}
/**
* Closes the physical connection and marks this <code>PooledConnection</code> so that it may not be used to
* generate any more logical <code>Connection</code>s.
*
* @throws SQLException
* Thrown when an error occurs or the connection is already closed.
*/
@Override
public void close() throws SQLException {
assertOpen();
closed = true;
try {
if (pStmtPool != null) {
try {
pStmtPool.close();
} finally {
pStmtPool = null;
}
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot close connection (return to pool failed)", e);
} finally {
try {
connection.close();
} finally {
connection = null;
}
}
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @return a {@link PStmtKey} for the given arguments.
*/
protected PStmtKey createKey(final String sql) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param autoGeneratedKeys
* A flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param columnIndexes
* An array of column indexes indicating the columns that should be returned from the inserted row or
* rows.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int columnIndexes[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, resultSetHoldability);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
* @since 2.4.0
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, resultSetHoldability, statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param resultSetType
* A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
* @since 2.4.0
*/
protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
resultSetConcurrency, statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param statementType
* The SQL statement type, prepared or callable.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final StatementType statementType) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType);
}
/**
* Creates a {@link PStmtKey} for the given arguments.
*
* @param sql
* The SQL statement.
* @param columnNames
* An array of column names indicating the columns that should be returned from the inserted row or rows.
* @return a key to uniquely identify a prepared statement.
*/
protected PStmtKey createKey(final String sql, final String columnNames[]) {
return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
}
/**
* My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
*
* @param key
* ignored
* @param pooledObject
* the wrapped {@link PreparedStatement} to be destroyed.
*/
@Override
public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
pooledObject.getObject().getInnermostDelegate().close();
}
/**
* Closes the physical connection and checks that the logical connection was closed as well.
*/
@Override
protected void finalize() throws Throwable {
// Closing the Connection ensures that if anyone tries to use it,
// an error will occur.
try {
connection.close();
} catch (final Exception ignored) {
// ignore
}
// make sure the last connection is marked as closed
if (logicalConnection != null && !logicalConnection.isClosed()) {
throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
}
}
private String getCatalogOrNull() {
try {
return connection == null ? null : connection.getCatalog();
} catch (final SQLException e) {
return null;
}
}
private String getSchemaOrNull() {
try {
return connection == null ? null : Jdbc41Bridge.getSchema(connection);
} catch (final SQLException e) {
return null;
}
}
/**
* Returns a JDBC connection.
*
* @return The database connection.
* @throws SQLException
* if the connection is not open or the previous logical connection is still open
*/
@Override
public Connection getConnection() throws SQLException {
assertOpen();
// make sure the last connection is marked as closed
if (logicalConnection != null && !logicalConnection.isClosed()) {
// should notify pool of error so the pooled connection can
// be removed !FIXME!
throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
}
// the spec requires that this return a new Connection instance.
logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
return logicalConnection;
}
/**
* Returns the value of the accessToUnderlyingConnectionAllowed property.
*
* @return true if access to the underlying is allowed, false otherwise.
*/
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return this.accessToUnderlyingConnectionAllowed;
}
/**
* My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
*
* @param key
* The key for the {@link PreparedStatement} to be created.
*/
@SuppressWarnings("resource")
@Override
public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
if (null == key) {
throw new IllegalArgumentException("Prepared statement key is null or invalid.");
}
if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
@SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
delegatingConnection);
return new DefaultPooledObject<DelegatingPreparedStatement>(pps);
}
final CallableStatement statement = (CallableStatement) key.createStatement(connection);
@SuppressWarnings("unchecked")
final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
(DelegatingConnection<Connection>) delegatingConnection);
return new DefaultPooledObject<DelegatingPreparedStatement>(pcs);
}
/**
* Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
* @param sql
* The SQL statement.
* @return the normalized SQL statement.
*/
protected String normalizeSQL(final String sql) {
return sql.trim();
}
/**
* Sends a connectionClosed event.
*/
void notifyListeners() {
final ConnectionEvent event = new ConnectionEvent(this);
final Object[] listeners = eventListeners.toArray();
for (final Object listener : listeners) {
((ConnectionEventListener) listener).connectionClosed(event);
}
}
/**
* My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently invokes
* {@link PreparedStatement#clearParameters}.
*
* @param key
* ignored
* @param pooledObject
* a wrapped {@link PreparedStatement}
*/
@Override
public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
throws Exception {
@SuppressWarnings("resource")
final DelegatingPreparedStatement dps = pooledObject.getObject();
dps.clearParameters();
dps.passivate();
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
* specified using JDBC call escape syntax.
* @return a default <code>CallableStatement</code> object containing the pre-compiled SQL statement.
* @exception SQLException
* Thrown if a database access error occurs or this method is called on a closed connection.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql) throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql);
}
try {
return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @return a <code>CallableStatement</code> object containing the pre-compiled SQL statement that will produce
* <code>ResultSet</code> objects with the given type and concurrency.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type and concurrency.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
}
try {
return (CallableStatement) pStmtPool.borrowObject(
createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link CallableStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain on or
* more '?' parameters.
* @param resultSetType
* one of the following <code>ResultSet</code> constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* one of the following <code>ResultSet</code> constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
* @param resultSetHoldability
* one of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
* or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
* @return a new <code>CallableStatement</code> object, containing the pre-compiled SQL statement, that will
* generate <code>ResultSet</code> objects with the given type, concurrency, and holdability.
* @throws SQLException
* Thrown if a database access error occurs, this method is called on a closed connection or the given
* parameters are not <code>ResultSet</code> constants indicating type, concurrency, and holdability.
* @since 2.4.0
*/
CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (pStmtPool == null) {
return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
try {
return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, StatementType.CALLABLE_STATEMENT));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareCall from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* the SQL statement.
* @return a {@link PoolablePreparedStatement}
*/
PreparedStatement prepareStatement(final String sql) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql);
}
try {
return pStmtPool.borrowObject(createKey(sql));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* an SQL statement that may contain one or more '?' IN parameter placeholders.
* @param autoGeneratedKeys
* a flag indicating whether auto-generated keys should be returned; one of
* <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
* @return a {@link PoolablePreparedStatement}
* @see Connection#prepareStatement(String, int)
*/
PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, autoGeneratedKeys);
}
try {
return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final int columnIndexes[]) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, columnIndexes);
}
try {
return pStmtPool.borrowObject(createKey(sql, columnIndexes));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* Creates or obtains a {@link PreparedStatement} from my pool.
*
* @param sql
* a <code>String</code> object that is the SQL statement to be sent to the database; may contain one or
* more '?' IN parameters.
* @param resultSetType
* a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
* <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
* @param resultSetConcurrency
* a concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
* <code>ResultSet.CONCUR_UPDATABLE</code>.
*
* @return a {@link PoolablePreparedStatement}.
* @see Connection#prepareStatement(String, int, int)
*/
PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
try {
return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
final int resultSetHoldability) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
try {
return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
PreparedStatement prepareStatement(final String sql, final String columnNames[]) throws SQLException {
if (pStmtPool == null) {
return connection.prepareStatement(sql, columnNames);
}
try {
return pStmtPool.borrowObject(createKey(sql, columnNames));
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Borrow prepareStatement from pool failed", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeConnectionEventListener(final ConnectionEventListener listener) {
eventListeners.remove(listener);
}
/* JDBC_4_ANT_KEY_BEGIN */
@Override
public void removeStatementEventListener(final StatementEventListener listener) {
statementEventListeners.remove(listener);
}
/* JDBC_4_ANT_KEY_END */
/**
* Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
* the underlying connection. (Default: false.)
*
* @param allow
* Access to the underlying connection is granted when true.
*/
public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
this.accessToUnderlyingConnectionAllowed = allow;
}
public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
pStmtPool = statementPool;
}
/**
* My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
*
* @param key
* Ignored.
* @param pooledObject
* Ignored.
* @return {@code true}
*/
@Override
public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
return true;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.
*/
/**
* <p>
* This package contains one public class which is a
* <code>ConnectionPoolDataSource</code> (CPDS) implementation that can be used to
* adapt older <code>Driver</code> based JDBC implementations. Below is an
* example of setting up the CPDS to be available via JNDI in the
* catalina servlet container.
* </p>
* <p>In server.xml, the following would be added to the &lt;Context&gt; for your
* webapp:
* </p>
*
* <pre>
* &lt;Resource name="jdbc/bookstoreCPDS" auth="Container"
* type="org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS"/&gt;
* &lt;ResourceParams name="jdbc/bookstoreCPDS"&gt;
* &lt;parameter&gt;
* &lt;name&gt;factory&lt;/name&gt;
* &lt;value&gt;org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;&lt;name&gt;user&lt;/name&gt;&lt;value&gt;root&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;&lt;name&gt;password&lt;/name&gt;&lt;value&gt;&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;driver&lt;/name&gt;
* &lt;value&gt;org.gjt.mm.mysql.Driver&lt;/value&gt;&lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;url&lt;/name&gt;
* &lt;value&gt;jdbc:mysql://localhost:3306/bookstore&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;/ResourceParams&gt;
* </pre>
*
* <p>
* In web.xml. Note that elements must be given in the order of the dtd
* described in the servlet specification:
* </p>
*
* <pre>
* &lt;resource-ref&gt;
* &lt;description&gt;
* Resource reference to a factory for java.sql.Connection
* instances that may be used for talking to a particular
* database that is configured in the server.xml file.
* &lt;/description&gt;
* &lt;res-ref-name&gt;
* jdbc/bookstoreCPDS
* &lt;/res-ref-name&gt;
* &lt;res-type&gt;
* org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS
* &lt;/res-type&gt;
* &lt;res-auth&gt;
* Container
* &lt;/res-auth&gt;
* &lt;/resource-ref&gt;
* </pre>
*
* <p>
* Catalina deploys all objects configured similarly to above within the
* <strong>java:comp/env</strong> namespace.
* </p>
*/
package org.apache.tomcat.dbcp.dbcp2.cpdsadapter;

View File

@@ -0,0 +1,426 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.ObjectPool;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.PooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link PooledObjectFactory} that creates
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnection PoolableConnection}s.
*
* @since 2.0
*/
class CPDSConnectionFactory
implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager {
private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection.";
private final ConnectionPoolDataSource cpds;
private final String validationQuery;
private final int validationQueryTimeoutSeconds;
private final boolean rollbackAfterValidation;
private ObjectPool<PooledConnectionAndInfo> pool;
private final String userName;
private char[] userPassword;
private long maxConnLifetimeMillis = -1;
/**
* Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
*/
private final Set<PooledConnection> validatingSet = Collections
.newSetFromMap(new ConcurrentHashMap<PooledConnection, Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnection's
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* Timeout in seconds before validation fails
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
* @param userName
* The user name to use to create connections
* @param userPassword
* The password to use to create connections
* @since 2.4.0
*/
public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
final char[] userPassword) {
this.cpds = cpds;
this.validationQuery = validationQuery;
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
this.userName = userName;
this.userPassword = userPassword;
this.rollbackAfterValidation = rollbackAfterValidation;
}
/**
* Creates a new {@code PoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnection's
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* Timeout in seconds before validation fails
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
* @param userName
* The user name to use to create connections
* @param userPassword
* The password to use to create connections
*/
public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
final String userPassword) {
this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName,
Utils.toCharArray(userPassword));
}
/**
* (Testing API) Gets the value of password for the default user.
*
* @return value of password.
*/
char[] getPasswordCharArray() {
return userPassword;
}
/**
* Returns the object pool used to pool connections created by this factory.
*
* @return ObjectPool managing pooled connections
*/
public ObjectPool<PooledConnectionAndInfo> getPool() {
return pool;
}
/**
*
* @param pool
* the {@link ObjectPool} in which to pool those {@link Connection}s
*/
public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) {
this.pool = pool;
}
@Override
public synchronized PooledObject<PooledConnectionAndInfo> makeObject() {
PooledConnectionAndInfo pci;
try {
PooledConnection pc = null;
if (userName == null) {
pc = cpds.getPooledConnection();
} else {
pc = cpds.getPooledConnection(userName, Utils.toString(userPassword));
}
if (pc == null) {
throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
}
// should we add this object as a listener or the pool.
// consider the validateObject method in decision
pc.addConnectionEventListener(this);
pci = new PooledConnectionAndInfo(pc, userName, userPassword);
pcMap.put(pc, pci);
} catch (final SQLException e) {
throw new RuntimeException(e.getMessage());
}
return new DefaultPooledObject<>(pci);
}
/**
* Closes the PooledConnection and stops listening for events from it.
*/
@Override
public void destroyObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
doDestroyObject(p.getObject());
}
private void doDestroyObject(final PooledConnectionAndInfo pci) throws Exception {
final PooledConnection pc = pci.getPooledConnection();
pc.removeConnectionEventListener(this);
pcMap.remove(pc);
pc.close();
}
@Override
public boolean validateObject(final PooledObject<PooledConnectionAndInfo> p) {
try {
validateLifetime(p);
} catch (final Exception e) {
return false;
}
boolean valid = false;
final PooledConnection pconn = p.getObject().getPooledConnection();
Connection conn = null;
validatingSet.add(pconn);
if (null == validationQuery) {
int timeoutSeconds = validationQueryTimeoutSeconds;
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
try {
conn = pconn.getConnection();
valid = conn.isValid(timeoutSeconds);
} catch (final SQLException e) {
valid = false;
} finally {
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
} else {
Statement stmt = null;
ResultSet rset = null;
// logical Connection from the PooledConnection must be closed
// before another one can be requested and closing it will
// generate an event. Keep track so we know not to return
// the PooledConnection
validatingSet.add(pconn);
try {
conn = pconn.getConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(validationQuery);
if (rset.next()) {
valid = true;
} else {
valid = false;
}
if (rollbackAfterValidation) {
conn.rollback();
}
} catch (final Exception e) {
valid = false;
} finally {
Utils.closeQuietly(rset);
Utils.closeQuietly(stmt);
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
}
return valid;
}
@Override
public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
@Override
public void activateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
// ***********************************************************************
// java.sql.ConnectionEventListener implementation
// ***********************************************************************
/**
* This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
* user calls the close() method of this connection object. What we need to do here is to release this
* PooledConnection from our pool...
*/
@Override
public void connectionClosed(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
// if this event occurred because we were validating, ignore it
// otherwise return the connection to the pool.
if (!validatingSet.contains(pc)) {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.returnObject(pci);
} catch (final Exception e) {
System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
pc.removeConnectionEventListener(this);
try {
doDestroyObject(pci);
} catch (final Exception e2) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e2.printStackTrace();
}
}
}
}
/**
* If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
*/
@Override
public void connectionErrorOccurred(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
if (null != event.getSQLException()) {
System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
}
pc.removeConnectionEventListener(this);
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(pci);
} catch (final Exception e) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e.printStackTrace();
}
}
// ***********************************************************************
// PooledConnectionManager implementation
// ***********************************************************************
/**
* Invalidates the PooledConnection in the pool. The CPDSConnectionFactory closes the connection and pool counters
* are updated appropriately. Also closes the pool. This ensures that all idle connections are closed and
* connections that are checked out are closed on return.
*/
@Override
public void invalidate(final PooledConnection pc) throws SQLException {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(pci); // Destroy instance and update pool counters
pool.close(); // Clear any other instances in this pool and kill others as they come back
} catch (final Exception ex) {
throw new SQLException("Error invalidating connection", ex);
}
}
/**
* Sets the database password used when creating new connections.
*
* @param userPassword
* new password
*/
public synchronized void setPassword(final char[] userPassword) {
this.userPassword = Utils.clone(userPassword);
}
/**
* Sets the database password used when creating new connections.
*
* @param userPassword
* new password
*/
@Override
public synchronized void setPassword(final String userPassword) {
this.userPassword = Utils.toCharArray(userPassword);
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation.
*
* @param maxConnLifetimeMillis
* A value of zero or less indicates an infinite lifetime. The default value is -1.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* Verifies that the user name matches the user whose connections are being managed by this factory and closes the
* pool if this is the case; otherwise does nothing.
*/
@Override
public void closePool(final String userName) throws SQLException {
synchronized (this) {
if (userName == null || !userName.equals(this.userName)) {
return;
}
}
try {
pool.close();
} catch (final Exception ex) {
throw new SQLException("Error closing connection pool", ex);
}
}
private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", Long.valueOf(lifetime),
Long.valueOf(maxConnLifetimeMillis)));
}
}
}
/**
* @since 2.6.0
*/
@Override
public synchronized String toString() {
final StringBuilder builder = new StringBuilder(super.toString());
builder.append("[cpds=");
builder.append(cpds);
builder.append(", validationQuery=");
builder.append(validationQuery);
builder.append(", validationQueryTimeoutSeconds=");
builder.append(validationQueryTimeoutSeconds);
builder.append(", rollbackAfterValidation=");
builder.append(rollbackAfterValidation);
builder.append(", pool=");
builder.append(pool);
builder.append(", maxConnLifetimeMillis=");
builder.append(maxConnLifetimeMillis);
builder.append(", validatingSet=");
builder.append(validatingSet);
builder.append(", pcMap=");
builder.append(pcMap);
builder.append("]");
return builder.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,340 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.tomcat.dbcp.dbcp2.ListException;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s or <code>PerUserPoolDataSource</code>s
*
* @since 2.0
*/
abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
private static final Map<String, InstanceKeyDataSource> instanceMap = new ConcurrentHashMap<>();
static synchronized String registerNewInstance(final InstanceKeyDataSource ds) {
int max = 0;
final Iterator<String> iterator = instanceMap.keySet().iterator();
while (iterator.hasNext()) {
final String s = iterator.next();
if (s != null) {
try {
max = Math.max(max, Integer.parseInt(s));
} catch (final NumberFormatException e) {
// no sweat, ignore those keys
}
}
}
final String instanceKey = String.valueOf(max + 1);
// Put a placeholder here for now, so other instances will not
// take our key. We will replace with a pool when ready.
instanceMap.put(instanceKey, ds);
return instanceKey;
}
static void removeInstance(final String key) {
if (key != null) {
instanceMap.remove(key);
}
}
/**
* Closes all pools associated with this class.
*
* @throws Exception
* a {@link ListException} containing all exceptions thrown by {@link InstanceKeyDataSource#close()}
* @see InstanceKeyDataSource#close()
* @see ListException
* @since 2.4.0 throws a {@link ListException} instead of, in 2.3.0 and before, the first exception thrown by
* {@link InstanceKeyDataSource#close()}.
*/
public static void closeAll() throws Exception {
// Get iterator to loop over all instances of this data source.
final List<Throwable> exceptionList = new ArrayList<>(instanceMap.size());
final Iterator<Entry<String, InstanceKeyDataSource>> instanceIterator = instanceMap.entrySet().iterator();
while (instanceIterator.hasNext()) {
// Bullet-proof to avoid anything else but problems from InstanceKeyDataSource#close().
final Entry<String, InstanceKeyDataSource> next = instanceIterator.next();
if (next != null) {
@SuppressWarnings("resource")
final InstanceKeyDataSource value = next.getValue();
if (value != null) {
try {
value.close();
} catch (final Exception e) {
exceptionList.add(e);
}
}
}
}
instanceMap.clear();
if (!exceptionList.isEmpty()) {
throw new ListException("Could not close all InstanceKeyDataSource instances.", exceptionList);
}
}
/**
* Implements ObjectFactory to create an instance of SharedPoolDataSource or PerUserPoolDataSource
*/
@Override
public Object getObjectInstance(final Object refObj, final Name name, final Context context,
final Hashtable<?, ?> env) throws IOException, ClassNotFoundException {
// The spec says to return null if we can't create an instance
// of the reference
Object obj = null;
if (refObj instanceof Reference) {
final Reference ref = (Reference) refObj;
if (isCorrectClass(ref.getClassName())) {
final RefAddr refAddr = ref.get("instanceKey");
if (refAddr != null && refAddr.getContent() != null) {
// object was bound to JNDI via Referenceable API.
obj = instanceMap.get(refAddr.getContent());
} else {
// Tomcat JNDI creates a Reference out of server.xml
// <ResourceParam> configuration and passes it to an
// instance of the factory given in server.xml.
String key = null;
if (name != null) {
key = name.toString();
obj = instanceMap.get(key);
}
if (obj == null) {
final InstanceKeyDataSource ds = getNewInstance(ref);
setCommonProperties(ref, ds);
obj = ds;
if (key != null) {
instanceMap.put(key, ds);
}
}
}
}
}
return obj;
}
private void setCommonProperties(final Reference ref, final InstanceKeyDataSource ikds)
throws IOException, ClassNotFoundException {
RefAddr refAddr = ref.get("dataSourceName");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDataSourceName(refAddr.getContent().toString());
}
refAddr = ref.get("description");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDescription(refAddr.getContent().toString());
}
refAddr = ref.get("jndiEnvironment");
if (refAddr != null && refAddr.getContent() != null) {
final byte[] serialized = (byte[]) refAddr.getContent();
ikds.setJndiEnvironment((Properties) deserialize(serialized));
}
refAddr = ref.get("loginTimeout");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setLoginTimeout(Integer.parseInt(refAddr.getContent().toString()));
}
// Pool properties
refAddr = ref.get("blockWhenExhausted");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultBlockWhenExhausted(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("evictionPolicyClassName");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultEvictionPolicyClassName(refAddr.getContent().toString());
}
// Pool properties
refAddr = ref.get("lifo");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultLifo(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("maxIdlePerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxIdle(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("maxTotalPerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxTotal(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("maxWaitMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMaxWaitMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("minEvictableIdleTimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMinEvictableIdleTimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("minIdlePerKey");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultMinIdle(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("numTestsPerEvictionRun");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultNumTestsPerEvictionRun(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("softMinEvictableIdleTimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultSoftMinEvictableIdleTimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
refAddr = ref.get("testOnCreate");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnCreate(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testOnBorrow");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnBorrow(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testOnReturn");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestOnReturn(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("testWhileIdle");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTestWhileIdle(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("timeBetweenEvictionRunsMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTimeBetweenEvictionRunsMillis(Long.parseLong(refAddr.getContent().toString()));
}
// Connection factory properties
refAddr = ref.get("validationQuery");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setValidationQuery(refAddr.getContent().toString());
}
refAddr = ref.get("validationQueryTimeout");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setValidationQueryTimeout(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("rollbackAfterValidation");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setRollbackAfterValidation(Boolean.valueOf(refAddr.getContent().toString()).booleanValue());
}
refAddr = ref.get("maxConnLifetimeMillis");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setMaxConnLifetimeMillis(Long.parseLong(refAddr.getContent().toString()));
}
// Connection properties
refAddr = ref.get("defaultAutoCommit");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultAutoCommit(Boolean.valueOf(refAddr.getContent().toString()));
}
refAddr = ref.get("defaultTransactionIsolation");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultTransactionIsolation(Integer.parseInt(refAddr.getContent().toString()));
}
refAddr = ref.get("defaultReadOnly");
if (refAddr != null && refAddr.getContent() != null) {
ikds.setDefaultReadOnly(Boolean.valueOf(refAddr.getContent().toString()));
}
}
/**
* @param className
* The class name to test.
*
* @return true if and only if className is the value returned from getClass().getName().toString()
*/
protected abstract boolean isCorrectClass(String className);
/**
* Creates an instance of the subclass and sets any properties contained in the Reference.
*
* @param ref
* The properties to be set on the created DataSource
*
* @return A configured DataSource of the appropriate type.
*
* @throws ClassNotFoundException
* If a class cannot be found during the deserialization of a configuration parameter.
* @throws IOException
* If an I/O error occurs during the deserialization of a configuration parameter.
*/
protected abstract InstanceKeyDataSource getNewInstance(Reference ref) throws IOException, ClassNotFoundException;
/**
* Deserializes the provided byte array to create an object.
*
* @param data
* Data to deserialize to create the configuration parameter.
*
* @return The Object created by deserializing the data.
*
* @throws ClassNotFoundException
* If a class cannot be found during the deserialization of a configuration parameter.
* @throws IOException
* If an I/O error occurs during the deserialization of a configuration parameter.
*/
protected static final Object deserialize(final byte[] data) throws IOException, ClassNotFoundException {
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new ByteArrayInputStream(data));
return in.readObject();
} finally {
if (in != null) {
try {
in.close();
} catch (final IOException ex) {
// ignore
}
}
}
}
}

View File

@@ -0,0 +1,351 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
/**
* A {@link KeyedPooledObjectFactory} that creates {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnection
* PoolableConnection}s.
*
* @since 2.0
*/
class KeyedCPDSConnectionFactory implements KeyedPooledObjectFactory<UserPassKey, PooledConnectionAndInfo>,
ConnectionEventListener, PooledConnectionManager {
private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but "
+ "I have no record of the underlying PooledConnection.";
private final ConnectionPoolDataSource cpds;
private final String validationQuery;
private final int validationQueryTimeoutSeconds;
private final boolean rollbackAfterValidation;
private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
private long maxConnLifetimeMillis = -1;
/**
* Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
*/
private final Set<PooledConnection> validatingSet = Collections
.newSetFromMap(new ConcurrentHashMap<PooledConnection, Boolean>());
/**
* Map of PooledConnectionAndInfo instances
*/
private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
/**
* Create a new {@code KeyedPoolableConnectionFactory}.
*
* @param cpds
* the ConnectionPoolDataSource from which to obtain PooledConnections
* @param validationQuery
* a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
* row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
* connections.
* @param validationQueryTimeoutSeconds
* The time, in seconds, to allow for the validation query to complete
* @param rollbackAfterValidation
* whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
*/
public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
this.cpds = cpds;
this.validationQuery = validationQuery;
this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
this.rollbackAfterValidation = rollbackAfterValidation;
}
public void setPool(final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool) {
this.pool = pool;
}
/**
* Returns the keyed object pool used to pool connections created by this factory.
*
* @return KeyedObjectPool managing pooled connections
*/
public KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> getPool() {
return pool;
}
/**
* Creates a new {@link PooledConnectionAndInfo} from the given {@link UserPassKey}.
*
* @param upkey
* {@link UserPassKey} containing user credentials
* @throws SQLException
* if the connection could not be created.
* @see org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory#makeObject(java.lang.Object)
*/
@Override
public synchronized PooledObject<PooledConnectionAndInfo> makeObject(final UserPassKey upkey) throws Exception {
PooledConnectionAndInfo pci = null;
PooledConnection pc = null;
final String userName = upkey.getUsername();
final String password = upkey.getPassword();
if (userName == null) {
pc = cpds.getPooledConnection();
} else {
pc = cpds.getPooledConnection(userName, password);
}
if (pc == null) {
throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
}
// should we add this object as a listener or the pool.
// consider the validateObject method in decision
pc.addConnectionEventListener(this);
pci = new PooledConnectionAndInfo(pc, userName, upkey.getPasswordCharArray());
pcMap.put(pc, pci);
return new DefaultPooledObject<>(pci);
}
/**
* Closes the PooledConnection and stops listening for events from it.
*/
@Override
public void destroyObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
final PooledConnection pc = p.getObject().getPooledConnection();
pc.removeConnectionEventListener(this);
pcMap.remove(pc);
pc.close();
}
/**
* Validates a pooled connection.
*
* @param key
* ignored
* @param pooledObject
* wrapped {@link PooledConnectionAndInfo} containing the connection to validate
* @return true if validation succeeds
*/
@Override
public boolean validateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> pooledObject) {
try {
validateLifetime(pooledObject);
} catch (final Exception e) {
return false;
}
boolean valid = false;
final PooledConnection pconn = pooledObject.getObject().getPooledConnection();
Connection conn = null;
validatingSet.add(pconn);
if (null == validationQuery) {
int timeoutSeconds = validationQueryTimeoutSeconds;
if (timeoutSeconds < 0) {
timeoutSeconds = 0;
}
try {
conn = pconn.getConnection();
valid = conn.isValid(timeoutSeconds);
} catch (final SQLException e) {
valid = false;
} finally {
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
} else {
Statement stmt = null;
ResultSet rset = null;
// logical Connection from the PooledConnection must be closed
// before another one can be requested and closing it will
// generate an event. Keep track so we know not to return
// the PooledConnection
validatingSet.add(pconn);
try {
conn = pconn.getConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(validationQuery);
if (rset.next()) {
valid = true;
} else {
valid = false;
}
if (rollbackAfterValidation) {
conn.rollback();
}
} catch (final Exception e) {
valid = false;
} finally {
Utils.closeQuietly(rset);
Utils.closeQuietly(stmt);
Utils.closeQuietly(conn);
validatingSet.remove(pconn);
}
}
return valid;
}
@Override
public void passivateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
@Override
public void activateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
validateLifetime(p);
}
// ***********************************************************************
// java.sql.ConnectionEventListener implementation
// ***********************************************************************
/**
* This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
* user calls the close() method of this connection object. What we need to do here is to release this
* PooledConnection from our pool...
*/
@Override
public void connectionClosed(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
// if this event occurred because we were validating, or if this
// connection has been marked for removal, ignore it
// otherwise return the connection to the pool.
if (!validatingSet.contains(pc)) {
final PooledConnectionAndInfo pci = pcMap.get(pc);
if (pci == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.returnObject(pci.getUserPassKey(), pci);
} catch (final Exception e) {
System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
pc.removeConnectionEventListener(this);
try {
pool.invalidateObject(pci.getUserPassKey(), pci);
} catch (final Exception e3) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
e3.printStackTrace();
}
}
}
}
/**
* If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
*/
@Override
public void connectionErrorOccurred(final ConnectionEvent event) {
final PooledConnection pc = (PooledConnection) event.getSource();
if (null != event.getSQLException()) {
System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
}
pc.removeConnectionEventListener(this);
final PooledConnectionAndInfo info = pcMap.get(pc);
if (info == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
try {
pool.invalidateObject(info.getUserPassKey(), info);
} catch (final Exception e) {
System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + info);
e.printStackTrace();
}
}
// ***********************************************************************
// PooledConnectionManager implementation
// ***********************************************************************
/**
* Invalidates the PooledConnection in the pool. The KeyedCPDSConnectionFactory closes the connection and pool
* counters are updated appropriately. Also clears any idle instances associated with the user name that was used to
* create the PooledConnection. Connections associated with this user are not affected and they will not be
* automatically closed on return to the pool.
*/
@Override
public void invalidate(final PooledConnection pc) throws SQLException {
final PooledConnectionAndInfo info = pcMap.get(pc);
if (info == null) {
throw new IllegalStateException(NO_KEY_MESSAGE);
}
final UserPassKey key = info.getUserPassKey();
try {
pool.invalidateObject(key, info); // Destroy and update pool counters
pool.clear(key); // Remove any idle instances with this key
} catch (final Exception ex) {
throw new SQLException("Error invalidating connection", ex);
}
}
/**
* Does nothing. This factory does not cache user credentials.
*/
@Override
public void setPassword(final String password) {
// Does nothing. This factory does not cache user credentials.
}
/**
* Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
* passivation and validation.
*
* @param maxConnLifetimeMillis
* A value of zero or less indicates an infinite lifetime. The default value is -1.
*/
public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
/**
* This implementation does not fully close the KeyedObjectPool, as this would affect all users. Instead, it clears
* the pool associated with the given user. This method is not currently used.
*/
@Override
public void closePool(final String userName) throws SQLException {
try {
pool.clear(new UserPassKey(userName));
} catch (final Exception ex) {
throw new SQLException("Error closing connection pool", ex);
}
}
private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
if (maxConnLifetimeMillis > 0) {
final long lifetime = System.currentTimeMillis() - p.getCreateTime();
if (lifetime > maxConnLifetimeMillis) {
throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", Long.valueOf(lifetime),
Long.valueOf(maxConnLifetimeMillis)));
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.io.IOException;
import java.util.Map;
import javax.naming.RefAddr;
import javax.naming.Reference;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
*
* @since 2.0
*/
public class PerUserPoolDataSourceFactory extends InstanceKeyDataSourceFactory {
private static final String PER_USER_POOL_CLASSNAME = PerUserPoolDataSource.class.getName();
@Override
protected boolean isCorrectClass(final String className) {
return PER_USER_POOL_CLASSNAME.equals(className);
}
@SuppressWarnings("unchecked") // Avoid warnings on deserialization
@Override
protected InstanceKeyDataSource getNewInstance(final Reference ref) throws IOException, ClassNotFoundException {
final PerUserPoolDataSource pupds = new PerUserPoolDataSource();
RefAddr ra = ref.get("defaultMaxTotal");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxTotal(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("defaultMaxIdle");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxIdle(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("defaultMaxWaitMillis");
if (ra != null && ra.getContent() != null) {
pupds.setDefaultMaxWaitMillis(Integer.parseInt(ra.getContent().toString()));
}
ra = ref.get("perUserDefaultAutoCommit");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultAutoCommit((Map<String, Boolean>) deserialize(serialized));
}
ra = ref.get("perUserDefaultTransactionIsolation");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultTransactionIsolation((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxTotal");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxTotal((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxIdle");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxIdle((Map<String, Integer>) deserialize(serialized));
}
ra = ref.get("perUserMaxWaitMillis");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserMaxWaitMillis((Map<String, Long>) deserialize(serialized));
}
ra = ref.get("perUserDefaultReadOnly");
if (ra != null && ra.getContent() != null) {
final byte[] serialized = (byte[]) ra.getContent();
pupds.setPerUserDefaultReadOnly((Map<String, Boolean>) deserialize(serialized));
}
return pupds;
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.io.Serializable;
/**
* @since 2.0
*/
class PoolKey implements Serializable {
private static final long serialVersionUID = 2252771047542484533L;
private final String dataSourceName;
private final String userName;
PoolKey(final String dataSourceName, final String userName) {
this.dataSourceName = dataSourceName;
this.userName = userName;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PoolKey other = (PoolKey) obj;
if (dataSourceName == null) {
if (other.dataSourceName != null) {
return false;
}
} else if (!dataSourceName.equals(other.dataSourceName)) {
return false;
}
if (userName == null) {
if (other.userName != null) {
return false;
}
} else if (!userName.equals(other.userName)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dataSourceName == null) ? 0 : dataSourceName.hashCode());
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer(50);
sb.append("PoolKey(");
sb.append(userName).append(", ").append(dataSourceName);
sb.append(')');
return sb.toString();
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.dbcp.dbcp2.datasources;
import javax.sql.PooledConnection;
import org.apache.tomcat.dbcp.dbcp2.Utils;
/**
* Immutable poolable object holding a PooledConnection along with the user name and password used to create the
* connection.
*
* @since 2.0
*/
final class PooledConnectionAndInfo {
private final PooledConnection pooledConnection;
private final char[] userPassword;
private final String userName;
private final UserPassKey upKey;
/**
* @since 2.4.0
*/
PooledConnectionAndInfo(final PooledConnection pc, final String userName, final char[] userPassword) {
this.pooledConnection = pc;
this.userName = userName;
this.userPassword = userPassword;
this.upKey = new UserPassKey(userName, userPassword);
}
/**
* @deprecated Since 2.4.0
*/
@Deprecated
PooledConnectionAndInfo(final PooledConnection pc, final String userName, final String userPassword) {
this(pc, userName, Utils.toCharArray(userPassword));
}
PooledConnection getPooledConnection() {
return pooledConnection;
}
UserPassKey getUserPassKey() {
return upKey;
}
/**
* Gets the value of password.
*
* @return value of password.
*/
String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password.
*
* @return value of password.
* @since 2.4.0
*/
char[] getPasswordCharArray() {
return userPassword;
}
/**
* Gets the value of userName.
*
* @return value of userName.
*/
String getUsername() {
return userName;
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.sql.SQLException;
import javax.sql.PooledConnection;
/**
* Methods to manage PoolableConnections and the connection pools that source them.
*
* @since 2.0
*/
interface PooledConnectionManager {
/**
* Closes the PooledConnection and remove it from the connection pool to which it belongs, adjusting pool counters.
*
* @param pc
* PooledConnection to be invalidated
* @throws SQLException
* if an SQL error occurs closing the connection
*/
void invalidate(PooledConnection pc) throws SQLException;
// /**
// * Sets the database password used when creating connections.
// *
// * @param password password used when authenticating to the database
// * @since 3.0.0
// */
// void setPassword(char[] password);
/**
* Sets the database password used when creating connections.
*
* @param password
* password used when authenticating to the database
*/
void setPassword(String password);
/**
* Closes the connection pool associated with the given user.
*
* @param userName
* user name
* @throws SQLException
* if an error occurs closing idle connections in the pool
*/
void closePool(String userName) throws SQLException;
}

View File

@@ -0,0 +1,245 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* <p>
* A pooling <code>DataSource</code> appropriate for deployment within J2EE environment. There are many configuration
* options, most of which are defined in the parent class. All users (based on user name) share a single maximum number
* of Connections in this data source.
* </p>
*
* <p>
* User passwords can be changed without re-initializing the data source. When a
* <code>getConnection(user name, password)</code> request is processed with a password that is different from those
* used to create connections in the pool associated with <code>user name</code>, an attempt is made to create a new
* connection using the supplied password and if this succeeds, idle connections created using the old password are
* destroyed and new connections are created using the new password.
* </p>
*
* @since 2.0
*/
public class SharedPoolDataSource extends InstanceKeyDataSource {
private static final long serialVersionUID = -1458539734480586454L;
// Pool properties
private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
private transient KeyedCPDSConnectionFactory factory;
/**
* Default no-argument constructor for Serialization
*/
public SharedPoolDataSource() {
// empty.
}
/**
* Closes pool being maintained by this data source.
*/
@Override
public void close() throws Exception {
if (pool != null) {
pool.close();
}
InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
}
// -------------------------------------------------------------------
// Properties
/**
* Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*
* @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*/
public int getMaxTotal() {
return this.maxTotal;
}
/**
* Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*
* @param maxTotal
* {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
*/
public void setMaxTotal(final int maxTotal) {
assertInitializationAllowed();
this.maxTotal = maxTotal;
}
// ----------------------------------------------------------------------
// Instrumentation Methods
/**
* Gets the number of active connections in the pool.
*
* @return The number of active connections in the pool.
*/
public int getNumActive() {
return pool == null ? 0 : pool.getNumActive();
}
/**
* Gets the number of idle connections in the pool.
*
* @return The number of idle connections in the pool.
*/
public int getNumIdle() {
return pool == null ? 0 : pool.getNumIdle();
}
// ----------------------------------------------------------------------
// Inherited abstract methods
@Override
protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword)
throws SQLException {
synchronized (this) {
if (pool == null) {
try {
registerPool(userName, userPassword);
} catch (final NamingException e) {
throw new SQLException("RegisterPool failed", e);
}
}
}
PooledConnectionAndInfo info = null;
final UserPassKey key = new UserPassKey(userName, userPassword);
try {
info = pool.borrowObject(key);
} catch (final Exception e) {
throw new SQLException("Could not retrieve connection info from pool", e);
}
return info;
}
@Override
protected PooledConnectionManager getConnectionManager(final UserPassKey upkey) {
return factory;
}
/**
* Returns a <code>SharedPoolDataSource</code> {@link Reference}.
*/
@Override
public Reference getReference() throws NamingException {
final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null);
ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
return ref;
}
private void registerPool(final String userName, final String password) throws NamingException, SQLException {
final ConnectionPoolDataSource cpds = testCPDS(userName, password);
// Create an object pool to contain our PooledConnections
factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeout(),
isRollbackAfterValidation());
factory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = new GenericKeyedObjectPoolConfig<>();
config.setBlockWhenExhausted(getDefaultBlockWhenExhausted());
config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName());
config.setLifo(getDefaultLifo());
config.setMaxIdlePerKey(getDefaultMaxIdle());
config.setMaxTotal(getMaxTotal());
config.setMaxTotalPerKey(getDefaultMaxTotal());
config.setMaxWaitMillis(getDefaultMaxWaitMillis());
config.setMinEvictableIdleTimeMillis(getDefaultMinEvictableIdleTimeMillis());
config.setMinIdlePerKey(getDefaultMinIdle());
config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun());
config.setSoftMinEvictableIdleTimeMillis(getDefaultSoftMinEvictableIdleTimeMillis());
config.setTestOnCreate(getDefaultTestOnCreate());
config.setTestOnBorrow(getDefaultTestOnBorrow());
config.setTestOnReturn(getDefaultTestOnReturn());
config.setTestWhileIdle(getDefaultTestWhileIdle());
config.setTimeBetweenEvictionRunsMillis(getDefaultTimeBetweenEvictionRunsMillis());
final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory,
config);
factory.setPool(tmpPool);
pool = tmpPool;
}
@Override
protected void setupDefaults(final Connection connection, final String userName) throws SQLException {
final Boolean defaultAutoCommit = isDefaultAutoCommit();
if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit.booleanValue()) {
connection.setAutoCommit(defaultAutoCommit.booleanValue());
}
final int defaultTransactionIsolation = getDefaultTransactionIsolation();
if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
connection.setTransactionIsolation(defaultTransactionIsolation);
}
final Boolean defaultReadOnly = isDefaultReadOnly();
if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly.booleanValue()) {
connection.setReadOnly(defaultReadOnly.booleanValue());
}
}
/**
* Supports Serialization interface.
*
* @param in
* a <code>java.io.ObjectInputStream</code> value
* @throws IOException
* if an error occurs
* @throws ClassNotFoundException
* if an error occurs
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
in.defaultReadObject();
final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory()
.getObjectInstance(getReference(), null, null, null);
this.pool = oldDS.pool;
} catch (final NamingException e) {
throw new IOException("NamingException: " + e);
}
}
@Override
protected void toStringFields(final StringBuilder builder) {
super.toStringFields(builder);
builder.append(", maxTotal=");
builder.append(maxTotal);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.dbcp.dbcp2.datasources;
import javax.naming.RefAddr;
import javax.naming.Reference;
/**
* A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
*
* @since 2.0
*/
public class SharedPoolDataSourceFactory extends InstanceKeyDataSourceFactory {
private static final String SHARED_POOL_CLASSNAME = SharedPoolDataSource.class.getName();
@Override
protected boolean isCorrectClass(final String className) {
return SHARED_POOL_CLASSNAME.equals(className);
}
@Override
protected InstanceKeyDataSource getNewInstance(final Reference ref) {
final SharedPoolDataSource spds = new SharedPoolDataSource();
final RefAddr ra = ref.get("maxTotal");
if (ra != null && ra.getContent() != null) {
spds.setMaxTotal(Integer.parseInt(ra.getContent().toString()));
}
return spds;
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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.dbcp.dbcp2.datasources;
import java.io.Serializable;
import org.apache.tomcat.dbcp.dbcp2.Utils;
/**
* <p>
* Holds a user name and password pair. Serves as a poolable object key for the KeyedObjectPool backing a
* SharedPoolDataSource. Two instances with the same user name are considered equal. This ensures that there will be
* only one keyed pool for each user in the pool. The password is used (along with the user name) by the
* KeyedCPDSConnectionFactory when creating new connections.
* </p>
*
* <p>
* {@link InstanceKeyDataSource#getConnection(String, String)} validates that the password used to create a connection
* matches the password provided by the client.
* </p>
*
* @since 2.0
*/
class UserPassKey implements Serializable {
private static final long serialVersionUID = 5142970911626584817L;
private final String userName;
private final char[] userPassword;
/**
* @since 2.4.0
*/
UserPassKey(final String userName) {
this(userName, (char[]) null);
}
/**
* @since 2.4.0
*/
UserPassKey(final String userName, final char[] password) {
this.userName = userName;
this.userPassword = password;
}
UserPassKey(final String userName, final String userPassword) {
this(userName, Utils.toCharArray(userPassword));
}
/**
* Only takes the user name into account.
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UserPassKey other = (UserPassKey) obj;
if (userName == null) {
if (other.userName != null) {
return false;
}
} else if (!userName.equals(other.userName)) {
return false;
}
return true;
}
/**
* Gets the value of password.
*
* @return value of password.
*/
public String getPassword() {
return Utils.toString(userPassword);
}
/**
* Gets the value of password.
*
* @return value of password.
*/
public char[] getPasswordCharArray() {
return userPassword;
}
/**
* Gets the value of user name.
*
* @return value of user name.
*/
public String getUsername() {
return userName;
}
/**
* Only takes the user name into account.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer(super.toString());
sb.append("[");
sb.append(userName);
sb.append(']');
return sb.toString();
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.
*/
/**
* <p>
* This package contains two DataSources: <code>PerUserPoolDataSource</code> and
* <code>SharedPoolDataSource</code> which provide a database connection pool.
* Below are a couple of usage examples. One shows deployment into a JNDI system.
* The other is a simple example initializing the pool using standard java code.
* </p>
*
* <h2>JNDI</h2>
*
* <p>
* Most
* J2EE containers will provide some way of deploying resources into JNDI. The
* method will vary among containers, but once the resource is available via
* JNDI, the application can access the resource in a container independent
* manner. The following example shows deployment into tomcat (catalina).
* </p>
* <p>In server.xml, the following would be added to the &lt;Context&gt; for your
* webapp:
* </p>
*
* <code>
* &lt;Resource name="jdbc/bookstore" auth="Container"
* type="org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolPoolDataSource"/&gt;
* &lt;ResourceParams name="jdbc/bookstore"&gt;
* &lt;parameter&gt;
* &lt;name&gt;factory&lt;/name&gt;
* &lt;value&gt;org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSourceFactory&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;dataSourceName&lt;/name&gt;&lt;value&gt;java:comp/env/jdbc/bookstoreCPDS&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;parameter&gt;
* &lt;name&gt;defaultMaxTotal&lt;/name&gt;&lt;value&gt;30&lt;/value&gt;
* &lt;/parameter&gt;
* &lt;/ResourceParams&gt;
* </code>
*
* <p>
* In web.xml. Note that elements must be given in the order of the dtd
* described in the servlet specification:
* </p>
*
* <code>
* &lt;resource-ref&gt;
* &lt;description&gt;
* Resource reference to a factory for java.sql.Connection
* instances that may be used for talking to a particular
* database that is configured in the server.xml file.
* &lt;/description&gt;
* &lt;res-ref-name&gt;
* jdbc/bookstore
* &lt;/res-ref-name&gt;
* &lt;res-type&gt;
* org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSource
* &lt;/res-type&gt;
* &lt;res-auth&gt;
* Container
* &lt;/res-auth&gt;
* &lt;/resource-ref&gt;
* </code>
*
* <p>
* Apache Tomcat deploys all objects configured similarly to above within the
* <strong>java:comp/env</strong> namespace. So the JNDI path given for
* the dataSourceName parameter is valid for a
* <code>ConnectionPoolDataSource</code> that is deployed as given in the
* <a href="../cpdsadapter/package.html">cpdsadapter example</a>
* </p>
*
* <p>
* The <code>DataSource</code> is now available to the application as shown
* below:
* </p>
*
* <code>
*
* Context ctx = new InitialContext();
* DataSource ds = (DataSource)
* ctx.lookup("java:comp/env/jdbc/bookstore");
* Connection con = null;
* try
* {
* con = ds.getConnection();
* ...
* use the connection
* ...
* }
* finally
* {
* if (con != null)
* con.close();
* }
*
* </code>
*
* <p>
* The reference to the <code>DataSource</code> could be maintained, for
* multiple getConnection() requests. Or the <code>DataSource</code> can be
* looked up in different parts of the application code.
* <code>PerUserPoolDataSourceFactory</code> and
* <code>SharedPoolDataSourceFactory</code> will maintain the state of the pool
* between different lookups. This behavior may be different in other
* implementations.
* </p>
*
* <h2>Without JNDI</h2>
*
* <p>
* Connection pooling is useful in applications regardless of whether they run
* in a J2EE environment and a <code>DataSource</code> can be used within a
* simpler environment. The example below shows SharedPoolDataSource using
* DriverAdapterCPDS as the backend source, though any CPDS is applicable.
* </p>
*
* <code>
*
* public class Pool
* {
* private static DataSource ds;
*
* static
* {
* DriverAdapterCPDS cpds = new DriverAdapterCPDS();
* cpds.setDriver("org.gjt.mm.mysql.Driver");
* cpds.setUrl("jdbc:mysql://localhost:3306/bookstore");
* cpds.setUser("foo");
* cpds.setPassword(null);
*
* SharedPoolDataSource tds = new SharedPoolDataSource();
* tds.setConnectionPoolDataSource(cpds);
* tds.setMaxTotal(10);
* tds.setMaxWaitMillis(50);
*
* ds = tds;
* }
*
* public static getConnection()
* {
* return ds.getConnection();
* }
* }
*
* </code>
*
* <p>
* This class can then be used wherever a connection is needed:
* </p>
*
* <code>
* Connection con = null;
* try
* {
* con = Pool.getConnection();
* ...
* use the connection
* ...
* }
* finally
* {
* if (con != null)
* con.close();
* }
* </code>
*/
package org.apache.tomcat.dbcp.dbcp2.datasources;

View File

@@ -0,0 +1,131 @@
/*
* 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.
*/
/**
* <p>
* Database Connection Pool API.
* </p>
*
* <b>Overview in Dialog Form</b>
* <p>
* Q: How do I use the DBCP package?
* </p>
* <p>
* A: There are two primary ways to access the DBCP pool, as a {@link java.sql.Driver Driver}, or as a
* {@link javax.sql.DataSource DataSource}. You'll want to create an instance of
* {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver} or {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource}. When using one
* of these interfaces, you can just use your JDBC objects the way you normally would. Closing a
* {@link java.sql.Connection} will simply return it to its pool.
* </p>
* <p>
* Q: But {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver PoolingDriver} and
* {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource PoolingDataSource} both expect an
* {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} as an input. Where do I get one of those?
* </p>
* <p>
* A: The {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} interface is defined in Commons Pool. You can use one
* of the provided implementations such as {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool GenericObjectPool} or
* {@link org.apache.tomcat.dbcp.pool2.impl.SoftReferenceObjectPool SoftReferenceObjectPool} or you can create your own.
* </p>
* <p>
* Q: Ok, I've found an {@link org.apache.tomcat.dbcp.pool2.ObjectPool ObjectPool} implementation that I think suits my
* connection pooling needs. But it wants a {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory PooledObjectFactory}.
* What should I use for that?
* </p>
* <p>
* A: The DBCP package provides a class for this purpose. It's called
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}. It implements the factory and lifecycle methods of
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} for {@link java.sql.Connection}s. But it doesn't create the
* actual database {@link java.sql.Connection}s itself, it uses a {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} for
* that. The {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory} will take {@link java.sql.Connection}s created
* by the {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} and wrap them with classes that implement the pooling
* behaviour.
* </p>
* <p>
* Several implementations of {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} are provided--one that uses
* {@link java.sql.DriverManager} to create connections
* ({@link org.apache.tomcat.dbcp.dbcp2.DriverManagerConnectionFactory}), one that uses a {@link java.sql.Driver} to create
* connections ({@link org.apache.tomcat.dbcp.dbcp2.DriverConnectionFactory}), one that uses a {@link javax.sql.DataSource}
* to create connections ({@link org.apache.tomcat.dbcp.dbcp2.DataSourceConnectionFactory}).
* </p>
* <p>
* Q: I think I'm starting to get it, but can you walk me though it again?
* </p>
* <p>
* A: Sure. Let's assume you want to create a {@link javax.sql.DataSource} that pools {@link java.sql.Connection}s.
* Let's also assume that those pooled {@link java.sql.Connection}s should be obtained from the
* {@link java.sql.DriverManager}. You'll want to create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource}.
* </p>
* <p>
* The {@link org.apache.tomcat.dbcp.dbcp2.PoolingDataSource} uses an underlying {@link org.apache.tomcat.dbcp.pool2.ObjectPool}
* to create and store its {@link java.sql.Connection}.
* </p>
* <p>
* To create a {@link org.apache.tomcat.dbcp.pool2.ObjectPool}, you'll need a
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} that creates the actual {@link java.sql.Connection}s. That's
* what {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory} is for.
* </p>
* <p>
* To create the {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}, you'll need at least two things:
* </p>
* <ol>
* <li>A {@link org.apache.tomcat.dbcp.dbcp2.ConnectionFactory} from which the actual database {@link java.sql.Connection}s
* will be obtained.</li>
* <li>An empty and factory-less {@link org.apache.tomcat.dbcp.pool2.ObjectPool} in which the {@link java.sql.Connection}s
* will be stored. <br>
* When you pass an {@link org.apache.tomcat.dbcp.pool2.ObjectPool} into the
* {@link org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory}, it will automatically register itself as the
* {@link org.apache.tomcat.dbcp.pool2.PooledObjectFactory} for that pool.</li>
* </ol>
* <p>
* In code, that might look like this:
* </p>
*
* <pre>
* GenericObjectPool connectionPool = new GenericObjectPool(null);
* ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "userName",
* "password");
* PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,
* connectionPool, null, null, false, true);
* PoolingDataSource dataSource = new PoolingDataSource(connectionPool);
* </pre>
* <p>
* To create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, we do the same thing, except that instead of creating a
* {@link javax.sql.DataSource} on the last line, we create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, and
* register the {@code connectionPool} with it. E.g.,:
* </p>
*
* <pre>
* GenericObjectPool connectionPool = new GenericObjectPool(null);
* ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "userName",
* "password");
* PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,
* connectionPool, null, null, false, true);
* PoolingDriver driver = new PoolingDriver();
* driver.registerPool("example", connectionPool);
* </pre>
* <p>
* Since the {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver} registers itself with the {@link java.sql.DriverManager}
* when it is created, now you can just go to the {@link java.sql.DriverManager} to create your
* {@link java.sql.Connection}s, like you normally would:
* </p>
*
* <pre>
* Connection conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:example");
* </pre>
*/
package org.apache.tomcat.dbcp.dbcp2;

View File

@@ -0,0 +1,124 @@
/*
* 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.dbcp.pool2;
/**
* A base implementation of <code>KeyedPooledObjectFactory</code>.
* <p>
* All operations defined here are essentially no-op's.
* </p>
* <p>
* This class is immutable, and therefore thread-safe.
* </p>
*
* @see KeyedPooledObjectFactory
*
* @param <K> The type of keys managed by this factory.
* @param <V> Type of element managed by this factory.
*
* @since 2.0
*/
public abstract class BaseKeyedPooledObjectFactory<K, V> extends BaseObject
implements KeyedPooledObjectFactory<K, V> {
/**
* Create an instance that can be served by the pool.
*
* @param key the key used when constructing the object
* @return an instance that can be served by the pool
*
* @throws Exception if there is a problem creating a new instance,
* this will be propagated to the code requesting an object.
*/
public abstract V create(K key)
throws Exception;
/**
* Wrap the provided instance with an implementation of
* {@link PooledObject}.
*
* @param value the instance to wrap
*
* @return The provided instance, wrapped by a {@link PooledObject}
*/
public abstract PooledObject<V> wrap(V value);
@Override
public PooledObject<V> makeObject(final K key) throws Exception {
return wrap(create(key));
}
/**
* Destroy an instance no longer needed by the pool.
* <p>
* The default implementation is a no-op.
* </p>
*
* @param key the key used when selecting the instance
* @param p a {@code PooledObject} wrapping the instance to be destroyed
*/
@Override
public void destroyObject(final K key, final PooledObject<V> p)
throws Exception {
// The default implementation is a no-op.
}
/**
* Ensures that the instance is safe to be returned by the pool.
* <p>
* The default implementation always returns {@code true}.
* </p>
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be validated
* @return always <code>true</code> in the default implementation
*/
@Override
public boolean validateObject(final K key, final PooledObject<V> p) {
return true;
}
/**
* Reinitialize an instance to be returned by the pool.
* <p>
* The default implementation is a no-op.
* </p>
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be activated
*/
@Override
public void activateObject(final K key, final PooledObject<V> p)
throws Exception {
// The default implementation is a no-op.
}
/**
* Uninitialize an instance to be returned to the idle object pool.
* <p>
* The default implementation is a no-op.
* </p>
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be passivated
*/
@Override
public void passivateObject(final K key, final PooledObject<V> p)
throws Exception {
// The default implementation is a no-op.
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.dbcp.pool2;
/**
* A base class for common functionality.
*
* @since 2.4.3
*/
public abstract class BaseObject {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append(" [");
toStringAppendFields(builder);
builder.append("]");
return builder.toString();
}
/**
* Used by sub-classes to include the fields defined by the sub-class in the
* {@link #toString()} output.
*
* @param builder Field names and values are appended to this object
*/
protected void toStringAppendFields(final StringBuilder builder) {
// do nothing by default, needed for b/w compatibility.
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.dbcp.pool2;
/**
* A simple base implementation of {@link ObjectPool}.
* Optional operations are implemented to either do nothing, return a value
* indicating it is unsupported or throw {@link UnsupportedOperationException}.
* <p>
* This class is intended to be thread-safe.
* </p>
*
* @param <T> Type of element pooled in this pool.
*
* @since 2.0
*/
public abstract class BaseObjectPool<T> extends BaseObject implements ObjectPool<T> {
@Override
public abstract T borrowObject() throws Exception;
@Override
public abstract void returnObject(T obj) throws Exception;
@Override
public abstract void invalidateObject(T obj) throws Exception;
/**
* Not supported in this base implementation.
*
* @return a negative value.
*/
@Override
public int getNumIdle() {
return -1;
}
/**
* Not supported in this base implementation.
*
* @return a negative value.
*/
@Override
public int getNumActive() {
return -1;
}
/**
* Not supported in this base implementation.
*
* @throws UnsupportedOperationException if the pool does not implement this
* method
*/
@Override
public void clear() throws Exception, UnsupportedOperationException {
throw new UnsupportedOperationException();
}
/**
* Not supported in this base implementation. Subclasses should override
* this behavior.
*
* @throws UnsupportedOperationException if the pool does not implement this
* method
*/
@Override
public void addObject() throws Exception, UnsupportedOperationException {
throw new UnsupportedOperationException();
}
/**
* Calls {@link ObjectPool#addObject()} <code>count</code>
* number of times.
*
* @param count
* the number of idle objects to add.
* @throws Exception
* when {@link ObjectPool#addObject()} fails.
* @since 2.8.0
*/
@Override
public void addObjects(final int count) throws Exception {
for (int i = 0; i < count; i++) {
addObject();
}
}
/**
* {@inheritDoc}
* <p>
* This affects the behavior of <code>isClosed</code> and
* <code>assertOpen</code>.
* </p>
*/
@Override
public void close() {
closed = true;
}
/**
* Has this pool instance been closed.
*
* @return <code>true</code> when this pool has been closed.
*/
public final boolean isClosed() {
return closed;
}
/**
* Throws an <code>IllegalStateException</code> when this pool has been
* closed.
*
* @throws IllegalStateException when this pool has been closed.
*
* @see #isClosed()
*/
protected final void assertOpen() throws IllegalStateException {
if (isClosed()) {
throw new IllegalStateException("Pool not open");
}
}
private volatile boolean closed = false;
@Override
protected void toStringAppendFields(final StringBuilder builder) {
builder.append("closed=");
builder.append(closed);
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.dbcp.pool2;
/**
* A base implementation of <code>PoolableObjectFactory</code>.
* <p>
* All operations defined here are essentially no-op's.
* <p>
* This class is immutable, and therefore thread-safe
*
* @param <T> Type of element managed in this factory.
*
* @see PooledObjectFactory
* @see BaseKeyedPooledObjectFactory
*
* @since 2.0
*/
public abstract class BasePooledObjectFactory<T> extends BaseObject implements PooledObjectFactory<T> {
/**
* Creates an object instance, to be wrapped in a {@link PooledObject}.
* <p>This method <strong>must</strong> support concurrent, multi-threaded
* activation.</p>
*
* @return an instance to be served by the pool
*
* @throws Exception if there is a problem creating a new instance,
* this will be propagated to the code requesting an object.
*/
public abstract T create() throws Exception;
/**
* Wrap the provided instance with an implementation of
* {@link PooledObject}.
*
* @param obj the instance to wrap
*
* @return The provided instance, wrapped by a {@link PooledObject}
*/
public abstract PooledObject<T> wrap(T obj);
@Override
public PooledObject<T> makeObject() throws Exception {
return wrap(create());
}
/**
* No-op.
*
* @param p ignored
*/
@Override
public void destroyObject(final PooledObject<T> p)
throws Exception {
// The default implementation is a no-op.
}
/**
* This implementation always returns {@code true}.
*
* @param p ignored
*
* @return {@code true}
*/
@Override
public boolean validateObject(final PooledObject<T> p) {
return true;
}
/**
* No-op.
*
* @param p ignored
*/
@Override
public void activateObject(final PooledObject<T> p) throws Exception {
// The default implementation is a no-op.
}
/**
* No-op.
*
* @param p ignored
*/
@Override
public void passivateObject(final PooledObject<T> p)
throws Exception {
// The default implementation is a no-op.
}
}

View File

@@ -0,0 +1,278 @@
/*
* 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.dbcp.pool2;
import java.io.Closeable;
import java.util.Collection;
import java.util.NoSuchElementException;
/**
* A "keyed" pooling interface.
* <p>
* A keyed pool maintains a pool of instances for each key value.
* </p>
* <p>
* Example of use:
* </p>
* <pre style="border:solid thin; padding: 1ex;"
* > Object obj = <code style="color:#00C">null</code>;
* Object key = <code style="color:#C00">"Key"</code>;
*
* <code style="color:#00C">try</code> {
* obj = pool.borrowObject(key);
* <code style="color:#0C0">//...use the object...</code>
* } <code style="color:#00C">catch</code>(Exception e) {
* <code style="color:#0C0">// invalidate the object</code>
* pool.invalidateObject(key, obj);
* <code style="color:#0C0">// do not return the object to the pool twice</code>
* obj = <code style="color:#00C">null</code>;
* } <code style="color:#00C">finally</code> {
* <code style="color:#0C0">// make sure the object is returned to the pool</code>
* <code style="color:#00C">if</code>(<code style="color:#00C">null</code> != obj) {
* pool.returnObject(key, obj);
* }
* }</pre>
* <p>
* {@link KeyedObjectPool} implementations <i>may</i> choose to store at most
* one instance per key value, or may choose to maintain a pool of instances
* for each key (essentially creating a {@link java.util.Map Map} of
* {@link ObjectPool pools}).
* </p>
* <p>
* See {@link org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool
* GenericKeyedObjectPool} for an implementation.
* </p>
*
* @param <K> The type of keys maintained by this pool.
* @param <V> Type of element pooled in this pool.
*
* @see KeyedPooledObjectFactory
* @see ObjectPool
* @see org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool
*
* @since 2.0
*/
public interface KeyedObjectPool<K, V> extends Closeable {
/**
* Create an object using the {@link KeyedPooledObjectFactory factory} or
* other implementation dependent mechanism, passivate it, and then place it
* in the idle object pool. <code>addObject</code> is useful for
* "pre-loading" a pool with idle objects (Optional operation).
*
* @param key the key a new instance should be added to
*
* @throws Exception
* when {@link KeyedPooledObjectFactory#makeObject} fails.
* @throws IllegalStateException
* after {@link #close} has been called on this pool.
* @throws UnsupportedOperationException
* when this pool cannot add new idle objects.
*/
void addObject(K key) throws Exception, IllegalStateException,
UnsupportedOperationException;
/**
* Calls {@link KeyedObjectPool#addObject(Object)} with each
* key in <code>keys</code> for <code>count</code> number of times. This has
* the same effect as calling {@link #addObjects(Object, int)}
* for each key in the <code>keys</code> collection.
*
* @param keys
* {@link Collection} of keys to add objects for.
* @param count
* the number of idle objects to add for each <code>key</code>.
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>keys</code>, or any value
* in <code>keys</code> is <code>null</code>.
* @see #addObjects(Object, int)
*/
void addObjects(final Collection<K> keys, final int count) throws Exception, IllegalArgumentException;
/**
* Calls {@link KeyedObjectPool#addObject(Object)}
* <code>key</code> <code>count</code> number of times.
*
* @param key
* the key to add objects for.
* @param count
* the number of idle objects to add for <code>key</code>.
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>key</code> is <code>null</code>.
* @since 2.8.0
*/
void addObjects(final K key, final int count) throws Exception, IllegalArgumentException;
/**
* Obtains an instance from this pool for the specified <code>key</code>.
* <p>
* Instances returned from this method will have been either newly created
* with {@link KeyedPooledObjectFactory#makeObject makeObject} or will be
* a previously idle object and have been activated with
* {@link KeyedPooledObjectFactory#activateObject activateObject} and then
* (optionally) validated with
* {@link KeyedPooledObjectFactory#validateObject validateObject}.
* </p>
* <p>
* By contract, clients <strong>must</strong> return the borrowed object
* using {@link #returnObject returnObject},
* {@link #invalidateObject invalidateObject}, or a related method as
* defined in an implementation or sub-interface, using a <code>key</code>
* that is {@link Object#equals equivalent} to the one used to borrow the
* instance in the first place.
* </p>
* <p>
* The behaviour of this method when the pool has been exhausted is not
* strictly specified (although it may be specified by implementations).
* </p>
*
* @param key the key used to obtain the object
*
* @return an instance from this pool.
*
* @throws IllegalStateException
* after {@link #close close} has been called on this pool
* @throws Exception
* when {@link KeyedPooledObjectFactory#makeObject
* makeObject} throws an exception
* @throws NoSuchElementException
* when the pool is exhausted and cannot or will not return
* another instance
*/
V borrowObject(K key) throws Exception, NoSuchElementException, IllegalStateException;
/**
* Clears the pool, removing all pooled instances (optional operation).
*
* @throws UnsupportedOperationException when this implementation doesn't
* support the operation
*
* @throws Exception if the pool cannot be cleared
*/
void clear() throws Exception, UnsupportedOperationException;
/**
* Clears the specified pool, removing all pooled instances corresponding to
* the given <code>key</code> (optional operation).
*
* @param key the key to clear
*
* @throws UnsupportedOperationException when this implementation doesn't
* support the operation
*
* @throws Exception if the key cannot be cleared
*/
void clear(K key) throws Exception, UnsupportedOperationException;
/**
* Close this pool, and free any resources associated with it.
* <p>
* Calling {@link #addObject addObject} or
* {@link #borrowObject borrowObject} after invoking this method on a pool
* will cause them to throw an {@link IllegalStateException}.
* </p>
* <p>
* Implementations should silently fail if not all resources can be freed.
* </p>
*/
@Override
void close();
/**
* Returns the total number of instances currently borrowed from this pool but
* not yet returned. Returns a negative value if this information is not
* available.
* @return the total number of instances currently borrowed from this pool but
* not yet returned.
*/
int getNumActive();
/**
* Returns the number of instances currently borrowed from but not yet
* returned to the pool corresponding to the given <code>key</code>.
* Returns a negative value if this information is not available.
*
* @param key the key to query
* @return the number of instances currently borrowed from but not yet
* returned to the pool corresponding to the given <code>key</code>.
*/
int getNumActive(K key);
/**
* Returns the total number of instances currently idle in this pool.
* Returns a negative value if this information is not available.
* @return the total number of instances currently idle in this pool.
*/
int getNumIdle();
/**
* Returns the number of instances corresponding to the given
* <code>key</code> currently idle in this pool. Returns a negative value if
* this information is not available.
*
* @param key the key to query
* @return the number of instances corresponding to the given
* <code>key</code> currently idle in this pool.
*/
int getNumIdle(K key);
/**
* Invalidates an object from the pool.
* <p>
* By contract, <code>obj</code> <strong>must</strong> have been obtained
* using {@link #borrowObject borrowObject} or a related method as defined
* in an implementation or sub-interface using a <code>key</code> that is
* equivalent to the one used to borrow the <code>Object</code> in the first
* place.
* </p>
* <p>
* This method should be used when an object that has been borrowed is
* determined (due to an exception or other problem) to be invalid.
* </p>
*
* @param key the key used to obtain the object
* @param obj a {@link #borrowObject borrowed} instance to be returned.
*
* @throws Exception if the instance cannot be invalidated
*/
void invalidateObject(K key, V obj) throws Exception;
/**
* Return an instance to the pool. By contract, <code>obj</code>
* <strong>must</strong> have been obtained using
* {@link #borrowObject borrowObject} or a related method as defined in an
* implementation or sub-interface using a <code>key</code> that is
* equivalent to the one used to borrow the instance in the first place.
*
* @param key the key used to obtain the object
* @param obj a {@link #borrowObject borrowed} instance to be returned.
*
* @throws IllegalStateException
* if an attempt is made to return an object to the pool that
* is in any state other than allocated (i.e. borrowed).
* Attempting to return an object more than once or attempting
* to return an object that was never borrowed from the pool
* will trigger this exception.
*
* @throws Exception if an instance cannot be returned to the pool
*/
void returnObject(K key, V obj) throws Exception;
}

View File

@@ -0,0 +1,153 @@
/*
* 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.dbcp.pool2;
/**
* An interface defining life-cycle methods for
* instances to be served by a {@link KeyedObjectPool}.
* <p>
* By contract, when an {@link KeyedObjectPool}
* delegates to a {@link KeyedPooledObjectFactory},
* </p>
* <ol>
* <li>
* {@link #makeObject} is called whenever a new instance is needed.
* </li>
* <li>
* {@link #activateObject} is invoked on every instance that has been
* {@link #passivateObject passivated} before it is
* {@link KeyedObjectPool#borrowObject borrowed} from the pool.
* </li>
* <li>
* {@link #validateObject} may be invoked on {@link #activateObject activated}
* instances to make sure they can be
* {@link KeyedObjectPool#borrowObject borrowed} from the pool.
* <code>validateObject</code> may also be used to test an
* instance being {@link KeyedObjectPool#returnObject returned} to the pool
* before it is {@link #passivateObject passivated}. It will only be invoked
* on an activated instance.
* </li>
* <li>
* {@link #passivateObject passivateObject}
* is invoked on every instance when it is returned to the pool.
* </li>
* <li>
* {@link #destroyObject destroyObject}
* is invoked on every instance when it is being "dropped" from the
* pool (whether due to the response from <code>validateObject</code>,
* or for reasons specific to the pool implementation.) There is no
* guarantee that the instance being destroyed will
* be considered active, passive or in a generally consistent state.
* </li>
* </ol>
* {@link KeyedPooledObjectFactory} must be thread-safe. The only promise
* an {@link KeyedObjectPool} makes is that the same instance of an object will
* not be passed to more than one method of a
* <code>KeyedPoolableObjectFactory</code> at a time.
* <p>
* While clients of a {@link KeyedObjectPool} borrow and return instances of
* the underlying value type V, the factory methods act on instances of
* {@link PooledObject PooledObject&lt;V&gt;}. These are the object wrappers that
* pools use to track and maintain state informations about the objects that
* they manage.
* </p>
*
* @see KeyedObjectPool
* @see BaseKeyedPooledObjectFactory
*
* @param <K> The type of keys managed by this factory.
* @param <V> Type of element managed by this factory.
*
* @since 2.0
*/
public interface KeyedPooledObjectFactory<K, V> {
/**
* Create an instance that can be served by the pool and
* wrap it in a {@link PooledObject} to be managed by the pool.
*
* @param key the key used when constructing the object
*
* @return a {@code PooledObject} wrapping an instance that can
* be served by the pool.
*
* @throws Exception if there is a problem creating a new instance,
* this will be propagated to the code requesting an object.
*/
PooledObject<V> makeObject(K key) throws Exception;
/**
* Destroy an instance no longer needed by the pool.
* <p>
* It is important for implementations of this method to be aware that there
* is no guarantee about what state <code>obj</code> will be in and the
* implementation should be prepared to handle unexpected errors.
* </p>
* <p>
* Also, an implementation must take in to consideration that instances lost
* to the garbage collector may never be destroyed.
* </p>
*
* @param key the key used when selecting the instance
* @param p a {@code PooledObject} wrapping the instance to be destroyed
*
* @throws Exception should be avoided as it may be swallowed by
* the pool implementation.
*
* @see #validateObject
* @see KeyedObjectPool#invalidateObject
*/
void destroyObject(K key, PooledObject<V> p) throws Exception;
/**
* Ensures that the instance is safe to be returned by the pool.
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be validated
*
* @return <code>false</code> if <code>obj</code> is not valid and should
* be dropped from the pool, <code>true</code> otherwise.
*/
boolean validateObject(K key, PooledObject<V> p);
/**
* Reinitialize an instance to be returned by the pool.
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be activated
*
* @throws Exception if there is a problem activating <code>obj</code>,
* this exception may be swallowed by the pool.
*
* @see #destroyObject
*/
void activateObject(K key, PooledObject<V> p) throws Exception;
/**
* Uninitialize an instance to be returned to the idle object pool.
*
* @param key the key used when selecting the object
* @param p a {@code PooledObject} wrapping the instance to be passivated
*
* @throws Exception if there is a problem passivating <code>obj</code>,
* this exception may be swallowed by the pool.
*
* @see #destroyObject
*/
void passivateObject(K key, PooledObject<V> p) throws Exception;
}

View File

@@ -0,0 +1,200 @@
/*
* 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.dbcp.pool2;
import java.io.Closeable;
import java.util.NoSuchElementException;
/**
* A pooling simple interface.
* <p>
* Example of use:
* </p>
* <pre style="border:solid thin; padding: 1ex;"
* > Object obj = <code style="color:#00C">null</code>;
*
* <code style="color:#00C">try</code> {
* obj = pool.borrowObject();
* <code style="color:#00C">try</code> {
* <code style="color:#0C0">//...use the object...</code>
* } <code style="color:#00C">catch</code>(Exception e) {
* <code style="color:#0C0">// invalidate the object</code>
* pool.invalidateObject(obj);
* <code style="color:#0C0">// do not return the object to the pool twice</code>
* obj = <code style="color:#00C">null</code>;
* } <code style="color:#00C">finally</code> {
* <code style="color:#0C0">// make sure the object is returned to the pool</code>
* <code style="color:#00C">if</code>(<code style="color:#00C">null</code> != obj) {
* pool.returnObject(obj);
* }
* }
* } <code style="color:#00C">catch</code>(Exception e) {
* <code style="color:#0C0">// failed to borrow an object</code>
* }</pre>
* <p>
* See {@link BaseObjectPool} for a simple base implementation.
* </p>
*
* @param <T> Type of element pooled in this pool.
*
* @see PooledObjectFactory
* @see KeyedObjectPool
* @see BaseObjectPool
*
* @since 2.0
*/
public interface ObjectPool<T> extends Closeable {
/**
* Creates an object using the {@link PooledObjectFactory factory} or other
* implementation dependent mechanism, passivate it, and then place it in
* the idle object pool. <code>addObject</code> is useful for "pre-loading"
* a pool with idle objects. (Optional operation).
*
* @throws Exception
* when {@link PooledObjectFactory#makeObject} fails.
* @throws IllegalStateException
* after {@link #close} has been called on this pool.
* @throws UnsupportedOperationException
* when this pool cannot add new idle objects.
*/
void addObject() throws Exception, IllegalStateException,
UnsupportedOperationException;
/**
* Calls {@link ObjectPool#addObject()} <code>count</code>
* number of times.
*
* @param count
* the number of idle objects to add.
* @throws Exception
* when {@link ObjectPool#addObject()} fails.
* @since 2.8.0
*/
void addObjects(final int count) throws Exception;
/**
* Obtains an instance from this pool.
* <p>
* Instances returned from this method will have been either newly created
* with {@link PooledObjectFactory#makeObject} or will be a previously
* idle object and have been activated with
* {@link PooledObjectFactory#activateObject} and then validated with
* {@link PooledObjectFactory#validateObject}.
* </p>
* <p>
* By contract, clients <strong>must</strong> return the borrowed instance
* using {@link #returnObject}, {@link #invalidateObject}, or a related
* method as defined in an implementation or sub-interface.
* </p>
* <p>
* The behaviour of this method when the pool has been exhausted
* is not strictly specified (although it may be specified by
* implementations).
* </p>
*
* @return an instance from this pool.
*
* @throws IllegalStateException
* after {@link #close close} has been called on this pool.
* @throws Exception
* when {@link PooledObjectFactory#makeObject} throws an
* exception.
* @throws NoSuchElementException
* when the pool is exhausted and cannot or will not return
* another instance.
*/
T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException;
/**
* Clears any objects sitting idle in the pool, releasing any associated
* resources (optional operation). Idle objects cleared must be
* {@link PooledObjectFactory#destroyObject(PooledObject)}.
*
* @throws UnsupportedOperationException
* if this implementation does not support the operation
*
* @throws Exception if the pool cannot be cleared
*/
void clear() throws Exception, UnsupportedOperationException;
/**
* Closes this pool, and free any resources associated with it.
* <p>
* Calling {@link #addObject} or {@link #borrowObject} after invoking this
* method on a pool will cause them to throw an {@link IllegalStateException}.
* </p>
* <p>
* Implementations should silently fail if not all resources can be freed.
* </p>
*/
@Override
void close();
/**
* Returns the number of instances currently borrowed from this pool. Returns
* a negative value if this information is not available.
* @return the number of instances currently borrowed from this pool.
*/
int getNumActive();
/**
* Returns the number of instances currently idle in this pool. This may be
* considered an approximation of the number of objects that can be
* {@link #borrowObject borrowed} without creating any new instances.
* Returns a negative value if this information is not available.
* @return the number of instances currently idle in this pool.
*/
int getNumIdle();
/**
* Invalidates an object from the pool.
* <p>
* By contract, <code>obj</code> <strong>must</strong> have been obtained
* using {@link #borrowObject} or a related method as defined in an
* implementation or sub-interface.
* </p>
* <p>
* This method should be used when an object that has been borrowed is
* determined (due to an exception or other problem) to be invalid.
* </p>
*
* @param obj a {@link #borrowObject borrowed} instance to be disposed.
*
* @throws Exception if the instance cannot be invalidated
*/
void invalidateObject(T obj) throws Exception;
/**
* Returns an instance to the pool. By contract, <code>obj</code>
* <strong>must</strong> have been obtained using {@link #borrowObject()} or
* a related method as defined in an implementation or sub-interface.
*
* @param obj a {@link #borrowObject borrowed} instance to be returned.
*
* @throws IllegalStateException
* if an attempt is made to return an object to the pool that
* is in any state other than allocated (i.e. borrowed).
* Attempting to return an object more than once or attempting
* to return an object that was never borrowed from the pool
* will trigger this exception.
*
* @throws Exception if an instance cannot be returned to the pool
*/
void returnObject(T obj) throws Exception;
}

View File

@@ -0,0 +1,717 @@
/*
* 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.dbcp.pool2;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/**
* This class consists exclusively of static methods that operate on or return
* ObjectPool or KeyedObjectPool related interfaces.
*
* @since 2.0
*/
public final class PoolUtils {
private static final String MSG_MIN_IDLE = "minIdle must be non-negative.";
public static final String MSG_NULL_KEY = "key must not be null.";
private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null.";
public static final String MSG_NULL_KEYS = "keys must not be null.";
private static final String MSG_NULL_POOL = "pool must not be null.";
/**
* Timer used to periodically check pools idle object count. Because a
* {@link Timer} creates a {@link Thread}, an IODH is used.
*/
static class TimerHolder {
static final Timer MIN_IDLE_TIMER = new Timer(true);
}
/**
* PoolUtils instances should NOT be constructed in standard programming.
* Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
* This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public PoolUtils() {
}
/**
* Should the supplied Throwable be re-thrown (eg if it is an instance of
* one of the Throwables that should never be swallowed). Used by the pool
* error handling for operations that throw exceptions that normally need to
* be ignored.
*
* @param t
* The Throwable to check
* @throws ThreadDeath
* if that is passed in
* @throws VirtualMachineError
* if that is passed in
*/
public static void checkRethrow(final Throwable t) {
if (t instanceof ThreadDeath) {
throw (ThreadDeath) t;
}
if (t instanceof VirtualMachineError) {
throw (VirtualMachineError) t;
}
// All other instances of Throwable will be silently swallowed
}
/**
* Periodically check the idle object count for the pool. At most one idle
* object will be added per period. If there is an exception when calling
* {@link ObjectPool#addObject()} then no more checks will be performed.
*
* @param pool
* the pool to check periodically.
* @param minIdle
* if the {@link ObjectPool#getNumIdle()} is less than this then
* add an idle object.
* @param period
* the frequency to check the number of idle objects in a pool,
* see {@link Timer#schedule(TimerTask, long, long)}.
* @param <T> the type of objects in the pool
* @return the {@link TimerTask} that will periodically check the pools idle
* object count.
* @throws IllegalArgumentException
* when <code>pool</code> is <code>null</code> or when
* <code>minIdle</code> is negative or when <code>period</code>
* isn't valid for {@link Timer#schedule(TimerTask, long, long)}
*/
public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool,
final int minIdle, final long period)
throws IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
}
if (minIdle < 0) {
throw new IllegalArgumentException(MSG_MIN_IDLE);
}
final TimerTask task = new ObjectPoolMinIdleTimerTask<>(pool, minIdle);
getMinIdleTimer().schedule(task, 0L, period);
return task;
}
/**
* Periodically check the idle object count for the key in the keyedPool. At
* most one idle object will be added per period. If there is an exception
* when calling {@link KeyedObjectPool#addObject(Object)} then no more
* checks for that key will be performed.
*
* @param keyedPool
* the keyedPool to check periodically.
* @param key
* the key to check the idle count of.
* @param minIdle
* if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
* this then add an idle object.
* @param period
* the frequency to check the number of idle objects in a
* keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return the {@link TimerTask} that will periodically check the pools idle
* object count.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>key</code> is
* <code>null</code> or when <code>minIdle</code> is negative or
* when <code>period</code> isn't valid for
* {@link Timer#schedule(TimerTask, long, long)}.
*/
public static <K, V> TimerTask checkMinIdle(
final KeyedObjectPool<K, V> keyedPool, final K key,
final int minIdle, final long period)
throws IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
}
if (key == null) {
throw new IllegalArgumentException(MSG_NULL_KEY);
}
if (minIdle < 0) {
throw new IllegalArgumentException(MSG_MIN_IDLE);
}
final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<>(
keyedPool, key, minIdle);
getMinIdleTimer().schedule(task, 0L, period);
return task;
}
/**
* Periodically check the idle object count for each key in the
* <code>Collection</code> <code>keys</code> in the keyedPool. At most one
* idle object will be added per period.
*
* @param keyedPool
* the keyedPool to check periodically.
* @param keys
* a collection of keys to check the idle object count.
* @param minIdle
* if the {@link KeyedObjectPool#getNumIdle(Object)} is less than
* this then add an idle object.
* @param period
* the frequency to check the number of idle objects in a
* keyedPool, see {@link Timer#schedule(TimerTask, long, long)}.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a {@link Map} of key and {@link TimerTask} pairs that will
* periodically check the pools idle object count.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>keys</code>, or any of the
* values in the collection is <code>null</code> or when
* <code>minIdle</code> is negative or when <code>period</code>
* isn't valid for {@link Timer#schedule(TimerTask, long, long)}
* .
* @see #checkMinIdle(KeyedObjectPool, Object, int, long)
*/
public static <K, V> Map<K, TimerTask> checkMinIdle(
final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys,
final int minIdle, final long period)
throws IllegalArgumentException {
if (keys == null) {
throw new IllegalArgumentException(MSG_NULL_KEYS);
}
final Map<K, TimerTask> tasks = new HashMap<>(keys.size());
final Iterator<K> iter = keys.iterator();
while (iter.hasNext()) {
final K key = iter.next();
final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
tasks.put(key, task);
}
return tasks;
}
/**
* Calls {@link ObjectPool#addObject()} on <code>pool</code> <code>count</code>
* number of times.
*
* @param pool
* the pool to prefill.
* @param count
* the number of idle objects to add.
* @param <T> the type of objects in the pool
* @throws Exception
* when {@link ObjectPool#addObject()} fails.
* @throws IllegalArgumentException
* when <code>pool</code> is <code>null</code>.
* @deprecated Use {@link ObjectPool#addObjects(int)}.
*/
@Deprecated
public static <T> void prefill(final ObjectPool<T> pool, final int count)
throws Exception, IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException(MSG_NULL_POOL);
}
pool.addObjects(count);
}
/**
* Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with
* <code>key</code> <code>count</code> number of times.
*
* @param keyedPool
* the keyedPool to prefill.
* @param key
* the key to add objects for.
* @param count
* the number of idle objects to add for <code>key</code>.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>keyedPool</code> or <code>key</code> is
* <code>null</code>.
* @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}.
*/
@Deprecated
public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
final K key, final int count) throws Exception,
IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
}
keyedPool.addObjects(key, count);
}
/**
* Calls {@link KeyedObjectPool#addObject(Object)} on <code>keyedPool</code> with each
* key in <code>keys</code> for <code>count</code> number of times. This has
* the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)}
* for each key in the <code>keys</code> collection.
*
* @param keyedPool
* the keyedPool to prefill.
* @param keys
* {@link Collection} of keys to add objects for.
* @param count
* the number of idle objects to add for each <code>key</code>.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @throws Exception
* when {@link KeyedObjectPool#addObject(Object)} fails.
* @throws IllegalArgumentException
* when <code>keyedPool</code>, <code>keys</code>, or any value
* in <code>keys</code> is <code>null</code>.
* @see #prefill(KeyedObjectPool, Object, int)
* @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}.
*/
@Deprecated
public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool,
final Collection<K> keys, final int count) throws Exception,
IllegalArgumentException {
if (keys == null) {
throw new IllegalArgumentException(MSG_NULL_KEYS);
}
keyedPool.addObjects(keys, count);
}
/**
* Returns a synchronized (thread-safe) PooledObjectFactory backed by the
* specified PooledObjectFactory.
*
* @param factory
* the PooledObjectFactory to be "wrapped" in a synchronized
* PooledObjectFactory.
* @param <T> the type of objects in the pool
* @return a synchronized view of the specified PooledObjectFactory.
*/
public static <T> PooledObjectFactory<T> synchronizedPooledFactory(
final PooledObjectFactory<T> factory) {
return new SynchronizedPooledObjectFactory<>(factory);
}
/**
* Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by
* the specified KeyedPoolableObjectFactory.
*
* @param keyedFactory
* the KeyedPooledObjectFactory to be "wrapped" in a
* synchronized KeyedPooledObjectFactory.
* @param <K> the type of the pool key
* @param <V> the type of pool entries
* @return a synchronized view of the specified KeyedPooledObjectFactory.
*/
public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory(
final KeyedPooledObjectFactory<K, V> keyedFactory) {
return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory);
}
/**
* Gets the <code>Timer</code> for checking keyedPool's idle count.
*
* @return the {@link Timer} for checking keyedPool's idle count.
*/
private static Timer getMinIdleTimer() {
return TimerHolder.MIN_IDLE_TIMER;
}
/**
* Timer task that adds objects to the pool until the number of idle
* instances reaches the configured minIdle. Note that this is not the same
* as the pool's minIdle setting.
*
* @param <T> type of objects in the pool
*/
private static final class ObjectPoolMinIdleTimerTask<T> extends TimerTask {
/** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
private final int minIdle;
/** Object pool */
private final ObjectPool<T> pool;
/**
* Create a new ObjectPoolMinIdleTimerTask for the given pool with the
* given minIdle setting.
*
* @param pool
* object pool
* @param minIdle
* number of idle instances to maintain
* @throws IllegalArgumentException
* if the pool is null
*/
ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle)
throws IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException(MSG_NULL_POOL);
}
this.pool = pool;
this.minIdle = minIdle;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
boolean success = false;
try {
if (pool.getNumIdle() < minIdle) {
pool.addObject();
}
success = true;
} catch (final Exception e) {
cancel();
} finally {
// detect other types of Throwable and cancel this Timer
if (!success) {
cancel();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("ObjectPoolMinIdleTimerTask");
sb.append("{minIdle=").append(minIdle);
sb.append(", pool=").append(pool);
sb.append('}');
return sb.toString();
}
}
/**
* Timer task that adds objects to the pool until the number of idle
* instances for the given key reaches the configured minIdle. Note that
* this is not the same as the pool's minIdle setting.
*
* @param <K> object pool key type
* @param <V> object pool value type
*/
private static final class KeyedObjectPoolMinIdleTimerTask<K, V> extends
TimerTask {
/** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
private final int minIdle;
/** Key to ensure minIdle for */
private final K key;
/** Keyed object pool */
private final KeyedObjectPool<K, V> keyedPool;
/**
* Creates a new KeyedObjecPoolMinIdleTimerTask.
*
* @param keyedPool
* keyed object pool
* @param key
* key to ensure minimum number of idle instances
* @param minIdle
* minimum number of idle instances
* @throws IllegalArgumentException
* if the key is null
*/
KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool<K, V> keyedPool,
final K key, final int minIdle) throws IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(
MSG_NULL_KEYED_POOL);
}
this.keyedPool = keyedPool;
this.key = key;
this.minIdle = minIdle;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
boolean success = false;
try {
if (keyedPool.getNumIdle(key) < minIdle) {
keyedPool.addObject(key);
}
success = true;
} catch (final Exception e) {
cancel();
} finally {
// detect other types of Throwable and cancel this Timer
if (!success) {
cancel();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("KeyedObjectPoolMinIdleTimerTask");
sb.append("{minIdle=").append(minIdle);
sb.append(", key=").append(key);
sb.append(", keyedPool=").append(keyedPool);
sb.append('}');
return sb.toString();
}
}
/**
* A fully synchronized PooledObjectFactory that wraps a
* PooledObjectFactory and synchronizes access to the wrapped factory
* methods.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library.
* </p>
*
* @param <T> pooled object factory type
*/
private static final class SynchronizedPooledObjectFactory<T> implements
PooledObjectFactory<T> {
/** Synchronization lock */
private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
/** Wrapped factory */
private final PooledObjectFactory<T> factory;
/**
* Creates a SynchronizedPoolableObjectFactory wrapping the given
* factory.
*
* @param factory
* underlying factory to wrap
* @throws IllegalArgumentException
* if the factory is null
*/
SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory)
throws IllegalArgumentException {
if (factory == null) {
throw new IllegalArgumentException("factory must not be null.");
}
this.factory = factory;
}
/**
* {@inheritDoc}
*/
@Override
public PooledObject<T> makeObject() throws Exception {
writeLock.lock();
try {
return factory.makeObject();
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroyObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.destroyObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean validateObject(final PooledObject<T> p) {
writeLock.lock();
try {
return factory.validateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void activateObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.activateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void passivateObject(final PooledObject<T> p) throws Exception {
writeLock.lock();
try {
factory.passivateObject(p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedPoolableObjectFactory");
sb.append("{factory=").append(factory);
sb.append('}');
return sb.toString();
}
}
/**
* A fully synchronized KeyedPooledObjectFactory that wraps a
* KeyedPooledObjectFactory and synchronizes access to the wrapped factory
* methods.
* <p>
* <b>Note:</b> This should not be used on pool implementations that already
* provide proper synchronization such as the pools provided in the Commons
* Pool library.
* </p>
*
* @param <K> pooled object factory key type
* @param <V> pooled object factory key value
*/
private static final class SynchronizedKeyedPooledObjectFactory<K, V>
implements KeyedPooledObjectFactory<K, V> {
/** Synchronization lock */
private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
/** Wrapped factory */
private final KeyedPooledObjectFactory<K, V> keyedFactory;
/**
* Creates a SynchronizedKeyedPoolableObjectFactory wrapping the given
* factory.
*
* @param keyedFactory
* underlying factory to wrap
* @throws IllegalArgumentException
* if the factory is null
*/
SynchronizedKeyedPooledObjectFactory(
final KeyedPooledObjectFactory<K, V> keyedFactory)
throws IllegalArgumentException {
if (keyedFactory == null) {
throw new IllegalArgumentException(
"keyedFactory must not be null.");
}
this.keyedFactory = keyedFactory;
}
/**
* {@inheritDoc}
*/
@Override
public PooledObject<V> makeObject(final K key) throws Exception {
writeLock.lock();
try {
return keyedFactory.makeObject(key);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroyObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.destroyObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean validateObject(final K key, final PooledObject<V> p) {
writeLock.lock();
try {
return keyedFactory.validateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void activateObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.activateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public void passivateObject(final K key, final PooledObject<V> p) throws Exception {
writeLock.lock();
try {
keyedFactory.passivateObject(key, p);
} finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("SynchronizedKeyedPoolableObjectFactory");
sb.append("{keyedFactory=").append(keyedFactory);
sb.append('}');
return sb.toString();
}
}
}

View File

@@ -0,0 +1,220 @@
/*
* 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.dbcp.pool2;
import java.io.PrintWriter;
import java.util.Deque;
/**
* Defines the wrapper that is used to track the additional information, such as
* state, for the pooled objects.
* <p>
* Implementations of this class are required to be thread-safe.
* </p>
*
* @param <T> the type of object in the pool
*
* @since 2.0
*/
public interface PooledObject<T> extends Comparable<PooledObject<T>> {
/**
* Obtains the underlying object that is wrapped by this instance of
* {@link PooledObject}.
*
* @return The wrapped object
*/
T getObject();
/**
* Obtains the time (using the same basis as
* {@link System#currentTimeMillis()}) that this object was created.
*
* @return The creation time for the wrapped object
*/
long getCreateTime();
/**
* Obtains the time in milliseconds that this object last spent in the
* active state (it may still be active in which case subsequent calls will
* return an increased value).
*
* @return The time in milliseconds last spent in the active state
*/
long getActiveTimeMillis();
/**
* Gets the number of times this object has been borrowed.
*
* @return The number of times this object has been borrowed.
* @since 2.7.0
*/
long getBorrowedCount();
/**
* Obtains the time in milliseconds that this object last spend in the
* idle state (it may still be idle in which case subsequent calls will
* return an increased value).
*
* @return The time in milliseconds last spent in the idle state
*/
long getIdleTimeMillis();
/**
* Obtains the time the wrapped object was last borrowed.
*
* @return The time the object was last borrowed
*/
long getLastBorrowTime();
/**
* Obtains the time the wrapped object was last returned.
*
* @return The time the object was last returned
*/
long getLastReturnTime();
/**
* Returns an estimate of the last time this object was used. If the class
* of the pooled object implements {@link TrackedUse}, what is returned is
* the maximum of {@link TrackedUse#getLastUsed()} and
* {@link #getLastBorrowTime()}; otherwise this method gives the same
* value as {@link #getLastBorrowTime()}.
*
* @return the last time this object was used
*/
long getLastUsedTime();
/**
* Orders instances based on idle time - i.e. the length of time since the
* instance was returned to the pool. Used by the GKOP idle object evictor.
*<p>
* Note: This class has a natural ordering that is inconsistent with
* equals if distinct objects have the same identity hash code.
* </p>
* <p>
* {@inheritDoc}
* </p>
*/
@Override
int compareTo(PooledObject<T> other);
@Override
boolean equals(Object obj);
@Override
int hashCode();
/**
* Provides a String form of the wrapper for debug purposes. The format is
* not fixed and may change at any time.
* <p>
* {@inheritDoc}
*/
@Override
String toString();
/**
* Attempts to place the pooled object in the
* {@link PooledObjectState#EVICTION} state.
*
* @return <code>true</code> if the object was placed in the
* {@link PooledObjectState#EVICTION} state otherwise
* <code>false</code>
*/
boolean startEvictionTest();
/**
* Called to inform the object that the eviction test has ended.
*
* @param idleQueue The queue of idle objects to which the object should be
* returned
*
* @return Currently not used
*/
boolean endEvictionTest(Deque<PooledObject<T>> idleQueue);
/**
* Allocates the object.
*
* @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
*/
boolean allocate();
/**
* Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE}
* if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}.
*
* @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
*/
boolean deallocate();
/**
* Sets the state to {@link PooledObjectState#INVALID INVALID}
*/
void invalidate();
/**
* Is abandoned object tracking being used? If this is true the
* implementation will need to record the stack trace of the last caller to
* borrow this object.
*
* @param logAbandoned The new configuration setting for abandoned
* object tracking
*/
void setLogAbandoned(boolean logAbandoned);
/**
* Configures the stack trace generation strategy based on whether or not fully detailed stack traces are required.
* When set to false, abandoned logs may only include caller class information rather than method names, line
* numbers, and other normal metadata available in a full stack trace.
*
* @param requireFullStackTrace the new configuration setting for abandoned object logging
* @since 2.7.0
*/
void setRequireFullStackTrace(final boolean requireFullStackTrace);
/**
* Record the current stack trace as the last time the object was used.
*/
void use();
/**
* Prints the stack trace of the code that borrowed this pooled object and
* the stack trace of the last code to use this object (if available) to
* the supplied writer.
*
* @param writer The destination for the debug output
*/
void printStackTrace(PrintWriter writer);
/**
* Returns the state of this object.
* @return state
*/
PooledObjectState getState();
/**
* Marks the pooled object as abandoned.
*/
void markAbandoned();
/**
* Marks the object as returning to the pool.
*/
void markReturning();
}

View File

@@ -0,0 +1,141 @@
/*
* 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.dbcp.pool2;
/**
* An interface defining life-cycle methods for instances to be served by an
* {@link ObjectPool}.
* <p>
* By contract, when an {@link ObjectPool} delegates to a
* {@link PooledObjectFactory},
* </p>
* <ol>
* <li>
* {@link #makeObject} is called whenever a new instance is needed.
* </li>
* <li>
* {@link #activateObject} is invoked on every instance that has been
* {@link #passivateObject passivated} before it is
* {@link ObjectPool#borrowObject borrowed} from the pool.
* </li>
* <li>
* {@link #validateObject} may be invoked on {@link #activateObject activated}
* instances to make sure they can be {@link ObjectPool#borrowObject borrowed}
* from the pool. {@link #validateObject} may also be used to
* test an instance being {@link ObjectPool#returnObject returned} to the pool
* before it is {@link #passivateObject passivated}. It will only be invoked
* on an activated instance.
* </li>
* <li>
* {@link #passivateObject} is invoked on every instance when it is returned
* to the pool.
* </li>
* <li>
* {@link #destroyObject} is invoked on every instance when it is being
* "dropped" from the pool (whether due to the response from
* {@link #validateObject}, or for reasons specific to the pool
* implementation.) There is no guarantee that the instance being destroyed
* will be considered active, passive or in a generally consistent state.
* </li>
* </ol>
* {@link PooledObjectFactory} must be thread-safe. The only promise
* an {@link ObjectPool} makes is that the same instance of an object will not
* be passed to more than one method of a <code>PoolableObjectFactory</code>
* at a time.
* <p>
* While clients of a {@link KeyedObjectPool} borrow and return instances of
* the underlying value type {@code V}, the factory methods act on instances of
* {@link PooledObject PooledObject&lt;V&gt;}. These are the object wrappers that
* pools use to track and maintain state information about the objects that
* they manage.
* </p>
*
* @param <T> Type of element managed in this factory.
*
* @see ObjectPool
*
* @since 2.0
*/
public interface PooledObjectFactory<T> {
/**
* Creates an instance that can be served by the pool and wrap it in a
* {@link PooledObject} to be managed by the pool.
*
* @return a {@code PooledObject} wrapping an instance that can be served by the pool
*
* @throws Exception if there is a problem creating a new instance,
* this will be propagated to the code requesting an object.
*/
PooledObject<T> makeObject() throws Exception;
/**
* Destroys an instance no longer needed by the pool.
* <p>
* It is important for implementations of this method to be aware that there
* is no guarantee about what state <code>obj</code> will be in and the
* implementation should be prepared to handle unexpected errors.
* </p>
* <p>
* Also, an implementation must take in to consideration that instances lost
* to the garbage collector may never be destroyed.
* </p>
*
* @param p a {@code PooledObject} wrapping the instance to be destroyed
*
* @throws Exception should be avoided as it may be swallowed by
* the pool implementation.
*
* @see #validateObject
* @see ObjectPool#invalidateObject
*/
void destroyObject(PooledObject<T> p) throws Exception;
/**
* Ensures that the instance is safe to be returned by the pool.
*
* @param p a {@code PooledObject} wrapping the instance to be validated
*
* @return <code>false</code> if <code>obj</code> is not valid and should
* be dropped from the pool, <code>true</code> otherwise.
*/
boolean validateObject(PooledObject<T> p);
/**
* Reinitializes an instance to be returned by the pool.
*
* @param p a {@code PooledObject} wrapping the instance to be activated
*
* @throws Exception if there is a problem activating <code>obj</code>,
* this exception may be swallowed by the pool.
*
* @see #destroyObject
*/
void activateObject(PooledObject<T> p) throws Exception;
/**
* Uninitializes an instance to be returned to the idle object pool.
*
* @param p a {@code PooledObject} wrapping the instance to be passivated
*
* @throws Exception if there is a problem passivating <code>obj</code>,
* this exception may be swallowed by the pool.
*
* @see #destroyObject
*/
void passivateObject(PooledObject<T> p) throws Exception;
}

View File

@@ -0,0 +1,87 @@
/*
* 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.dbcp.pool2;
/**
* Provides the possible states that a {@link PooledObject} may be in.
*
* @since 2.0
*/
public enum PooledObjectState {
/**
* In the queue, not in use.
*/
IDLE,
/**
* In use.
*/
ALLOCATED,
/**
* In the queue, currently being tested for possible eviction.
*/
EVICTION,
/**
* Not in the queue, currently being tested for possible eviction. An
* attempt to borrow the object was made while being tested which removed it
* from the queue. It should be returned to the head of the queue once
* eviction testing completes.
* TODO: Consider allocating object and ignoring the result of the eviction
* test.
*/
EVICTION_RETURN_TO_HEAD,
/**
* In the queue, currently being validated.
*/
VALIDATION,
/**
* Not in queue, currently being validated. The object was borrowed while
* being validated and since testOnBorrow was configured, it was removed
* from the queue and pre-allocated. It should be allocated once validation
* completes.
*/
VALIDATION_PREALLOCATED,
/**
* Not in queue, currently being validated. An attempt to borrow the object
* was made while previously being tested for eviction which removed it from
* the queue. It should be returned to the head of the queue once validation
* completes.
*/
VALIDATION_RETURN_TO_HEAD,
/**
* Failed maintenance (e.g. eviction test or validation) and will be / has
* been destroyed
*/
INVALID,
/**
* Deemed abandoned, to be invalidated.
*/
ABANDONED,
/**
* Returning to the pool.
*/
RETURNING
}

View File

@@ -0,0 +1,36 @@
/*
* 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.dbcp.pool2;
/**
* Pools that unavoidably swallow exceptions may be configured with an instance
* of this listener so the user may receive notification of when this happens.
* The listener should not throw an exception when called but pools calling
* listeners should protect themselves against exceptions anyway.
*
* @since 2.0
*/
public interface SwallowedExceptionListener {
/**
* This method is called every time the implementation unavoidably swallows
* an exception.
*
* @param e The exception that was swallowed
*/
void onSwallowException(Exception e);
}

View File

@@ -0,0 +1,36 @@
/*
* 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.dbcp.pool2;
/**
* This interface allows pooled objects to make information available about when
* and how they were used available to the object pool. The object pool may, but
* is not required, to use this information to make more informed decisions when
* determining the state of a pooled object - for instance whether or not the
* object has been abandoned.
*
* @since 2.0
*/
public interface TrackedUse {
/**
* Get the last time this object was used in ms.
*
* @return long time in ms
*/
long getLastUsed();
}

View File

@@ -0,0 +1,39 @@
/*
* 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.dbcp.pool2;
/**
* This interface may be implemented by an object pool to enable clients
* (primarily those clients that wrap pools to provide pools with extended
* features) to provide additional information to the pool relating to object
* using allowing more informed decisions and reporting to be made regarding
* abandoned objects.
*
* @param <T> The type of object provided by the pool.
*
* @since 2.0
*/
public interface UsageTracking<T> {
/**
* This method is called every time a pooled object is used to enable the pool to
* better track borrowed objects.
*
* @param pooledObject The object that is being used
*/
void use(T pooledObject);
}

View File

@@ -0,0 +1,292 @@
/*
* 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.dbcp.pool2.impl;
import java.io.PrintWriter;
/**
* Configuration settings for abandoned object removal.
*
* @since 2.0
*/
public class AbandonedConfig {
/**
* Whether or not borrowObject performs abandoned object removal.
*/
private boolean removeAbandonedOnBorrow = false;
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when borrowObject is invoked.</p>
*
* <p>The default value is false.</p>
*
* <p>If set to true, abandoned objects are removed by borrowObject if
* there are fewer than 2 idle objects available in the pool and
* <code>getNumActive() &gt; getMaxTotal() - 3</code></p>
*
* @return true if abandoned objects are to be removed by borrowObject
*/
public boolean getRemoveAbandonedOnBorrow() {
return this.removeAbandonedOnBorrow;
}
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when borrowObject is invoked.</p>
*
* @param removeAbandonedOnBorrow true means abandoned objects will be
* removed by borrowObject
* @see #getRemoveAbandonedOnBorrow()
*/
public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) {
this.removeAbandonedOnBorrow = removeAbandonedOnBorrow;
}
/**
* Whether or not pool maintenance (evictor) performs abandoned object
* removal.
*/
private boolean removeAbandonedOnMaintenance = false;
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when pool maintenance (the "evictor")
* runs.</p>
*
* <p>The default value is false.</p>
*
* <p>If set to true, abandoned objects are removed by the pool
* maintenance thread when it runs. This setting has no effect
* unless maintenance is enabled by setting
*{@link GenericObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis}
* to a positive number.</p>
*
* @return true if abandoned objects are to be removed by the evictor
*/
public boolean getRemoveAbandonedOnMaintenance() {
return this.removeAbandonedOnMaintenance;
}
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when pool maintenance runs.</p>
*
* @param removeAbandonedOnMaintenance true means abandoned objects will be
* removed by pool maintenance
* @see #getRemoveAbandonedOnMaintenance
*/
public void setRemoveAbandonedOnMaintenance(final boolean removeAbandonedOnMaintenance) {
this.removeAbandonedOnMaintenance = removeAbandonedOnMaintenance;
}
/**
* Timeout in seconds before an abandoned object can be removed.
*/
private int removeAbandonedTimeout = 300;
/**
* <p>Timeout in seconds before an abandoned object can be removed.</p>
*
* <p>The time of most recent use of an object is the maximum (latest) of
* {@link org.apache.tomcat.dbcp.pool2.TrackedUse#getLastUsed()}
* (if this class of the object implements
* TrackedUse) and the time when the object was borrowed from the pool.</p>
*
* <p>The default value is 300 seconds.</p>
*
* @return the abandoned object timeout in seconds
*/
public int getRemoveAbandonedTimeout() {
return this.removeAbandonedTimeout;
}
/**
* <p>Sets the timeout in seconds before an abandoned object can be
* removed</p>
*
* <p>Setting this property has no effect if
* {@link #getRemoveAbandonedOnBorrow() removeAbandonedOnBorrow} and
* {@link #getRemoveAbandonedOnMaintenance() removeAbandonedOnMaintenance}
* are both false.</p>
*
* @param removeAbandonedTimeout new abandoned timeout in seconds
* @see #getRemoveAbandonedTimeout()
*/
public void setRemoveAbandonedTimeout(final int removeAbandonedTimeout) {
this.removeAbandonedTimeout = removeAbandonedTimeout;
}
/**
* Determines whether or not to log stack traces for application code
* which abandoned an object.
*/
private boolean logAbandoned = false;
/**
* Flag to log stack traces for application code which abandoned
* an object.
*
* Defaults to false.
* Logging of abandoned objects adds overhead for every object created
* because a stack trace has to be generated.
*
* @return boolean true if stack trace logging is turned on for abandoned
* objects
*
*/
public boolean getLogAbandoned() {
return this.logAbandoned;
}
/**
* Sets the flag to log stack traces for application code which abandoned
* an object.
*
* @param logAbandoned true turns on abandoned stack trace logging
* @see #getLogAbandoned()
*
*/
public void setLogAbandoned(final boolean logAbandoned) {
this.logAbandoned = logAbandoned;
}
/**
* Determines whether or not to log full stack traces when logAbandoned is true.
* If disabled, then a faster method for logging stack traces with only class data
* may be used if possible.
*
* @since 2.5
*/
private boolean requireFullStackTrace = true;
/**
* Indicates if full stack traces are required when {@link #getLogAbandoned() logAbandoned}
* is true. Defaults to true. Logging of abandoned objects requiring a full stack trace will
* generate an entire stack trace to generate for every object created. If this is disabled,
* a faster but less informative stack walking mechanism may be used if available.
*
* @return true if full stack traces are required for logging abandoned connections, or false
* if abbreviated stack traces are acceptable
* @see CallStack
* @since 2.5
*/
public boolean getRequireFullStackTrace() {
return requireFullStackTrace;
}
/**
* Sets the flag to require full stack traces for logging abandoned connections when enabled.
*
* @param requireFullStackTrace indicates whether or not full stack traces are required in
* abandoned connection logs
* @see CallStack
* @see #getRequireFullStackTrace()
* @since 2.5
*/
public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
this.requireFullStackTrace = requireFullStackTrace;
}
/**
* PrintWriter to use to log information on abandoned objects.
* Use of default system encoding is deliberate.
*/
private PrintWriter logWriter = new PrintWriter(System.out);
/**
* Returns the log writer being used by this configuration to log
* information on abandoned objects. If not set, a PrintWriter based on
* System.out with the system default encoding is used.
*
* @return log writer in use
*/
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Sets the log writer to be used by this configuration to log
* information on abandoned objects.
*
* @param logWriter The new log writer
*/
public void setLogWriter(final PrintWriter logWriter) {
this.logWriter = logWriter;
}
/**
* If the pool implements
* {@link org.apache.tomcat.dbcp.pool2.UsageTracking}, should the pool
* record a stack trace every time a method is called on a pooled object and
* retain the most recent stack trace to aid debugging of abandoned objects?
*/
private boolean useUsageTracking = false;
/**
* If the pool implements
* {@link org.apache.tomcat.dbcp.pool2.UsageTracking}, should the pool
* record a
* stack trace every time a method is called on a pooled object and retain
* the most recent stack trace to aid debugging of abandoned objects?
*
* @return <code>true</code> if usage tracking is enabled
*/
public boolean getUseUsageTracking() {
return useUsageTracking;
}
/**
* If the pool implements
* {@link org.apache.tomcat.dbcp.pool2.UsageTracking}, configure whether the
* pool
* should record a stack trace every time a method is called on a pooled
* object and retain the most recent stack trace to aid debugging of
* abandoned objects.
*
* @param useUsageTracking A value of <code>true</code> will enable
* the recording of a stack trace on every use
* of a pooled object
*/
public void setUseUsageTracking(final boolean useUsageTracking) {
this.useUsageTracking = useUsageTracking;
}
/**
* @since 2.4.3
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("AbandonedConfig [removeAbandonedOnBorrow=");
builder.append(removeAbandonedOnBorrow);
builder.append(", removeAbandonedOnMaintenance=");
builder.append(removeAbandonedOnMaintenance);
builder.append(", removeAbandonedTimeout=");
builder.append(removeAbandonedTimeout);
builder.append(", logAbandoned=");
builder.append(logAbandoned);
builder.append(", logWriter=");
builder.append(logWriter);
builder.append(", useUsageTracking=");
builder.append(useUsageTracking);
builder.append("]");
return builder.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,750 @@
/*
* 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.dbcp.pool2.impl;
import org.apache.tomcat.dbcp.pool2.BaseObject;
/**
* Provides the implementation for the common attributes shared by the
* sub-classes. New instances of this class will be created using the defaults
* defined by the public constants.
* <p>
* This class is not thread-safe.
* </p>
*
* @param <T> Type of element pooled.
* @since 2.0
*/
public abstract class BaseObjectPoolConfig<T> extends BaseObject implements Cloneable {
/**
* The default value for the {@code lifo} configuration attribute.
* @see GenericObjectPool#getLifo()
* @see GenericKeyedObjectPool#getLifo()
*/
public static final boolean DEFAULT_LIFO = true;
/**
* The default value for the {@code fairness} configuration attribute.
* @see GenericObjectPool#getFairness()
* @see GenericKeyedObjectPool#getFairness()
*/
public static final boolean DEFAULT_FAIRNESS = false;
/**
* The default value for the {@code maxWait} configuration attribute.
* @see GenericObjectPool#getMaxWaitMillis()
* @see GenericKeyedObjectPool#getMaxWaitMillis()
*/
public static final long DEFAULT_MAX_WAIT_MILLIS = -1L;
/**
* The default value for the {@code minEvictableIdleTimeMillis}
* configuration attribute.
* @see GenericObjectPool#getMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
*/
public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS =
1000L * 60L * 30L;
/**
* The default value for the {@code softMinEvictableIdleTimeMillis}
* configuration attribute.
* @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
*/
public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;
/**
* The default value for {@code evictorShutdownTimeoutMillis} configuration
* attribute.
* @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
* @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
*/
public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS =
10L * 1000L;
/**
* The default value for the {@code numTestsPerEvictionRun} configuration
* attribute.
* @see GenericObjectPool#getNumTestsPerEvictionRun()
* @see GenericKeyedObjectPool#getNumTestsPerEvictionRun()
*/
public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3;
/**
* The default value for the {@code testOnCreate} configuration attribute.
* @see GenericObjectPool#getTestOnCreate()
* @see GenericKeyedObjectPool#getTestOnCreate()
*
* @since 2.2
*/
public static final boolean DEFAULT_TEST_ON_CREATE = false;
/**
* The default value for the {@code testOnBorrow} configuration attribute.
* @see GenericObjectPool#getTestOnBorrow()
* @see GenericKeyedObjectPool#getTestOnBorrow()
*/
public static final boolean DEFAULT_TEST_ON_BORROW = false;
/**
* The default value for the {@code testOnReturn} configuration attribute.
* @see GenericObjectPool#getTestOnReturn()
* @see GenericKeyedObjectPool#getTestOnReturn()
*/
public static final boolean DEFAULT_TEST_ON_RETURN = false;
/**
* The default value for the {@code testWhileIdle} configuration attribute.
* @see GenericObjectPool#getTestWhileIdle()
* @see GenericKeyedObjectPool#getTestWhileIdle()
*/
public static final boolean DEFAULT_TEST_WHILE_IDLE = false;
/**
* The default value for the {@code timeBetweenEvictionRunsMillis}
* configuration attribute.
* @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
* @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
*/
public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L;
/**
* The default value for the {@code blockWhenExhausted} configuration
* attribute.
* @see GenericObjectPool#getBlockWhenExhausted()
* @see GenericKeyedObjectPool#getBlockWhenExhausted()
*/
public static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true;
/**
* The default value for enabling JMX for pools created with a configuration
* instance.
*/
public static final boolean DEFAULT_JMX_ENABLE = true;
/**
* The default value for the prefix used to name JMX enabled pools created
* with a configuration instance.
* @see GenericObjectPool#getJmxName()
* @see GenericKeyedObjectPool#getJmxName()
*/
public static final String DEFAULT_JMX_NAME_PREFIX = "pool";
/**
* The default value for the base name to use to name JMX enabled pools
* created with a configuration instance. The default is <code>null</code>
* which means the pool will provide the base name to use.
* @see GenericObjectPool#getJmxName()
* @see GenericKeyedObjectPool#getJmxName()
*/
public static final String DEFAULT_JMX_NAME_BASE = null;
/**
* The default value for the {@code evictionPolicyClassName} configuration
* attribute.
* @see GenericObjectPool#getEvictionPolicyClassName()
* @see GenericKeyedObjectPool#getEvictionPolicyClassName()
*/
public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = DefaultEvictionPolicy.class.getName();
private boolean lifo = DEFAULT_LIFO;
private boolean fairness = DEFAULT_FAIRNESS;
private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
private long minEvictableIdleTimeMillis =
DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
private long evictorShutdownTimeoutMillis =
DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS;
private long softMinEvictableIdleTimeMillis =
DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
private int numTestsPerEvictionRun =
DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
private EvictionPolicy<T> evictionPolicy = null; // Only 2.6.0 applications set this
private String evictionPolicyClassName = DEFAULT_EVICTION_POLICY_CLASS_NAME;
private boolean testOnCreate = DEFAULT_TEST_ON_CREATE;
private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW;
private boolean testOnReturn = DEFAULT_TEST_ON_RETURN;
private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
private long timeBetweenEvictionRunsMillis =
DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED;
private boolean jmxEnabled = DEFAULT_JMX_ENABLE;
// TODO Consider changing this to a single property for 3.x
private String jmxNamePrefix = DEFAULT_JMX_NAME_PREFIX;
private String jmxNameBase = DEFAULT_JMX_NAME_BASE;
/**
* Get the value for the {@code lifo} configuration attribute for pools
* created with this configuration instance.
*
* @return The current setting of {@code lifo} for this configuration
* instance
*
* @see GenericObjectPool#getLifo()
* @see GenericKeyedObjectPool#getLifo()
*/
public boolean getLifo() {
return lifo;
}
/**
* Get the value for the {@code fairness} configuration attribute for pools
* created with this configuration instance.
*
* @return The current setting of {@code fairness} for this configuration
* instance
*
* @see GenericObjectPool#getFairness()
* @see GenericKeyedObjectPool#getFairness()
*/
public boolean getFairness() {
return fairness;
}
/**
* Set the value for the {@code lifo} configuration attribute for pools
* created with this configuration instance.
*
* @param lifo The new setting of {@code lifo}
* for this configuration instance
*
* @see GenericObjectPool#getLifo()
* @see GenericKeyedObjectPool#getLifo()
*/
public void setLifo(final boolean lifo) {
this.lifo = lifo;
}
/**
* Set the value for the {@code fairness} configuration attribute for pools
* created with this configuration instance.
*
* @param fairness The new setting of {@code fairness}
* for this configuration instance
*
* @see GenericObjectPool#getFairness()
* @see GenericKeyedObjectPool#getFairness()
*/
public void setFairness(final boolean fairness) {
this.fairness = fairness;
}
/**
* Get the value for the {@code maxWait} configuration attribute for pools
* created with this configuration instance.
*
* @return The current setting of {@code maxWait} for this
* configuration instance
*
* @see GenericObjectPool#getMaxWaitMillis()
* @see GenericKeyedObjectPool#getMaxWaitMillis()
*/
public long getMaxWaitMillis() {
return maxWaitMillis;
}
/**
* Set the value for the {@code maxWait} configuration attribute for pools
* created with this configuration instance.
*
* @param maxWaitMillis The new setting of {@code maxWaitMillis}
* for this configuration instance
*
* @see GenericObjectPool#getMaxWaitMillis()
* @see GenericKeyedObjectPool#getMaxWaitMillis()
*/
public void setMaxWaitMillis(final long maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
/**
* Get the value for the {@code minEvictableIdleTimeMillis} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code minEvictableIdleTimeMillis} for
* this configuration instance
*
* @see GenericObjectPool#getMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
*/
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
/**
* Set the value for the {@code minEvictableIdleTimeMillis} configuration
* attribute for pools created with this configuration instance.
*
* @param minEvictableIdleTimeMillis The new setting of
* {@code minEvictableIdleTimeMillis} for this configuration instance
*
* @see GenericObjectPool#getMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()
*/
public void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
/**
* Get the value for the {@code softMinEvictableIdleTimeMillis}
* configuration attribute for pools created with this configuration
* instance.
*
* @return The current setting of {@code softMinEvictableIdleTimeMillis}
* for this configuration instance
*
* @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
*/
public long getSoftMinEvictableIdleTimeMillis() {
return softMinEvictableIdleTimeMillis;
}
/**
* Set the value for the {@code softMinEvictableIdleTimeMillis}
* configuration attribute for pools created with this configuration
* instance.
*
* @param softMinEvictableIdleTimeMillis The new setting of
* {@code softMinEvictableIdleTimeMillis} for this configuration
* instance
*
* @see GenericObjectPool#getSoftMinEvictableIdleTimeMillis()
* @see GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()
*/
public void setSoftMinEvictableIdleTimeMillis(
final long softMinEvictableIdleTimeMillis) {
this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
}
/**
* Get the value for the {@code numTestsPerEvictionRun} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code numTestsPerEvictionRun} for this
* configuration instance
*
* @see GenericObjectPool#getNumTestsPerEvictionRun()
* @see GenericKeyedObjectPool#getNumTestsPerEvictionRun()
*/
public int getNumTestsPerEvictionRun() {
return numTestsPerEvictionRun;
}
/**
* Set the value for the {@code numTestsPerEvictionRun} configuration
* attribute for pools created with this configuration instance.
*
* @param numTestsPerEvictionRun The new setting of
* {@code numTestsPerEvictionRun} for this configuration instance
*
* @see GenericObjectPool#getNumTestsPerEvictionRun()
* @see GenericKeyedObjectPool#getNumTestsPerEvictionRun()
*/
public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
}
/**
* Get the value for the {@code evictorShutdownTimeoutMillis} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code evictorShutdownTimeoutMillis} for
* this configuration instance
*
* @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
* @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
*/
public long getEvictorShutdownTimeoutMillis() {
return evictorShutdownTimeoutMillis;
}
/**
* Set the value for the {@code evictorShutdownTimeoutMillis} configuration
* attribute for pools created with this configuration instance.
*
* @param evictorShutdownTimeoutMillis The new setting of
* {@code evictorShutdownTimeoutMillis} for this configuration
* instance
*
* @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
* @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
*/
public void setEvictorShutdownTimeoutMillis(
final long evictorShutdownTimeoutMillis) {
this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis;
}
/**
* Get the value for the {@code testOnCreate} configuration attribute for
* pools created with this configuration instance.
*
* @return The current setting of {@code testOnCreate} for this
* configuration instance
*
* @see GenericObjectPool#getTestOnCreate()
* @see GenericKeyedObjectPool#getTestOnCreate()
*
* @since 2.2
*/
public boolean getTestOnCreate() {
return testOnCreate;
}
/**
* Set the value for the {@code testOnCreate} configuration attribute for
* pools created with this configuration instance.
*
* @param testOnCreate The new setting of {@code testOnCreate}
* for this configuration instance
*
* @see GenericObjectPool#getTestOnCreate()
* @see GenericKeyedObjectPool#getTestOnCreate()
*
* @since 2.2
*/
public void setTestOnCreate(final boolean testOnCreate) {
this.testOnCreate = testOnCreate;
}
/**
* Get the value for the {@code testOnBorrow} configuration attribute for
* pools created with this configuration instance.
*
* @return The current setting of {@code testOnBorrow} for this
* configuration instance
*
* @see GenericObjectPool#getTestOnBorrow()
* @see GenericKeyedObjectPool#getTestOnBorrow()
*/
public boolean getTestOnBorrow() {
return testOnBorrow;
}
/**
* Set the value for the {@code testOnBorrow} configuration attribute for
* pools created with this configuration instance.
*
* @param testOnBorrow The new setting of {@code testOnBorrow}
* for this configuration instance
*
* @see GenericObjectPool#getTestOnBorrow()
* @see GenericKeyedObjectPool#getTestOnBorrow()
*/
public void setTestOnBorrow(final boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
/**
* Get the value for the {@code testOnReturn} configuration attribute for
* pools created with this configuration instance.
*
* @return The current setting of {@code testOnReturn} for this
* configuration instance
*
* @see GenericObjectPool#getTestOnReturn()
* @see GenericKeyedObjectPool#getTestOnReturn()
*/
public boolean getTestOnReturn() {
return testOnReturn;
}
/**
* Set the value for the {@code testOnReturn} configuration attribute for
* pools created with this configuration instance.
*
* @param testOnReturn The new setting of {@code testOnReturn}
* for this configuration instance
*
* @see GenericObjectPool#getTestOnReturn()
* @see GenericKeyedObjectPool#getTestOnReturn()
*/
public void setTestOnReturn(final boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
/**
* Get the value for the {@code testWhileIdle} configuration attribute for
* pools created with this configuration instance.
*
* @return The current setting of {@code testWhileIdle} for this
* configuration instance
*
* @see GenericObjectPool#getTestWhileIdle()
* @see GenericKeyedObjectPool#getTestWhileIdle()
*/
public boolean getTestWhileIdle() {
return testWhileIdle;
}
/**
* Set the value for the {@code testWhileIdle} configuration attribute for
* pools created with this configuration instance.
*
* @param testWhileIdle The new setting of {@code testWhileIdle}
* for this configuration instance
*
* @see GenericObjectPool#getTestWhileIdle()
* @see GenericKeyedObjectPool#getTestWhileIdle()
*/
public void setTestWhileIdle(final boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
/**
* Get the value for the {@code timeBetweenEvictionRunsMillis} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code timeBetweenEvictionRunsMillis} for
* this configuration instance
*
* @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
* @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
*/
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
/**
* Set the value for the {@code timeBetweenEvictionRunsMillis} configuration
* attribute for pools created with this configuration instance.
*
* @param timeBetweenEvictionRunsMillis The new setting of
* {@code timeBetweenEvictionRunsMillis} for this configuration
* instance
*
* @see GenericObjectPool#getTimeBetweenEvictionRunsMillis()
* @see GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis()
*/
public void setTimeBetweenEvictionRunsMillis(
final long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
/**
* Get the value for the {@code evictionPolicyClass} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code evictionPolicyClass} for this
* configuration instance
*
* @see GenericObjectPool#getEvictionPolicy()
* @see GenericKeyedObjectPool#getEvictionPolicy()
* @since 2.6.0
*/
public EvictionPolicy<T> getEvictionPolicy() {
return evictionPolicy;
}
/**
* Get the value for the {@code evictionPolicyClassName} configuration
* attribute for pools created with this configuration instance.
*
* @return The current setting of {@code evictionPolicyClassName} for this
* configuration instance
*
* @see GenericObjectPool#getEvictionPolicyClassName()
* @see GenericKeyedObjectPool#getEvictionPolicyClassName()
*/
public String getEvictionPolicyClassName() {
return evictionPolicyClassName;
}
/**
* Set the value for the {@code evictionPolicyClass} configuration
* attribute for pools created with this configuration instance.
*
* @param evictionPolicy The new setting of
* {@code evictionPolicyClass} for this configuration instance
*
* @see GenericObjectPool#getEvictionPolicy()
* @see GenericKeyedObjectPool#getEvictionPolicy()
* @since 2.6.0
*/
public void setEvictionPolicy(final EvictionPolicy<T> evictionPolicy) {
this.evictionPolicy = evictionPolicy;
}
/**
* Set the value for the {@code evictionPolicyClassName} configuration
* attribute for pools created with this configuration instance.
*
* @param evictionPolicyClassName The new setting of
* {@code evictionPolicyClassName} for this configuration instance
*
* @see GenericObjectPool#getEvictionPolicyClassName()
* @see GenericKeyedObjectPool#getEvictionPolicyClassName()
*/
public void setEvictionPolicyClassName(final String evictionPolicyClassName) {
this.evictionPolicyClassName = evictionPolicyClassName;
}
/**
* Get the value for the {@code blockWhenExhausted} configuration attribute
* for pools created with this configuration instance.
*
* @return The current setting of {@code blockWhenExhausted} for this
* configuration instance
*
* @see GenericObjectPool#getBlockWhenExhausted()
* @see GenericKeyedObjectPool#getBlockWhenExhausted()
*/
public boolean getBlockWhenExhausted() {
return blockWhenExhausted;
}
/**
* Set the value for the {@code blockWhenExhausted} configuration attribute
* for pools created with this configuration instance.
*
* @param blockWhenExhausted The new setting of {@code blockWhenExhausted}
* for this configuration instance
*
* @see GenericObjectPool#getBlockWhenExhausted()
* @see GenericKeyedObjectPool#getBlockWhenExhausted()
*/
public void setBlockWhenExhausted(final boolean blockWhenExhausted) {
this.blockWhenExhausted = blockWhenExhausted;
}
/**
* Gets the value of the flag that determines if JMX will be enabled for
* pools created with this configuration instance.
*
* @return The current setting of {@code jmxEnabled} for this configuration
* instance
*/
public boolean getJmxEnabled() {
return jmxEnabled;
}
/**
* Sets the value of the flag that determines if JMX will be enabled for
* pools created with this configuration instance.
*
* @param jmxEnabled The new setting of {@code jmxEnabled}
* for this configuration instance
*/
public void setJmxEnabled(final boolean jmxEnabled) {
this.jmxEnabled = jmxEnabled;
}
/**
* Gets the value of the JMX name base that will be used as part of the
* name assigned to JMX enabled pools created with this configuration
* instance. A value of <code>null</code> means that the pool will define
* the JMX name base.
*
* @return The current setting of {@code jmxNameBase} for this
* configuration instance
*/
public String getJmxNameBase() {
return jmxNameBase;
}
/**
* Sets the value of the JMX name base that will be used as part of the
* name assigned to JMX enabled pools created with this configuration
* instance. A value of <code>null</code> means that the pool will define
* the JMX name base.
*
* @param jmxNameBase The new setting of {@code jmxNameBase}
* for this configuration instance
*/
public void setJmxNameBase(final String jmxNameBase) {
this.jmxNameBase = jmxNameBase;
}
/**
* Gets the value of the JMX name prefix that will be used as part of the
* name assigned to JMX enabled pools created with this configuration
* instance.
*
* @return The current setting of {@code jmxNamePrefix} for this
* configuration instance
*/
public String getJmxNamePrefix() {
return jmxNamePrefix;
}
/**
* Sets the value of the JMX name prefix that will be used as part of the
* name assigned to JMX enabled pools created with this configuration
* instance.
*
* @param jmxNamePrefix The new setting of {@code jmxNamePrefix}
* for this configuration instance
*/
public void setJmxNamePrefix(final String jmxNamePrefix) {
this.jmxNamePrefix = jmxNamePrefix;
}
@Override
protected void toStringAppendFields(final StringBuilder builder) {
builder.append("lifo=");
builder.append(lifo);
builder.append(", fairness=");
builder.append(fairness);
builder.append(", maxWaitMillis=");
builder.append(maxWaitMillis);
builder.append(", minEvictableIdleTimeMillis=");
builder.append(minEvictableIdleTimeMillis);
builder.append(", softMinEvictableIdleTimeMillis=");
builder.append(softMinEvictableIdleTimeMillis);
builder.append(", numTestsPerEvictionRun=");
builder.append(numTestsPerEvictionRun);
builder.append(", evictionPolicyClassName=");
builder.append(evictionPolicyClassName);
builder.append(", testOnCreate=");
builder.append(testOnCreate);
builder.append(", testOnBorrow=");
builder.append(testOnBorrow);
builder.append(", testOnReturn=");
builder.append(testOnReturn);
builder.append(", testWhileIdle=");
builder.append(testWhileIdle);
builder.append(", timeBetweenEvictionRunsMillis=");
builder.append(timeBetweenEvictionRunsMillis);
builder.append(", blockWhenExhausted=");
builder.append(blockWhenExhausted);
builder.append(", jmxEnabled=");
builder.append(jmxEnabled);
builder.append(", jmxNamePrefix=");
builder.append(jmxNamePrefix);
builder.append(", jmxNameBase=");
builder.append(jmxNameBase);
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.dbcp.pool2.impl;
import java.io.PrintWriter;
/**
* Strategy for obtaining and printing the current call stack. This is primarily useful for
* {@link org.apache.tomcat.dbcp.pool2.UsageTracking usage tracking} so
* that different JVMs and configurations can use more efficient strategies
* for obtaining the current call stack depending on metadata needs.
*
* @see CallStackUtils
* @since 2.4.3
*/
public interface CallStack {
/**
* Prints the current stack trace if available to a PrintWriter. The format is undefined and is primarily useful
* for debugging issues with {@link org.apache.tomcat.dbcp.pool2.PooledObject} usage in user code.
*
* @param writer a PrintWriter to write the current stack trace to if available
* @return true if a stack trace was available to print or false if nothing was printed
*/
boolean printStackTrace(final PrintWriter writer);
/**
* Takes a snapshot of the current call stack. Subsequent calls to {@link #printStackTrace(PrintWriter)} will print
* out that stack trace until it is {@linkplain #clear() cleared}.
*/
void fillInStackTrace();
/**
* Clears the current stack trace snapshot. Subsequent calls to {@link #printStackTrace(PrintWriter)} will be
* no-ops until another call to {@link #fillInStackTrace()}.
*/
void clear();
}

View File

@@ -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.dbcp.pool2.impl;
import java.security.AccessControlException;
/**
* Utility methods for {@link CallStack}.
*
* @since 2.4.3
*/
public final class CallStackUtils {
/**
* Returns whether the caller can create a security manager in the current environment.
*
* @return {@code true} if it is able to create a security manager in the current environment, {@code false}
* otherwise.
*/
private static boolean canCreateSecurityManager() {
final SecurityManager manager = System.getSecurityManager();
if (manager == null) {
return true;
}
try {
manager.checkPermission(new RuntimePermission("createSecurityManager"));
return true;
} catch (final AccessControlException ignored) {
return false;
}
}
/**
* Constructs a new {@link CallStack} using the fastest allowed strategy.
*
* @param messageFormat message (or format) to print first in stack traces
* @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; otherwise,
* print message format literally
* @return a new CallStack
* @deprecated use {@link #newCallStack(String, boolean, boolean)}
*/
@Deprecated
public static CallStack newCallStack(final String messageFormat, final boolean useTimestamp) {
return newCallStack(messageFormat, useTimestamp, false);
}
/**
* Constructs a new {@link CallStack} using the fasted allowed strategy.
*
* @param messageFormat message (or format) to print first in stack traces
* @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp;
* otherwise, print message format literally
* @param requireFullStackTrace if true, forces the use of a stack walking mechanism that includes full stack trace
* information; otherwise, uses a faster implementation if possible
* @return a new CallStack
* @since 2.5
*/
public static CallStack newCallStack(final String messageFormat,
final boolean useTimestamp,
final boolean requireFullStackTrace) {
return canCreateSecurityManager() && !requireFullStackTrace
? new SecurityManagerCallStack(messageFormat, useTimestamp)
: new ThrowableCallStack(messageFormat, useTimestamp);
}
/**
* Hidden constructor.
*/
private CallStackUtils() {
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.dbcp.pool2.impl;
import org.apache.tomcat.dbcp.pool2.PooledObject;
/**
* Provides the default implementation of {@link EvictionPolicy} used by the
* pools. Objects will be evicted if the following conditions are met:
* <ul>
* <li>the object has been idle longer than
* {@link GenericObjectPool#getMinEvictableIdleTimeMillis()} /
* {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis()}</li>
* <li>there are more than {@link GenericObjectPool#getMinIdle()} /
* {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in
* the pool and the object has been idle for longer than
* {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} /
* {@link GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()}
* </ul>
* <p>
* This class is immutable and thread-safe.
* </p>
*
* @param <T> the type of objects in the pool
*
* @since 2.0
*/
public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
@Override
public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
final int idleCount) {
if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() &&
config.getMinIdle() < idleCount) ||
config.getIdleEvictTime() < underTest.getIdleTimeMillis()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,298 @@
/*
* 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.dbcp.pool2.impl;
import java.io.PrintWriter;
import java.util.Deque;
import org.apache.tomcat.dbcp.pool2.PooledObject;
import org.apache.tomcat.dbcp.pool2.PooledObjectState;
import org.apache.tomcat.dbcp.pool2.TrackedUse;
/**
* This wrapper is used to track the additional information, such as state, for
* the pooled objects.
* <p>
* This class is intended to be thread-safe.
* </p>
*
* @param <T> the type of object in the pool
*
* @since 2.0
*/
public class DefaultPooledObject<T> implements PooledObject<T> {
private final T object;
private PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid
private final long createTime = System.currentTimeMillis();
private volatile long lastBorrowTime = createTime;
private volatile long lastUseTime = createTime;
private volatile long lastReturnTime = createTime;
private volatile boolean logAbandoned = false;
private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE;
private volatile CallStack usedBy = NoOpCallStack.INSTANCE;
private volatile long borrowedCount = 0;
/**
* Creates a new instance that wraps the provided object so that the pool can
* track the state of the pooled object.
*
* @param object The object to wrap
*/
public DefaultPooledObject(final T object) {
this.object = object;
}
@Override
public T getObject() {
return object;
}
@Override
public long getCreateTime() {
return createTime;
}
@Override
public long getActiveTimeMillis() {
// Take copies to avoid threading issues
final long rTime = lastReturnTime;
final long bTime = lastBorrowTime;
if (rTime > bTime) {
return rTime - bTime;
}
return System.currentTimeMillis() - bTime;
}
@Override
public long getIdleTimeMillis() {
final long elapsed = System.currentTimeMillis() - lastReturnTime;
// elapsed may be negative if:
// - another thread updates lastReturnTime during the calculation window
// - System.currentTimeMillis() is not monotonic (e.g. system time is set back)
return elapsed >= 0 ? elapsed : 0;
}
@Override
public long getLastBorrowTime() {
return lastBorrowTime;
}
@Override
public long getLastReturnTime() {
return lastReturnTime;
}
/**
* Gets the number of times this object has been borrowed.
* @return The number of times this object has been borrowed.
* @since 2.1
*/
@Override
public long getBorrowedCount() {
return borrowedCount;
}
/**
* Returns an estimate of the last time this object was used. If the class
* of the pooled object implements {@link TrackedUse}, what is returned is
* the maximum of {@link TrackedUse#getLastUsed()} and
* {@link #getLastBorrowTime()}; otherwise this method gives the same
* value as {@link #getLastBorrowTime()}.
*
* @return the last time this object was used
*/
@Override
public long getLastUsedTime() {
if (object instanceof TrackedUse) {
return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime);
}
return lastUseTime;
}
@Override
public int compareTo(final PooledObject<T> other) {
final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime();
if (lastActiveDiff == 0) {
// Make sure the natural ordering is broadly consistent with equals
// although this will break down if distinct objects have the same
// identity hash code.
// see java.lang.Comparable Javadocs
return System.identityHashCode(this) - System.identityHashCode(other);
}
// handle int overflow
return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
}
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
result.append("Object: ");
result.append(object.toString());
result.append(", State: ");
synchronized (this) {
result.append(state.toString());
}
return result.toString();
// TODO add other attributes
}
@Override
public synchronized boolean startEvictionTest() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.EVICTION;
return true;
}
return false;
}
@Override
public synchronized boolean endEvictionTest(
final Deque<PooledObject<T>> idleQueue) {
if (state == PooledObjectState.EVICTION) {
state = PooledObjectState.IDLE;
return true;
} else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
state = PooledObjectState.IDLE;
if (!idleQueue.offerFirst(this)) {
// TODO - Should never happen
}
}
return false;
}
/**
* Allocates the object.
*
* @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
*/
@Override
public synchronized boolean allocate() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowTime = System.currentTimeMillis();
lastUseTime = lastBorrowTime;
borrowedCount++;
if (logAbandoned) {
borrowedBy.fillInStackTrace();
}
return true;
} else if (state == PooledObjectState.EVICTION) {
// TODO Allocate anyway and ignore eviction test
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
return false;
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
}
/**
* Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE}
* if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}.
*
* @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
*/
@Override
public synchronized boolean deallocate() {
if (state == PooledObjectState.ALLOCATED ||
state == PooledObjectState.RETURNING) {
state = PooledObjectState.IDLE;
lastReturnTime = System.currentTimeMillis();
borrowedBy.clear();
return true;
}
return false;
}
/**
* Sets the state to {@link PooledObjectState#INVALID INVALID}
*/
@Override
public synchronized void invalidate() {
state = PooledObjectState.INVALID;
}
@Override
public void use() {
lastUseTime = System.currentTimeMillis();
usedBy.fillInStackTrace();
}
@Override
public void printStackTrace(final PrintWriter writer) {
boolean written = borrowedBy.printStackTrace(writer);
written |= usedBy.printStackTrace(writer);
if (written) {
writer.flush();
}
}
/**
* Returns the state of this object.
* @return state
*/
@Override
public synchronized PooledObjectState getState() {
return state;
}
/**
* Marks the pooled object as abandoned.
*/
@Override
public synchronized void markAbandoned() {
state = PooledObjectState.ABANDONED;
}
/**
* Marks the object as returning to the pool.
*/
@Override
public synchronized void markReturning() {
state = PooledObjectState.RETURNING;
}
@Override
public void setLogAbandoned(final boolean logAbandoned) {
this.logAbandoned = logAbandoned;
}
/**
* Configures the stack trace generation strategy based on whether or not fully
* detailed stack traces are required. When set to false, abandoned logs may
* only include caller class information rather than method names, line numbers,
* and other normal metadata available in a full stack trace.
*
* @param requireFullStackTrace the new configuration setting for abandoned object
* logging
* @since 2.5
*/
@Override
public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
"yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
true, requireFullStackTrace);
usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
false, requireFullStackTrace);
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.dbcp.pool2.impl;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import org.apache.tomcat.dbcp.pool2.PooledObject;
/**
* Implementation of object that is used to provide information on pooled
* objects via JMX.
*
* @since 2.0
*/
public class DefaultPooledObjectInfo implements DefaultPooledObjectInfoMBean {
private final PooledObject<?> pooledObject;
/**
* Create a new instance for the given pooled object.
*
* @param pooledObject The pooled object that this instance will represent
*/
public DefaultPooledObjectInfo(final PooledObject<?> pooledObject) {
this.pooledObject = pooledObject;
}
@Override
public long getCreateTime() {
return pooledObject.getCreateTime();
}
@Override
public String getCreateTimeFormatted() {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
return sdf.format(Long.valueOf(pooledObject.getCreateTime()));
}
@Override
public long getLastBorrowTime() {
return pooledObject.getLastBorrowTime();
}
@Override
public String getLastBorrowTimeFormatted() {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
return sdf.format(Long.valueOf(pooledObject.getLastBorrowTime()));
}
@Override
public String getLastBorrowTrace() {
final StringWriter sw = new StringWriter();
pooledObject.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
@Override
public long getLastReturnTime() {
return pooledObject.getLastReturnTime();
}
@Override
public String getLastReturnTimeFormatted() {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
return sdf.format(Long.valueOf(pooledObject.getLastReturnTime()));
}
@Override
public String getPooledObjectType() {
return pooledObject.getObject().getClass().getName();
}
@Override
public String getPooledObjectToString() {
return pooledObject.getObject().toString();
}
@Override
public long getBorrowedCount() {
return pooledObject.getBorrowedCount();
}
/**
* @since 2.4.3
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("DefaultPooledObjectInfo [pooledObject=");
builder.append(pooledObject);
builder.append("]");
return builder.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More