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 deleted file mode 100644 index 03d8df7bf..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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 deleted file mode 100644 index f66ff62ae..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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/DatastoreDao.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java deleted file mode 100644 index 1fd97db8a..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * 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 deleted file mode 100644 index 22a2e13ce..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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/servlets/CreateBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java deleted file mode 100644 index e2f2653e5..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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/ReadBookServlet.java b/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java deleted file mode 100644 index cb7cd1a75..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 deleted file mode 100644 index c22ae3678..000000000 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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 deleted file mode 100644 index 341dbc579..000000000 --- a/2-structured-data/src/main/webapp/WEB-INF/appengine-web.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - 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 deleted file mode 100644 index 6467df305..000000000 --- a/2-structured-data/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - 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 deleted file mode 100644 index e95ea8fc0..000000000 --- a/2-structured-data/src/main/webapp/base.jsp +++ /dev/null @@ -1,57 +0,0 @@ -<%@ 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 deleted file mode 100644 index 3e757efe6..000000000 --- a/2-structured-data/src/main/webapp/form.jsp +++ /dev/null @@ -1,52 +0,0 @@ -<%@ 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 deleted file mode 100644 index ffadb1288..000000000 --- a/2-structured-data/src/main/webapp/view.jsp +++ /dev/null @@ -1,51 +0,0 @@ -<%@ 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 deleted file mode 100644 index 0be6a4097..000000000 --- a/3-binary-data/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - 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/daos/CloudSqlDao.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java deleted file mode 100644 index da1e90a3c..000000000 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * 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 deleted file mode 100644 index 589ffa251..000000000 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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/Result.java b/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java deleted file mode 100644 index 9f48b9a79..000000000 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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/webapp/WEB-INF/appengine-web.xml b/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml deleted file mode 100644 index 2948a5f71..000000000 --- a/3-binary-data/src/main/webapp/WEB-INF/appengine-web.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 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 deleted file mode 100644 index 6467df305..000000000 --- a/3-binary-data/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - 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 deleted file mode 100644 index 154130d81..000000000 --- a/3-binary-data/src/main/webapp/base.jsp +++ /dev/null @@ -1,57 +0,0 @@ -<%@ 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 deleted file mode 100644 index 83641ae35..000000000 --- a/3-binary-data/src/main/webapp/form.jsp +++ /dev/null @@ -1,62 +0,0 @@ -<%@ 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 deleted file mode 100644 index 251ff3c49..000000000 --- a/3-binary-data/src/main/webapp/view.jsp +++ /dev/null @@ -1,51 +0,0 @@ -<%@ 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)}

-
-
-
- - - diff --git a/README.md b/README.md index 7d28ce365..fe35593b6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,11 @@ # Getting started with Java on Google Cloud Platform The code for the samples is contained in individual folders on this repository. +Follow the instructions at [Java Getting Started on Google Cloud Platform](http://cloud.google.com/java/getting-started) for instructions on how to run locally and deploy. 1. Helloworld - -## Prerequisites - -Before you start developing, follow these steps: - -* Download the [Google Cloud SDK](https://cloud.google.com/sdk/) -* Follow the instructions to [install maven](https://cloud.google.com/appengine/docs/java/managed-vms/maven) -and [add the Cloud SDK plugin](https://cloud.google.com/appengine/docs/java/managed-vms/maven#adding_the_cloud_sdk_app_engine_maven_plugin_to_an_existing_maven_project) -to your project. - -## Testing an deploying your app - -* Test the app by running `mvn gcloud:run` -* Deploy the application using `mvn gcloud:deploy` +1. Structured Data +1. Binary Data ## Contributing changes diff --git a/2-structured-data/pom.xml b/bookshelf/pom.xml similarity index 71% rename from 2-structured-data/pom.xml rename to bookshelf/pom.xml index 0bc5616b9..408f8f03f 100644 --- a/2-structured-data/pom.xml +++ b/bookshelf/pom.xml @@ -20,7 +20,7 @@ Copyright 2015 Google Inc. All Rights Reserved. war 1.0-SNAPSHOT com.example.appengine.gettingstartedjava - bookshelf2 + bookshelf javax.servlet @@ -29,10 +29,35 @@ Copyright 2015 Google Inc. All Rights Reserved. jar provided + + javax.servlet + jsp-api + 2.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.6.0 + + + com.google.api-client + google-api-client + 1.20.0 + + + com.google.apis + google-api-services-plus + v1-rev319-1.21.0 + + + com.google.appengine + appengine-api-1.0-sdk + 1.9.30 + com.google.gcloud - gcloud-java-datastore - 0.0.12 + gcloud-java + 0.1.1 jstl @@ -52,7 +77,12 @@ Copyright 2015 Google Inc. All Rights Reserved. mysql mysql-connector-java - 5.1.37 + 5.1.38 + + + joda-time + joda-time + 2.9.1 diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java similarity index 97% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java index 82ddc1123..9f494998b 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/RootServlet.java @@ -34,7 +34,7 @@ public class RootServlet extends HttpServlet { @Override public void init() throws ServletException { - String storageType = System.getenv("AEV2_JAVA_STORAGETYPE"); + String storageType = System.getenv("STORAGETYPE"); BookDao dao = null; switch (storageType) { case "datastore": diff --git a/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LoginServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LoginServlet.java new file mode 100644 index 000000000..7fe7b3c60 --- /dev/null +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LoginServlet.java @@ -0,0 +1,73 @@ +/** + * 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.auth; + +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.plus.PlusScopes; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collection; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(name = "login", value = "/login") +@SuppressWarnings("serial") +public class LoginServlet extends HttpServlet { + private GoogleAuthorizationCodeFlow flow; + private static final Collection SCOPE = + Arrays.asList(PlusScopes.USERINFO_EMAIL, PlusScopes.PLUS_LOGIN); + private static final JsonFactory JSON_FACTORY = new JacksonFactory(); + private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + flow = + new GoogleAuthorizationCodeFlow.Builder( + HTTP_TRANSPORT, + JSON_FACTORY, + System.getenv("CLIENT_ID"), + System.getenv("CLIENT_SECRET"), + SCOPE) + .build(); + String state = new BigInteger(130, new SecureRandom()).toString(32); + req.getSession().setAttribute("state", state); + if(req.getAttribute("loginDestination") != null) { + req.getSession().setAttribute("loginDestination", req.getAttribute("loginDestination")); + } else { + req.getSession().setAttribute("loginDestination", "/books"); + } + // callback url should be the one registered in Google Developers Console + String url = + flow.newAuthorizationUrl() + .setRedirectUri(System.getenv("CALLBACK_URL")) + .setState(state) + .build(); + resp.sendRedirect(url); + } +} diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LogoutServlet.java similarity index 59% rename from 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LogoutServlet.java index 19d44a857..b34710fc9 100644 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/LogoutServlet.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; - -import com.example.appengine.gettingstartedjava.daos.BookDao; +package com.example.appengine.gettingstartedjava.auth; import java.io.IOException; @@ -26,20 +24,17 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +@WebServlet(name = "logout", value = "/logout") @SuppressWarnings("serial") -@WebServlet(name = "delete", value = "/delete") -public class DeleteBookServlet extends HttpServlet { +public class LogoutServlet 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); - } + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + // you can also make an authenticated request to logout + req.getSession().removeAttribute("token"); + req.getSession().removeAttribute("userEmail"); + req.getSession().removeAttribute("userImageUrl"); + req.getRequestDispatcher("/books").forward(req, resp); } } diff --git a/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/Oauth2CallbackServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/Oauth2CallbackServlet.java new file mode 100644 index 000000000..689ca6537 --- /dev/null +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/auth/Oauth2CallbackServlet.java @@ -0,0 +1,101 @@ +/** + * 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.auth; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.plus.PlusScopes; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(name = "oauth2callback", value = "/oauth2callback") +@SuppressWarnings("serial") +public class Oauth2CallbackServlet extends HttpServlet { + + private GoogleAuthorizationCodeFlow flow; + private static final Collection SCOPE = + Arrays.asList(PlusScopes.USERINFO_EMAIL, PlusScopes.PLUS_LOGIN); + private static final String LOGIN_API_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; + private static final JsonFactory JSON_FACTORY = new JacksonFactory(); + private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, + ServletException { + // Ensure that this is no request forgery going on, and that the user + // sending us this connect request is the user that was supposed to. + if (!req.getParameter("state").equals(req.getSession().getAttribute("state"))) { + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + resp.getWriter().print("Invalid state parameter."); + return; + } + // remove one-time use state + req.getSession().removeAttribute("state"); + + flow = + new GoogleAuthorizationCodeFlow.Builder( + HTTP_TRANSPORT, + JSON_FACTORY, + System.getenv("CLIENT_ID"), + System.getenv("CLIENT_SECRET"), + SCOPE) + .build(); + final TokenResponse tockenResponse = + flow.newTokenRequest(req.getParameter("code")) + .setRedirectUri(System.getenv("CALLBACK_URL")) + .execute(); + + // keep track of the token + req.getSession().setAttribute("token", tockenResponse.toString()); + final Credential credential = flow.createAndStoreCredential(tockenResponse, null); + final HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential); + // Make an authenticated request + final GenericUrl url = new GenericUrl(LOGIN_API_URL); + final HttpRequest request = requestFactory.buildGetRequest(url); + request.getHeaders().setContentType("application/json"); + + final String jsonIdentity = request.execute().parseAsString(); + @SuppressWarnings("unchecked") + HashMap userIdResult = + new ObjectMapper().readValue(jsonIdentity, HashMap.class); + // from this map, extract the relevant profile info and store it in the session + req.getSession().setAttribute("userEmail", userIdResult.get("email")); + req.getSession().setAttribute("userId", userIdResult.get("id")); + req.getSession().setAttribute("userImageUrl", userIdResult.get("picture")); + req.getRequestDispatcher( + req.getSession().getAttribute("loginDestination").toString()).forward(req, resp); + } +} diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/CreateBookServlet.java similarity index 74% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/CreateBookServlet.java index 79a18461d..51a423ae6 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/CreateBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/CreateBookServlet.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; 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 java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; @@ -34,12 +36,17 @@ @WebServlet(name = "create", value = "/create") public class CreateBookServlet extends HttpServlet { + private final Logger logger = + Logger.getLogger( + com.example.appengine.gettingstartedjava.basicactions.CreateBookServlet.class.getName()); + @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); + req.setAttribute("page", "form"); + req.getRequestDispatcher("/base.jsp").forward(req, resp); } @Override @@ -49,8 +56,16 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Serv (CloudStorageHelper) req.getServletContext().getAttribute("storageHelper"); String imageUrl = storageHelper.getImageUrl(req, resp); BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); + String createdByString = ""; + String createdByIdString = ""; + if (req.getSession().getAttribute("token") != null) { + createdByString = req.getSession().getAttribute("userEmail").toString(); + createdByIdString = req.getSession().getAttribute("userId").toString(); + } Book book = new Book.Builder() .author(req.getParameter("author")) + .createdBy(createdByString) + .createdById(createdByIdString) .description(req.getParameter("description")) .publishedDate(req.getParameter("publishedDate")) .title(req.getParameter("title")) @@ -58,6 +73,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Serv .build(); try { Long id = dao.createBook(book); + logger.log(Level.INFO, "Created book {0}", 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/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/DeleteBookServlet.java similarity index 95% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/DeleteBookServlet.java index 19d44a857..ca0feb6d4 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/DeleteBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/DeleteBookServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; import com.example.appengine.gettingstartedjava.daos.BookDao; diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListBookServlet.java similarity index 83% rename from 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListBookServlet.java index f2e52745f..c2a91c765 100644 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListBookServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; import com.example.appengine.gettingstartedjava.daos.BookDao; import com.example.appengine.gettingstartedjava.objects.Book; @@ -22,6 +22,8 @@ import java.io.IOException; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; @@ -33,6 +35,10 @@ @WebServlet(name = "list", value = "/books") public class ListBookServlet extends HttpServlet { + private final Logger logger = + Logger.getLogger( + com.example.appengine.gettingstartedjava.basicactions.ListBookServlet.class.getName()); + @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { @@ -42,6 +48,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc String endCursor = null; try { Result result = dao.listBooks(startCursor); + logger.log(Level.INFO, "Retrieved list of all books"); books = result.result; endCursor = result.cursor; } catch (Exception e) { @@ -49,6 +56,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc } req.getSession().getServletContext().setAttribute("books", books); req.setAttribute("cursor", endCursor); + req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListByUserServlet.java similarity index 76% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListByUserServlet.java index f2e52745f..02bb87a45 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ListBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ListByUserServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; import com.example.appengine.gettingstartedjava.daos.BookDao; import com.example.appengine.gettingstartedjava.objects.Book; @@ -30,18 +30,24 @@ import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") -@WebServlet(name = "list", value = "/books") -public class ListBookServlet extends HttpServlet { +@WebServlet(name = "listbyuser", value = "/books/mine") +public class ListByUserServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + if (req.getSession().getAttribute("token") == null) { + req.setAttribute("loginDesintaion", "/books/mine"); + req.getRequestDispatcher("/login").forward(req, resp); + return; + } BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); String startCursor = req.getParameter("cursor"); List books = null; String endCursor = null; try { - Result result = dao.listBooks(startCursor); + Result result = dao.listBooksByUser( + req.getSession().getAttribute("userId").toString(), startCursor); books = result.result; endCursor = result.cursor; } catch (Exception e) { @@ -49,6 +55,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc } req.getSession().getServletContext().setAttribute("books", books); req.setAttribute("cursor", endCursor); + req.setAttribute("page", "list"); req.getRequestDispatcher("/base.jsp").forward(req, resp); } } diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ReadBookServlet.java similarity index 77% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ReadBookServlet.java index cb7cd1a75..7939d01d0 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/ReadBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/ReadBookServlet.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; import com.example.appengine.gettingstartedjava.daos.BookDao; import com.example.appengine.gettingstartedjava.objects.Book; import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; @@ -31,6 +33,10 @@ @WebServlet(name = "read", value = "/read") public class ReadBookServlet extends HttpServlet { + private final Logger logger = + Logger.getLogger( + com.example.appengine.gettingstartedjava.basicactions.ReadBookServlet.class.getName()); + @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { @@ -38,8 +44,10 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc BookDao dao = (BookDao) this.getServletContext().getAttribute("dao"); try { Book book = dao.readBook(id); + logger.log(Level.INFO, "Read book with id {0}", id); req.setAttribute("book", book); - req.getRequestDispatcher("/view.jsp").forward(req, resp); + req.setAttribute("page", "view"); + req.getRequestDispatcher("/base.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/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/UpdateBookServlet.java similarity index 77% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/UpdateBookServlet.java index dbd440277..917105529 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/servlets/UpdateBookServlet.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/basicactions/UpdateBookServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.appengine.gettingstartedjava.servlets; +package com.example.appengine.gettingstartedjava.basicactions; import com.example.appengine.gettingstartedjava.daos.BookDao; import com.example.appengine.gettingstartedjava.objects.Book; @@ -43,7 +43,8 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Servl req.setAttribute("book", book); req.setAttribute("action", "Edit"); req.setAttribute("destination", "update"); - req.getRequestDispatcher("/form.jsp").forward(req, resp); + req.setAttribute("page", "form"); + req.getRequestDispatcher("/base.jsp").forward(req, resp); } catch (Exception e) { throw new ServletException("Error loading book for editing", e); } @@ -56,15 +57,18 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Serv (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 { + Book oldBook = dao.readBook(Long.decode(req.getParameter("id"))); + Book book = new Book.Builder() + .author(req.getParameter("author")) + .createdBy(oldBook.getCreatedBy()) + .createdById(oldBook.getCreatedById()) + .description(req.getParameter("description")) + .id(Long.decode(req.getParameter("id"))) + .publishedDate(req.getParameter("publishedDate")) + .title(req.getParameter("title")) + .imageUrl(imageUrl) + .build(); dao.updateBook(book); resp.sendRedirect("/read?id=" + req.getParameter("id")); } catch (Exception e) { diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java similarity index 92% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java index f66ff62ae..8c440e66b 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/BookDao.java @@ -30,4 +30,6 @@ public interface BookDao { public void deleteBook(Long bookId) throws Exception; public Result listBooks(String startCursor) throws Exception; + + public Result listBooksByUser(String userId, String startCursor) throws Exception; } diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java similarity index 51% rename from 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java index f6da47491..848e3ea15 100644 --- a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/CloudSqlDao.java @@ -37,8 +37,9 @@ 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))"; + + "AUTO_INCREMENT, author VARCHAR(255), createdBy VARCHAR(255), createdById 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); } @@ -46,15 +47,19 @@ public CloudSqlDao() throws SQLException { @Override public Long createBook(Book book) throws SQLException { - final String createBookString = "INSERT INTO books (author, description, publishedDate, title) " - + "VALUES (?, ?, ?, ?)"; + final String createBookString = "INSERT INTO books " + + "(author, createdBy, createdById, 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(2, book.getCreatedBy()); + createBookStmt.setString(3, book.getCreatedById()); + createBookStmt.setString(4, book.getDescription()); + createBookStmt.setString(5, book.getPublishedDate()); + createBookStmt.setString(6, book.getTitle()); + createBookStmt.setString(7, book.getImageUrl()); createBookStmt.executeUpdate(); try (ResultSet keys = createBookStmt.getGeneratedKeys()) { keys.next(); @@ -72,11 +77,14 @@ public Book readBook(Long bookId) throws SQLException { 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")) + .author(keys.getString(Book.AUTHOR)) + .createdBy(keys.getString(Book.CREATED_BY)) + .createdById(keys.getString(Book.CREATED_BY_ID)) + .description(keys.getString(Book.DESCRIPTION)) + .id(keys.getLong(Book.ID)) + .publishedDate(keys.getString(Book.PUBLISHED_DATE)) + .title(keys.getString(Book.TITLE)) + .imageUrl(keys.getString(Book.IMAGE_URL)) .build(); } } @@ -84,15 +92,18 @@ public Book readBook(Long bookId) throws SQLException { @Override public void updateBook(Book book) throws SQLException { - final String updateBookString = "UPDATE books SET author = ?, description = ?, " - + "publishedDate = ?, title = ? WHERE id = ?"; + final String updateBookString = "UPDATE books SET author = ?, createdBy = ?, createdById = ?, " + + "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.setLong(5, book.getId()); + updateBookStmt.setString(2, book.getCreatedBy()); + updateBookStmt.setString(3, book.getCreatedById()); + updateBookStmt.setString(4, book.getDescription()); + updateBookStmt.setString(5, book.getPublishedDate()); + updateBookStmt.setString(6, book.getTitle()); + updateBookStmt.setString(7, book.getImageUrl()); + updateBookStmt.setLong(8, book.getId()); updateBookStmt.executeUpdate(); } } @@ -113,8 +124,9 @@ public Result listBooks(String cursor) throws SQLException { 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 ?"; + final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + + "description, id, publishedDate, title, imageUrl FROM books ORDER BY title ASC " + + "LIMIT 10 OFFSET ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { listBooksStmt.setInt(1, offset); @@ -122,11 +134,57 @@ public Result listBooks(String cursor) throws SQLException { 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")) + .author(rs.getString(Book.AUTHOR)) + .createdBy(rs.getString(Book.CREATED_BY)) + .createdById(rs.getString(Book.CREATED_BY_ID)) + .description(rs.getString(Book.DESCRIPTION)) + .id(rs.getLong(Book.ID)) + .publishedDate(rs.getString(Book.PUBLISHED_DATE)) + .title(rs.getString(Book.TITLE)) + .imageUrl(rs.getString(Book.IMAGE_URL)) + .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); + } + } + } + } + + @Override + public Result listBooksByUser(String userId, String startCursor) throws Exception { + int offset = 0; + if (startCursor != null && !startCursor.equals("")) { + offset = Integer.parseInt(startCursor); + } + final String listBooksString = "SELECT SQL_CALC_FOUND_ROWS author, createdBy, createdById, " + + "description, id, publishedDate, title, imageUrl FROM books WHERE createdById = ? " + + "ORDER BY title ASC LIMIT 10 OFFSET ?"; + try (Connection conn = dataSource.getConnection(); + PreparedStatement listBooksStmt = conn.prepareStatement(listBooksString)) { + listBooksStmt.setString(1, userId); + listBooksStmt.setInt(2, offset); + List resultBooks = new ArrayList<>(); + try (ResultSet rs = listBooksStmt.executeQuery()) { + while (rs.next()) { + Book book = new Book.Builder() + .author(rs.getString(Book.AUTHOR)) + .createdBy(rs.getString(Book.CREATED_BY)) + .createdById(rs.getString(Book.CREATED_BY_ID)) + .description(rs.getString(Book.DESCRIPTION)) + .id(rs.getLong(Book.ID)) + .publishedDate(rs.getString(Book.PUBLISHED_DATE)) + .title(rs.getString(Book.TITLE)) + .imageUrl(rs.getString(Book.IMAGE_URL)) .build(); resultBooks.add(book); } diff --git a/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java new file mode 100644 index 000000000..f0c359ce7 --- /dev/null +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/daos/DatastoreDao.java @@ -0,0 +1,191 @@ +/** + * 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.google.gcloud.datastore.StructuredQuery.PropertyFilter; + +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(Book.AUTHOR, book.getAuthor()) + .set(Book.CREATED_BY, book.getCreatedBy()) + .set(Book.CREATED_BY_ID, book.getCreatedById()) + .set(Book.DESCRIPTION, book.getDescription()) + .set(Book.PUBLISHED_DATE, book.getPublishedDate()) + .set(Book.TITLE, book.getTitle()) + .set(Book.IMAGE_URL, 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(Book.AUTHOR)) + .createdBy( + bookEntity.contains(Book.CREATED_BY) ? bookEntity.getString(Book.CREATED_BY) : "") + .createdById( + bookEntity.contains( + Book.CREATED_BY_ID) ? bookEntity.getString(Book.CREATED_BY_ID) : "") + .description(bookEntity.getString(Book.DESCRIPTION)) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString(Book.PUBLISHED_DATE)) + .title(bookEntity.getString(Book.TITLE)) + .imageUrl(bookEntity.contains(Book.IMAGE_URL) ? bookEntity.getString(Book.IMAGE_URL) : null) + .build(); + } + + @Override + public void updateBook(Book book) { + Key key = keyFactory.newKey(book.getId()); + Entity entity = Entity.builder(key) + .set(Book.AUTHOR, book.getAuthor()) + .set(Book.CREATED_BY, book.getCreatedBy()) + .set(Book.CREATED_BY_ID, book.getCreatedById()) + .set(Book.DESCRIPTION, book.getDescription()) + .set(Book.PUBLISHED_DATE, book.getPublishedDate()) + .set(Book.TITLE, book.getTitle()) + .set(Book.IMAGE_URL, 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) { + Cursor startCursor = null; + if(startCursorString != null && !startCursorString.equals("")) { + startCursor = Cursor.fromUrlSafe(startCursorString); + } + Query 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(Book.AUTHOR)) + .createdBy( + bookEntity.contains(Book.CREATED_BY) ? bookEntity.getString(Book.CREATED_BY) : "") + .createdById( + bookEntity.contains( + Book.CREATED_BY_ID) ? bookEntity.getString(Book.CREATED_BY_ID) : "") + .description(bookEntity.getString(Book.DESCRIPTION)) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString(Book.PUBLISHED_DATE)) + .title(bookEntity.getString(Book.TITLE)) + .imageUrl( + bookEntity.contains(Book.IMAGE_URL) ? bookEntity.getString(Book.IMAGE_URL) : 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); + } + } + + @Override + public Result listBooksByUser(String userId, String startCursorString) throws Exception { + Cursor startCursor = null; + if(startCursorString != null && !startCursorString.equals("")) { + startCursor = Cursor.fromUrlSafe(startCursorString); + } + Query q = Query.entityQueryBuilder() + .kind("Book") + .filter(PropertyFilter.eq(Book.CREATED_BY_ID, userId)) + .limit(10) + .startCursor(startCursor) + // a custom datastore index is required since you are filtering by one property + // but ordering by another + .orderBy(OrderBy.asc(Book.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(Book.AUTHOR)) + .createdBy( + bookEntity.contains(Book.CREATED_BY) ? bookEntity.getString(Book.CREATED_BY) : "") + .createdById( + bookEntity.contains( + Book.CREATED_BY_ID) ? bookEntity.getString(Book.CREATED_BY_ID) : "") + .description(bookEntity.getString(Book.DESCRIPTION)) + .id(bookEntity.key().id()) + .publishedDate(bookEntity.getString(Book.PUBLISHED_DATE)) + .title(bookEntity.getString(Book.TITLE)) + .imageUrl( + bookEntity.contains(Book.IMAGE_URL) ? bookEntity.getString(Book.IMAGE_URL) : 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/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java similarity index 69% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java index 246ec31ba..2121708ff 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/objects/Book.java @@ -20,15 +20,27 @@ public class Book { private String title; private String author; + private String createdBy; + private String createdById; private String publishedDate; private String description; private Long id; private String imageUrl; + public static final String AUTHOR = "author"; + public static final String CREATED_BY = "createdBy"; + public static final String CREATED_BY_ID = "createdById"; + public static final String DESCRIPTION = "description"; + public static final String ID = "id"; + public static final String PUBLISHED_DATE = "publishedDate"; + public static final String TITLE = "title"; + public static final String IMAGE_URL = "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.createdBy = b.createdBy; + this.createdById = b.createdById; this.publishedDate = b.publishedDate; this.description = b.description; this.id = b.id; @@ -38,6 +50,8 @@ private Book(Builder b) { public static class Builder { private String title; private String author; + private String createdBy; + private String createdById; private String publishedDate; private String description; private Long id; @@ -53,6 +67,16 @@ public Builder author(String author) { return this; } + public Builder createdBy(String createdBy) { + this.createdBy = createdBy; + return this; + } + + public Builder createdById(String createdById) { + this.createdById = createdById; + return this; + } + public Builder publishedDate(String publishedDate) { this.publishedDate = publishedDate; return this; @@ -94,6 +118,22 @@ public void setAuthor(String author) { this.author = author; } + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getCreatedById() { + return createdById; + } + + public void setCreatedById(String createdById) { + this.createdById = createdById; + } + public String getPublishedDate() { return publishedDate; } @@ -128,6 +168,8 @@ public void setImageUrl(String imageUrl) { @Override public String toString() { - return author; + return + "Title: " + title + ", Author: " + author + ", Published date: " + publishedDate + + ", Added by: " + createdBy; } } diff --git a/2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java similarity index 100% rename from 2-structured-data/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/objects/Result.java diff --git a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java similarity index 88% rename from 3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java rename to bookshelf/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java index 6b7085b77..a09d8a1df 100644 --- a/3-binary-data/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java +++ b/bookshelf/src/main/java/com/example/appengine/gettingstartedjava/util/CloudStorageHelper.java @@ -33,6 +33,8 @@ import java.nio.channels.Channels; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -41,6 +43,9 @@ public class CloudStorageHelper { + private final Logger logger = + Logger.getLogger( + com.example.appengine.gettingstartedjava.util.CloudStorageHelper.class.getName()); private static Storage storage = null; public CloudStorageHelper() { @@ -52,7 +57,8 @@ public String uploadFile(Part filePart) throws IOException { DateTime dt = DateTime.now(DateTimeZone.UTC); String dtString = dt.toString(dtf); final String fileName = filePart.getSubmittedFileName() + dtString; - String BUCKET_NAME = System.getenv("BUCKET_NAME"); + final 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)); @@ -62,6 +68,9 @@ public String uploadFile(Part filePart) throws IOException { ByteStreams.copy(filecontent, Channels.newOutputStream(blobWriter)); blobWriter.close(); blobInfo = storage.get(BUCKET_NAME, fileName); + logger.log( + Level.INFO, "Uploaded file {0} as {1}", new Object[]{ + filePart.getSubmittedFileName(), fileName}); return blobInfo.mediaLink(); } diff --git a/bookshelf/src/main/webapp/WEB-INF/appengine-web.xml b/bookshelf/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 000000000..66cdc3760 --- /dev/null +++ b/bookshelf/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,39 @@ + + + + true + true + true + + + + + + + + + + + + + + + + + + + diff --git a/bookshelf/src/main/webapp/WEB-INF/datastore-indexes.xml b/bookshelf/src/main/webapp/WEB-INF/datastore-indexes.xml new file mode 100644 index 000000000..e46d2f5d4 --- /dev/null +++ b/bookshelf/src/main/webapp/WEB-INF/datastore-indexes.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/bookshelf/src/main/webapp/WEB-INF/logging.properties b/bookshelf/src/main/webapp/WEB-INF/logging.properties new file mode 100644 index 000000000..649083e1c --- /dev/null +++ b/bookshelf/src/main/webapp/WEB-INF/logging.properties @@ -0,0 +1,27 @@ +# 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. +# +# A default java.util.logging configuration. +# (All App Engine logging is through java.util.logging by default). +# +# To use this configuration, copy it into your application's WEB-INF +# folder and add the following to your appengine-web.xml: +# +# +# +# +# + +# Set the default logging level for all loggers to INFO +.level = INFO diff --git a/bookshelf/src/main/webapp/WEB-INF/web.xml b/bookshelf/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..de2f4f0ef --- /dev/null +++ b/bookshelf/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + root + com.example.appengine.gettingstartedjava.RootServlet + + + root + / + + diff --git a/bookshelf/src/main/webapp/base.jsp b/bookshelf/src/main/webapp/base.jsp new file mode 100644 index 000000000..6accbbf80 --- /dev/null +++ b/bookshelf/src/main/webapp/base.jsp @@ -0,0 +1,56 @@ +<%@ 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 + + + + + + + + + diff --git a/bookshelf/src/main/webapp/form.jsp b/bookshelf/src/main/webapp/form.jsp new file mode 100644 index 000000000..e700f0e23 --- /dev/null +++ b/bookshelf/src/main/webapp/form.jsp @@ -0,0 +1,60 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> + +
+

+ + book +

+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+
diff --git a/bookshelf/src/main/webapp/list.jsp b/bookshelf/src/main/webapp/list.jsp new file mode 100644 index 000000000..5db068fff --- /dev/null +++ b/bookshelf/src/main/webapp/list.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" %> + +
+

Books

+ + + Add book + + + +

No books found

+
+ + + + + + + + +
+
\ No newline at end of file diff --git a/bookshelf/src/main/webapp/view.jsp b/bookshelf/src/main/webapp/view.jsp new file mode 100644 index 000000000..c5c49c295 --- /dev/null +++ b/bookshelf/src/main/webapp/view.jsp @@ -0,0 +1,46 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> + +
+

Book

+ + +
+
+ +
+
+

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

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

${fn:escapeXml(book.description)}

+ Added by + ${fn:escapeXml(not empty book.createdBy?book.createdBy:'Anonymous')} +
+
+
diff --git a/1-helloworld/pom.xml b/helloworld/pom.xml similarity index 96% rename from 1-helloworld/pom.xml rename to helloworld/pom.xml index eabd9d27c..784b19d5f 100644 --- a/1-helloworld/pom.xml +++ b/helloworld/pom.xml @@ -55,9 +55,6 @@ Copyright 2015 Google Inc. All Rights Reserved. com.google.appengine gcloud-maven-plugin 2.0.9.89.v20151202 - - true -
diff --git a/1-helloworld/src/main/java/com/example/appengine/gettingstartedjava/helloworld/HelloServlet.java b/helloworld/src/main/java/com/example/appengine/gettingstartedjava/helloworld/HelloServlet.java similarity index 100% rename from 1-helloworld/src/main/java/com/example/appengine/gettingstartedjava/helloworld/HelloServlet.java rename to helloworld/src/main/java/com/example/appengine/gettingstartedjava/helloworld/HelloServlet.java diff --git a/1-helloworld/src/main/webapp/WEB-INF/appengine-web.xml b/helloworld/src/main/webapp/WEB-INF/appengine-web.xml similarity index 100% rename from 1-helloworld/src/main/webapp/WEB-INF/appengine-web.xml rename to helloworld/src/main/webapp/WEB-INF/appengine-web.xml