Using HTTP Callback with Java

Using HTTP Callback with Java

Until now, it was possible to use the standard Java library to access a YoctoHub or a VirtualHub, but one working mode was missing: the HTTP callback mode. This mode, previously available for PHP and Nodes.js only, is now available in the beta version of the Java API (v1.10). Let's show how to write a Java Servlet that can interact with a Yocto-Hub.




The HTTP callback mode, what's that?

Usually, it is the computer/smartphone that contacts the Yoctopuce modules with the YAPI.RegisterHub() function. This is the default operating mode for all languages. The application code connects to the YoctoHub (or VirtualHubs) and can then immediately communicate with the Yoctopuce modules. In this mode, the application manages when connections are made, and it is also the application that manages the number of used threads. We could summarize this mode with the following sentence: "The program runs and sends instructions to the Yoctopuce modules through a YoctoHub/VirtualHub".

In standard applications it is the API that manages connections to multiples YoctoHub
In standard applications it is the API that manages connections to multiples YoctoHub



In HTTP callback mode, it goes the other way around. It is the IP hub that connects to the API and starts the application. We configure each YoctoHub that we want to use so that it connects periodically to a script (or when a sensor value has changed). We could express it in this way: "Every x seconds, the YoctoHub connects to a server to transmit its state and to retrieve instructions to send to the Yoctopuce devices". This has several implications which must be taken into account:

1. TCP connections are reversed. This means that the modules are not blocked by the Internet router (or any other NAT filter) anymore. We can therefore install a Java servlet on a server in the "cloud" and put a YoctoHub at home in the garden without having to reconfigure the DSL router.

In HTTP callback mode, the TCP connection is reversed, which makes it work accross a NAT filter
In HTTP callback mode, the TCP connection is reversed, which makes it work accross a NAT filter



2. The API has access only to the devices of the connected hub. For instance, if we have a Yocto-Meteo on YoctoHub A, and a Yocto-Display on YoctoHub B, it is not possible to directly display the Yocto-Meteo temperature on the Yocto-Display. When YoctoHub A connects to the service, the application must store the Yocto-Meteo temperature value and wait until YoctoHub B connects to the service to display this temperature value on the Yocto-Display.

In HTTP callback mode, the API can use only one YoctoHub at a time
In HTTP callback mode, the API can use only one YoctoHub at a time



3. It is the YoctoHub which determines (by connecting to the server) when the application is run. It is possible (and even most probable) that two YoctoHub connect at the same time to the application. Thus, if several YoctoHubs are configured to use the application, it is imperative to ensure that the application supports concurrency.

A servlet example

We have already used the HTTP callback API in several posts, but each time we used the PHP language. To illustrate the differences with PHP, here is the Java code of a servlet displaying the latest posts from our blog on a Yocto-MaxiDisplay. The original article is available here.


....
private void doRSSReader() throws YAPI_Exception
{
  ArrayList<RSSItem> rssItems = RSSReader.getInstance().getAllItems();
  YDisplay display = YDisplay.FirstDisplay();
  while (display != null) {
    // flash the beacon during sequence recording (for debug purposes)
    YModule module = display.module();
    module.set_beacon(YModule.BEACON_ON);
    // start recording a Sequence
    display.newSequence();
    // execute command for all rss items
    for (RSSItem item : rssItems) {
      String productname = module.get_productName();
      if ("Yocto-MiniDisplay".equals(productname)) {
        OutputMiniDisplay(display, item);
      } else {
        OutputMaxiDisplay(display, item);
      }
    }
    display.playSequence("rss");
    display.saveSequence("rss");
    display.playSequence("rss");
    // stop the beacon
    module.set_beacon(YModule.BEACON_OFF);
    // look if we get another display connected
    display = display.nextDisplay();
  }
}

/**
 * Handles the HTTP POST method.
 *
 * @param request servlet request
 * @param response servlet response
 * @throws ServletException if a servlet-specific error occurs
 * @throws IOException if an I/O error occurs
 */

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
{
  ServletOutputStream out = response.getOutputStream();
  ServletInputStream in = request.getInputStream();
  try {
    // Setup the Yoctopuce API in HTTP Callback mode
    YAPI.RegisterHub("callback",
            request.getInputStream(),
            response.getOutputStream());
    doRSSReader();
  } catch (YAPI_Exception ex) {
    System.out.println("Yoctopuce API error:" + ex.getMessage());
  } finally {
    YAPI.FreeAPI();
  }
}
...
 



As you can see, using the Yoctopuce API is very similar. The only difference is the call to the YAPI.RegisterHub method. In Java, on top of the "callback" keyword, we need to provide two additional parameters (InputStream request et OutputStream response) for the Yoctopuce API to retrieve data sent by the YoctoHub and send back commands. Another difference is that, in Java, it is compulsory to handle exceptions correctly.

Yes, but...

Due to the way servlets work, if you keep it as is and connect several YoctoHubs to your servlet, you will get troubles. Let see why, and how to fix that.

In the opposite to PHP, memory space is shared in Java
In the opposite to PHP, memory space is shared in Java



In PHP, each request is run in a new process, thus each request is called with a memory space dedicated to the request. In the opposite, in Java, all the requests are run in the same process, but in distinct threads, and memory space is shared. While it's more efficient because there is no need to create a new process, this implies that two YoctoHubs connecting at the same time are going to compete to access the Yoctopuce API internal caches.

To avoid this issue, we must configure the API, when loading the servlet, so that internal caches are not shared between threads. To do so, we must call the YAPI.SetThreadSpecificMode() method in the servlet init() method.

And here is the "thread safe" final version of the code:


...
@Override
public void init() throws ServletException
{
  super.init();
  System.out.println("Setup Yoctpuce API to use thread specific cache");
  try {
      YAPI.SetThreadSpecificMode();
  } catch (YAPI_Exception ex) {
      System.out.println("Unable to setup API:" + ex.getMessage());
  }
}
...
 



This new feature is available in the beta version of the Java API (available here or on GitHub). Some methods may still change depending on the early feedback we are going to receive. As usual, all comments (even negative ones) are welcome, so don't hesitate to send us an email or to post a comment.

Add a comment No comment yet
Back to blog












Yoctopuce, get your stuff connected.