Skip to content

Commit 61952d4

Browse files
committed
Added MySQL Servlet connectivity sample.
Fix checkstyle violations.
1 parent 89ca142 commit 61952d4

File tree

8 files changed

+621
-1
lines changed

8 files changed

+621
-1
lines changed

cloud-sql/mysql/servlet/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Connecting to Cloud SQL - MySQL
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).
7+
8+
1. Create a 2nd Gen Cloud SQL Instance by following these
9+
[instructions](https://cloud.google.com/sql/docs/mysql/create-instance). Note the connection string,
10+
database user, and database password that you create.
11+
12+
1. Create a database for your application by following these
13+
[instructions](https://cloud.google.com/sql/docs/mysql/create-manage-databases). Note the database
14+
name.
15+
16+
1. Create a service account with the 'Cloud SQL Client' permissions by following these
17+
[instructions](https://cloud.google.com/sql/docs/mysql/connect-external-app#4_if_required_by_your_authentication_method_create_a_service_account).
18+
Download a JSON key to use to authenticate your connection.
19+
20+
1. Use the information noted in the previous steps:
21+
```bash
22+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
23+
export CLOUD_SQL_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>'
24+
export DB_USER='my-db-user'
25+
export DB_PASS='my-db-pass'
26+
export DB_NAME='my_db'
27+
```
28+
29+
## Deploying locally
30+
31+
To run this application locally, run the following command inside the project folder:
32+
33+
```bash
34+
mvn jetty:run
35+
```
36+
37+
Navigate towards `http://127.0.0.1:8080` to verify your application is running correctly.
38+
39+
## Google AppEngine-Standard
40+
41+
To run on GAE-Standard, create an AppEngine project by following the setup for these
42+
[instructions](https://cloud.google.com/appengine/docs/standard/java/quickstart#before-you-begin)
43+
and verify that `appengine-api-1.0-sdk` is listed as a dependency in the pom.xml.
44+
45+
46+
### Development Server
47+
48+
The following command will run the application locally in the the GAE-development server:
49+
```bash
50+
mvn appengine:run
51+
```
52+
53+
### Deploy to Google Cloud
54+
55+
First, update `src/main/webapp/WEB-INF/appengine-web.xml` with the correct values to pass the
56+
environment variables into the runtime.
57+
58+
Next, the following command will deploy the application to your Google Cloud project:
59+
```bash
60+
mvn appengine:deploy
61+
```

cloud-sql/mysql/servlet/pom.xml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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</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+
41+
<!-- Required only for AppEngine Standard -->
42+
<dependency>
43+
<groupId>com.google.appengine</groupId>
44+
<artifactId>appengine-api-1.0-sdk</artifactId>
45+
<version>1.9.64</version>
46+
</dependency>
47+
48+
<dependency>
49+
<groupId>javax.servlet</groupId>
50+
<artifactId>javax.servlet-api</artifactId>
51+
<version>3.1.0</version>
52+
<type>jar</type>
53+
<scope>provided</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>mysql</groupId>
57+
<artifactId>mysql-connector-java</artifactId>
58+
<version>8.0.11</version>
59+
</dependency>
60+
<dependency>
61+
<groupId>com.google.cloud.sql</groupId>
62+
<artifactId>mysql-socket-factory-connector-j-8</artifactId>
63+
<version>1.0.11</version>
64+
</dependency>
65+
<dependency>
66+
<groupId>com.zaxxer</groupId>
67+
<artifactId>HikariCP</artifactId>
68+
<version>3.1.0</version>
69+
</dependency>
70+
</dependencies>
71+
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.eclipse.jetty</groupId>
76+
<artifactId>jetty-maven-plugin</artifactId>
77+
<version>9.4.10.v20180503</version>
78+
<configuration>
79+
<scanIntervalSeconds>1</scanIntervalSeconds>
80+
</configuration>
81+
</plugin>
82+
<plugin>
83+
<groupId>com.google.cloud.tools</groupId>
84+
<artifactId>appengine-maven-plugin</artifactId>
85+
<version>1.3.2</version>
86+
</plugin>
87+
</plugins>
88+
</build>
89+
</project>
Lines changed: 138 additions & 0 deletions
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 javax.servlet.ServletContextEvent;
25+
import javax.servlet.ServletContextListener;
26+
import javax.servlet.annotation.WebListener;
27+
import javax.sql.DataSource;
28+
29+
@WebListener
30+
public class ConnectionPoolContextListener implements ServletContextListener {
31+
32+
private static final String CLOUD_SQL_INSTANCE_NAME = System.getenv("CLOUD_SQL_INSTANCE_NAME");
33+
private static final String DB_USER = System.getenv("DB_USER");
34+
private static final String DB_PASS = System.getenv("DB_PASS");
35+
private static final String DB_NAME = System.getenv("DB_NAME");
36+
37+
private DataSource mysqlConnectionPool() {
38+
// [START cloud_sql_mysql_connection_pool]
39+
// The configuration object specifies behaviors for the connection pool.
40+
HikariConfig config = new HikariConfig();
41+
42+
// Configure which instance and what database user to connect with.
43+
config.setJdbcUrl(String.format("jdbc:mysql:///%s", DB_NAME));
44+
config.setUsername(DB_USER); // e.g. "root", "postgres"
45+
config.setPassword(DB_PASS); // e.g. "my-password"
46+
47+
// For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections.
48+
// See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details.
49+
config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
50+
config.addDataSourceProperty("cloudSqlInstance", CLOUD_SQL_INSTANCE_NAME);
51+
config.addDataSourceProperty("useSSL", "false");
52+
53+
// ... Specify additional connection properties here.
54+
55+
// [START_EXCLUDE]
56+
57+
// [START cloud_sql_max_connections]
58+
// maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal
59+
// values for this setting are highly variable on app design, infrastructure, and database.
60+
config.setMaximumPoolSize(5);
61+
// [END cloud_sql_max_connections]
62+
63+
// [START cloud_sql_connection_timeout]
64+
// setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout.
65+
// Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an
66+
// SQLException.
67+
config.setConnectionTimeout(10000); // 10 seconds
68+
// [END cloud_sql_connection_timeout]
69+
70+
// [START cloud_sql_connection_backoff]
71+
// Hikari automatically delays between failed connection attempts, eventually reaching a
72+
// maximum delay of `connectionTimeout / 2` between attempts.
73+
// [END cloud_sql_connection_backoff]
74+
75+
// [START cloud_sql_connection_lifetime]
76+
// maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that
77+
// live longer than this many milliseconds will be closed and reestablished between uses.
78+
config.setMaxLifetime(1800000); // 30 minutes
79+
// [END cloud_sql_connection_lifetime]
80+
81+
// [START cloud_sql_idle_connections]
82+
// minimumIdle is the minimum number of idle connections Hikari maintains in the pool.
83+
// Additional connections will be established to meet this value unless the pool is full.
84+
config.setMinimumIdle(5);
85+
// idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that
86+
// sit idle for this many milliseconds are retried if minimumIdle is exceeded.
87+
config.setIdleTimeout(600000); // 10 minutes
88+
// [END cloud_sql_idle_connections]
89+
// [END_EXCLUDE]
90+
91+
// Initialize the connection pool using the configuration object.
92+
DataSource pool = new HikariDataSource(config);
93+
// [END cloud_sql_mysql_connection_pool]
94+
return pool;
95+
}
96+
97+
private void createTableSchema(DataSource pool) {
98+
// Safely attempt to create the table schema.
99+
try (Connection conn = pool.getConnection()) {
100+
PreparedStatement createTableStatement = conn.prepareStatement(
101+
"CREATE TABLE IF NOT EXISTS votes ( "
102+
+ "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, canidate CHAR(6) NOT NULL, "
103+
+ "PRIMARY KEY (vote_id) );"
104+
);
105+
createTableStatement.execute();
106+
} catch (SQLException e) {
107+
throw new Error(
108+
"Unable to successfully verify table schema. Please double check the steps in the README"
109+
+ " and restart the application. \n" + e.toString());
110+
}
111+
}
112+
113+
@Override
114+
public void contextDestroyed(ServletContextEvent event) {
115+
// This function is called when the Servlet is destroyed.
116+
DataSource pool = (DataSource) event.getServletContext().getAttribute("my-pool");
117+
if (pool != null) {
118+
try {
119+
pool.unwrap(HikariDataSource.class).close();
120+
} catch (SQLException e) {
121+
// Handle exception
122+
System.out.println("Any error occurred while the application was shutting down: " + e);
123+
}
124+
}
125+
}
126+
127+
@Override
128+
public void contextInitialized(ServletContextEvent event) {
129+
// This function is called when the application starts and will safely create a connection pool
130+
// that can be used to connect to.
131+
DataSource pool = (DataSource) event.getServletContext().getAttribute("my-pool");
132+
if (pool == null) {
133+
pool = mysqlConnectionPool();
134+
event.getServletContext().setAttribute("my-pool", pool);
135+
}
136+
createTableSchema(pool);
137+
}
138+
}

0 commit comments

Comments
 (0)