Permalink
Browse files

New feature: uploading a file (e.g., an image) with each message.

  • Loading branch information...
Max-Hailperin committed May 8, 2012
1 parent a0e6d99 commit 4ca6c3283721995cd5114021f6758938ed46c48e
@@ -2,6 +2,8 @@
import java.util.List;
+import edu.gac.mcs270.messageboard.shared.BlobService;
+import edu.gac.mcs270.messageboard.shared.BlobServiceAsync;
import edu.gac.mcs270.messageboard.shared.Message;
import edu.gac.mcs270.messageboard.shared.MessageStore;
import edu.gac.mcs270.messageboard.shared.MessageStoreAsync;
@@ -13,23 +15,34 @@
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FileUpload;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
/**
* User interface for the message board.
+ * The feature to store images in Blobstore uses code taken from the BlobStore Tutorial
+ * written by the blogger "fishbone":
+ * http://www.fishbonecloud.com/2010/12/tutorial-gwt-application-for-storing.html
* @author max
*/
public class MessageBoard implements EntryPoint {
// The interval in milliseconds between periodic updates.
private static final int UPDATE_INTERVAL_MS = 10000;
private final MessageStoreAsync messageStore = GWT
.create(MessageStore.class);
+ private final BlobServiceAsync blobService = GWT
+ .create(BlobService.class);
private VerticalPanel messagesPanel;
private Label updatingLabel;
private Label failureLabel;
@@ -40,10 +53,12 @@
*/
public void onModuleLoad() {
final VerticalPanel mainPanel = new VerticalPanel();
+ final FormPanel uploadForm = new FormPanel();
final HorizontalPanel entryPanel = new HorizontalPanel();
entryPanel.addStyleName("entryPanel");
- mainPanel.add(entryPanel);
-
+ mainPanel.add(uploadForm);
+ uploadForm.add(entryPanel);
+
final Label authorLabel = new Label("Author:");
final TextBox authorField = new TextBox();
entryPanel.add(authorLabel);
@@ -54,9 +69,23 @@ public void onModuleLoad() {
entryPanel.add(textLabel);
entryPanel.add(textField);
+ final FileUpload upload = new FileUpload();
+ entryPanel.add(upload);
+
final Button postButton = new Button("Post");
entryPanel.add(postButton);
-
+
+ // The upload form, when submitted, will trigger an HTTP call to the
+ // servlet. The following parameters must be set
+ uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
+ uploadForm.setMethod(FormPanel.METHOD_POST);
+
+ // Set Names for the text boxes so that they can be retrieved from the
+ // HTTP call as parameters
+ authorField.setName("author");
+ textField.setName("text");
+ upload.setName("upload");
+
final HorizontalPanel statusPanel = new HorizontalPanel();
statusPanel.setHeight("3em");
updatingLabel = new Label("Updating...");
@@ -74,28 +103,61 @@ public void onModuleLoad() {
postButton.addClickHandler(new ClickHandler(){
@Override
public void onClick(ClickEvent event) {
- messageStore.storeMessage(
- new Message(
- authorField.getText(),
- textField.getText()),
- new AsyncCallback<Void>(){
- @Override
- public void onFailure(Throwable caught) {
- failureLabel.setVisible(true);
- }
-
- @Override
- public void onSuccess(Void result) {
- failureLabel.setVisible(false);
- updateMessages();
- }
- });
- authorField.setText("");
- textField.setText("");
+
+ blobService
+ .getBlobStoreUploadUrl(new AsyncCallback<String>() {
+
+ @Override
+ public void onSuccess(String result) {
+ // Set the form action to the newly created
+ // blobstore upload URL
+ uploadForm.setAction(result.toString());
+
+ // Submit the form to complete the upload
+ uploadForm.submit();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ failureLabel.setVisible(true);
+ }
+ });
+
+ }
+ });
+
+ uploadForm.addSubmitHandler(new FormPanel.SubmitHandler() {
+ public void onSubmit(SubmitEvent event) {
+ // This event is fired just before the form is submitted. We can take
+ // this opportunity to perform validation.
+ if (authorField.getText().length() == 0) {
+ Window.alert("The author is required.");
+ event.cancel();
+ }
+ if (textField.getText().length() == 0) {
+ Window.alert("The text is required.");
+ event.cancel();
+ }
+ if (upload.getFilename().length() == 0) {
+ Window.alert("The upload file is required.");
+ event.cancel();
+ }
+ }
+ });
+
+
+ uploadForm
+ .addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
+ @Override
+ public void onSubmitComplete(SubmitCompleteEvent event) {
+ failureLabel.setVisible(false);
+ updateMessages();
+ uploadForm.reset();
authorField.setFocus(true);
}
+
});
-
+
updateMessages();
new Timer(){
@Override
@@ -133,6 +195,9 @@ public void onSuccess(List<Message> result) {
Label heading = new Label(m.getAuthor());
heading.addStyleName("messageHeading");
messagesPanel.insert(heading, position++);
+ Anchor link = new Anchor("uploaded file", m.getURL());
+ link.setTarget("_blank");
+ messagesPanel.insert(link, position++);
Label body = new Label(m.getText());
body.addStyleName("messageBody");
messagesPanel.insert(body, position++);
@@ -0,0 +1,49 @@
+package edu.gac.mcs270.messageboard.server;
+
+import java.io.IOException;
+
+import javax.jdo.PersistenceManager;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import edu.gac.mcs270.messageboard.shared.BlobService;
+
+import com.google.appengine.api.blobstore.BlobKey;
+import com.google.appengine.api.blobstore.BlobstoreService;
+import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+/* This is extracted from code in the BlobStore Tutorial written by the blogger "fishbone":
+ * http://www.fishbonecloud.com/2010/12/tutorial-gwt-application-for-storing.html */
+
+@SuppressWarnings("serial")
+public class BlobServiceImpl extends RemoteServiceServlet implements
+ BlobService {
+
+ //Start a GAE BlobstoreService session and persistence manager
+ BlobstoreService blobstoreService = BlobstoreServiceFactory
+ .getBlobstoreService();
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+
+ //Generate a Blobstore Upload URL from the GAE BlobstoreService
+ @Override
+ public String getBlobStoreUploadUrl() {
+
+ //Map the UploadURL to the uploadservice which will be called by
+ //submitting the FormPanel
+ return blobstoreService
+ .createUploadUrl("/messageboard/uploadservice");
+ }
+
+ //Override doGet to serve blobs. This will be called when an uploaded file is viewed
+ //in the client
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
+ blobstoreService.serve(blobKey, resp);
+
+ }
+}
@@ -8,7 +8,6 @@
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
-import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
@@ -21,16 +20,9 @@
public class MessageStoreImpl extends RemoteServiceServlet implements
MessageStore {
- private static final long serialVersionUID = 7367373321119740703L;
+ private static final long serialVersionUID = 2637409426426537753L;
- private static final PersistenceManagerFactory pmf =
- JDOHelper.getPersistenceManagerFactory("transactions-optional");
-
- @Override
- public void storeMessage(Message msg) {
- PersistenceManager pm = pmf.getPersistenceManager();
- pm.makePersistent(msg);
- }
+ private static final PersistenceManagerFactory pmf = PMF.get();
@Override
public List<Message> getMessages(Long minimumID) {
@@ -0,0 +1,15 @@
+package edu.gac.mcs270.messageboard.server;
+
+import javax.jdo.JDOHelper;
+import javax.jdo.PersistenceManagerFactory;
+
+public final class PMF {
+ private static final PersistenceManagerFactory pmfInstance =
+ JDOHelper.getPersistenceManagerFactory("transactions-optional");
+
+ private PMF() {}
+
+ public static PersistenceManagerFactory get() {
+ return pmfInstance;
+ }
+}
@@ -0,0 +1,52 @@
+package edu.gac.mcs270.messageboard.server;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.jdo.PersistenceManager;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.appengine.api.blobstore.BlobKey;
+import com.google.appengine.api.blobstore.BlobstoreService;
+import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
+
+import edu.gac.mcs270.messageboard.shared.Message;
+
+/* This is adapted from code in the BlobStore Tutorial written by the blogger "fishbone":
+ * http://www.fishbonecloud.com/2010/12/tutorial-gwt-application-for-storing.html */
+
+//The FormPanel must submit to a servlet that extends HttpServlet
+//RemoteServiceServlet cannot be used
+@SuppressWarnings("serial")
+public class UploadServiceImpl extends HttpServlet {
+
+ //Start Blobstore and persistence manager
+ BlobstoreService blobstoreService = BlobstoreServiceFactory
+ .getBlobstoreService();
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+
+ //Override the doPost method to store the Blob's meta-data
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse res)
+ throws ServletException, IOException {
+
+ @SuppressWarnings("deprecation")
+ Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
+ BlobKey blobKey = blobs.get("upload");
+
+ //Get the paramters from the request to populate the Message object
+ Message msg =
+ new Message(
+ req.getParameter("author"),
+ req.getParameter("text"),
+ //Map the ImageURL to the blobservice servlet, which will serve the image
+ "/messageboard/blobservice?blob-key=" + blobKey.getKeyString()
+ );
+
+ pm.makePersistent(msg);
+ }
+
+}
@@ -0,0 +1,14 @@
+package edu.gac.mcs270.messageboard.shared;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+/* This is extracted from code in the BlobStore Tutorial written by the blogger "fishbone":
+ * http://www.fishbonecloud.com/2010/12/tutorial-gwt-application-for-storing.html */
+
+@RemoteServiceRelativePath("blobservice")
+public interface BlobService extends RemoteService {
+
+ String getBlobStoreUploadUrl();
+
+}
@@ -0,0 +1,9 @@
+package edu.gac.mcs270.messageboard.shared;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public interface BlobServiceAsync {
+
+ void getBlobStoreUploadUrl(AsyncCallback<String> callback);
+
+}
@@ -14,13 +14,16 @@
*/
@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class Message implements Serializable{
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
@Persistent
private String author;
@Persistent
private String text;
+
+ @Persistent
+ private String url;
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
@@ -29,10 +32,12 @@
/**
* @param author the name of the Message's author
* @param text the body of the Message
+ * @param url the URL of the uploaded file
*/
- public Message(String author, String text) {
+ public Message(String author, String text, String url) {
this.author = author;
this.text = text;
+ this.url = url;
}
/**
@@ -49,6 +54,13 @@ public String getText() {
return text;
}
+ /**
+ * @return the URL of the file uploaded with this Message
+ */
+ public String getURL() {
+ return url;
+ }
+
/**
* @return the id of this Message
*/
Oops, something went wrong.

0 comments on commit 4ca6c32

Please sign in to comment.