Categories
Computing

Android: NullPointerException when scrolling through a MapView w/ ItemizedOverlay

When I implemented a MapView with an ItemizedOverlay (the implementation steps are described in the Android developer documentation), I encountered a flaw in the API, or at least in the corresponding documentation. If you add an ItemizedOverlay to your MapView, but do not add any OverlayItems (which may be the default for your application, with OverlayItems being added during runtime), scrolling through the MapView causes the following NullPointerException:

ERROR/AndroidRuntime(232): java.lang.NullPointerException
  at com.google.android.maps.ItemizedOverlay.getItemsAtLocation(ItemizedOverlay.java:617)
  at com.google.android.maps.ItemizedOverlay.getItemAtLocation(ItemizedOverlay.java:586)
  at com.google.android.maps.ItemizedOverlay.handleMotionEvent(ItemizedOverlay.java:498)
  at com.google.android.maps.ItemizedOverlay.onTouchEvent(ItemizedOverlay.java:572)
  at com.google.android.maps.OverlayBundle.onTouchEvent(OverlayBundle.java:63)
  at com.google.android.maps.MapView.onTouchEvent(MapView.java:625)
  at android.view.View.dispatchTouchEvent(View.java:3709)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
  at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
  at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:822)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
  at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
  at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
  at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:123)
  at android.app.ActivityThread.main(ActivityThread.java:4363)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:521)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
  at dalvik.system.NativeStart.main(Native Method)

After long hours of debugging, I was able to find help in the Android Bug Tracking system. An easy workaround for this problem (which should really be addressed in the API and/or the corresponding documentation) is to add a call to the “populate()” function in the constructor of the class that extends ItemizedOverlay. Then, your subclass constructor should look like this:

super(boundCenterBottom(defaultMarker));
populate();

This solves the problem and scrolling, even without any OverlayItems, works like a charm.

Categories
Computing

Java Servlet & JSP Form-based File Upload

Surprisingly, Java Servlets and JSP do not have build-in support for handling form-based file uploads. However, there are several open source libraries available for this purpose, among which the Apache Commons FileUpload is one of the most stable. This post will demonstrate how to use this library to handle form-based file uploads using JSP technology.

Two files will be required for this to work: the Apache Commons FileUpload library, as well as the Apache Commons IO library.

In order to install them correctly, one needs to extract the downloaded archives and copy the commons-fileupload-<version>.jar and commons-io-<version>.jar to the WEB-INF/lib directory of the web app that is to be developed. If these libraries rather be available to all web applications on the server, the jar files should be copied to $CATALINA_HOME/shared/lib/, where $CATALINE_HOME is the root directory of the Tomcat installation.

The following assumes that two files have been set up: first, a simple HTML file with a form that allows the user to select the file to be uploaded. Note that enctype=”multipart/form-data” is essential here.

<form method="POST" action="action.jsp" enctype="multipart/form-data">
  <input type="file" name="file"/>
  <input type="submit"/>
</form>

Second, a file action.jsp that receives the HTML file’s HTTP request and processes it. To do so, it will need to import several packages:

< %@page import="org.apache.commons.fileupload.servlet.*"%>
< %@page import="org.apache.commons.fileupload.disk.*"%>
< %@page import="org.apache.commons.fileupload.*"%>
< %@page import="java.io.InputStream"%>

The next thing to do is to check if the HTTP request is correctly encoded in the multipart format.

if (ServletFileUpload.isMultipartContent(request)){
  // Process the request
}

The actual parsing of the request is done via the ServletFileUpload class and the DiskFileItemFactory class. Here, the uploaded file is stored as a byte array in the session.

ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());

try {
  @SuppressWarnings("unchecked")
  List< fileitem> fileItemsList = servletFileUpload.parseRequest(request);
  for (FileItem fileItem : fileItemsList) {
    if (fileItem.isFormField()) {
      /* The fileItem is not a file, but rather a name-value pair. */
      String name = fileItem.getFieldName();
      String value = fileItem.getString();
      // business logic here
    } else {
      /* The fileItem is an uploaded file. */
      byte[] fileData = fileItem.get();
      session.setAttribute("file", fileData);
    }
  }
} catch (Exception ex) {
  /* Possible exceptions include the file exceeding the upload size limit. */
}

This is only a very basic example, of course, and may not perform well with large files for example. Moreover, you might want to change the default settings for parsing the HTTP request, using the methods setSizeThreshold() and setRespository() of the DiskFileItemFactory class and the setSizeMax() method of the ServletFileUpload class.