Author: fhanik Date: Thu Jan 6 18:22:34 2011 New Revision: 1055989 URL: http://svn.apache.org/viewvc?rev=1055989&view=rev Log: 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
Added: tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java (with props) Modified: tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/context.xml Added: tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java?rev=1055989&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java (added) +++ tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java Thu Jan 6 18:22:34 2011 @@ -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; + + + +/** + * <p>Object factory for resource links for shared data sources.</p> + * + * @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()); + } + } + + } + + + + +} Propchange: tomcat/trunk/java/org/apache/naming/factory/DataSourceLinkFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1055989&r1=1055988&r2=1055989&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Thu Jan 6 18:22:34 2011 @@ -44,6 +44,9 @@ <section name="Tomcat 7.0.6 (markt)"> <subsection name="Catalina"> <changelog> + <fix><bug>49543</bug> Allow Tomcat to use shared data sources with + per application credentials. (fhanik) + </fix> <fix> <bug>8705</bug>: <code>org.apache.catalina.SessionListener</code> now extends <code>java.util.EventListener</code>. (markt) Modified: tomcat/trunk/webapps/docs/config/context.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/context.xml?rev=1055989&r1=1055988&r2=1055989&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/context.xml (original) +++ tomcat/trunk/webapps/docs/config/context.xml Thu Jan 6 18:22:34 2011 @@ -979,8 +979,68 @@ application when it performs a lookup for this resource link.</p> </attribute> + <attribute name="factory" required="false"> + <p>The fully qualified Java class name for the class creating these objects. + This class should implement the <code>javax.naming.spi.ObjectFactory</code> interface.</p> + </attribute> </attributes> + + <p>When the attribute <code>factory="org.apache.naming.factory.DataSourceLinkFactory"</code> 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 <code>javax.sql.DataSource</code> + type, different contexts can share a global data source with different credentials. + Under the hood, what happens is that a call to <a href="http://download.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection()"><code>getConnection()</code></a> + is simply translated to a call <a href="http://download.oracle.com/javase/6/docs/api/javax/sql/DataSource.html#getConnection(java.lang.String,%20java.lang.String)"> + <code>getConnection(username, password)</code></a> 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. + </p> + <attributes> + + <attribute name="username" required="false"> + <p></p> + </attribute> + <attribute name="password" required="false"> + <p></p> + </attribute> + + </attributes> + <p>Shared Data Source Example</p> +<source> +<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> +</source> + <p>When a request for <code>getConnection()</code> is made in the <code>/foo</code> context, the request is translated into + <code>getConnection("foo","foopass")</code>, while a request in the <code>/bar</code> gets passed straight through.</p> </subsection> <subsection name="Transaction"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org