001: /*
002:
003: Derby - Class org.apache.derby.impl.drda.Database
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.drda;
023:
024: import java.sql.Connection;
025: import java.sql.Driver;
026: import java.sql.PreparedStatement;
027: import java.sql.Statement;
028: import java.sql.ResultSet;
029: import java.sql.SQLException;
030: import java.util.Hashtable;
031: import java.util.Enumeration;
032: import java.util.Properties;
033:
034: import org.apache.derby.iapi.jdbc.EngineConnection;
035: import org.apache.derby.iapi.reference.Attribute;
036: import org.apache.derby.iapi.tools.i18n.LocalizedResource;
037: import org.apache.derby.iapi.services.sanity.SanityManager;
038:
039: /**
040: Database stores information about the current database
041: It is used so that a session may have more than one database
042: */
043: class Database {
044:
045: protected String dbName; // database name
046: protected String shortDbName; // database name without attributes
047: String attrString = ""; // attribute string
048: protected int securityMechanism; // Security mechanism
049: protected String userId; // User Id
050: protected String password; // password
051: protected String decryptedUserId; // Decrypted User id
052: protected String decryptedPassword; // Decrypted password
053: protected byte[] passwordSubstitute;// password substitute - SECMEC_USRSSBPWD
054: protected boolean rdbAllowUpdates = true; // Database allows updates -default is true
055: protected int accessCount; // Number of times we have tried to
056: // set up access to this database (only 1
057: // allowed)
058: protected byte[] secTokenIn; // Security token from app requester
059: protected byte[] secTokenOut; // Security token sent to app requester
060: protected byte[] crrtkn; // Correlation token
061: protected String typDefNam; // Type definition name
062: protected int byteOrder; //deduced from typDefNam, save String comparisons
063: protected int ccsidSBC; // Single byte CCSID
064: protected int ccsidDBC; // Double byte CCSID
065: protected int ccsidMBC; // Mixed byte CCSID
066: protected String ccsidSBCEncoding; // Encoding for single byte code page
067: protected String ccsidDBCEncoding; // Encoding for double byte code page
068: protected String ccsidMBCEncoding; // Encoding for mixed byte code page
069: protected boolean RDBUPDRM_sent = false; //We have sent that an update
070: // occurred in this transaction
071: protected boolean sendTRGDFTRT = false; // Send package target default value
072:
073: /**
074: * Connection to the database in the embedded engine.
075: */
076: private EngineConnection conn;
077: DRDAStatement defaultStatement; // default statement used
078: // for execute imm
079: private DRDAStatement currentStatement; // current statement we are working on
080: private Hashtable stmtTable; // Hash table for storing statements
081:
082: boolean forXA = false;
083:
084: // constructor
085: /**
086: * Database constructor
087: *
088: * @param dbName database name
089: */
090: Database(String dbName) {
091: if (dbName != null) {
092: int attrOffset = dbName.indexOf(';');
093: if (attrOffset != -1) {
094: this .attrString = dbName.substring(attrOffset, dbName
095: .length());
096: this .shortDbName = dbName.substring(0, attrOffset);
097: } else
098: this .shortDbName = dbName;
099: }
100:
101: this .dbName = dbName;
102: this .stmtTable = new Hashtable();
103: initializeDefaultStatement();
104: }
105:
106: private void initializeDefaultStatement() {
107: this .defaultStatement = new DRDAStatement(this );
108: }
109:
110: /**
111: * Set connection and create the SQL statement for the default statement
112: *
113: * @param conn Connection
114: * @exception SQLException
115: */
116: final void setConnection(EngineConnection conn) throws SQLException {
117: this .conn = conn;
118: if (conn != null)
119: defaultStatement.setStatement(conn);
120: }
121:
122: /**
123: * Get the connection
124: *
125: * @return connection
126: */
127: final EngineConnection getConnection() {
128: return conn;
129: }
130:
131: /**
132: * Get current DRDA statement
133: *
134: * @return DRDAStatement
135: * @exception SQLException
136: */
137: protected DRDAStatement getCurrentStatement() {
138: return currentStatement;
139: }
140:
141: /**
142: * Get default statement for use in EXCIMM
143: *
144: * @return DRDAStatement
145: */
146: protected DRDAStatement getDefaultStatement() {
147: currentStatement = defaultStatement;
148: return defaultStatement;
149: }
150:
151: /**
152: * Get default statement for use in EXCIMM with specified pkgnamcsn
153: * The pkgnamcsn has the encoded isolation level
154: *
155: * @param pkgnamcsn package/ section # for statement
156: * @return DRDAStatement
157: */
158: protected DRDAStatement getDefaultStatement(Pkgnamcsn pkgnamcsn) {
159: currentStatement = defaultStatement;
160: currentStatement.setPkgnamcsn(pkgnamcsn);
161: return currentStatement;
162: }
163:
164: /**
165: * Get a new DRDA statement and store it in the stmtTable if stortStmt is
166: * true. If possible recycle an existing statement. When the server gets a
167: * new statement with a previously used pkgnamcsn, it means that
168: * client-side statement associated with this pkgnamcsn has been closed. In
169: * this case, server can re-use the DRDAStatement by doing the following:
170: * 1) Retrieve the old DRDAStatement associated with this pkgnamcsn and
171: * close it.
172: * 2) Reset the DRDAStatement state for re-use.
173: *
174: * @param pkgnamcsn Package name and section
175: * @return DRDAStatement
176: */
177: protected DRDAStatement newDRDAStatement(Pkgnamcsn pkgnamcsn)
178: throws SQLException {
179: DRDAStatement stmt = getDRDAStatement(pkgnamcsn);
180: if (stmt != null) {
181: stmt.close();
182: stmt.reset();
183: } else {
184: stmt = new DRDAStatement(this );
185: stmt.setPkgnamcsn(pkgnamcsn);
186: storeStatement(stmt);
187: }
188: return stmt;
189: }
190:
191: /**
192: * Get DRDA statement based on pkgnamcsn
193: *
194: * @param pkgnamcsn - key to access statement
195: * @return DRDAStatement
196: */
197: protected DRDAStatement getDRDAStatement(Pkgnamcsn pkgnamcsn) {
198: DRDAStatement newStmt = (DRDAStatement) stmtTable.get(pkgnamcsn
199: .getStatementKey());
200: if (newStmt != null) {
201: currentStatement = newStmt;
202: currentStatement.setCurrentDrdaResultSet(pkgnamcsn);
203: }
204: return newStmt;
205: }
206:
207: /**
208: * Make a new connection using the database name and set
209: * the connection in the database
210: * @param p Properties for connection attributes to pass to connect
211: */
212: void makeConnection(Properties p) throws SQLException {
213: p.put(Attribute.USERNAME_ATTR, userId);
214:
215: // take care of case of SECMEC_USRIDONL
216: if (password != null)
217: p.put(Attribute.PASSWORD_ATTR, password);
218:
219: // Contract between network server and embedded engine
220: // is that any connection returned implements EngineConnection.
221: EngineConnection conn = (EngineConnection) NetworkServerControlImpl
222: .getDriver().connect(
223: Attribute.PROTOCOL + shortDbName + attrString,
224: p);
225: if (conn != null) {
226: conn.setAutoCommit(false);
227: }
228: setConnection(conn);
229: }
230:
231: /**
232: * This makes a dummy connection to the database in order to
233: * boot and/or create this last one. If database cannot
234: * be found or authentication does not succeed, this will throw
235: * a SQLException which we catch and do nothing. We don't pass a
236: * userid and password here as we don't need to for the purpose
237: * of this method - main goal is to cause the database to be
238: * booted via a dummy connection.
239: */
240: void makeDummyConnection() {
241: try {
242: // Contract between network server and embedded engine
243: // is that any connection returned implements EngineConnection.
244: EngineConnection conn = (EngineConnection) NetworkServerControlImpl
245: .getDriver().connect(
246: Attribute.PROTOCOL + shortDbName
247: + attrString, new Properties());
248:
249: // If we succeeded in getting a connection, well just close it
250: if (conn != null) {
251: conn.close();
252: }
253: } catch (SQLException se) {
254: } // Simply do nothing
255: }
256:
257: // Create string to pass to DataSource.setConnectionAttributes
258: String appendAttrString(Properties p) {
259: if (p == null)
260: return null;
261:
262: Enumeration pKeys = p.propertyNames();
263: while (pKeys.hasMoreElements()) {
264: String key = (String) pKeys.nextElement();
265: attrString += ";" + key + "=" + p.getProperty(key);
266: }
267:
268: return attrString;
269: }
270:
271: /**
272: * Store DRDA prepared statement
273: * @param stmt DRDA prepared statement
274: */
275: protected void storeStatement(DRDAStatement stmt)
276: throws SQLException {
277: stmtTable.put(stmt.getPkgnamcsn().getStatementKey(), stmt);
278: }
279:
280: protected void removeStatement(DRDAStatement stmt)
281: throws SQLException {
282: stmtTable.remove(stmt.getPkgnamcsn().getStatementKey());
283: stmt.close();
284: }
285:
286: /**
287: * Make statement the current statement
288: * @param stmt
289: *
290: */
291:
292: protected void setCurrentStatement(DRDAStatement stmt) {
293: currentStatement = stmt;
294: }
295:
296: protected void commit() throws SQLException {
297:
298: if (conn != null)
299: conn.commit();
300: }
301:
302: protected void rollback() throws SQLException {
303:
304: if (conn != null)
305: conn.rollback();
306: }
307:
308: /**
309: * Close the connection and clean up the statement table
310: * @throws SQLException on conn.close() error to be handled in DRDAConnThread.
311: */
312: protected void close() throws SQLException {
313:
314: try {
315: if (stmtTable != null) {
316: for (Enumeration e = stmtTable.elements(); e
317: .hasMoreElements();) {
318: ((DRDAStatement) e.nextElement()).close();
319: }
320:
321: }
322: if (defaultStatement != null)
323: defaultStatement.close();
324: if ((conn != null) && !conn.isClosed()) {
325: if (!forXA) {
326: conn.rollback();
327: }
328: conn.close();
329: }
330: } finally {
331: conn = null;
332: currentStatement = null;
333: defaultStatement = null;
334: stmtTable = null;
335: }
336: }
337:
338: final void setDrdaID(String drdaID) {
339: if (conn != null)
340: conn.setDrdaID(drdaID);
341: }
342:
343: /**
344: * Set the internal isolation level to use for preparing statements.
345: * Subsequent prepares will use this isoalation level
346: * @param level internal isolation level
347: *
348: * @throws SQLException
349: * @see EngineConnection#setPrepareIsolation
350: *
351: */
352: final void setPrepareIsolation(int level) throws SQLException {
353: conn.setPrepareIsolation(level);
354: }
355:
356: final int getPrepareIsolation() throws SQLException {
357: return conn.getPrepareIsolation();
358: }
359:
360: protected String buildRuntimeInfo(String indent,
361: LocalizedResource localLangUtil) {
362:
363: String s = indent
364: + localLangUtil
365: .getTextMessage("DRDA_RuntimeInfoDatabase.I")
366: + dbName
367: + "\n"
368: + localLangUtil
369: .getTextMessage("DRDA_RuntimeInfoUser.I")
370: + userId
371: + "\n"
372: + localLangUtil
373: .getTextMessage("DRDA_RuntimeInfoNumStatements.I")
374: + stmtTable.size() + "\n";
375: s += localLangUtil
376: .getTextMessage("DRDA_RuntimeInfoPreparedStatementHeader.I");
377: for (Enumeration e = stmtTable.elements(); e.hasMoreElements();) {
378: s += ((DRDAStatement) e.nextElement()).toDebugString(indent
379: + "\t")
380: + "\n";
381: }
382: return s;
383: }
384:
385: /**
386: * This method resets the state of this Database object so that it can
387: * be re-used.
388: * Note: currently this method resets the variables related to security
389: * mechanisms that have been investigated as needing a reset.
390: * TODO: Investigate what all variables in this class need to be
391: * reset when this database object is re-used on a connection pooling or
392: * transaction pooling. see DRDAConnThread.parseACCSEC (CodePoint.RDBNAM)
393: * where database object is re-used on a connection reset.
394: */
395: public void reset() {
396: // Reset variables for connection re-use. Currently only takes care
397: // of reset the variables that affect EUSRIDPWD and USRSSBPWD
398: // security mechanisms. (DERBY-1080)
399: decryptedUserId = null;
400: decryptedPassword = null;
401: passwordSubstitute = null;
402: secTokenIn = null;
403: secTokenOut = null;
404: userId = null;
405: password = null;
406: securityMechanism = 0;
407: }
408: }
|