001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Stephane Lacoin
018 */
019package org.nuxeo.runtime.datasource.geronimo;
020
021import java.sql.Connection;
022import java.sql.SQLException;
023import java.sql.SQLFeatureNotSupportedException;
024import java.util.Hashtable;
025import java.util.logging.Logger;
026
027import javax.naming.Context;
028import javax.naming.Name;
029import javax.naming.NamingException;
030import javax.naming.RefAddr;
031import javax.naming.Reference;
032import javax.resource.ResourceException;
033import javax.resource.spi.InvalidPropertyException;
034import javax.resource.spi.ManagedConnectionFactory;
035import javax.sql.XADataSource;
036
037import org.apache.commons.logging.LogFactory;
038import org.nuxeo.runtime.datasource.PooledDataSourceRegistry;
039import org.nuxeo.runtime.datasource.PooledDataSourceRegistry.PooledDataSource;
040import org.nuxeo.runtime.jtajca.NuxeoConnectionManagerConfiguration;
041import org.nuxeo.runtime.jtajca.NuxeoConnectionManagerFactory;
042import org.nuxeo.runtime.jtajca.NuxeoContainer;
043import org.nuxeo.runtime.jtajca.NuxeoContainer.ConnectionManagerWrapper;
044import org.tranql.connector.NoExceptionsAreFatalSorter;
045import org.tranql.connector.jdbc.JDBCDriverMCF;
046import org.tranql.connector.jdbc.LocalDataSourceWrapper;
047import org.tranql.connector.jdbc.TranqlDataSource;
048import org.tranql.connector.jdbc.XADataSourceWrapper;
049
050public class PooledDataSourceFactory implements PooledDataSourceRegistry.Factory {
051
052    protected static class DataSource extends TranqlDataSource implements PooledDataSource {
053
054        protected ConnectionManagerWrapper wrapper;
055
056        public DataSource(ManagedConnectionFactory mcf, ConnectionManagerWrapper wrapper) {
057            super(mcf, wrapper);
058            this.wrapper = wrapper;
059        }
060
061        @Override
062        public void dispose() {
063            wrapper.dispose();
064        }
065
066        @Override
067        public Connection getConnection(boolean noSharing) throws SQLException {
068            if (!noSharing) {
069                return getConnection();
070            }
071            wrapper.enterNoSharing();
072            try {
073                return getConnection();
074            } finally {
075                wrapper.exitNoSharing();
076            }
077        }
078
079        @Override
080        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
081            throw new SQLFeatureNotSupportedException("not yet available");
082        }
083    }
084
085    @Override
086    public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable<?, ?> environment) {
087        Reference ref = (Reference) obj;
088        ManagedConnectionFactory mcf;
089        ConnectionManagerWrapper cm;
090        try {
091            mcf = createFactory(ref, ctx);
092            cm = createManager(ref, ctx);
093        } catch (ResourceException | NamingException e) {
094            throw new RuntimeException(e);
095        }
096        return new DataSource(mcf, cm);
097    }
098
099    protected ConnectionManagerWrapper createManager(Reference ref, Context ctx) throws ResourceException {
100        NuxeoConnectionManagerConfiguration config = NuxeoConnectionManagerFactory.getConfig(ref);
101        String className = ref.getClassName();
102        config.setXAMode(XADataSource.class.getName().equals(className));
103        return NuxeoContainer.initConnectionManager(config);
104    }
105
106    protected ManagedConnectionFactory createFactory(Reference ref, Context ctx) throws NamingException,
107            InvalidPropertyException {
108        String className = ref.getClassName();
109        if (XADataSource.class.getName().equals(className)) {
110            String user = refAttribute(ref, "User", "");
111            String password = refAttribute(ref, "Password", "");
112            String name = refAttribute(ref, "dataSourceJNDI", null);
113            XADataSource ds = NuxeoContainer.lookup(name, XADataSource.class);
114            XADataSourceWrapper wrapper = new XADataSourceWrapper(ds);
115            wrapper.setUserName(user);
116            wrapper.setPassword(password);
117            return wrapper;
118        }
119        if (javax.sql.DataSource.class.getName().equals(className)) {
120            String user = refAttribute(ref, "username", "");
121            if (user.isEmpty()) {
122                user = refAttribute(ref, "user", "");
123                if (!user.isEmpty()) {
124                    LogFactory.getLog(PooledDataSourceFactory.class).warn(
125                            "wrong attribute 'user' in datasource descriptor, should use 'username' instead");
126                }
127            }
128            String password = refAttribute(ref, "password", "");
129            String dsname = refAttribute(ref, "dataSourceJNDI", "");
130            if (!dsname.isEmpty()) {
131                javax.sql.DataSource ds = NuxeoContainer.lookup(dsname, DataSource.class);
132                LocalDataSourceWrapper wrapper = new LocalDataSourceWrapper(ds);
133                wrapper.setUserName(user);
134                wrapper.setPassword(password);
135                return wrapper;
136            }
137            String name = refAttribute(ref, "driverClassName", null);
138            String url = refAttribute(ref, "url", null);
139            String sqlExceptionSorter = refAttribute(ref, "sqlExceptionSorter",
140                    NoExceptionsAreFatalSorter.class.getName());
141            boolean commitBeforeAutocommit = Boolean.valueOf(refAttribute(ref, "commitBeforeAutocommit", "true")).booleanValue();
142            JDBCDriverMCF factory = new JDBCDriverMCF();
143            factory.setDriver(name);
144            factory.setUserName(user);
145            factory.setPassword(password);
146            factory.setConnectionURL(url);
147            factory.setExceptionSorterClass(sqlExceptionSorter);
148            factory.setCommitBeforeAutocommit(commitBeforeAutocommit);
149            return factory;
150        }
151        throw new IllegalArgumentException("unsupported class " + className);
152    }
153
154    protected String refAttribute(Reference ref, String key, String defvalue) {
155        RefAddr addr = ref.get(key);
156        if (addr == null) {
157            if (defvalue == null) {
158                throw new IllegalArgumentException(key + " address is mandatory");
159            }
160            return defvalue;
161        }
162        return (String) addr.getContent();
163    }
164
165}