001 package railo.commons.io.res.type.datasource; 002 003 import java.io.IOException; 004 import java.io.InputStream; 005 import java.io.OutputStream; 006 import java.io.PipedInputStream; 007 import java.io.PipedOutputStream; 008 import java.sql.Connection; 009 import java.sql.SQLException; 010 import java.util.Iterator; 011 import java.util.List; 012 import java.util.Map; 013 import java.util.WeakHashMap; 014 015 import org.apache.commons.collections.map.ReferenceMap; 016 017 import railo.commons.io.res.Resource; 018 import railo.commons.io.res.ResourceProvider; 019 import railo.commons.io.res.Resources; 020 import railo.commons.io.res.type.datasource.core.Core; 021 import railo.commons.io.res.type.datasource.core.MSSQL; 022 import railo.commons.io.res.type.datasource.core.MySQL; 023 import railo.commons.io.res.util.ResourceLockImpl; 024 import railo.commons.io.res.util.ResourceUtil; 025 import railo.commons.lang.SizeOf; 026 import railo.commons.lang.StringUtil; 027 import railo.runtime.config.Config; 028 import railo.runtime.config.ConfigImpl; 029 import railo.runtime.db.DatasourceConnection; 030 import railo.runtime.db.DatasourceManagerImpl; 031 import railo.runtime.engine.ThreadLocalPageContext; 032 import railo.runtime.exp.ApplicationException; 033 import railo.runtime.exp.DatabaseException; 034 import railo.runtime.exp.PageException; 035 import railo.runtime.exp.PageRuntimeException; 036 import railo.runtime.op.Caster; 037 import railo.runtime.type.Sizeable; 038 039 040 /** 041 * Resource Provider for ram resource 042 */ 043 public final class DatasourceResourceProvider implements ResourceProvider,Sizeable { 044 045 public static final int DBTYPE_ANSI92=0; 046 public static final int DBTYPE_MSSQL=1; 047 public static final int DBTYPE_MYSQL=2; 048 049 private static final int MAXAGE = 5000; 050 051 //private static final int CONNECTION_ID = 0; 052 053 private String scheme="ds"; 054 055 boolean caseSensitive=true; 056 //private Resources resources; 057 private long lockTimeout=1000; 058 private ResourceLockImpl lock=new ResourceLockImpl(lockTimeout,caseSensitive); 059 private DatasourceManagerImpl _manager; 060 private String defaultPrefix="rdr"; 061 //private DataSourceManager manager; 062 //private Core core; 063 private Map cores=new WeakHashMap(); 064 private Map attrCache=new ReferenceMap(); 065 private Map attrsCache=new ReferenceMap(); 066 private Map arguments; 067 068 069 @Override 070 public long sizeOf() { 071 return SizeOf.size(cores)+SizeOf.size(attrCache)+SizeOf.size(attrsCache)+SizeOf.size(lock); 072 } 073 074 075 076 077 /** 078 * initalize ram resource 079 * @param scheme 080 * @param arguments 081 * @return RamResource 082 */ 083 public ResourceProvider init(String scheme,Map arguments) { 084 if(!StringUtil.isEmpty(scheme))this.scheme=scheme; 085 086 if(arguments!=null) { 087 this.arguments=arguments; 088 // case-sensitive 089 Object oCaseSensitive= arguments.get("case-sensitive"); 090 if(oCaseSensitive!=null) { 091 caseSensitive=Caster.toBooleanValue(oCaseSensitive,true); 092 } 093 094 // prefix 095 Object oPrefix= arguments.get("prefix"); 096 if(oPrefix!=null) { 097 defaultPrefix=Caster.toString(oPrefix,defaultPrefix); 098 } 099 100 // lock-timeout 101 Object oTimeout = arguments.get("lock-timeout"); 102 if(oTimeout!=null) { 103 lockTimeout=Caster.toLongValue(oTimeout,lockTimeout); 104 } 105 } 106 lock.setLockTimeout(lockTimeout); 107 lock.setCaseSensitive(caseSensitive); 108 return this; 109 } 110 111 @Override 112 public Resource getResource(String path) { 113 StringBuilder sb=new StringBuilder(); 114 return new DatasourceResource(this,parse(sb,path),sb.toString()); 115 } 116 117 118 public ConnectionData parse(StringBuilder subPath,String path) { 119 path=ResourceUtil.removeScheme(scheme,path); 120 121 ConnectionData data=new ConnectionData(); 122 int atIndex=path.indexOf('@'); 123 int slashIndex=path.indexOf('/'); 124 if(slashIndex==-1){ 125 slashIndex=path.length(); 126 path+="/"; 127 } 128 int index; 129 130 // username/password 131 if(atIndex!=-1) { 132 index=path.indexOf(':'); 133 if(index!=-1 && index<atIndex) { 134 data.setUsername(path.substring(0,index)); 135 data.setPassword(path.substring(index+1,atIndex)); 136 } 137 else data.setUsername(path.substring(0,atIndex)); 138 } 139 // host port 140 if(slashIndex>atIndex+1) { 141 data.setDatasourceName(path.substring(atIndex+1,slashIndex)); 142 } 143 if(slashIndex>atIndex+1) { 144 index=path.indexOf(':',atIndex+1); 145 if(index!=-1 && index>atIndex && index<slashIndex) { 146 data.setDatasourceName(path.substring(atIndex+1,index)); 147 data.setPrefix(path.substring(index+1,slashIndex)); 148 } 149 else { 150 data.setDatasourceName(path.substring(atIndex+1,slashIndex)); 151 data.setPrefix(defaultPrefix); 152 } 153 } 154 subPath.append(path.substring(slashIndex)); 155 return data; 156 } 157 158 159 @Override 160 public String getScheme() { 161 return scheme; 162 } 163 @Override 164 public void setResources(Resources resources) { 165 //this.resources=resources; 166 } 167 168 @Override 169 public void lock(Resource res) throws IOException { 170 lock.lock(res); 171 } 172 173 @Override 174 public void unlock(Resource res) { 175 lock.unlock(res); 176 } 177 178 @Override 179 public void read(Resource res) throws IOException { 180 lock.read(res); 181 } 182 183 @Override 184 public boolean isAttributesSupported() { 185 return false; 186 } 187 188 @Override 189 public boolean isCaseSensitive() { 190 return caseSensitive; 191 } 192 193 @Override 194 public boolean isModeSupported() { 195 return true; 196 } 197 198 private DatasourceManagerImpl getManager() { 199 if(_manager==null){ 200 Config config = ThreadLocalPageContext.getConfig(); 201 _manager=new DatasourceManagerImpl((ConfigImpl) config); 202 } 203 return _manager; 204 } 205 206 private Core getCore(ConnectionData data) throws PageException{ 207 Core core = (Core) cores.get(data.datasourceName); 208 if(core==null){ 209 DatasourceConnection dc = getManager().getConnection(ThreadLocalPageContext.get(), data.getDatasourceName(), data.getUsername(), data.getPassword()); 210 try { 211 212 dc.getConnection().setAutoCommit(false); 213 dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); 214 215 if("com.microsoft.jdbc.sqlserver.SQLServerDriver".equals(dc.getDatasource().getClazz().getName())) 216 core=new MSSQL(dc,data.getPrefix()); 217 else if("com.microsoft.sqlserver.jdbc.SQLServerDriver".equals(dc.getDatasource().getClazz().getName())) 218 core=new MSSQL(dc,data.getPrefix()); 219 else if("net.sourceforge.jtds.jdbc.Driver".equals(dc.getDatasource().getClazz().getName())) 220 core=new MSSQL(dc,data.getPrefix()); 221 else if("org.gjt.mm.mysql.Driver".equals(dc.getDatasource().getClazz().getName())) 222 core=new MySQL(dc,data.getPrefix()); 223 else 224 throw new ApplicationException("there is no DatasourceResource driver for this database ["+data.getPrefix()+"]"); 225 226 cores.put(data.datasourceName, core); 227 } 228 catch(SQLException e) { 229 throw new DatabaseException(e,dc); 230 } 231 finally { 232 release(dc); 233 //manager.releaseConnection(CONNECTION_ID,dc); 234 } 235 } 236 return core; 237 } 238 239 private DatasourceConnection getDatasourceConnection(ConnectionData data, boolean autoCommit) throws PageException { 240 DatasourceConnection dc = getManager().getConnection(ThreadLocalPageContext.get(), data.getDatasourceName(), data.getUsername(), data.getPassword()); 241 242 try { 243 dc.getConnection().setAutoCommit(autoCommit); 244 dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); 245 } 246 catch (SQLException e) { 247 throw new DatabaseException(e,dc); 248 } 249 250 return dc; 251 } 252 253 private DatasourceConnection getDatasourceConnection(ConnectionData data) throws PageException { 254 return getDatasourceConnection(data,false); 255 } 256 257 public Attr getAttr(ConnectionData data, int fullPathHash,String path, String name) { 258 Attr attr=getFromCache(data,path,name); 259 if(attr!=null) return attr; 260 try { 261 return _getAttr(data, fullPathHash,path, name); 262 } 263 catch (PageException pe) { 264 throw new PageRuntimeException(pe); 265 } 266 } 267 268 private Attr _getAttr(ConnectionData data, int fullPathHash,String path, String name) throws PageException { 269 if(!StringUtil.isEmpty(data.getDatasourceName())) { 270 DatasourceConnection dc=null; 271 try { 272 dc = getDatasourceConnection(data); 273 Attr attr=getCore(data).getAttr(dc,data.getPrefix(),fullPathHash,path,name); 274 if(attr!=null)return putToCache(data,path,name, attr); 275 } 276 catch (SQLException e) { 277 throw new DatabaseException(e,dc); 278 } 279 finally { 280 getManager().releaseConnection(ThreadLocalPageContext.get(),dc); 281 } 282 } 283 return putToCache(data,path,name,Attr.notExists(name,path)); 284 } 285 286 public Attr[] getAttrs(ConnectionData data, int pathHash,String path) throws PageException { 287 if(StringUtil.isEmpty(data.getDatasourceName())) 288 return null; 289 290 //Attr[] attrs = getFromCache(data, path); 291 //if(attrs!=null) return attrs; 292 293 DatasourceConnection dc=null; 294 try { 295 dc = getDatasourceConnection(data); 296 List list=getCore(data).getAttrs(dc,data.getPrefix(),pathHash,path); 297 298 if(list!=null){ 299 Iterator it = list.iterator(); 300 Attr[] rtn=new Attr[list.size()]; 301 int index=0; 302 while(it.hasNext()) { 303 rtn[index]=(Attr) it.next(); 304 putToCache(data,rtn[index].getParent(),rtn[index].getName(),rtn[index]); 305 index++; 306 } 307 //putToCache(data, path, rtn); 308 return rtn; 309 } 310 } 311 catch (SQLException e) { 312 throw new DatabaseException(e,dc); 313 } 314 finally { 315 release(dc); 316 //manager.releaseConnection(CONNECTION_ID,dc); 317 } 318 return null; 319 } 320 321 public void create(ConnectionData data, int fullPathHash,int pathHash,String path, String name, int type) throws IOException { 322 if(StringUtil.isEmpty(data.getDatasourceName())) 323 throw new IOException("missing datasource definition"); 324 325 removeFromCache(data, path, name); 326 327 328 DatasourceConnection dc=null; 329 try { 330 dc = getDatasourceConnection(data); 331 getCore(data).create(dc,data.getPrefix(),fullPathHash, pathHash,path,name,type); 332 } 333 catch (SQLException e) { 334 throw new IOException(e.getMessage()); 335 } 336 catch (PageException e) { 337 throw new PageRuntimeException(e); 338 } 339 finally { 340 release(dc); 341 } 342 } 343 344 345 public void delete(ConnectionData data, int fullPathHash,String path, String name) throws IOException { 346 347 Attr attr = getAttr(data, fullPathHash,path, name); 348 if(attr==null) throw new IOException("can't delete resource "+path+name+", resource does not exist"); 349 350 DatasourceConnection dc=null; 351 try { 352 dc = getDatasourceConnection(data); 353 getCore(data).delete(dc,data.getPrefix(),attr); 354 } 355 catch (SQLException e) { 356 throw new IOException(e.getMessage()); 357 } 358 catch (PageException e) { 359 throw new PageRuntimeException(e); 360 } 361 finally { 362 removeFromCache(data, path, name); 363 release(dc); 364 //manager.releaseConnection(CONNECTION_ID,dc); 365 } 366 } 367 368 public InputStream getInputStream(ConnectionData data, int fullPathHash, String path,String name) throws IOException { 369 Attr attr = getAttr(data, fullPathHash,path, name); 370 if(attr==null) throw new IOException("file ["+path+name+"] does not exist"); 371 DatasourceConnection dc=null; 372 try { 373 dc = getDatasourceConnection(data); 374 return getCore(data).getInputStream(dc, data.getPrefix(), attr); 375 } 376 catch (SQLException e) { 377 throw new IOException(e.getMessage()); 378 } 379 catch (PageException e) { 380 throw new PageRuntimeException(e); 381 } 382 finally { 383 release(dc); 384 //manager.releaseConnection(CONNECTION_ID,dc); 385 } 386 } 387 388 public synchronized OutputStream getOutputStream(ConnectionData data, int fullPathHash, int pathHash,String path,String name,boolean append) throws IOException { 389 390 Attr attr = getAttr(data, fullPathHash,path, name); 391 if(attr.getId()==0){ 392 create(data, fullPathHash, pathHash, path, name, Attr.TYPE_FILE); 393 attr = getAttr(data, fullPathHash,path, name); 394 } 395 396 397 //InputStream is = new ByteArrayInputStream("susi".getBytes()); 398 399 PipedInputStream pis = new PipedInputStream(); 400 PipedOutputStream pos = new PipedOutputStream(); 401 pis.connect(pos); 402 DatasourceConnection dc=null; 403 //Connection c=null; 404 try { 405 dc = getDatasourceConnection(data); 406 //Connection c = dc.getConnection(); 407 408 DataWriter writer=new DataWriter(getCore(data),dc, data.getPrefix(), attr, pis,this,append); 409 writer.start(); 410 411 return new DatasourceResourceOutputStream(writer,pos); 412 //core.getOutputStream(dc, name, attr, pis); 413 } 414 catch (PageException e) { 415 throw new PageRuntimeException(e); 416 } 417 finally { 418 removeFromCache(data, path, name); 419 //manager.releaseConnection(CONNECTION_ID,dc); 420 } 421 } 422 423 424 public boolean setLastModified(ConnectionData data, int fullPathHash,String path,String name,long time) { 425 try { 426 Attr attr = getAttr(data, fullPathHash,path, name); 427 DatasourceConnection dc = getDatasourceConnection(data); 428 try { 429 getCore(data).setLastModified(dc,data.getPrefix(),attr,time); 430 } 431 /*catch (SQLException e) { 432 return false; 433 } */ 434 finally { 435 removeFromCache(data, path, name); 436 release(dc); 437 //manager.releaseConnection(CONNECTION_ID,dc); 438 } 439 } 440 catch(Throwable t) { 441 return false; 442 } 443 return true; 444 } 445 446 public boolean setMode(ConnectionData data, int fullPathHash,String path,String name,int mode) { 447 try { 448 Attr attr = getAttr(data, fullPathHash, path, name); 449 DatasourceConnection dc = getDatasourceConnection(data); 450 try { 451 getCore(data).setMode(dc,data.getPrefix(),attr,mode); 452 } 453 /*catch (SQLException e) { 454 return false; 455 } */ 456 finally { 457 removeFromCache(data, path, name); 458 release(dc); 459 //manager.releaseConnection(CONNECTION_ID,dc); 460 } 461 } 462 catch(Throwable t) { 463 return false; 464 } 465 return true; 466 } 467 468 public boolean concatSupported(ConnectionData data) { 469 try { 470 return getCore(data).concatSupported(); 471 } catch (PageException e) { 472 return false; 473 } 474 } 475 476 private Attr removeFromCache(ConnectionData data, String path,String name) { 477 attrsCache.remove(data.key()+path); 478 return (Attr) attrCache.remove(data.key()+path+name); 479 } 480 481 private Attr getFromCache(ConnectionData data, String path,String name) { 482 String key=data.key()+path+name; 483 Attr attr=(Attr) attrCache.get(key); 484 485 if(attr!=null && attr.timestamp()+MAXAGE<System.currentTimeMillis()) { 486 attrCache.remove(key); 487 return null; 488 } 489 return attr; 490 } 491 492 private Attr putToCache(ConnectionData data, String path,String name, Attr attr) { 493 attrCache.put(data.key()+path+name, attr); 494 return attr; 495 } 496 497 498 499 /*private Attr[] getFromCache(ConnectionData data, String path) { 500 String key=data.key()+path; 501 Attr[] attrs= (Attr[]) attrsCache.get(key); 502 503 / *if(attr!=null && attr.timestamp()+MAXAGE<System.currentTimeMillis()) { 504 attrCache.remove(key); 505 return null; 506 }* / 507 return attrs; 508 } 509 510 private Attr[] putToCache(ConnectionData data, String path, Attr[] attrs) { 511 attrsCache.put(data.key()+path, attrs); 512 return attrs; 513 }*/ 514 515 516 517 public class ConnectionData { 518 private String username; 519 private String password; 520 private String datasourceName; 521 private String prefix; 522 /** 523 * @return the prefix 524 */ 525 public String getPrefix() { 526 return prefix; 527 } 528 529 /** 530 * @param prefix the prefix to set 531 */ 532 public void setPrefix(String prefix) { 533 this.prefix = prefix; 534 } 535 536 /** 537 * @return the username 538 */ 539 public String getUsername() { 540 return username; 541 } 542 543 /** 544 * @param username the username to set 545 */ 546 public void setUsername(String username) { 547 this.username = username; 548 } 549 /** 550 * @return the password 551 */ 552 public String getPassword() { 553 return password; 554 } 555 /** 556 * @param password the password to set 557 */ 558 public void setPassword(String password) { 559 this.password = password; 560 } 561 /** 562 * @return the datasourceName 563 */ 564 public String getDatasourceName() { 565 return datasourceName; 566 } 567 /** 568 * @param datasourceName the datasourceName to set 569 */ 570 public void setDatasourceName(String datasourceName) { 571 this.datasourceName = datasourceName; 572 } 573 574 public String key() { 575 if(StringUtil.isEmpty(username)) 576 return datasourceName; 577 return username+":"+password+"@"+datasourceName; 578 } 579 580 } 581 582 583 584 /** 585 * release datasource connection 586 * @param dc 587 * @param autoCommit 588 */ 589 void release(DatasourceConnection dc) { 590 if(dc!=null) { 591 592 try { 593 dc.getConnection().commit(); 594 dc.getConnection().setAutoCommit(true); 595 dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_NONE); 596 } 597 catch (SQLException e) {} 598 599 getManager().releaseConnection(ThreadLocalPageContext.get(),dc); 600 } 601 } 602 603 @Override 604 public Map getArguments() { 605 return arguments; 606 } 607 608 609 610 611 612 613 614 }