»
December 24, 2009
»

AppEngine BlobStore With Guice

Today I finally started to play with BlobStore introduced in AppEngine 1.3.0. Currently this service is in “experimental” stage (the same is for Task Queue) but works fine.

The only problem I came across was 404 on BlobStore upload success servlet what I had configured using Guice Servlet module:

serve("/upload/finished").with(UploadFinishedServlet.class);

Guice servlet module is bound to Servlet container infrastructure using filter and that filter is routing requests to actual servlets. But for some reason (at least on AppEngine dev server) the request to the callback path bypasses filters and Jetty reports 404. I came up with this solution:

1. declare upload URL callback servlet in web.xml

<servlet>
  <servlet-name>uploadFinished</servlet-name>
  <servlet-class>com.foo.servlet.UploadFinishedServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>uploadFinished</servlet-name>
  <url-pattern>/upload/finished</url-pattern>
</servlet-mapping>

2. create upload URL with that path

// bound to "/upload/finished", see below
@Named("uploadFinishedPath") String uploadFinishedPath;

BlobStoreService bs = blobStoreServiceProvider.get();
String url = bs.createUploadUrl(uploadFinishedPath);

3. in servlet process uploaded BlobKeys and redirect to guice managed servlet

public class UploadFinishedServlet extends HttpServlet {

  public static final String REDIRECT_TO = "/upload/commit";
  public static final String KEY_PARAMETER = "key";
  public static final String FILE_FIELD = "file";

  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) 
      throws ServletException, IOException {
      
    BlobStoreService blobStoreService = BlobstoreServiceFactory.getBlobstoreService();
    Map<String, BlobKey> keys = blobStoreService.getUploadedBlobs(req);
    BlobKey key = keys.get(FILE_FIELD);

    String parameters = getParameters(key);
    resp.sendRedirect(REDIRECT_TO + parameters);
  }

  private String getParameters(BlobKey key) {
    if (key != null) {
      return format("?%s=%s", KEY_PARAMETER, key.getKeyString());
    } else {
      return "";
    }
  }

}

4. In Guice servlet module bind servlet what responds to redirect url

bindConstant().annotatedWith(named("uploadFinishedPath")).to("/upload/finished");
bindConstant().annotatedWith(named("uploadFileField")).to(UploadFinishedServlet.FILE_FIELD);
serve(UploadedServlet.REDIRECT_TO).with(CommitUploadServlet.class);
@Singleton
public class CommitUploadServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
      throws ServletException, IOException {

    String key = req.getParameter(UploadFinishedServlet.KEY_PARAMETER);

    resp.setContentType("text/html");
    resp.setStatus(200);

    String result = key != null ? key : "";
    resp.getOutputStream().write(result.getBytes());
  }

}
 
Internet Explorer 6
Are you serious?