http://www.javatips.net/blog/2014/06/cxf-webservice-using-embedded-jetty
@BeforeClass
public static void
setUp() throws
Exception {
server = new
Server();
Connector connector = new SelectChannelConnector();
connector.setPort(9000);
server.setConnectors(new
Connector[] { connector
});
WebAppContext wactx = new WebAppContext();
wactx.setContextPath("/CXFRestfulTutorial");
wactx.setWar("CXFRestfulTutorial.war");
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new
Handler[] { wactx,
new DefaultHandler() });
server.setHandler(handlers);
try {
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
==========
Going REST: embedding Jetty with Spring and JAX-RS (Apache CXF)
http://www.javacodegeeks.com/2013/01/going-rest-embedding-jetty-with-spring-and-jax-rs-apache-cxf.html
For hardcore server-side Java developer the only way to ‘speak’ out to the world is by using APIs. Today’s post is all about
JAX-RS: writing and exposing
RESTful services using Java.
But we won’t do that using a traditional, heavyweight approach involving application server, WAR packaging and whatnot. Instead, we will use awesome
Apache CXF framework and as always rely on
Spring to wire all pieces together. And for sure we won’t stop on that either as we need a web server to run our services on. Using
fat or one jar concept we will embed
Jetty server into our application and make our final JAR redistributable (all dependencies included) and runnable.
It’s a lot of work so let’s get started. As we stated above, we will use
Apache CXF,
Spring and
Jetty as a building blocks so let’s have them described in a POM file. The one additional dependency worth mentioning is excellent
Jackson library for JSON processing.
002 |
< modelversion >4.0.0</ modelversion > |
004 |
< groupid >com.example</ groupid > |
005 |
< artifactid >spring-one-jar</ artifactid > |
006 |
< version >0.0.1-SNAPSHOT</ version > |
007 |
< packaging >jar</ packaging > |
010 |
< project.build.sourceencoding >UTF-8</ project.build.sourceencoding > |
011 |
< org.apache.cxf.version >2.7.2</ org.apache.cxf.version > |
012 |
< org.springframework.version >3.2.0.RELEASE</ org.springframework.version > |
013 |
< org.eclipse.jetty.version >8.1.8.v20121106</ org.eclipse.jetty.version > |
018 |
< groupid >org.apache.cxf</ groupid > |
019 |
< artifactid >cxf-rt-frontend-jaxrs</ artifactid > |
020 |
< version >${org.apache.cxf.version}</ version > |
024 |
< groupid >javax.inject</ groupid > |
025 |
< artifactid >javax.inject</ artifactid > |
030 |
< groupid >org.codehaus.jackson</ groupid > |
031 |
< artifactid >jackson-jaxrs</ artifactid > |
032 |
< version >1.9.11</ version > |
036 |
< groupid >org.codehaus.jackson</ groupid > |
037 |
< artifactid >jackson-mapper-asl</ artifactid > |
038 |
< version >1.9.11</ version > |
042 |
< groupid >cglib</ groupid > |
043 |
< artifactid >cglib-nodep</ artifactid > |
044 |
< version >2.2</ version > |
048 |
< groupid >org.springframework</ groupid > |
049 |
< artifactid >spring-core</ artifactid > |
050 |
< version >${org.springframework.version}</ version > |
054 |
< groupid >org.springframework</ groupid > |
055 |
< artifactid >spring-context</ artifactid > |
056 |
< version >${org.springframework.version}</ version > |
060 |
< groupid >org.springframework</ groupid > |
061 |
< artifactid >spring-web</ artifactid > |
062 |
< version >${org.springframework.version}</ version > |
066 |
< groupid >org.eclipse.jetty</ groupid > |
067 |
< artifactid >jetty-server</ artifactid > |
068 |
< version >${org.eclipse.jetty.version}</ version > |
072 |
< groupid >org.eclipse.jetty</ groupid > |
073 |
< artifactid >jetty-webapp</ artifactid > |
074 |
< version >${org.eclipse.jetty.version</ version > |
081 |
< groupid >org.apache.maven.plugins</ groupid > |
082 |
< artifactid >maven-compiler-plugin</ artifactid > |
083 |
< version >3.0</ version > |
090 |
< groupid >org.apache.maven.plugins</ groupid > |
091 |
< artifactid >maven-jar-plugin</ artifactid > |
095 |
< mainclass >com.example.Starter</ mainclass > |
101 |
< groupid >org.dstovall</ groupid > |
102 |
< artifactid >onejar-maven-plugin</ artifactid > |
103 |
< version >1.4.4</ version > |
107 |
< onejarversion >0.97</ onejarversion > |
108 |
< classifier >onejar</ classifier > |
121 |
< id >onejar-maven-plugin.googlecode.com</ id > |
124 |
</ pluginrepositories > |
128 |
< id >maven2-repository.dev.java.net</ id > |
It’s a lot of stuff but should be pretty clear. Now, we are ready to develop our first
JAX-RS services by starting with simple
JAX-RS application.
1 |
package
com.example.rs; |
3 |
import
javax.ws.rs.ApplicationPath; |
4 |
import
javax.ws.rs.core.Application; |
6 |
@ApplicationPath (
'api' ) |
7 |
public
class JaxRsApiApplication extends
Application { |
As simple as it looks like, our application defines an /api to be the entry path for the
JAX-RS services. The sample service will manage people represented by
Person class.
01 |
package
com.example.model; |
05 |
private
String firstName; |
06 |
private
String lastName; |
11 |
public
Person( final
String email ) { |
15 |
public
String getEmail() { |
19 |
public
void setEmail(
final String email ) { |
23 |
public
String getFirstName() { |
27 |
public
String getLastName() { |
31 |
public
void setFirstName(
final String firstName ) { |
32 |
this .firstName = firstName; |
35 |
public
void setLastName(
final String lastName ) { |
36 |
this .lastName = lastName; |
And following bare bones business service (for simplicity, no database or any other storage are included).
01 |
package
com.example.services; |
03 |
import
java.util.ArrayList; |
04 |
import
java.util.Collection; |
06 |
import
org.springframework.stereotype.Service; |
08 |
import
com.example.model.Person; |
11 |
public
class PeopleService { |
12 |
public
Collection< Person > getPeople( int
page, int
pageSize ) { |
13 |
Collection< Person > persons =
new ArrayList< Person >( pageSize ); |
15 |
for (
int index =
0 ; index < pageSize; ++index ) { |
16 |
persons.add(
new Person( String.format(
'person+%[email protected]' , ( pageSize * ( page -
1 ) + index +
1 ) ) ) ); |
22 |
public
Person addPerson( String email ) { |
23 |
return
new Person( email ); |
As you can see, we will generate a list of persons on the fly depending on the page requested. Standard
Spring annotation @Service marks this class as a service bean. Our
JAX-RS service PeopleRestService will use it for retrieving persons as the following code demonstrates.
01 |
package
com.example.rs; |
03 |
import
java.util.Collection; |
05 |
import
javax.inject.Inject; |
06 |
import
javax.ws.rs.DefaultValue; |
07 |
import
javax.ws.rs.FormParam; |
08 |
import
javax.ws.rs.GET; |
09 |
import
javax.ws.rs.PUT; |
10 |
import
javax.ws.rs.Path; |
11 |
import
javax.ws.rs.Produces; |
12 |
import
javax.ws.rs.QueryParam; |
14 |
import
com.example.model.Person; |
15 |
import
com.example.services.PeopleService; |
18 |
public
class PeopleRestService { |
19 |
@Inject
private PeopleService peopleService; |
21 |
@Produces ( {
'application/json'
} ) |
23 |
public
Collection< Person > getPeople( @QueryParam (
'page' ) @DefaultValue (
'1' ) final
int page ) { |
24 |
return
peopleService.getPeople( page, 5
); |
27 |
@Produces ( {
'application/json'
} ) |
29 |
public
Person addPerson( @FormParam (
'email' )
final String email ) { |
30 |
return
peopleService.addPerson( email ); |
Though simple, this class needs more explanations. First of all, we want to expose our
RESTful service to /people endpoint. Combining it with
/api (where our
JAX-RS application resides), it gives as the
/api/people as qualified path.
Now, whenever someone issues
HTTP GET to this path, the method
getPeople should be invoked. This method accepts optional parameter
page (with default value 1) and returns list of persons as JSON. In turn, if someone issues
HTTP PUT to the same path, the method
addPerson should be invoked (with required parameter email) and return new person as a
JSON.
Now let’s take a look on
Spring configuration, the core of our application.
01 |
package
com.example.config; |
03 |
import
java.util.Arrays; |
05 |
import
javax.ws.rs.ext.RuntimeDelegate; |
07 |
import
org.apache.cxf.bus.spring.SpringBus; |
08 |
import
org.apache.cxf.endpoint.Server; |
09 |
import
org.apache.cxf.jaxrs.JAXRSServerFactoryBean; |
10 |
import
org.codehaus.jackson.jaxrs.JacksonJsonProvider; |
11 |
import
org.springframework.context.annotation.Bean; |
12 |
import
org.springframework.context.annotation.Configuration; |
14 |
import
com.example.rs.JaxRsApiApplication; |
15 |
import
com.example.rs.PeopleRestService; |
16 |
import
com.example.services.PeopleService; |
19 |
public
class AppConfig { |
20 |
@Bean ( destroyMethod =
'shutdown' ) |
21 |
public
SpringBus cxf() { |
22 |
return
new SpringBus(); |
26 |
public
Server jaxRsServer() { |
27 |
JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean. class
); |
28 |
factory.setServiceBeans( Arrays.< Object >asList( peopleRestService() ) ); |
29 |
factory.setAddress(
'/' + factory.getAddress() ); |
30 |
factory.setProviders( Arrays.< Object >asList( jsonProvider() ) ); |
31 |
return
factory.create(); |
35 |
public
JaxRsApiApplication jaxRsApiApplication() { |
36 |
return
new JaxRsApiApplication(); |
40 |
public
PeopleRestService peopleRestService() { |
41 |
return
new PeopleRestService(); |
45 |
public
PeopleService peopleService() { |
46 |
return
new PeopleService(); |
50 |
public
JacksonJsonProvider jsonProvider() { |
51 |
return
new JacksonJsonProvider(); |
It doesn’t look complicated but a lot happens under the hood. Let’s dissect it into the peices. The two key component here are the factory
JAXRSServerFactoryBean which does all heavy lifting for configuring our instance of
JAX-RS server, and SpringBus instance which seamlessly glues
Spring and
Apache CXF together. All other components represent regular
Spring beans.
What’s not on a picture yet is embedding
Jetty web server instance. Our main application class
Starter does exactly that.
03 |
import
org.apache.cxf.transport.servlet.CXFServlet; |
04 |
import
org.eclipse.jetty.server.Server; |
05 |
import
org.eclipse.jetty.servlet.ServletContextHandler; |
06 |
import
org.eclipse.jetty.servlet.ServletHolder; |
07 |
import
org.springframework.web.context.ContextLoaderListener; |
08 |
import
org.springframework.web.context.support.AnnotationConfigWebApplicationContext; |
10 |
import
com.example.config.AppConfig; |
12 |
public
class Starter { |
13 |
public
static void
main( final String[] args )
throws Exception { |
14 |
Server server =
new Server(
8080 ); |
17 |
final
ServletHolder servletHolder = new
ServletHolder( new
CXFServlet() ); |
18 |
final
ServletContextHandler context = new
ServletContextHandler(); |
19 |
context.setContextPath(
'/' ); |
20 |
context.addServlet( servletHolder,
'/rest/*' );
|
21 |
context.addEventListener(
new ContextLoaderListener() ); |
23 |
context.setInitParameter(
'contextClass' , AnnotationConfigWebApplicationContext. class .getName() ); |
24 |
context.setInitParameter(
'contextConfigLocation' , AppConfig. class .getName() ); |
26 |
server.setHandler( context ); |
Looking through this code uncovers that we are running
Jetty server instance on port 8080, we are configuring
Apache CXF servlet to handle all request at
/rest/* path (which together with our
JAX-RS application and service gives us the
/rest/api/people), we are adding
Spring context listener parametrized with the configuration we have defined above and finally we are starting server up. What we have at this point is full-blown web server hosting our
JAX-RS services. Let’s see it in action. Firstly, let’s package it as single, runnable and redistributable
fat or one jar:
Let’s pick up the bits from the target folder and run them:
1 |
java -jar target/spring-one-jar- 0.0 . 1 -SNAPSHOT.one-jar.jar |
And we should see the output like that:
01 |
2013 - 01 - 19
11 : 43 : 08.636 :INFO:oejs.Server:jetty- 8.1 . 8 .v20121106 |
02 |
2013 - 01 - 19
11 : 43 : 08.698 :INFO:/:Initializing Spring root WebApplicationContext |
03 |
Jan 19 ,
2013 11 : 43 : 08
AM org.springframework.web.context.ContextLoader initWebApplicationContext |
04 |
INFO: Root WebApplicationContext: initialization started |
05 |
Jan 19 ,
2013 11 : 43 : 08
AM org.springframework.context.support.AbstractApplicationContext prepareRefresh |
06 |
INFO: Refreshing Root WebApplicationContext: startup date [Sat Jan
19 11 : 43 : 08
EST 2013 ]; root of context hierarchy |
07 |
Jan 19 ,
2013 11 : 43 : 08
AM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters |
08 |
INFO: JSR- 330
'javax.inject.Named' annotation found and supported
for component scanning |
09 |
Jan 19 ,
2013 11 : 43 : 08
AM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions |
10 |
INFO: Successfully resolved class
for [com.example.config.AppConfig] |
11 |
Jan 19 ,
2013 11 : 43 : 09
AM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
12 |
INFO: JSR- 330
'javax.inject.Inject' annotation found and supported
for autowiring |
13 |
Jan 19 ,
2013 11 : 43 : 09
AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons |
14 |
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory @1f8166e5 :
|
15 |
defining beans [org.springframework.context.annotation.internal |
16 |
ConfigurationAnnotationProcessor, |
17 |
org.springframework.context.annotation.internalAutowiredAnnotationProcessor, |
18 |
org.springframework.context.annotation.internalRequiredAnnotationProces |
19 |
sor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig, |
20 |
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,c |
21 |
xf,jaxRsServer,jaxRsApiApplication,peopleRestService,peopleService,jsonProvider]; root of factory hierarchy |
22 |
Jan 19 ,
2013 11 : 43 : 10
AM org.apache.cxf.endpoint.ServerImpl initDestination |
23 |
INFO: Setting the server's publish address to be /api |
24 |
Jan 19 ,
2013 11 : 43 : 10
AM org.springframework.web.context.ContextLoader initWebApplicationContext |
25 |
INFO: Root WebApplicationContext: initialization completed in
2227 ms |
26 |
2013 - 01 - 19
11 : 43 : 10.957 :INFO:oejsh.ContextHandler:started o.e.j.s.ServletContextHandler{/, null } |
27 |
2013 - 01 - 19
11 : 43 : 11.019 :INFO:oejs.AbstractConnector:Started SelectChannelConnector @0 .0. 0.0 : 8080 |
Having our server up and running, let’s issue some HTTP requests to it so to be sure everything works just as we expected:
Awesome! And please notice, we are completely XML-free! Source code:
https://github.com/reta/spring-one-jar/tree/jetty-embedded
Before ending the post, I would like to mention one great project,
Dropwizard, which uses quite similar concepts but pushes it to the level of excellent, well-designed framework, thanks to
Yammer guys for that.
Reference: Going REST: embedding Jetty with Spring
and JAX-RS (Apache CXF) from our
JCG partner Andrey Redko at the
Andriy Redko {devmind} blog.