Objectify

Objectify is a Java API created for the datastore.
The datastore is the official repository of Google's cloud.
If you think of datastore as a hash map of a hash map, you can easily understand it.

Add Objectify dependencies to pom.xml

Add the following to the dependencies in pom.xml.

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.0</version>
</dependency>
<dependency>
    <groupId>com.googlecode.objectify</groupId>
    <artifactId>objectify</artifactId>
    <version>5.1.21</version>
</dependency>

The guestbook project is using Objectify, so the above configuration already exists.

Add Delete function

Let's look at the Objectify code snippets implemented in guestbook.jsp.

Fetch List
List<Greeting> greetings = ObjectifyService.ofy()
	.load()
	.type(Greeting.class) // We want only Greetings
	.ancestor(theBook)    // Anyone in this book
	.order("-date")       // Most recent first - date is indexed.
	.limit(5)             // Only show 5 of them.
	.list();
Save Entity
ObjectifyService.ofy().save().entity(greeting).now();

Here is a objectify code snippet that we will add.

Delete Entity
ObjectifyService.ofy().delete().key(key).now();

Where key is the unique key of the entity. The unique key is obtained with the following code.

Key.create(theBook, Greeting.class, id);

Using this code, you can get the unique key for each Greeting object in guestbook.jsp. To do this, first add a method in Greeting.java that returns its own key as follows:

public Key<Greeting> getKey() {
	return Key.create(theBook, Greeting.class, id);
}

The type of Key is com.googlecode.objectify.Key. Key has toWebSafeString(), a method that returns a String that can be restored to itself. Add the following highlighting in the guestbook.jsp.

// Look at all of our greetings
for (Greeting greeting : greetings) {
	pageContext.setAttribute("greeting_content", greeting.content);
	pageContext.setAttribute("keyString", greeting.getKey().toWebSafeString());
		
	//..omit..

Using the keyString stored in pageContext, you can create a link that passes the keyString to the JavaScript function.
(Javascript and the required form tags will be covered soon) Add the following highlighting in the guestbook.jsp.

<p><b>${fn:escapeXml(greeting_user)}</b> wrote:</p>
<blockquote>${fn:escapeXml(greeting_content)}</blockquote>
<blockquote><a href="javascript:del('${keyString }')">Del</a></blockquote>

If you implement the above, the user can delete posts, not the author. Even non-logged-in users can delete posts. Spring security tags allow you to selectively render views based on permissions. To compare a login user with an author, you need to store the author ID in the pageContext. Modify guestbook.jsp as follows.

// Look at all of our greetings
for (Greeting greeting : greetings) {
    pageContext.setAttribute("greeting_content", greeting.content);
    pageContext.setAttribute("keyString", greeting.getKey().toWebSafeString());
    String author;
    String author_id = null;
    if (greeting.author_email == null) {
        author = "An anonymous person";
    } else {
        author = greeting.author_email;
        author_id = greeting.author_id;
        if (user != null && user.getUserId().equals(author_id)) {
            author += " (You)";
        }
    }
    pageContext.setAttribute("greeting_user", author);
    pageContext.setAttribute("author_id", author_id);
    
    //..omit..

You are all ready to compare the login user with the author.
Add the following highlighting in the guestbook.jsp.

<p><b>${fn:escapeXml(greeting_user)}</b> wrote:</p>
<blockquote>${fn:escapeXml(greeting_content)}</blockquote>
<security:authorize access="isAuthenticated() and (#author_id == principal.userId or hasRole('ROLE_ADMIN'))">
	<blockquote><a href="javascript:del('${keyString }')">Del</a></blockquote>
</security:authorize>

To use the Spring Security tag, add the Spring Security tag library directive to guestbook.jsp.

<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>

Add the following JavaScript function to guestbook.jsp.

<script type="text/javascript">
function del(key) {
	var check = confirm('Are you sure you want to delete this greeting?');
	if (check) {
    	var form = document.getElementById("delForm");
    	form.keyString.value = key;
    	form.submit();
	}
}
</script>

Add the following form to guestbook.jsp.

<form id="delForm" action="/guestbook/del" method="post" style="display: none;">
	<input type="hidden" name="keyString" />
	<input type="hidden" name="guestbookName" value="${fn:escapeXml(guestbookName)}"/>
	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>

Add a handler to the GuestbookController that handles the "/guestbook/del" request as shown below.

@PostMapping("/guestbook/del")
public String del(String guestbookName, String keyString) {
	Key<Greeting> key = Key.create(keyString);
	ObjectifyService.ofy().delete().key(key).now();
	return "redirect:/guestbook/?guestbookName=" + guestbookName;
}	

This code is not performing user verification. Method security in Spring Security allows you to verify that the login user is the author on the server side. Spring official documentation recommends implementing method security in the service layer. Create a service layer as shown below.

GuestbookService.java
package net.java_school.guestbook;

import org.springframework.stereotype.Service;

@Service
public interface GuestbookService {
	public void sign(Greeting greeting);
	public void del(Greeting greeting);
}
GuestbookServiceImpl.java
package net.java_school.guestbook;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import com.googlecode.objectify.Key;
import static com.googlecode.objectify.ObjectifyService.ofy;//1.

@Service
public class GuestbookServiceImpl implements GuestbookService{
	public void sign(Greeting greeting) {
		ofy().save().entity(greeting).now();//1.
	}

	@PreAuthorize("isAuthenticated() and (#greeting.author_id == principal.userId or hasRole('ROLE_ADMIN'))")//2.
	public void del(Greeting greeting) {
		Key<Greeting> key = greeting.getKey();
		ofy().delete().key(key).now();//1.
	}
}

1. You need an import static com.googlecode.objectify.ObjectifyService.ofy; to use Objectify codes as below:

ofy().save().entity(greeting).now();
ofy().delete().key(key).now();

2. In the annotation of the del() method, #greeting.author_id calls the getAuthor_id() method of the greeting instance.
So you need to add the following getter to Greeting.java:

Greeting.java
public String getAuthor_id() {
	return author_id;
}

Modify the controller to use the service layer.

GuestbookController.java
//omit
import net.java_school.spring.security.GaeUserAuthentication;
import net.java_school.user.GaeUser;
import static com.googlecode.objectify.ObjectifyService.ofy;
//omit

@Autowired
private GuestbookService guestbookService;

//omit

@PostMapping("/guestbook/sign")
public String sign(String guestbookName, String content, GaeUserAuthentication gaeUserAuthentication) {
	Greeting greeting = null;

	if (gaeUserAuthentication != null) {
		GaeUser gaeUser = (GaeUser) gaeUserAuthentication.getPrincipal();
		greeting = new Greeting(guestbookName, content, gaeUser.getUserId(), gaeUser.getEmail());
	} else {
		greeting = new Greeting(guestbookName, content);
	}
	
	guestbookService.sign(greeting);
	
	return "redirect:/guestbook/?guestbookName=" + guestbookName;
}

@PostMapping("/guestbook/del")
public String del(String guestbookName, String keyString) {
	Key<Greeting> key = Key.create(keyString);
	Greeting greeting = ofy().load().key(key).now();
	guestbookService.del(greeting);
	return "redirect:/guestbook/?guestbookName=" + guestbookName;
}

Local Test

mvn clean
mvn appengine:run

When a user who is not logged in visits the guestbook, they can not see the Del link.
not login Log in as a normal user.
Normal user login screen User can see links to delete his posts.
Normal user login Log out and log in as an administrator.
Admin user login screen Administrator can see the Delete link for all posts.
Delete an article written by an anonymous user.
Admin login Confirm that the article written by the anonymous user has been deleted.
delete Anomynous user's greeting

References