2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Data / Mainsoft.Data.Jdbc.Providers.jvm / GenericProvider.cs
blob690250c05958f50b22b0267e6692d8bfa893259f
1 //
2 // System.Data.OleDb.OleDbConnection
3 //
4 // Authors:
5 // Konstantin Triger <kostat@mainsoft.com>
6 // Boris Kirzner <borisk@mainsoft.com>
7 //
8 // (C) 2006 Mainsoft Corporation (http://www.mainsoft.com)
9 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Data.Common;
35 using System.Data.Configuration;
36 using System.Collections;
37 using System.Collections.Specialized;
38 using System.Reflection;
39 using System.Text.RegularExpressions;
40 using Mainsoft.Data.Configuration;
42 using System.Globalization;
44 using java.sql;
45 using javax.sql;
46 using javax.naming;
48 namespace Mainsoft.Data.Jdbc.Providers
50 public class GenericProvider : IConnectionProvider
52 #region JdbcUrlConnector
54 sealed class JdbcUrlConnector {
55 #region Consts
57 private static readonly Regex JdbcUrlPatternRegex= new Regex (@"\$\{(?<VALUE>[^$\{\}]*)\}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
59 #endregion // Consts
61 #region Fields
63 readonly IConnectionStringDictionary _keyMapper;
64 readonly GenericProvider _provider;
65 readonly ArrayList _excludedKeys = new ArrayList();
67 #endregion // Fields
69 #region Constructors
71 internal JdbcUrlConnector (GenericProvider provider, IConnectionStringDictionary keyMapper) {
72 _provider = provider;
73 _keyMapper = keyMapper;
76 #endregion // Constructors
79 #region Methods
81 internal java.sql.Connection Connection {
82 get {
83 java.util.Properties properties = new java.util.Properties ();
85 string url = (string)_keyMapper["JdbcURL"];
86 if (url == null) {
87 string jdbcUrlPattern = (string)_provider.ProviderInfo [ConfigurationConsts.JdbcUrlPattern];
88 if (jdbcUrlPattern == null || jdbcUrlPattern.Length == 0) {
89 //throw ExceptionHelper.JdbcUrlPatternNotFound ((string)_provider.ProviderInfo [ConfigurationConsts.Name]);
90 object [] args = new object [] {_provider.ProviderInfo [ConfigurationConsts.Name]};
91 throw new ArgumentException (String.Format("Provider '{0}' is not configured with valid JDBC URL pattern.",args));
93 MatchEvaluator evaluator = new MatchEvaluator (ReplaceEvaluator);
94 url = JdbcUrlPatternRegex.Replace (jdbcUrlPattern, evaluator);
96 else {
97 // hack for backward comatibility:
98 // if the connection string contains 'Provider',
99 // the following mapping will hold:
100 // 'User', 'User Id' --> 'user'
101 // 'Password' --> 'password'
102 if (_keyMapper["Provider"] != null) {
104 const string USER = "User";
105 const string USERID = "User Id";
106 const string PASSWORD = "Password";
108 string user = (string)_keyMapper[USER];
109 if (user != null) {
110 properties.put("user", user);
111 _excludedKeys.Add(USER);
113 else {
114 user = (string)_keyMapper[USERID];
115 if (user != null) {
116 properties.put("user", user);
117 _excludedKeys.Add(USERID);
121 string password = (string)_keyMapper[PASSWORD];
122 if (password != null) {
123 properties.put("password", password);
124 _excludedKeys.Add(PASSWORD);
129 if (_provider._excludedKeys != null)
130 _excludedKeys.AddRange(_provider._excludedKeys);
132 foreach(string key in _provider.KeyMapping.Keys) {
133 object value = _keyMapper [key];
134 if (value == null)
135 continue;
136 bool contains = false;
137 for (int i = 0; i < _excludedKeys.Count; i++) {
138 if (String.Compare((string)_excludedKeys[i], key,
139 true, CultureInfo.InvariantCulture) == 0) {
140 contains = true;
141 break;
144 if (!contains) {
145 properties.put (key, value);
146 _excludedKeys.Add(key);
150 for (int i = 0; i < _excludedKeys.Count; i++) {
151 string value = _keyMapper.GetConnectionStringKey((string)_excludedKeys[i]);
152 if (value != null)
153 _excludedKeys[i] = value;
156 foreach(string key in _keyMapper.Keys) {
157 object value = _keyMapper [key];
158 if (value == null)
159 continue;
160 bool contains = false;
161 for (int i = 0; i < _excludedKeys.Count; i++) {
162 if (String.Compare((string)_excludedKeys[i], key,
163 true, CultureInfo.InvariantCulture) == 0) {
164 contains = true;
165 break;
168 if (!contains) {
169 if (_provider._unsupportedKeys != null)
170 for (int i = 0; i < _provider._unsupportedKeys.Length; i++)
171 if (String.Compare ((string) _provider._unsupportedKeys [i], key,
172 true, CultureInfo.InvariantCulture) == 0)
173 throw new NotSupportedException (
174 String.Format ("The parameter '{0}' is not supported.", key));
176 properties.put (key, value);
180 Driver d = ActivateJdbcDriver ();
181 // TBD : add DriverManager.setLoginTimeout
182 if (d != null)
183 return d.connect (url, properties);
185 return DriverManager.getConnection (url, properties);
189 private string ReplaceEvaluator (Match m) {
190 Group g = m.Groups["VALUE"];
192 if (!g.Success)
193 return String.Empty;
195 string usedKey = g.Value.Trim();
197 string value = (string)_keyMapper [usedKey];
198 if (value == null)
199 throw new ArgumentException(
200 String.Format("Missing parameter {0}", g.Value),
201 "ConnectionString");
203 _excludedKeys.Add(usedKey);
204 return value;
207 private Driver ActivateJdbcDriver () {
208 string driver = (string) _keyMapper["JdbcDriverClassName"];
209 if (driver == null)
210 driver = (string) _provider.ProviderInfo [ConfigurationConsts.JdbcDriverClassName];
212 if (driver != null && driver.Length != 0) {
213 try {
214 java.lang.ClassLoader contextLoader = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
215 if (contextLoader != null)
216 return (Driver) contextLoader.loadClass (driver).newInstance ();
217 return (Driver) java.lang.Class.forName (driver).newInstance ();
219 catch (java.lang.ClassNotFoundException e) {
220 throw new TypeLoadException (e.Message, e);
222 catch (java.lang.InstantiationException e) {
223 throw new MemberAccessException (e.Message, e);
225 catch (java.lang.IllegalAccessException e) {
226 throw new MissingMethodException (e.Message, e);
230 return null;
233 #endregion // Methods
236 #endregion // JdbcUrlBuilder
238 #region DataSourceCache
240 private sealed class DataSourceCache : AbstractDbMetaDataCache {
241 internal DataSource GetDataSource(string dataSourceName,string namingProviderUrl,string namingFactoryInitial) {
242 Hashtable cache = Cache;
244 DataSource ds = cache[dataSourceName] as DataSource;
246 if (ds != null) {
247 return ds;
250 Context ctx = null;
252 java.util.Properties properties = new java.util.Properties();
254 if ((namingProviderUrl != null) && (namingProviderUrl.Length > 0)) {
255 properties.put("java.naming.provider.url",namingProviderUrl);
258 if ((namingFactoryInitial != null) && (namingFactoryInitial.Length > 0)) {
259 properties.put("java.naming.factory.initial",namingFactoryInitial);
262 ctx = new InitialContext(properties);
264 try {
265 ds = (DataSource)ctx.lookup(dataSourceName);
267 catch(javax.naming.NameNotFoundException e) {
268 // possible that is a Tomcat bug,
269 // so try to lookup for jndi datasource with "java:comp/env/" appended
270 ds = (DataSource)ctx.lookup("java:comp/env/" + dataSourceName);
273 cache[dataSourceName] = ds;
274 return ds;
278 #endregion // DatasourceCache
280 #region Fields
282 private static DataSourceCache _dataSourceCache = new DataSourceCache();
284 private readonly IDictionary _providerInfo;
285 private NameValueCollection _keyMapping;
286 private string[] _excludedKeys;
287 private string[] _unsupportedKeys;
289 #endregion // Fields
291 #region Constructors
293 public GenericProvider(IDictionary providerInfo)
295 _providerInfo = providerInfo;
296 _keyMapping = null;
299 #endregion // Constructors
301 #region Properties
303 protected IDictionary ProviderInfo
305 get { return _providerInfo; }
308 private NameValueCollection KeyMapping
312 if (_keyMapping == null)
313 InitKeyMapping ();
315 return _keyMapping;
319 #endregion // Properties
321 #region Methods
323 public virtual java.sql.Connection GetConnection (IConnectionStringDictionary conectionStringBuilder)
325 string dataSourceJndi = (string) conectionStringBuilder.GetValue ("jndi-datasource-name");
327 if (dataSourceJndi != null && dataSourceJndi.Length > 0) {
329 string namingProviderUrl = (string) conectionStringBuilder.GetValue ("naming-provider-url");
330 string namingFactoryInitial = (string) conectionStringBuilder.GetValue ("naming-factory-initial");
331 DataSource ds = _dataSourceCache.GetDataSource(dataSourceJndi,namingProviderUrl,namingFactoryInitial);
332 return ds.getConnection();
335 JdbcUrlConnector connector = new JdbcUrlConnector (this, conectionStringBuilder);
336 return connector.Connection;
339 public virtual IConnectionStringDictionary GetConnectionStringBuilder (string connectionString)
341 return new ConnectionStringDictionary(connectionString, KeyMapping);
344 private void InitKeyMapping ()
346 lock (this) {
347 if (_keyMapping != null)
348 return;
350 _keyMapping = new NameValueCollection (StringComparer.OrdinalIgnoreCase);
352 // create key mappings collection
353 string keyMappingsStr = (string) _providerInfo [ConfigurationConsts.KeyMapping];
354 if (keyMappingsStr != null) {
355 string [] keyMappings = keyMappingsStr.Split (ConfigurationConsts.SemicolonArr);
356 foreach (string keyMapping in keyMappings) {
357 if (keyMapping.Length == 0)
358 continue;
359 int equalsIndex = keyMapping.IndexOf ('=');
360 string key = keyMapping.Substring (0, equalsIndex).Trim ();
361 string [] mappings = keyMapping.Substring (equalsIndex + 1).Trim ().Split (ConfigurationConsts.CommaArr);
362 foreach (string mapping in mappings)
363 _keyMapping.Add (key, mapping.Trim ());
367 string keyMappingExcludesStr = (string) _providerInfo [ConfigurationConsts.KeyMappingExcludes];
368 if (keyMappingExcludesStr != null) {
369 _excludedKeys = keyMappingExcludesStr.Split (ConfigurationConsts.CommaArr);
370 for (int i = 0; i < _excludedKeys.Length; i++)
371 _excludedKeys [i] = _excludedKeys [i].Trim ();
374 string keyMappingUnsupportedStr = (string) _providerInfo [ConfigurationConsts.KeyMappingUnsupported];
375 if (keyMappingUnsupportedStr != null) {
376 _unsupportedKeys = keyMappingUnsupportedStr.Split (ConfigurationConsts.CommaArr);
377 for (int i = 0; i < _unsupportedKeys.Length; i++)
378 _unsupportedKeys [i] = _unsupportedKeys [i].Trim ();
383 #endregion // Methods