001 package hep.aida.ref.sql; 002 003 // Aida 004 import hep.aida.ref.AidaUtils; 005 006 // Java 007 import java.util.Map; 008 import java.util.HashMap; 009 010 // SQL 011 import java.sql.DriverManager; 012 import java.sql.Connection; 013 import java.sql.SQLException; 014 015 // Log4J 016 import org.apache.log4j.Logger; 017 018 /** <code>Accessor</code> provides access to RDBMS. 019 * Following properties are required: 020 * <ul> 021 * <li><code>hep.aida.ref.sql.db</code></li> 022 * <li><code>hep.aida.ref.sql.schema</code>, optional</li> 023 * <li><code>hep.aida.ref.sql.name</code>, optional</li> 024 * <li><code>hep.aida.ref.sql.src</code>, 025 * default = <code>hep/aida/ref/sql/StmtSrc.properties</code></li> 026 * <li><code>hep.aida.ref.sql.user</code></li> 027 * <li><code>hep.aida.ref.sql.passwd</code></li> 028 * </ul> 029 * Static Accessor functions form an Accesor Factory. It tries to 030 * reuse already created Accessors if they share the same characteristics 031 * (options). 032 * <p><font color="#880088"> 033 * <pre> 034 * $Log: Accessor.java,v $ 035 * Revision 1.54 2007/09/26 07:58:51 hrivnac 036 * cache savable fromjas menu 037 * 038 * Revision 1.53 2007/05/23 16:38:43 hrivnac 039 * logical connections for Plotter; better UML 040 * 041 * Revision 1.52 2007/05/22 23:33:20 hrivnac 042 * Plotter uses real arguments parsing 043 * 044 * Revision 1.51 2006/12/15 00:16:13 hrivnac 045 * FORWARD_ONLY allows to plot big ntuples 046 * 047 * Revision 1.50 2006/12/12 15:21:42 hrivnac 048 * McKoi replaced with Derby; uppercase-only dbs handled better; moving to JAS 0.8.3 049 * 050 * Revision 1.49 2006/11/20 13:51:59 hrivnac 051 * Connection is closed except within JAS3 052 * 053 * Revision 1.48 2006/11/15 16:16:01 hrivnac 054 * Oracle work fine in CERN, cache enabled 055 * 056 * Revision 1.47 2004/12/07 01:04:56 hrivnac 057 * name pattern can be specified 058 * 059 * Revision 1.46 2004/12/01 16:07:15 hrivnac 060 * better support for ColMan JAS3 plugin 061 * 062 * Revision 1.45 2004/11/24 14:32:32 hrivnac 063 * better support for schemas 064 * 065 * Revision 1.44 2004/11/22 18:09:52 hrivnac 066 * Oracle treatment 067 * 068 * Revision 1.43 2004/10/29 22:27:25 hrivnac 069 * imports corrected 070 * 071 * Revision 1.42 2004/10/27 14:19:03 hrivnac 072 * small bug fixes 073 * 074 * Revision 1.41 2004/10/22 15:32:59 hrivnac 075 * cleaned 076 * 077 * Revision 1.40 2004/10/22 14:22:44 hrivnac 078 * properties loading refactored 079 * 080 * Revision 1.39 2004/10/20 23:02:29 hrivnac 081 * Types mapping simplified 082 * 083 * Revision 1.38 2004/10/12 13:25:40 hrivnac 084 * small improvements 085 * 086 * Revision 1.37 2004/10/08 15:22:33 hrivnac 087 * JAS3 plugin works 088 * 089 * Revision 1.36 2004/05/22 15:12:59 hrivnac 090 * class id reformated 091 * 092 * Revision 1.35 2004/04/16 15:32:57 hrivnac 093 * Type.properties to setup Type.class 094 * 095 * Revision 1.34 2004/04/14 13:54:41 hrivnac 096 * potential bugs fixed 097 * 098 * Revision 1.33 2004/04/14 13:39:46 hrivnac 099 * 1.5 warnings fixed 100 * 101 * Revision 1.32 2004/04/13 15:45:54 hrivnac 102 * AIDA URL introduced 103 * 104 * Revision 1.31 2004/03/10 13:55:57 hrivnac 105 * *** empty log message *** 106 * 107 * Revision 1.30 2004/03/10 13:55:15 hrivnac 108 * *** empty log message *** 109 * 110 * Revision 1.29 2004/02/12 16:39:40 hrivnac 111 * niceing 112 * 113 * Revision 1.28 2004/02/10 14:50:58 hrivnac 114 * JavaDoc tags completed 115 * 116 * Revision 1.27 2004/02/04 13:30:39 hrivnac 117 * - improvement of Enums internal mapping 118 * - general cleaning 119 * 120 * Revision 1.26 2004/01/30 18:10:30 hrivnac 121 * - StmtSrc.properties specified a Property 122 * - customised FreeHEP AIDA services included in jar file 123 * 124 * Revision 1.25 2003/11/20 17:21:57 hrivnac 125 * Java 1.5 natively supported, Log4J reporting improved. 126 * 127 * Revision 1.24 2003/11/17 10:18:28 hrivnac 128 * Cleaning. 129 * 130 * Revision 1.23 2003/11/05 19:46:22 hrivnac 131 * - FreeHEP 1.2.1 132 * - JAIDA 3.2.1 133 * 134 * Revision 1.10 2003/10/10 13:21:36 hrivnac 135 * Works between different DB technologies. 136 * 137 * Revision 1.9 2003/10/10 10:12:21 hrivnac 138 * All three testing pgms work. 139 * 140 * Revision 1.5 2003/10/03 16:02:21 hrivnac 141 * Better handling of multiple technologies. 142 * 143 * Revision 1.3 2003/10/02 14:24:00 hrivnac 144 * Cleaning. 145 * 146 * Revision 1.2 2003/10/02 10:00:40 hrivnac 147 * Cleaning. 148 * 149 * </pre> 150 * </font></p> 151 * @opt attributes 152 * @opt operations 153 * @opt types 154 * @opt visibility 155 * @version $Id: Accessor.java,v 1.54 2007/09/26 07:58:51 hrivnac Exp $ 156 * @author <a href="mailto:Julius.Hrivnac@cern.ch">J.Hrivnac</a> */ 157 public class Accessor { 158 159 /** Get Accesor corresponding to optionsMap if it already exists. 160 * Creates new Accessor otherwise. 161 * @param optionsMap The map of options to define the Accessor. 162 * @return The Accessor associated to specified optionsMap. */ 163 public static Accessor getAccessor(Map optionsMap) throws SQLTupleException { 164 if (_accessors.containsKey(optionsMap)) { 165 log.debug("Accessor reused: " + optionsMap); 166 return _accessors.get(optionsMap); 167 } 168 else { 169 log.debug("Accessor created: " + optionsMap); 170 Accessor accessor = new Accessor(optionsMap); 171 _accessors.put(optionsMap, accessor); 172 return accessor; 173 } 174 } 175 176 /** Get Accesor corresponding to optionsString if it already exists. 177 * Creates new Accessor otherwise. 178 * @param optionsString The string of name=option pairs to define the Accessor (separated by ;). 179 * @return The Accessor associated to specified optionsString. 180 * @throws SQLTupleException if the Accessor can't be created. */ 181 public static Accessor getAccessor(String optionsString) throws SQLTupleException { 182 return getAccessor(AidaUtils.parseOptions(optionsString)); 183 } 184 185 /** Creates using informations from {@link java.util.Map}. 186 * @param optionsMap The map of options to define the Accessor. 187 * @throws SQLTupleException if the Accessor can't be created */ 188 private Accessor(Map optionsMap) throws SQLTupleException { 189 _db = (String)optionsMap.get("hep.aida.ref.sql.db"); 190 _src = (String)optionsMap.get("hep.aida.ref.sql.src"); 191 _schema = (String)optionsMap.get("hep.aida.ref.sql.schema"); 192 _user = (String)optionsMap.get("hep.aida.ref.sql.user"); 193 _passwd = (String)optionsMap.get("hep.aida.ref.sql.passwd"); 194 _protocol = (new AIDAURL(_db)).protocol(); 195 String forwardOnly = (String)optionsMap.get("hep.aida.ref.sql.forwardonly"); 196 _forwardOnly = "true".equals(forwardOnly) || supportsOnlyForwardOnly(); 197 if (_src == null) { 198 _src = "hep/aida/ref/sql/StmtSrc.properties"; 199 } 200 if (supportsOnlyUpperCase()) { 201 _user = _user.toUpperCase(); 202 if (_schema != null) { 203 _schema = _schema.toUpperCase(); 204 } 205 } 206 connection(); 207 } 208 209 /** Create using informations from properties String. 210 * @param optionsString The string of name=option pairs to define the Accessor (separated by ;). 211 * @throws SQLTupleException if the Accessor can't be created. */ 212 private Accessor(String optionsString) throws SQLTupleException { 213 this(AidaUtils.parseOptions(optionsString)); 214 } 215 216 /** Gives {@link Connection}, creates one if it doesn't exist yet. 217 * @return The Connection defined by this Accessor. 218 * @throws SQLTupleException if the Connection can't be obtained. */ 219 public Connection connection() throws SQLTupleException { 220 // Return Connection if it is already established 221 if (_connection != null) { 222 return _connection; 223 } 224 // Check for suitable driver 225 try { 226 Class.forName(driver()); 227 } 228 catch (Exception e) { 229 throw new SQLTupleException("Unable to load SQL driver " + driver() + " for URL " + _db, e); 230 } 231 // Get Connection 232 try { 233 if (_connection == null) { 234 log.debug("Getting connection to database " + _db + creationCommand() + " as " + _user + "(" + _passwd + ")"); 235 _connection = DriverManager.getConnection(_db + creationCommand(), _user, _passwd); 236 _connection.setAutoCommit(false); 237 _types = new Type(this); 238 } 239 } 240 catch (SQLException e) { 241 throw new SQLTupleException("Unable to connect to SQL database " + _db + creationCommand() + " as " + _user, e); 242 } 243 return _connection; 244 } 245 246 /** Give native SQL type name corresponding to Java Class. 247 * @param aidaType The Java Class representing AIDA type. 248 * @return The corresponding native SQL type name. */ 249 public String sqlName(Class aidaType) { 250 return _types.sqlType(aidaType); 251 } 252 253 /** Commit. 254 * @throws SQLTupleException if the {@link Connection} can't be commited. */ 255 public void commit() throws SQLTupleException { 256 log.debug("Commiting to database " + _db + " as " + _user + "(" + _passwd + ")"); 257 try { 258 connection().commit(); 259 } 260 catch (SQLException e) { 261 throw new SQLTupleException("Can't commit connection to database " + _db, e); 262 } 263 } 264 265 /** Roll back. 266 * @throws SQLTupleException if the {@link Connection} can't be rolledback. */ 267 public void rollback() throws SQLTupleException { 268 log.debug("Rolling back to database " + _db + " as " + _user + "(" + _passwd + ")"); 269 try { 270 connection().rollback(); 271 } 272 catch (SQLException e) { 273 throw new SQLTupleException("Can't rollback connection to database " + _db, e); 274 } 275 } 276 277 /** Close connection. 278 * @throws SQLTupleException if the {@link Connection} can't be closed. */ 279 public void close() throws SQLTupleException { 280 log.debug("Closing connection to database " + _db + " as " + _user + "(" + _passwd + ")"); 281 if (_connection != null) { 282 try { 283 _connection.close(); 284 } 285 catch (SQLException e) { 286 throw new SQLTupleException("Can't close connection to database " + _db, e); 287 } 288 finally { 289 _connection = null; 290 } 291 } 292 else { 293 log.debug("Can't close because Connection is unknown"); 294 } 295 } 296 297 /** Check, if both {@link Accessor}s use the same 298 * <code>driver</code>, <code>database</code>, <code>schema</code> (if set), 299 * <code>user</code> and <code>src</code> */ 300 public boolean equals(Object other) { 301 if (!(other instanceof Accessor)) { 302 return false; 303 } 304 Accessor a = (Accessor)other; 305 return toString().equals(a.toString()); 306 } 307 308 /** Give used driver. 309 * @return The name of the associated SQL driver Class. */ 310 public String driver() { 311 return Implementation.driver(_protocol); 312 } 313 /** Give the database creation command, if needed. 314 * @return The database creation command, if needed. */ 315 public String creationCommand() { 316 return Implementation.creationCommand(_protocol); 317 } 318 319 /** Give used protocol. 320 * @return The name of the associated JDBC protocol. */ 321 public String protocol() { 322 return _protocol; 323 } 324 325 /** Tell if database supports replication. 326 * @return Whether database supports replication. */ 327 public boolean supportsReplication() { 328 return Implementation.supportsReplication(_protocol); 329 } 330 331 /** Tell if database supports only TYPE_FORWARD_ONLY cursor. 332 * @return Whether database supports only YPE_FORWARD_ONLY cursor. */ 333 public boolean supportsOnlyForwardOnly() { 334 return Implementation.supportsOnlyForwardOnly(_protocol); 335 } 336 337 /** Tell if database supports only uppercase names.. 338 * @return Whether database supports only uppercase names. */ 339 public boolean supportsOnlyUpperCase() { 340 return Implementation.supportsOnlyUpperCase(_protocol); 341 } 342 343 /** Give connected database name. 344 * @return The name of the associated database. */ 345 public String db() { 346 return _db; 347 } 348 349 /** Give connected database schema. 350 * @return The schema of the associated database. */ 351 public String schema() { 352 return _schema; 353 } 354 355 /** Set connected <code>StmtSrc.properties</code>. 356 * @param src The StmtSrc to be used, <code>null</code> 357 * means the default StmtSrc. */ 358 public void setSrc(String src) { 359 if (src == null) { 360 _src = "hep/aida/ref/sql/StmtSrc.properties"; 361 } 362 else { 363 _src = src; 364 } 365 } 366 367 /** Give connected <code>StmtSrc.properties</code>. 368 * @return The used StmtSrc. */ 369 public String src() { 370 return _src; 371 } 372 373 /** Give connected user. 374 * @return The used username. */ 375 public String user() { 376 return _user; 377 } 378 379 /** Give connected user' password. 380 * @return The used password. */ 381 public String passwd() { 382 return _passwd; 383 } 384 385 /** Tell if {@link java.sql.ResultSet} should be <em>FOWARD_ONLY</em>. 386 * @return Whether {@link java.sql.ResultSet} should be <em>FOWARD_ONLY</em>. */ 387 public boolean forwardOnly() { 388 return _forwardOnly; 389 } 390 391 /** TBD */ 392 public static void setWithinJAS() { 393 _withinJAS = true; 394 } 395 396 /** TBD */ 397 public static boolean isWinhinJAS() { 398 return _withinJAS; 399 } 400 401 public String toString() { 402 return (_schema == null) ? "Accessor(" + _db + "[" + _user + "] using " + driver() + "[" + _src + "])" 403 : "Accessor(" + _db + "/" + _schema + "[" + _user + "] using " + driver() + "[" + _src + "])"; 404 } 405 406 private static boolean _withinJAS = false; 407 408 private static Map<Map, Accessor> _accessors = new HashMap<Map, Accessor>(); 409 410 private String _protocol; 411 412 private Connection _connection; 413 414 private Type _types; 415 416 private String _db; 417 418 private String _schema; 419 420 private String _src; 421 422 private String _user; 423 424 private String _passwd; 425 426 private boolean _forwardOnly; 427 428 /** Logging . */ 429 private static Logger log = Logger.getLogger(Accessor.class); 430 431 }