diff --git a/flexible/cloudsql/README.md b/flexible/cloudsql/README.md index d2c3f3e6e67..871f5f9f855 100644 --- a/flexible/cloudsql/README.md +++ b/flexible/cloudsql/README.md @@ -1,17 +1,38 @@ # Cloud SQL sample for Google App Engine Flexible -This sample demonstrates how to use [Cloud SQL](https://cloud.google.com/sql/) on Google App Engine -Flexible. + +This sample demonstrates how to use [Cloud SQL](https://cloud.google.com/cloudsql/) on Google App +Engine Flexible ## Setup -Before you can run or deploy the sample, you will need to create a [Cloud SQL instance)](https://cloud.google.com/sql/docs/create-instance) -1. Create a new user and database for the application. The easiest way to do this is via the [Google -Developers Console](https://console.cloud.google.com/sql/instances). Alternatively, you can use -MySQL tools such as the command line client or workbench. -2. Change the root password (under Access Control) and / or create a new user / password. -3. Create a Database (under Databases) (or use MySQL with `gcloud sql connect --user=root`) -4. Note the **Instance connection name** under Overview > Properties -(It will look like project:region:zone for 2nd Generation) +* If you haven't already, Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) + + `gcloud init` + +* If you haven't already, Create an App Engine app within the current Google Cloud Project + + `gcloud app create` + +* If you haven't already, Setup +[Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) + + `gcloud auth application-default login` + +* [Create an instance](https://cloud.google.com/sql/docs/mysql/create-instance) + +* [Create a Database](https://cloud.google.com/sql/docs/mysql/create-manage-databases) + +* [Create a user](https://cloud.google.com/sql/docs/mysql/create-manage-users) + +* Note the **Instance connection name** under Overview > properties + +Looks like: `projectID:region:instance` + +## Running locally + +```bash +$ mvn clean jetty:run -DINSTANCE_CONNECTION_NAME=instanceConnectionName -Duser=root -Dpassword=myPassowrd -Ddatabase=myDatabase +``` ## Deploying @@ -20,10 +41,8 @@ $ mvn clean appengine:deploy -DINSTANCE_CONNECTION_NAME=instanceConnectionName - -Dpassword=myPassword -Ddatabase=myDatabase ``` -Or you can update the properties in `pom.xml` -## Running locally +## Cleaning up + +* [Delete your Instance](https://cloud.google.com/sql/docs/mysql/delete-instance) -```bash -$ mvn clean jetty:run -DINSTANCE_CONNECTION_NAME=instanceConnectionName -Duser=root -Dpassword=myPassowrd -Ddatabase=myDatabase -``` diff --git a/flexible/cloudsql/pom.xml b/flexible/cloudsql/pom.xml index 0890ba4a03c..d82f96512e6 100644 --- a/flexible/cloudsql/pom.xml +++ b/flexible/cloudsql/pom.xml @@ -29,13 +29,13 @@ - - + Project:Region:Instance root - + myPassword sqldemo 1.8 @@ -43,7 +43,6 @@ false - 1.3.1 9.4.4.v20170414 jdbc:mysql://google/${database}?cloudSqlInstance=${INSTANCE_CONNECTION_NAME}&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=${user}&password=${password}&useSSL=false @@ -82,7 +81,7 @@ com.google.cloud.sql mysql-socket-factory-connector-j-6 - 1.0.2 + 1.0.3 @@ -97,28 +96,10 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes - - - com.google.cloud.tools appengine-maven-plugin - ${appengine.maven.plugin} + 1.3.1 diff --git a/flexible/cloudsql/src/main/java/com/example/cloudsql/CloudSqlServlet.java b/flexible/cloudsql/src/main/java/com/example/cloudsql/CloudSqlServlet.java index 45558bb24f8..7e55cb23bee 100644 --- a/flexible/cloudsql/src/main/java/com/example/cloudsql/CloudSqlServlet.java +++ b/flexible/cloudsql/src/main/java/com/example/cloudsql/CloudSqlServlet.java @@ -16,6 +16,8 @@ package com.example.cloudsql; +import com.google.common.base.Stopwatch; + import java.io.IOException; import java.io.PrintWriter; import java.net.Inet4Address; @@ -29,6 +31,7 @@ import java.sql.Timestamp; import java.util.Date; import java.util.Properties; +import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; @@ -40,47 +43,53 @@ @SuppressWarnings("serial") @WebServlet(name = "cloudsql", value = "") public class CloudSqlServlet extends HttpServlet { - String url; + Connection conn; @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - // store only the first two octets of a users ip address - String userIp = req.getRemoteAddr(); - InetAddress address = InetAddress.getByName(userIp); - if (address instanceof Inet6Address) { - // nest indexOf calls to find the second occurrence of a character in a - // string - // an alternative is to use Apache Commons Lang: - // StringUtils.ordinalIndexOf() - userIp = userIp.substring(0, userIp.indexOf(":", userIp.indexOf(":") + 1)) + ":*:*:*:*:*:*"; - } else if (address instanceof Inet4Address) { - userIp = userIp.substring(0, userIp.indexOf(".", userIp.indexOf(".") + 1)) + ".*.*"; - } - final String createTableSql = "CREATE TABLE IF NOT EXISTS visits ( visit_id INT NOT NULL " + "AUTO_INCREMENT, user_ip VARCHAR(46) NOT NULL, timestamp DATETIME NOT NULL, " + "PRIMARY KEY (visit_id) )"; final String createVisitSql = "INSERT INTO visits (user_ip, timestamp) VALUES (?, ?)"; final String selectSql = "SELECT user_ip, timestamp FROM visits ORDER BY timestamp DESC " + "LIMIT 10"; + + String path = req.getRequestURI(); + if (path.startsWith("/favicon.ico")) { + return; // ignore the request for favicon.ico + } + PrintWriter out = resp.getWriter(); resp.setContentType("text/plain"); - try (Connection conn = DriverManager.getConnection(url); - PreparedStatement statementCreateVisit = conn.prepareStatement(createVisitSql)) { + // store only the first two octets of a users ip address + String userIp = req.getRemoteAddr(); + InetAddress address = InetAddress.getByName(userIp); + if (address instanceof Inet6Address) { + // nest indexOf calls to find the second occurrence of a character in a string + // an alternative is to use Apache Commons Lang: StringUtils.ordinalIndexOf() + userIp = userIp.substring(0, userIp.indexOf(":", userIp.indexOf(":") + 1)) + ":*:*:*:*:*:*"; + } else if (address instanceof Inet4Address) { + userIp = userIp.substring(0, userIp.indexOf(".", userIp.indexOf(".") + 1)) + ".*.*"; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + try (PreparedStatement statementCreateVisit = conn.prepareStatement(createVisitSql)) { conn.createStatement().executeUpdate(createTableSql); statementCreateVisit.setString(1, userIp); statementCreateVisit.setTimestamp(2, new Timestamp(new Date().getTime())); statementCreateVisit.executeUpdate(); try (ResultSet rs = conn.prepareStatement(selectSql).executeQuery()) { + stopwatch.stop(); out.print("Last 10 visits:\n"); while (rs.next()) { String savedIp = rs.getString("user_ip"); String timeStamp = rs.getString("timestamp"); out.print("Time: " + timeStamp + " Addr: " + savedIp + "\n"); } + out.println("Elapsed: " + stopwatch.elapsed(TimeUnit.MILLISECONDS)); } } catch (SQLException e) { throw new ServletException("SQL error", e); @@ -88,14 +97,32 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc } @Override - public void init() { + public void init() throws ServletException { try { + String url; + Properties properties = new Properties(); - properties.load( - getServletContext().getResourceAsStream("/WEB-INF/classes/config.properties")); - url = properties.getProperty("sqlUrl"); - } catch (IOException e) { - log("no property", e); // Servlet Init should never fail. + try { + properties.load( + getServletContext().getResourceAsStream("/WEB-INF/classes/config.properties")); + url = properties.getProperty("sqlUrl"); + } catch (IOException e) { + log("no property", e); // Servlet Init should never fail. + return; + } + + log("connecting to: " + url); + try { + Class.forName("com.mysql.jdbc.Driver"); + conn = DriverManager.getConnection(url); + } catch (ClassNotFoundException e) { + throw new ServletException("Error loading JDBC Driver", e); + } catch (SQLException e) { + throw new ServletException("Unable to connect to PostGre", e); + } + + } finally { + // Nothing really to do here. } } } diff --git a/flexible/postgres/.gitignore b/flexible/postgres/.gitignore new file mode 100644 index 00000000000..83926cdbcaa --- /dev/null +++ b/flexible/postgres/.gitignore @@ -0,0 +1,2 @@ +/bin/ +target diff --git a/flexible/postgres/README.md b/flexible/postgres/README.md new file mode 100644 index 00000000000..72474868eb0 --- /dev/null +++ b/flexible/postgres/README.md @@ -0,0 +1,48 @@ +# PostgreSQL sample for Google App Engine Flexible + +This sample demonstrates how to use [Cloud SQL](https://cloud.google.com/sql/) on Google App +Engine Flexible + +## Setup + +* If you haven't already, Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) + + `gcloud init` + +* If you haven't already, Create an App Engine app within the current Google Cloud Project + + `gcloud app create` + +* If you haven't already, Setup +[Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) + + `gcloud auth application-default login` + +* [Create an instance](https://cloud.google.com/sql/docs/postgresql/create-instance) + +* [Create a Database](https://cloud.google.com/sql/docs/postgresql/create-manage-databases) + +* [Create a user](https://cloud.google.com/sql/docs/postgresql/create-manage-users) + +* Note the **Instance connection name** under Overview > properties + +Looks like: `projectID:region:instance` + +## Running locally + +```bash +$ mvn clean jetty:run -DINSTANCE_CONNECTION_NAME=instanceConnectionName -Duser=root -Dpassword=myPassowrd -Ddatabase=myDatabase +``` + +## Deploying + +```bash +$ mvn clean appengine:deploy -DINSTANCE_CONNECTION_NAME=instanceConnectionName -Duser=root +-Dpassword=myPassword -Ddatabase=myDatabase +``` + + +## Cleaning up + +* [Delete your Instance](https://cloud.google.com/sql/docs/postgresql/delete-instance) + diff --git a/flexible/postgres/pom.xml b/flexible/postgres/pom.xml new file mode 100644 index 00000000000..72e004e5551 --- /dev/null +++ b/flexible/postgres/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.flexible + flexible-postgres + + + doc-samples + com.google.cloud + 1.0.0 + ../.. + + + + + + Project:Region:Instance + root + myPassword + sqldemo + + 1.8 + 1.8 + + false + + 9.4.4.v20170414 + + jdbc:postgresql://google/${database}?useSSL=false&socketFactoryArg=${INSTANCE_CONNECTION_NAME}&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=${user}&password=${password} + + + + + + com.google.api-client + google-api-client + 1.22.0 + + + com.google.api-client + google-api-client-appengine + 1.22.0 + + + com.google.api-client + google-api-client-servlet + 1.22.0 + + + javax.servlet + javax.servlet-api + 3.1.0 + jar + provided + + + + org.postgresql + postgresql + 42.1.1 + + + + com.google.cloud.sql + postgres-socket-factory + 1.0.3 + + + + + + + src/main/resources + true + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + + com.google.cloud.tools + appengine-maven-plugin + 1.3.1 + + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty} + + + + diff --git a/flexible/postgres/src/main/appengine/app.yaml b/flexible/postgres/src/main/appengine/app.yaml new file mode 100644 index 00000000000..ca251053293 --- /dev/null +++ b/flexible/postgres/src/main/appengine/app.yaml @@ -0,0 +1,24 @@ +# Copyright 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +runtime: java +env: flex + +handlers: +- url: /.* + script: this field is required, but ignored + +automatic_scaling: + min_num_instances: 1 + max_num_instances: 2 diff --git a/flexible/postgres/src/main/java/com/example/postgres/PostgresSqlServlet.java b/flexible/postgres/src/main/java/com/example/postgres/PostgresSqlServlet.java new file mode 100644 index 00000000000..310e5141035 --- /dev/null +++ b/flexible/postgres/src/main/java/com/example/postgres/PostgresSqlServlet.java @@ -0,0 +1,127 @@ +/** + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.postgres; + +import com.google.common.base.Stopwatch; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +// [START example] +@SuppressWarnings("serial") +@WebServlet(name = "postgresql", value = "") +public class PostgresSqlServlet extends HttpServlet { + Connection conn; + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + + final String createTableSql = "CREATE TABLE IF NOT EXISTS visits ( visit_id SERIAL NOT NULL, " + + "user_ip VARCHAR(46) NOT NULL, ts timestamp NOT NULL, " + + "PRIMARY KEY (visit_id) );"; + final String createVisitSql = "INSERT INTO visits (user_ip, ts) VALUES (?, ?);"; + final String selectSql = "SELECT user_ip, ts FROM visits ORDER BY ts DESC " + + "LIMIT 10;"; + + String path = req.getRequestURI(); + if (path.startsWith("/favicon.ico")) { + return; // ignore the request for favicon.ico + } + + PrintWriter out = resp.getWriter(); + resp.setContentType("text/plain"); + + // store only the first two octets of a users ip address + String userIp = req.getRemoteAddr(); + InetAddress address = InetAddress.getByName(userIp); + if (address instanceof Inet6Address) { + // nest indexOf calls to find the second occurrence of a character in a string + // an alternative is to use Apache Commons Lang: StringUtils.ordinalIndexOf() + userIp = userIp.substring(0, userIp.indexOf(":", userIp.indexOf(":") + 1)) + ":*:*:*:*:*:*"; + } else if (address instanceof Inet4Address) { + userIp = userIp.substring(0, userIp.indexOf(".", userIp.indexOf(".") + 1)) + ".*.*"; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + try (PreparedStatement statementCreateVisit = conn.prepareStatement(createVisitSql)) { + conn.createStatement().executeUpdate(createTableSql); + statementCreateVisit.setString(1, userIp); + statementCreateVisit.setTimestamp(2, new Timestamp(new Date().getTime())); + statementCreateVisit.executeUpdate(); + + try (ResultSet rs = conn.prepareStatement(selectSql).executeQuery()) { + stopwatch.stop(); + out.print("Last 10 visits:\n"); + while (rs.next()) { + String savedIp = rs.getString("user_ip"); + String timeStamp = rs.getString("ts"); + out.println("Time: " + timeStamp + " Addr: " + savedIp); + } + out.println("Elapsed: " + stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } + } catch (SQLException e) { + throw new ServletException("SQL error", e); + } + } + + @Override + public void init() throws ServletException { + String url; + + Properties properties = new Properties(); + try { + properties.load( + getServletContext().getResourceAsStream("/WEB-INF/classes/config.properties")); + url = properties.getProperty("sqlUrl"); + } catch (IOException e) { + log("no property", e); // Servlet Init should never fail. + return; + } + + log("connecting to: " + url); + try { + Class.forName("org.postgresql.Driver"); + conn = DriverManager.getConnection(url); + } catch (ClassNotFoundException e) { + throw new ServletException("Error loading JDBC Driver", e); + } catch (SQLException e) { + throw new ServletException("Unable to connect to PostGre", e); + } finally { + // Nothing really to do here. + } + } +} +// [END example] diff --git a/flexible/postgres/src/main/resources/config.properties b/flexible/postgres/src/main/resources/config.properties new file mode 100644 index 00000000000..405c4a6848e --- /dev/null +++ b/flexible/postgres/src/main/resources/config.properties @@ -0,0 +1 @@ +sqlUrl=${sqlURL} diff --git a/pom.xml b/pom.xml index 749ab2282c1..73b0c5ba6dc 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,7 @@ flexible/mailgun flexible/mailjet flexible/memcache + flexible/postgres flexible/pubsub flexible/sendgrid flexible/sparkjava