From de790c35d06399f5ed56d6fe356965339add1ed2 Mon Sep 17 00:00:00 2001 From: fhanik Date: Thu, 6 Jan 2011 18:22:34 +0000 Subject: [PATCH] https://issues.apache.org/bugzilla/show_bug.cgi?id=49543 Add the ability to specify a data source link, to use a shared datasource with per application credentials git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1055989 13f79535-47bb-0310-9956-ffa450edef68 --- .../naming/factory/DataSourceLinkFactory.java | 140 +++++++++++++++++++++ webapps/docs/changelog.xml | 3 + webapps/docs/config/context.xml | 60 +++++++++ 3 files changed, 203 insertions(+) create mode 100644 java/org/apache/naming/factory/DataSourceLinkFactory.java diff --git a/java/org/apache/naming/factory/DataSourceLinkFactory.java b/java/org/apache/naming/factory/DataSourceLinkFactory.java new file mode 100644 index 000000000..bab654d6f --- /dev/null +++ b/java/org/apache/naming/factory/DataSourceLinkFactory.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.naming.factory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; +import javax.sql.DataSource; + +import org.apache.naming.ResourceLinkRef; + + + +/** + *

Object factory for resource links for shared data sources.

+ * + * @author Filip Hanik + * @version $Id: ResourceLinkFactory.java 939311 2010-04-29 14:01:02Z kkolinko $ + */ + +public class DataSourceLinkFactory extends ResourceLinkFactory + implements ObjectFactory { + + + // -------------------------------------------------- ObjectFactory Methods + + + /** + * Create a new DataSource instance. + * + * @param obj The reference object describing the DataSource + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) + throws NamingException { + Object result = super.getObjectInstance(obj, name, nameCtx, environment); + // Can we process this request? + if (result!=null) { + Reference ref = (Reference) obj; + + RefAddr userAttr = ref.get("username"); + RefAddr passAttr = ref.get("password"); + if (userAttr.getContent()!=null && passAttr.getContent()!=null) { + result = wrapDataSource(result,userAttr.getContent().toString(), passAttr.getContent().toString()); + } + } + return result; + } + + protected Object wrapDataSource(Object datasource, String username, String password) throws NamingException { + try { + Class proxyClass = Proxy.getProxyClass(datasource.getClass().getClassLoader(), datasource.getClass().getInterfaces()); + Constructor proxyConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + DataSourceHandler handler = new DataSourceHandler((DataSource)datasource, username, password); + return proxyConstructor.newInstance(handler); + }catch (Exception x) { + if (x instanceof NamingException) throw (NamingException)x; + else { + NamingException nx = new NamingException(x.getMessage()); + nx.initCause(x); + throw nx; + } + } + } + + /** + * Simple wrapper class that will allow a user to configure a ResourceLink for a data source + * so that when {@link javax.sql.DataSource#getConnection()} is called, it will invoke + * {@link javax.sql.DataSource#getConnection(String, String)} with the preconfigured username and password. + */ + public static class DataSourceHandler implements InvocationHandler { + DataSource ds; + String username; + String password; + public DataSourceHandler(DataSource ds, String username, String password) { + this.ds = ds; + this.username = username; + this.password = password; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + if ("getConnection".equals(method.getName()) && args.length==0) { + args = new String[] {username,password}; + } else if ("unwrap".equals(method.getName())) { + return unwrap((Class)args[0]); + } + try { + return method.invoke(ds,args); + }catch (Throwable t) { + if (t instanceof InvocationTargetException) { + InvocationTargetException it = (InvocationTargetException)t; + throw it.getCause()!=null?it.getCause():it; + } else { + throw t; + } + } + } + + public Object unwrap(Class iface) throws SQLException { + if (iface == DataSource.class) { + return ds; + } else { + throw new SQLException("Not a wrapper of "+iface.getName()); + } + } + + } + + + + +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 862b2eccc..aad0e7ab7 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -44,6 +44,9 @@
+ 49543 Allow Tomcat to use shared data sources with + per application credentials. (fhanik) + 8705: org.apache.catalina.SessionListener now extends java.util.EventListener. (markt) diff --git a/webapps/docs/config/context.xml b/webapps/docs/config/context.xml index 90136f45b..e9dd89521 100644 --- a/webapps/docs/config/context.xml +++ b/webapps/docs/config/context.xml @@ -979,8 +979,68 @@ application when it performs a lookup for this resource link.

+ +

The fully qualified Java class name for the class creating these objects. + This class should implement the javax.naming.spi.ObjectFactory interface.

+
+ +

When the attribute factory="org.apache.naming.factory.DataSourceLinkFactory" the resource link can be used with + two additional attributes to allow a shared data source to be used with different credentials. + When these two additional attributes are used in combination with the javax.sql.DataSource + type, different contexts can share a global data source with different credentials. + Under the hood, what happens is that a call to getConnection() + is simply translated to a call + getConnection(username, password) on the global data source. This is an easy way to get code to be transparent to what schemas are being used, + yet be able to control connections (or pools) in the global configuration. +

+ + + +

+
+ + +

+
+
+

Shared Data Source Example

+ +<GlobalNamingResources ...> + ... + <Resource name="sharedDataSource" + global="sharedDataSource" + type="javax.sql.DataSource" + username="bar" + password="barpass" + + ... + ... +</GlobalNamingResources> + +<Context path="/foo"...> + ... + <ResourceLink + name="appDataSource" + global="sharedDataSource" + type="javax.sql.DataSource" + factory="org.apache.naming.factory.DataSourceLinkFactory" + username="foo" + password="foopass" + ... +</Context> +<Context path="/bar"...> + ... + <ResourceLink + name="appDataSource" + global="sharedDataSource" + type="javax.sql.DataSource" + ... +</Context> + +

When a request for getConnection() is made in the /foo context, the request is translated into + getConnection("foo","foopass"), while a request in the /bar gets passed straight through.

-- 2.11.0