Preferred Semantics for CXF JAX-RS - java

I was wondering what the preferred semantics are for jaxrs:server configurations in a CXF XML context file.
For example, if I have two service implementations for users and orders, and they're accessible from a relative path "/user" and "/order".
Would I configure the services this way:
<jaxrs:server id="userService" address="/user">
<jaxrs:serviceBeans>
<bean class="com.example.UserServiceImpl />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
<jaxrs:server id="orderService" address="/order">
<jaxrs:serviceBeans>
<bean class="com.example.OrderServiceImpl />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
Or this way:
<jaxrs:server id="appService" address="/">
<jaxrs:serviceBeans>
<!--
Path configured using #Path annotations on the class definition:
#Path(value="/user")
public class UserServiceImpl {...}
-->
<bean class="com.example.UserServiceImpl />
<bean class="com.example.OrderServiceImpl />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
It seems like it's only a semantic difference. The second way allows us to not repeat the providers. But I was wondering what I should be considering when performing this configuration?
Thank you!

I use the second way and try to group services together if they relate... if you can get orders for specific user, then they relate. So I usually have one "v1" api server (versioning support), one for documentation of it (there I use different providers or extension mappings), one for specialized (like admin with more strict security) access, etc.
But i would use some address and not leave it empty, for example "api" or version "v1" at least.
In other way, your cxf.xml can be full of jaxrs servers. And if they relate, there is little chance that they will need different providers, mappings, extensions.
But this question is about opinion and perhaps will be closed.

I would usually group them if they have same base address
lets say we have resource URLs like this
<jaxrs:server id="userService" address="api/user">
</jaxrs:server>
<jaxrs:server id="orderService" address="api/order">
</jaxrs:server>
as
<jaxrs:server id="appService" address="/api">
<jaxrs:serviceBeans>
<!--
#Path(value="/user")
-->
<bean class="com.example.UserServiceImpl />
<bean class="com.example.OrderServiceImpl />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
and individually incase they have different Base URLs
like
/profile/user
/cart/orders

Related

Expose spring integration amqp/jms channel messages metrics to prometheus

I am trying to enable metrics to feed message statistics from a spring-integration RabbitMq <=> MqSeries gateway into Prometheus.
The documentation states that the channel must extends AbstractMessageChannel in order the metrics to apply. Since I am quite uncomfortable with namespaces, I am not sure if this is the case here.
Also, I don't understand how to add a MeterRegistryBean (and which one!) in order to trigger the metrics on.
And eventually, I don't understand how to use it.
Basically, since I am new to most of the framework implied here, the documentation here lacks an example that could help me understand it a little better.
Here's how I do my channel definition (the "xml" way is not a choice : It is already implemented this way and I cannot change it ftm) :
<!-- *** MQseries *** -->
<!-- ========================== -->
<bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQConnectionFactory" >
<property...>
</bean>
<bean id="jmsConnectionFactory_cred"
class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="jmsConnectionFactory" />
<property...>
</bean>
<bean id="connectionFactoryCaching" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnectionFactory_cred" />
<property...>
</bean>
<bean id="jmsQueue" class="com.ibm.mq.jms.MQQueue" depends-on="jmsConnectionFactory">
<property...>
</bean>
<bean id="fixedBackOff" class="org.springframework.util.backoff.FixedBackOff">
<property...>
</bean>
<bean id="myListener" class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
<property...>
<property name="connectionFactory" ref="connectionFactoryCaching" />
<property name="destination" ref="jmsQueue" />
</bean>
<int-jms:message-driven-channel-adapter id="jmsIn" container="myListener" channel="channelMQ_RMQ" error-channel="processChannel1"/>
<!-- *** Rabbit *** -->
<!-- ====================== -->
<bean id="connectionAmqpFactoryDest" class="com.rabbitmq.client.ConnectionFactory">
<property...>
</bean>
<!-- Attribute : addresses = List of addresses; e.g. host1,host2:4567,host3 - overrides host/port if supplied. -->
<rabbit:connection-factory id="rabbitConnectionFactory"
connection-factory="connectionAmqpFactoryDest"
addresses="..." ... />
<bean id="simpleMessageConverter" class="org.springframework.amqp.support.converter.SimpleMessageConverter">
<property...>
</bean>
<rabbit:template id="rabbitTemplate"
connection-factory="rabbitConnectionFactory"
mandatory="true"
channel-transacted="true"
message-converter="simpleMessageConverter"/>
<int-amqp:outbound-channel-adapter channel="channelMQ_RMQ"
...
amqp-template="rabbitTemplate" />
Any idea on how I can do that?
Spring Integration metrics (as well as Spring JMS and Spring AMQP) are fully based on the Micrometer implementation: https://docs.spring.io/spring-integration/docs/current/reference/html/system-management.html#micrometer-integration. That all is good if you use some of the latest, supported Spring Integration version: https://spring.io/projects/spring-integration#learn.
If you don't use Spring Boot, then you need to declare a MeterRegistry bean in the application context. And probably the one exactly for Prometheus: https://micrometer.io/docs/registry/prometheus.
If you are new to the framework, consider to move away from the XML configuration in favor of Java DSL: https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl.
Also make yourself familiar with Spring Boot which really auto-configurs for us many things, even a MeterRegistry for Prometheus: https://spring.io/projects/spring-boot

Cxf Failure Interceptor for one service bean in multi-bean service

I've a declaration in spring two services which are deployed on the same address. I would like to declare failure interceptor for only one of them, but is there a easy way to do that without changing address for two services? I would like to have them on the same address.
<jaxrs:server id="service" address="http://0.0.0.0:${service.port:7070}/">
<jaxrs:serviceBeans>
<ref bean="firstService"/>
<ref bean="secondService"/>
</jaxrs:serviceBeans>
<jaxrs:outInterceptors>
<ref bean="failureInterceptor" />
</jaxrs:outInterceptors>
<jaxrs:features>
<cxf:logging/>
<ref bean="commonValidationFeature"/>
</jaxrs:features>
<jaxrs:providers>
<ref bean="jsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
You can declare two jax-rs server, each one with its own interceptor if you could adapt slightly the relative path of the services
For example, you can use both equivalently
<jaxrs:server id="ServiceAImpl" address="/test/a">
<jaxrs:server id="ServiceBImpl" address="/test/b">
<jaxrs:server id="serviceImpl" address="/test">
<jaxrs:serviceBeans>
<ref bean="serviceABean"/> <!-- /test/a service -->
<ref bean="serviceBBean"/> <!-- /test/b service -->
but it is not allowed
<jaxrs:server id="ServiceAImpl" address="/test">
<jaxrs:server id="ServiceBImpl" address="/test">
If it is not possible for you, you could determine at interceptor which is the source service bean ( analysing method name or uri) and fire the especific interceptor manager

Alternate to #XmlRootElement?

in process of exposing existing statefull service as a RESTfull service.
I do not want to make any changes to any existing java class.I have been able to configure other annotations such as #path, #GET using spring-config.xml
spring-config.xml
<!-- Inquiry Services -->
<bean id="retrieveContactHistoryBP" class="com.csc.fs.ws.contact.history.impl.RetrieveContactHistoryBPService"/>
<!-- Update Services -->
<bean id="startContactBP" class="com.csc.fs.ws.contact.impl.StartContactBPService"/>
<!-- REST services -->
<bean id="startContactBPRest" class="com.csc.fs.rest.contact.StartContactBP" scope="prototype" />
<bean id="retrieveContactHistoryBPRest" class="com.csc.fs.rest.contact.RetrieveContactHistoryBP" scope="prototype" />
<!-- Exposing beans as rest services -->
<jaxrs:server id="restServer" address="/rest/">
<jaxrs:model id="restModel">
<jaxrs:resource name="com.csc.fs.rest.contact.RetrieveContactHistoryBP" path="retrieveContactHistoryBP">
<jaxrs:operation name="retrieve" path="{partyId}" consumes="application/json" produces="application/json" verb="GET">
<jaxrs:param name="req" type="CONTEXT"/>
<jaxrs:param name="partyId" type="PATH"/>
</jaxrs:operation>
</jaxrs:resource>
<jaxrs:resource name="com.csc.fs.rest.contact.StartContactBP" path="startContactBP">
<jaxrs:operation name="startContact" path="/" consumes="application/json" produces="application/json" verb="PUT">
<jaxrs:param name="req" type="CONTEXT"/>
<jaxrs:param name="startContact" type="REQUEST_BODY"/>
</jaxrs:operation>
</jaxrs:resource>
</jaxrs:model>
<jaxrs:serviceBeans>
<!-- <ref bean="startContactBPRest"/> --> <!-- Instead configure above -->
<!-- <ref bean="retrieveContactHistoryBPRest"/> -->
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="feed" value="application/atom+xml"/>
<entry key="json" value="application/json"/>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</jaxrs:extensionMappings>
<jaxrs:providers>
<ref bean="jaxbProvider"/>
<ref bean="jsonProvider" />
</jaxrs:providers>
</jaxrs:server>
The thing I am facing problem is with the #XmlRootElement. I have not been successful in configuring it through the xml.
And I get the following error when trying to access the REST service
org.apache.cxf.interceptor.Fault
org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:67)
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:315)
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:113)
org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:105)
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:461)
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:188)
org.apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.java:148)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:179)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:108)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:159)
root cause
java.lang.NullPointerException
org.apache.cxf.jaxrs.model.wadl.WadlGenerator.handleOperation(WadlGenerator.java:310)
org.apache.cxf.jaxrs.model.wadl.WadlGenerator.handleResource(WadlGenerator.java:253)
org.apache.cxf.jaxrs.model.wadl.WadlGenerator.handleRequest(WadlGenerator.java:185)
org.apache.cxf.jaxrs.impl.RequestPreprocessor.checkMetadataRequest(RequestPreprocessor.java:189)
org.apache.cxf.jaxrs.impl.RequestPreprocessor.preprocess(RequestPreprocessor.java:82)
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:112)
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:88)
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:255)
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:113)
org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:105)
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:461)
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:188)
org.apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.java:148)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:179)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:108)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:159)
So, Is there a way to configure the information in the XmlRoot annotation externally, so we don’t have to add it to Java code?
From the Apache-cxf documentation on jaxrs-data-bindings:
Alternatively to using #XmlRootElement and Collection wrappers, one can
provide an Object factory which will tell JAXB how to marshal a given
type (in case of Collections - its template type). Another option is to
return/accept a JAXBElement directly from/in a given method.
Another option is to register one or more JAX-RS ContextResolver providers
capable of creating JAXBContexts for a number of different types. The
default JAXBElementProvider will check these resolvers first before
attempting to create a JAXBContext on its own.

CXF is ignoring Spring bean configuration

I have a web application which is exposing a rest web service by cxf jax-rs. In my application context file I have something like this:
...
<bean id="service" class="SomeClass">
<constructor-arg index="0">
<ref bean="bean1" />
</constructor-arg>
<constructor-arg index="1"
value="some value" />
</bean>
<jaxrs:server id="restContainer" address="/">
<jaxrs:serviceBeans>
<ref bean="service" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
...
I also have a constructor in my service class that accepts those two parameters and initializes the service.
When I deploy my application, spring context loader is creating the service bean correctly and the correct constructor is getting called. The problem is when the first Rest request comes to service. Cxf Jax-rs is creating its own instance by "default constructor" and I will lose those two properties.
The same thing happens if I user property setters instead of constructor args. When I researched cxf jax-rs, none of the examples had a service which has some properties! Is there a reason for this or is this some implementation constraint by cxf?
Any ideas?

How to force disable JSR-303 support in Spring 3?

I have some legacy Spring MVC code mixed with gwt code in same artifact (built using maven) and I cannot make it run. It wants validation provider at runtime which i do not need (since I'm not using any JSR-303 validation annotations) and do not want in CP (it may conflict with some app containers this artifact will be deployed in)
How to force spring not to do any JSR-303 validations and get rid of runtime dependency on validation provider?
PS artifact has validation-api in CP since GWT is using it somehow
PPS
Seems like removing <mvc:annotation-driven/> from Spring config fixes this.
Binding and classic validations still works (I have <context:annotation-config/> enabled)
As you already discovered, <mvc:annotation-driven/> sets a lot of features including JSR-303. The equivalent is
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversion-service"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
So you may substitute the tag onto this xml configuration and remove parts you don't need.

Categories