1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.jdbcx; |
7 | |
8 | import java.io.IOException; |
9 | import java.io.ObjectInputStream; |
10 | import java.io.PrintWriter; |
11 | import java.io.Serializable; |
12 | import java.sql.Connection; |
13 | import java.sql.SQLException; |
14 | import java.util.Properties; |
15 | import javax.naming.Reference; |
16 | import javax.naming.Referenceable; |
17 | import javax.naming.StringRefAddr; |
18 | import javax.sql.ConnectionPoolDataSource; |
19 | import javax.sql.DataSource; |
20 | import javax.sql.PooledConnection; |
21 | import javax.sql.XAConnection; |
22 | import javax.sql.XADataSource; |
23 | import org.h2.Driver; |
24 | import org.h2.jdbc.JdbcConnection; |
25 | import org.h2.message.TraceObject; |
26 | import org.h2.util.StringUtils; |
27 | |
28 | //## Java 1.7 ## |
29 | import java.util.logging.Logger; |
30 | //*/ |
31 | |
32 | /** |
33 | * A data source for H2 database connections. It is a factory for XAConnection |
34 | * and Connection objects. This class is usually registered in a JNDI naming |
35 | * service. To create a data source object and register it with a JNDI service, |
36 | * use the following code: |
37 | * |
38 | * <pre> |
39 | * import org.h2.jdbcx.JdbcDataSource; |
40 | * import javax.naming.Context; |
41 | * import javax.naming.InitialContext; |
42 | * JdbcDataSource ds = new JdbcDataSource(); |
43 | * ds.setURL("jdbc:h2:˜/test"); |
44 | * ds.setUser("sa"); |
45 | * ds.setPassword("sa"); |
46 | * Context ctx = new InitialContext(); |
47 | * ctx.bind("jdbc/dsName", ds); |
48 | * </pre> |
49 | * |
50 | * To use a data source that is already registered, use the following code: |
51 | * |
52 | * <pre> |
53 | * import java.sql.Connection; |
54 | * import javax.sql.DataSource; |
55 | * import javax.naming.Context; |
56 | * import javax.naming.InitialContext; |
57 | * Context ctx = new InitialContext(); |
58 | * DataSource ds = (DataSource) ctx.lookup("jdbc/dsName"); |
59 | * Connection conn = ds.getConnection(); |
60 | * </pre> |
61 | * |
62 | * In this example the user name and password are serialized as |
63 | * well; this may be a security problem in some cases. |
64 | */ |
65 | public class JdbcDataSource extends TraceObject implements XADataSource, |
66 | DataSource, ConnectionPoolDataSource, Serializable, Referenceable { |
67 | |
68 | private static final long serialVersionUID = 1288136338451857771L; |
69 | |
70 | private transient JdbcDataSourceFactory factory; |
71 | private transient PrintWriter logWriter; |
72 | private int loginTimeout; |
73 | private String userName = ""; |
74 | private char[] passwordChars = { }; |
75 | private String url = ""; |
76 | private String description; |
77 | |
78 | static { |
79 | org.h2.Driver.load(); |
80 | } |
81 | |
82 | /** |
83 | * The public constructor. |
84 | */ |
85 | public JdbcDataSource() { |
86 | initFactory(); |
87 | int id = getNextId(TraceObject.DATA_SOURCE); |
88 | setTrace(factory.getTrace(), TraceObject.DATA_SOURCE, id); |
89 | } |
90 | |
91 | /** |
92 | * Called when de-serializing the object. |
93 | * |
94 | * @param in the input stream |
95 | */ |
96 | private void readObject(ObjectInputStream in) throws IOException, |
97 | ClassNotFoundException { |
98 | initFactory(); |
99 | in.defaultReadObject(); |
100 | } |
101 | |
102 | private void initFactory() { |
103 | factory = new JdbcDataSourceFactory(); |
104 | } |
105 | |
106 | /** |
107 | * Get the login timeout in seconds, 0 meaning no timeout. |
108 | * |
109 | * @return the timeout in seconds |
110 | */ |
111 | @Override |
112 | public int getLoginTimeout() { |
113 | debugCodeCall("getLoginTimeout"); |
114 | return loginTimeout; |
115 | } |
116 | |
117 | /** |
118 | * Set the login timeout in seconds, 0 meaning no timeout. |
119 | * The default value is 0. |
120 | * This value is ignored by this database. |
121 | * |
122 | * @param timeout the timeout in seconds |
123 | */ |
124 | @Override |
125 | public void setLoginTimeout(int timeout) { |
126 | debugCodeCall("setLoginTimeout", timeout); |
127 | this.loginTimeout = timeout; |
128 | } |
129 | |
130 | /** |
131 | * Get the current log writer for this object. |
132 | * |
133 | * @return the log writer |
134 | */ |
135 | @Override |
136 | public PrintWriter getLogWriter() { |
137 | debugCodeCall("getLogWriter"); |
138 | return logWriter; |
139 | } |
140 | |
141 | /** |
142 | * Set the current log writer for this object. |
143 | * This value is ignored by this database. |
144 | * |
145 | * @param out the log writer |
146 | */ |
147 | @Override |
148 | public void setLogWriter(PrintWriter out) { |
149 | debugCodeCall("setLogWriter(out)"); |
150 | logWriter = out; |
151 | } |
152 | |
153 | /** |
154 | * Open a new connection using the current URL, user name and password. |
155 | * |
156 | * @return the connection |
157 | */ |
158 | @Override |
159 | public Connection getConnection() throws SQLException { |
160 | debugCodeCall("getConnection"); |
161 | return getJdbcConnection(userName, |
162 | StringUtils.cloneCharArray(passwordChars)); |
163 | } |
164 | |
165 | /** |
166 | * Open a new connection using the current URL and the specified user name |
167 | * and password. |
168 | * |
169 | * @param user the user name |
170 | * @param password the password |
171 | * @return the connection |
172 | */ |
173 | @Override |
174 | public Connection getConnection(String user, String password) |
175 | throws SQLException { |
176 | if (isDebugEnabled()) { |
177 | debugCode("getConnection("+quote(user)+", \"\");"); |
178 | } |
179 | return getJdbcConnection(user, convertToCharArray(password)); |
180 | } |
181 | |
182 | private JdbcConnection getJdbcConnection(String user, char[] password) |
183 | throws SQLException { |
184 | if (isDebugEnabled()) { |
185 | debugCode("getJdbcConnection("+quote(user)+", new char[0]);"); |
186 | } |
187 | Properties info = new Properties(); |
188 | info.setProperty("user", user); |
189 | info.put("password", password); |
190 | Connection conn = Driver.load().connect(url, info); |
191 | if (conn == null) { |
192 | throw new SQLException("No suitable driver found for " + url, |
193 | "08001", 8001); |
194 | } else if (!(conn instanceof JdbcConnection)) { |
195 | throw new SQLException( |
196 | "Connecting with old version is not supported: " + url, |
197 | "08001", 8001); |
198 | } |
199 | return (JdbcConnection) conn; |
200 | } |
201 | |
202 | /** |
203 | * Get the current URL. |
204 | * |
205 | * @return the URL |
206 | */ |
207 | public String getURL() { |
208 | debugCodeCall("getURL"); |
209 | return url; |
210 | } |
211 | |
212 | /** |
213 | * Set the current URL. |
214 | * |
215 | * @param url the new URL |
216 | */ |
217 | public void setURL(String url) { |
218 | debugCodeCall("setURL", url); |
219 | this.url = url; |
220 | } |
221 | |
222 | /** |
223 | * Get the current URL. |
224 | * This method does the same as getURL, but this methods signature conforms |
225 | * the JavaBean naming convention. |
226 | * |
227 | * @return the URL |
228 | */ |
229 | public String getUrl() { |
230 | debugCodeCall("getUrl"); |
231 | return url; |
232 | } |
233 | |
234 | /** |
235 | * Set the current URL. |
236 | * This method does the same as setURL, but this methods signature conforms |
237 | * the JavaBean naming convention. |
238 | * |
239 | * @param url the new URL |
240 | */ |
241 | public void setUrl(String url) { |
242 | debugCodeCall("setUrl", url); |
243 | this.url = url; |
244 | } |
245 | |
246 | /** |
247 | * Set the current password. |
248 | * |
249 | * @param password the new password. |
250 | */ |
251 | public void setPassword(String password) { |
252 | debugCodeCall("setPassword", ""); |
253 | this.passwordChars = convertToCharArray(password); |
254 | } |
255 | |
256 | /** |
257 | * Set the current password in the form of a char array. |
258 | * |
259 | * @param password the new password in the form of a char array. |
260 | */ |
261 | public void setPasswordChars(char[] password) { |
262 | if (isDebugEnabled()) { |
263 | debugCode("setPasswordChars(new char[0]);"); |
264 | } |
265 | this.passwordChars = password; |
266 | } |
267 | |
268 | private static char[] convertToCharArray(String s) { |
269 | return s == null ? null : s.toCharArray(); |
270 | } |
271 | |
272 | private static String convertToString(char[] a) { |
273 | return a == null ? null : new String(a); |
274 | } |
275 | |
276 | /** |
277 | * Get the current password. |
278 | * |
279 | * @return the password |
280 | */ |
281 | public String getPassword() { |
282 | debugCodeCall("getPassword"); |
283 | return convertToString(passwordChars); |
284 | } |
285 | |
286 | /** |
287 | * Get the current user name. |
288 | * |
289 | * @return the user name |
290 | */ |
291 | public String getUser() { |
292 | debugCodeCall("getUser"); |
293 | return userName; |
294 | } |
295 | |
296 | /** |
297 | * Set the current user name. |
298 | * |
299 | * @param user the new user name |
300 | */ |
301 | public void setUser(String user) { |
302 | debugCodeCall("setUser", user); |
303 | this.userName = user; |
304 | } |
305 | |
306 | /** |
307 | * Get the current description. |
308 | * |
309 | * @return the description |
310 | */ |
311 | public String getDescription() { |
312 | debugCodeCall("getDescription"); |
313 | return description; |
314 | } |
315 | |
316 | /** |
317 | * Set the description. |
318 | * |
319 | * @param description the new description |
320 | */ |
321 | public void setDescription(String description) { |
322 | debugCodeCall("getDescription", description); |
323 | this.description = description; |
324 | } |
325 | |
326 | /** |
327 | * Get a new reference for this object, using the current settings. |
328 | * |
329 | * @return the new reference |
330 | */ |
331 | @Override |
332 | public Reference getReference() { |
333 | debugCodeCall("getReference"); |
334 | String factoryClassName = JdbcDataSourceFactory.class.getName(); |
335 | Reference ref = new Reference(getClass().getName(), factoryClassName, null); |
336 | ref.add(new StringRefAddr("url", url)); |
337 | ref.add(new StringRefAddr("user", userName)); |
338 | ref.add(new StringRefAddr("password", convertToString(passwordChars))); |
339 | ref.add(new StringRefAddr("loginTimeout", String.valueOf(loginTimeout))); |
340 | ref.add(new StringRefAddr("description", description)); |
341 | return ref; |
342 | } |
343 | |
344 | /** |
345 | * Open a new XA connection using the current URL, user name and password. |
346 | * |
347 | * @return the connection |
348 | */ |
349 | @Override |
350 | public XAConnection getXAConnection() throws SQLException { |
351 | debugCodeCall("getXAConnection"); |
352 | int id = getNextId(XA_DATA_SOURCE); |
353 | return new JdbcXAConnection(factory, id, getJdbcConnection(userName, |
354 | StringUtils.cloneCharArray(passwordChars))); |
355 | } |
356 | |
357 | /** |
358 | * Open a new XA connection using the current URL and the specified user |
359 | * name and password. |
360 | * |
361 | * @param user the user name |
362 | * @param password the password |
363 | * @return the connection |
364 | */ |
365 | @Override |
366 | public XAConnection getXAConnection(String user, String password) |
367 | throws SQLException { |
368 | if (isDebugEnabled()) { |
369 | debugCode("getXAConnection("+quote(user)+", \"\");"); |
370 | } |
371 | int id = getNextId(XA_DATA_SOURCE); |
372 | return new JdbcXAConnection(factory, id, getJdbcConnection(user, |
373 | convertToCharArray(password))); |
374 | } |
375 | |
376 | /** |
377 | * Open a new pooled connection using the current URL, user name and |
378 | * password. |
379 | * |
380 | * @return the connection |
381 | */ |
382 | @Override |
383 | public PooledConnection getPooledConnection() throws SQLException { |
384 | debugCodeCall("getPooledConnection"); |
385 | return getXAConnection(); |
386 | } |
387 | |
388 | /** |
389 | * Open a new pooled connection using the current URL and the specified user |
390 | * name and password. |
391 | * |
392 | * @param user the user name |
393 | * @param password the password |
394 | * @return the connection |
395 | */ |
396 | @Override |
397 | public PooledConnection getPooledConnection(String user, String password) |
398 | throws SQLException { |
399 | if (isDebugEnabled()) { |
400 | debugCode("getPooledConnection("+quote(user)+", \"\");"); |
401 | } |
402 | return getXAConnection(user, password); |
403 | } |
404 | |
405 | /** |
406 | * [Not supported] Return an object of this class if possible. |
407 | * |
408 | * @param iface the class |
409 | */ |
410 | @Override |
411 | public <T> T unwrap(Class<T> iface) throws SQLException { |
412 | throw unsupported("unwrap"); |
413 | } |
414 | |
415 | /** |
416 | * [Not supported] Checks if unwrap can return an object of this class. |
417 | * |
418 | * @param iface the class |
419 | */ |
420 | @Override |
421 | public boolean isWrapperFor(Class<?> iface) throws SQLException { |
422 | throw unsupported("isWrapperFor"); |
423 | } |
424 | |
425 | /** |
426 | * [Not supported] |
427 | */ |
428 | //## Java 1.7 ## |
429 | @Override |
430 | public Logger getParentLogger() { |
431 | return null; |
432 | } |
433 | //*/ |
434 | |
435 | /** |
436 | * INTERNAL |
437 | */ |
438 | @Override |
439 | public String toString() { |
440 | return getTraceObjectName() + ": url=" + url + " user=" + userName; |
441 | } |
442 | |
443 | } |