001 package railo.runtime.type.scope.storage; 002 003 import java.sql.Connection; 004 import java.sql.PreparedStatement; 005 import java.sql.SQLException; 006 import java.sql.Types; 007 008 import railo.commons.io.log.Log; 009 import railo.runtime.PageContext; 010 import railo.runtime.config.Config; 011 import railo.runtime.config.ConfigImpl; 012 import railo.runtime.converter.ConverterException; 013 import railo.runtime.converter.ScriptConverter; 014 import railo.runtime.db.DataSourceImpl; 015 import railo.runtime.db.DatasourceConnection; 016 import railo.runtime.db.DatasourceConnectionPool; 017 import railo.runtime.db.SQL; 018 import railo.runtime.db.SQLCaster; 019 import railo.runtime.db.SQLImpl; 020 import railo.runtime.db.SQLItem; 021 import railo.runtime.db.SQLItemImpl; 022 import railo.runtime.debug.DebuggerImpl; 023 import railo.runtime.exp.ApplicationException; 024 import railo.runtime.exp.DatabaseException; 025 import railo.runtime.exp.PageException; 026 import railo.runtime.op.Caster; 027 import railo.runtime.type.Collection; 028 import railo.runtime.type.KeyImpl; 029 import railo.runtime.type.Query; 030 import railo.runtime.type.QueryImpl; 031 import railo.runtime.type.Struct; 032 import railo.runtime.type.dt.DateTime; 033 import railo.runtime.type.dt.DateTimeImpl; 034 import railo.runtime.type.scope.ScopeContext; 035 036 /** 037 * client scope that store it's data in a datasource 038 */ 039 public abstract class StorageScopeDatasource extends StorageScopeImpl { 040 041 private static final long serialVersionUID = 239179599401918216L; 042 043 public static final Collection.Key EXPIRES = KeyImpl.intern("expires"); 044 045 public static final String PREFIX = "cf"; 046 047 private String datasourceName; 048 049 private String appName; 050 051 private String cfid; 052 053 054 /** 055 * Constructor of the class 056 * @param pc 057 * @param name 058 * @param sct 059 * @param b 060 */ 061 protected StorageScopeDatasource(PageContext pc,String datasourceName, String strType,int type,Struct sct) { 062 super( 063 sct, 064 type==SCOPE_CLIENT?doNowIfNull(pc,Caster.toDate(sct.get(TIMECREATED,null),false,pc.getTimeZone(),null)):null, 065 doNowIfNull(pc,Caster.toDate(sct.get(LASTVISIT,null),false,pc.getTimeZone(),null)), 066 -1, 067 type==SCOPE_CLIENT?Caster.toIntValue(sct.get(HITCOUNT,"1"),1):0, 068 strType,type); 069 070 this.datasourceName=datasourceName; 071 appName=pc.getApplicationContext().getName(); 072 cfid=pc.getCFID(); 073 } 074 075 /** 076 * Constructor of the class, clone existing 077 * @param other 078 */ 079 protected StorageScopeDatasource(StorageScopeDatasource other,boolean deepCopy) { 080 super(other,deepCopy); 081 this.datasourceName=other.datasourceName; 082 } 083 084 private static DateTime doNowIfNull(PageContext pc,DateTime dt) { 085 if(dt==null)return new DateTimeImpl(pc.getConfig()); 086 return dt; 087 } 088 089 090 091 092 protected static Struct _loadData(PageContext pc, String datasourceName,String strType,int type, Log log, boolean mxStyle) throws PageException { 093 DatasourceConnection dc=null; 094 Query query=null; 095 // select 096 SQL sqlSelect=mxStyle? 097 new SQLImpl("mx"): 098 new SQLImpl("select data from "+PREFIX+"_"+strType+"_data where cfid=? and name=? and expires > ?" 099 ,new SQLItem[]{ 100 new SQLItemImpl(pc.getCFID(),Types.VARCHAR), 101 new SQLItemImpl(pc.getApplicationContext().getName(),Types.VARCHAR), 102 new SQLItemImpl(now(pc.getConfig()),Types.VARCHAR) 103 }); 104 105 ConfigImpl config = (ConfigImpl)pc.getConfig(); 106 DatasourceConnectionPool pool = config.getDatasourceConnectionPool(); 107 try { 108 dc=pool.getDatasourceConnection(pc,config.getDataSource(datasourceName),null,null); 109 if(!((DataSourceImpl)dc.getDatasource()).isStorage()) 110 throw new ApplicationException("storage usage for this datasource is disabled, you can enable this in the railo administrator."); 111 query = new QueryImpl(dc,sqlSelect,-1,-1,-1,"query"); 112 } 113 catch (DatabaseException de) { 114 if(dc==null) throw de; 115 ScopeContext.info(log,"create table "+PREFIX+"_"+strType+"_data in datasource ["+datasourceName+"]"); 116 try { 117 new QueryImpl(dc,createSQL(dc,mxStyle,"text",strType),-1,-1,-1,"query"); 118 } 119 catch (DatabaseException _de) { 120 try { 121 new QueryImpl(dc,createSQL(dc,mxStyle,"memo",strType),-1,-1,-1,"query"); 122 } 123 catch (DatabaseException __de) { 124 new QueryImpl(dc,createSQL(dc,mxStyle,"clob",strType),-1,-1,-1,"query"); 125 } 126 } 127 query = new QueryImpl(dc,sqlSelect,-1,-1,-1,"query"); 128 } 129 finally { 130 if(dc!=null) pool.releaseDatasourceConnection(dc); 131 } 132 boolean debugUsage=DebuggerImpl.debugQueryUsage(pc,query); 133 ((DebuggerImpl)pc.getDebugger()).addQuery(debugUsage?query:null,datasourceName,"",sqlSelect,query.getRecordcount(),pc.getCurrentPageSource(),query.executionTime()); 134 boolean _isNew = query.getRecordcount()==0; 135 136 if(_isNew) { 137 ScopeContext.info(log,"create new "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID()+" in datasource ["+datasourceName+"]"); 138 return null; 139 } 140 String str=Caster.toString(query.get(KeyImpl.DATA)); 141 if(mxStyle) return null; 142 Struct s = (Struct)pc.evaluate(str); 143 ScopeContext.info(log,"load existing data from ["+datasourceName+"."+PREFIX+"_"+strType+"_data] to create "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID()); 144 145 return s; 146 } 147 148 /** 149 * @see railo.runtime.type.scope.storage.StorageScopeImpl#touchAfterRequest(railo.runtime.PageContext) 150 */ 151 public void touchAfterRequest(PageContext pc) { 152 setTimeSpan(pc); 153 super.touchAfterRequest(pc); 154 155 store(pc.getConfig()); 156 } 157 158 public void store(Config config) { 159 //if(!super.hasContent()) return; 160 161 DatasourceConnection dc = null; 162 ConfigImpl ci = (ConfigImpl)config; 163 DatasourceConnectionPool pool = ci.getDatasourceConnectionPool(); 164 try { 165 dc=pool.getDatasourceConnection(null,config.getDataSource(datasourceName),null,null); 166 int recordsAffected = executeUpdate(config,dc.getConnection(),"update "+PREFIX+"_"+getTypeAsString()+"_data set expires=?,data=? where cfid=? and name=?",false); 167 if(recordsAffected>1) { 168 executeUpdate(config,dc.getConnection(),"delete from "+PREFIX+"_"+getTypeAsString()+"_data where cfid=? and name=?",true); 169 recordsAffected=0; 170 } 171 if(recordsAffected==0) { 172 executeUpdate(config,dc.getConnection(),"insert into "+PREFIX+"_"+getTypeAsString()+"_data (expires,data,cfid,name) values(?,?,?,?)",false); 173 } 174 175 } 176 catch (Exception e) {} 177 finally { 178 if(dc!=null) pool.releaseDatasourceConnection(dc); 179 } 180 } 181 182 public void unstore(Config config) { 183 ConfigImpl ci=(ConfigImpl) config; 184 DatasourceConnection dc = null; 185 186 187 DatasourceConnectionPool pool = ci.getDatasourceConnectionPool(); 188 try { 189 dc=pool.getDatasourceConnection(null,config.getDataSource(datasourceName),null,null); 190 executeUpdate(config,dc.getConnection(),"delete from "+PREFIX+"_"+getTypeAsString()+"_data where cfid=? and name=?",true); 191 } 192 catch (Exception e) {} 193 finally { 194 if(dc!=null) pool.releaseDatasourceConnection(dc); 195 } 196 } 197 198 199 200 private int executeUpdate(Config config,Connection conn, String strSQL, boolean ignoreData) throws SQLException, PageException, ConverterException { 201 //String appName = pc.getApplicationContext().getName(); 202 SQLImpl sql = new SQLImpl(strSQL,new SQLItem[]{ 203 new SQLItemImpl(createExpires(getTimeSpan(), config),Types.VARCHAR), 204 new SQLItemImpl(new ScriptConverter().serializeStruct(sct,ignoreSet),Types.VARCHAR), 205 new SQLItemImpl(cfid,Types.VARCHAR), 206 new SQLItemImpl(appName,Types.VARCHAR) 207 }); 208 if(ignoreData)sql = new SQLImpl(strSQL,new SQLItem[]{ 209 new SQLItemImpl(cfid,Types.VARCHAR), 210 new SQLItemImpl(appName,Types.VARCHAR) 211 }); 212 213 return execute(conn, sql); 214 } 215 216 217 218 219 220 private static String createExpires(long timespan,Config config) { 221 return Caster.toString(timespan+new DateTimeImpl(config).getTime()); 222 } 223 224 private static String now(Config config) { 225 return Caster.toString(new DateTimeImpl(config).getTime()); 226 } 227 228 private static int execute(Connection conn, SQLImpl sql) throws SQLException, PageException { 229 PreparedStatement preStat = conn.prepareStatement(sql.getSQLString()); 230 int count=0; 231 try { 232 SQLItem[] items=sql.getItems(); 233 for(int i=0;i<items.length;i++) { 234 SQLCaster.setValue(preStat,i+1,items[i]); 235 } 236 count= preStat.executeUpdate(); 237 } 238 finally { 239 preStat.close(); 240 } 241 return count; 242 } 243 244 private static SQL createSQL(DatasourceConnection dc, boolean mxStyle, String textType, String type) { 245 String clazz = dc.getDatasource().getClazz().getName(); 246 247 boolean isMSSQL= 248 clazz.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver") || 249 clazz.equals("net.sourceforge.jtds.jdbc.Driver"); 250 boolean isHSQLDB= 251 clazz.equals("org.hsqldb.jdbcDriver"); 252 boolean isOracle= 253 clazz.indexOf("OracleDriver")!=-1; 254 255 StringBuffer sb=new StringBuffer("CREATE TABLE "); 256 if(mxStyle) {} 257 else { 258 if(isMSSQL)sb.append("dbo."); 259 sb.append(PREFIX+"_"+type+"_data ("); 260 261 // expires 262 sb.append("expires varchar(64) NOT NULL, "); 263 // cfid 264 sb.append("cfid varchar(64) NOT NULL, "); 265 // name 266 sb.append("name varchar(255) NOT NULL, "); 267 // data 268 sb.append("data "); 269 if(isHSQLDB)sb.append("varchar "); 270 else if(isOracle)sb.append("CLOB "); 271 else sb.append(textType+" "); 272 sb.append(" NOT NULL"); 273 } 274 sb.append(")"); 275 return new SQLImpl(sb.toString()); 276 } 277 278 279 /** 280 * 281 * @see railo.runtime.type.scope.ClientSupportOld#initialize(railo.runtime.PageContext) 282 */ 283 public void touchBeforeRequest(PageContext pc) { 284 setTimeSpan(pc); 285 super.touchBeforeRequest(pc); 286 } 287 288 /** 289 * @see railo.runtime.type.scope.storage.StorageScope#getStorageType() 290 */ 291 public String getStorageType() { 292 return "Datasource"; 293 } 294 295 /** 296 * @return the datasourceName 297 */ 298 public String getDatasourceName() { 299 return datasourceName; 300 } 301 }