001package com.hfg.sql.jdbc; 002 003import com.hfg.security.LoginCredentials; 004import com.hfg.util.StringUtil; 005 006import java.sql.Connection; 007import java.sql.DriverManager; 008import java.sql.SQLException; 009import java.util.concurrent.*; 010 011//------------------------------------------------------------------------------ 012/** 013 * Represents a JDBC-compatible RDBMS database container. 014 * 015 * @author J. Alex Taylor, hairyfatguy.com 016 */ 017//------------------------------------------------------------------------------ 018// com.hfg XML/HTML Coding Library 019// 020// This library is free software; you can redistribute it and/or 021// modify it under the terms of the GNU Lesser General Public 022// License as published by the Free Software Foundation; either 023// version 2.1 of the License, or (at your option) any later version. 024// 025// This library is distributed in the hope that it will be useful, 026// but WITHOUT ANY WARRANTY; without even the implied warranty of 027// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 028// Lesser General Public License for more details. 029// 030// You should have received a copy of the GNU Lesser General Public 031// License along with this library; if not, write to the Free Software 032// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 033// 034// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 035// jataylor@hairyfatguy.com 036//------------------------------------------------------------------------------ 037 038public class JDBCServer 039{ 040 private String mName; 041 private String mHost; 042 private Integer mPort; 043 private RDBMS mRDBMS; 044 045 046 //########################################################################### 047 // CONSTRUCTORS 048 //########################################################################### 049 050 //--------------------------------------------------------------------------- 051 public JDBCServer(RDBMS inRDBMS, String inHostname) 052 { 053 mRDBMS = inRDBMS; 054 mHost = inHostname; 055 setPort(mRDBMS.getDefaultPort()); 056 } 057 058 059 //########################################################################### 060 // PUBLIC METHODS 061 //########################################################################### 062 063 //--------------------------------------------------------------------------- 064 public JDBCServer setName(String inValue) 065 { 066 mName = inValue; 067 return this; 068 } 069 070 //--------------------------------------------------------------------------- 071 public String name() 072 { 073 return mName; 074 } 075 076 //--------------------------------------------------------------------------- 077 /** 078 Returns the relational database management system associated with the database server. 079 */ 080 public RDBMS getRDBMS() 081 { 082 return mRDBMS; 083 } 084 085 //--------------------------------------------------------------------------- 086 /** 087 Specifies the host machine where the database server is located. 088 */ 089 public JDBCServer setHost(String inValue) 090 { 091 mHost = inValue; 092 return this; 093 } 094 095 //--------------------------------------------------------------------------- 096 /** 097 Returns the host machine where the database server is located. 098 */ 099 public String getHost() 100 { 101 return mHost; 102 } 103 104 //--------------------------------------------------------------------------- 105 /** 106 Specifies the port to use to connect to the database via JDBC. 107 */ 108 public JDBCServer setPort(int inValue) 109 { 110 mPort = inValue; 111 return this; 112 } 113 114 //--------------------------------------------------------------------------- 115 /** 116 Returns the port to use to connect to the database via JDBC. 117 */ 118 public Integer getPort() 119 { 120 return mPort; 121 } 122 123 //--------------------------------------------------------------------------- 124 public Connection getConnection(String inDatabaseName, LoginCredentials inCredentials, JDBCConnectionSettings inSettings) 125 { 126 Connection conn; 127 128 Integer timeout = (inSettings != null ? inSettings.getConnectTimeoutInSeconds() : null); 129 130 ExecutorService service = Executors.newSingleThreadExecutor(); 131 Future<Connection> result = service.submit(new ConnectionBroker(inDatabaseName, inCredentials, inSettings)); 132 133 try 134 { 135 if (timeout != null) 136 { 137 conn = result.get(timeout, TimeUnit.SECONDS); 138 } 139 else 140 { 141 conn = result.get(); 142 } 143 144 } 145 catch (TimeoutException e) 146 { 147 result.cancel(true); 148 throw new JDBCException("Database connection attempt to " + (StringUtil.isSet(name()) ? name() : inDatabaseName) 149 + " timed out after " + timeout + " sec!", e); 150 } 151 catch (Exception e) 152 { 153 result.cancel(true); 154 throw new JDBCException("Problem while establishing a connection to " 155 + (StringUtil.isSet(name()) ? name() : inDatabaseName) + "!", e); 156 } 157 finally 158 { 159 service.shutdown(); 160 } 161 162 return conn; 163 } 164 165 //--------------------------------------------------------------------------- 166 public JDBCConnectionPool establishConnectionPool(String inDatabaseName, LoginCredentials inCredentials, JDBCConnectionPoolSettings inPoolSettings) 167 { 168 return getRDBMS().establishConnectionPool(this, inDatabaseName, inCredentials, inPoolSettings); 169 } 170 171 //--------------------------------------------------------------------------- 172 public String getConnectString(String inDatabaseName) 173 { 174 return getRDBMS().getConnectString(this, inDatabaseName); 175 } 176 177 //--------------------------------------------------------------------------- 178 public String getConnectString(String inDatabaseName, JDBCConnectionSettings inSettings) 179 { 180 return getRDBMS().getConnectString(this, inDatabaseName, inSettings); 181 } 182 183 //########################################################################### 184 // INNER CLASS 185 //########################################################################### 186 187 private class ConnectionBroker implements Callable<Connection> 188 { 189 String mDatabaseName; 190 LoginCredentials mCredentials; 191 JDBCConnectionSettings mSettings; 192 193 //------------------------------------------------------------------------ 194 public ConnectionBroker(String inDatabaseName, LoginCredentials inCredentials, JDBCConnectionSettings inSettings) 195 { 196 mDatabaseName = inDatabaseName; 197 mCredentials = inCredentials; 198 mSettings = inSettings; 199 } 200 201 //------------------------------------------------------------------------ 202 public Connection call() 203 throws SQLException 204 { 205 if (mSettings != null 206 && mSettings.getConnectTimeoutInSeconds() != null 207 && mSettings.getConnectTimeoutInSeconds() > 0) 208 { 209 DriverManager.setLoginTimeout(mSettings.getConnectTimeoutInSeconds()); 210 } 211 212 Connection conn; 213 try 214 { 215 conn = initConn(); 216 } 217 catch (SQLException e) 218 { 219 if (e.getMessage().contains("No suitable driver found")) 220 { 221 // JDBC 4 is *supposed* to not need a Class.forName() driver initialization. However, when a different 222 // classloader is used as is the case for a Tomcat-deployed webapp, it still needs to be done. 223 try 224 { 225 Class.forName(getRDBMS().getDriverClassName()); 226 conn = initConn(); 227 } 228 catch (Exception e2) 229 { 230 throw e; 231 } 232 } 233 else 234 { 235 throw e; 236 } 237 } 238 239 return conn; 240 } 241 242 //------------------------------------------------------------------------ 243 private Connection initConn() 244 throws SQLException 245 { 246 return DriverManager.getConnection(getConnectString(mDatabaseName, mSettings), 247 mCredentials.getUser(), 248 mCredentials.getPasswordString()); 249 } 250 } 251 252}