001/*
002 * Copyright 2012-2016 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.autoconfigure.jdbc;
018
019import java.sql.Connection;
020import java.sql.SQLException;
021
022import javax.sql.DataSource;
023
024import org.springframework.dao.DataAccessException;
025import org.springframework.jdbc.core.ConnectionCallback;
026import org.springframework.jdbc.core.JdbcTemplate;
027import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
028import org.springframework.util.Assert;
029import org.springframework.util.ClassUtils;
030
031/**
032 * Connection details for {@link EmbeddedDatabaseType embedded databases}.
033 *
034 * @author Phillip Webb
035 * @author Dave Syer
036 * @author Stephane Nicoll
037 * @see #get(ClassLoader)
038 */
039public enum EmbeddedDatabaseConnection {
040
041        /**
042         * No Connection.
043         */
044        NONE(null, null, null),
045
046        /**
047         * H2 Database Connection.
048         */
049        H2(EmbeddedDatabaseType.H2, "org.h2.Driver",
050                        "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"),
051
052        /**
053         * Derby Database Connection.
054         */
055        DERBY(EmbeddedDatabaseType.DERBY, "org.apache.derby.jdbc.EmbeddedDriver",
056                        "jdbc:derby:memory:%s;create=true"),
057
058        /**
059         * HSQL Database Connection.
060         */
061        HSQL(EmbeddedDatabaseType.HSQL, "org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s");
062
063        private static final String DEFAULT_DATABASE_NAME = "testdb";
064
065        private final EmbeddedDatabaseType type;
066
067        private final String driverClass;
068
069        private final String url;
070
071        EmbeddedDatabaseConnection(EmbeddedDatabaseType type, String driverClass,
072                        String url) {
073                this.type = type;
074                this.driverClass = driverClass;
075                this.url = url;
076        }
077
078        /**
079         * Returns the driver class name.
080         * @return the driver class name
081         */
082        public String getDriverClassName() {
083                return this.driverClass;
084        }
085
086        /**
087         * Returns the {@link EmbeddedDatabaseType} for the connection.
088         * @return the database type
089         */
090        public EmbeddedDatabaseType getType() {
091                return this.type;
092        }
093
094        /**
095         * Returns the URL for the connection using the default database name.
096         * @return the connection URL
097         */
098        public String getUrl() {
099                return getUrl(DEFAULT_DATABASE_NAME);
100        }
101
102        /**
103         * Returns the URL for the connection using the specified {@code databaseName}.
104         * @param databaseName the name of the database
105         * @return the connection URL
106         */
107        public String getUrl(String databaseName) {
108                Assert.hasText(databaseName, "DatabaseName must not be null.");
109                return this.url != null ? String.format(this.url, databaseName) : null;
110        }
111
112        /**
113         * Override for testing.
114         */
115        static EmbeddedDatabaseConnection override;
116
117        /**
118         * Convenience method to determine if a given driver class name represents an embedded
119         * database type.
120         * @param driverClass the driver class
121         * @return true if the driver class is one of the embedded types
122         */
123        public static boolean isEmbedded(String driverClass) {
124                return driverClass != null && (driverClass.equals(HSQL.driverClass)
125                                || driverClass.equals(H2.driverClass)
126                                || driverClass.equals(DERBY.driverClass));
127        }
128
129        /**
130         * Convenience method to determine if a given data source represents an embedded
131         * database type.
132         * @param dataSource the data source to interrogate
133         * @return true if the data source is one of the embedded types
134         */
135        public static boolean isEmbedded(DataSource dataSource) {
136                try {
137                        return new JdbcTemplate(dataSource).execute(new IsEmbedded());
138                }
139                catch (DataAccessException ex) {
140                        // Could not connect, which means it's not embedded
141                        return false;
142                }
143        }
144
145        /**
146         * Returns the most suitable {@link EmbeddedDatabaseConnection} for the given class
147         * loader.
148         * @param classLoader the class loader used to check for classes
149         * @return an {@link EmbeddedDatabaseConnection} or {@link #NONE}.
150         */
151        public static EmbeddedDatabaseConnection get(ClassLoader classLoader) {
152                if (override != null) {
153                        return override;
154                }
155                for (EmbeddedDatabaseConnection candidate : EmbeddedDatabaseConnection.values()) {
156                        if (candidate != NONE && ClassUtils.isPresent(candidate.getDriverClassName(),
157                                        classLoader)) {
158                                return candidate;
159                        }
160                }
161                return NONE;
162        }
163
164        /**
165         * {@link ConnectionCallback} to determine if a connection is embedded.
166         */
167        private static class IsEmbedded implements ConnectionCallback<Boolean> {
168
169                @Override
170                public Boolean doInConnection(Connection connection)
171                                throws SQLException, DataAccessException {
172                        String productName = connection.getMetaData().getDatabaseProductName();
173                        if (productName == null) {
174                                return false;
175                        }
176                        productName = productName.toUpperCase();
177                        EmbeddedDatabaseConnection[] candidates = EmbeddedDatabaseConnection.values();
178                        for (EmbeddedDatabaseConnection candidate : candidates) {
179                                if (candidate != NONE && productName.contains(candidate.name())) {
180                                        return true;
181                                }
182                        }
183                        return false;
184                }
185
186        }
187
188}