I would like to send JMS messages containing Java POJOs to ActiveMQ and all messages should be converted to JSON documents. So I need mechanism that will convert POJO to JSON and will send created document as text message to ActiveMQ. I would like to use ProducerTemplate#send(...) method without need to define routes. I am using routes on the server, but in my opinion doing so on the client side is an overkill.
This is xml config:
<camel:camelContext id="camel-client">
<camel:template id="camelTemplate" />
<camel:dataFormats>
<camel:json id="json" library="Jackson" />
</camel:dataFormats>
</camel:camelContext>
and java code:
#EndpointInject(uri = "jms:queue:test?jmsMessageType=Text")
private ProducerTemplate camelTemplate;
#Test
public void send() {
Address address = new Adress("Eric Mouller", "ForstenriederAlle 99", 81476);
camelTemplate.sendBody(address);
}
The current implementation calls toString() on Adress, but I would like to automatically convert it to JSON, is it possible?
From my understanding you are trying to take a java object and convert it into a json string. So something like Gson would do wonders for you.
Gson gson = new Gson();
String address = gson.toJson(address);
Reference:
https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/Gson.html
Related
10:39
I want to create a route that returns a JSON response to a user via REST and then sends data to BigQuery. Something like:
rest()
.get("/getSchedule")
.route()
.process("business logic - creates object with schedule AND big query statement")
The problem is that these are 2 different things. For the REST response to be correct I have to put the object with the schedule into the exchange body. But in order to send the BigQuery statement to the BigQuery component I have to set the BQ statement into the exchange object. Which messes up the REST response.
How can I accomplish this?
OK, I figured this out. What I want is a solution that returns with the REST response immediately and sends some other data to BigQuery as well. Since the 2nd step may block I will put an asynchronous process in between (could be a queue but in my case was Google PubSub). My route looks like this (leaving out things like .consumes, .outTypes for clarity):
rest()
.get("/getSchedule")
.route()
.processor(<business_logic>)
.wiretap("direct:pubsub");
from("direct:pubsub")
.process("processorA")
.to("google-pubsub topic");
from("google-pubsub subscription")
.processor("processorB")
.to("google-bigquery");
The first processor with business logic computes desired schedule data, puts it into a POJO, and puts a bigquery statement into an exchange header named "event".
#Override
public void process(Exchange exchange)
{
<business logic>
ScheduleData sData = new ScheduleData();
<insert values>
exchange.getIn().setBody(sData)
String insertStatement = "<insert_stmt>"
exchange.getIn().setHeader("event",insertStatement )
}
The ScheduleData data becomes the response I want. Now ProcessorA does some needed work before we put the data into the topic:
#Override
public void process(Exchange exchange)
{
String stmt = (String)exchange.getIn().getHeader("event")
byte[] data = <convert "stmt" into UTF8 bytes>
exchange.getIn().setBody(data);
}
Now the BigQuery data is sent through PubSub, received by Camel, and sent to ProcessorB.
public void process(Exchange exchange)
{
byte[] data = exchange.getIn().getBody(byte[].class)
String stmt = <convert UTF8 bytes to string>
exchange.getIn().setBody(stmt);
Putting the statement back in the exchange body it can now be sent to the BiqQuery component. Which I have not solved yet. But at least the wiretap method was the answer to my use case.
Requisite disclaimer about being new to Camel--and, frankly, new to developing generally. I'd like to have a string generated as the output of some function be the source of my camel route which then gets written to some file. It's the first part that seems challenging: I have a string, how do I turn it into a message? I can't write it into a file nor can I use JMS. I feel like it should be easy and obvious, but I'm having a hard time finding a simple guide to help.
Some pseudo-code using the Java DSL:
def DesiredString() {return "MyString";}
// A camel route to be implemented elsewhere; I want something like:
class MyRoute() extends RouteBuilder {
source(DesiredString())
.to("file://C:/out/?fileName=MyFileFromString.txt");
}
I vaguely understand using the bean component, but I'm not sure that solves the problem: I can execute my method that generates the string, but how do I turn that into a message? The "vague" is doing a lot of work there: I could be missing something there.
Thanks!
Not sure if I understand your problem. There is a bit of confusion about what the String should be become: the route source or the message body.
However, I guess that you want to write the String returned by your method into a File through a Camel route.
If this is correct, I have to clarify first the route source. A Camel Route normally starts with
from(component:address)
So if you want to receive requests from remote via HTTP it could be
from("http4:localhost:8080")
This creates an HTTP server that listens on port 8080 for messages.
In your case I don't know if the method that returns the String is in the same application as the Camel route. If it is, you can use the Direct component for "method-like" calls in the same process.
from(direct:input)
.to("file:...");
input is a name you can freely choose. You can then route messages to this route from another Camel route or with a ProducerTemplate
ProducerTemplate template = camelContext.createProducerTemplate();
template.sendBody("direct:input", "This is my string");
The sendBody method takes the endpoint where to send the message and the message body. But there are much more variants of sendBody with different signatures depending on what you want to send it (headers etc).
If you want to dive into Camel get a copy of Camel in Action 2nd edition. It contains everything you need to know about Camel.
Example:Sending String(as a body content)to store in file using camel Java DSL:
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("timer:StringSentToFile?period=2000")
.setBody(simple(DesiredString()))
.to("file:file://C:/out/?fileName=MyFileFromString.txt&noop=true")
.log("completed route");
}
});
ProducerTemplate template = context.createProducerTemplate();
context.start();
Apache Camel 2.12.1
Is it possible to use the Camel CSV component with a pollEnrich? Every example I see is like:
from("file:somefile.csv").marshal...
Whereas I'm using the pollEnrich, like:
pollEnrich("file:somefile.csv", new CSVAggregator())
So within CSVAggregator I have no csv...I just have a file, which I have to do csv processing myself. So is there a way of hooking up the marshalling to the enrich bit somehow...?
EDIT
To make this more general... eg:
from("direct:start")
.to("http:www.blah")
.enrich("file:someFile.csv", new CSVAggregationStrategy) <--how can I call marshal() on this?
...
public class CSVAggregator implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
/* Here I have:
oldExchange = results of http blah endpoint
newExchange = the someFile.csv GenericFile object */
}
Is there any way I can avoid this and use marshal().csv sort of call on the route itself?
Thanks,
Mr Tea
You can use any endpoint in enrich. That includes direct endpoints pointing to other routes. Your example...
Replace this:
from("direct:start")
.to("http:www.blah")
.enrich("file:someFile.csv", new CSVAggregationStrategy)
With this:
from("direct:start")
.to("http:www.blah")
.enrich("direct:readSomeFile", new CSVAggregationStrategy);
from("direct:readSomeFile")
.to("file:someFile.csv")
.unmarshal(myDataFormat);
I ran into the same issue and managed to solve it with the following code (note, I'm using the scala dsl). My use case was slightly different, I wanted to load a CSV file and enrich it with data from an additional static CSV file.
from("direct:start") pollEnrich("file:c:/data/inbox?fileName=vipleaderboard.inclusions.csv&noop=true") unmarshal(csv)
from("file:c:/data/inbox?fileName=vipleaderboard.${date:now:yyyyMMdd}.csv") unmarshal(csv) enrich("direct:start", (current:Exchange, myStatic:Exchange) => {
// both exchange in bodies will contain lists instead of the file handles
})
Here the second route is the one which looks for a file in a specific directory. It unmarshals the CSV data from any matching file it finds and enriches it with the direct route defined in the preceding line. That route is pollEnriching with my static file and as I don't define an aggregation strategy it just replaces the contents of the body with the static file data. I can then unmarshal that from CSV and return the data.
The aggregation function in the second route then has access to both files' CSV data as List<List<String>> instead of just a file.
My project is based on GAE/J and utilizing the recent launched PULL queue, but I think the question can also be applied to Python.
Basically, when I put a task into the PULL queue, I need to set some params of the task for the later consumer to pick it up.
I have implemented in the params setting in both ways:
1) By using param():
TaskOptions taskOptions = TaskOptions.Builder.
withMethod(TaskOptions.Method.PULL);
taskOptions.param("param", paramValue);
taskOptions.param("param2", paramValue2);
2) By using payload():
TaskOptions taskOptions = TaskOptions.Builder.
withMethod(TaskOptions.Method.PULL);
taskOptions.payload("payloadValue");
Both approaches are working, however, what I would like to know is what's the differences between the two, and which way should be the preferred way in terms of efficiency or convenience.
I can see that by using param(), it is easy to set multiple parameters and also easy to retrieve the parameters for the consumer.
But for one parameter cases, then payload may come in more handy as it saves the code to catch Exceptions throwing out when the consumer extract parameters.
However, I would be happy to know any more differences between these two apart from what I have menitoned.
Per the python documentation, I would say that in your case is exactly the same.
In PULL requests, Do not specify params if you already specified a payload. Params are encoded as application/x-www-form-urlencoded and set to the payload.
There is difference in .param() and .payload() functions of TaskOptions. You can use these functions as follows;
taskOptions.param("param1","Invoice_3344");
Now at receiver end, lets say you are calling a servlet , the in the HttpRequest, you can receive the sent parameters as request parameter.
public class MyInvoiceTask extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String invoiceNum = request.getParameter("param1");
}
}
Now Assume you wanted to serialize your entire custom class object which has huge data. In such case you would need to user .payload() function as internally, it send the payload data in a request body.
//**Custom class object
Person person = new Person("Abc", "Mumbai", 22);
//**Convert the object into JSON so that can be converted into String(required for Payload)
//**Use Gson library
Gson gson = new Gson();
String personObjString = gson.toJson(person);
//**put the payload in task option as byte array
taskOption.payload(personObjString.toByteArray());
Now at receiver end lets say using servlet, then from HttpRequest object, we would need to get the payload byte array and convert it back into cutsom object i.e. "Person" class object in our case.
private byte[] getPayloadFromHttpRequest(HttpServletRequest req) throws IOException
{
InputStream inputStream = req.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int length;
byte[] buffer = new byte[1024];
while ((length = inputStream.read(buffer)) >= 0)
byteArrayOutputStream.write(buffer, 0, length);
if (byteArrayOutputStream.size() > 0){
return byteArrayOutputStream.toByteArray();
}
return null;
}
//**Now this received byteArray can be used with Gson to convert it back into Person object
byte[] payload = getPayloadFromHttpRequest(request);
Gson gson = new Gson();
String personJsonString = new String(payload);
Person person = gson.fromJson(personJsonString, Person.class);
I want to upload a JPG file and a JSON-serialized Java object. On the server I am using Apache CXF, on the client I am integration testing with rest-assured.
My server code looks like:
#POST
#Path("/document")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#Multipart(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
throws IOException
{}
My client code looks like:
given().
multiPart("document", new File("./data/json.txt"), "application/json").
multiPart("image", new File("./data/image.txt"), "image/jpeg").
expect().
statusCode(Response.Status.CREATED.getStatusCode()).
when().
post("/document");
Everything works fine when I read the json part from the file as in the first multiPart line. However, when I want to serialize the json instance I come into problems. I tried many variants, but none worked.
I thought this variant should work: on the client
JsonBean json = new JsonBean();
json.setVal1("Value 1");
json.setVal2("Value 2");
given().
contentType("application/json").
formParam("document", json).
multiPart("image", new File("./data/image.txt"), "image/jpeg").
...
and on the server
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#FormParam(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
but no. Can anyone tell me how it should be?
Try different approach (worked for me), I am not sure if this is suitable in your case.
Make JsonBean a JAXB entity, that it add #XmlRootEntity above class definition.
Then, instead of formParam
given().
contentType("application/json").
body(bean). //bean is your JsonBean
multiPart("image", new File("./data/image.txt"), "image/jpeg").
then
public Response storeTravelDocument(
#Context UriInfo uriInfo,
JsonBean bean, //should be deserialized properly
#Multipart(value = "image") InputStream pictureStream)
I've never tried that with #Multipart part, but, hopefully it would work.
Multipart/form-data follows the rules of multipart MIME data streams, see w3.org. This means that each part of the request forms a part in the stream. Rest-assured supports already simple fields (strings), files and streams, but not object serialization into a part. After asking on the mailing list, Johan Haleby (the author of rest-assured) suggested to add an issue. The issue is already accepted, see issue 166.
The server will stay as it is:
#POST
#Path("/document")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#Multipart(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
throws IOException
{}
The client code will look like:
given().
multiPartObject("document", objectToSerialize, "application/json").
multiPart("image", new File("./data/image.txt"), "image/jpeg").
expect().
statusCode(Response.Status.CREATED.getStatusCode()).
when().
post("/document");
Maybe the name "multiPartObject" will change. We will see once it is implemented.