Skip to content

Conversation

@ihsan314
Copy link
Owner

Two deletion features exist: one simply limits how many comments appear at a time, and the other clears all comments made.

Copy link

@ccondit ccondit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this PR is adding all of the functionality from previous steps (not just delete). Is the parent branch correct?

Comment on lines 33 to 37
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.9.59</version>
</dependency>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - keep these dependencies alphabetical by groupId...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll adjust that.

HashMap<String, String> messages = new HashMap<String, String>();
// ArrayList<String> messages = new ArrayList<String>();
static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
static List<Key> keys = new ArrayList<>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before going further here - what will happen if the server restarts but the data store stays persisted? We'll lose this list of keys. Can you look into another way to get the keys? Possibly through the persistence layer?
https://cloud.google.com/appengine/docs/standard/java/datastore/retrieving-query-results

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll do that.

@ihsan314
Copy link
Owner Author

The first three commits in this pull request were on the week3 branch I believe, and they were approved for merging in pull request #4. I didn't merge them into master, instead I had the mega branch last-steps-week3 built on top of those commits. The last six commits are the only new content, and they are all datastore/deletion based.

I believe it most correctly extends post-comments since the last six commits are built on the commits in post-comments.

@ihsan314 ihsan314 requested a review from ccondit July 14, 2020 18:14
public class DataServlet extends HttpServlet {
static DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
static List<Key> keys = new ArrayList<>();
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** Servlet that returns some example content. TODO: modify this file to handle comments data */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - update this comment to remove the TODO (since it's done!)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

List<Key> keys = DataServlet.keys;
datastore.delete(keys);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Comment").setKeysOnly();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you missing an import here for Query?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I was.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there are more missing imports here. (DatastoreServiceFactory, for instance).

Try invoking mvn compile to ensure that your PR is building as expected.

List<Key> keys = DataServlet.keys;
datastore.delete(keys);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Comment").setKeysOnly();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that we'll probably want to find a common place to keep these constants (like "Comment") so that if they need to change, they will only need to be changed in one spot. No need to do anything now - just something to think about...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that could be a static class member later.

@ihsan314 ihsan314 requested a review from ccondit July 14, 2020 18:57
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Query;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this class exists. And I think you meant to add
import com.google.appengine.api.datastore.Query; to the DeleteServlet.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I've been gettting lazy whoops, I really need to compile before committing.

List<Key> keys = DataServlet.keys;
datastore.delete(keys);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Comment").setKeysOnly();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there are more missing imports here. (DatastoreServiceFactory, for instance).

Try invoking mvn compile to ensure that your PR is building as expected.

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Comment").setKeysOnly();
PreparedQuery keys = datastore.prepare(query);
datastore.delete(keys.asIterable());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DatastoreService.delete takes a Key, not an Entity. Try something like:

PreparedQuery preparedQuery = datastore.prepare(query);
    Iterator<Entity> entities = preparedQuery.asIterator();
    while (entities.hasNext()) {
      Key key = entities.next().getKey();
      datastore.delete(key);	
    }

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I will do that then.

HashMap<String, String> messages = new HashMap<String, String>();
// ArrayList<String> messages = new ArrayList<String>();
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private int maxNumComments;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we initialize this with a default value in case doGet is executed before doPost?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that's a good idea.

} catch (NumberFormatException e) {
System.err.println("Could not convert to int: " + maxNumCommentsString);
System.err.println("Using default value of 7.");
maxNumComments = 7;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a constant for the default value? It would be it easier to read if it ever needs to be changed.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will do that.

}

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This post is doing two things now. It is used to post a comment and also to set the configuration property maxNumComments. Have you consider using a different Servlet to post configuration? Maybe it is overkilled, thoughts?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think each of the two things it's doing are relatively small though, especially setting the configuration property. For this example it's probably overkill, but in the future I'll be careful to scope the post method carefully.

}

Entity commentEntity = new Entity("Comment");
commentEntity.setProperty("username", username);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constants like "Comment", "username", "message" and "timestamp" are used multiple times. Consider defining a constant for those to make it less error prone.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, I'll consider it

import javax.servlet.http.HttpServletResponse;

/** Servlet that deletes data from the database. */
@WebServlet("/delete-data")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally am not a fan of this, but Servlets do have a doDelete method: https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html#doDelete-javax.servlet.http.HttpServletRequest-javax.servlet.http.HttpServletResponse-

In REST API for example methods are used like this:
doPost - to create a new resource
doPut - to update a resource
doGet - to get a resource
doDelete - to delete a resource

If you are interested instead of defining a new servlet to delete you could simply override doDelete() method.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting, I didn't know that, I'll keep that in mind for later.

import javax.servlet.http.HttpServletResponse;

/** Servlet that deletes data from the database. */
@WebServlet("/delete-data")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you consider instead of using 'data' in your Servlet path using a more specific term? For example message.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider that, I shall change it.

@ihsan314 ihsan314 requested a review from ccondit July 15, 2020 01:26
public class DataServlet extends HttpServlet {
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private int maxNumComments;
private final int defaultMaxNumComments = 7;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - per style guide, constants should be UPPER_CASE.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't know that, I'll fix that.

private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private int maxNumComments;
private final int defaultMaxNumComments = 7;
private int maxNumComments = defaultMaxNumComments;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you'll want to leave this variable declaration in the doGet method, otherwise you'll risk a race condition where user 1 gets user 2's max number of comments.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll move that.

Comment on lines 69 to 70
System.err.println("Could not convert to int: " + maxNumCommentsString);
System.err.println("Using default value of 7.");
maxNumComments = 7;
System.err.println("Using default value of " + defaultMaxNumComments + ".");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change anything now, but when the time comes for logging, we'll want to use Java.util.logging, per the app engine guide - just a heads up for the capstone.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok good to know.

import com.google.gson.Gson;

/** Servlet that returns some example content. TODO: modify this file to handle comments data */
/** Servlet that returns some example content.*/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this javadoc now needs to be updated to reflect the new features.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yea I forgot about that, I'll get to it.

HashMap<String, String> messages = new HashMap<String, String>();
// ArrayList<String> messages = new ArrayList<String>();
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private final int defaultMaxNumComments = 7;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be a static constant:

Suggested change
private final int defaultMaxNumComments = 7;
private static final int DEFAULT_MAX_NUM_COMMENTS = 7;

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

// ArrayList<String> messages = new ArrayList<String>();
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private final int defaultMaxNumComments = 7;
private int maxNumComments = defaultMaxNumComments;
Copy link
Collaborator

@kaveh096 kaveh096 Jul 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that since you're defining this field at the class level, when a user changes the number, it'll affect all users using your website. If you only want these comment num changes to affect individual users you should use session-scoped attributes.

For example here you would call request.getSession().setAttribute("maxNumComments", maxNumComments);. And to read it back you call (int)request.getSession().getAttribute("maxNumComments");.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I'll do that then.

Comment on lines 69 to 70
System.err.println("Could not convert to int: " + maxNumCommentsString);
System.err.println("Using default value of " + defaultMaxNumComments + ".");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative approach to format strings is to use the String.format java method. Here are some examples. It'll give you more control and in some cases it's less error prone.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll adjust the code to use String.format.

@ihsan314 ihsan314 requested review from ccondit and kaveh096 July 15, 2020 17:05
import com.google.gson.Gson;

/** Servlet that returns some example content. TODO: modify this file to handle comments data */
/** Servlet that adds/retrieves comments stored in a Datastore*/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - missing a space before the closing comment marker.

Suggested change
/** Servlet that adds/retrieves comments stored in a Datastore*/
/** Servlet that adds/retrieves comments stored in a Datastore */

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I wasn't aware, I'll fix that.

System.err.println("Could not convert to int: " + maxNumCommentsString);
System.err.println("Using default value of " + defaultMaxNumComments + ".");
maxNumComments = defaultMaxNumComments;
System.err.format("Could not convert to int: %d%n", maxNumCommentsString);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a mismatch here since maxNumCommentsString is a string. s/%d/%s/.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll do that replacement.

Entity commentEntity = new Entity("Comment");
commentEntity.setProperty("username", username);
commentEntity.setProperty("message", message);
commentEntity.setProperty("timestamp", System.currentTimeMillis());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One notable omission here is that the comment is never added to the datastore. Do you want to add that here?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be useful for the function of the website, so I'll add it.

@ihsan314 ihsan314 requested a review from ccondit July 16, 2020 00:10
Query query = new Query("Comment").addSort("timestamp", SortDirection.DESCENDING);
PreparedQuery results = datastore.prepare(query);

int maxNumComments = (int)request.getSession().getAttribute("maxNumComments");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just another note here - that the session won't have this attribute on your first page load.

no need to update now, but something to ponder for future work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants