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