CXF and optional entity parameters - cxf

I'm using Apache CXF as JAX-RS implementation and I have a problem with optional "entity parameters". Take a look at this piece of code:
#Path("/")
public class myClass {
#PUT
#Path("/{p1}/something/{p2}/something/{p3}/something/{p4}")
#Consumes(MediaType.APPLICATION_XML)
Response updateStuff(
#PathParam("p1") String p1,
#PathParam("p2") Long p2,
#PathParam("p3") String p3,
#PathParam("p4") Long p4,
MyEntity entity);
}
If entity is not null, everything works fine.
However I want to handle requests with empty body. I would expect, that in such case entity would be null. But it is not true. It seems that CXF can't find my method if body is null. In the logs I can find warning about empty message body (from AbstractJAXBProvider) and that's all.
I also tried to overload updateStuff method:
#Path("/")
public class myClass {
#PUT
#Path("/{p1}/something/{p2}/something/{p3}/something/{p4}")
#Consumes(MediaType.APPLICATION_XML)
Response updateStuff(
#PathParam("p1") String p1,
#PathParam("p2") Long p2,
#PathParam("p3") String p3,
#PathParam("p4") Long p4,
MyEntity entity);
#PUT
#Path("/{p1}/something/{p2}/something/{p3}/something/{p4}")
Response updateStuff(
#PathParam("p1") String p1,
#PathParam("p2") Long p2,
#PathParam("p3") String p3,
#PathParam("p4") Long p4);
}
But it didn't work either.
Do you have any ideas how to handle null entites parameters?

I think, if you webservice #Consumes(MediaType.APPLICATION_XML) the body can't be null.
But you can handle this having a #Provider to read MyEntity object.
#Provider
#Consumes(MediaType.APPLICATION_XML)
public class XMLReader implements MessageBodyReader<MyEntity>{
}

Method overloading, as in your example, works (tested in Apache-cxf 3.0), but the client must not send a Content-Type: application/xml header when he wants to hit the method without the entity in the signature.
If another media type is already defined at the class level, annotate the method with #Consumes(MediaType.WILDCARD), see the example below.
#Path("/")
#Consumes(MediaType.APPLICATION_XML)
public class myClass {
#PUT
#Path("/{p1}/something/{p2}/something/{p3}/something/{p4}")
Response updateStuff(
#PathParam("p1") String p1,
#PathParam("p2") Long p2,
#PathParam("p3") String p3,
#PathParam("p4") Long p4,
MyEntity entity);
#PUT
#Path("/{p1}/something/{p2}/something/{p3}/something/{p4}")
#Consumes(MediaType.WILDCARD)
Response updateStuff(
#PathParam("p1") String p1,
#PathParam("p2") Long p2,
#PathParam("p3") String p3,
#PathParam("p4") Long p4);
}

Related

Best way to unmashal an object to a POJO in Apache Camel

I am trying to retrieve an object out of the process method in Camel response.
However once I got an empty response in the following code:
from("timer://simpleTimer?repeatCount=1").routeId("myroute")
.setHeader("client_id", constant("abc"))
.setHeader("client_secret",constant("def"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader(Exchange.HTTP_URI, constant(url))
.marshal().json(JsonLibrary.Gson)
.log("trying to send message")
.to(url)
.convertBodyTo(String.class)
.process(new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
final Message message = exchange.getIn();
int responseCode = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
final String responseBody = message.getBody(String.class);
System.out.println("in final block of process:" +
responseCode + ",Body class name=" + responseBody.getClass()+
"body="+responseBody);
}
);
Here body is not being printed.This is strange as the body is being printed
I need a JSON representation back and also want to store it in a object so that we can return it while returning thi object from this method.
Is there something missing?What should be added to meet the requirement?

How take always first parameter when requested array type param in spring mvc using #RequestParam

I wrote this code.
#GetMapping("/test")
public Response search(#RequestParam String value) {
System.out.println(value);
return new Response(value)
}
Some body request like
/test?value=a&value=b&value=c
value binded a,b,c
I want always bind first parmeter. Take a, ignore b, c.
Is there way using #RequestParam?
Or have to use HttpServletRequest and parsing parameter?
In this case you can use #RequestParam List<String> value instead of #RequestParam String value, and get the first value value.get(0) ignore the rest of them
For Example
http://rentacar.com/api/v1/search?make=audi&model=A8&type=6&type=11&type=12&color=RED&color=GREY
Method
public List<Vehicle> search(
#RequestParam(value="make", required=false) String make,
#RequestParam(value="model", required=false) String model,
#RequestParam(value="type", required=false) List<String> types,
#RequestParam(value="color", required=false) List<String> colors)
{
....
}
Great question!
I wrote this code to find out how this works. I included it in the test packages.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class ControllerTest {
#LocalServerPort
private int port;
private URL url;
#Autowired
private TestRestTemplate template;
#Before
public void setUp() throws Exception {
this.url = new URL("http://localhost:" + port + "/test?value=a&value=b&value=c");
}
#Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(url.toString(),
String.class);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Assert.assertEquals(response.getBody(), "a");
System.out.println("response = " + response);
}
}
I then modified your code to accept an array of strings, and only pass the first element to your Response Constructor.
Notice the changes in your code in the signature and return statement.
#GetMapping("/test")
public String search(#RequestParam String[] value) {
System.out.println(value);
return new Response(value[0]);
}
With your test, you can now explore using a List type for your request param and quickly see how the behaviour has changed.

RecipientList with RequestReply Using Apache Camel

I think that I have a gap in my understanding of the RecipientList . My understanding is that RecipientList EIP can be used to represent dynamic destinations. I am attempting to use is with the RequestReply EIP but I am getting some strange results.
The code below is a unit test for RequestReply and aggregation of replies back to the sender. Message arrives at incomingMessages1-update, gets routed to outgoingMessages-[123]-update queues. The results come back on outgoingMessages-[123]-reply queues. The results are aggregated and sent back on incomingMessages1-reply queue.
See below a unit test that works:
public class AggregateStrategyTestOnMultipleReplyQueues extends CamelTestSupport {
#Test
public void testRequestReplyWithRecipientListAndCustomGather()
throws Exception {
int numberOfMessages = 5;
getMockEndpoint("mock:end").setExpectedMessageCount(numberOfMessages);
context.addRoutes(new RouteBuilder() {
public void configure() throws Exception {
from("jms:incomingMessages1-update")
.multicast(new GatherResponses())
.to("jms:outgoingMessages1-update?exchangePattern=InOut&replyTo=queue:outgoingMessages1-reply&preserveMessageQos=true") //1
.to("jms:outgoingMessages2-update?exchangePattern=InOut&replyTo=queue:outgoingMessages2-reply&preserveMessageQos=true") //2
.to("jms:outgoingMessages3-update?exchangePattern=InOut&replyTo=queue:outgoingMessages3-reply&preserveMessageQos=true") //3
.to("mock:end");
//this is what the adapters will be doing
from("jms:outgoingMessages1-update").setBody(constant("Hello World")).to(
"mock:end");
from("jms:outgoingMessages2-update").setBody(constant("Welcome World")).to(
"mock:end");
from("jms:outgoingMessages3-update").setBody(constant("Hi World")).to(
"mock:end");
}
});
String messageSent = "Message sent from template";
Object response = template
.requestBodyAndHeader(
"jms:incomingMessages1-update?exchangePattern=InOut&preserveMessageQos=true",
messageSent, "JMSReplyTo", "incomingMessages1-reply");
assertEquals("Hello World" + " "+ "Welcome World"+ " "+ "Hi World"+ " " + messageSent ,
response);
}
private class GatherResponses implements AggregationStrategy {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
if (oldExchange == null) {
return newExchange;
}
String oldBody = oldExchange.getIn().getBody(String.class);
String newBody = newExchange.getIn().getBody(String.class);
String body = oldBody + " " + newBody;
oldExchange.getIn().setBody(body);
return oldExchange;
}
}
}
I attempted to change the code above (//1, //2 and //3 to a recipient list like below) and it didn't work:
from("jms:incomingMessages1-update")
.recipientList(header("myRecipientList")).aggregationStrategy(new GatherResponses()).parallelProcessing().end()
.to("mock:end");
I loaded the URIs like this:
List<String> recipientList = new ArrayList<String>();
recipientList.add("jms:outgoingMessages1-update?exchangePattern=InOut&replyTo=queue:outgoingMessages1-reply&preserveMessageQos=true");
recipientList.add("jms:outgoingMessages2-update?exchangePattern=InOut&replyTo=queue:outgoingMessages1-reply&preserveMessageQos=true");
recipientList.add("jms:outgoingMessages3-update?exchangePattern=InOut&replyTo=queue:outgoingMessages1-reply&preserveMessageQos=true");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("JMSReplyTo", "incomingMessages1-reply");
headers.put("myRecipientList", recipientList);
I am getting the original message back and I am not seeing the reply queues created. Can you please point me to what I am missing?
You cannot send a List/Map etc as JMS headers. The JMS spec does not allow that.
See section Message format when sending at
http://camel.apache.org/jms
And also the JMS spec / api / javadoc etc.
You can instead store the values in a String separated by comma. The Camel recipient list will automatic use comma as delimiter, so that should then work out of the box.

Sending a complex object as a parameter in Resteasy

I am using resteasy, and till now I am just sending and receiving string as parameters and every thing was OK, but now I want to send a complex object ( List<Map<String, ObjectVal>> ) as one of my parameters. My objectVal class has two simple field ( id and value, with getters and setters).
I can find different question and answers for sending objects as parameters but all of them are missing something and not useful for me.
here is my functions with a simple string parameter
#GET
#Path("/isUserAuthorizedToDocument")
public Response isUserAuthorizedToDocumentService(
#QueryParam("userID") String userID){
.............
.............
}
and the client
private ClientRequest req =new ClientRequest(....url with path and ....)
req.queryParameter("userID", user.getUserId());
ClientResponse<Boolean> response = req.get(Boolean.class);
Now I want to send a parameter from my client in the form of List<Map<String,ObjectVal>> and recieve it in my rest function.
My ObjectVal class
#XmlRootElement(name = "objectValueDTO")
public class ObjectValueDTO implements Serializable {
/**
* Id for this class
*/
private static final long serialVersionUID = 164186789404269392L;
// Id on object type
private String objectTypeID = "";
// Selection
private String value = "";
/** Getter and Setters */
#XmlElement
public String getObjectTypeID() {
return objectTypeID;
}
public void setObjectTypeID(String objectTypeID) {
this.objectTypeID = objectTypeID;
}
#XmlElement
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
any help will be appreciated
I may be niave on this. But when you have to send complex parameters, you need to use PUT and send the parameters in the request.

Persistence with EJB3 doesn't work

I have a method to save a new object in an EJB bean. This method is called, without error, but nothing changes in the database. I can't understand why.
Here is the code:
#Stateless(name = "Ar", mappedName = "ManagementBean")
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
...
#Override
public int storeRawSms(String raw, String requestUid, String text, String service, boolean correctlyAnalysed, Date receivedTimestamp,
boolean toBeAnalysed, String phoneNumber) {
// Get phone number, create if it dosn't exist
PhoneNumber pn = getOrCreatePhoneNumberPrivate(phoneNumber);
// Create rawSMS
RawSms rawSms = new RawSms(raw, requestUid, text, service, correctlyAnalysed, receivedTimestamp, toBeAnalysed, pn);
// Store and return result
em.persist(rawSms);
int result = rawSms.getId();
em.flush();
em.clear();
return result;
}
...
And the caller:
#PersistenceContext private EntityManager em;
...
int rawSmsIs = bean.storeRawSms(raw, requestUid, message, service, false, new Date(), true, sender);
Do you have an idea?
I see that you inject a reference to the EntityManager in the client (not sure why), but I don't see it in the session bean (maybe simply because you did not include the line in your message). Is it possible that you forgot to use the annotation #PersistenceContext in your stateless session bean?
Also, be careful: depending on the JPA implementation you are using and the generation strategy for the ids, you should call flush() before calling getId(). Indeed, if you let the DB generate your IDs, then you need a flush() to have this happen before the method returns the value.
Thanks, the prposed solution worked!
I use the container-managed transactions like this:
#Stateless(name = "Ar", mappedName = "ManagementBean")
#TransactionManagement(TransactionManagementType.CONTAINER)
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
....
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public int storeRawSms(String raw, String requestUid, String text, String service, boolean correctlyAnalysed, Date receivedTimestamp, boolean toBeAnalysed, String phoneNumber) {
....
Thanks again!
It seems that your transaction never commited, so try changing transaction management:
#Stateless(name = "Ar", mappedName = "ManagementBean")
#TransactionManagement(TransactionManagementType.BEAN)
public class ManagementBean implements IManagementBeanLocal, IManagementBeanRemote {
#Resource
private UserTransaction utx;
#Override
public int storeRawSms(..) {
try {
utx.begin();
..
em.persist(rawSms);
int result = rawSms.getId();
utx.commit();
}
catch(Exception ex) {
//EXCEPTION HANDLING
utx.rollback();
}
}
}

Resources