001/*
002 * Copyright 2012-2018 the original author or authors.
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
017package org.springframework.boot.jta.bitronix;
018
019import java.io.PrintWriter;
020import java.sql.SQLException;
021import java.sql.SQLFeatureNotSupportedException;
022import java.util.Properties;
023import java.util.logging.Logger;
024
025import javax.sql.XAConnection;
026import javax.sql.XADataSource;
027
028import bitronix.tm.resource.common.ResourceBean;
029import bitronix.tm.resource.common.XAStatefulHolder;
030import bitronix.tm.resource.jdbc.PoolingDataSource;
031
032import org.springframework.beans.factory.BeanNameAware;
033import org.springframework.beans.factory.InitializingBean;
034import org.springframework.boot.context.properties.ConfigurationProperties;
035import org.springframework.util.StringUtils;
036
037/**
038 * Spring friendly version of {@link PoolingDataSource}. Provides sensible defaults and
039 * also supports direct wrapping of a {@link XADataSource} instance.
040 *
041 * @author Phillip Webb
042 * @author Josh Long
043 * @author Andy Wilkinson
044 * @since 1.2.0
045 */
046@SuppressWarnings("serial")
047@ConfigurationProperties(prefix = "spring.jta.bitronix.datasource")
048public class PoolingDataSourceBean extends PoolingDataSource
049                implements BeanNameAware, InitializingBean {
050
051        private static final ThreadLocal<PoolingDataSourceBean> source = new ThreadLocal<>();
052
053        private XADataSource dataSource;
054
055        private String beanName;
056
057        public PoolingDataSourceBean() {
058                setMaxPoolSize(10);
059                setAllowLocalTransactions(true);
060                setEnableJdbc4ConnectionTest(true);
061        }
062
063        @Override
064        public synchronized void init() {
065                source.set(this);
066                try {
067                        super.init();
068                }
069                finally {
070                        source.remove();
071                }
072        }
073
074        @Override
075        public void setBeanName(String name) {
076                this.beanName = name;
077        }
078
079        @Override
080        public void afterPropertiesSet() throws Exception {
081                if (!StringUtils.hasLength(getUniqueName())) {
082                        setUniqueName(this.beanName);
083                }
084        }
085
086        /**
087         * Set the {@link XADataSource} directly, instead of calling
088         * {@link #setClassName(String)}.
089         * @param dataSource the data source to use
090         */
091        public void setDataSource(XADataSource dataSource) {
092                this.dataSource = dataSource;
093                setClassName(DirectXADataSource.class.getName());
094                setDriverProperties(new Properties());
095        }
096
097        protected final XADataSource getDataSource() {
098                return this.dataSource;
099        }
100
101        @Override
102        public XAStatefulHolder createPooledConnection(Object xaFactory, ResourceBean bean)
103                        throws Exception {
104                if (xaFactory instanceof DirectXADataSource) {
105                        xaFactory = ((DirectXADataSource) xaFactory).getDataSource();
106                }
107                return super.createPooledConnection(xaFactory, bean);
108        }
109
110        @Override
111        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
112                try {
113                        return this.getParentLogger();
114                }
115                catch (Exception ex) {
116                        // Work around https://jira.codehaus.org/browse/BTM-134
117                        return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
118                }
119        }
120
121        /**
122         * A {@link XADataSource} implementation that delegates to the {@link ThreadLocal}
123         * {@link PoolingDataSourceBean}.
124         *
125         * @see PoolingDataSourceBean#setDataSource(XADataSource)
126         */
127        public static class DirectXADataSource implements XADataSource {
128
129                private final XADataSource dataSource;
130
131                public DirectXADataSource() {
132                        this.dataSource = source.get().dataSource;
133                }
134
135                @Override
136                public PrintWriter getLogWriter() throws SQLException {
137                        return this.dataSource.getLogWriter();
138                }
139
140                @Override
141                public XAConnection getXAConnection() throws SQLException {
142                        return this.dataSource.getXAConnection();
143                }
144
145                @Override
146                public XAConnection getXAConnection(String user, String password)
147                                throws SQLException {
148                        return this.dataSource.getXAConnection(user, password);
149                }
150
151                @Override
152                public void setLogWriter(PrintWriter out) throws SQLException {
153                        this.dataSource.setLogWriter(out);
154                }
155
156                @Override
157                public void setLoginTimeout(int seconds) throws SQLException {
158                        this.dataSource.setLoginTimeout(seconds);
159                }
160
161                @Override
162                public int getLoginTimeout() throws SQLException {
163                        return this.dataSource.getLoginTimeout();
164                }
165
166                @Override
167                public Logger getParentLogger() throws SQLFeatureNotSupportedException {
168                        return this.dataSource.getParentLogger();
169                }
170
171                public XADataSource getDataSource() {
172                        return this.dataSource;
173                }
174
175        }
176
177}