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    }