Showing posts with label JBOSS. Show all posts
Showing posts with label JBOSS. Show all posts

Thursday, March 28, 2013

Richfaces Ajax CDI Push and JBoss



You may not know it, but when you go to a restaurant, you most likely to be served via a method called service à la russe (service in the Russian Style). This is in contradiction to the much older traditional service à la française (service in the French Style).

In the original French Style, all the food was brought from the kitchen into the dining room of the aristocrats or high clergy and served all at once. Then the cooks who lost their jobs during the French revolution, end-up opening places call restaurants to make a living when common people could come to eat and relax (from the verb se restaurer in French), meaning "to restore itself".

French serving style remains in some high class restaurants where small tables or guéridons are moved close to the guest's table where the food preparation is completed and served.



The buffet is a variation of the French Style where the guests help themselves from the table (in French it is traditionally a piece of furniture that looks like a table with drawers called in fact buffet).











On the other hand, the Russian Style that comes from Russia was introduced in the French restaurants during the 19th century where courses are brought to the table sequentially by the waiter. This is now the style in which most modern western restaurants serve food (with some significant modifications).

The place setting (called a cover) for each guest includes a service plate, all the necessary cutlery except those required for dessert, and stemmed glasses for water, wines and champagne

Guests immediately remove their napkins and place them in their laps.

The rule is as such: a filled plate is always replaced with an empty one, and no place goes without a plate until just before the dessert course.

Directly before dessert everything is removed from the place settings but the wine and water glasses. Crumbs are cleared and dessert is served.

As you can see, both styles have very precise rules and mechanisms that must be followed to ensure the best service.




Richfaces itself offers several Ajax server-side push mechanisms to bring data from the server to the client.

Interesting to point-out by the way, that a waiter in french is called "un serveur" (someone who serves) and that the same word is used to design a server in computer science.

These three Ajax Push methods are:
  • TopicsContext - accesses a RichFaces message queue directly
  • Push CDI - uses the CDI Event mechanism to fire messages
  • Push JMS - the RichFaces Push consumes messages from an enterprise messaging system and exposes them to the client (tightly coupled with the JMS runtime) 
In a previous article I was explaining how RichFaces Extended Data Tables can be enhanced.

In this article, I will describe how Richfaces Ajax Push CDI can be implemented and deployed specifically on a JBoss container (I am using JBoss application server 7.0)



The Richfaces showcase describes well as usual the java and JSF code needed to implements the CDI Ajax Push.  One thing though, the Push JMS mechanism in not explicated in the show case, but described in the chapter 3 of the documentation.

First what does CDI means?

If you look at your project  facets (I am using Eclipse IDE), you will see that CDI stands for Context and Dependency Injection.

Container/Context Dependency Injection in Java EE 6 decouples the processing threads of  event producers and event consumers by using the Observer pattern in the form of event broadcasting.

An event in CDI is just a regular POJO that can be fired by any class through the use of the Event implementation injected automatically by the container via the @Inject annotation.







 

import java.io.Serializable;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;

import javax.inject.Inject;
import javax.inject.Named;

import org.richfaces.cdi.push.Push;

@Named
@RequestScoped
public class PushCdiBean implements Serializable {

 private static final long serialVersionUID = 6414191802542861042L;
 public static final String PUSH_CDI_TOPIC = "pushCdi";
 private String message = "";

 @Inject
 @Push(topic = PUSH_CDI_TOPIC)
 Event<string>pushEvent;
 
 
 public void sendMessage() throws Exception {
  if (pushEvent == null) {
  } else {
   pushEvent.fire(message);
  }
  message = "";
 }

 public String getMessage() {
  return message;
 }

 public void setMessage(String message) {
  this.message = message;
 }
}

If you download the source code of the Richfaces showcase (I am using richfaces-4.3.1.Final), you will see that the readme file located under .\richfaces-4.3.1.Final\examples\richfaces-showcase describes useful steps necessary to deploy the sample application on various containers, including JBoss.



First, you need your Maven pom file to have both the Java API CDI and the atmosphere dependencies:
<dependency>
 <groupId>javax.enterprise</groupId>
 <artifactId>cdi-api</artifactId>
 <version>1.1-20130222</version> 
</dependency>
<dependency>
 <groupId>org.atmosphere</groupId>
        <artifactId>atmosphere-runtime</artifactId>
        <version>1.0.10</version>
</dependency>

To be able to inject your CDI bean, your WEB-INF folder needs to contain a bean.xml file:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:weld="http://jboss.org/schema/weld/beans" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd http://jboss.org/schema/weld/beans http://jboss.org/schema/weld/beans_1_1.xsd"> </beans>

On your deployment platform, you need JBoss standalone configuration located in .\standalone\configuration\standalone-full.xml to have a JMS topic that match your Push CDI topic:


<jms-destinations>
 <jms-queue name="testQueue">       
  <entry name="queue/test"/>  
  <entry name="java:jboss/exported/jms/queue/test"/>
        </jms-queue>  
 <jms-topic name="pushCdi">
         <entry name="topic/pushCdi"/>  
 </jms-topic>     
</jms-destinations>


You also need to start the JBoss server in full configuration:























Another necessary step is  to create the JMS user account/password:

























From there, you should be able to test your Ajax CDI push application. In my case, the consumer is a popup window that I launch by clicking a button (see code below):

Producer code:

<ui:define name="body">
 <h:form id ="from">
  <a4j:commandButton value="Message Consumer"
   oncomplete="#{rich:component('cdi_message_consumer_popup')}.show();" />
  <h:panelGrid columns="4">
   <h:outputLabel value="Message:" />
   <h:inputText id="messageInput" value="#{pushCdiBean.message}"/>
   <a4j:commandButton value="Send" 
    action="#{pushCdiBean.sendMessage}"
    execute="@form" 
    render="messageInput" 
    oncomplete="#{rich:element('messageInput')}.value=''"/>
  </h:panelGrid>
 </h:form>
 <ui:include src="rf_ajax_push_consumer_popup.xhtml" />
</ui:define>

Consumer code:

<rich:popupPanel 
 id="cdi_message_consumer_popup" 
 modal="false"
 resizeable="false" top="100" 
 left="300" 
 autosized="true"
 domElementAttachment="parent">
 <f:facet name="header">
  <h:outputText value="Ajax Push/CDI Message Consumer    " />
 </f:facet>
 <f:facet name="controls">
  <h:outputLink value="#"
   onclick="#{rich:component('cdi_message_consumer_popup')}.hide(); return false;">
   <h:outputText value="X" styleClass="textHeader" />
  </h:outputLink>
 </f:facet>
 <h:form>
  <h:panelGrid columns="3">
   <a4j:push address="pushCdi" 
    onerror="alert('error: ' + event.rf.data)"
    ondataavailable="jQuery('<li />').prependTo('#messages').text(event.rf.data)">
   </a4j:push>
   <ul id="messages" />
  </h:panelGrid>
 </h:form>
</rich:popupPanel>















The message (simple string) is send to the server and pushed back to the browser. The original message is then cleaned:
















A big thank to my colleague Asha Ambikavijayakumaran for figuring out all the tricky details for the JBoss deployment for this Richfaces showcase!


All ingredients and open source code related to this recipe can be found at YummyCode on the JSF Plate project.





Tuesday, May 29, 2012

REST API with CXF and Spring

 

For the past couple of years, I have developed REST APIs using RESTEasy since the target deployment platform was JBOSS. More recently I have considered using Apache CXF instead. One reason is that we wanted to use a single technology to create web services (we are using CXF for our SOAP based web services).

In this article I will show how to create a very simple REST web service using CXF and Spring and describe how to deploy it on JBOSS AS 7.0.

I have found several tutorials describing how to create CXF based REST web services but none with Spring  and working when deployed on JBOSS AS 7.0. Part of the proof of concept (POC) code sample I am describing in this article come from Sandeep Bhandari article "REST Web Service Using CXF - Beginner's Tutorial" which I enriched with Spring integration and deployment configuration for JBOSS AS 7.0.




In my POC example, there is not much differences for the POJO classes representing the order resources (I made them a little bit simple):








class Order
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "order")
public class Order {
 
    private String itemName;
    private int quantity;
    private String customerName;
    
    @XmlElement
    public String getItemName() { return itemName; }
    
    public void setItemName(String itemName) { this.itemName = itemName; }
    
    @XmlElement
    public int getQuantity() { return quantity; }
    
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
    
    @XmlElement
    public String getCustomerName() { return customerName; }
    
    public void setCustomerName(String customerName) { this.customerName = customerName; }
}
 
class OrderList
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "OrderList")
public class OrderList {

    List orders;
   
    @XmlElement(name = "order")
    public List getOrder() {
        if (orders == null) {
            orders = new ArrayList();
        }
        return this.orders;
    }
}
 
The web.xml file specify only the CXF servlet:
<web-app>

    <display-name>Archetype Created Web Application</display-name>
   
 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <servlet-class>
   org.apache.cxf.transport.servlet.CXFServlet
  </servlet-class>
   <load-on-startup>1</load-on-startup> 
 </servlet>

 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/api/*</url-pattern>
 </servlet-mapping>
 
</web-app> 
 
The file cxf-servlet.xml specify the service bean and the JAX-RS providers:
 <beans 
    xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:beans='http://www.springframework.org/schema/beans'
 xmlns:jaxrs="http://cxf.apache.org/jaxrs"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://cxf.apache.org/jaxrs 
 http://cxf.apache.org/schemas/jaxrs.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
  

  <jaxrs:server id="restContainer" address="/" >
  
            <jaxrs:serviceBeans>
        <ref bean="simpleService" />
     </jaxrs:serviceBeans>  
     
           <jaxrs:providers>
               <ref bean="jaxbProvider"/>
               <ref bean="jsonProvider"/> 
           </jaxrs:providers>
      
        </jaxrs:server>
     
     <bean id="simpleService" class="com.acme.poc.restapi.cxf.service.SimpleServiceImpl" />
  <bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider"/>  
  <bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
  
</beans> 
 
The SimpleService interface: 
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;  

@Path("/order/")
public interface SimpleService {
 @GET
 @Produces("application/xml")
 @Path("{orderId}")
 public Order getOrderXml(@PathParam ("orderId") int id);
 
 @GET
 @Produces("application/json") 
 @Path("/")
 public Order getOrderJson(@QueryParam("id") @DefaultValue("-1") String strId);
 
 @GET
 @Produces("application/xml")
 @Path("all")
 public OrderList getAllOrders();
} 
 
The SimpleServiceImpl class: 
import java.util.ArrayList;
import java.util.List;

public class SimpleServiceImpl implements SimpleService {
 
 List list = new ArrayList();
 
 public SimpleServiceImpl () {
         Order order = new Order();
         order.setItemName("Veggie Pizza");
         order.setQuantity(9);
         order.setCustomerName("OptumInsight");
         list.add(order);
         
         order = new Order();
         order.setItemName("Green Salad");
         order.setQuantity(2);
         order.setCustomerName("OptumInsight");
         list.add(order);
  }
 
 @Override
    public Order getOrderXml(int id) {
   return getOrder(id);
    }
 
 @Override
 public Order getOrderJson(String strId) {
  int id = Integer.valueOf(strId);
  return getOrder(id);
 }
 
 @Override
 public OrderList getAllOrders() {
  OrderList fullList = new OrderList();
        for(Order order : list) {
         fullList.getOrder().add(order);
        }
        return fullList;
 }
 
 // Common method returning an Order POJO
 public Order getOrder(int id) {
  if ((id > 0) && (id <= list.size())) {
   return list.get(id - 1);
  }
         else
          return null;
 }
}
 
Finally the pom.xml file for maven: 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.acme.poc.restapi</groupId>
  <artifactId>poc_restapi_cxf_client</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>poc_restapi_cxf_client</name>
  <url>http://maven.apache.org</url>
  
    <properties>
      <spring.version>3.1.0.RELEASE</spring.version>
      <cxf.version>2.6.0</cxf.version>
      <jackson.version>1.9.7</jackson.version>
      <junit.version>4.8.1</junit.version>    
   </properties>
  
   <dependencies>
   
     <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>${cxf.version}</version>
   <scope>provided</scope>
     </dependency>
   
     <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-search</artifactId>
      <version>${cxf.version}</version>
    <scope>provided</scope>
     </dependency>
     
     <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-providers</artifactId>
      <version>${cxf.version}</version>
    <scope>provided</scope>
     </dependency>
  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    
  </dependencies>
  
  <build>
    <finalName>poc_restapi_cxf_client</finalName>
     <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
               <source>1.6</source>
               <target>1.6</target>
               <encoding>UTF-8</encoding>
            </configuration>
         </plugin>
    </plugins>
  </build>
</project>



The file jboss-deployment-structure.xml is used for JBOSS deployment: 
<jboss-deployment-structure>
 <deployment>
  <dependencies>
   <module name="org.apache.cxf" services="import">
    <imports>
     <include path="**" />
    </imports>
   </module>
  </dependencies>
 </deployment>
</jboss-deployment-structure>
 
 
In addition to this, before you deploy on JBOSS, you need to make some modifications on your web application server:

1) Download the latest edition of CXF (2.6.0) and placed just one jar from the distribution into the folder /modules/org/apache/cxf/main/cxf-2.6.0.jar (This contains all of the CXF codebase, except the dependencies)
  
2) Open /modules/org/apache/cxf/main/module.xml
   Remove all resource elements (the JARs)
   Add a new resource element similar to the deleted ones with cxf-2.6.0.jar being the only Jar referenced
   Add dependency to module named: javax.ws.rs.api :


<module xmlns="urn:jboss:module:1.1" name="org.apache.cxf">
   <resources>
        <resource-root path="cxf-2.6.0.jar"/>
        <!-- 
        <resource-root path="cxf-api-2.4.4.jar"/>
          ...
        <resource-root path="cxf-xjc-ts-2.4.0.jar"/> 
        -->
    </resources>
     <dependencies>
        <module name="asm.asm" />
        <module name="javax.api" />
        <module name="javax.annotation.api" />
        <module name="javax.jms.api" />
        <module name="javax.jws.api" />
        <module name="javax.mail.api" />
        <module name="javax.resource.api" />
        <module name="javax.servlet.api" />
        <module name="javax.ws.rs.api" />
        <module name="javax.xml.bind.api" services="import"/>
        <module name="com.sun.xml.bind" services="import"/>
        <module name="javax.wsdl4j.api" />
        <module name="javax.xml.soap.api" />
        <module name="javax.xml.stream.api" />
        <module name="javax.xml.ws.api" />
        <module name="org.apache.commons.lang" />
        <module name="org.apache.neethi" />
        <module name="org.apache.velocity" />
        <module name="org.apache.xml-resolver" />
        <module name="org.apache.ws.xmlschema" />
        <module name="org.apache.ws.security" />
        <module name="org.apache.santuario.xmlsec" />
        <module name="org.springframework.spring" optional="true"/>
    </dependencies>
</module> 
 
3) Create folder /modules/org/springframework/spring/main/
   Create module.xml
   Copy all the text from the CXF module.xml into this new one
   Erase the last dependency (itself)
   Erase the resource against cxf JAR  (because this is the copy for spring)
   Download latest Spring framework, and copy all the Spring jars into the folder
   Add all of these JARs to the module.xml inside tag.
   Add dependency to module named: org.apache.commons.logging 


<module xmlns="urn:jboss:module:1.1" name="org.springframework.spring">
    <resources>
  <resource-root path="spring-aop-3.1.0.RELEASE.jar" />
  <resource-root path="spring-asm-3.1.0.RELEASE.jar" />
  <resource-root path="spring-beans-3.1.0.RELEASE.jar" />
  <resource-root path="spring-context-3.1.0.RELEASE.jar" />
  <resource-root path="spring-core-3.1.0.RELEASE.jar" />
  <resource-root path="spring-expression-3.1.0.RELEASE.jar" />
  <resource-root path="spring-web-3.1.0.RELEASE.jar" />
        <!-- Insert resources here -->
    </resources>
    <dependencies>
        <module name="asm.asm" />
        <module name="javax.api" />
        <module name="javax.annotation.api" />
        <module name="javax.jms.api" />
        <module name="javax.jws.api" />
        <module name="javax.mail.api" />
        <module name="javax.resource.api" />
        <module name="javax.servlet.api" />
        <module name="javax.xml.bind.api" services="import"/>
        <module name="com.sun.xml.bind" services="import"/>
        <module name="javax.wsdl4j.api" />
        <module name="javax.xml.soap.api" />
        <module name="javax.xml.stream.api" />
        <module name="javax.xml.ws.api" />
        <module name="org.apache.commons.lang" />
        <module name="org.apache.commons.logging" />
        <module name="org.apache.neethi" />
        <module name="org.apache.velocity" />
        <module name="org.apache.xml-resolver" />
        <module name="org.apache.ws.xmlschema" />
        <module name="org.apache.ws.security" />
        <module name="org.apache.santuario.xmlsec" />
    </dependencies>
</module>

Monday, April 2, 2012

RESTEasy Interceptors



RESTEasy can intercept JAX-RS invocations and route them through listener-like objects called interceptors. There are four interception points on the server side:
  • wrapping around MessageBodyWriter invocations
  • wrapping around MessageBodyReader invocations
  • through pre-processors, which intercept the incoming request before unmarshalling occurs
  • through post-processors, which are invoked immediately after the JAX-RS method finishes 
An interceptor can be very useful to implement crosscutting concerns such as logging and security. Although interceptors are not as powerful as Aspect Oriented Programming (AOP) - (e.g. no type-safety or debugging) , they still offer a quick way to create more compact and maintainable code.

In this article I will describe how to use RESTEasy interceptors to log incoming requests into a REST API.

I am currently using RESTEasy to expose HealthCare web services for consumption by mobiles applications, and there is a need to log transactions for auditing purpose, including for HIPAA compliance.

The first step is to specify in your web.xml file, that you are going to use a new interceptor  - (e.g. named LogInterceptor):
<web-app>
   ...  
   <context-param>
      <param-name>resteasy.providers</param-name>
      <param-value>com.acme.api.rest.service.LogInterceptor</param-value>
   </context-param>
   ...
</web-app>


Then you create the corresponding java class for LogInterceptor:
package com.acme.api.rest.service;
  
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.WebApplicationException;

 
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
import org.jboss.resteasy.spi.Failure;

...
 
@Provider
@ServerInterceptor
public class LogInterceptor implements PreProcessInterceptor {
 
 // An handler to the Transaction log
 private static final Logger translog = Logger.getLogger(JNDIMessageLoggingHandler.class);

 // Getting the request as a context
 @Context HttpServletRequest request;

 @Override
 public ServerResponse preProcess(HttpRequest arg0, ResourceMethod method) throws Failure, WebApplicationException {
  final TransactionLogEntry transactionLogEntry = new TransactionLogEntry(Long.toString(Thread.currentThread().getId()),
                         System.currentTimeMillis(), 
                         getClientIP(request), 
                         request.getRequestURL().toString());
      translog.info(transactionLogEntry);
 }  
 
}

In this example, the TransactionLogEntry constructor takes :
  • a transaction ID
  • a timestamp
  • the client that initiated the request  (e.g. request.getRemoteAddr()+":"+request.getRemotePort())
  • the request URL
but you can also log additional information such as the body content of POST requests (storing XML or JSON content).

Thursday, March 31, 2011

JUnit based integration testing with Simple JNDI



I regularly use TJWS  a light weight Java Web Server build as a servlet container to provide standard Web Server capabilities at building and testing time.

The main advantage is that I don't have to deploy my war file to my production server (e.g. JBoss) to test my Java Web application. My maven build runs complex integration JUnit tests immediately after building the war file with maven.

In my configuration however, I have to use JNDI (Java Naming and Directory Interface) to connect to a specific datasource (DB2 via JDBC) for some of  my tests.

I used Simple JNDI for this which also offer an easy and elegant way to configure data source access.

Here are the steps I follow to setup my JUnit tests using Simple JNDI:

    • Add dependencies in Maven 2 Pom file for Simple JNDI, java persistence and DB2 JNDI/JDBC:
        
        <dependency>
            <groupId>simple-jndi</groupId>
            <artifactId>simple-jndi</artifactId>
            <version>0.11.4.1</version>
            <scope>test</scope>
        </dependency>
    
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0</version>
            <scope>test</scope>
        </dependency>
      
        <dependency>
            <groupId>com.ibm.db2</groupId>
            <artifactId>db2jcc4</artifactId>
            <version>9.7.0.2</version>
            <scope>test</scope>
        </dependency>
        

    • Create a small Java class for the JNDI setup
         import javax.naming.InitialContext;
         import javax.sql.DataSource;
    
         public class JndiSetup { 
             /**
              * Setup the Data Source
              */
             public static void doSetup(String ds_name) {
                 try {
                     InitialContext ctxt = new InitialContext();
                     DataSource ds = (DataSource) ctxt.lookup("jdbc."+ds_name);
                     // rebind for alias if needed
                     ctxt.rebind("jdbc/"+ds_name, ds);
                 } catch (Exception ex) {
                     ex.printStackTrace();
                 }
             }
         }

    In more complex situations, you may have also to create an EntityManager and use an EntityManagerFactory.

    • In the JUnit java script code, setup your JNDI connection before running your tests:
        @BeforeClass
         public static void setUpClass() throws Exception {
              JndiSetup.doSetup("");
         }


    • Create a jndi.properties file in the "\src\test\resources" path in your project 
     
         java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
         org.osjava.sj.root=target/test-classes/config
         org.osjava.jndi.delimiter=/
         org.osjava.sj.jndi.shared=true

    • Create a jdbc.properties file in the "\src\test\resources\config" path in your project (I am using an IBM DB2 data source). Create the config directory if it doesn't exist. The "config" name comes from org.osjava.sj.root parameter in jndi.properties file. If you want a different name, "foo", for the folder, make sure to update the "org.osjava.sj.root" property and create a "foo" folder in "\src\test\resources" path. Make sure the directory /src/test/resources is in the Java build path, and the output folder is set to target/test-classes
    
         <your-ds>.type=javax.sql.DataSource
         <your-ds>.driver=com.ibm.db2.jcc.DB2Driver
         <your-ds>.url=jdbc:db2://<your-database-host>:50000/<your-ds-or-ds-alias>
         <your-ds>.user=<your-db-login>
         <your-ds>.password=<your-db-password>

    With all of this you should be ready to test your integration using >mvn clean install.

    You may have also to do a >mvn eclipse:eclipse -DdownloadJavadocs=true as well if you are using Eclipse and your new dependencies and imports do not work properly.

    Friday, September 10, 2010

    Spring Dependency Injection with JBOSS : the CLASSPATH issue

    When facing the problem of deploying web archives (war) to be configured through Spring dependency injection, you probably want to have generic applications that do not have to be recompile every time you deploy them on new configurations.

    In my current project I need to configure a REST API with various parameters (host name, database paths, maximum of records per request). For this I use Spring dependency injection where the parameters are injected at run-time via a resource file located outside the war file, in a folder specified by the Windows CLASSPATH variable (my testing and production platforms are windows machine).

    First I need to add a windows CLASSPATH system variable (in your system properties/environment variables)  if this variable does not exist. Then I add the resources.xml file directly in the folder specified by CLASSPATH. You can also use a sub-folder but you will need to hard-code the name of the folder in your spring config file - in my case applicationContext.xml located in ./src/main/webapp/WEB-INF/

    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
        <import resource="classpath:/resources.xml" />
    </beans>
    

    My resources.xml looks like this:

    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
      <bean id="custService" 
                          scope="prototype"
                          class=".....">
       <property name="hostName" value="121.122.123.124"/>
       <property name="databasePath" value="..."/>
       <property name="maxRecordsPerRequest" value="1000"/>
      </bean>
    </beans>
    

    One issue you might encounter when you try to deploy your application on JBoss is that the web application server does not take into account the CLASSPATH out-of-the-box (I am using redhat EAP 5.0.X - production setting), but this might be also the case with JBOSS community edition.

    Your war file will probably fail to deploy and you will find a bunch of errors in your log file ./jboss-as/server/<setting>/log/server.log  including:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: 
    Failed to import bean definitions from URL location [classpath:/resources.xml]
    Offending resource: ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException:
    IOException parsing XML document from class path resource [resources.xml]; 
    nested exception is java.io.FileNotFoundException: 
    class path resource [resources.xml] cannot be opened because it does not exist
    

    What is missing is that you need to tell JBoss about your CLASSPATH variable.
    Just edit ./jboss-as/bin/run.bat and add the CLASSPATH variable and you will be up and running in no time.

    :RESTART
    "%JAVA%" %JAVA_OPTS% ^
       -Djava.endorsed.dirs="%JBOSS_ENDORSED_DIRS%" ^
       -classpath "%JBOSS_CLASSPATH%;%CLASSPATH%" ^
       org.jboss.Main -b 0.0.0.0 -c production %*
    

    Tuesday, August 24, 2010

    RESTeasy JAX-RS embeddable server and SpringBeanProcessor

    TJWS (Tiny Java Web Server and Servlet Container) is a very convenient miniature Java Web Server build as a servlet container with HTTPD servlet providing standard Web server functionality.

    I have been using TJWS for testing a REST API to be deployed on JBoss application server. The advantage is that JUnit tests can run without the need to deploy a war file on JBoss. Since I have implemented the REST API with RESTEasy, I am using the embedded TJWS server part of the org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer package.

    The RESTEasy documentation (chapter 23) describes how to use the embedded container.

    @Path("/")public class MyResource {
    
       @GET
       public String get() { return "hello world"; }
     
       public static void main(String[] args) throws Exception 
       {
          TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();
          tjws.setPort(8081);
          tjws.getRegistry().addPerRequestResource(MyResource.class);
          tjws.start();
       }
    }

    As you can see, TJWS is very simple to use. You create an instance of the server, setup the port (this is very useful when for example certain ports are already used - I had to set a specific port for our Hudson continuous builds). Then you specify the class to test and you start the server.

    In my JUnit tests, I start the server before each tests and stop it after the tests are completed:

    private TJWSEmbeddedJaxrsServer server; 
    
    @Before
        public void start() {
          
         server = new TJWSEmbeddedJaxrsServer();
         server.setPort(SERVER_PORT);
         server.getDeployment().getActualResourceClasses().add(MyResource.class);
         server.start();
        }
    
    @After
        public void stop() {
         server.stop();
        }

    Since I am using Spring I was interested to leverage the framework for dependency injection in order to configure certain server settings. However the RESTeasy documentation provides only some pseudo-code example:

    public static void main(String[] args) throws Exception 
       {
          final TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();
          tjws.setPort(8081);
    
          org.resteasy.plugins.server.servlet.SpringBeanProcessor processor = new SpringBeanProcessor(tjws.getRegistry(), tjws.getFactory();
          ConfigurableBeanFactory factory = new XmlBeanFactory(...);
          factory.addBeanPostProcessor(processor);
    
          tjws.start();
       }
    

    I had to make some modifications to the code provided as follow:

    @Before
        public void start() {
          
         server = new TJWSEmbeddedJaxrsServer();
         server.setPort(SERVER_PORT);
         server.getDeployment().getActualResourceClasses().add(MyResource.class);
         server.start();
         
         Resource resource = new FileSystemResource("src/test/resources/resources.xml");
         ConfigurableListableBeanFactory factory = new XmlBeanFactory(resource);
         SpringBeanProcessor processor = new SpringBeanProcessor(
                 server.getDeployment().getDispatcher(),
                 server.getDeployment().getRegistry(), 
                 server.getDeployment().getProviderFactory());
         processor.postProcessBeanFactory(factory);
        }
    

    Alternatively you can define your Spring resource file in a static string directly in your JUnit test class:

    Resource resource = new ByteArrayResource(SPRING_BEAN_CONFIG_FILE.getBytes());

    Wednesday, July 28, 2010

    How to secure the JBoss JMX and Web Consoles?

    Lately I have been using JBoss more and more as my deployment platform of choice. I am currently using the latest JBoss Enterprise Middleware solution (EAP version 5.0.1). This is a commercial version, but you can also use the community edition as well which offers most of the same features.

    One of the issue I have encountered recently was how to secure the web based administration consoles?

    As a matter of fact, the default installation offers a login and password for the admin console which is typically accessible on http://localhost:8080/admin-console if your web application runs locally, or more generally on http://<host>:<port>/admin-console.
    However if you want to protect the JMX console (http://localhost:8080/jmx-console) and the JBoss Web console (http://localhost:8080/web-console) you have to make sure that certain files in your installation are setup correctly.


    Generally, the JBoss community and RedHat are quite good at documenting the features of their products, but I was disappointed to find incomplete information in the main page on this subject.

    This page explains that "the jmx-console and web-console are standard servlet 2.3 deployments that can
    be secured using J2EE role based security.  Both also have a skeleton setup to allow one to easily enable security using username/password/role mappings found in the jmx-console.war and web-console.war deployments in the corresponding WEB-INF/classes users.properties and roles.properties files".

    Until this point, it is quite clear. The difficulty starts with a vague description where to find the files in questions :

    To secure the JMX Console using a username/password file -
    • Locate the  directory.  This will normally be in  directory..
    The author probably assumes that the various locations are obvious to everyone. Let me be more precise and generous in details:


    First you will need to know which profile/configuration you are running. JBoss EAP has six configurations based on your needs:
    • all (everything, including clustering support and other enterprise extensions)
    • default (for application developers)
    • minimal (the strict minimum)
    • production (everything but optimized for production environments)
    • standard (tested for Java EE compliance)
    • web (experimental lightweight configuration) 

      If you did not specify your profile explicitly at starting time (run.sh -c )you most likely use the default profile.





      You can check which profile is running by looking at your JBoss EAP Admin Console. The name of the profile/configuration is indicated at the top of the server hierarchy.

      By the way, the various applications that you will likely developed (war, ear, rar or jar files) will be deployed under the corresponding folders under the Applications node.

      First you might want to change the login and password of the admin console itself before adding those for the JMX and Web consoles?


      One important "meta" file is login-config.xml which is located under \jboss-as\server\ 

       





      This file specify for a specific profile (e.g. default) the security-domain values for the consoles:

      <application-policy name = "jmx-console">
          <authentication>
            <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
              flag="required">
              <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
              <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
            </login-module>
          </authentication>
        </application-policy> 

      <application-policy name = "web-console">
          <authentication>
            <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
              flag="required">
              <module-option name="usersProperties">web-console-users.properties</module-option>
              <module-option name="rolesProperties">web-console-roles.properties</module-option>
            </login-module>
          </authentication>
        </application-policy>

      In other words, this file can help you locate and map how authentication (login and password in users.properties file) and authorization (access control in roles.properties file) is specified.

      Since these consoles are themselves web applications, you will need to look at exploded war files under your profile, under the deploy folder.

      Securing the JMX Console:

      • Locate the folder jmx-console.war under ./server/<config>/deploy
      • Open the file ./server/<config>/deploy/mx-console.war/WEB-INF/web.xml
      • Verify that the <security-constraint> section is not commented (in this section, you should see specified  the roles for authorization (see below)
      <security-constraint>
           <web-resource-collection>
             <web-resource-name>HtmlAdaptor</web-resource-name>
             <description>An example security config that only allows users with the
               role JBossAdmin to access the HTML JMX console web application
             </description>
             <url-pattern>/*</url-pattern>
           </web-resource-collection>
           <auth-constraint>
             <role-name>JBossAdmin</role-name>
           </auth-constraint>
         </security-constraint>
      • Locate the file: .\server\<config>\conf\props\jmx-console-users.properties (if the file name has not been changed in login-config.xml
      • change admin=admin to your new <new_login>=<new_password>
      The authentication method is specified in the following section:
       <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>JBoss JMX Console</realm-name>
         </login-config>
      
         <security-role>
            <role-name>JBossAdmin</role-name>
         </security-role>

      Securing the Web Console:
      • Login credentials are the same as used for the JMX console - in : .\server\<config>\conf\props\jmx-console-users.properties 
      • change admin=admin to your new <new_login>=<new_password> 

      For more information and this topic you can also look at Securing the JMX Console and Web Console (HTTP).

      Tuesday, June 1, 2010

      JAXB-JSON Rest API using RESTEasy on JBoss EAP

      In this second part of my evaluation of JBoss RESTEasy, I focus on adapting the JAXB-JSON samples provided by RESTEasy for JBoss Enterprise Application Platform 5.0.1.

      Earlier I find myself to adapt the maven POM file to have the proper dependencies for the Twitter RESTEasy client.

      Initially this simple JAXB-JSON sample had been designed to run on Jetty Web Server which run fine out-of-the box. However I had to make some modifications to the original project structure to have the code running as a simple eclipse project that can be deploy on JBoss EAP 5.0.X from RedHat (this will also work on Jboss community edition).

      The new project (eclipse) structure looks as below:





      Notice that I have also moved the code for both packages:  org.jboss.resteasy.annotations.providers.jaxb.json
      org.jboss.resteasy.plugins.providers.jaxb.json
      at the root of my project, since I am not using the remaining code of the example.

      I also made some additional adaptations for JBoss to some of the project files including:
      • pom.xml file
      • web.xml
      I also added a small test suite to test the REST API operations using JUnit which will work after the first deployment (I run JBoss locally on http://localhost:8080).

      By the way, make sure you have src/main/resources/META-INF/services/javax.ws.rs.ext.Providers included in your project.









      Here is the content of my new pom.xml file for JBoss:

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <groupId>org.jboss.resteasy.examples</groupId>
          <artifactId>jaxb-json</artifactId>
          <version>0.1.0</version>
          <packaging>war</packaging>
          <name/>
          <description/>
      
          <repositories>
              <repository>
                  <id>java.net</id>
                  <url>http://download.java.net/maven/1</url>
                  <layout>legacy</layout>
              </repository>
              <repository>
                  <id>maven repo</id>
                  <name>maven repo</name>
                  <url>http://repo1.maven.org/maven2/</url>
              </repository>
              <!-- For resteasy -->
              <repository>
                  <id>jboss</id>
                  <name>jboss repo</name>
                  <url>http://repository.jboss.org/maven2</url>
              </repository>
          </repositories>
          <dependencies>
          
              <!-- core library -->
              
              <dependency>
                  <groupId>org.jboss.resteasy</groupId>
                  <artifactId>resteasy-jaxrs</artifactId>
                  <version>1.2.1.GA</version>
                  <!-- filter out unwanted jars -->
                  <exclusions>
                      <exclusion>
                          <groupId>commons-httpclient</groupId>
                          <artifactId>commons-httpclient</artifactId>
                      </exclusion>
                      <exclusion>
                          <groupId>tjws</groupId>
                          <artifactId>webserver</artifactId>
                      </exclusion>
                      <exclusion>
                          <groupId>javax.servlet</groupId>
                          <artifactId>servlet-api</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
              
              <!-- optional modules -->
              
            <dependency>
                  <groupId>org.jboss.resteasy</groupId>
                  <artifactId>resteasy-jettison-provider</artifactId>
                  <version>1.2.1.GA</version>
              </dependency>
              
              <!-- modules already provided by Java 6.0 -->
               
         <dependency>
             <groupId>javax.xml.bind</groupId>  
             <artifactId>jaxb-api</artifactId>
             <version>2.1</version>
             <scope>provided</scope>
         </dependency>
         
          <dependency>
               <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.1</version>
                       <scope>test</scope>
                   </dependency>
           
          </dependencies>
      
          <build>
              <finalName>jaxb-json</finalName>
              <plugins>
                  <plugin>
                      <groupId>org.codehaus.mojo</groupId>
                      <artifactId>jboss-maven-plugin</artifactId>
                      <version>1.4</version>
                      <configuration>
                          <jbossHome>C:\JBoss\EnterprisePlatform-5.0.0.GA\jboss-as</jbossHome>
                   <contextPath>/</contextPath>
                   <serverName>default</serverName>
                   <fileName>target/jaxb-json.war</fileName>
                </configuration>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <configuration>
                          <source>1.5</source>
                          <target>1.5</target>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
      
      

      I modified the web.xml for the URL looks more simple by removing the mapping to reasteasy:

      <?xml version="1.0"?>
      <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
              "http://java.sun.com/dtd/web-app_2_3.dtd">
      
      <web-app>
         <display-name>Archetype Created Web Application</display-name>
      
         <context-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>org.jboss.resteasy.examples.service.LibraryApplication</param-value>
         </context-param>
      
         <context-param>
            <param-name>resteasy.servlet.mapping.prefix</param-name>
            <param-value>/</param-value>
         </context-param>
      
         <listener>
            <listener-class>
               org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
            </listener-class>
         </listener>
      
         <servlet>
            <servlet-name>Resteasy</servlet-name>
            <servlet-class>
               org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
            </servlet-class>
         </servlet>
      
         <servlet-mapping>
            <servlet-name>Resteasy</servlet-name>
            <url-pattern>/</url-pattern>
         </servlet-mapping>
      
      </web-app>
      

      The JUnit code looks like this:

      package org.jboss.resteasy.examples.test;
       
      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      import java.net.HttpURLConnection;
      import java.net.URL;
      
      import junit.framework.Assert;
      import junit.framework.Test;
      import junit.framework.TestCase;
      import junit.framework.TestSuite;
        
      public class LibraryTest extends TestCase
      {
          /**
           * Create the test case
           *
           * @param testName name of the test case
           */
          public LibraryTest( String testName )
          {
              super( testName );
          }
      
          /**
           * @return the suite of tests being tested
           */
          public static Test suite()
          {
              return new TestSuite( LibraryTest.class );
          }
      
            
          /**
           * Testing the Library REST API
           */
          
          public void testGetMapped()  
          {
           validateRESTCall("GET", "http://localhost:8080/jaxb-json/library/books/mapped");
           assertTrue( true );
          }
          
      
          public void testGetBadger()  
          {
           validateRESTCall("GET", "http://localhost:8080/jaxb-json/library/books/badger");
           assertTrue( true );
          }
          
          private void validateRESTCall(String method, String url) {
           
           try {
               System.out.println("*** "+method);
               URL resURL = new URL(url);
               System.out.println("URL: " + url.toString());
               HttpURLConnection connection = (HttpURLConnection) resURL.openConnection(); 
               connection.setRequestMethod(method);
               System.out.println("Content-Type: " + connection.getContentType());
               
               BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
       
               String line = reader.readLine();
               while (line != null)
               {
                  System.out.println(line);
                  line = reader.readLine();
               }
               Assert.assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
               connection.disconnect();
           } catch (Exception err) { 
               System.out.print("Error in VHRResourceTest.validateRESTCall : " + err); 
              };
          }
      }
      

      To build I am using Maven (I recommend to install the maven eclipse plugin) with the goals mvn clean install compile package. Make sure also that before that you do a mvn eclipse:eclipse to update the dependencies in your project.

      To deploy jaxb-json.war file from eclipse (so I don't have to manually copy the war file from the target folder), I have installed the JBoss eclipse plugin. As a result I can make it deployable (accessible by a right-click) and it appear in the Eclipse JBoss server view:


      The REST API JSON Library resources are then accessible directly on a browser via http://localhost:8080/jaxb-json/library/books/mapped or http://localhost:8080/jaxb-json/library/books/badger.

      If you want to compress your response, RESTEasy provides GZIP Compression/Decompression support using a very simple @GZIP annotation:

         @GET
         @Path("books/mapped")
         @Produces("application/json")
         @GZIP
         public BookListing getBooksMapped()
         {
            return getListing();
         }

      Just import the following class:

      import org.jboss.resteasy.annotations.GZIP;

      Overall the adaptation from Jetty to JBoss was easy and the documentation very clear.

      Additional discussions, recommendations and information can be found on the JBoss Community.

      For an example of using REST architecture for Mobile Applications (HealthCare) see this post.

      Tuesday, May 18, 2010

      Enhanced POM for JBoss RESTEasy Twitter API Client Sample

      I recently looked at JBOSS RESTEasy as a way to create and test RESTful APIs. The platform looks very promising with a lot of praise from developers. Also the documentation seems very extensive and precise.

      I started by downloading RESTEasy 1.2.1 GA and tried the sample code. I started with a java client to access existing RESTful Web Services and APIs. Among the api-clients, there is a Twitter small client that works out-of-the box (located under /RESTEASY_1_2_1_GA/examples/api-clients/src/main/java/org/jboss/resteasy/examples/twitter).

      However when I started to extract the code and wanted to create a Maven 2 based stand-alone project, I encountered some issues related to JAR dependency conflicts, including the following error message also described here.

      java.lang.NoClassDefFoundError: Could not initialize class com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl 
      

      The project (eclipse) structure looks as below:


















      I managed to fix these issues by modifying the POM file as follow:

      <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
          <modelVersion>4.0.0</modelVersion>
       
          <groupId>org.jboss.resteasy.examples</groupId>
       <artifactId>api-clients</artifactId>
       <version>1.2.1.GA</version>
        
        <dependencies>
          <!-- Resteasy Core -->
          <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
          </dependency>
          <!-- JAXB support -->
         <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxb-provider</artifactId>
         </dependency>
          
        </dependencies>
        <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.jboss.resteasy</groupId>
                      <artifactId>resteasy-bom</artifactId>
                      <version>1.2.1.GA</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
              </dependencies>
         </dependencyManagement>
         
         <!-- Build Settings --> 
         <build>
          <plugins>  
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <configuration>
                <source>1.6</source>
                <target>1.6</target>
              </configuration>
            </plugin>
          </plugins>
         </build>
        
        <!-- Environment Settings -->
        <repositories>
          <repository>
            <id>jboss</id>
            <name>jboss repo</name>
            <url>http://repository.jboss.org/maven2</url>
           </repository>
         </repositories>
        
      </project>
      

      The most important piece, beside the cleaning of the POM file, was to include a pom that can be imported so the versions of the individual modules do not have to be specified (see RESTEasy documentation - Chapter 43. Maven and RESTEasy).

      I also made sure to have correct dependencies for resteasy-jaxrs and resteasy-jaxb-provider.

      As a result, I was able to compile the whole project without any errors (mvn clean compile) and run it to access the Twitter REST API

      mvn exec:java -Dexec.mainClass="org.jboss.resteasy.examples.twitter.TwitterClient" -Dexec.args="<userid> <password>"
      (Replace last parameters by your twitter user and password).

      The small client in question leverages JAX-RS annotations to read and write the Twitter API resources:

      package org.jboss.resteasy.examples.twitter;
      
      import java.util.Date;
      import java.util.List;
      
      import javax.ws.rs.FormParam;
      import javax.ws.rs.GET;
      import javax.ws.rs.POST;
      import javax.ws.rs.Path;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlRootElement;
      import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
      
      import org.apache.commons.httpclient.Credentials;
      import org.apache.commons.httpclient.HttpClient;
      import org.apache.commons.httpclient.UsernamePasswordCredentials;
      import org.apache.commons.httpclient.auth.AuthScope;
      import org.jboss.resteasy.client.ProxyFactory;
      import org.jboss.resteasy.client.ClientExecutor;
      import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
      import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
      import org.jboss.resteasy.spi.ResteasyProviderFactory;
      
      public class TwitterClient
      {
         static final String friendTimeline = "http://twitter.com/statuses/friends_timeline.xml";
      
         public static void main(String[] args) throws Exception
         {
            RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
            final ClientExecutor clientExecutor = new ApacheHttpClientExecutor(createClient(args[0], args[1]));
            TwitterResource twitter = ProxyFactory.create(TwitterResource.class,
                  "http://twitter.com", clientExecutor);
            System.out.println("===> first run");
            printStatuses(twitter.getFriendsTimelines());
            
            twitter
            .updateStatus("I programmatically tweeted with the RESTEasy Client at "
                  + new Date());
            
            System.out.println("===> second run");
            printStatuses(twitter.getFriendsTimelines());
         }
      
         public static interface TwitterResource
         {
            @Path("/statuses/friends_timeline.xml")
            @GET
            Statuses getFriendsTimelines();
      
            @Path("/statuses/update.xml")
            @POST
            Status updateStatus(@FormParam("status") String status);
         }
      
         private static void printStatuses(Statuses statuses)
         {
            for (Status status : statuses.status)
               System.out.println(status);
         }
      
         private static HttpClient createClient(String userId, String password)
         {
            Credentials credentials = new UsernamePasswordCredentials(userId,
                  password);
            HttpClient httpClient = new HttpClient();
            httpClient.getState().setCredentials(AuthScope.ANY, credentials);
            httpClient.getParams().setAuthenticationPreemptive(true);
            return httpClient;
         }
      
         @XmlRootElement
         public static class Statuses
         {
            public List<Status> status;
         }
      
         @XmlRootElement
         public static class Status
         {
            public String text;
            public User user;
      
            @XmlElement(name = "created_at")
            @XmlJavaTypeAdapter(value = DateAdapter.class)
            public Date created;
      
            public String toString()
            {
               return String.format("== %s: %s (%s)", user.name, text, created);
            }
         }
      
         public static class User
         {
            public String name;
         }
      
      }
      
      

      The small DateAdapter class is a utility class for date formatting:

      package org.jboss.resteasy.examples.twitter;
      
      import java.util.Date;
      import java.util.List;
      package org.jboss.resteasy.examples.twitter;
       
      import java.util.Date;
      import javax.xml.bind.annotation.adapters.XmlAdapter;
      import org.jboss.resteasy.util.DateUtil;
      
      public class DateAdapter extends XmlAdapter<String, Date> {
      
         @Override
         public String marshal(Date date) throws Exception {
             return DateUtil.formatDate(date, "EEE MMM dd HH:mm:ss Z yyyy");
         }
      
         @Override
         public Date unmarshal(String string) throws Exception {
             try {
                 return DateUtil.parseDate(string);
             } catch (IllegalArgumentException e) {
                 System.err.println(String.format(
                         "Could not parse date string '%s'", string));
                 return null;
             }
         }
      }