Since a week i have been trying to create an camel route for following requirement.
With Request key, route looks into filenet
If there is a document for key in filenet - it will be retrieved and sent to Portal
If not - request will be sent to Outside WS, retrieved document will be stored in filenet.
For Next same request key, document will be retrieved from filenet based on metadata and sent as response
Below are the 2 interfaces/camel-routes for retreiving from and storing in filenet
Store document
from("activemq:queue:STORE_DOCUMENT_QUEUE")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().getHeaders().put(PropertyIds.CONTENT_STREAM_MIME_TYPE, "application/pdf; charset=UTF-8");
exchange.getIn().getHeaders().put(PropertyIds.NAME, exchange.getIn().getHeader(Exchange.FILE_NAME));
exchange.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/TEST");
}
})
.to("cmis://${header.cmisUrl}");
Retrieve document
from("activemq:queue:RETRIEVE_DOCUMENT_QUEUE")
.setBody(constant("SELECT * FROM cmis:document WHERE cmis:name LIKE '%requestkey%'"))
.to("cmis://${header.cmisUrl}")
.split(body())
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
Map<String, Object> body = (Map<String, Object>)exchange.getIn().getBody();
exchange.getIn().getHeaders().put(Exchange.FILE_NAME, body.get(PropertyIds.NAME));
exchange.getIn().setBody(body.get(CamelCMISConstants.CAMEL_CMIS_CONTENT_STREAM));
}
})
.to("file:src/test");
In above routes, there is no/very less metadata information...
If i need to search for document by metadata or store to filenet a doc using metadata as key.
Example : Store a document with following key as metadata and retrieving using same
1. Identity Number
2. Type of Document
3. Receipt Date
Can somebody give me a knowledge of setting metadata in camel exchange or using metadata for storing and retreival of document ??
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.
A simple test of setting the document class of document stored in filenet worked in camel-cmis 2.16.2. Below is the route
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(PropertyIds.CONTENT_STREAM_MIME_TYPE, "application/pdf; charset=UTF-8");
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/Test");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, e.getIn().getHeader(Exchange.FILE_NAME));
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
When i check the document class of file stored in IBM Filenet - i could see the doc class as Test (symbolic name: doc_Test). But when i add atleast one of the parameter value of that class like below
e.getOut().getHeaders().put("prp_Field1","TestValue1");
Im getting NoSuchHeaderException for parameter "cmis:name" which i have already set as you can see the above route. Is this the correct way to set metadata parameters??
Below route for storing worked. Decide on the Customized classes,fields & data types, create metadata based on that & then same field names are to be set in camel headers before sending to cmis uri (Storing).
ObjectTypeId - Customized class name
CMIS_FOLDER_PATH - filenet folder path inside repository
NAME - File name to be stored
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/TEST");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, fileName + ".pdf");
e.getOut().getHeaders().put("prp_Field1","TestValue1");
e.getOut().getHeaders().put("prp_Field2","TestValue2");
e.getOut().getHeaders().put("prp_Field3","TestValue3");
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
Hope it helps someone..
I have a hypothetical scenario: let’s pretend I have an Apache Camel websocket server and I’m allowing many websocket connections. Each client connection will need to be associated with a ClientID. The ClientID is obtained by a new connection via an InitConnection json message where a ClientID is a member of the message. The question is: is it possible to have camel associate a websocket instance with a ClientID in order to perform content based routing?
yes, It is possible. you can retrieve a UUID of each client by below method:
from("direct:Consumer1")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
Map<String, Object> headers=exchange.getIn().getHeaders();
//you can get a unique connection key from the exchange header.
//store this key somewhere, to send messages to particular client.
String uniqueConnectionKey=headers.get("websocket.connectionKey").toString();
//you can get message from the client like below.
String dataFromClient=exchange.getIn().getBody().toString();
}
}).end();
you need to map this unique key with ur client id, so that u can send messages to particular client using this UUID.
CamelContext camelContext=new DefaultCamelContext();
ProducerTemplate template=camelContext.createProducerTemplate();
template.sendBodyAndHeader("direct:Producer1", {message}, "connectionKey", {connectionkey});
direct:Producer1 : producer endpoint name.
connectionkey : a unique connection key, which you will get from the exchange header in websocket consumer.
message : message to the websocket endpoint.
Edit :
here is the producer route.
from("direct:Producer1").
//we will use this connectionKey for uniquely identifying each connection from the client.
setHeader(WebsocketConstants.CONNECTION_KEY, header("connectionKey")).
to("websocket://{host}:{port}/camel-websocket?sendToAll=false").end();
From a programming point of view, I have a very simple business case. However, I can't figure out how to implement it using Apache Camel... Well, I have 2 JMS queues: one to receive commands, another - to store large number of message which should be delivered to external system in a batches of 1000 or less.
Here is the concept message exchange algorithm:
upon receiving a command message in 1st JMS queue I prepare XML
message
Send the XML message to external SOAP Web Service to obtain a usertoken
Using the usertoken, prepare another XML message and send it to a REST service to obtain jobToken
loop:
4.1. aggregate messages from 2nd JMS queue in batches of 1000, stop aggregation at timeout
4.2. for every batch, convert it to CSV file
4.3. send csv via HTTP Post to a REST service
4.4. retain batchtoken assigned to each batch
using the jobtoken prepare XML message and send to REST service to commit the batches
using batchtoken check execution status of each batch via XML message to REST service
While looking at Camel I could create a sample project where I can model out the exchange 1-3, 5:
from("file:src/data?noop=true")
.setHeader("sfUsername", constant("a#fd.com"))
.setHeader("sfPwd", constant("12345"))
.to("velocity:com/eip/vm/bulkPreLogin.vm?contentCache=false")
.setHeader(Exchange.CONTENT_TYPE, constant("text/xml; charset=UTF-8"))
.setHeader("SOAPAction", constant("login"))
.setHeader("CamelHttpMethod", constant("POST"))
.to("http4://bulklogin") // send login
.to("xslt:com/eip/xslt/bulkLogin.xsl") //xslt transformation to retrieve userToken
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String body = (String) exchange.getIn().getBody();
String[] bodyParts = body.split(",");
exchange.getProperties().put("userToken", bodyParts[0]);
.....
}
})
.to("velocity:com/eip/vm/jobInsertTeamOppStart.vm")
.setHeader(Exchange.CONTENT_TYPE, constant("application/xml; charset=UTF-8"))
.setHeader("X-Session", property("userToken"))
.setHeader("CamelHttpMethod", constant("POST"))
.to("http4://scheduleJob") //schedule job
.to("xslt:com//eip/xslt/jobInfoTransform.xsl")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String body = (String) exchange.getIn().getBody();
exchange.getProperties().put("jobToken",body.trim());
}
})
//add batches in a loop ???
.to("velocity:com/eip/vm/jobInsertTeamOppEnd.vm")
.setHeader(Exchange.HTTP_URI, simple("https://na15.com/services/async/job/${property.jobToken}"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/xml; charset=UTF-8"))
.setHeader("X-ID-Session", property("userToken"))
.setHeader("CamelHttpMethod", constant("POST"))
.to("http4://closeJob") //schedule job
//check batch?
.bean(new SomeBean());
So, my question is:
How can I read messages from my 2nd JMS queue?
This doesn't strike me as a very good use-case for a single camel route. I think you should implement the main functionality in a POJO and use Camels Bean Integration for consuming and producing messages. This will result in much more easy to maintain code, and also for easier Exception handling.
See https://camel.apache.org/pojo-consuming.html
Apache Camel 2.12.1
I have a route setup like this:
public void configure() throws Exception {
from("direct:start")
.process(new AuthorizationHeaderProcessor(configureCreds()))
.to(httpSourceEndpoint)
.process(new GenerateSQLFromMessageProcessor))
.enrich("jdbc:dataSource", new DBAggregator())
//...do things with result...
1) HttpSourceEndPoint is a get request from some url.
2) I then want to use the result of this to generate the SQL as per GenerateSQLFromMessageProcessor, which is provided as input to the JDBC route.
My problem is, within the DBAggregator, the parameters that come through are:
oldExchange = the raw SQL string that was sent to the JDBC call
newExchange = the result set from the DB query
ie there is no sign of the original message that came from the http source endpoint, which is how I would have expected aggregation to work. How am I supposed to combine the 2 streams? Was it the GenerateSQLFromMessageProcessor call that consumed the original message? If so, should you specify the SQL in a bean for an enrich?
EDIT
So setting in the header like this:
public void configure() throws Exception {
from("direct:start")
.process(new AuthorizationHeaderProcessor(configureCreds()))
.to(httpSourceEndpoint)
.setHeader(new BeanExpression(MySQLBean.class, "methodToGenerateSQL")
.enrich("jdbc:dataSource", new DBAggregator())
//...do things with result...
results in my aggregator looking like this:
public class DBAggregator implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
Here I have:
oldExchange = the resulting SQL string that methodToGenerateSQL generated
newExchange = the result set from the SQL query
The problem is I do not have access to the original message that came from httpSourceEndpoint.
As this is an aggregator I would have expected oldExchange to be the incoming message, not just an SQL string.
After all, it is an aggregator, and yet I have effectively lost the incoming message - this is not "enriching"!
Thanks,
Mr Tea