Skip to content

Commit 3d6f7be

Browse files
authored
Add Cloud SQL Postgres connectivity sample for servlets. (GoogleCloudPlatform#1255)
* Add Cloud SQL Prostgres connectivity sample for serverlets. * Add MySQL prefix to region tags.
1 parent ffdb971 commit 3d6f7be

File tree

11 files changed

+637
-13
lines changed

11 files changed

+637
-13
lines changed

cloud-sql/mysql/servlet/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<packaging>war</packaging>
1919
<version>1.0-SNAPSHOT</version>
2020
<groupId>com.example.cloudsql</groupId>
21-
<artifactId>tabs-vs-spaces</artifactId>
21+
<artifactId>tabs-vs-spaces-mysql</artifactId>
2222

2323
<!--
2424
The parent pom defines common style checks and testing strategies for our samples.

cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -59,37 +59,37 @@ private DataSource createConnectionPool() {
5959

6060
// [START_EXCLUDE]
6161

62-
// [START cloud_sql_limit_connections]
62+
// [START cloud_sql_mysql_limit_connections]
6363
// maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal
6464
// values for this setting are highly variable on app design, infrastructure, and database.
6565
config.setMaximumPoolSize(5);
6666
// minimumIdle is the minimum number of idle connections Hikari maintains in the pool.
6767
// Additional connections will be established to meet this value unless the pool is full.
6868
config.setMinimumIdle(5);
69-
// [END cloud_sql_limit_connections]
69+
// [END cloud_sql_mysql_limit_connections]
7070

71-
// [START cloud_sql_connection_timeout]
71+
// [START cloud_sql_mysql_connection_timeout]
7272
// setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout.
7373
// Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an
7474
// SQLException.
7575
config.setConnectionTimeout(10000); // 10 seconds
7676
// idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that
7777
// sit idle for this many milliseconds are retried if minimumIdle is exceeded.
7878
config.setIdleTimeout(600000); // 10 minutes
79-
// [END cloud_sql_connection_timeout]
79+
// [END cloud_sql_mysql_connection_timeout]
8080

81-
// [START cloud_sql_connection_backoff]
81+
// [START cloud_sql_mysql_connection_backoff]
8282
// Hikari automatically delays between failed connection attempts, eventually reaching a
8383
// maximum delay of `connectionTimeout / 2` between attempts.
84-
// [END cloud_sql_connection_backoff]
84+
// [END cloud_sql_mysql_connection_backoff]
8585

86-
// [START cloud_sql_connection_lifetime]
86+
// [START cloud_sql_mysql_connection_lifetime]
8787
// maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that
8888
// live longer than this many milliseconds will be closed and reestablished between uses. This
8989
// value should be several minutes shorter than the database's timeout value to avoid unexpected
9090
// terminations.
9191
config.setMaxLifetime(1800000); // 30 minutes
92-
// [END cloud_sql_connection_lifetime]
92+
// [END cloud_sql_mysql_connection_lifetime]
9393

9494
// [END_EXCLUDE]
9595

cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/IndexServlet.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp)
8585
throw new ServletException("Unable to successfully connect to the database. Please check the "
8686
+ "steps in the README and try again.", ex);
8787
}
88-
// [END cloud_sql_example_query]
8988

9089
// Add variables and render the page
9190
req.setAttribute("tabCount", tabCount);
@@ -111,7 +110,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp)
111110

112111
// Reuse the pool that was created in the ContextListener when the Servlet started.
113112
DataSource pool = (DataSource) req.getServletContext().getAttribute("my-pool");
114-
// [START cloud_sql_example_statement]
113+
// [START cloud_sql_mysql_example_statement]
115114
// Using a try-with-resources statement ensures that the connection is always released back
116115
// into the pool at the end of the statement (even if an error occurs)
117116
try (Connection conn = pool.getConnection()) {
@@ -135,7 +134,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp)
135134
+ "logs for more details.");
136135
// [END_EXCLUDE]
137136
}
138-
// [END cloud_sql_example_statement]
137+
// [END cloud_sql_mysql_example_statement]
139138

140139
resp.setStatus(200);
141140
resp.getWriter().printf("Vote successfully cast for '%s' at time %s!\n", team, now);

cloud-sql/postgres/servlet/README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Connecting to Cloud SQL - Postgres
2+
3+
## Before you begin
4+
5+
1. If you haven't already, set up a Java Development Environment (including google-cloud-sdk and
6+
maven utilities) by following the [java setup guide](https://cloud.google.com/java/docs/setup) and
7+
[create a project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project).
8+
9+
1. Create a 2nd Gen Cloud SQL Instance by following these
10+
[instructions](https://cloud.google.com/sql/docs/postgres/create-instance). Note the connection string,
11+
database user, and database password that you create.
12+
13+
1. Create a database for your application by following these
14+
[instructions](https://cloud.google.com/sql/docs/postgres/create-manage-databases). Note the database
15+
name.
16+
17+
1. Create a service account with the 'Cloud SQL Client' permissions by following these
18+
[instructions](https://cloud.google.com/sql/docs/postgres/connect-external-app#4_if_required_by_your_authentication_method_create_a_service_account).
19+
Download a JSON key to use to authenticate your connection.
20+
21+
1. Use the information noted in the previous steps:
22+
```bash
23+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
24+
export CLOUD_SQL_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>'
25+
export DB_USER='my-db-user'
26+
export DB_PASS='my-db-pass'
27+
export DB_NAME='my_db'
28+
```
29+
Note: Saving credentials in environment variables is convenient, but not secure - consider a more
30+
secure solution such as [Cloud KMS](https://cloud.google.com/kms/) to help keep secrets safe.
31+
32+
## Deploying locally
33+
34+
To run this application locally, run the following command inside the project folder:
35+
36+
```bash
37+
mvn jetty:run
38+
```
39+
40+
Navigate towards `http://127.0.0.1:8080` to verify your application is running correctly.
41+
42+
## Google App Engine Standard
43+
44+
To run on GAE-Standard, create an AppEngine project by following the setup for these
45+
[instructions](https://cloud.google.com/appengine/docs/standard/java/quickstart#before-you-begin)
46+
and verify that
47+
[appengine-maven-plugin](https://cloud.google.com/java/docs/setup#optional_install_maven_or_gradle_plugin_for_app_engine)
48+
has been added in your build section as a plugin.
49+
50+
51+
### Development Server
52+
53+
The following command will run the application locally in the the GAE-development server:
54+
```bash
55+
mvn appengine:run
56+
```
57+
58+
### Deploy to Google Cloud
59+
60+
First, update `src/main/webapp/WEB-INF/appengine-web.xml` with the correct values to pass the
61+
environment variables into the runtime.
62+
63+
Next, the following command will deploy the application to your Google Cloud project:
64+
```bash
65+
mvn appengine:deploy
66+
```

cloud-sql/postgres/servlet/pom.xml

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!--
2+
Copyright 2018 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
<project>
17+
<modelVersion>4.0.0</modelVersion>
18+
<packaging>war</packaging>
19+
<version>1.0-SNAPSHOT</version>
20+
<groupId>com.example.cloudsql</groupId>
21+
<artifactId>tabs-vs-spaces-postgres</artifactId>
22+
23+
<!--
24+
The parent pom defines common style checks and testing strategies for our samples.
25+
Removing or replacing it should not affect the execution of the samples in anyway.
26+
-->
27+
<parent>
28+
<groupId>com.google.cloud.samples</groupId>
29+
<artifactId>shared-configuration</artifactId>
30+
<version>1.0.10</version>
31+
</parent>
32+
33+
<properties>
34+
<maven.compiler.target>1.8</maven.compiler.target>
35+
<maven.compiler.source>1.8</maven.compiler.source>
36+
<failOnMissingWebXml>false</failOnMissingWebXml>
37+
</properties>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>javax.servlet</groupId>
42+
<artifactId>javax.servlet-api</artifactId>
43+
<version>3.1.0</version>
44+
<type>jar</type>
45+
<scope>provided</scope>
46+
</dependency>
47+
<dependency>
48+
<groupId>javax.servlet</groupId>
49+
<artifactId>jstl</artifactId>
50+
<version>1.2</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.postgresql</groupId>
54+
<artifactId>postgresql</artifactId>
55+
<version>42.2.5</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>com.google.cloud.sql</groupId>
59+
<artifactId>postgres-socket-factory</artifactId>
60+
<version>1.0.11</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.zaxxer</groupId>
64+
<artifactId>HikariCP</artifactId>
65+
<version>3.1.0</version>
66+
</dependency>
67+
</dependencies>
68+
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.eclipse.jetty</groupId>
73+
<artifactId>jetty-maven-plugin</artifactId>
74+
<version>9.4.10.v20180503</version>
75+
<configuration>
76+
<scanIntervalSeconds>1</scanIntervalSeconds>
77+
</configuration>
78+
</plugin>
79+
<!-- Only required for AppEngine Deployments -->
80+
<plugin>
81+
<groupId>com.google.cloud.tools</groupId>
82+
<artifactId>appengine-maven-plugin</artifactId>
83+
<version>1.3.2</version>
84+
</plugin>
85+
</plugins>
86+
</build>
87+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2018 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.cloudsql;
18+
19+
import com.zaxxer.hikari.HikariConfig;
20+
import com.zaxxer.hikari.HikariDataSource;
21+
import java.sql.Connection;
22+
import java.sql.PreparedStatement;
23+
import java.sql.SQLException;
24+
import java.util.logging.Logger;
25+
import javax.servlet.ServletContextEvent;
26+
import javax.servlet.ServletContextListener;
27+
import javax.servlet.annotation.WebListener;
28+
import javax.sql.DataSource;
29+
30+
@WebListener("Creates a connection pool that is stored in the Servlet's context for later use.")
31+
public class ConnectionPoolContextListener implements ServletContextListener {
32+
33+
private static final Logger LOGGER = Logger.getLogger(IndexServlet.class.getName());
34+
35+
// Saving credentials in environment variables is convenient, but not secure - consider a more
36+
// secure solution such as https://cloud.google.com/kms/ to help keep secrets safe.
37+
private static final String CLOUD_SQL_INSTANCE_NAME = System.getenv("CLOUD_SQL_INSTANCE_NAME");
38+
private static final String DB_USER = System.getenv("DB_USER");
39+
private static final String DB_PASS = System.getenv("DB_PASS");
40+
private static final String DB_NAME = System.getenv("DB_NAME");
41+
42+
private DataSource createConnectionPool() {
43+
// [START cloud_sql_postgres_connection_pool]
44+
// The configuration object specifies behaviors for the connection pool.
45+
HikariConfig config = new HikariConfig();
46+
47+
// Configure which instance and what database user to connect with.
48+
config.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME));
49+
config.setUsername(DB_USER); // e.g. "root", "postgres"
50+
config.setPassword(DB_PASS); // e.g. "my-password"
51+
52+
// For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections.
53+
// See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details.
54+
config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
55+
config.addDataSourceProperty("cloudSqlInstance", CLOUD_SQL_INSTANCE_NAME);
56+
57+
// ... Specify additional connection properties here.
58+
59+
// [START_EXCLUDE]
60+
61+
// [START cloud_sql_postgres_limit_connections]
62+
// maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal
63+
// values for this setting are highly variable on app design, infrastructure, and database.
64+
config.setMaximumPoolSize(5);
65+
// minimumIdle is the minimum number of idle connections Hikari maintains in the pool.
66+
// Additional connections will be established to meet this value unless the pool is full.
67+
config.setMinimumIdle(5);
68+
// [END cloud_sql_postgres_limit_connections]
69+
70+
// [START cloud_sql_postgres_connection_timeout]
71+
// setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout.
72+
// Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an
73+
// SQLException.
74+
config.setConnectionTimeout(10000); // 10 seconds
75+
// idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that
76+
// sit idle for this many milliseconds are retried if minimumIdle is exceeded.
77+
config.setIdleTimeout(600000); // 10 minutes
78+
// [END cloud_sql_postgres_connection_timeout]
79+
80+
// [START cloud_sql_postgres_connection_backoff]
81+
// Hikari automatically delays between failed connection attempts, eventually reaching a
82+
// maximum delay of `connectionTimeout / 2` between attempts.
83+
// [END cloud_sql_postgres_connection_backoff]
84+
85+
// [START cloud_sql_postgres_connection_lifetime]
86+
// maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that
87+
// live longer than this many milliseconds will be closed and reestablished between uses. This
88+
// value should be several minutes shorter than the database's timeout value to avoid unexpected
89+
// terminations.
90+
config.setMaxLifetime(1800000); // 30 minutes
91+
// [END cloud_sql_postgres_connection_lifetime]
92+
93+
// [END_EXCLUDE]
94+
95+
// Initialize the connection pool using the configuration object.
96+
DataSource pool = new HikariDataSource(config);
97+
// [END cloud_sql_postgres_connection_pool]
98+
return pool;
99+
}
100+
101+
private void createTable(DataSource pool) throws SQLException {
102+
// Safely attempt to create the table schema.
103+
try (Connection conn = pool.getConnection()) {
104+
PreparedStatement createTableStatement = conn.prepareStatement(
105+
"CREATE TABLE IF NOT EXISTS votes ( "
106+
+ "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, candidate CHAR(6) NOT NULL,"
107+
+ " PRIMARY KEY (vote_id) );"
108+
);
109+
createTableStatement.execute();
110+
}
111+
}
112+
113+
@Override
114+
public void contextDestroyed(ServletContextEvent event) {
115+
// This function is called when the Servlet is destroyed.
116+
HikariDataSource pool = (HikariDataSource) event.getServletContext().getAttribute("my-pool");
117+
if (pool != null) {
118+
pool.close();
119+
}
120+
}
121+
122+
@Override
123+
public void contextInitialized(ServletContextEvent event) {
124+
// This function is called when the application starts and will safely create a connection pool
125+
// that can be used to connect to.
126+
DataSource pool = (DataSource) event.getServletContext().getAttribute("my-pool");
127+
if (pool == null) {
128+
pool = createConnectionPool();
129+
event.getServletContext().setAttribute("my-pool", pool);
130+
}
131+
try {
132+
createTable(pool);
133+
} catch (SQLException ex) {
134+
throw new RuntimeException("Unable to verify table schema. Please double check the steps"
135+
+ "in the README and try again.", ex);
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)