Monday, 4 February 2013

JBoss 7 logging tips


To fully embrace jboss 7, use the jboss logging mechanism to specify categories and handlers.  It's not the easiest thing to get working, so I've put together a few hints and tips.

1) Use slf4j-api.jar as the logging facade.  Update the dependencies in the pom.xml to exclude everything else.

Be thorough as the slightest presence of log4j or any other logger, overrides the jboss logging mechanism.  In practice the easiest way is to do this it to use Eclipse to view the pom.xml "Dependency Hierarchy" and exclude any other loggers.   

2) Make sure log4j.properties is not on the classpath of the deployment.  Jboss uses log4j.properties by default - I don't know why!

3) Once and handler has been added, restart the server.

4) If logging is still not working, add the following system property:
org.jboss.as.logging.per-deployment=false



Monday, 14 January 2013

Jboss 7, not triggering @PostConstruct

Whilst porting a JSF 2 project over to Jboss 7 I came up against a problem of the @PostConstruct not getting triggered within a @ManagedBean. 

So have:


@ManagedBean
@ViewScoped
public class MyController {

  public String inp;

  @PostConstruct
  public void init() {
    // do something
  }

  public void setInp( String inp ) {
    this.inp = inp;
  }

  public String getInp() {
    return inp;
  }
}


Solution - Add the following to the web.xml:


<context-param>
 <param-name> 
   com.sun.faces.injectionProvider</param-name>
 <param-value>
   com.sun.faces.vendor.WebContainerInjectionProvider    
 </param-value>
</context-param>

The Jboss provider also works:
org.jboss.web.jsf.integration.injection.JBossInjectionProvider



See here for a detailed discussion.

Monday, 17 December 2012

How to create a JSF 2 component

Java Server Pages (JSF) is an extensible JEE web framework.  In this blog post I will demonstrate how to create a simple JSF component.

When developing a JSF component one of the technical aim is to make it as simple and obvious as possible for a developer to use it.  A user of our component shouldn't have to worry about handling call backs, updating state etc.  With this in mind lets begin:


Step 1) Create the java component:  


public class TextToUpperComponent {

  private String input;
  private String output;

  public void convert() {
    output = input.toUpperCase();
  }

  + getters and setters
}

This contains the state and the call back method ( convert() ) to handle button presses.

Step 2) Create the JSF Manatged Bean:.

@ManagedBean
public class MyBean {

  private TextToUpperComponent a = new TextToUpperComponent();
  private TextToUpperComponent b = new TextToUpperComponent();
  private TextToUpperComponent c = new TextToUpperComponent();

  + getters and setters
}

What we've done here, is to make the TextToUpperComponent component a property of the ManagedBean (3 times).   Other listeners/callbacks can be defined to inspect these properties and apply business logic as required.


Step 3)  Define the component -  webapp/resources/texttoupper.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <composite:interface>
    <composite:attribute name="title" />
    <composite:attribute name="jcomponent" />
  </composite:interface>

  <composite:implementation>
    <h:form id="myform">
      <h:panelGrid>
        <h:outputLabel value="#{cc.attrs.title}: " for="word" />
        <h:inputText id="word" value="#{cc.attrs.jcomponent.input}" />
        <h:outputText value="#{cc.attrs.jcomponent.output}" />
        <h:commandButton value="To Upper">
          <f:ajax listener="#{cc.attrs.jcomponent.convert()}" execute="myform" render="myform"/>
        </h:commandButton>
        <br/>
      </h:panelGrid>
    </h:form>
  </composite:implementation>
</html>


As we're placing it under webapp/resources/coder36, the component namespace will be: xmlns:c="http://java.sun.com/jsf/composite/coder36".

A point to note is that once the JSF page is rendered, the components are completely independent of each other, so there is no need to worry about clashing id's etc.


Step 4)  Create index.xhtml Page

Don't forget to include the namespace: 


<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"      
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jsf/composite/coder36"
      xmlns:ui="http://java.sun.com/jsf/facelets"> 

 <h:head></h:head> 
 <h:body>
   <c:texttoupper jcomponent="#{myBean.a}" title="First"/>      
   <c:texttoupper jcomponent="#{myBean.b}" title="Second"/>
   <c:texttoupper jcomponent="#{myBean.c}" title="Third"/>
 </h:body> 
</html>


And that's it!  With very little effort we have created a simple reusable JSF component and demonstrated how to integrate it.






Wednesday, 14 November 2012

Setting up a Jboss 7.1 Cluster

Sadly, setting up clustering in jboss 7.1.1 is not as straight forward as it should be.  After hours of googling and numerous false starts, I finally got it all working.  Here are some of the gotcha's which tripped me up.

My starting assumption is that we're running a domain.  Out of the box, the Master node defines 3 servers:
 Server-1    - main-server-group
 Server-2    - other-server-group
 Server-3    - main-server-group

Server-1 and Server-2 are configured to start automatically.  In a cluster these are of no use - disable them.  We're only interested in Server-3 which uses the full-ha profile.

Before we go on, a quick heads up:

  • You only need to amend the domain.xml on the master node
Some helpfull links:


Gotcha #1

When you start up the nodes using the domain.bat scripts, always provide the bind ip and ports.  If you don't then you seem to get some strange behaviours:

domain.sh-b 192.168.0.24 -bmanagement 192.168.0.24     (this is the master)
and
domain.sh-b 192.168.0.25 -bmanagement 192.168.0.25


Gotcha #2

Server-3 does not start cleanly out of the box, due to a mismatch between the full-ha progile and the socket bindings.  In domain.xml, amend as follows:


<server-group name="other-server-group" profile="full-ha">
  <jvm name="default"
    <heap size="64m" max-size="512m"/>
  </jvm>
  <socket-binding-group ref="full-ha-sockets"/>
</server-group>


This will allow Server-2 to start cleanly.

Gotcha #3

The hornetq-server needs configuring with a user name and password.  If you don't you will get an exception along the lines of:

[Server:server-three] 16:24:36,875 ERROR [org.hornetq.core.protocol.core.impl.Ho
rnetQPacketHandler] (Old I/O server worker (parentId: 2128830893, [id: 0x7ee361a
d, /192.168.0.24:5695])) Failed to create session : HornetQException[errorCode=1
05 message=Unable to validate user: HORNETQ.CLUSTER.ADMIN.USER]

Update domain.xml with:
<subsystem xmlns="urn:jboss:domain:messaging:1.1">
  <hornetq-server>                
  <cluster-user>fred</cluster-user>
  <cluster-password>password</cluster-password>


It looks as though you can use any user and password.  It doesn't appear to be authenticated against anything!

Gotcha #4

Use the correct version of @Clustered annotation!   If you don't then in a failover situation you will get the "No EJBReceiver" exception.  The version of @Clustered your'e after is org.jboss.ejb3.annotation.Clustered.  This comes from the jboss-ejb3-ext-api dependency, which is only available from the jboss repo.  Assuming that you are using maven add the following to the pom.xml:

<dependencies>
 ...

  <dependency>
    <groupId>org.jboss.ejb3</groupId>
    <artifactId>jboss-ejb3-ext-api</artifactId>
    <version>2.0.0-beta-3</version>
    <scope>provided</scope>
  </dependency>

<dependencies>
...
<repositories>
  <repository>
    <id>JBoss repository</id>
    <url>http://repository.jboss.org/nexus/content/groups/public/</url>
  </repository>
<repositories>

Then correctly annotate the bean:

import org.jboss.ejb3.annotation.Clustered;

@Stateful
@Clustered
@Remote( coder36.demo.service.MyService.class )
public class MyServiceImpl implements MyService {
...

Gotcha #5

When injecting the EJB do not use the @Inject annotation!   Use @EJB on it's own.  No JNDI name is required:


@ManagedBean
public class PersonController implements Serializable {
  @EJB
  private MyService myService;


Note - the @EJB annotation is available via the following dependency:


<dependency>
  <groupId>org.jboss.spec.javax.ejb</groupId>
  <artifactId>jboss-ejb-api_3.1_spec</artifactId>
</dependency>


Gotcha #6

To make the web application cluster-able ensure that webapp/WEB-INF/web.xml exists, and has the <distributable/> tag:


<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0"> 

  <distributable/> 
</web-app>


To be extra helpful, I've provided the web.xml namespaces :)

Gotcha #7

Assuming you have downloaded and installed mod_cluster 1,2.Final from here.

Out of the box, mod_cluster does't  not work with jboss 7.1.1. A few tweaks are needed.  Keeping things as simple as possible, I've stripped the provided httpd.conf down the bare minimum:


LoadModule authz_host_module modules/mod_authz_host.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule manager_module modules/mod_manager.so
LoadModule slotmem_module modules/mod_slotmem.so
LoadModule advertise_module modules/mod_advertise.so

Listen 192.168.0.24:80
Listen 192.168.0.24:10001

<VirtualHost 192.168.0.24:10001>
  <Location />
    Order deny,allow
    Deny from all
    Allow from all
  </Location>

  KeepAliveTimeout 300
  MaxKeepAliveRequests 0

  EnableMCPMReceive

  <Location /mod_cluster_manager>
    SetHandler mod_cluster-manager
    Order deny,allow
    Deny from all
    Allow from all
  </Location>
</VirtualHost>
ErrorLog "logs/error_log"


I used port 10001 as the default port 6666 didn't work for me.  This httpd.conf should be enough to get you going.

The domain.xml also needs updating to link it up with the mod_cluster instance:


<subsystem xmlns="urn:jboss:domain:modcluster:1.0">
  <mod-cluster-config advertise-socket="modcluster" 
                      proxy-list="192.168.0.24:10001">
  ...


and

<subsystem xmlns="urn:jboss:domain:web:1.1" 
            default-virtual-server="default-host" 
            instance-id="${jboss.node.name}" 
            native="false">
...


Testing

To test everything is working, fire up the domain, deploy an application and start httpd (on windows 7 I needed to run it as administrator otherwise it complained about file permissions).  Navigate to:  http://192.168.0.24:10001/mod_cluster_manager.  You should see:



The web application can be now accessed through the mod_cluster proxy sitting on http://192.168.0.24 and http://192.168.0.24:10001:

ie. http://192.168.0.24/demo/index/jsf

Monday, 12 November 2012

How to set up a Jboss 7.1 domain

With the new JBOSS 7.1 domain controller, a cluster of jboss instances can be administered via a single console.  In this tutorial we will step through the simple process of setting up a domain controller, and 3 nodes.

Target Architecture:

Simplistically we are aiming for the following architecture:



I've assumed IP addresses, passwords and names to keep thing simple.

Later on we will need base64 version of the identity.  I used this website to generate the following base64 strings:

Identity     Base64
node1id      bm9kZTFpZA==
node2id      bm9kZTJpZA==
node3id      bm9kZTNpZA==

1) Configure the ManagementRealm

The nodes authenticates with the domain controller (DC) by passing it's node name and password.  The DC does a name/password lookup against the ManagementRealm.

so... the first step is to configure the ManagementRealm.

On the domain controller run:
add-user node1    (when prompted set password to node1id)
add-user node2    (when prompted set password to node1id)
add-user root      (when prompted set password to password)

(The root user is used for logging into the jboss console.)

Start up the domain controller:
domain -b 192.168.0.24 -bmanagement 192.168.0.24

2) Configure Node 1

Open host.xml (under jboss-as-7.1.1.Final\domain\configuration) and edit as follows:

a) Update <host tag to read:
<host name="node1" xmlns="urn:jboss:domain:1.2">

b) Add server-identities section to <security-realm name="ManagementRealm">


<security-realm name="ManagementRealm">
  <server-identities>
    <secret value="bm9kZTFpZA=="/>
  </server-identities>

  <authentication>
    ...
  </authentication>
</security-realm


The secret value is the base64 encoded version of the Node password.

c) Edit the <domain-controller> section to read:

<domain-controller>

  <remote host="${jboss.domain.master.address:192.168.0.24}"
          port="${jboss.domain.master.port:9999}" 
          security-realm="ManagementRealm"/>
</domain-controller>


Don't forget to add the the security-realm attribute otherwise you will get messages like:


[Host Controller] 16:26:06,454 ERROR [org.jboss.remoting.remote.connection] (Rem
oting "host1:MANAGEMENT" read-1) JBREM000200: Remote connection failed: javax.se
curity.sasl.SaslException: Authentication failed: all available authentication m
echanisms failed
[Host Controller] 16:26:06,469 ERROR [org.jboss.as.host.controller] (Controller
Boot Thread) JBAS010901: Could not connect to master. Aborting. Error was: java.
lang.IllegalStateException: JBAS010942: Unable to connect due to authentication
failure.


d) Start up the node
domain.bat -b 192.168.0.25 -bmanagment 192.168.0.25

3) Configure Node 2

Configure as above,  setting <host name="node2" and <secret value="bm9kZTJpZA=="

4) Configure Node 3

Configure as above,  setting <host name="node3" and <secret value="bm9kZTNpZA=="

Testing

If everything is working, then open a browser and navigate to: http://192.168.0.24:9990/console using root/password when prompted.  On the left hand side, there should be a list of nodes.


Friday, 9 November 2012

How to enable security logging in Jboss 7

To view what's happening within the jboss 7 security subsystem, you need to enable logging of org.jboss.security.

Assuming standalone.xml, update the <subsystem xmlns="urn:jboss:domain:logging:1.1"> section, adding a new console-handler which deals with TRACE level messages only:


<console-handler name="CONSOLE_TRC">
  <level name="TRACE"/>
  <formatter>
    <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
  </formatter>
</console-handler>

and link it up to a new org.jboss.securty logger:

<logger category="org.jboss.security">
  <level name="TRACE"/>
  <handlers>
    <handler name="CONSOLE_TRC"/>
  </handlers>
</logger>

Finally, restart the jboss server :)

Thursday, 8 November 2012

Securing JBoss 7 management console using SSL

This tutorial describes how to secure the JBOSS 7 web management console and the CLI management interface.   For clarity, it assumes you're running a stand alone jboss server.

Create a java keystore

Start by creating a java keystore with a self signed certificate:
c:
mkdir ssl
cd ssl
keytool -genkey -keyalg RSA -alias mycert -keystore keystore.jks -storepass   password -keypass password -validity 360 -keysize 2048 -dname "cn=coder36.com, O=Coder36 L=Newcastle, S=England, C=GB"

The keystore password is: password and the private key password is: password

View the certificate using:
keytool -list -v -keystore keystore.jks -alias mycert -storepass password

c:/ssl/keystore.jks will now  contain your private key and a self signed certificate.

Enable SSL

The next step is to enable SSL on the management interfaces.  Edit standalone.xml.  Search for the <management> tags and update.  I've highlighted what's changed from out of the box:




<management>
  <security-realms>
    <security-realm name="ManagementRealm">
      <server-identities>
        <ssl protocol="TLSv1">
          <keystore path="C:/ssl/keystore.jks" password="password"/>
        </ssl>
      </server-identities>
      <authentication>
        <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
      </authentication>
    </security-realm>
    <security-realm name="ApplicationRealm">
      <authentication>
        <properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
      </authentication>
    </security-realm>
  </security-realms>
  <management-interfaces>
    <native-interface security-realm="ManagementRealm">
      <socket-binding native="management-native"/>
    </native-interface>
    <http-interface security-realm="ManagementRealm">
      <socket-binding https="management-https"/>
    </http-interface>
  </management-interfaces>
</management>

Testing

Restart the JBoss standalone server and test:
Open a web and navigate to: https://localhost:9443/console. You will be presented with the usual warnings about using untrusted certificates   Interestingly, the https console does not work with Google Chrome, but works fine with Internet Explorer, and what ever is built into eclipse.

Test the CLI using:
jboss-cli.sh -c


Using this solution we can prove that the jboss server we are calling is who we think we are calling.  It's also more secure in that it encrypts traffic to and from the server.

Comments

With a few changes, it straight forward to get the management console protected with SSL.

I'm of the opinion that java key stores are a bit clunky and unfriendly.  I much prefer the .pem format with openssl etc.  From my commercial experience, it's generally a bad idea to use java to do the SSL work.  Architecturally it would be better to offload the SSL termination to dedicated hardware (CISCO provides ACE modules to do this kind of work), or dedicated software like apache - see one of my earlier posts for how this could be done.