Red Hat

Undertow.js 1.0.1.Final has been released

Undertow.js 1.0.1.Final has been released. This allows you to integrate JavaScript and Java EE in your applications. There have been some notable changes since I first wrote about this at http://wildfly.org/news/2015/08/10/Javascript-Support-In-Wildfly/.

Websocket Support

It is now possible to use WebSockets with Undertow.js. You can declare a websocket handler with the websocket method, which takes a path and a connection handler. The connection handler gets passed the connection object, which can be used to send messages, and can respond to messages by setting the onText, onBinary and onClose functions.

Binary messages are send and received using the JavaScript ArrayBuffer object.

$undertow
    .websocket("/websocket1", ['cdi:myBean', function (myBean, connection) {
        connection.send(myBean.getConnectedMessage());
        connection.onText = function(message) {
            return "echo-" + message;
        }
        connection.onBinary = function(message) {
            return message;
        }
        connection.onClose = function (message) {
            print(message.reason + " " + message.code);
        }
    }]);

Declarative Security

Declarative security allows you to specify the roles that are allowed to use a certain handler. This uses standard Servlet authentication, so if a user is not authenticated then the container will attempt to authenticate them using whatever authentication mechanism is defined in web.xml. The special role '**' means any authenticated user.

To use this we simply need to add a roles_allowed list to a handlers options map:

$undertow
    .onGet("/rest/endpoint",
        {roles_allowed: ['admin', 'user'], headers: {"content-type": "application/json"}},
        [function ($exchange) {
            return {message: 'Hello World'};
        }])

Transaction Support

It is now possible to mark a handler as transactional, which means that a new transaction will be started when the method is invoked. If the method completes without an exception the transaction will be commited, otherwise it will be rolled back. To use this add transactional: true to the methods options map:

$undertow
    .onGet("/rest/endpoint",
        {transactional: true, headers: {"content-type": "application/json"}},
        [function ($exchange) {
            return {message: 'Hello World'};
        }])

Option map defaults

It is now possible to set defaults that will be applied to every that is declared after the option is set (i.e. setting a default will not affect any previously declared handlers, only new handlers). This can be used to set transaction or security defaults for all methods, or set a default set of response headers. It is possible to override the defaults by explicitly specifying the option in the options map.

The handler below will behave the same as the transactional one above, however the defaults will be applied to any other handlers declared after the setDefault call:

$undertow
    .setDefault('transactional', true)
    .setDefault('headers' {"content-type": "application/json"})
    .onGet("/rest/endpoint",
        [function ($exchange) {
            return {message: 'Hello World'};
        }])

Freemarker support

As well as Mustache.js we now support Freemarker as a template engine. To use Freemarker as the template engine set template_type: 'freemarker' in the methods options map. If all your templates are Freemarker templates you can use setDefault('template_type', 'freemarker').

Going forward

We would welcome any feedback or suggestions. If you want to contribute, or have any comments head to undertow-dev@lists.jboss.org.

Using Server-Side JavaScript with WildFly

The WildFly 10.0.0.Beta1 release includes support for an experimental new feature, that allows you to use JavaScript on the server side, using the Nashorn JavaScript engine that is built into the JDK. Combined with another new feature that allows you to serve web resources from outside the deployment it is possible to write server-side code with no redeploy of copy step involved.

This feature also allows Java EE resources such as CDI beans to be injected into your JavaScript handlers, allowing you to write your back end using Java, and the front end in JavaScript.

This is a new feature, and as such may change before release based on user feedback.

If you want to skip the tutorial and just want to see the code there are some examples at https://github.com/stuartwdouglas/undertow.js-examples

Getting Started

The first step is to tell WildFly where to find your JavaScript. To do this we create a file WEB-INF/undertow-scripts.conf. In this file you list your server side JavaScript files, one per line. For the sake of this post I will assume we have a single file, example.js.

Note that even though the server JavaScript files are located in the web context, the JavaScript integration will not allow them to be served. If a user requests a server side JS file a 404 will be returned instead.

Setting up hot deployment

To enable hot deployment create a file called WEB-INF/undertow-external-mounts.conf, and in this file add a single line that points to your local workspaces web content root (for example, /Users/stuart/workspace/jsexample/src/main/webapp). With this file in place resources will be served from your local workspace, instead of the deployment. This means that any changes you make to web resources or server-side JavaScript will be immediately visible as soon as you refresh your browser.

This hot deployment mechanism is independent of the server side JS integration, and should work for any web resources.

A simple HTTP endpoint

$undertow
    .onGet("/hello",
        {headers: {"content-type": "text/plain"}},
        [function ($exchange) {
            return "Hello World";
        }])

Add this to your example.js file and point your browser at /hello. You should see your Hello World message appear.

To test out hot deployment try modifying the message and refreshing your browser, the changes should be reflected immediately.

Lets walk through the things that are happening here. $undertow is the entry point for all JavaScript functionality, it contains methods to register HTTP handlers, as well as some other utility methods. The onGet method registers a handler for GET requests. This method takes three parameters, the handler path, an (optional) options map, and a handler injection list (or just a plain handler function, if no injection is required).

This function returns a string, which will be sent to the user as an HTTP response.

Now lets try a simple JSON REST endpoint:

$undertow
    .onGet("/rest/endpoint",
        {headers: {"content-type": "application/json"}},
        [function ($exchange) {
            return {message: 'Hello World'};
        }])

` This is basically the same, the only difference is that a map is returned instead of a string. Any object other than simple strings that are returned from a handler function will be converted into JSON and sent to the client.

Now lets try out an injection, so our handler can interact with the app server. We will start by injecting the default datasource:

$undertow
    .onGet("/rest/members",
        {headers: {"content-type": "application/json"}},
        ['jndi:java:jboss/datasources/ExampleDS', function ($exchange, db) {
            return db.select("select * from members");
        }])

JavaScript injection has the format 'type:name'. It uses angular.js style injection, where you pass in a list of injection specifiers, with the function to inject as the last item in the list. These injections will be resolved at runtime, and passed into the handler function in the order they are specified. The following injection types are supported out of the box:

jndi

This injection is looked up from JNDI, any item that can be looked up in JNDI can be injected into your JavaScript function.

cdi

This allows you to inject CDI beans into a handler. The beans are resolved by name, and as such must have the @Named annotation for them to be resolvable.

$entity

This allows the request entity to be injected directly into your function (for request types that have an entity, such as POST and PUT). By default this is injected as a string, however the following types are also supported.

  • $entity:json injects parsed JSON.

  • $entity:form injects parsed form or multipart encoded data.

Undertow.js will also automatically wrap some objects (such as datasources) in a JavaScript friendly API. The raw JDBC API is not particularly script friendly, so in the example above the DataSource that is injected is actually a wrapper that supports the select, selectOne and query methods. With any wrapped object you can access the underlying Java object using the objects $underlying property.

Going back to our example above, there are two issues with it:

  • We have not set up any tables or added any data to the database.

  • Writing the full JNDI name every time is cumbersome and error prone.

Lets address those issues:

$undertow
    .alias('db', 'jndi:java:jboss/datasources/ExampleDS')
    .onGet("/rest/members",
        {headers: {"content-type": "application/json"}},
        ['db', function ($exchange, db) {
            return db.select("select * from member");
        }]);


var db = $undertow.resolve("db");
try {
    db.query("create table member (id serial primary key not null, name varchar(100), email varchar(100) unique, phone_number varchar(100))");
    db.query("insert into member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.jsp.com', '2125551212')");
    db.query("insert into member (id, name, email, phone_number) values (1, 'Stuart Douglas', 'stuart@notmyrealaddress.com', '0487694837')");
} catch(e) {
    print("DB create failed")
}

We have added two things to this handler, the first is a call to alias, that allows us to alias commonly used names to shorter versions, and the second is some code to create the database. This DB setup code is mostly for example purposes, and is not a recommended approach, as every time this file is modified it will attempt to re-setup the database (and fail, as the table already exists).

Templates

There is also support for templates, at the moment Undertow.js supports Mustache, with plans to support more in the future.

To use a template simply specify the template name in the parameter map, the template will be rendered using the return value of your function as the data. An example is shown below:

$undertow
    .onGet("/hello",
        {template: 'hello.txt', headers: {"content-type": "text/plain"}},
        [function ($exchange) {
            return {name: 'Stuart'};
        }]);

And in hello.txt:

Hello {{name}}

Handling POST requests

POST (and other requests that contain a body) can be handled using entity injection. The body can be injected as a string, or one of the built in parsers can be used to parse JSON or form encoded data (including multipart data).

An example of all three approaches is shown below:

$undertow
    .onPost("/string",
        {headers: {"content-type": "text/plain"}},
        ['$entity', function ($exchange, entity) {
            return "You posted: " + entity;
        }])
    .onPost("/json",
        {headers: {"content-type": "text/plain"}},
        ['$entity:json', function ($exchange, entity) {
                return "You posted: " + entity['name'];
        }])
    .onPost("/form",
        {headers: {"content-type": "text/plain"}},
        ['$entity:form', function ($exchange, entity) {
            return "You posted: " + entity.get('name');
        }])

Going forward

At the moment the following additional features are planned:

  • Support for more template engines

  • Support for declarative security

This feature is very new, and will evolve over the coming months based on user feedback. If you want to contribute, or have any suggestions/comments head to undertow-dev@lists.jboss.org.

Centralized Logging for WildFly with the ELK Stack

The ELK stack; elasticsearch, logstash and kibana can be used for centralize logging. It’s not the intention of this post to be a tutorial on how to configure logstash. We will go through a basic logstash configuration then configure WildFly to send log messages to logstash.

Download and Configure logstash

First we need to download logstash. Once the download is complete simply extract logstash from the archive.

Next we will need to create a configuraton file. In the logstash directory create a file called logstash-wildfly.conf and add the following content to the configuration file.

input {
  tcp {
    port => 8000
  }
}

filter {
  json {
    source => "message"
  }
}

output {

  elasticsearch {
    # Use the embedded elsasticsearch for convienence
    embedded => true
    protocol => "http"
  }
}

Start logstash with the configuration file we just created ./bin/logstash agent -f logstash-wildfly.conf. In the example configuration above logstash should be listening on port 8000. Make note of the port you use as we’ll need it later when configuring WildFly.

Configure WildFly

If you don’t have a local install of WildFly you’ll want to download a recent version. In my example I’ll be use WildFly 9.0.1.Final. However any other version should work the same.

We also need to download the jboss-logmanager-ext library so that we can install it as a module. This library includes the formatter and handler we’ll use for logging.

Start up WildFly in admin-only mode so we can configure logging, $JBOSS_HOME/bin/standalone.sh --admin-only. Once the server is running start a CLI console, $JBOSS_HOME/bin/jboss-cli.sh -c, to install the module and configure logging. The following commands can be entered manually or placed in a CLI script.

batch
# Add the module, replace the directory on the resources attribute to the path where you downloaded the jboss-logmanager-ext library
module add --name=org.jboss.logmanager.ext --dependencies=org.jboss.logmanager,javax.json.api,javax.xml.stream.api --resources=~/tmp/jboss-logmanager-ext-1.0.0.Alpha3.jar

# Add the logstash formatter
/subsystem=logging/custom-formatter=logstash:add(class=org.jboss.logmanager.ext.formatters.LogstashFormatter,module=org.jboss.logmanager.ext)

# Add a socket-handler using the logstash formatter. Replace the hostname and port to the values needed for your logstash install
/subsystem=logging/custom-handler=logstash-handler:add(class=org.jboss.logmanager.ext.handlers.SocketHandler,module=org.jboss.logmanager.ext,named-formatter=logstash,properties={hostname=localhost, port=8000})

# Add the new handler to the root-logger
/subsystem=logging/root-logger=ROOT:add-handler(name=logstash-handler)

# Reload the server which will boot the server into normal mode as well as write messages to logstash
:reload
run-batch

With these changes WildFly should be writing to logstash. You can view the log messages from logstash with kibana. With the defaults we used you should just be able to start kibana with bin/kibana and the default configuration. My dashboard looks like the following.

kibana

Conclusion

If you’re already using the ELK stack for centralized logging adding WildFly to the aggregation is rather simple. If you’re just looking for a way to view and filter log messages using the ELK stack with WildFly could be a good fit as well.

One thing to note is if you’re seeing performance issues or you’re writing to a remote logstash server you may want to use an async-handler.

back to top