View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  
15  package de.softwareforge.testing.postgres.embedded;
16  
17  import static com.google.common.base.Preconditions.checkState;
18  import static java.lang.String.format;
19  
20  import jakarta.annotation.Nonnull;
21  import java.sql.SQLException;
22  import java.util.Optional;
23  import java.util.stream.Collectors;
24  import javax.sql.DataSource;
25  
26  import com.google.auto.value.AutoValue;
27  import com.google.common.annotations.VisibleForTesting;
28  import com.google.common.collect.ImmutableMap;
29  
30  /**
31   * Information about a database located on a PostgreSQL server connected to an {@link EmbeddedPostgres} instance.
32   */
33  @AutoValue
34  public abstract class DatabaseInfo {
35  
36      private static final String JDBC_FORMAT = "jdbc:postgresql://localhost:%d/%s";
37  
38      DatabaseInfo() {
39      }
40  
41      /**
42       * The default user used for databases.
43       */
44      static final String PG_DEFAULT_USER = "postgres";
45  
46      /**
47       * The default database name.
48       */
49      static final String PG_DEFAULT_DB = "postgres";
50  
51      /**
52       * Returns the name of the database.
53       *
54       * @return Name of the database. Is never null.
55       */
56      @Nonnull
57      public abstract String dbName();
58  
59      /**
60       * Returns the TCP port for the database server.
61       *
62       * @return A port number. May be -1 if this objects represents an error connection.
63       */
64      public abstract int port();
65  
66      /**
67       * Returns the user that can connect to this database.
68       *
69       * @return The user name. Is never null.
70       */
71      @Nonnull
72      public abstract String user();
73  
74      /**
75       * Returns all properties that are be applied to a new data source connection to this database. See
76       * <a href="https://jdbc.postgresql.org/documentation/head/connect.html#connection-parameters">the
77       * PostgreSQL JDBC driver documentation</a> for a comprehensive list.
78       *
79       * @return Map of key-value pairs representing data source connection properties.
80       * @since 3.0
81       */
82      @Nonnull
83      public abstract ImmutableMap<String, String> connectionProperties();
84  
85      @Nonnull
86      abstract Optional<SQLException> exception();
87  
88      @Nonnull
89      static Builder builder() {
90          return new AutoValue_DatabaseInfo.Builder()
91                  .dbName(PG_DEFAULT_DB)
92                  .user(PG_DEFAULT_USER);
93      }
94  
95      @Nonnull
96      static DatabaseInfo forException(SQLException e) {
97          return builder().exception(e).port(-1).build();
98      }
99  
100     @VisibleForTesting
101     String getBaseUrl() {
102         return format(JDBC_FORMAT, port(), dbName());
103     }
104 
105     @VisibleForTesting
106     String getAdditionalParameters() {
107         return connectionProperties().entrySet().stream()
108                 .map(e -> format("%s=%s", e.getKey(), e.getValue()))
109                 .collect(Collectors.joining("&"));
110     }
111 
112     /**
113      * Returns a JDBC url to connect to the described database.
114      *
115      * @return A JDBC url that can be used to connect to the database. Never null.
116      */
117     @Nonnull
118     public String asJdbcUrl() {
119         checkState(exception().isEmpty(), "DatabaseInfo contains SQLException: %s", exception());
120         var parameters = getAdditionalParameters();
121         return parameters.isEmpty() ? getBaseUrl() : getBaseUrl() + "?" + parameters;
122     }
123 
124     /**
125      * Returns a {@link DataSource} instance connected to the described database.
126      *
127      * @return An initialized {@link DataSource} object. Never null.
128      * @throws SQLException A problem occurred trying to connect to the database.
129      */
130     @Nonnull
131     public DataSource asDataSource() throws SQLException {
132         if (exception().isPresent()) {
133             throw exception().get();
134         }
135 
136         return EmbeddedPostgres.createDataSource(user(), dbName(), port(), connectionProperties());
137     }
138 
139     @AutoValue.Builder
140     abstract static class Builder {
141 
142         abstract Builder dbName(String dbName);
143 
144         abstract Builder port(int port);
145 
146         abstract Builder user(String user);
147 
148         abstract Builder exception(SQLException exception);
149 
150         abstract ImmutableMap.Builder<String, String> connectionPropertiesBuilder();
151 
152         final Builder addConnectionProperty(String key, String value) {
153             connectionPropertiesBuilder().put(key, value);
154             return this;
155         }
156 
157         abstract Builder connectionProperties(ImmutableMap<String, String> connectionProperties);
158 
159         abstract DatabaseInfo build();
160 
161     }
162 }