Sunday, January 26, 2020

CXF REST API with JPA Hibernate in Karaf on JBoss FUSE 7.4


In this post I will demonstrate how to configure CXF REST API in JPA implementation Hibernate on Karaf platform on JBoss fuse 7.4. I will use previous JPA project as code base to start the development. You can refer about it from my previous blog post.

Prerequisites 
  1. You should have install java 1.8 or above.
  2. You should have IDE installed in your PC in my case I'm using IntelliJ IDEA.
  3. Your PC should setup Maven installed and configured.
  4. Your  PC should install and setup SQLServer.
  5.  Checkout base project from JPA project as code base.


Application module Dependency 

First Lets add relevant dependencies in to Application POM file. Following is the full pom file content and updated part on bold font. Since we are using CXF REST we need to add relevant dependency for that.


<?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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>FuseHibernate</artifactId>
        <groupId>com.fuse.hibernate.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Application</artifactId>
    <name>OSGi Application</name>
    <packaging>bundle</packaging>
    <dependencies>
        <dependency>
            <groupId>com.fuse.hibernate.example</groupId>
            <artifactId>Service</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.karaf.shell</groupId>
            <artifactId>org.apache.karaf.shell.core</artifactId>
        </dependency>

        <!--        CXF related dependencies -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-client</artifactId>
            <version>3.0.4.redhat-621084</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.karaf.tooling</groupId>
                <artifactId>karaf-services-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <configuration>
                    <instructions>
                        <Private-Package>com.fuse.hibernate.example.app</Private-Package>
                        <Import-Package>com.fuse.hibernate.example.model,org.hibernate.jpa, org.apache.karaf.shell*;version="[4,5)", *</Import-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Defining the REST API END POINT 

Then lets add our rest API end point. We just need add one class which we can define relevant methods we need to expose. For this example I will add GET,PUT,POST and DELETE methods in to our REST end point.
I will add "RestEndPoint " class in com.fuse.hibernate.example.app package with following content.



package com.fuse.hibernate.example.app;

import com.fuse.hibernate.example.model.Person;
import com.fuse.hibernate.example.service.PersonServiceImpl;
import org.apache.karaf.shell.api.action.lifecycle.Reference;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.math.BigInteger;

@Service
@Path("/personservice/")
public class RestEndPoint {

    @Reference
    private PersonServiceImpl personService;
    static Logger LOG = LoggerFactory.getLogger(ExchangeProcessor.class);

    @GET
    @Path("/persons/{id}/")
    @Produces("application/xml")
    public Person getPerson(@PathParam("id") String id) {
        LOG.info("----invoking getPerson, Person name is: " + id);
        Person c = personService.findPerson(BigInteger.valueOf(Long.valueOf(id)));
        return c;
    }

    @PUT
    @Path("/persons/")
    public Response updatePerson(Person person) {
        LOG.info("----invoking updatePerson, Person name is: " + person.getName());
        Person c = personService.findPerson(person.getId());
        Response r;
        if (c != null) {
            personService.updateCustomer(person);
            r = Response.ok().build();
        } else {
            r = Response.notModified().build();
        }

        return r;
    }

    @POST
    @Path("/persons/")
    public Response addPerson(Person person) {
        LOG.info("----invoking addPerson, Person name is: " + person.getName());

        Person saved=personService.createPerson(person);

        return Response.ok().type("application/xml").entity(saved).build();
    }

    @DELETE
    @Path("/persons/{id}/")
    @Produces("application/xml")
    public Response deletePerson(@PathParam("id") String id) {
        LOG.info("----invoking getPerson, Person name is: " + id);
        Person c = personService.findPerson(BigInteger.valueOf(Long.valueOf(id)));
        Response r;
        if (c != null) {
            personService.removePersonById(c.getId());
            r = Response.ok().build();
        } else {
            r = Response.notModified().build();
        }
        return r;
    }

    public PersonServiceImpl getPersonService() {
        return personService;
    }

    public void setPersonService(PersonServiceImpl personService) {
        this.personService = personService;
    }

}



Defining the REST END POINT in BluePrint  

Then lets add created rest endpoint class in to our BlurPrint.xml file in the Application module. We just need to modify following content which colored in bold.


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:jpa="http://aries.apache.org/xmlns/jpa/v2.0.0" xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.2.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
           xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
             http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd
             http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">
  <jpa:enable />
  <tx:enable-annotations />
  <service ref="personService" interface="com.fuse.hibernate.example.service.PersonService" />
  <bean id="personService" class="com.fuse.hibernate.example.service.PersonServiceImpl" />
  <bean
          class="com.fuse.hibernate.example.app.ExchangeProcessor" id="exchangeProcessor">
    <property name="personService" ref="personService"/>
  </bean>

  <camelContext xmlns="http://camel.apache.org/schema/blueprint"
                id="cbr-example-context" >
    <route id="cbr-route" >
      <from uri="file:work/cbr/input" />
      <log message="Sending order ${file:name} to another country" />
      <log id="logStatusIncident" message="OrderDetails Call ${body}"/>
      <to uri="file:work/cbr/output/others" />
      <convertBodyTo type="java.lang.String"/>
      <process ref="exchangeProcessor"/>
      <log  message="${body}" />

    </route>
  </camelContext>

  <jaxrs:server id="personsService" address="/crm">
    <jaxrs:serviceBeans>
      <ref component-id="personSvc"/>
    </jaxrs:serviceBeans>
  </jaxrs:server>

  <bean id="personSvc" class="com.fuse.hibernate.example.app.RestEndPoint">
    <property name="personService" ref="personService"/>
  </bean>
</blueprint>

Now its ready to deploy application in to FUSE server.

Installing in to FUSE 7.4

We can use same set of commands except one new dependency addition code. In order to add dependencies first we have to run the fuse.bat file and then go to the console and enter following commands one by one. Only the third command new from previous post.


osgi:install -s mvn:com.microsoft.sqlserver/mssql-jdbc/7.4.1.jre8

osgi:install -s mvn:org.ops4j.pax.jdbc/pax-jdbc-mssql/1.3.5

osgi:install -s mvn:org.apache.cxf/cxf-rt-rs-client/3.0.4.redhat-621084



Then lets install application by entering following commands one by one


feature:repo-add mvn:com.fuse.hibernate.example/Feature/1.0-SNAPSHOT/xml



feature:install osgi-customer-management-datasource

feature:install Model

feature:install Service

feature:install Application


Then after "list" command you should be able to see all the things we installed. Similar to below figure.

After Successful installation LIST command should return something similar to this.

You can verify by accessing http://localhost:8181/cxf/ link you should be able to see following UI in browser.

http://localhost:8181/cxf/ output once successfully installed the Application 

Now you can click the WADL link and see the defined endpoints in there.

WADL output for defined REST service.


Test the functionality

We can test the functionality by executing following commands in command prompt.



To Create new person

curl -X POST -T C:/Users/Person3.xml -H "Content-Type: text/xml" http://localhost:8181/cxf/crm/personservice/persons

You should get similar result as below figure and you can see Person table which inserted new value.
Command Line output of POST command

Database Person table after executing above command




To Get existing person

curl -X GET http://localhost:8181/cxf/crm/personservice/persons/1

Output of GET command


To Update existing person

curl -X PUT -T C:/Users/Person4.xml -H "Content-Type: text/xml" http://localhost:8181/cxf/crm/personservice/persons

For this command you will not get any visible output but then if you run previous GET command you could see the Person details has been updated. As show on below figure.

Updated user details

To Delete existing person

curl -X DELETE http://localhost:8181/cxf/crm/personservice/persons/1

For this command also you will not get any visible output on Command Line but after this if you run GET command then you could see you will not get any data. As show on below figure. Also you can check on database and see that Person already deleted from the database.

Deletion command output on Command Line


You can download the entire source code from following GIT hub URL




2 comments:

  1. Hi Nirmal,

    Thanks for the share all these steps, I checkout your code and updated the database details and try to install the application in fuse. Unfortunately I'm getting following error. Could you please help me on this ?


    EventDispatcher: Error during dispatch. (javax.persistence.PersistenceException: [PersistenceUnit: persons] Unable to build Hibernate SessionFactory)
    javax.persistence.PersistenceException: [PersistenceUnit: persons] Unable to build Hibernate SessionFactory
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1016) ~[?:?]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:942) ~[?:?]
    at org.hibernate.osgi.OsgiPersistenceProvider.createContainerEntityManagerFactory(OsgiPersistenceProvider.java:96) ~[?:?]
    at org.apache.aries.jpa.container.impl.AriesEntityManagerFactoryBuilder.createAndPublishEMF(AriesEntityManagerFactoryBuilder.java:371) ~[?:?]
    .
    .
    .
    Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The TCP/IP connection to the host CMP-061219DN16SQLEXPRESS01, port 1433 has failed. Error: "CMP-061219DN16SQLEXPRESS01. Verify the connection properties. Make sure that an instance of SQL Server is running on the host and accepting TCP/IP connections at the port. Make sure that TCP connections to the port are not blocked by a firewall.".
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:234) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerException.ConvertConnectExceptionToSQLServerException(SQLServerException.java:285) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SocketFinder.findSocket(IOBuffer.java:2431) ~[?:?]
    at com.microsoft.sqlserver.jdbc.TDSChannel.open(IOBuffer.java:656) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:2472) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:2142) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1993) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1164) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerDataSource.getConnectionInternal(SQLServerDataSource.java:1108) ~[?:?]
    at com.microsoft.sqlserver.jdbc.SQLServerDataSource.getConnection(SQLServerDataSource.java:85) ~[?:?]
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[?:?]
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180) ~[?:?]
    at org.hibernate.resource.transaction.backend.jta.internal.DdlTransactionIsolatorJtaImpl.(DdlTransactionIsolatorJtaImpl.java:59) ~[?:?]
    at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl.buildDdlTransactionIsolator(JtaTransactionCoordinatorBuilderImpl.java:46) ~[?:?]
    at org.hibernate.tool.schema.internal.HibernateSchemaManagementTool.getDdlTransactionIsolator(HibernateSchemaManagementTool.java:175) ~[?:?]
    at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:94) ~[?:?]
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183) ~[?:?]
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72) ~[?:?]
    at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:310) ~[?:?]
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467) ~[?:?]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939) ~[?:?]
    ... 53 more

    ReplyDelete
  2. Is "CMP-061219DN16" your correct host name? Make sure you correctly mentioned it in your "feature.xml" file.
    Refer to following URL https://kb.sos-berlin.com/pages/viewpage.action?pageId=17499564 for more details.
    Also check following things
    01) make sure your DB allow to authenticate by using username and passwords.
    02) Configure correctly on SQL Server Configuration Manager




    ReplyDelete