Wednesday, August 14, 2013

Simpler Audit Logging Example for WildFly 8.0.0.Alpha4

Tomaz on the WildFly team pointed out that my previous blog post on secure audit logging in WildFly 8.0.0.Alpha4 was a bit too scary :-) So I'd like to point out that that was probably the most complicated setup possible, and to walk through a few simpler examples here while explaining a bit more what goes on. These features are available in WildFly 8.0.0.Alpha4 and later. You can get it from here.

WildFly out of the box comes preconfigured for audit logging to $WILDFLY_HOME/standalone/data/audit-log.log., the only thing you need to do is to enable it. There is a section in $WILDFLY_HOME/standalone/configuration/standalone.xml which contains the configuration for the audit log:
        <audit-log>
            <formatters>
                <json-formatter name="json-formatter">
            </json-formatter></formatters>
            <handlers>
                <file-handler formatter="json-formatter" name="file" path="audit-log.log" relative-to="jboss.server.data.dir">
                </file-handler>
            </handlers>
            <logger enabled="false" log-boot="true" log-read-only="false">
                <handlers>
                    <handler name="file">
                </handler></handlers>
            </logger>
        </audit-log>
This config contains the only audit log formatter we have at the moment, the JSON formatter which formats the audit log records.
It also contains a file-handler called file which points to the file which it should log to.
The file file-handler references the 'json-formatter' which means this handler uses 'json-formatter' to format its log records.
Finally the logger entry references the 'file' handler which means that the 'file' handler will be used for logging audit log records.
The attributes of the logger element mean that we will audit log the operation that happen on boot, that we will not log read-only operations, and that audit logging is not enabled. The not enabled part takes precedence, so we don't log anything at all.

Now if you start up WildFly by running (of course change ~/Downloads/wildfly-8.0.0.Alpha4 to wherever you have installed WildFly)
$cd ~/Downloads/wildfly-8.0.0.Alpha4/bin/
$./standalone.sh
you will see no $WILDFLY_HOME/standalone/data/audit-log.log. file. This is because we need to enable audit logging.



Enable audit logging

In another terminal window go to WildFly's bin directory, start our command-line interface and execute the operation to change the enabled attribute to true:

$cd ~/Downloads/wildfly-8.0.0.Alpha4/bin/

[~/Downloads/wildfly-8.0.0.Alpha4/bin]
$./jboss-cli.sh 
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect 
[standalone@localhost:9990 /] /core-service=management/access=audit/logger=audit-log:write-attribute(name=enabled,value=true){"outcome" => "success"}
[standalone@localhost:9990 /] 
If you look in $WILDFLY_HOME/standalone/configuration/standalone.xml again, you will see that the value of the enabled attribute we saw earlier is now true.

Now, if you look, you will see that $WILDFLY_HOME/standalone/data/audit-log.log has been created, and it contains the following:
$more data/audit-log.log
2013-08-14 15:26:16 - {
    "type" : "core",
    "r/o" : false,
    "booting" : false,
    "user" : "$local",
    "domainUUID" : null,
    "access" : "NATIVE",
    "remote-address" : "127.0.0.1/127.0.0.1",
    "success" : true,
    "ops" : [{
        "address" : [
            {
                "core-service" : "management"
            },
            {
                "access" : "audit"
            },
            {
                "logger" : "audit-log"
            }
        ],
        "operation" : "write-attribute",
        "name" : "enabled",
        "value" : true,
        "operation-headers" : {"caller-type" : "user"}
    }]
}

The actual contents of the fields is explained in the documentation but a few things worth pointing out is that access=NATIVE means it came from the native management interface, and ops contains the operation that we executed to enable audit logging.

Enable syslog logging

First you need a syslog server which can accept connections via UDP or TCP/IP. I am on OS X, so I enabled my local syslog server to accept UDP connections on port 514 by following these tips. If you are on linux you probably have rsyslog installed, so you would typically modify /etc/rsyslog.conf to include the following directives
...
$ModLoad imudp.so
$UDPServerRun 514
...

Then restart the syslog service. I think the OS X link shows you how to do that, and for linux you need to restart the rsyslogd service. In the worst case reboot, and start up wildfly and the command-line interface again ;-)

Then in the command-line interface window, execute these following commands to add a syslog handler called mysyslog:
[standalone@localhost:9990 /] batch 
[standalone@localhost:9990 / #] /core-service=management/access=audit/syslog-handler=mysyslog:add(formatter=json-formatter)
#1 /core-service=management/access=audit/syslog-handler=mysyslog:add(formatter=json-formatter)
[standalone@localhost:9990 / #] /core-service=management/access=audit/syslog-handler=mysyslog/protocol=udp:add(host=localhost,port=514)
#2 /core-service=management/access=audit/syslog-handler=mysyslog/protocol=udp:add(host=localhost,port=514)
[standalone@localhost:9990 / #] run-batch 
The batch executed successfully
Then add a reference to mysyslog to the logger:
[standalone@localhost:9990 /] /core-service=management/access=audit/logger=audit-log/handler=mysyslog:add
{"outcome" => "success"}

You should now see the executed commands logged in $WILDFLY_HOME/standalone/data/audit-log.log., e.g.:
2013-08-14 15:55:20 - {
    "type" : "core",
    "r/o" : false,
    "booting" : false,
    "user" : "$local",
    "domainUUID" : null,
    "access" : "NATIVE",
    "remote-address" : "127.0.0.1/127.0.0.1",
    "success" : true,
    "ops" : [{
        "operation" : "composite",
        "address" : [],
        "steps" : [
            {
                "address" : [
                    {
                        "core-service" : "management"
                    },
                    {
                        "access" : "audit"
                    },
                    {
                        "syslog-handler" : "mysyslog"
                    }
                ],
                "operation" : "add",
                "formatter" : "json-formatter",
                "operation-headers" : {"caller-type" : "user"},
                "max-length" : null,
                "syslog-format" : null,
                "truncate" : null,
                "max-failure-count" : null
            },
            {
                "address" : [
                    {
                        "core-service" : "management"
                    },
                    {
                        "access" : "audit"
                    },
                    {
                        "syslog-handler" : "mysyslog"
                    },
                    {
                        "protocol" : "udp"
                    }
                ],
                "operation" : "add",
                "port" : 514,
                "host" : "localhost",
                "operation-headers" : {"caller-type" : "user"}
            }
        ],
        "operation-headers" : {"caller-type" : "user"}
    }]
}
2013-08-14 15:56:34 - {
    "type" : "core",
    "r/o" : false,
    "booting" : false,
    "user" : "$local",
    "domainUUID" : null,
    "access" : "NATIVE",
    "remote-address" : "127.0.0.1/127.0.0.1",
    "success" : true,
    "ops" : [{
        "address" : [
            {
                "core-service" : "management"
            },
            {
                "access" : "audit"
            },
            {
                "logger" : "audit-log"
            },
            {
                "handler" : "mysyslog"
            }
        ],
        "operation" : "add",
        "operation-headers" : {"caller-type" : "user"}
    }]
}
And if you look at syslog,
  • On OS X, open the Console.app and go to 'All Messages'
  • On Linux, look in /var/log/messages
In any case you should see the logger handler reference add in syslog
14/08/2013 15:56:35.000 2013-08-14T15: 56:35.529+01:00 localhost WildFly 2348 - - 2013-08-14 15:56:34 - {
    "type" : "core",
    "r/o" : false,
    "booting" : false,
    "user" : "$local",
    "domainUUID" : null,
    "access" : "NATIVE",
    "remote-address" : "127.0.0.1/127.0.0.1",
    "success" : true,
    "ops" : [{
        "address" : [
            {
                "core-service" : "management"
            },
            {
                "access" : "audit"
            },
            {
                "logger" : "audit-log"
            },
            {
                "handler" : "mysyslog"
            }
        ],
        "operation" : "add",
        "operation-headers" : {"caller-type" : "user"}
    }]
(The reason you did not see the add of the handler is that the audit log subsystem does not actually start using it until the logger handler reference is added). If you execute the following command in the command-line interface
[standalone@localhost:9990 /] /system-property=test123:add(value=testing)
{"outcome" => "success"}
You should see the operation get logged in both syslog and the file.

Finally, if you look at $WILDFLY_HOME/standalone/configuration/standalone.xml again you will see that the syslog handler and reference have been persisted:

        <audit-log>
            <formatters>
                <json-formatter name="json-formatter"/>
            </formatters>
            <handlers>
                <file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
                <syslog-handler name="mysyslog" formatter="json-formatter">
                    <udp host="localhost" port="514"/>
                </syslog-handler>
            </handlers>
            <logger log-boot="true" log-read-only="false" enabled="true">
                <handlers>
                    <handler name="file"/>
                    <handler name="mysyslog"/>
                </handlers>
            </logger>
        </audit-log>

Tuesday, August 13, 2013

Setting up Secure Audit Logging to a Remote Rsyslog Instance in WildFly 8.0.0.Alpha4

WildFly is the new name for JBoss Application Server. It builds on the same architecture as JBoss Application Server 7. WildFly is the upstream for JBoss Enterprise Application Platform 6, which is our supported middleware offering at Red Hat. Some of our larger customers have asked for additional security features when it comes to managing their installations, and one of the pieces for this is to implement audit logging of the management layer. The initial implementation of audit logging will be part of the WildFly 8.0.0.Alpha4 release, which was released today.

Note that this is a pretty advanced topic, for a simpler overview of audit logging see my follow-up post.

The WildFly Admin Guide covers the reference for the configuration of audit logging framework, but I thought it would be a good idea to show step by step how to configure WildFly to log to a remote syslog server over TLS. I will also throw client certificate authentication into the mix. I have used rsyslog for my experiments since that comes with nice documentation for how to set it up to use TLS. You need to install the rsyslog-gnutls package for the following to work.

I do my main work on OS X but have a linux server with rsyslog installed so I will use that as the syslog server. It's address is kabirlinux. Wherever you see the name kabirlinux you should replace it with the host name of your machine running rsyslog.

Generating the server certificates

First I ssh into kabirlinux and generate the keys we need to set up rsyslog to accept remote connections over TLS (taken from http://www.rsyslog.com/tag/tls/ and http://www.rsyslog.com/doc/rsyslog_tls.html). The first thing we need to do is to generate the ca certificate that will be used for signing the other certificates. Please note that wherever I have used sudo, it is very important that you do so too, otherwise rsyslog might not recognise your keys.
$ssh kabirlinux
Last login: Mon Aug 12 16:15:28 2013 from 192.168.1.102

[kabirlinux ~]
$mkdir tmp

[kabirlinux ~]
$cd tmp

[kabirlinux ~/tmp]
$sudo certtool --generate-privkey --outfile ca-key.pem
[sudo] password for kabir: 
Generating a 2560 bit RSA private key...
Then we generate the ca certificate using the private key:
[kabirlinux ~]
$sudo certtool --generate-self-signed --load-privkey ca-key.pem --outfile ca.pem
Generating a self signed certificate...
Please enter the details of the certificate's distinguished name. Just press enter to ignore a field.
Country name (2 chars): UK
Organization name: Red Hat
Organizational unit name: JBoss
Locality name: London
State or province name: London
Common name: kabirlinux
UID: 
This field should not be used in new certificates.
E-mail: me@redhat.com
Enter the certificate's serial number in decimal (default: 1376387226): 


Activation/Expiration time.
The certificate will expire in (days): 3650


Extensions.
Does the certificate belong to an authority? (y/N): y
Path length constraint (decimal, -1 for no constraint): 
Is this a TLS web client certificate? (y/N): 
Will the certificate be used for IPsec IKE operations? (y/N): 
Is this also a TLS web server certificate? (y/N): 
Enter the e-mail of the subject of the certificate:    
Will the certificate be used to sign other certificates? (y/N): y
Will the certificate be used to sign CRLs? (y/N): 
Will the certificate be used to sign code? (y/N): 
Will the certificate be used to sign OCSP requests? (y/N): 
Will the certificate be used for time stamping? (y/N): 
Enter the URI of the CRL distribution point: 
X.509 Certificate Information:
 Version: 3
.........

Is the above information ok? (y/N): y


Signing certificate...
Next we generate a certificate request, and the server certificate that will be used to secure TLS.
[kabirlinux ~/tmp]
$sudo certtool  --generate-privkey --outfile server-key.pem --bits 2048
** Note: Please use the --sec-param instead of --bits
Generating a 2048 bit RSA private key...

[kabirlinux ~/tmp]
$sudo certtool --generate-request --load-privkey server-key.pem --outfile request.pem
Generating a PKCS #10 certificate request...
Country name (2 chars): UK
Organization name: Red Hat
Organizational unit name: JBoss
Locality name: London
State or province name: London
Common name: kabirlinux
UID: 
Enter a dnsName of the subject of the certificate: kabirlinux
Enter a dnsName of the subject of the certificate: 
Enter the IP address of the subject of the certificate: 
Enter the e-mail of the subject of the certificate: me@redhat.com
Enter a challenge password: 
Does the certificate belong to an authority? (y/N): 
Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (y/N): 
Will the certificate be used for encryption (RSA ciphersuites)? (y/N): 
Is this a TLS web client certificate? (y/N): y
Is this also a TLS web server certificate? (y/N): y

[kabirlinux ~/tmp]
$sudo certtool --generate-certificate --load-request request.pem --outfile server-cert.pem --load-ca-certificate ca.pem --load-ca-privkey ca-key.pem
Generating a signed certificate...
Enter the certificate's serial number in decimal (default: 1376387546): 


Activation/Expiration time.
The certificate will expire in (days): 3650


Extensions.
Do you want to honour the extensions from the request? (y/N): 
Does the certificate belong to an authority? (y/N): 
Is this a TLS web client certificate? (y/N): y
Will the certificate be used for IPsec IKE operations? (y/N): 
Is this also a TLS web server certificate? (y/N): y
Enter a dnsName of the subject of the certificate: kabirlinux
Enter a dnsName of the subject of the certificate: 
Enter the IP address of the subject of the certificate: 
Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (y/N): 
Will the certificate be used for encryption (RSA ciphersuites)? (y/N): 
X.509 Certificate Information:
 Version: 3

.....

Is the above information ok? (y/N): y


Signing certificate...

[kabirlinux ~/tmp]
$rm request.pem 
We then need to protect the ca private key
[kabirlinux ~/tmp]
$sudo chmod 400 ca-key.pem
Now move the generated files across to /etc/pki/rsyslog, keeping a copy of the server certificate in the tmp directory: 
[kabirlinux ~/tmp]
$sudo mv ca-key.pem ca.pem server-cert.pem server-key.pem /etc/pki/rsyslog/

[kabirlinux ~/tmp]
$sudo cp /etc/pki/rsyslog/server-cert.pem .
Then modify the rsyslog configuration file at /etc/rsyslog.conf to allow TLS. In a vanilla installation this is just after the initial ModLoad directives.
....
#### MODULES ####
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
$ModLoad imklog   # provides kernel logging support (previously done by rklogd)
#$ModLoad immark  # provides --MARK-- message capability

# Provides TLS syslog reception
$ModLoad imtcp #Load tcp driver
$DefaultNetstreamDriver gtls # Sets the gtls driver as the default
# Certificates
$DefaultNetstreamDriverCAFile /etc/pki/rsyslog/ca.pem
$DefaultNetstreamDriverCertFile /etc/pki/rsyslog/server-cert.pem
$DefaultNetstreamDriverKeyFile /etc/pki/rsyslog/server-key.pem
# run driver in TLS-only mode
$InputTCPServerStreamDriverMode 1
$InputTCPServerStreamDriverAuthMode anon 
$InputTCPServerRun 514 # start up listener at port 514
....
Make sure the syslog server's firewall is open to TCP requests on port 514. A simple way to get rid of all rules is to run this when ssh'ed into the syslog server (you will of course want to set this up properly once done experimenting):
[kabirlinux ~]
$sudo iptables -F
Then restart the rsyslog daemon:
[kabirlinux ~]
$sudo service rsyslog restart
[sudo] password for kabir: 
Redirecting to /bin/systemctl restart  rsyslog.service
In the syslog you should then see messages indicating that the service has been restarted
[kabirlinux ~]
$sudo tail -f /var/log/messages
.....
Aug 12 16:50:32 kabir systemd[1]: Stopping System Logging Service...
Aug 12 16:50:32 kabir systemd[1]: Starting System Logging Service...
Aug 12 16:50:32 kabir systemd[1]: Started System Logging Service.

Importing the server certificate into a Java truststore

Since the server certificate is not signed by a trusted certificate authorization authority, in my local machine where I will be running WildFly, I need to import the server certificate into a truststore. First I copy it down to my local machine:
[~]
$mkdir test

[~]
$cd test

[~/test]
$sftp kabirlinux
Connected to kabirlinux.
sftp> cd tmp
sftp> ls
server-cert.pem
sftp> get server-cert.pem
Fetching /home/kabir/tmp/server-cert.pem to server-cert.pem
/home/kabir/tmp/server-cert.pem                                                    100% 1529     1.5KB/s   00:00    
sftp> exit

[~/test]
Then I import the certificate into a trustore file using the keytool command, and 'test123' as the password:
[~/test]
$keytool -import -trustcacerts -file server-cert.pem -alias CA_ALIAS -keystore truststore
Enter keystore password:  
Re-enter new password: 
...

Trust this certificate? [no]:  yes
Certificate was added to keystore

Set up WildFly to use syslog with TLS for audit logging

Make sure you are using WildFly 8.0.0.Alpha4 or later! Out of the box wildfly's audit log configuration in its standalone/configuration/standalone.xml will look like this
<audit-log>
            <formatters>
                <json-formatter name="json-formatter"/>
            </formatters>
            <handlers>
                <file-handler name="file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
            </handlers>
            <logger log-boot="true" log-read-only="false" enabled="false">
                <handlers>
                    <handler name="file"/>
                </handlers>
            </logger>
        </audit-log>
Change this to
<audit-log>
            <formatters>
                <json-formatter name="json-formatter"/>
            </formatters>
            <handlers>
                <syslog-handler name="syslog-tls" formatter="json-formatter" syslog-format="RFC5424">
                    <tls host="kabirlinux" port="514" message-transfer="NON_TRANSPARENT_FRAMING">
                        <truststore path="/Users/kabir/test/truststore" keystore-password="test123"/>
                    </tls>
                </syslog-handler>
            </handlers>
            <logger log-boot="true" log-read-only="true" enabled="true">
                <handlers>
                    <handler name="syslog-tls"/>
                </handlers>
            </logger>
        </audit-log>
If having the keystore password hard wired in the xml worries you, please take a look at how to use our vault for sensitive information like that. Now if you stop and start WildFly and look in /var/log/messages on the server running rsyslog, you should see audit logged messages on bootup. Also, when executing operations, for example from the CLI you should see those added to the syslog.

Client certificate authentication

Rsyslog supports full blown client certificate authentication, however I have not tried setting that up. However, it also has a slightly more permissive mode which simply checks that the client has a certificate signed by the server's ca. So first we generate the client certificate, I do this on my syslog server:
[~/test]
$ssh kabirlinux
Last login: Mon Aug 12 18:02:25 2013 from 192.168.1.102

[kabirlinux ~]
$cd tmp/

[kabirlinux ~/tmp]
$certtool --generate-privkey --outfile client-key.pem
Generating a 2560 bit RSA private key...

[kabirlinux ~/tmp]
$certtool --generate-request --load-privkey client-key.pem --outfile client-request.pem
Generating a PKCS #10 certificate request...
Country name (2 chars): UK
Organization name: Red Hat
Organizational unit name: JBoss
Locality name: London
State or province name: London
Common name: client    
UID: 
Enter a dnsName of the subject of the certificate: kabirlinux
Enter a dnsName of the subject of the certificate: 
Enter the IP address of the subject of the certificate: 
Enter the e-mail of the subject of the certificate: me@redhat.com
Enter a challenge password: 
Does the certificate belong to an authority? (y/N): N
Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (y/N): 
Will the certificate be used for encryption (RSA ciphersuites)? (y/N): 
Is this a TLS web client certificate? (y/N): y
Is this also a TLS web server certificate? (y/N): y

[kabirlinux ~/tmp]
$sudo certtool  --generate-certificate --load-request client-request.pem --outfile client-cert.pem  --load-ca-certificate /etc/pki/rsyslog/ca.pem --load-ca-privkey /etc/pki/rsyslog/ca-key.pem
[sudo] password for kabir: 
Generating a signed certificate...
Enter the certificate's serial number in decimal (default: 1376327802): 


Activation/Expiration time.
The certificate will expire in (days): 3650


Extensions.
Do you want to honour the extensions from the request? (y/N): 
Does the certificate belong to an authority? (y/N): 
Is this a TLS web client certificate? (y/N): y
Will the certificate be used for IPsec IKE operations? (y/N): 
Is this also a TLS web server certificate? (y/N): y
Enter a dnsName of the subject of the certificate: kabirlinux
Enter a dnsName of the subject of the certificate: 
Enter the IP address of the subject of the certificate: 
Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (y/N): 
Will the certificate be used for encryption (RSA ciphersuites)? (y/N): 
X.509 Certificate Information:
...

Is the above information ok? (y/N): y
We then need to combine the client certificate and key to pkcs12 format to copy across to the client, using 'test123' as the export password
[kabirlinux ~/tmp]
$openssl pkcs12  -export -in client-cert.pem -inkey client-key.pem -out client.p12 -name "Whatever"
Enter Export Password:
Verifying - Enter Export Password:

[kabirlinux ~/tmp]
$rm -rf client*.pem
Now on my Mac I get the client.p12 file and import it into a jks keystore
[~/test]
$sftp kabirlinux
Connected to kabirlinux.
sftp> cd tmp 
sftp> get client.p12
Fetching /home/kabir/tmp/client.p12 to client.p12
/home/kabir/tmp/client.p12                                                                                                                                               100% 3094     3.0KB/s   00:00    
sftp> exit

[~/test]
$keytool -v -importkeystore -srckeystore client.p12 -srcstoretype PKCS12 -destkeystore client.jks -deststoretype JKS
Enter destination keystore password:  
Re-enter new password: 
Enter source keystore password:  
Entry for alias whatever successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled
[Storing client.jks]
Again, I use 'test123' as the keystore password. Then on the rsyslog server we change /etc/rsyslog.conf to use 'x509/certvalid' instead of 'anon' for the InputTCPServerStreamDriverAuthMode directive, e.g:
$InputTCPServerStreamDriverAuthMode x509/certvalid
Next we add the client-certificate-store information to the syslog handler in the audit logging section of standalone/configuration/standalone.xml in the WildFly installation so it looks like:
<audit-log>
            <formatters>
                <json-formatter name="json-formatter"/>
            </formatters>
            <handlers>
                <syslog-handler name="syslog-tls" formatter="json-formatter" syslog-format="RFC5424">
                    <tls host="kabirlinux" port="514" message-transfer="NON_TRANSPARENT_FRAMING">
                        <truststore path="/Users/kabir/test/truststore" keystore-password="test123"/>
                        <client-certificate-store path="/Users/kabir/test/client.jks" keystore-password="test123"/>
                    </tls>
                </syslog-handler>
            </handlers>
            <logger log-boot="true" log-read-only="true" enabled="true">
                <handlers>
                    <handler name="syslog-tls"/>
                </handlers>
            </logger>
        </audit-log>
Now if you restart the rsyslog daemon and WildFly you should see the log messages come through again. So this means that you can host rsyslog on a machine not accessible by the people who have access to your WildFly installation. Communication between the WildFly installation and rsyslog is encrypted since it goes over TLS, and only users who have the correct certificate are able to send data to rsyslog.

Wednesday, March 14, 2012

Transylvania Java User Group Roundup

I just got back from the presenting JBoss Application Server 7 at the Transylvania User Group last night. I didn't know much about Romania before I went, but I was impressed by the friendly people and enthusiastic crowd at the event, and was taken very well care of by Gabriel Pop, the organiser, and the other locals. Everything was great apart from that the only direct flights between Cluj and London mean getting up at 4am both going there and coming back :-) The centre of Cluj was pretty, and the restaurants I got taken to were excellent.

We covered why JBoss AS 7 is so fast and had a demo introducing Java EE 6. A majority of the crowd were Spring users but the feedback I got indicated that they were impressed by the simplified programming model introduced in Java EE 6. I got a few chuckles when I complained that the initial start of the application server during the demo was slow, since it started in 2 seconds. Restarting it it started in the 1.3s I am used to on my laptop.

I then went on to explain and demo the JBoss 7 management model, and then revisited the original EE 6 demo and tested it using Arquillian before adding JAX-RS features to it and deploying it to OpenShift using a mobile HTML 5 client to interact with the server via JAX-RS.

The feedback was very good after the event, I had lots of people using JBoss AS 5.x, WebLogic, WebSphere etc. coming up to me saying they now wanted to move to AS 7. And thanks to the people who hung around for dinner afterwards, it was fun - and yes I did manage to get up for my flight after 3 hours of sleep!

Friday, March 02, 2012

JBoss AS 7 at Transylvania JUG

I'll be going to Transylvania to present at the Transylvania Java User Group on 13 March.

I'll be talking about the recently released JBoss AS 7. I'll cover the road to Java EE 6 certification, how we did the performance improvements and our brand new management layer. There will be plenty of demos taking a basic EE application, testing it and getting it working in the cloud with a HTML 5 client.

Hope to see you there!

Wednesday, November 23, 2011

JBoss AS 7 at Devoxx

I just got back from my first ever Devoxx, and it is a really great conference - I was very pleasantly surprised! The auditoriums for the main talks were amazing.

Dimitris, Pete, Carlo, Dan, Lincoln, Aslak, Andrew, Max and I (apologies if I forgot anybody, there were quite a few of us) did a BOF on JBoss AS 7 with good attendance and some great questions from the audience. In hindsight I wish I'd prepared some demos for anticipated questions though.

It was a very nice experience to hang around the JBoss booth and finally meet some real AS 7 users after having worked on AS 7 the past year and a half. Users of older versions of the application server seemed to be impressed by the much improved configuration and domain management of AS 7. From the chats I had that seemed to be the thing people were most impressed by/disliked the most in previous versions.

Then there were numerous other talks by the usual JBoss suspects, all of which were great and packed. Since I'm biased and work on AS 7 I loved Andrew Rubinger and Dan Allen's talk on AS 7, and of course Pete Muir's talk on OpenShift. There will be more blogs from the rest of the team on our jboss.org events page.

Wednesday, June 29, 2011

Getting started with JBoss Application Server 7.0.0 CR1 in minutes

JBoss Application Server 7.0.0 CR1 was released this morning (GMT). I'm sure Jason Greene, the AS 7 lead will post more details in the next day or two. In the meantime, I'd like to give you a very quick overview of how to get this up and running in a few minutes.

The most time-consuming step is to grab it from http://download.jboss.org/jbossas/7.0/jboss-7.0.0.CR1/jboss-7.0.0.CR1.zip (later releases will be accessible from the main download page http://www.jboss.org/jbossas/downloads).

Now go to your download directory and unzip it to somewhere, and then go to the jboss-7.0.0.CR1/bin folder:

[kabir ~/Downloads]
$unzip -q jboss-7.0.0.CR1.zip -d ~/AS7

[kabir ~/Downloads]
$cd ~/AS7/jboss-7.0.0.CR1/bin/



Now that the slow part is done, let's start it up in single server mode:


[kabir ~/AS7/jboss-7.0.0.CR1/bin]
$./standalone.sh
=========================================================================

JBoss Bootstrap Environment

JBOSS_HOME: /Users/kabir/AS7/jboss-7.0.0.CR1

JAVA: /Library/Java/Home//bin/java

JAVA_OPTS: -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djboss.modules.system.pkgs=org.jboss.byteman

=========================================================================

11:55:25,759 INFO [org.jboss.modules] JBoss Modules version 1.0.0.CR4
11:55:26,009 INFO [org.jboss.msc] JBoss MSC version 1.0.0.CR2
11:55:26,077 INFO [org.jboss.as] JBoss AS 7.0.0.CR1 "White Rabbit" starting
11:55:26,955 INFO [org.jboss.as] creating http management service using network interface (management) port (9990) securePort (-1)
11:55:26,965 INFO [org.jboss.as.logging] Removing bootstrap log handlers
11:55:26,997 INFO [org.jboss.as.connector.subsystems.datasources] (Controller Boot Thread) Deploying JDBC-compliant driver class org.h2.Driver (version 1.2)
11:55:27,101 INFO [org.jboss.as.naming] (Controller Boot Thread) Activating Naming Subsystem
11:55:27,157 INFO [org.jboss.as.osgi] (Controller Boot Thread) Activating OSGi Subsystem
11:55:27,188 INFO [org.jboss.as.security] (Controller Boot Thread) Activating Security Subsystem
11:55:27,208 INFO [org.jboss.as.naming] (MSC service thread 1-4) Starting Naming Service
11:55:27,215 INFO [org.jboss.remoting] (MSC service thread 1-3) JBoss Remoting version 3.2.0.Beta2
11:55:27,229 INFO [org.xnio] (MSC service thread 1-3) XNIO Version 3.0.0.Beta3
11:55:27,248 INFO [org.xnio.nio] (MSC service thread 1-3) XNIO NIO Implementation Version 3.0.0.Beta3
11:55:27,518 INFO [org.apache.catalina.core.AprLifecycleListener] (MSC service thread 1-2) The Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
11:55:27,539 INFO [org.jboss.as.remoting] (MSC service thread 1-3) Listening on /127.0.0.1:9999
11:55:27,575 WARN [org.jboss.osgi.framework.internal.URLHandlerPlugin] (MSC service thread 1-2) Unable to set the URLStreamHandlerFactory
11:55:27,576 WARN [org.jboss.osgi.framework.internal.URLHandlerPlugin] (MSC service thread 1-2) Unable to set the ContentHandlerFactory
11:55:27,590 INFO [org.jboss.as.ee] (Controller Boot Thread) Activating EE subsystem
11:55:27,593 INFO [org.jboss.as.jmx.JMXConnectorService] (MSC service thread 1-4) Starting remote JMX connector
11:55:27,604 INFO [org.apache.coyote.http11.Http11Protocol] (MSC service thread 1-3) Starting Coyote HTTP/1.1 on http--127.0.0.1-8080
11:55:27,732 INFO [org.jboss.as.connector] (MSC service thread 1-3) Starting JCA Subsystem (JBoss IronJacamar 1.0.0.CR2)
11:55:27,855 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) Bound data source [java:jboss/datasources/ExampleDS]
11:55:28,255 INFO [org.jboss.as.deployment] (MSC service thread 1-1) Started FileSystemDeploymentService for directory /Users/kabir/AS7/jboss-7.0.0.CR1/standalone/deployments
11:55:28,267 INFO [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.0.CR1 "White Rabbit" started in 2750ms - Started 91 of 146 services (55 services are passive or on-demand)!

And that's it! JBoss Application Server 7.0.0 started in 2750ms. Leave it running for now.

Go to http://localhost:8080/ for the welcome page which contains links to our admin console and documentation. Our documentation is being created online at https://docs.jboss.org/author/display/AS7/Documentation and there is a fair bit of content already, but make sure you check in regularly since for our next release we will be focussing 100% on our documentation. The docs include a more extensive quick start guide than this.

To deploy applications one way to do it is to copy them to the jboss-7.0.0.CR1/standalone/deployments folder. Since this is not a Java EE tutorial, let's use the first example war I could find online:



[kabir ~/AS7]
$curl http://gwt-examples.googlecode.com/files/Calendar.war > Calendar.war
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 529k 100 529k 0 0 312k 0 0:00:01 0:00:01 --:--:-- 356k

[kabir ~/AS7]
$cp Calendar.war jboss-7.0.0.CR1/standalone/deployments/



Now in the terminal window where you started AS 7 you should see that the application has been deployed:


11:55:28,267 INFO [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.0.CR1 "White Rabbit" started in 2750ms - Started 91 of 146 services (55 services are passive or on-demand)
12:05:13,713 INFO [org.jboss.as.server.deployment] (MSC service thread 1-1) Starting deployment of "Calendar.war"
12:05:14,262 INFO [org.jboss.web] (MSC service thread 1-3) registering web context: /Calendar
12:05:14,290 INFO [org.jboss.as.server.controller] (DeploymentScanner-threads - 2) Deployed "Calendar.war"


If you go to http://localhost:8080/Calendar you should see the deployed web app.

See the docs for more information about how to make JBoss AS 7 work for you!

And please report any issues at our users forum.

Monday, July 12, 2010

Microcontainer Inspector Tool Update

The Microcontainer Inspector Tool build did not work following the move to our new Maven repository at JBoss. I have updated the build and also changed the packaging to create a client jar and a server jar to be deployed. The Installation section of the original post have been updated with the new instructions.

This has made me wonder if the build will work for people who do NOT have the JBoss maven repositories set up as described here. If you are trying to build it and it does not work for you please leave me a comment and I'll fix the build.

Friday, May 14, 2010

JBoss User Group Belgium

I've been invited to talk at the Belgian JBoss User Group near Antwerp on 3rd June. The title of my talk will be "What's Cooking in JBoss AS 6" and I will talk about our current development model, ongoing optimizations and give a high-level overview of the new projects in AS 6 such as Weld, RestEasy, HornetQ and many more.

Hope to see you there!

Tuesday, May 04, 2010

JSR-330 and Qualifiers in the JBoss Microcontainer

The latest JBoss Microcontainer jboss-kernel 2.2.0.Alphas contain support for the @javax.inject.Inject and @javax.inject.Qualifier annotations from JSR-330.

While most of the use of the JBoss Microcontainer in JBoss AS uses explicitly named injection, e.g.

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="MyDependency" class="org.jboss.test.Dependency"/>
   <bean name="MyBean" class="org.jboss.test.Bean">
      <property name="dependency"><inject bean="MyDependency"/></property>
   </bean>
</deployment>

The above means that when deploying the MyBean bean bean it will look up the MyDependency bean by name and inject that instance into MyBean's dependency property.

Contextual injection
We can also look up things by type, first let us see how to do this using xml. This has been supported by the Microcontainer for a while, so this might be familiar:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="MyDependency" class="org.jboss.test.Dependency"/>
   <bean name="MyBean" class="org.jboss.test.Bean">
      <property name="dependency"><inject/></property>
   </bean>
</deployment>

Now if the definition of MyBean's class is: package org.jboss.test;
public class Bean{
   Dependency dependency;

   public void setDependency(Dependency dependency){
      this.dependency = dependency;
   }
}

When creating MyBean this time, we have not given the name of the bean to use when configuring the dependency property. Instead the Microcontainer will check the type of the property, and find which beans are installed in the Microcontainer that can be cast to that type. In this case it finds MyDependency and uses that. If there had been more beans that could be cast to this type, an error would have been thrown. We'll see later how qualifiers can be used to resolve the ambiguities.

Using @javax.inject.Inject 
Now let us do the same using annotations. We first need to install the beans into the MC, the simplest way is to use an xml file to define the beans:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="MyDependency" class="org.jboss.test.Dependency"/>
   <bean name="MyBean" class="org.jboss.test.Bean"/> 
</deployment>

Notice that we did not define any injection for the dependency property in this case, instead we use @javax.inject.Inject on MyBean's class to define the injection:

package org.jboss.test; 

import javax.inject.Inject; 

public class Bean{

   Dependency dependency;

   @Inject
   public void setDependency(Dependency dependency){ 
      this.dependency = dependency;
   } 
} 
What happens now is that when MyBean is deployed we find the @javax.inject.Inject annotation on the dependency property and this causes a lookup of the bean to be found. As in the last example it looks up the bean by type, and throws an error if more that one can be found.

Qualifiers to the rescue
If we deploy the following beans:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="SomeDependency" class="org.jboss.test.SomeDependency"/>
   <bean name="TestDependency" class="org.jboss.test.TestDependency"/>
   <bean name="MyBean" class="org.jboss.test.Bean"/>
</deployment>

with the following classes
package org.jboss.test;

public class SomeDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

public class TestDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;
import javax.inject.Inject;

public class Bean{
   Dependency dependency;

   @Inject
   public void setDependency(Dependency dependency){
      this.dependency = dependency;
   }
}

Now we have an ambiguity since both TestDependency and SomeDependency can be cast to org.jboss.test.Dependency. To give the Microcontainer a hint about which of these instances should be used we can use a qualifier. A qualifier is any annotation that has been annotated with the @javax.inject.Qualifier annotation. For example:
package org.jboss.test;

import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
public @interface Test{
}

Now we can use this qualifier on our beans by adding it to TestDependency's class and to the Bean.dependency injection point:

package org.jboss.test;

@Test
public class TestDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

import javax.inject.Inject;

public class Bean{
   Dependency dependency;

   @Inject @Test
   public void setDependency(Dependency dependency){
      this.dependency = dependency;
   }
}

Now the Microcontainer will look up all beans that can be cast to org.jboss.test.Dependency AND that have the @org.jboss.test.Test annotation. In this case only TestDependency satisfies those criteria and is used for injection.

Qualifiers via xml
As part of implementing qualifiers in the Microcontainer we built in native support for qualifiers and came up with something somewhat more powerful that what is indicated by JSR-330. First let's deploy the classes from the previous example without any annotations:

package org.jboss.test;

public class SomeDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

public class TestDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

import javax.inject.Inject;

public class Bean{
   Dependency dependency;

   public void setDependency(Dependency dependency){
      this.dependency = dependency;
   }
}

This time the injection and qualifier annotations are driven by xml:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="SomeDependency" class="org.jboss.test.SomeDependency"/>
   <bean name="TestDependency" class="org.jboss.test.TestDependency">
      <qualifier content="Annotation">@org.jboss.test.Test</qualifier>
   </bean>
   <bean name="MyBean" class="org.jboss.test.Bean">
      <property name="dependency">
         <inject>
            <qualifier content="Annotation" type="Required">@org.jboss.Test</qualifier>
         </inject>
      </property>
   </bean>
</deployment>

Now, we're saying that TestDependency provides the @org.jboss.test.Test qualifier, and when injecting Bean.dependency we want to inject any bean that is of type Dependency and that has the @org.jboss.test.Test qualifier. As when using "real" annotations it will pick out the TestDependency bean. The qualifier content is Annotation which means the parser will try to create an annotation from the body of the qualifier annotation. When qualifier annotations are applied in this way, they do not need to be annotated with @javax.inject.Inject.

A qualifier does not have to be an annotation, simple strings can be used by omitting the content attribute:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="SomeDependency" class="org.jboss.test.SomeDependency"/>
   <bean name="TestDependency" class="org.jboss.test.TestDependency">
      <qualifier>test</qualifier>
   </bean>
   <bean name="MyBean" class="org.jboss.test.Bean">
      <property name="dependency">
         <inject>
            <qualifier type="Required">test</qualifier>
         </inject>
      </property>
   </bean>
</deployment>

Qualifiers can also be used in other places supporting the inject element, such as parameters to constructors and install methods.

Bean level qualifiers
In the previous example we applied qualifiers to a particular property. As a convenience you can choose default qualifiers that should be applied when doing injection at bean level. This is useful when you have a lot of properties that need injecting, here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="SomeDependency" class="org.jboss.test.SomeDependency"/>
   <bean name="TestDependency" class="org.jboss.test.TestDependency">
      <qualifier>test</qualifier>
   </bean>
   <bean name="SomeOtherDependency" class="org.jboss.test.SomeOtherDependency">
      <qualifier>test</qualifier>
   </bean>
   <bean name="TestOtherDependency" class="org.jboss.test.TestOtherDependency">
      <qualifier>test</qualifier>
      <qualifier>other</qualifier>
   </bean>
   <bean name="MyBean" class="org.jboss.test.Bean">
      <qualifier type="Required">test</qualifier>
      <qualifier type="Optional">other</qualifier>
      <property name="dependency"><inject/></property>
      <property name="otherDependency"><inject/></property>
   </bean>
</deployment>

Now we are saying that when doing contextual injection into MyBean's properties, the candidate beans MUST have the test qualifier, and that the other qualifier can be used to further strip out ambiguities. If we have the following bean classes:

package org.jboss.test;

public class SomeDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

public class TestDependency extends Dependency{
}
- - - - - - - - -
package org.jboss.test;

public class SomeOtherDependency extends OtherDependency{
}
- - - - - - - - -
package org.jboss.test;

public class TestOtherDependency extends OtherDependency{
}
- - - - - - - - -
package org.jboss.test;

import javax.inject.Inject;

public class Bean{
   Dependency dependency;

   OtherDependency otherDependency;

   public void setDependency(Dependency dependency){
      this.dependency = dependency;
   }

   public void setOtherDependency(OtherDependency otherDependency){
      this.otherDependency = otherDependency;
   }
}

then TestDependency is used for MyBean.dependency and TestOtherDependency is used for injection into MyBean.otherDependency.

Custom qualifiers
If the above qualifiers are not enough to narrow down what you want to inject, you can create custom qualifiers as shown in this simple example. If we deploy the following beans we end up with Dependency3 being injected into MyBean.dependency:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
   <bean name="TestMatcherAndParser"
      class="org.jboss.test.kernel.qualifiers.support.TestMatcherAndParser"/>
      <bean name="QualifierMatchers" class="org.jboss.kernel.spi.qualifier.QualifierMatchers">
         <constructor factoryMethod="getInstance">
      </constructor>
      <install method="addParser">
         <parameter><inject bean="TestMatcherAndParser"/></parameter>
      </install>
      <install method="addMatcher">
         <parameter><inject bean="TestMatcherAndParser"/></parameter>
      </install>
      </bean> 
      <bean name="Dependency1" class="org.jboss.test.SomeDependency">
         <qualifier>test</qualifier>
      <qualifier content="Test">Hello-xxx</qualifier>
      </bean>
      <bean name="Dependency2" class="org.jboss.test.Dependency">
         <qualifier>test</qualifier>
      </bean>
      <bean name="Dependency3" class="org.jboss.test.TestDependency">
         <qualifier>test</qualifier>
         <qualifier content="Test">Hola-xxx</qualifier>
      </bean>
      <bean name="MyBean" class="org.jboss.test.Bean">
         <qualifier type="Required">test</qualifier>
      <qualifier type="Required" content="Test">xxx-Hola</qualifier>
    </bean>
</deployment>

The magic behind understanding content="Test" lives in the TestMatcherAndParser bean which is installed via the QualifierParser singleton:
package org.jboss.test.kernel.qualifiers.support;

import java.util.Set;
import org.jboss.beans.metadata.api.model.QualifierContent;
import org.jboss.dependency.spi.ControllerContext;
import org.jboss.kernel.spi.qualifier.QualifierMatcher;
import org.jboss.kernel.spi.qualifier.QualifierParser;

public class TestMatcherAndParser implements QualifierMatcher, QualifierParser{
   public TestMatcherAndParser(){
   }

   //QualifierParser methods
   public QualifierContent getHandledContent(){
      return QualifierContent.getContent("Test");
   }

   public Object parseSupplied(ClassLoader cl, Object rawQualifier){
      return new Supplied((String)rawQualifier);
   }

   public Object parseWanted(ClassLoader cl, Object object){
      return new Wanted((String)object);
   }

   //QualifierMatcher methods
   public Class getHandledType(){
      return Wanted.class;
   }

   public boolean matches(ControllerContext context, Set<Object> suppliedQualifiers, Wanted qualifier){
      for (Object supplied : suppliedQualifiers){
         if (supplied instanceof Supplied)
            return qualifier.getString().equals(((Supplied)supplied).getString());
      }
      return false;
   }

   private static class Supplied{
      String string;

      public Supplied(String string){
         int i = string.indexOf("-xxx");
         if (i >= 0)
            string = string.substring(0, i);
            this.string = string;
      }

      public String getString(){
         return string;
      }
   }
}
- - - - - - - - -
package org.jboss.test.kernel.qualifiers.support;

public class Wanted{
   String string;
   public Wanted(String string){
      this.string = string;
      if (string.startsWith("xxx-"))
         string = string.substring(4);
         this.string = string;
      }

   public String getString(){
      return string;
   }
}

TestMatcherAndParser implements the QualifierParser and QualifierMatcher interfaces. The getHandledContent() method is what links this parser to content="Test". When parsing a qualifier with that content type supplied from from a bean (e.g. the qualifier entries in Dependency1 and Dependency3) we end up in the parseSupplied() method which creates an instance of Supplied. Similarly when parsing a qualifier with that content type from a bean wanting to inject something we end up in the parseWanted() method which creates an instance of Wanted.

So for Dependency1 we end up with a Supplied containing the string Hello and for Dependency3 we end up with a Supplied containing the string Hola. We end up with a Wanted containing the string Hola for MyBean.
Next when checking the qualifiers when doing injection into MyBean and checking

<qualifier type="Required" content="Test">xxx-Hola</qualifier>

behind the scenes the microcontainer finds all the contexts that are of the correct type and which have supplied qualifiers where type content="Test", i.e. the contexts Dependency1 and Dependency3. Then for each of these contexts it calls matches() with all the qualifiers for the candidate bean along with the parsed Wanted qualifier for MyBean.

When checking Dependency1 it calls matches() with the Supplied containing Hello and the Wanted containing Hola which does not match, and for Dependency2 it calls matches() with the Supplied containing Hello and the Wanted containing Hola which matches, meaning that only Dependency1 matches all the criteria. When calling matches() the ControllerContext (with information about the candidate bean's name, type, instance and other things) is passed in as well.

In the provided example we are only doing a silly little check where the Supplied gets rid of the final -xxx and the Wanted gets rid of the leading -xxx. The point here is that it is simple to implement more advanced qualifier checking than is provided out-of-the-box, and this mechanism has already been used to provide support for matching OSGi properties.

Monday, March 08, 2010

Microcontainer Inspector Tool

For those of you familiar with the Twiddle application, which allows you to inspect the JMX MBeans in JBoss Application Server, I have created a similar tool for use with the JBoss Microcontainer. This means that you can now manage and inspect ALL the beans in the JBoss Application Server (or in a standalone Microcontainer application). I'll briefly outline the existing functionality below and how to use it. The functionality supported so far is quite basic, but hopefully it will be useful to somebody. I'll be adding more commands to it when I have the time.

Implementing commands to extend the feature set is very easy. If you would like to be a Microcontainer committer, this is a great opportunity to get involved by implementing something simple while you get to know the code and the team. If you are interested please contact me privately or via our forums.

Installation
If you want to run this in a standalone environment you simply create an instance of org.jboss.microcontainer.mcinspector.bean.McInspector and pass it your bootstrapped kernel as shown in the test cases. To install it into a JBoss Application Server installed to ~/jbossas/:


  • Check out the source from http://anonsvn.jboss.org/repos/jbossas/projects/mc-tools/mcinspector/trunk/ 
  • Build it by going to the checkout directory and run mvn install
  • Copy target/mcinspector-server.jar to ~/jbossas/server/default/deploy
  • Copy target/mcinspector.jar to ~/jbossas/bin
  • Copy src/main/resources/mcinspector.sh to ~/jbossas/bin
  • chmod 755 ~/jbossas/bin/mcinspector.sh

That's all, now start up JBoss as normal, and in another terminal: cd ~/jbossas/bin. This last terminal is the one we will be using to try out the commands in the rest of this demo.

Help
To get help you enter: ./mcinspector.sh help, which gives you an overview of the commands available. The command and its output are shown below:
$./mcinspector.sh help
Available commands:
  help   Prints the list of available commands.
  list   Lists all the available contexts.
  change Changes the state of the context.
  bean   Inspect a particular bean.
For more information about a particular command, enter 'help '.
Handled in 1026ms

You can also get more information about a particular command, e.g.:
$./mcinspector.sh help list
Lists all the contexts in the controller.
Usage: list [-o | -v] [ []*]
If no state arguments are passed in all states will be checked for contexts. Otherwise
only the passed in states will be checked
Options:
-o Overview. Show all the states in the controller with the number of contexts in each
   Normally only states with contexts will be shown
-e Error. Show only the contexts which are in the error state. Can not be used in combination  
   with state arguments.
-v Verbose. Show extra information for each context. Contexts with unresolved dependencies
   will have a '*' after their name. Additional information is in the format 
   (ControllerMode, Current state)
Handled in 523ms

List
Let's try out the list command. As promised in the help section, it lists all the states, in ascending order, in the Application Server's main controller and displays the number of beans found in each state:

$./mcinspector.sh list -o
NOT INSTALLED: 0 contexts
PREPARSE: 0 contexts
PARSE: 0 contexts
POSTPARSE: 0 contexts
PREDESCRIBE: 0 contexts
DESCRIBE: 0 contexts
CLASSLOADER: 0 contexts
POSTCLASSLOADER: 0 contexts
PREREAL: 0 contexts
REAL: 0 contexts
PREINSTALL: 0 contexts
DESCRIBED: 33 contexts
INSTANTIATED: 0 contexts
CONFIGURED: 0 contexts
CREATE: 0 contexts
START: 0 contexts
INSTALLED: 997 contexts
Handled in 509ms

To view which beans are in the different states we execute ./mcinspector.sh list. Since there are lot of beans in the Application Server I have deleted most of the output below:

DESCRIBED:
JBossLogManagerContextSelectorService
org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory
org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory
org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory
org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory
org.jboss.netty.channel.socket.oio.OioDatagramChannelFactory
org.jboss.netty.channel.local.DefaultLocalClientChannelFactory
org.jboss.netty.channel.local.DefaultLocalServerChannelFactory
ModClusterListener
ModClusterService
...
INSTALLED:
JBossServer
ClassLoaderSystem
ClassLoaderDomain
ClassLoading
bootstrap-classloader:0.0.0$MODULE
bootstrap-classloader:0.0.0
stdio-classloader:0.0.0$MODULE
stdio-classloader:0.0.0
JBossStdioSystemOutStream
JBossStdioSystemErrStream
…
McInspectorCommands
McInspector
org.jboss.kernel:service=McInspector
McInspectorHelpCommand
McInspectorListCommand
McInspectorChangeCommand
McInspectorBeanCommand
file:///Users/kabir/sourcecontrol/jbossas/trunk/subversion/build/target/jboss-6.0.0-SNAPSHOT/server/default/deploy/mcinspector.jar
Handled in 596ms

To get more information about the contexts for a given state, you can do ./mcinspector.sh list -v DESCRIBED. The verbose output currently shows the controller mode of the beans' contexts, along with the current state of the bean. The * means that the context has unresolved dependencies.

$./mcinspector.sh list -v DESCRIBED
DESCRIBED:
JBossLogManagerContextSelectorService* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.socket.oio.OioDatagramChannelFactory* (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.local.DefaultLocalClientChannelFactory (ON_DEMAND, DESCRIBED)
org.jboss.netty.channel.local.DefaultLocalServerChannelFactory (ON_DEMAND, DESCRIBED)
ModClusterListener* (ON_DEMAND, DESCRIBED)
ModClusterService* (ON_DEMAND, DESCRIBED)
HAModClusterService* (ON_DEMAND, DESCRIBED)
ModClusterConfig* (ON_DEMAND, DESCRIBED)
DynamicLoadBalanceFactorProvider* (ON_DEMAND, DESCRIBED)
AverageSystemLoadMetric* (ON_DEMAND, DESCRIBED)
OperatingSystemLoadMetricSource (ON_DEMAND, DESCRIBED)
BusyConnectorsLoadMetric* (ON_DEMAND, DESCRIBED)
ThreadPoolLoadMetricSource* (ON_DEMAND, DESCRIBED)
SimpleLoadBalanceFactorProvider* (ON_DEMAND, DESCRIBED)
SystemMemoryUsageLoadMetric* (ON_DEMAND, DESCRIBED)
HeapMemoryUsageLoadMetric* (ON_DEMAND, DESCRIBED)
RequestCountLoadMetric* (ON_DEMAND, DESCRIBED)
ReceiveTrafficLoadMetric* (ON_DEMAND, DESCRIBED)
SendTrafficLoadMetric* (ON_DEMAND, DESCRIBED)
RequestProcessorLoadMetricSource* (ON_DEMAND, DESCRIBED)
ActiveSessionsLoadMetric* (ON_DEMAND, DESCRIBED)
SessionLoadMetricSource* (ON_DEMAND, DESCRIBED)
ConnectionPoolUsageMetric* (ON_DEMAND, DESCRIBED)
ConnectionPoolLoadMetricSource* (ON_DEMAND, DESCRIBED)
MBeanAttributeLoadMetric* (ON_DEMAND, DESCRIBED)
MBeanAttributeRatioLoadMetric* (ON_DEMAND, DESCRIBED)
MBeanQueryLoadMetricSource* (ON_DEMAND, DESCRIBED)
XnioProvider* (ON_DEMAND, DESCRIBED)
Handled in 479ms

Bean
To get more information about one or more beans in particular, you can run the bean command. e.g.:
$./mcinspector.sh bean ModClusterListener ModClusterService
Name='ModClusterListener'
  bean type=org.jboss.modcluster.catalina.CatalinaEventHandlerAdapter
  mode=ON_DEMAND
  state=DESCRIBED
  required state=DESCRIBED
  context type=org.jboss.kernel.plugins.dependency.AbstractKernelControllerContext
  unresolved dependencies=1
Name='ModClusterService'
  bean type=org.jboss.modcluster.ModClusterService
  mode=ON_DEMAND
  state=DESCRIBED
  required state=DESCRIBED
  context type=org.jboss.kernel.plugins.dependency.AbstractKernelControllerContext
  unresolved dependencies=5
Handled in 485ms

To get some more information about the dependencies, you can pass in the -d flag
$./mcinspector.sh bean -d ModClusterListener
Name='ModClusterListener'
  bean type=org.jboss.modcluster.catalina.CatalinaEventHandlerAdapter
  mode=ON_DEMAND
  state=DESCRIBED
  required state=DESCRIBED
  context type=org.jboss.kernel.plugins.dependency.AbstractKernelControllerContext
  unresolved dependencies=1
  Resolved dependencies:
  Unresolved dependencies:
    iDependOn=ModClusterService; whenRequired=INSTANTIATED; dependentState=INSTALLED

The -u flag is the same as -d apart from it does not look for resolved dependencies:

$./mcinspector.sh bean -d ModClusterListener
Name='ModClusterListener'
  bean type=org.jboss.modcluster.catalina.CatalinaEventHandlerAdapter
  mode=ON_DEMAND
  state=DESCRIBED
  required state=DESCRIBED
  context type=org.jboss.kernel.plugins.dependency.AbstractKernelControllerContext
  unresolved dependencies=1
  Unresolved dependencies:
    iDependOn=ModClusterService; whenRequired=INSTANTIATED; dependentState=INSTALLED
Handled in 492ms


Change
Finally there is the change command which attempts to change the state of a bean, so it can for example be used to temporarily disable a bean. Only do this if you know what you are doing! For example, let us try to stop the bean that implements the MC Inspector's bean command:

$./mcinspector.sh change INSTANTIATED McInspectorBeanCommand
Changed 'McInspectorBeanCommand' to INSTANTIATED
Handled in 473ms

This has stopped the bean implementing the bean command, so bean no longer appears in the list of available commands:
$./mcinspector.sh help
Available commands:
  help Prints the list of available commands.
  list Lists all the available contexts.
  change Changes the state of the context.
  For more information about a particular command, enter 'help '.
Handled in 497ms

And it appears in the INSTANTIATED state:
$./mcinspector.sh list INSTANTIATED -v
INSTANTIATED:
McInspectorBeanCommand (AUTOMATIC, INSTANTIATED)
Handled in 488ms


Let us make it installed again:
$./mcinspector.sh change INSTALLED McInspectorBeanCommand
Changed 'McInspectorBeanCommand' to INSTALLED
Handled in 474ms

Now bean appears in the list of available commands again:
$./mcinspector.sh help
Available commands:
  help Prints the list of available commands.
  list Lists all the available contexts.
  change Changes the state of the context.
  bean Inspect a particular bean.
For more information about a particular command, enter 'help
'.
Handled in 480ms


Friday, February 19, 2010

Optimizing the JBoss Microcontainer


Ales linked to the ongoing optimization work before I had planned on writing anything, so here is a very rushed overview of what is going on :-) 

For JBoss Application Server 6.0.0.M2 a lot of work went in to optimizing the boot time. Some of the work done by the AS team that really made a difference was optimizing the algorithm to add the deployers, and to make things like the admin console start when first accessed rather than use eager loading.

As part of this, for JBoss Kernel 2.2.0.Alpha6 I took a look at optimizing the dependency resolution algorithm of the Microcontainer, which is used to install and wire together the services the core of the application server. The dependency resolution algorithm is used to determine if the beans implementing the services can be moved through the states on the way from being put into the Microcontainer to being fully up and running. Initially I did work on a fully optimized prototype, but this was too big and risky an undertaking for the short timeframe and did not yield the expected results. This prototype is far from "finished" and there is plenty of room for improvement, so I might come back to it at some later stage.

So instead I went through and used JProfiler to see what is taking the time in the dependency resolution algorithm that we have today. What really made a difference here was that I found that when a bean can not be moved to the next state we were looking up its unresolved dependency three times in the Microcontainer. Wrapping these three calls into one gave a lot less overhead. That was the main optimization we had time for for JBoss Kernel 2.2.0.Alpha6 which is what is used in AS 6.0.0.M2. 

Since then I have been working on quite a few other optimizations that you can see on the Microcontainer Development Forum, more specifically here, which so far significantly improve the boot time of 6.0.0.M2 in my local benchmarks, and I am nowhere not done yet. The fixes are mainly making sure that objects used in maps are properly hashed, and making objects that are expensive to create less expensive to create. All these fixes will all go into the next community release of JBoss AS: 6.0.0.M3.

Another thing is that John Bailey is currently integrating JBoss Virtual File System 3 into AS. VFS is used as an abstraction layer to read files from several different sources, and VFS 3 promises to really speed things up when reading from zipped archives. This is something that happens a lot in the application server, when scanning archives for deployment descriptors and annotations, and loading classes and so on. So it will be very exciting to see this in JBoss AS 6.0.0.M3!