lucee.commons.io.res.type.datasource.DatasourceResourceProvider.java Source code

Java tutorial

Introduction

Here is the source code for lucee.commons.io.res.type.datasource.DatasourceResourceProvider.java

Source

/**
 *
 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/
package lucee.commons.io.res.type.datasource;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import lucee.commons.io.res.Resource;
import lucee.commons.io.res.ResourceProvider;
import lucee.commons.io.res.ResourceProviderPro;
import lucee.commons.io.res.Resources;
import lucee.commons.io.res.type.datasource.core.Core;
import lucee.commons.io.res.type.datasource.core.MSSQL;
import lucee.commons.io.res.type.datasource.core.MySQL;
import lucee.commons.io.res.util.ResourceLockImpl;
import lucee.commons.io.res.util.ResourceUtil;
import lucee.commons.lang.SizeOf;
import lucee.commons.lang.StringUtil;
import lucee.runtime.config.Config;
import lucee.runtime.config.ConfigImpl;
import lucee.runtime.db.DatasourceConnection;
import lucee.runtime.db.DatasourceManagerImpl;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.exp.ApplicationException;
import lucee.runtime.exp.DatabaseException;
import lucee.runtime.exp.PageException;
import lucee.runtime.exp.PageRuntimeException;
import lucee.runtime.op.Caster;
import lucee.runtime.type.Sizeable;

import org.apache.commons.collections.map.ReferenceMap;

/**
 * Resource Provider for ram resource
 */
public final class DatasourceResourceProvider implements ResourceProviderPro, Sizeable {

    public static final int DBTYPE_ANSI92 = 0;
    public static final int DBTYPE_MSSQL = 1;
    public static final int DBTYPE_MYSQL = 2;

    private static final int MAXAGE = 5000;

    //private static final int CONNECTION_ID = 0;

    private String scheme = "ds";

    boolean caseSensitive = true;
    //private Resources resources;
    private long lockTimeout = 1000;
    private ResourceLockImpl lock = new ResourceLockImpl(lockTimeout, caseSensitive);
    private DatasourceManagerImpl _manager;
    private String defaultPrefix = "rdr";
    //private DataSourceManager manager;
    //private Core core;
    private Map cores = new WeakHashMap();
    private Map attrCache = new ReferenceMap();
    private Map attrsCache = new ReferenceMap();
    private Map arguments;

    @Override
    public long sizeOf() {
        return SizeOf.size(cores) + SizeOf.size(attrCache) + SizeOf.size(attrsCache) + SizeOf.size(lock);
    }

    /**
     * initalize ram resource
     * @param scheme
     * @param arguments
     * @return RamResource
     */
    public ResourceProvider init(String scheme, Map arguments) {
        if (!StringUtil.isEmpty(scheme))
            this.scheme = scheme;

        if (arguments != null) {
            this.arguments = arguments;
            // case-sensitive
            Object oCaseSensitive = arguments.get("case-sensitive");
            if (oCaseSensitive != null) {
                caseSensitive = Caster.toBooleanValue(oCaseSensitive, true);
            }

            // prefix
            Object oPrefix = arguments.get("prefix");
            if (oPrefix != null) {
                defaultPrefix = Caster.toString(oPrefix, defaultPrefix);
            }

            // lock-timeout
            Object oTimeout = arguments.get("lock-timeout");
            if (oTimeout != null) {
                lockTimeout = Caster.toLongValue(oTimeout, lockTimeout);
            }
        }
        lock.setLockTimeout(lockTimeout);
        lock.setCaseSensitive(caseSensitive);
        return this;
    }

    @Override
    public Resource getResource(String path) {
        StringBuilder sb = new StringBuilder();
        return new DatasourceResource(this, parse(sb, path), sb.toString());
    }

    public ConnectionData parse(StringBuilder subPath, String path) {
        path = ResourceUtil.removeScheme(scheme, path);

        ConnectionData data = new ConnectionData();
        int atIndex = path.indexOf('@');
        int slashIndex = path.indexOf('/');
        if (slashIndex == -1) {
            slashIndex = path.length();
            path += "/";
        }
        int index;

        // username/password
        if (atIndex != -1) {
            index = path.indexOf(':');
            if (index != -1 && index < atIndex) {
                data.setUsername(path.substring(0, index));
                data.setPassword(path.substring(index + 1, atIndex));
            } else
                data.setUsername(path.substring(0, atIndex));
        }
        // host port
        if (slashIndex > atIndex + 1) {
            data.setDatasourceName(path.substring(atIndex + 1, slashIndex));
        }
        if (slashIndex > atIndex + 1) {
            index = path.indexOf(':', atIndex + 1);
            if (index != -1 && index > atIndex && index < slashIndex) {
                data.setDatasourceName(path.substring(atIndex + 1, index));
                data.setPrefix(path.substring(index + 1, slashIndex));
            } else {
                data.setDatasourceName(path.substring(atIndex + 1, slashIndex));
                data.setPrefix(defaultPrefix);
            }
        }
        subPath.append(path.substring(slashIndex));
        return data;
    }

    @Override
    public String getScheme() {
        return scheme;
    }

    @Override
    public void setResources(Resources resources) {
        //this.resources=resources;
    }

    @Override
    public void lock(Resource res) throws IOException {
        lock.lock(res);
    }

    @Override
    public void unlock(Resource res) {
        lock.unlock(res);
    }

    @Override
    public void read(Resource res) throws IOException {
        lock.read(res);
    }

    @Override
    public boolean isAttributesSupported() {
        return false;
    }

    @Override
    public boolean isCaseSensitive() {
        return caseSensitive;
    }

    @Override
    public boolean isModeSupported() {
        return true;
    }

    private DatasourceManagerImpl getManager() {
        if (_manager == null) {
            Config config = ThreadLocalPageContext.getConfig();
            _manager = new DatasourceManagerImpl((ConfigImpl) config);
        }
        return _manager;
    }

    private Core getCore(ConnectionData data) throws PageException {
        Core core = (Core) cores.get(data.datasourceName);
        if (core == null) {
            DatasourceConnection dc = getManager().getConnection(ThreadLocalPageContext.get(),
                    data.getDatasourceName(), data.getUsername(), data.getPassword());
            try {

                dc.getConnection().setAutoCommit(false);
                dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

                if ("com.microsoft.jdbc.sqlserver.SQLServerDriver".equals(dc.getDatasource().getClazz().getName()))
                    core = new MSSQL(dc, data.getPrefix());
                else if ("com.microsoft.sqlserver.jdbc.SQLServerDriver"
                        .equals(dc.getDatasource().getClazz().getName()))
                    core = new MSSQL(dc, data.getPrefix());
                else if ("net.sourceforge.jtds.jdbc.Driver".equals(dc.getDatasource().getClazz().getName()))
                    core = new MSSQL(dc, data.getPrefix());
                else if ("org.gjt.mm.mysql.Driver".equals(dc.getDatasource().getClazz().getName()))
                    core = new MySQL(dc, data.getPrefix());
                else
                    throw new ApplicationException(
                            "there is no DatasourceResource driver for this database [" + data.getPrefix() + "]");

                cores.put(data.datasourceName, core);
            } catch (SQLException e) {
                throw new DatabaseException(e, dc);
            } finally {
                release(dc);
                //manager.releaseConnection(CONNECTION_ID,dc);
            }
        }
        return core;
    }

    private DatasourceConnection getDatasourceConnection(ConnectionData data, boolean autoCommit)
            throws PageException {
        DatasourceConnection dc = getManager().getConnection(ThreadLocalPageContext.get(), data.getDatasourceName(),
                data.getUsername(), data.getPassword());

        try {
            dc.getConnection().setAutoCommit(autoCommit);
            dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        } catch (SQLException e) {
            throw new DatabaseException(e, dc);
        }

        return dc;
    }

    private DatasourceConnection getDatasourceConnection(ConnectionData data) throws PageException {
        return getDatasourceConnection(data, false);
    }

    public Attr getAttr(ConnectionData data, int fullPathHash, String path, String name) {
        Attr attr = getFromCache(data, path, name);
        if (attr != null)
            return attr;
        try {
            return _getAttr(data, fullPathHash, path, name);
        } catch (PageException pe) {
            throw new PageRuntimeException(pe);
        }
    }

    private Attr _getAttr(ConnectionData data, int fullPathHash, String path, String name) throws PageException {
        if (!StringUtil.isEmpty(data.getDatasourceName())) {
            DatasourceConnection dc = null;
            try {
                dc = getDatasourceConnection(data);
                Attr attr = getCore(data).getAttr(dc, data.getPrefix(), fullPathHash, path, name);
                if (attr != null)
                    return putToCache(data, path, name, attr);
            } catch (SQLException e) {
                throw new DatabaseException(e, dc);
            } finally {
                getManager().releaseConnection(ThreadLocalPageContext.get(), dc);
            }
        }
        return putToCache(data, path, name, Attr.notExists(name, path));
    }

    public Attr[] getAttrs(ConnectionData data, int pathHash, String path) throws PageException {
        if (StringUtil.isEmpty(data.getDatasourceName()))
            return null;

        //Attr[] attrs = getFromCache(data, path);
        //if(attrs!=null) return attrs;

        DatasourceConnection dc = null;
        try {
            dc = getDatasourceConnection(data);
            List list = getCore(data).getAttrs(dc, data.getPrefix(), pathHash, path);

            if (list != null) {
                Iterator it = list.iterator();
                Attr[] rtn = new Attr[list.size()];
                int index = 0;
                while (it.hasNext()) {
                    rtn[index] = (Attr) it.next();
                    putToCache(data, rtn[index].getParent(), rtn[index].getName(), rtn[index]);
                    index++;
                }
                //putToCache(data, path, rtn);
                return rtn;
            }
        } catch (SQLException e) {
            throw new DatabaseException(e, dc);
        } finally {
            release(dc);
            //manager.releaseConnection(CONNECTION_ID,dc);
        }
        return null;
    }

    public void create(ConnectionData data, int fullPathHash, int pathHash, String path, String name, int type)
            throws IOException {
        if (StringUtil.isEmpty(data.getDatasourceName()))
            throw new IOException("missing datasource definition");

        removeFromCache(data, path, name);

        DatasourceConnection dc = null;
        try {
            dc = getDatasourceConnection(data);
            getCore(data).create(dc, data.getPrefix(), fullPathHash, pathHash, path, name, type);
        } catch (SQLException e) {
            throw new IOException(e.getMessage());
        } catch (PageException e) {
            throw new PageRuntimeException(e);
        } finally {
            release(dc);
        }
    }

    public void delete(ConnectionData data, int fullPathHash, String path, String name) throws IOException {

        Attr attr = getAttr(data, fullPathHash, path, name);
        if (attr == null)
            throw new IOException("can't delete resource " + path + name + ", resource does not exist");

        DatasourceConnection dc = null;
        try {
            dc = getDatasourceConnection(data);
            getCore(data).delete(dc, data.getPrefix(), attr);
        } catch (SQLException e) {
            throw new IOException(e.getMessage());
        } catch (PageException e) {
            throw new PageRuntimeException(e);
        } finally {
            removeFromCache(data, path, name);
            release(dc);
            //manager.releaseConnection(CONNECTION_ID,dc);
        }
    }

    public InputStream getInputStream(ConnectionData data, int fullPathHash, String path, String name)
            throws IOException {
        Attr attr = getAttr(data, fullPathHash, path, name);
        if (attr == null)
            throw new IOException("file [" + path + name + "] does not exist");
        DatasourceConnection dc = null;
        try {
            dc = getDatasourceConnection(data);
            return getCore(data).getInputStream(dc, data.getPrefix(), attr);
        } catch (SQLException e) {
            throw new IOException(e.getMessage());
        } catch (PageException e) {
            throw new PageRuntimeException(e);
        } finally {
            release(dc);
            //manager.releaseConnection(CONNECTION_ID,dc);
        }
    }

    public synchronized OutputStream getOutputStream(ConnectionData data, int fullPathHash, int pathHash,
            String path, String name, boolean append) throws IOException {

        Attr attr = getAttr(data, fullPathHash, path, name);
        if (attr.getId() == 0) {
            create(data, fullPathHash, pathHash, path, name, Attr.TYPE_FILE);
            attr = getAttr(data, fullPathHash, path, name);
        }

        PipedInputStream pis = new PipedInputStream();
        PipedOutputStream pos = new PipedOutputStream();
        pis.connect(pos);
        DatasourceConnection dc = null;
        //Connection c=null;
        try {
            dc = getDatasourceConnection(data);
            //Connection c = dc.getConnection();

            DataWriter writer = new DataWriter(getCore(data), dc, data.getPrefix(), attr, pis, this, append);
            writer.start();

            return new DatasourceResourceOutputStream(writer, pos);
            //core.getOutputStream(dc, name, attr, pis);
        } catch (PageException e) {
            throw new PageRuntimeException(e);
        } finally {
            removeFromCache(data, path, name);
            //manager.releaseConnection(CONNECTION_ID,dc);
        }
    }

    public boolean setLastModified(ConnectionData data, int fullPathHash, String path, String name, long time) {
        try {
            Attr attr = getAttr(data, fullPathHash, path, name);
            DatasourceConnection dc = getDatasourceConnection(data);
            try {
                getCore(data).setLastModified(dc, data.getPrefix(), attr, time);
            }
            /*catch (SQLException e) {
               return false;
            } */
            finally {
                removeFromCache(data, path, name);
                release(dc);
                //manager.releaseConnection(CONNECTION_ID,dc);
            }
        } catch (Throwable t) {
            return false;
        }
        return true;
    }

    public boolean setMode(ConnectionData data, int fullPathHash, String path, String name, int mode) {
        try {
            Attr attr = getAttr(data, fullPathHash, path, name);
            DatasourceConnection dc = getDatasourceConnection(data);
            try {
                getCore(data).setMode(dc, data.getPrefix(), attr, mode);
            }
            /*catch (SQLException e) {
               return false;
            } */
            finally {
                removeFromCache(data, path, name);
                release(dc);
                //manager.releaseConnection(CONNECTION_ID,dc);
            }
        } catch (Throwable t) {
            return false;
        }
        return true;
    }

    public boolean concatSupported(ConnectionData data) {
        try {
            return getCore(data).concatSupported();
        } catch (PageException e) {
            return false;
        }
    }

    private Attr removeFromCache(ConnectionData data, String path, String name) {
        attrsCache.remove(data.key() + path);
        return (Attr) attrCache.remove(data.key() + path + name);
    }

    private Attr getFromCache(ConnectionData data, String path, String name) {
        String key = data.key() + path + name;
        Attr attr = (Attr) attrCache.get(key);

        if (attr != null && attr.timestamp() + MAXAGE < System.currentTimeMillis()) {
            attrCache.remove(key);
            return null;
        }
        return attr;
    }

    private Attr putToCache(ConnectionData data, String path, String name, Attr attr) {
        attrCache.put(data.key() + path + name, attr);
        return attr;
    }

    /*private Attr[] getFromCache(ConnectionData data, String path) {
       String key=data.key()+path;
       Attr[] attrs= (Attr[]) attrsCache.get(key);
           
       / *if(attr!=null && attr.timestamp()+MAXAGE<System.currentTimeMillis()) {
     attrCache.remove(key);
     return null;
       }* /
       return attrs;
    }
        
    private Attr[] putToCache(ConnectionData data, String path, Attr[] attrs) {
       attrsCache.put(data.key()+path, attrs);
       return attrs;
    }*/

    public class ConnectionData {
        private String username;
        private String password;
        private String datasourceName;
        private String prefix;

        /**
         * @return the prefix
         */
        public String getPrefix() {
            return prefix;
        }

        /**
         * @param prefix the prefix to set
         */
        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        /**
         * @return the username
         */
        public String getUsername() {
            return username;
        }

        /**
         * @param username the username to set
         */
        public void setUsername(String username) {
            this.username = username;
        }

        /**
         * @return the password
         */
        public String getPassword() {
            return password;
        }

        /**
         * @param password the password to set
         */
        public void setPassword(String password) {
            this.password = password;
        }

        /**
         * @return the datasourceName
         */
        public String getDatasourceName() {
            return datasourceName;
        }

        /**
         * @param datasourceName the datasourceName to set
         */
        public void setDatasourceName(String datasourceName) {
            this.datasourceName = datasourceName;
        }

        public String key() {
            if (StringUtil.isEmpty(username))
                return datasourceName;
            return username + ":" + password + "@" + datasourceName;
        }

    }

    /**
     * release datasource connection
     * @param dc
     * @param autoCommit 
     */
    void release(DatasourceConnection dc) {
        if (dc != null) {

            try {
                dc.getConnection().commit();
                dc.getConnection().setAutoCommit(true);
                dc.getConnection().setTransactionIsolation(Connection.TRANSACTION_NONE);
            } catch (SQLException e) {
            }

            getManager().releaseConnection(ThreadLocalPageContext.get(), dc);
        }
    }

    @Override
    public Map getArguments() {
        return arguments;
    }

    @Override
    public char getSeparator() {
        return '/';
    }

}