Skip to content

REST + SOAP for cxf.apache @ appfuse

SCREENCAST GETTING THIS DONE / video tutorial



i am still working on it, but this works so far:
i am going to install appfuse with a new entity “Person”.
The Method findByLastName is accessible through REST WS.

for more operations on cxf restfull services see JAX-RS (JSR-311)
this doc is explaining everythig For example: retrieving Collections needs an extra wrapper class.
there is still an error while listing the services: CXF-1695

<pre>mvn archetype:create -DarchetypeGroupId=org.appfuse.archetypes
-DarchetypeArtifactId=appfuse-basic-spring
-DremoteRepositories=http://static.appfuse.org/releases
-DarchetypeVersion=2.0.2 -DgroupId=org.appfuse.tutorial -DartifactId=tutorial
cd tutorial
mvn appfuse:full-source
edit pom.xml for database login (jdbc)
mvn install eclipse:eclipse

edit the pom.xml file:

update the spring version to 2.5.6 below <!– Framework dependency versions –>

delete the dependency with the artifactId ‘xfire-java5′ and ‘xfire-spring’

insert the cxf dependencies:

<dependency>
	<groupId>org.apache.cxf</groupId>
  	<artifactId>cxf-api</artifactId>
  	<version>2.1.3</version>
</dependency>
<dependency>
  	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>2.1.3</version>
        <exclusions>
              <!-- http://www.jroller.com/melix/entry/apache_cxf_maven_javamail_awful -->
              <exclusion>
                   <groupId>org.apache.geronimo.specs</groupId>
                   <artifactId>geronimo-javamail_1.4_spec</artifactId>
              </exclusion>
        </exclusions>
</dependency>
<dependency>
  	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxrs</artifactId>
	<version>2.1.3</version>
</dependency>
<dependency>
    <groupId>javax.xml</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.1.3</version> </dependency> <dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-core</artifactId>
      <version>2.1.3</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
  <artifactId>jsr311-api</artifactId>
  <version>0.8</version><!--!!!!!!!!!!!!ACTUAL VERSION IS 1.0!!!!!!!!!!!!(missing methods and classes ;( will be fixed in the next cxf version 2.1.4) -->
</dependency>
<dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-databinding-aegis</artifactId>
 <version>2.1.3</version>
</dependency>

exclude asm and the cglib from the hibernate dependency

<exclusion>
	<groupId>asm</groupId>
	<artifactId>asm</artifactId>
</exclusion>
<exclusion>
	<groupId>asm</groupId>
	<artifactId>asm-attrs</artifactId>
</exclusion>
<exclusion>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
</exclusion>

insert the asm-all and cglib dependencies to the top level <dependencies> above the cxf dependencies:

<dependency>
    <groupId>asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>3.1</version>
</dependency>
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

insert the spring-core and spring-web dependency:

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>${spring.version}</version>
</dependency>
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
</dependency>

edit the web.xml file and change /WEB-INF/xfire-servlet.xml in the <param-value> of the <!– Context Configuration locations for Spring XML files to /WEB-INF/cxf-servlet.xml

also replace the servlet named xfire with

<servlet>
      <servlet-name>CXFServlet</servlet-name>
      <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
</servlet>

and replace the xfire servlet mapping with

<servlet-mapping>
      <servlet-name>CXFServlet</servlet-name>
      <url-pattern>/services/*</url-pattern>
</servlet-mapping>

delete WEB-INF/xfire-servlet.xml and create a new file there called cxf-servlet.xml which has this content:

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xmlns:jaxrs="http://cxf.apache.org/jaxrs"
        xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
 
        <import resource="classpath:META-INF/cxf/cxf.xml" />
        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
        <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
        <!-- #errorManager points to my manager(with the @webservice annotation)
             defined in my applicationContext.xml -->
        <jaxws:endpoint
          id="personServiceSOAP"
          implementor="#personManager"
          address="/PersonServiceSOAP" >
       </jaxws:endpoint>
 
       <jaxrs:server id="personService1" address="/PersonService">
            <jaxrs:serviceBeans>
              <ref bean="personManager" />
            </jaxrs:serviceBeans>
          </jaxrs:server>
 
</beans>

add Person.java Model:

package org.appfuse.tutorial.model;
import javax.persistence.Column;
  import javax.persistence.Entity;
  import javax.persistence.GeneratedValue;
  import javax.persistence.GenerationType;
  import javax.persistence.Id;
  import javax.persistence.Table;
  import javax.xml.bind.annotation.XmlRootElement;
 
  @Entity @Table(name="person")
  @XmlRootElement(name = "Person")
  public class Person extends BaseObject {
  private Long id;
  private String firstName;
  private String lastName;
 
 @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  public Long getId() {
  return id;
  }
 public void setId(Long id) {
  this.id = id;
  }
 @Column(name=("first_name"), length=50)
  public String getFirstName() {
  return firstName;
  }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
  }
 @Column(name=("last_name"), length=50)
  public String getLastName() {
  return lastName;
  }
 public void setLastName(String lastName) {
  this.lastName = lastName;
  }
 
 public boolean equals(Object o) {
  if (this == o) return true;
  if (o == null || getClass() != o.getClass()) return false;
 Person person = (Person) o;
 if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false;
  if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false;
 return true;
  }
 public int hashCode() {
  int result;
  result = (firstName != null ? firstName.hashCode() : 0);
  result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
  return result;
  }
 public String toString() {
  return "Person{" +
  "id=" + id +
  ", firstName='" + firstName + ''' +
  ", lastName='" + lastName + ''' +
  '}';
  }
  }

execute mvn appfuse:gen -Dentity=Person

add the PersonDao:

package org.appfuse.tutorial.dao;
import java.util.List;
import org.appfuse.tutorial.model.Person;
/**
  * @author mraible
  *
  public interface PersonDao extends GenericDao&lt;Person, Long&gt; {
  public List&lt;Person&gt; findByLastName(String lastName);
  }

add personDaoHibernate:

package org.appfuse.tutorial.dao.hibernate;
import java.util.List;
import org.appfuse.tutorial.dao.PersonDao;
  import org.appfuse.tutorial.model.Person;
public class PersonDaoHibernate extends GenericDaoHibernate&lt;Person, Long&gt; implements PersonDao {
 public PersonDaoHibernate() {
  super(Person.class);
  }
 public List&lt;Person&gt; findByLastName(String lastName) {
  return getHibernateTemplate().find("from Person where lastName=?", lastName);
  }
  }

add the PersonManager interface and class:

package org.appfuse.tutorial.service;
import java.util.List;
import javax.jws.WebService;
import org.appfuse.tutorial.model.Person;
@WebService
  public interface PersonManager extends GenericManager&lt;Person, Long&gt; {
  public Person findByLastName(String lastName);
}
package org.appfuse.tutorial.service.impl;
import java.util.List;
import javax.jws.WebService;
  import javax.ws.rs.GET;
  import javax.ws.rs.Path;
  import javax.ws.rs.PathParam;
import org.appfuse.tutorial.dao.PersonDao;
  import org.appfuse.tutorial.model.Person;
  import org.appfuse.tutorial.service.PersonManager;
@WebService(serviceName = "PersonService", endpointInterface = "org.appfuse.tutorial.service.PersonManager")
  @Path("/personservice/")
  public class PersonManagerImpl extends GenericManagerImpl&lt;Person, Long&gt; implements PersonManager {
  PersonDao personDao;
 public PersonManagerImpl(PersonDao personDao) {
  super(personDao);
  this.personDao = personDao;
  }
  @GET
  @Path("/person/{lastName}")
  public List&lt;Person&gt; findByLastName(@PathParam("lastName") String lastName) {
  return personDao.findByLastName(lastName);
  }
  }

add the Dao to applicationContext-Dao.xml and the Manager to dispatcher-servlet.xml and delete the genericManager which was created automatically:

&lt;bean id="personDao" class="org.appfuse.tutorial.dao.hibernate.PersonDaoHibernate"&gt;
  	&lt;property name="sessionFactory" ref="sessionFactory"/&gt;
&lt;/bean&gt;
&lt;bean id="personManager" class="org.appfuse.tutorial.service.impl.PersonManagerImpl"&gt;
  	&lt;constructor-arg ref="personDao"/&gt;
&lt;/bean&gt;

register your webservice at cxf-servlet.xml, i have not figured out how to avaid registering every endpoint:

add jsr311-api-0.8.jar to the eclipse project classpath.

execute:

mvn jetty:run

to see if it “compiles”

try it by calling: http://127.0.0.1:8080/services/PersonService/personservice/person/somelastNameValueFromTheDatabase

in Return you should get this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person>
   <firstName>sdf</firstName>
   <id>-3</id>
   <lastName>somelastNameValueFromTheDatabase</lastName>
</Person>

SCREENCAST GETTING THIS DONE / video tutorial

Categories: Uncategorized.

Tags: , , , , , , , , ,

Comment Feed

7 Responses

  1. Thanks for working on this!

    A comment and a question:
    1) I was trying this out and it seem that the default aspectj version in appfuse (1.6.0) is incompatible with spring 2.5.6. (This doesn’t seem to prevent anything from working.. but leads to a ajcore file complaining about aspect incompatibility between aspect j and the spring files.) Updating the aspect j versions to 1.6.2 from 1.6.0 seems to fix the issue.

    2) Could you post your entire pom.xml? I’m not sure what you mean from a “top level dependency” versus the other snippets which get added to the pom.xml, and I think I may have put something in the wrong place: My “mvn compile” is clean, but when i run “mvn jetty:run” I’m getting an error when Spring tries to create the TransactionInterceptor with the error: cannot resolve reference to bean ‘sessionFactory’ while setting bean property ‘sessionFactory’ The 1/6/2009 blog entry had someone mentioning something similar in the comments, but they just said it was a bad pom.xml and didn’t elaborate on the issue.

    Thanks!

    Full (first error) is below:
    RROR [main] ContextLoader.initWebApplicationContext(215) | Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.transaction.interceptor.TransactionInterceptor#0′: Cannot resolve reference to bean ‘transactionManager’ while setting bean property ‘transactionManager’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘transactionManager’ defined in class path resource [applicationContext-dao.xml]: Cannot resolve reference to bean ‘sessionFactory’ while setting bean property ‘sessionFactory’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [applicationContext-dao.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.(I)V
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104)

  2. Well I seem to have resolved my problem with “sessionFactory” thing up above. I had to put the dependencies for asm and cglib immediately after:

    which is probably what you meant by top level.

    I swear I had tried that… but I think I had a subsequent error on something, and was looking too far up in my console. (forgot to clear.) Anyways… mystery solved.

    A few subsequent notes:
    [WARNING] While downloading javax.xml:jaxb-api:2.1
    This artifact has been relocated to javax.xml.bind:jaxb-api:2.1.

    can be fixed by updating the dependency to include the “.bind”

  3. Ok.. two other issues that I’m having trying to get this working. I’m just curious if you happened to see them in the course of your development:

    1) Following issue on Jetty startup.
    INFO: Setting the server’s publish address to be /PersonServiceSOAP
    Feb 15, 2009 2:22:58 PM org.apache.cxf.jaxrs.JAXRSServiceFactoryBean checkMethodDispatcher
    WARNING: No resource methods found for resource class org.apache.cxf.jaxrs.model.ClassResourceInfo
    ERROR [main] ContextLoader.initWebApplicationContext(215) | Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘personService1′: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
    PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property ‘serviceBeans’ threw exception; nested exception is java.lang.NullPointerException

    (I did ensure that personManager is defined in the applicationContext-services.xml)

    2) I somehow got by the above problem in my own coding attempt… but now only the top level /services will resolve.. trying to actually call a real service always says it can’t find the request for ‘s observer.

    console snippet:
    Feb 15, 2009 2:58:17 PM org.apache.cxf.transport.servlet.ServletController invoke
    WARNING: Can’t find the request for http://127.0.0.1:8080/services/CreditService/‘s Observer
    Feb 15, 2009 2:58:29 PM org.apache.cxf.transport.servlet.ServletController invoke
    WARNING: Can’t find the request for http://127.0.0.1:8080/services/CreditService/creditservice/creditDetail/1‘s Observer
    Feb 15, 2009 2:58:35 PM org.apache.cxf.transport.servlet.ServletController invoke
    WARNING: Can’t find the request for http://127.0.0.1:8080/services/CreditService/creditservice/findCreditDetail/1‘s Observer
    Feb 15, 2009 2:58:43 PM org.apache.cxf.transport.servlet.ServletController invoke
    WARNING: Can’t find the request for http://127.0.0.1:8080/services/CreditService/findCreditDetail/1‘s Observer

    Any thoughts? Ideas where to start debugging?
    Thanks! (i’ll stop the marathon posts now… PROMISE!)

  4. Hello!
    Very Interesting post! Thank you for such interesting resource!
    PS: Sorry for my bad english, I’v just started to learn this language ;)
    See you!
    Your, Raiul Baztepo

    RaiulBaztepoApril 1, 2009 @ 11:59Reply
  5. Hey,

    First of all, thanks for the tutorial and video. They were a HUGE help. With regards to the service listing issue, if you upgrade cxf to 2.2.4 and the javax.ws.rs version to 1.1 it works fine. Thanks again for your help!

    Michael Minella



Some HTML is OK

or, reply to this post via trackback.

Continuing the Discussion