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.
- Full Project: system-info-micro-0-0-1-snapshot-project
- Ready to go fat jar: system-info-micro-0-0-1-snapshot
