#

Thursday, February 9, 2012

Junit with Jetty & RESTful CXF

Recently I fitted my JUnits, to work with a self starting & dying Jetty embedded server. So this tutorial will show you how to Hook in your CXF WebService Unit with a self sustaining Jetty Server as a bonus ;)

The context of our example is a bit of code I am currently working on. A generic Query service that can query any entity based on custom annotations that supplement JPA. The Domain & JPA part is out of scope, so I'm not going to define the core domain model here but focus on the Unit Test & related Springs configs.

Before I get into the code, I'd like to futher illustrate the folder structure of the project so that you know where our Self instantiated Jetty server can pick things from; and adapt this to your requirements. The project I am working on is packaged as a JAR and has no WAR structure to begin with so it makes for an interesting use case to begin with.

Project Structure (Standard Maven with a test/webapp to support our cause)


1. Setup a Server

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A Self Server to Start & stop
*
* Code help taken from:

* How to start and stop Jetty – revisited
*

* Run Jetty Web Server Within Your Application
*
* @author Arjun Dhar
*/
public class SelfServer {
private static Logger log = LoggerFactory.getLogger(SelfServer.class);

private Server server;

private int port = 9091;
private int stopPort = 9092;
private String contextPath = "/testContext";
private String webPath = "src/test/webapp/WEB-INF";
private String host = "127.0.0.1";

public SelfServer() {}
public SelfServer(String host, int port, String contextPath, String webPath) {
this.host = host;
this.port = port;
this.contextPath = contextPath;
this.webPath = webPath;
}

/**
* This is a Blocking call and will wait till the server is Started
* @throws Exception
*/
public void start() throws Exception {
Thread t = new Thread() {
public void run() {
server = new Server();
SocketConnector connector = new SocketConnector();
connector.setPort(port);
server.setConnectors(new Connector[] { connector });

WebAppContext context = new WebAppContext();
context.setServer(server);
context.setContextPath(contextPath);
//context.setWar(warFilePath);

//Note: Set WAR assumes all resources etc in place like genuine WAR,
//in our case resources scattered across so use the following instead:
context.setResourceBase("src/main/resources");
context.setDescriptor(webPath + "/web.xml");
server.addHandler(context);

Thread monitor = new MonitorThread(host, stopPort, server);
monitor.start();

try {
server.start();
server.join();
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
};

t.setDaemon(true);
t.start();

while(server==null || !server.isStarted()) {/* Block till started */}

log.info("[start] Started Server @ " + host + ":" + port );
log.info("[start] Server Ready & Running - " + server.isRunning());
}

public void stop() throws Exception {
Socket s = new Socket(InetAddress.getByName(host), stopPort);
OutputStream out = s.getOutputStream();
log.info("[stop] sending jetty stop request @ " + host + ":" + stopPort );
out.write(("\r\n").getBytes());
out.flush();
s.close();

if (server!=null && server.isStarted()) {
server.stop();
}
}


private static final class MonitorThread extends Thread {

private ServerSocket socket;
private Server server;
private int stopPort;
private String host;

public MonitorThread(String host, int stopPort, Server server) {
this.server = server;
this.stopPort = stopPort;
this.host = host;

setDaemon(true);
setName("StopMonitor");
try {
socket = new ServerSocket(stopPort, 1, InetAddress.getByName(host));
} catch(Exception e) {
throw new RuntimeException(e);
}
}

@Override
public void run() {
log.info("[run] Running Jetty Stop Thread");
Socket accept;
try {
accept = socket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
reader.readLine();
log.info("[run] Stopping embedded Jetty Server");
server.stop();
accept.close();
socket.close();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}

}



2. Create the JUnit Test Case

This test case, assumes a Domain model (irrelevant to our conversation), and a Web Service that has a simple test method getTestString(), that returns a String. (text/plain) type.


One important thing to note is that the Service being used directly as proxy here.

In amore complicated scenarios, where parameters need to be marshaled and more
explicit Header type information is required, then you need to refer to
CXF JAX-RS Client API


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import junit.framework.Assert;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.neurosys.selfserver.SelfServer;

/**
* Test CXF REST services
*
* @author Arjun Dhar
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:com/neurosys/amorphous/applicationContext.xml",
"classpath:com/neurosys/amorphous/rest-test-client.xml"
})
public class RESTServicesTest {
private static Logger log = LoggerFactory.getLogger(RESTServicesTest.class);

@Autowired
@Qualifier("testclient")
protected GenericObjectQueryService proxy;

@Before
public void init() throws Exception {
/* please note, in actuality, for multiple tests you will have
to ensure a single version of the server is running only.

For each test case, it will invoke start and will give an error.
This is simplified for Blog consumption here only. */
SelfServer server = new SelfServer();
server.start();
}

@Test
public void testConnect() throws Exception {
/* Note: Service being used directly as proxy here.
In more complicated scenarios, where parameters need to be marshalled and more
explicit Header type information is required, then you need to refer to
http://cxf.apache.org/docs/jax-rs-client-api.html
*/
Response response = proxy.getTestString();
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());

BufferedReader r = new BufferedReader(new InputStreamReader(((ByteArrayInputStream)response.getEntity())));
StringBuilder sb = new StringBuilder();
while(r.ready()) {
sb.append(r.readLine());
}
log.info("[testConnect] Result = " + sb.toString());
Assert.assertEquals(GenericObjectQueryService.test, sb.toString());
}

}


3. Next you need to ensure your Spring config for your CXF is up



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">







































4. Define the CXF Test Client Proxy

You would have noticed in the test case the inclusion of rest-test-client.xml. Additionally, we are using a property that should be injected via your Spring applicationContenxt.xml.

Variable : ${webservice.address.genericobjectservice} = genericobject-query in this example.


xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context = "http://www.springframework.org/schema/context"
xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">


serviceClass="com.neurosys.amorphous.service.jaxrs.GenericObjectQueryService">









...and thats it!

- Arjun Dhar
My Company NeuroSys

Wednesday, February 1, 2012

Expresso Shots!


  • org.apache.commons.beanutils.BeanUtils are nice but they return almost everything as a String which is annoying.

    To get the value of a field use this or a variant of it: BeanUtilsBean2.getInstance().getPropertyUtils().getSimpleProperty();


  • c3Po Configurations for shared hosted environments


    Connection Pooling on Production Infrastructure


    On a shared hosted 3rd party environment, sometimes Connections Pooling can be a big mess to deal with. One more often than not ends up with dropped connections (inspite of reconnect=true). Connections that are in the Pool but no longer honoured by the database itself, which results in such situations also among others. The issues with DBCP are worse than c3po, but they exist everywhere. Worse, in a shared hosted environment you do not have access to the Database as root and you don't have access to anything but whats in your application.

    Exceptions


    Some of the exceptions are of the following nature:

    Caused by: org.hibernate.TransactionException: JDBC begin failed:
    ...
    Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
    ...
    INFO [com.mchange.v2.c3p0.impl.NewPooledConnection] [c3p0] Exceptions occurred while trying to close a PooledConnection's resources normally.
    INFO [com.mchange.v2.c3p0.impl.NewPooledConnection] [c3p0] NewPooledConnection close Exception.
    com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.


    Solution
    I wrote a script that automated the process of testing various combination's (certain key params), and then I fine tuned it manually. It was heuristic, and there may be other combination's but this really works well and fast. It is reliable and has proven good on several projects of that nature. I have my theory on why it works, but because the nature of determination was heuristic, I don't think its worth delving into.





























    true
    false

    ${db.credential.username}
    ${db.credential.password}