From f8d51b3e8ede2ca76370d1cf9e3b36f32a86f638 Mon Sep 17 00:00:00 2001 From: Shun Fan Date: Fri, 4 Dec 2015 16:19:40 -0800 Subject: [PATCH] Add structured-data step Add binary-data step --- 2-structured-data/pom.xml | 86 ++++++++++ .../gettingstartedjava/RootServlet.java | 60 +++++++ .../gettingstartedjava/daos/BookDao.java | 33 ++++ .../gettingstartedjava/daos/CloudSqlDao.java | 147 +++++++++++++++++ .../gettingstartedjava/daos/DatastoreDao.java | 128 +++++++++++++++ .../gettingstartedjava/objects/Book.java | 117 ++++++++++++++ .../gettingstartedjava/objects/Result.java | 35 ++++ .../servlets/CreateBookServlet.java | 59 +++++++ .../servlets/DeleteBookServlet.java | 45 ++++++ .../servlets/ListBookServlet.java | 54 +++++++ .../servlets/ReadBookServlet.java | 47 ++++++ .../servlets/UpdateBookServlet.java | 67 ++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 15 ++ .../src/main/webapp/WEB-INF/web.xml | 10 ++ 2-structured-data/src/main/webapp/base.jsp | 57 +++++++ 2-structured-data/src/main/webapp/form.jsp | 52 ++++++ 2-structured-data/src/main/webapp/view.jsp | 51 ++++++ 3-binary-data/pom.xml | 81 ++++++++++ .../gettingstartedjava/RootServlet.java | 63 ++++++++ .../gettingstartedjava/daos/BookDao.java | 33 ++++ .../gettingstartedjava/daos/CloudSqlDao.java | 151 ++++++++++++++++++ .../gettingstartedjava/daos/DatastoreDao.java | 136 ++++++++++++++++ .../gettingstartedjava/objects/Book.java | 133 +++++++++++++++ .../gettingstartedjava/objects/Result.java | 35 ++++ .../servlets/CreateBookServlet.java | 66 ++++++++ .../servlets/DeleteBookServlet.java | 45 ++++++ .../servlets/ListBookServlet.java | 54 +++++++ .../servlets/ReadBookServlet.java | 47 ++++++ .../servlets/UpdateBookServlet.java | 74 +++++++++ .../util/CloudStorageHelper.java | 85 ++++++++++ .../src/main/webapp/WEB-INF/appengine-web.xml | 16 ++ 3-binary-data/src/main/webapp/WEB-INF/web.xml | 10 ++ 3-binary-data/src/main/webapp/base.jsp | 57 +++++++ 3-binary-data/src/main/webapp/form.jsp | 62 +++++++ 3-binary-data/src/main/webapp/view.jsp | 51 ++++++ 35 files changed, 2262 insertions(+) create mode 100644 2-structured-data/pom.xml create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java create mode 100644 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java create mode 100644 2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 2-structured-data/src/main/webapp/WEB-INF/web.xml create mode 100644 2-structured-data/src/main/webapp/base.jsp create mode 100644 2-structured-data/src/main/webapp/form.jsp create mode 100644 2-structured-data/src/main/webapp/view.jsp create mode 100644 3-binary-data/pom.xml create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java create mode 100644 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java create mode 100644 3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml create mode 100644 3-binary-data/src/main/webapp/WEB-INF/web.xml create mode 100644 3-binary-data/src/main/webapp/base.jsp create mode 100644 3-binary-data/src/main/webapp/form.jsp create mode 100644 3-binary-data/src/main/webapp/view.jsp diff --git a/2-structured-data/pom.xml b/2-structured-data/pom.xml new file mode 100644 index 000000000..0bc5616b9 --- /dev/null +++ b/2-structured-data/pom.xml @@ -0,0 +1,86 @@ + + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine.gettingstartedjava + bookshelf2 + + + javax.servlet + javax.servlet-api + 3.1.0 + jar + provided + + + com.google.gcloud + gcloud-java-datastore + 0.0.12 + + + jstl + jstl + 1.2 + + + taglibs + standard + 1.1.2 + + + org.apache.commons + commons-dbcp2 + 2.1.1 + + + mysql + mysql-connector-java + 5.1.37 + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + org.apache.maven.plugins + maven-war-plugin + 2.6 + + false + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.7 + 1.7 + + + + com.google.appengine + gcloud-maven-plugin + 2.0.9.89.v20151202 + + + + diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java new file mode 100644 index 000000000..03d8df7bf --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java @@ -0,0 +1,60 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.daos.CloudSqlDao; +import com.example.appengine.gettingstartedjava.daos.DatastoreDao; + +import java.io.IOException; +import java.sql.SQLException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +public class RootServlet extends HttpServlet { + + @Override + public void init() throws ServletException { + String storageType = System.getenv("AEV2_JAVA_STORAGETYPE"); + BookDao dao = null; + switch (storageType) { + case "datastore": + dao = new DatastoreDao(); + break; + case "cloudsql": + try { + dao = new CloudSqlDao(); + } catch (SQLException e) { + throw new ServletException("SQL error", e); + } + break; + default: + throw new IllegalStateException("Invalid storage type. Check if environment variable is set."); + } + this.getServletContext().setAttribute("dao", dao); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + req.getRequestDispatcher("/books").forward(req, resp); + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java new file mode 100644 index 000000000..f66ff62ae --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java @@ -0,0 +1,33 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +public interface BookDao { + + public Long createBook(Book book) throws Exception; + + public Book readBook(Long bookId) throws Exception; + + public void updateBook(Book book) throws Exception; + + public void deleteBook(Long bookId) throws Exception; + + public Result listBooks(String startCursor) throws Exception; +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java new file mode 100644 index 000000000..f6da47491 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java @@ -0,0 +1,147 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import org.apache.commons.dbcp2.BasicDataSource; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +public class CloudSqlDao implements BookDao { + + private static final BasicDataSource dataSource = new BasicDataSource(); + + public CloudSqlDao() throws SQLException { + final String url = System.getenv("SQL_DATABASE_URL"); + dataSource.setUrl(url); + final String createTableSql = "CREATE TABLE IF NOT EXISTS books ( id INT NOT NULL " + + "AUTO_INCREMENT, author VARCHAR(255), description VARCHAR(255), publishedDate " + + "VARCHAR(255), title VARCHAR(255), PRIMARY KEY (id))"; + try (Connection conn = dataSource.getConnection()) { + conn.createStatement().executeUpdate(createTableSql); + } + } + + @Override + public Long createBook(Book book) throws SQLException { + final String createBookString = "INSERT INTO books (author, description, publishedDate, title) " + + "VALUES (?, ?, ?, ?)"; + try (Connection conn = dataSource.getConnection(); + final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, + Statement.RETURN_GENERATED_KEYS)) { + createBookStmt.setString(1, book.getAuthor()); + createBookStmt.setString(2, book.getDescription()); + createBookStmt.setString(3, book.getPublishedDate()); + createBookStmt.setString(4, book.getTitle()); + createBookStmt.executeUpdate(); + try (ResultSet keys = createBookStmt.getGeneratedKeys()) { + keys.next(); + return keys.getLong(1); + } + } + } + + @Override + public Book readBook(Long bookId) throws SQLException { + final String readBookString = "SELECT * FROM books WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { + readBookStmt.setLong(1, bookId); + try (ResultSet keys = readBookStmt.executeQuery()) { + keys.next(); + return new Book.Builder() + .author(keys.getString("author")) + .description(keys.getString("description")) + .id(keys.getLong("id")) + .publishedDate(keys.getString("publishedDate")) + .title(keys.getString("title")) + .build(); + } + } + } + + @Override + public void updateBook(Book book) throws SQLException { + final String updateBookString = "UPDATE books SET author = ?, description = ?, " + + "publishedDate = ?, title = ? WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { + updateBookStmt.setString(1, book.getAuthor()); + updateBookStmt.setString(2, book.getDescription()); + updateBookStmt.setString(3, book.getPublishedDate()); + updateBookStmt.setString(4, book.getTitle()); + updateBookStmt.setLong(5, book.getId()); + updateBookStmt.executeUpdate(); + } + } + + @Override + public void deleteBook(Long bookId) throws SQLException { + final String deleteBookString = "DELETE FROM books WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { + deleteBookStmt.setLong(1, bookId); + deleteBookStmt.executeUpdate(); + } + } + + @Override + public Result listBooks(String cursor) throws SQLException { + int offset = 0; + if (cursor != null && !cursor.equals("")) { + offset = Integer.parseInt(cursor); + } + final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, description, id, " + + "publishedDate, title FROM books ORDER BY title ASC LIMIT 10 OFFSET ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { + listBooksStmt.setInt(1, offset); + List resultBooks = new ArrayList<>(); + try (ResultSet rs = listBooksStmt.executeQuery()) { + while (rs.next()) { + Book book = new Book.Builder() + .author(rs.getString("author")) + .description(rs.getString("description")) + .id(rs.getLong("id")) + .publishedDate(rs.getString("publishedDate")) + .title(rs.getString("title")) + .build(); + resultBooks.add(book); + } + } + try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { + int totalNumRows = 0; + if (rs.next()) { + totalNumRows = rs.getInt(1); + } + if (totalNumRows > offset + 10) { + return new Result<>(resultBooks, Integer.toString(offset + 10)); + } else { + return new Result<>(resultBooks); + } + } + } + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java new file mode 100644 index 000000000..1fd97db8a --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java @@ -0,0 +1,128 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import com.google.gcloud.datastore.Cursor; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreOptions; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.FullEntity; +import com.google.gcloud.datastore.IncompleteKey; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.KeyFactory; +import com.google.gcloud.datastore.Query; +import com.google.gcloud.datastore.QueryResults; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.util.ArrayList; +import java.util.List; + +public class DatastoreDao implements BookDao { + + private Datastore datastore; + private KeyFactory keyFactory; + + public DatastoreDao() { + datastore = DatastoreOptions + .builder() + .projectId(System.getenv("PROJECT_ID")) + .build() + .service(); + keyFactory = datastore.newKeyFactory().kind("Book"); + } + + @Override + public Long createBook(Book book) { + IncompleteKey key = keyFactory.kind("Book").newKey(); + FullEntity incBookEntity = Entity.builder(key) + .set("author", book.getAuthor()) + .set("description", book.getDescription()) + .set("publishedDate", book.getPublishedDate()) + .set("title", book.getTitle()) + .build(); + Entity bookEntity = datastore.add(incBookEntity); + return bookEntity.key().id(); + } + + @Override + public Book readBook(Long bookId) { + Entity bookEntity = datastore.get(keyFactory.newKey(bookId)); + return new Book.Builder() + .author(bookEntity.getString("author")) + .description(bookEntity.getString("description")) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString("publishedDate")) + .title(bookEntity.getString("title")) + .build(); + } + + @Override + public void updateBook(Book book) { + Key key = keyFactory.newKey(book.getId()); + Entity entity = Entity.builder(key) + .set("author", book.getAuthor()) + .set("description", book.getDescription()) + .set("publishedDate", book.getPublishedDate()) + .set("title", book.getTitle()) + .build(); + datastore.update(entity); + } + + @Override + public void deleteBook(Long bookId) { + Key key = keyFactory.newKey(bookId); + datastore.delete(key); + } + + @Override + public Result listBooks(String startCursorString) { + Query q; + Cursor startCursor = null; + if(startCursorString != null && !startCursorString.equals("")) { + startCursor = Cursor.fromUrlSafe(startCursorString); + } + q = Query.entityQueryBuilder() + .kind("Book") + .limit(10) + .startCursor(startCursor) + .orderBy(OrderBy.asc("title")) + .build(); + QueryResults resultList = datastore.run(q); + List resultBooks = new ArrayList<>(); + while(resultList.hasNext()) { + Entity bookEntity = resultList.next(); + Book book = new Book.Builder() + .author(bookEntity.getString("author")) + .description(bookEntity.getString("description")) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString("publishedDate")) + .title(bookEntity.getString("title")) + .build(); + resultBooks.add(book); + } + Cursor cursor = resultList.cursorAfter(); // note cursorAfter() doesn't work currently + if(cursor != null) { + String cursorString = cursor.toUrlSafe(); + return new Result<>(resultBooks, cursorString); + } else { + return new Result<>(resultBooks); + } + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java new file mode 100644 index 000000000..22a2e13ce --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java @@ -0,0 +1,117 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.objects; + +public class Book { + + private String title; + private String author; + private String publishedDate; + private String description; + private Long id; + + // We use a Builder pattern here to simplify and standardize construction of Book objects. + private Book(Builder b) { + this.title = b.title; + this.author = b.author; + this.publishedDate = b.publishedDate; + this.description = b.description; + this.id = b.id; + } + + public static class Builder { + private String title; + private String author; + private String publishedDate; + private String description; + private Long id; + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder author(String author) { + this.author = author; + return this; + } + + public Builder publishedDate(String publishedDate) { + this.publishedDate = publishedDate; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder id(Long id) { + this.id = id; + return this; + } + + public Book build() { + return new Book(this); + } + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getPublishedDate() { + return publishedDate; + } + + public void setPublishedDate(String publishedDate) { + this.publishedDate = publishedDate; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public String toString() { + return author; + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java new file mode 100644 index 000000000..9f48b9a79 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java @@ -0,0 +1,35 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.objects; + +import java.util.List; + +public class Result { + + public String cursor; + public List result; + + public Result(List result, String cursor) { + this.result = result; + this.cursor = cursor; + } + + public Result(List result) { + this.result = result; + this.cursor = null; + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java new file mode 100644 index 000000000..e2f2653e5 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java @@ -0,0 +1,59 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "create", value = "/create") +public class CreateBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + req.setAttribute("action", "Add"); + req.setAttribute("destination", "create"); + req.getRequestDispatcher("/form.jsp").forward(req, resp); + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + Book book = new Book.Builder() + .author(req.getParameter("author")) + .description(req.getParameter("description")) + .publishedDate(req.getParameter("publishedDate")) + .title(req.getParameter("title")) + .build(); + try { + Long id = dao.createBook(book); + resp.sendRedirect("/read?id="+id.toString()); + } catch (Exception e) { + throw new ServletException("Error creating book", e); + } + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java new file mode 100644 index 000000000..19d44a857 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java @@ -0,0 +1,45 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "delete", value = "/delete") +public class DeleteBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + Long id = Long.decode(req.getParameter("id")); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + dao.deleteBook(id); + resp.sendRedirect("/books"); + } catch (Exception e) { + throw new ServletException("Error deleting book", e); + } + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java new file mode 100644 index 000000000..f2e52745f --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java @@ -0,0 +1,54 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "list", value = "/books") +public class ListBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + String startCursor = req.getParameter("cursor"); + List books = null; + String endCursor = null; + try { + Result result = dao.listBooks(startCursor); + books = result.result; + endCursor = result.cursor; + } catch (Exception e) { + throw new ServletException("Error listing books", e); + } + req.getSession().getServletContext().setAttribute("books", books); + req.setAttribute("cursor", endCursor); + req.getRequestDispatcher("/base.jsp").forward(req, resp); + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java new file mode 100644 index 000000000..cb7cd1a75 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java @@ -0,0 +1,47 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "read", value = "/read") +public class ReadBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + Long id = Long.decode(req.getParameter("id")); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + Book book = dao.readBook(id); + req.setAttribute("book", book); + req.getRequestDispatcher("/view.jsp").forward(req, resp); + } catch (Exception e) { + throw new ServletException("Error reading book", e); + } + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java new file mode 100644 index 000000000..c22ae3678 --- /dev/null +++ b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java @@ -0,0 +1,67 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "update", value = "/update") +public class UpdateBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + Book book = dao.readBook(Long.decode(req.getParameter("id"))); + req.setAttribute("book", book); + req.setAttribute("action", "Edit"); + req.setAttribute("destination", "update"); + req.getRequestDispatcher("/form.jsp").forward(req, resp); + } catch (Exception e) { + throw new ServletException("Error loading book for editing", e); + } + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + Book book = new Book.Builder() + .author(req.getParameter("author")) + .description(req.getParameter("description")) + .id(Long.decode(req.getParameter("id"))) + .publishedDate(req.getParameter("publishedDate")) + .title(req.getParameter("title")) + .build(); + try { + dao.updateBook(book); + resp.sendRedirect("/read?id=" + req.getParameter("id")); + } catch (Exception e) { + throw new ServletException("Error updating book", e); + } + } +} diff --git a/2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml b/2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 000000000..341dbc579 --- /dev/null +++ b/2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,15 @@ + + + true + true + true + + + + + + + + + + diff --git a/2-structured-data/src/main/webapp/WEB-INF/web.xml b/2-structured-data/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6467df305 --- /dev/null +++ b/2-structured-data/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + root + com.example.appengine.gettingstartedjava.RootServlet + + + root + / + + diff --git a/2-structured-data/src/main/webapp/base.jsp b/2-structured-data/src/main/webapp/base.jsp new file mode 100644 index 000000000..e95ea8fc0 --- /dev/null +++ b/2-structured-data/src/main/webapp/base.jsp @@ -0,0 +1,57 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Java on Google Cloud Platform + + + + + + +
+

Books

+ + + Add book + + + +

No books found

+
+ + + + + + + + +
+
+ + diff --git a/2-structured-data/src/main/webapp/form.jsp b/2-structured-data/src/main/webapp/form.jsp new file mode 100644 index 000000000..3e757efe6 --- /dev/null +++ b/2-structured-data/src/main/webapp/form.jsp @@ -0,0 +1,52 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Python on Google Cloud Platform + + + + + + +
+

book

+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+
+ + diff --git a/2-structured-data/src/main/webapp/view.jsp b/2-structured-data/src/main/webapp/view.jsp new file mode 100644 index 000000000..ffadb1288 --- /dev/null +++ b/2-structured-data/src/main/webapp/view.jsp @@ -0,0 +1,51 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Python on Google Cloud Platform + + + + + + +
+

Book

+ + +
+
+ +
+
+

+ ${fn:escapeXml(book.title)} + ${fn:escapeXml(book.publishedDate)} +

+
By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}
+

${fn:escapeXml(book.description)}

+
+
+
+ + + diff --git a/3-binary-data/pom.xml b/3-binary-data/pom.xml new file mode 100644 index 000000000..0be6a4097 --- /dev/null +++ b/3-binary-data/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine.gettingstartedjava + bookshelf3 + + + javax.servlet + javax.servlet-api + 3.1.0 + jar + provided + + + com.google.gcloud + gcloud-java-datastore + 0.0.12 + + + com.google.gcloud + gcloud-java-storage + 0.0.12 + + + jstl + jstl + 1.2 + + + taglibs + standard + 1.1.2 + + + org.apache.commons + commons-dbcp2 + 2.1.1 + + + mysql + mysql-connector-java + 5.1.37 + + + joda-time + joda-time + 2.9.1 + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + org.apache.maven.plugins + maven-war-plugin + 2.6 + + false + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.7 + 1.7 + + + + com.google.appengine + gcloud-maven-plugin + 2.0.9.89.v20151202 + + + + diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java new file mode 100644 index 000000000..82ddc1123 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java @@ -0,0 +1,63 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.daos.CloudSqlDao; +import com.example.appengine.gettingstartedjava.daos.DatastoreDao; +import com.example.appengine.gettingstartedjava.util.CloudStorageHelper; + +import java.io.IOException; +import java.sql.SQLException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +public class RootServlet extends HttpServlet { + + @Override + public void init() throws ServletException { + String storageType = System.getenv("AEV2_JAVA_STORAGETYPE"); + BookDao dao = null; + switch (storageType) { + case "datastore": + dao = new DatastoreDao(); + break; + case "cloudsql": + try { + dao = new CloudSqlDao(); + } catch (SQLException e) { + throw new ServletException("SQL error", e); + } + break; + default: + throw new IllegalStateException("Invalid storage type. Check if environment variable is set."); + } + this.getServletContext().setAttribute("dao", dao); + CloudStorageHelper storageHelper = new CloudStorageHelper(); + this.getServletContext().setAttribute("storageHelper", storageHelper); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + req.getRequestDispatcher("/books").forward(req, resp); + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java new file mode 100644 index 000000000..f66ff62ae --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java @@ -0,0 +1,33 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +public interface BookDao { + + public Long createBook(Book book) throws Exception; + + public Book readBook(Long bookId) throws Exception; + + public void updateBook(Book book) throws Exception; + + public void deleteBook(Long bookId) throws Exception; + + public Result listBooks(String startCursor) throws Exception; +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java new file mode 100644 index 000000000..da1e90a3c --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java @@ -0,0 +1,151 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import org.apache.commons.dbcp2.BasicDataSource; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +public class CloudSqlDao implements BookDao { + + private static final BasicDataSource dataSource = new BasicDataSource(); + + public CloudSqlDao() throws SQLException { + final String url = System.getenv("SQL_DATABASE_URL"); + dataSource.setUrl(url); + final String createTableSql = "CREATE TABLE IF NOT EXISTS books3 ( id INT NOT NULL " + + "AUTO_INCREMENT, author VARCHAR(255), description VARCHAR(255), publishedDate " + + "VARCHAR(255), title VARCHAR(255), imageUrl VARCHAR(255), PRIMARY KEY (id))"; + try (Connection conn = dataSource.getConnection()) { + conn.createStatement().executeUpdate(createTableSql); + } + } + + @Override + public Long createBook(Book book) throws SQLException { + final String createBookString = "INSERT INTO books3 " + + "(author, description, publishedDate, title, imageUrl) VALUES (?, ?, ?, ?, ?)"; + try (Connection conn = dataSource.getConnection(); + final PreparedStatement createBookStmt = conn.prepareStatement(createBookString, + Statement.RETURN_GENERATED_KEYS)) { + createBookStmt.setString(1, book.getAuthor()); + createBookStmt.setString(2, book.getDescription()); + createBookStmt.setString(3, book.getPublishedDate()); + createBookStmt.setString(4, book.getTitle()); + createBookStmt.setString(5, book.getImageUrl()); + createBookStmt.executeUpdate(); + try (ResultSet keys = createBookStmt.getGeneratedKeys()) { + keys.next(); + return keys.getLong(1); + } + } + } + + @Override + public Book readBook(Long bookId) throws SQLException { + final String readBookString = "SELECT * FROM books3 WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement readBookStmt = conn.prepareStatement(readBookString)) { + readBookStmt.setLong(1, bookId); + try (ResultSet keys = readBookStmt.executeQuery()) { + keys.next(); + return new Book.Builder() + .author(keys.getString("author")) + .description(keys.getString("description")) + .id(keys.getLong("id")) + .publishedDate(keys.getString("publishedDate")) + .title(keys.getString("title")) + .imageUrl(keys.getString("imageUrl")) + .build(); + } + } + } + + @Override + public void updateBook(Book book) throws SQLException { + final String updateBookString = "UPDATE books3 SET author = ?, description = ?, " + + "publishedDate = ?, title = ?, imageUrl = ? WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement updateBookStmt = conn.prepareStatement(updateBookString)) { + updateBookStmt.setString(1, book.getAuthor()); + updateBookStmt.setString(2, book.getDescription()); + updateBookStmt.setString(3, book.getPublishedDate()); + updateBookStmt.setString(4, book.getTitle()); + updateBookStmt.setString(5, book.getImageUrl()); + updateBookStmt.setLong(6, book.getId()); + updateBookStmt.executeUpdate(); + } + } + + @Override + public void deleteBook(Long bookId) throws SQLException { + final String deleteBookString = "DELETE FROM books3 WHERE id = ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement deleteBookStmt = conn.prepareStatement(deleteBookString)) { + deleteBookStmt.setLong(1, bookId); + deleteBookStmt.executeUpdate(); + } + } + + @Override + public Result listBooks(String cursor) throws SQLException { + int offset = 0; + if (cursor != null && !cursor.equals("")) { + offset = Integer.parseInt(cursor); + } + final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, description, id, " + + "publishedDate, title, imageUrl FROM books3 ORDER BY title ASC LIMIT 10 OFFSET ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { + listBooksStmt.setInt(1, offset); + List resultBooks = new ArrayList<>(); + try (ResultSet rs = listBooksStmt.executeQuery()) { + while (rs.next()) { + Book book = new Book.Builder() + .author(rs.getString("author")) + .description(rs.getString("description")) + .id(rs.getLong("id")) + .publishedDate(rs.getString("publishedDate")) + .title(rs.getString("title")) + .imageUrl(rs.getString("imageUrl")) + .build(); + resultBooks.add(book); + } + } + try (ResultSet rs = conn.createStatement().executeQuery("SELECT FOUND_ROWS()")) { + int totalNumRows = 0; + if (rs.next()) { + totalNumRows = rs.getInt(1); + } + if (totalNumRows > offset + 10) { + return new Result<>(resultBooks, Integer.toString(offset + 10)); + } else { + return new Result<>(resultBooks); + } + } + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java new file mode 100644 index 000000000..589ffa251 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java @@ -0,0 +1,136 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.daos; + +import com.google.gcloud.datastore.Cursor; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreOptions; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.FullEntity; +import com.google.gcloud.datastore.IncompleteKey; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.KeyFactory; +import com.google.gcloud.datastore.Query; +import com.google.gcloud.datastore.QueryResults; +import com.google.gcloud.datastore.StructuredQuery.OrderBy; + +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.util.ArrayList; +import java.util.List; + +public class DatastoreDao implements BookDao { + + private Datastore datastore; + private KeyFactory keyFactory; + + public DatastoreDao() { + datastore = DatastoreOptions + .builder() + .projectId(System.getenv("PROJECT_ID")) + .build() + .service(); + keyFactory = datastore.newKeyFactory().kind("Book"); + } + + @Override + public Long createBook(Book book) { + IncompleteKey key = keyFactory.kind("Book").newKey(); + FullEntity incBookEntity = Entity.builder(key) + .set("author", book.getAuthor()) + .set("description", book.getDescription()) + .set("publishedDate", book.getPublishedDate()) + .set("title", book.getTitle()) + .set("imageUrl", book.getImageUrl()) + .build(); + Entity bookEntity = datastore.add(incBookEntity); + return bookEntity.key().id(); + } + + @Override + public Book readBook(Long bookId) { + Entity bookEntity = datastore.get(keyFactory.newKey(bookId)); + return new Book.Builder() + .author(bookEntity.getString("author")) + .description(bookEntity.getString("description")) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString("publishedDate")) + .title(bookEntity.getString("title")) + // maintain backwards compatibility with + // books created in 2-structured data which do not have imageUrl property + .imageUrl(bookEntity.contains("imageUrl") ? bookEntity.getString("imageUrl") : null) + .build(); + } + + @Override + public void updateBook(Book book) { + Key key = keyFactory.newKey(book.getId()); + Entity entity = Entity.builder(key) + .set("author", book.getAuthor()) + .set("description", book.getDescription()) + .set("publishedDate", book.getPublishedDate()) + .set("title", book.getTitle()) + .set("imageUrl", book.getImageUrl()) + .build(); + datastore.update(entity); + } + + @Override + public void deleteBook(Long bookId) { + Key key = keyFactory.newKey(bookId); + datastore.delete(key); + } + + @Override + public Result listBooks(String startCursorString) { + Query q; + Cursor startCursor = null; + if(startCursorString != null && !startCursorString.equals("")) { + startCursor = Cursor.fromUrlSafe(startCursorString); + } + q = Query.entityQueryBuilder() + .kind("Book") + .limit(10) + .startCursor(startCursor) + .orderBy(OrderBy.asc("title")) + .build(); + QueryResults resultList = datastore.run(q); + List resultBooks = new ArrayList<>(); + while(resultList.hasNext()) { + Entity bookEntity = resultList.next(); + Book book = new Book.Builder() + .author(bookEntity.getString("author")) + .description(bookEntity.getString("description")) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString("publishedDate")) + .title(bookEntity.getString("title")) + // maintain backwards compatibility with + // books created in 2-structured data which do not have imageUrl property + .imageUrl(bookEntity.contains("imageUrl") ? bookEntity.getString("imageUrl") : null) + .build(); + resultBooks.add(book); + } + Cursor cursor = resultList.cursorAfter(); // note cursorAfter() doesn't work currently + if(cursor != null) { + String cursorString = cursor.toUrlSafe(); + return new Result<>(resultBooks, cursorString); + } else { + return new Result<>(resultBooks); + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java new file mode 100644 index 000000000..246ec31ba --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java @@ -0,0 +1,133 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.objects; + +public class Book { + + private String title; + private String author; + private String publishedDate; + private String description; + private Long id; + private String imageUrl; + + // We use a Builder pattern here to simplify and standardize construction of Book objects. + private Book(Builder b) { + this.title = b.title; + this.author = b.author; + this.publishedDate = b.publishedDate; + this.description = b.description; + this.id = b.id; + this.imageUrl = b.imageUrl; + } + + public static class Builder { + private String title; + private String author; + private String publishedDate; + private String description; + private Long id; + private String imageUrl; + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder author(String author) { + this.author = author; + return this; + } + + public Builder publishedDate(String publishedDate) { + this.publishedDate = publishedDate; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder id(Long id) { + this.id = id; + return this; + } + + public Builder imageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public Book build() { + return new Book(this); + } + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getPublishedDate() { + return publishedDate; + } + + public void setPublishedDate(String publishedDate) { + this.publishedDate = publishedDate; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + @Override + public String toString() { + return author; + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java new file mode 100644 index 000000000..9f48b9a79 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java @@ -0,0 +1,35 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.objects; + +import java.util.List; + +public class Result { + + public String cursor; + public List result; + + public Result(List result, String cursor) { + this.result = result; + this.cursor = cursor; + } + + public Result(List result) { + this.result = result; + this.cursor = null; + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java new file mode 100644 index 000000000..79a18461d --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java @@ -0,0 +1,66 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.util.CloudStorageHelper; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@MultipartConfig +@WebServlet(name = "create", value = "/create") +public class CreateBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + req.setAttribute("action", "Add"); + req.setAttribute("destination", "create"); + req.getRequestDispatcher("/form.jsp").forward(req, resp); + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + CloudStorageHelper storageHelper = + (CloudStorageHelper) req.getServletContext().getAttribute("storageHelper"); + String imageUrl = storageHelper.getImageUrl(req, resp); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + Book book = new Book.Builder() + .author(req.getParameter("author")) + .description(req.getParameter("description")) + .publishedDate(req.getParameter("publishedDate")) + .title(req.getParameter("title")) + .imageUrl(imageUrl) + .build(); + try { + Long id = dao.createBook(book); + resp.sendRedirect("/read?id="+id.toString()); + } catch (Exception e) { + throw new ServletException("Error creating book", e); + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java new file mode 100644 index 000000000..19d44a857 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java @@ -0,0 +1,45 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "delete", value = "/delete") +public class DeleteBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + Long id = Long.decode(req.getParameter("id")); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + dao.deleteBook(id); + resp.sendRedirect("/books"); + } catch (Exception e) { + throw new ServletException("Error deleting book", e); + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java new file mode 100644 index 000000000..f2e52745f --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java @@ -0,0 +1,54 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.objects.Result; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "list", value = "/books") +public class ListBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + String startCursor = req.getParameter("cursor"); + List books = null; + String endCursor = null; + try { + Result result = dao.listBooks(startCursor); + books = result.result; + endCursor = result.cursor; + } catch (Exception e) { + throw new ServletException("Error listing books", e); + } + req.getSession().getServletContext().setAttribute("books", books); + req.setAttribute("cursor", endCursor); + req.getRequestDispatcher("/base.jsp").forward(req, resp); + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java new file mode 100644 index 000000000..cb7cd1a75 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java @@ -0,0 +1,47 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@WebServlet(name = "read", value = "/read") +public class ReadBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + Long id = Long.decode(req.getParameter("id")); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + Book book = dao.readBook(id); + req.setAttribute("book", book); + req.getRequestDispatcher("/view.jsp").forward(req, resp); + } catch (Exception e) { + throw new ServletException("Error reading book", e); + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java new file mode 100644 index 000000000..dbd440277 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java @@ -0,0 +1,74 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.servlets; + +import com.example.appengine.gettingstartedjava.daos.BookDao; +import com.example.appengine.gettingstartedjava.objects.Book; +import com.example.appengine.gettingstartedjava.util.CloudStorageHelper; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +@MultipartConfig +@WebServlet(name = "update", value = "/update") +public class UpdateBookServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + try { + Book book = dao.readBook(Long.decode(req.getParameter("id"))); + req.setAttribute("book", book); + req.setAttribute("action", "Edit"); + req.setAttribute("destination", "update"); + req.getRequestDispatcher("/form.jsp").forward(req, resp); + } catch (Exception e) { + throw new ServletException("Error loading book for editing", e); + } + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, + IOException { + CloudStorageHelper storageHelper = + (CloudStorageHelper) req.getServletContext().getAttribute("storageHelper"); + String imageUrl = storageHelper.getImageUrl(req, resp); + BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + Book book = new Book.Builder() + .author(req.getParameter("author")) + .description(req.getParameter("description")) + .id(Long.decode(req.getParameter("id"))) + .publishedDate(req.getParameter("publishedDate")) + .title(req.getParameter("title")) + .imageUrl(imageUrl) + .build(); + try { + dao.updateBook(book); + resp.sendRedirect("/read?id=" + req.getParameter("id")); + } catch (Exception e) { + throw new ServletException("Error updating book", e); + } + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java new file mode 100644 index 000000000..6b7085b77 --- /dev/null +++ b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java @@ -0,0 +1,85 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.appengine.gettingstartedjava.util; + +import com.google.common.io.ByteStreams; +import com.google.gcloud.storage.Acl; +import com.google.gcloud.storage.BlobInfo; +import com.google.gcloud.storage.BlobWriteChannel; +import com.google.gcloud.storage.Storage; +import com.google.gcloud.storage.StorageOptions; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.Channels; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; + +public class CloudStorageHelper { + + private static Storage storage = null; + + public CloudStorageHelper() { + storage = StorageOptions.defaultInstance().service(); + } + + public String uploadFile(Part filePart) throws IOException { + DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS"); + DateTime dt = DateTime.now(DateTimeZone.UTC); + String dtString = dt.toString(dtf); + final String fileName = filePart.getSubmittedFileName() + dtString; + String BUCKET_NAME = System.getenv("BUCKET_NAME"); + // Modify access list to allow all users with link to read file + List acls = new ArrayList<>(); + acls.add(new Acl(Acl.User.ofAllUsers(), Acl.Role.READER)); + BlobInfo blobInfo = BlobInfo.builder(BUCKET_NAME, fileName).acl(acls).build(); + InputStream filecontent = filePart.getInputStream(); + BlobWriteChannel blobWriter = storage.writer(blobInfo); + ByteStreams.copy(filecontent, Channels.newOutputStream(blobWriter)); + blobWriter.close(); + blobInfo = storage.get(BUCKET_NAME, fileName); + return blobInfo.mediaLink(); + } + + public String getImageUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + Part filePart = req.getPart("file"); + final String fileName = filePart.getSubmittedFileName(); + String imageUrl = req.getParameter("imageUrl"); + if (fileName != null && !fileName.isEmpty()) { + final String extension = fileName.substring(fileName.lastIndexOf('.') + 1); + String[] allowedExt = { "jpg", "jpeg", "png", "gif" }; + for (String s : allowedExt) { + if (extension.equals(s)) { + return this.uploadFile(filePart); + } + } + throw new ServletException("file must be an image"); + } + return imageUrl; + } +} diff --git a/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml b/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 000000000..2948a5f71 --- /dev/null +++ b/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,16 @@ + + + true + true + true + + + + + + + + + + + diff --git a/3-binary-data/src/main/webapp/WEB-INF/web.xml b/3-binary-data/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6467df305 --- /dev/null +++ b/3-binary-data/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + root + com.example.appengine.gettingstartedjava.RootServlet + + + root + / + + diff --git a/3-binary-data/src/main/webapp/base.jsp b/3-binary-data/src/main/webapp/base.jsp new file mode 100644 index 000000000..154130d81 --- /dev/null +++ b/3-binary-data/src/main/webapp/base.jsp @@ -0,0 +1,57 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Java on Google Cloud Platform + + + + + + +
+

Books

+ + + Add book + + + +

No books found

+
+ + + + + + + + +
+
+ + diff --git a/3-binary-data/src/main/webapp/form.jsp b/3-binary-data/src/main/webapp/form.jsp new file mode 100644 index 000000000..83641ae35 --- /dev/null +++ b/3-binary-data/src/main/webapp/form.jsp @@ -0,0 +1,62 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Java on Google Cloud Platform + + + + + + +
+

book

+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+
+ + diff --git a/3-binary-data/src/main/webapp/view.jsp b/3-binary-data/src/main/webapp/view.jsp new file mode 100644 index 000000000..251ff3c49 --- /dev/null +++ b/3-binary-data/src/main/webapp/view.jsp @@ -0,0 +1,51 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + + + + Bookshelf - Java on Google Cloud Platform + + + + + + +
+

Book

+ + +
+
+ +
+
+

+ ${fn:escapeXml(book.title)} + ${fn:escapeXml(book.publishedDate)} +

+
By ${fn:escapeXml(not empty book.author?book.author:'Unknown')}
+

${fn:escapeXml(book.description)}

+
+
+
+ + +