Connecting to the GAE development server running on another machine

Google App Engine comes with a nice development server located in APP_ENGINE_HOME/bin/dev_appserver.sh

The syntax to run it is:  dev_appserver.sh PATH_OF_YOUR_GAE_EXPLODED_WAR

When you do that, your application is accessible from http://localhost:8080, however connecting to it from another machine using http://YOUR_IP:8080 will probably not work.

If this is your case, then it’s probably because it only listens on your loopback address (127.0.0.1)

[laurent@localhost ~]$ netstat -an | grep 8080
tcp 0 0 ::ffff:127.0.0.1:8080 :::* LISTE

To fix that, launch your dev appserver with “-a 0.0.0.0” and you should be good to go: dev_appserver.sh -a 0.0.0.0 PATH_OF_YOUR_GAE_EXPLODED_WAR

[laurent@localhost ~]$ netstat -an | grep 8080
tcp        0      0 :::8080                     :::*                        LISTEN

… and if it still doesn’t work, check your firewall settings: maybe the access to post 8080 is blocked.

Laurent KUBASKI

Advertisements

Rotating photos from mobile devices uploaded to Google App Engine

I’m currently working on a Google App Engine application that allows users to upload photos from their mobile device and then view these photos in a slideshow.

So far, so good, until I realized that some of the uploaded images where sometimes rotated.

What the… ?

This is where I learnt about the concept of EXIF metadata. Long story short: each photo on your mobile device is stored as a jpeg file which contains the photo itself, but also a bunch of extra metadata, like the photo orientation.

This photo orientation depends on your mobile device orientation when the photo is taken. Using the iphone as an example, you basically have 4 possible positions:

  • If the iphone “home” button points downwards when the photo is taken, the photo is rotated 90° counter clockwise (“turned to the left” if you prefer)
  • If the iphone “home” button points upwards when the photo is taken, the photo is rotated 90° clockwise (“turned to the right” if you prefer)
  • If the iphone “home” button points to the left when the photo is taken,  the photo is rotated 180°.
  • If the iphone “home” button points to the right when the photo is taken,  the photo is not rotated (I guess this is how the iphone is supposed to be held when taking pictures).

What I ended up doing is:

  1. User uploads a photo
  2. GAE application determines what the photo orientation is and rotates it accordingly
  3. GAE application stores the rotated photo in the datastore

How to determine the photo orientation

As previously explained, you need to access the photo EXIF metadata and get the value of the “orientation” property.

Luckily, there is a great open-source project that can help us extract this value: it’s called metadata-extractor.

Here is how I used this library:


private int getEXIFOrientation(byte[] bytes) {
int orientation = -1; // default = unknown
 ByteArrayInputStream bis = null;
 try {
 bis = new ByteArrayInputStream(bytes);
 Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(bis), false);
 ExifIFD0Directory exifDir = metadata.getDirectory(ExifIFD0Directory.class);
 if (exifDir != null) {
 orientation = exifDir.getInt(274); // 274 is the EXIF orientation standard code
 }
 } catch (Exception e) {
 log.warning("Couldn't extract EXIF orientation from image");
 } finally {
 if (bis != null) try {
 bis.close();
 } catch (IOException e) {
 // nothing
 }
 }
 return orientation;
}

How do I know that 274 is the internal code for the orientation EXIF tag ? Because of this link.

The orientation property can have 8 different values, but only 4 of them (in bold) are used:

  • 1: image is Normal-> that’s the orientation value you get when the iphone home button is on the right
  • 2: image is flipped horizontally
  • 3: image is rotated 180° -> that’s the orientation value you get when the iphone home button is on the left
  • 4: image is flipped vertically
  • 5: image is rotated 90° CCW and flipped vertically
  • 6: image is rotated 90° CCW -> that’s the orientation value you get when the iphone home button is upwards
  • 7: image is rotated 90° CW and flipped vertically
  • 8: image is rotated 90° CW -> that’s the orientation value you get when the iphone home button is downwards

How to rotate the image

That’s the easy part: we just use the GAE Image service, more precisely: ImagesServiceFactory.makeRotate().

  • orientation = 1 -> don’t rotate
  • orientation = 3 -> rotate 180° clockwise
  • orientation = 6 -> rotate 90° clockwise
  • orientation = 8 -> rotate -90° clockwise (or 90° counter clockwise if you prefer).

So we end up with:


private byte[] rotateImage(byte[] bytes) {
byte[] result = bytes;
int orientation = getEXIFOrientation(bytes);
int degrees = -1;
if (orientation == 3) degrees = 180;
else if (orientation == 6) degrees = 90;
else if (orientation == 8) degrees = -90;
if (degrees != -1) {
Image img = ImagesServiceFactory.makeImage(bytes);
Transform rotation = ImagesServiceFactory.makeRotate(degrees);
// GAE changes output format from JPEG to PNG while rotating the image if the expected output format is not given:
OutputSettings settings = new OutputSettings(ImagesService.OutputEncoding.JPEG);
settings.setQuality(1);
img = ImagesServiceFactory.getImagesService().applyTransform(rotation, img, settings);
result = img.getImageData();
}
return result;
}

Laurent KUBASKI

Serving dynamic images in Google App Engine

Serving a static image in Google App Engine is as easy as:


<img src="/images/01.jpg">

But what if the image is stored in the datastore as a BLOB ?

There is an official “Serving Dynamic Images with Google App Engine” article in the Google App Engine knowledge base, but it doesn’t explain all the needed steps to accomplish this… and that’s exactly what I’m going to do.

The thing is: the “src” attribute of the HTML “img” tag doesn’t need to be the path of an image on the server filesystem. It can also be the url of a Servlet that will return an image.

So, assuming that you know the id of your image in the datastore, you can have this:


<img src="/serveImage?id=<%=key.getId()%>">

/serveImage is the URI of my ServeImageServlet, as configured in web.xml:


<servlet>
<servlet-name>ServeImageServlet</servlet-name>
<servlet-class>fr.lk.servlet.ServeImageServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>ServeImageServlet</servlet-name>
<url-pattern>/serveImage</url-pattern>
</servlet-mapping>

Now let’s see ServeImageServlet.java:

public class ServeImageServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
byte[] bytes = new DAO().getImage(Long.parseLong(id));
resp.setContentType("image/jpeg");
resp.getOutputStream().write(bytes);
}
}

As you can see, the “id” request parameter is used to retrieve the image from the datastore (as a raw byte array), and we just directly write it to the response output stream.

Laurent KUBASKI

Real-time error monitoring in Google app engine

Yesterday, I showed you how your Google app engine application can send a mail whenever an error occurs (an uncaught Exception for example).

Today we are going to see how to monitor your application in real-time using Google talk.

Of course, the Google talk client has a little icon that tells you how many unread emails you have:

But that’s not enough: we want the GAE application to send a “google talk message” each time an error occurs. This can easily be done using the XMPP service.

First, we define a servlet to handle all exceptions:

<servlet>
  <servlet-name>ShowErrorServlet</servlet-name>
  <servlet-class>fr.kubaski.servlet.ShowError</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>ShowErrorServlet</servlet-name>
  <url-pattern>/showerror</url-pattern>
</servlet-mapping>
<error-page>
  <exception-type>java.lang.Exception</exception-type>
  <location>/showerror</location>
</error-page>

And here is the code for the servlet:

public class ShowError extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // note that 't' cannot be null since this servlet is defined as the error handler in web.xml:
    Throwable t = (Throwable) req.getAttribute("javax.servlet.error.exception");
    JID jid = new JID("theadmin@gmail.com");
    String msgBody = "an error occured: " + t.getMessage();
    Message msg = new MessageBuilder()
    .withRecipientJids(jid)
    .withBody(msgBody)
    .build();
    boolean isSent = false;
    XMPPService xmpp = XMPPServiceFactory.getXMPPService();
    if (xmpp.getPresence(jid).isAvailable()) {
      SendResponse status = xmpp.sendMessage(msg);
      isSent = (status.getStatusMap().get(jid) == SendResponse.Status.SUCCESS);
    }
    if (!isSent) {
      // error handling
    }
    getServletContext().getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(req, resp);
  }
}

In the code sample above, “theadmin@gmail.com” is the email of an administrator of the GAE application: replace it with yours !

Now you need to invite your GAE application from the Google talk client (exactly like you would invite a friend using his email). To do this, just invite [APPLICATION_IDENTIFIER]@appspot.com. In my case, my application is http://atestapplication.appspot.com so I need to invite atestapplication@appspot.com:

Now generate an exception in your application so that the error servlet is invoked and you will receive an instant notification:

Laurent KUBASKI

Error monitoring in Google app engine

Checking the GAE app engine logs each day for error messages can be very boring: what if you could receive a mail each time a non-handled Exception occurs in your Google app engine Java application ?

Well, it’s very easy: first define an generic “error handler” servlet in web.xml:

<servlet>
  <servlet-name>ShowErrorServlet</servlet-name>
  <servlet-class>fr.kubaski.servlet.ShowError</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>ShowErrorServlet</servlet-name>
  <url-pattern>/showerror</url-pattern>
</servlet-mapping>

<error-page>
  <exception-type>java.lang.Exception</exception-type>
  <location>/showerror</location>
</error-page>

And here is the code for the servlet:

public class ShowError extends HttpServlet {
  private final Log log = LogFactory.getLog(getClass());

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Throwable t = (Throwable) req.getAttribute("javax.servlet.error.exception");
    log.error("error", t);
    try {
      Message msg = newMessage("theadmin@gmail.com", t.getMessage(), getDisplayableThrowable(t));
      Transport.send(msg);
    } catch (Exception e) {
      // we just log the error: this is not a big deal if the mail was not sent
      log.error("error", t);
    }
    getServletContext().getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(req, resp);
  }

  public static String getDisplayableThrowable(Throwable t) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    t.printStackTrace(pw);
    String result = sw.toString();
    try {
      sw.close();
    } catch (IOException e) { /* nothing */}
    pw.close();
    return result;
  }

  private Message newMessage(String adminMail, String subject, String body) throws MessagingException {
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);
    Message msg = new MimeMessage(session);
    // As stated in GAE documentation:
    // "For security purposes, the sender address of a message must be the email address of an administrator for the application"
    msg.setFrom(new InternetAddress(adminMail));
    msg.addRecipient(Message.RecipientType.TO, new InternetAddress(adminMail));
    msg.setSubject(subject);
    msg.setText(body);
    return msg;
  }
}

Note that the mail sending feature does NOT work in “development mode” and that you need to deploy your app in GAE for this to work.

Laurent KUBASKI