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=&quot;org.apache.naming.factory.DataSourceLinkFactory&quot;</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>
+&lt;GlobalNamingResources ...&gt;
+  ...
+  &lt;Resource name=&quot;sharedDataSource&quot;
+            global=&quot;sharedDataSource&quot;
+            type=&quot;javax.sql.DataSource&quot;
+            username=&quot;bar&quot;
+            password=&quot;barpass&quot;
+            
+            ...
+  ...
+&lt;/GlobalNamingResources&gt;
+
+&lt;Context path=&quot;/foo&quot;...&gt;
+  ...
+  &lt;ResourceLink 
+            name=&quot;appDataSource&quot;
+            global=&quot;sharedDataSource&quot;
+            type=&quot;javax.sql.DataSource&quot;
+            factory=&quot;org.apache.naming.factory.DataSourceLinkFactory&quot;
+            username=&quot;foo&quot;
+            password=&quot;foopass&quot;
+  ...
+&lt;/Context&gt;
+&lt;Context path=&quot;/bar&quot;...&gt;
+  ...
+  &lt;ResourceLink 
+            name=&quot;appDataSource&quot;
+            global=&quot;sharedDataSource&quot;
+            type=&quot;javax.sql.DataSource&quot;
+  ...
+&lt;/Context&gt;
+</source>    
+    <p>When a request for <code>getConnection()</code> is made in the 
<code>/foo</code> context, the request is translated into
+       <code>getConnection(&quot;foo&quot;,&quot;foopass&quot;)</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

Reply via email to