1 10 package org.mmbase.storage.implementation.database; 11 12 import java.sql.*; 13 import java.util.StringTokenizer ; 14 15 import javax.naming.*; 16 import javax.sql.DataSource ; 17 import javax.servlet.ServletContext ; 18 import java.io.*; 19 20 import org.mmbase.module.core.MMBaseContext; 21 import org.mmbase.storage.*; 22 import org.mmbase.storage.search.implementation.database.*; 23 import org.mmbase.storage.search.SearchQueryHandler; 24 import org.mmbase.storage.util.StorageReader; 25 import org.mmbase.util.logging.*; 26 import org.mmbase.util.ResourceLoader; 27 import org.xml.sax.InputSource ; 28 29 44 public class DatabaseStorageManagerFactory extends StorageManagerFactory { 45 46 private static final Logger log = Logging.getLoggerInstance(DatabaseStorageManagerFactory.class); 47 48 private final static String [] STANDARD_SQL_KEYWORDS = 50 {"absolute","action","add","all","allocate","alter","and","any","are","as","asc","assertion","at","authorization","avg","begin","between","bit","bit_length", 51 "both","by","cascade","cascaded","case","cast","catalog","char","character","char_length","character_length","check","close","coalesce","collate","collation", 52 "column","commit","connect","connection","constraint","constraints","continue","convert","corresponding","count","create","cross","current","current_date", 53 "current_time","current_timestamp","current_user","cursor","date","day","deallocate","dec","decimal","declare","default","deferrable","deferred","delete", 54 "desc","describe","descriptor","diagnostics","disconnect","distinct","domain","double","drop","else","end","end-exec","escape","except","exception","exec", 55 "execute","exists","external","extract","false","fetch","first","float","for","foreign","found","from","full","get","global","go","goto","grant","group","having","hour", 56 "identity","immediate","in","indicator","initially","inner","input","insensitive","insert","int","integer","intersect","interval","into","is","isolation","join", 57 "key","language","last","leading","left","level","like","local","lower","match","max","min","minute","module","month","names","national","natural","nchar","next","no", 58 "not","null","nullif","numeric","octet_length","of","on","only","open","option","or","order","outer","output","overlaps","pad","partial","position","precision", 59 "prepare","preserve","primary","prior","privileges","procedure","public","read","real","references","relative","restrict","revoke","right","rollback","rows", 60 "schema","scroll","second","section","select","session","session_user","set","size","smallint","some","space","sql","sqlcode","sqlerror","sqlstate","substring", 61 "sum","system_user","table","temporary","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","transaction","translate","translation", 62 "trim","true","union","unique","unknown","update","upper","usage","user","using","value","values","varchar","varying","view","when","whenever","where","with","work", 63 "write","year","zone"}; 64 65 66 private final static Class DEFAULT_QUERY_HANDLER_CLASS = org.mmbase.storage.search.implementation.database.BasicSqlHandler.class; 68 69 private final static Class DEFAULT_STORAGE_MANAGER_CLASS = org.mmbase.storage.implementation.database.RelationalDatabaseStorageManager.class; 71 72 75 protected String catalog = null; 76 private String databaseName = null; 77 81 protected DataSource dataSource; 82 83 88 protected int transactionIsolation = Connection.TRANSACTION_NONE; 89 90 93 protected boolean supportsTransactions = false; 94 95 96 private static final String BASE_PATH_UNSET = "UNSET"; 97 100 private String basePath = BASE_PATH_UNSET; 101 102 public double getVersion() { 103 return 0.1; 104 } 105 106 public boolean supportsTransactions() { 107 return supportsTransactions; 108 } 109 110 public String getCatalog() { 111 return catalog; 112 } 113 114 private static final java.util.regex.Pattern JDBC_URL_DB = java.util.regex.Pattern.compile("(?i)jdbc:.*;.*DatabaseName=([^;]+?)"); 116 117 private static final java.util.regex.Pattern JDBC_URL = java.util.regex.Pattern.compile("(?i)jdbc:.*:(?:.*[/@])?(.*?)(?:[;\\?].*)?"); 119 120 private static String getDatabaseName(String url) { 121 if (url == null) return null; 122 java.util.regex.Matcher matcher = JDBC_URL_DB.matcher(url); 123 if (matcher.matches()) { 124 return matcher.group(1); 125 } 126 matcher = JDBC_URL.matcher(url); 127 if (matcher.matches()) { 128 return matcher.group(1); 129 } 130 return null; 131 } 132 133 137 public String getDatabaseName() { 138 return databaseName; 139 } 140 141 145 public DataSource getDataSource() { 146 return dataSource; 147 } 148 149 153 protected DataSource createDataSource(String binaryFileBasePath) { 154 DataSource ds = null; 155 String dataSourceURI = mmbase.getInitParameter("datasource"); 159 if (dataSourceURI != null) { 160 try { 161 String contextName = mmbase.getInitParameter("datasource-context"); 162 if (contextName == null) { 163 contextName = "java:comp/env"; 164 } 165 log.service("Using configured datasource " + dataSourceURI); 166 Context initialContext = new InitialContext(); 167 Context environmentContext = (Context ) initialContext.lookup(contextName); 168 ds = (DataSource )environmentContext.lookup(dataSourceURI); 169 } catch(NamingException ne) { 170 log.warn("Datasource '" + dataSourceURI + "' not available. (" + ne.getMessage() + "). Attempt to use JDBC Module to access database."); 171 } 172 } 173 if (ds == null) { 174 log.service("No data-source configured, using Generic data source"); 175 if (binaryFileBasePath == null) { 178 ds = new GenericDataSource(mmbase); } else { 180 ds = new GenericDataSource(mmbase, binaryFileBasePath); 181 } 182 } 183 return ds; 185 186 } 187 188 193 protected synchronized void load() throws StorageException { 194 storageManagerClass = DEFAULT_STORAGE_MANAGER_CLASS; 196 197 queryHandlerClasses.add(DEFAULT_QUERY_HANDLER_CLASS); 199 200 201 202 dataSource = createDataSource(null); 203 205 String sqlKeywords; 206 207 { 211 Connection con = null; 212 try { 213 con = dataSource.getConnection(); 214 if (con == null) throw new StorageException("Did get 'null' connection from data source " + dataSource); 215 catalog = con.getCatalog(); 216 log.service("Connecting to catalog with name " + catalog); 217 218 DatabaseMetaData metaData = con.getMetaData(); 219 String url = metaData.getURL(); 220 String db = getDatabaseName(url); 221 if (db != null) { 222 databaseName = db; 223 } else { 224 log.service("No db found in database connection meta data URL '" + url + "'"); 225 databaseName = catalog; 226 } 227 log.service("Connecting to database with name " + getDatabaseName()); 228 229 supportsTransactions = metaData.supportsTransactions() && metaData.supportsMultipleTransactions(); 231 232 if (metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE)) { 234 transactionIsolation = Connection.TRANSACTION_SERIALIZABLE; 235 } else if (metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)) { 236 transactionIsolation = Connection.TRANSACTION_REPEATABLE_READ; 237 } else if (metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)) { 238 transactionIsolation = Connection.TRANSACTION_READ_COMMITTED; 239 } else if (metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)) { 240 transactionIsolation = Connection.TRANSACTION_READ_UNCOMMITTED; 241 } else { 242 supportsTransactions = false; 243 } 244 sqlKeywords = ("" + metaData.getSQLKeywords()).toLowerCase(); 245 246 } catch (SQLException se) { 247 throw new StorageInaccessibleException(se); 249 } finally { 250 if (con != null) { 251 try { 252 con.close(); 253 } catch (SQLException se) { 254 log.error(se); 255 } 256 } 257 } 258 } 259 260 261 263 setOption(Attributes.SUPPORTS_TRANSACTIONS, supportsTransactions); 264 setAttribute(Attributes.TRANSACTION_ISOLATION_LEVEL, new Integer (transactionIsolation)); 265 setOption(Attributes.SUPPORTS_COMPOSITE_INDEX, true); 266 setOption(Attributes.SUPPORTS_DATA_DEFINITION, true); 267 268 for (int i = 0; i < STANDARD_SQL_KEYWORDS.length; i++) { 271 disallowedFields.put(STANDARD_SQL_KEYWORDS[i], null); } 273 274 StringTokenizer tokens = new StringTokenizer (sqlKeywords,", "); 277 while (tokens.hasMoreTokens()) { 278 String tok = tokens.nextToken(); 279 disallowedFields.put(tok, null); 280 } 281 282 283 super.load(); 285 log.service("Now creating the real data source"); 286 dataSource = createDataSource(getBinaryFileBasePath(false)); 287 288 setAttribute(Attributes.DATA_SOURCE, dataSource); 291 292 supportsTransactions = hasOption(Attributes.SUPPORTS_TRANSACTIONS); 294 } 295 296 305 public StorageReader getDocumentReader() throws StorageException { 306 StorageReader reader = super.getDocumentReader(); 307 if (reader == null) { 309 String databaseResourcePath; 310 String databaseName = mmbase.getInitParameter("database"); 312 if (databaseName != null) { 313 if (databaseName.endsWith(".xml")) { 315 databaseResourcePath = databaseName; 316 } else { 317 databaseResourcePath = "storage/databases/" + databaseName + ".xml"; 318 } 319 } else { 320 322 DatabaseStorageLookup lookup = new DatabaseStorageLookup(); 324 Connection con = null; 325 try { 326 con = dataSource.getConnection(); 327 DatabaseMetaData metaData = con.getMetaData(); 328 databaseResourcePath = lookup.getResourcePath(metaData); 329 if(databaseResourcePath == null) { 330 throw new StorageConfigurationException("No filter found in " + lookup.getSystemId() + " for driver class:" + metaData.getConnection().getClass().getName() + "\n"); 332 } 333 } catch (SQLException sqle) { 334 throw new StorageInaccessibleException(sqle); 335 } finally { 336 if (con != null) { 338 try { con.close(); } catch (SQLException sqle) {} 339 } 340 } 341 } 342 log.service("Configuration used for database storage: " + databaseResourcePath); 344 try { 345 InputSource in = ResourceLoader.getConfigurationRoot().getInputSource(databaseResourcePath); 346 reader = new StorageReader(this, in); 347 } catch (java.io.IOException ioe) { 348 throw new StorageConfigurationException(ioe); 349 } 350 351 } 352 return reader; 353 } 354 355 358 public String getBinaryFileBasePath() { 359 return getBinaryFileBasePath(true); 360 } 361 362 367 public String getBinaryFileBasePath(boolean check) { 368 if (basePath == BASE_PATH_UNSET) { 369 basePath = (String ) getAttribute(Attributes.BINARY_FILE_PATH); 370 if (basePath == null || basePath.equals("")) { 371 ServletContext sc = MMBaseContext.getServletContext(); 372 basePath = sc != null ? sc.getRealPath("/WEB-INF/data") : null; 373 if (basePath == null) { 374 basePath = System.getProperty("user.dir") + File.separator + "data"; 375 } 376 377 } else { 378 java.io.File baseFile = new java.io.File (basePath); 379 if (! baseFile.isAbsolute()) { 380 ServletContext sc = MMBaseContext.getServletContext(); 381 String absolute = sc != null ? sc.getRealPath("/") + File.separator : null; 382 if (absolute == null) absolute = System.getProperty("user.dir") + File.separator; 383 basePath = absolute + basePath; 384 } 385 } 386 if (basePath == null) { 387 log.warn("Cannot determin a a Binary File Base Path"); 388 return null; 389 } 390 File baseDir = new File(basePath); 391 try { 392 basePath = baseDir.getCanonicalPath(); 393 if (check) checkBinaryFileBasePath(basePath); 394 } catch (IOException ioe) { 395 log.error(ioe); 396 } 397 if (! basePath.endsWith(File.separator)) { 398 basePath += File.separator; 399 } 400 } 401 return basePath; 402 } 403 404 409 public static boolean checkBinaryFileBasePath(String basePath) { 410 File baseDir = new File(basePath); 411 if (! baseDir.mkdirs() && ! baseDir.exists()) { 412 log.error("Cannot create the binary file path " + basePath); 413 } 414 if (! baseDir.canWrite()) { 415 log.error("Cannot write in the binary file path " + basePath); 416 return false; 417 } else { 418 return true; 419 } 420 } 421 422 protected Object instantiateBasicHandler(Class handlerClass) { 423 try { 425 java.lang.reflect.Constructor constructor = handlerClass.getConstructor(new Class [] {}); 426 SqlHandler sqlHandler = (SqlHandler) constructor.newInstance( new Object [] {} ); 427 log.service("Instantiated SqlHandler of type " + handlerClass.getName()); 428 return sqlHandler; 429 } catch (NoSuchMethodException nsme) { 430 throw new StorageConfigurationException(nsme); 431 } catch (java.lang.reflect.InvocationTargetException ite) { 432 throw new StorageConfigurationException(ite); 433 } catch (IllegalAccessException iae) { 434 throw new StorageConfigurationException(iae); 435 } catch (InstantiationException ie) { 436 throw new StorageConfigurationException(ie); 437 } 438 } 439 440 protected Object instantiateChainedHandler(Class handlerClass, Object handler) { 441 try { 443 java.lang.reflect.Constructor constructor = handlerClass.getConstructor(new Class [] {SqlHandler.class}); 444 ChainedSqlHandler sqlHandler = (ChainedSqlHandler) constructor.newInstance(new Object [] { handler }); 445 log.service("Instantiated chained SQLHandler of type " + handlerClass.getName()); 446 return sqlHandler; 447 } catch (NoSuchMethodException nsme) { 448 throw new StorageConfigurationException(nsme); 449 } catch (java.lang.reflect.InvocationTargetException ite) { 450 throw new StorageConfigurationException(ite); 451 } catch (IllegalAccessException iae) { 452 throw new StorageConfigurationException(iae); 453 } catch (InstantiationException ie) { 454 throw new StorageConfigurationException(ie); 455 } 456 } 457 458 protected SearchQueryHandler instantiateQueryHandler(Object data) { 459 return new BasicQueryHandler((SqlHandler)data); 460 } 461 462 463 public static void main(String [] args) { 464 String u = "jdbc:hsql:test;test=b"; 465 if (args.length > 0) u = args[0]; 466 System.out.println("Database " + getDatabaseName(u)); 467 } 468 } 469 470 471 | Popular Tags |