A short visit at the vert.x

Last weekend I wrote the shortest program, I ever wrote, that was more then just a little test. OK, micro-services are supposed to be small, but a POM file and one class is really not much. Let’s have a look at the POM first.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>de.fx-world</groupId>
  <artifactId>system-info-micro</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <vertx.version>3.3.3</vertx.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-core</artifactId>
      <version>${vertx.version}</version>
    </dependency>

    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-web</artifactId>
      <version>${vertx.version}</version>
    </dependency>

    <dependency>
      <groupId>com.github.dblock</groupId>
      <artifactId>oshi-json</artifactId>
      <version>3.2</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <!-- Run shade goal on package phase -->
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <createDependencyReducedPom>
                 false
              </createDependencyReducedPom>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Main-Class>io.vertx.core.Launcher</Main-Class>
                    <Main-Verticle>
                      de.fxworld.systeminforest.SystemInfoRest
                    </Main-Verticle>
                  </manifestEntries>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>
                    META-INF/services/io.vertx.core.spi.VerticleFactory
                  </resource>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

As you can see I used vert.x, which was the library I wanted to experiment with. There was a Twitter post about vert.x and micro-services and it looked interesting. So let’s give it a try. I wanted something small to check the system health of some of my machines. And yes I know there are already tools out there, that do that job very well. But hey I had something new to try, so let’s write it ourselves. Java itself does not offer much to get free memory of the system, only stats about the JVM run-time. So after some search, I found OSHI, which provides a variety of information about the system and does this in JSON too. Perfect. The whole build section is just the configuration to build a fat jar, to drop it somewhere and execute without the need for other jars.

Now the main application class, the one and only class in this project I had to write.

package de.fxworld.systeminforest;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Launcher;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import oshi.json.SystemInfo;

public class SystemInfoRest extends AbstractVerticle {

  private SystemInfo systemInfo;
  
  // Convenience method so you can run it in your IDE
  public static void main(String[] args) {
    Launcher.executeCommand("run", SystemInfoRest.class.getName());
  }

  @Override
  public void start() {
    Router router = Router.router(vertx);
    
    router.route().handler(BodyHandler.create());
    router.get("/").handler(this::handleGetSystemInfo);

    systemInfo = new SystemInfo();
    
    vertx.createHttpServer()
      .requestHandler(router::accept)
      .listen(config().getInteger("http.port", 4000));
  }
  
  private void handleGetSystemInfo(RoutingContext routingContext) {		
    vertx.<String>executeBlocking(
      future -> future.complete(systemInfo.toPrettyJSON()), 
      result -> routingContext.response()
        .putHeader("content-type", "application/json")
        .end(result.result())			
    );
  }
}

The start method creates a new HTTP server, that listens on a configurable port, default is 4000. And when a request comes in, it lets the method handleGetSystemInfo do the rest. In this method we could simply write routingContext.response().putHeader("content-type", "application/json").end(systemInfo.toPrettyJSON());, but this would lead to a nice exception:

Nov 09, 2016 9:34:05 PM io.vertx.core.impl.BlockedThreadChecker
WARNUNG: Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 6182 ms, time limit is 2000
io.vertx.core.VertxException: Thread blocked
  at com.sun.jna.Native.invokeInt(Native Method)
  at com.sun.jna.Function.invoke(Function.java:390)
  at com.sun.jna.Function.invoke(Function.java:323)
  at com.sun.jna.Function.invoke(Function.java:275)
  at com.sun.jna.Function.invoke(Function.java:266)
  at com...COM.COMInvoker._invokeNativeObject(COMInvoker.java:37)
  at oshi...COM.EnumWbemClassObject.Next(EnumWbemClassObject.java:40)
  at oshi...WmiUtil.enumerateProperties(WmiUtil.java:459)
  at oshi...WmiUtil.queryWMI(WmiUtil.java:304)
  at oshi...WmiUtil.selectUint32From(WmiUtil.java:87)
  ..
  at io...nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:465)
  at io...nio.NioEventLoop.run(NioEventLoop.java:437)
  at io...run(SingleThreadEventExecutor.java:873)
  at java.lang.Thread.run(Unknown Source)

So we use executeBlocking, which allows long running methods without complaining. So there it is – the REST service that displays a lot of system information. A shorted version can be seen here.

{

    "platform": "WINDOWS",
    "operatingSystem": {
        "manufacturer": "Microsoft",
        "family": "Windows",
        "version": {},
        "fileSystem": {},
        "processID": 10488,
        "processCount": 138,
        "threadCount": 2096,
        "processes": []
    },
    "hardware": {
        "processor": {
            "name": "AMD FX(tm)-6300 Six-Core Processor             ",
            "physicalProcessorCount": 3,
            "logicalProcessorCount": 6,
            "systemSerialNumber": "To be filled by O.E.M.",
            "vendor": "AuthenticAMD",
            "vendorFreq": -1,
            "cpu64bit": false,
            "family": "21",
            "model": "2",
            "stepping": "0",
            "systemCpuLoadBetweenTicks": 0.5163247905577707,
            "systemCpuLoadTicks": [],
            "systemCpuLoad": 0.515481764476452,
            "systemLoadAverage": -1.0,
            "systemLoadAverages": [],
            "processorCpuLoadBetweenTicks": [],
            "processorCpuLoadTicks": [],
            "systemUptime": 10822
        },
        "memory": {},
        "powerSources": [],
        "disks": [],
        "networks": [],
        "displays": [],
        "sensors": {},
        "usbDevices": []         
    }
}

I would be careful if used in production, because there is no security layer what so ever. No IP-filter, no user authentication or anything that restricts access. So you would expose a lot of sensitive information about your system to everybody listening. And that is not a good idea.

And for all the people out there, complaining about the usage of vert.x here: yes I am aware, that this is a terrible use case for it, but it works.

Advertisements

Leave a Reply