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