Camel persisting exchange from two routes - apache-camel

So, i have to get some objects from route1, process then and send then to route2. After that, if the object has an attachment i've to send the body from route1 and a key from route2 to route3, something in the lines of this example:
from("timer:myT?period=600s").to("https://some-service")
.routeId("route1")
.unmarshal(new ListJacksonDataFormat(MyClass.class))
.process(new SomeProcessor())
.split(body())
.to("direct:create");
from("direct:create")
.routeId("route2")
.setHeader("someProperty", simple("${body.someProperty}"))
.setBody(simple("${body.comment}"))
.to("someComponent://add?url=" + url + "&username=" + user + "&password=" + pw)
.aggregate(new GroupedExchangeAggregationStrategy())
.constant(simple("test"))
.completionTimeout(1000)
.choice().when(simple("${body.attach}").isNotNull())
.to("direct:attach");
from("direct:attach")
.routeId("route3")
.setHeader("key", simple("${body.key}"))
.setBody(simple("${body.attach}"))
.process(new AttachmentProcessor())
.to("someComponent:attach?url=" + url + "&username=" + user + "&password=" + pw);
My problem is that i can't get the 2 exchanges. I've tryed with aggregation strategy (both custom and grouped) and multicast but without success. What am i doing wrong? What should be the best approach?

Related

Finding the ID of a Salesforce Global Value Set

I'm attempting to write an APEX class that will add items to a global valueset, but in order to do so I need to know the ID of the global value set. Is there a way to find the ID of the global valueset (through APEX, not by looking at the URL) that a picklist field is using? Ideally I'd be able to do something similar to:
Case.picklistField__c.getdescribe();
and get a response that includes the ID of the global value set that it uses - that way I can then use my the metadataAPI to update the values.
Alternatively if I could find the global valueset by name I could use that with the metadata api as a work around.
UPDATE: Was able to get this to work using Eyescreams suggestion with the tooling API - full implementation:
String gvsName = 'TestValueSet'; //name of the global valueset you want the Id for goes here
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/query/?q=select+id+from+globalvalueset+Where+developername='+gvsName);
req.setMethod('GET');
Http httpreq = new Http();
HttpResponse res = httpreq.send(req);
system.debug(res.getBody());
SELECT Id, FullName, Description
FROM GlobalValueSet
But it's not available in straight Apex queries, you'd need Tooling API (meaning a REST callout). You can play with it in Developer Console -> Query Editor, just check the tooling api checkbox on bottom
These days you need to escape the variable like this:
String gvsName = 'TestValueSet'; //name of the global valueset you want the Id for goes here
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v48.0/tooling/query/?q=select+id+from+globalvalueset+Where+developername=\''+gvsName+'\'');
req.setMethod('GET');
Http httpreq = new Http();
HttpResponse res = httpreq.send(req);
system.debug(res.getBody());

How to send Twilio SMS via Web Service in Salesforce Apex

I want to send SMS from Twilio noticed they have libraries built for Java, .Net, Node, etc. so that we can use them if we are upto those technologies.
But I want to do the same from Salesforce Apex and trying to figure out how to build the Http parameters to make the authorization.
I tried to map with cURL example given in Twilio documentation and can't find header keys for Auth token.
Below is my current code and looking for how to set the authentication params.
req.setEndpoint(baseURL + '/2010-04-01/Accounts/account_sid/Messages.json');
req.setMethod('POST');
req.setHeader('to', EncodingUtil.urlEncode('+to_number', 'UTF-8'));
req.setHeader('from', EncodingUtil.urlEncode('+from_number', 'UTF-8'));
Http ht = new Http();
HttpResponse res = ht.send(req);
Updated request :
Blob headerValue = Blob.valueOf('my-twilio-account-sid:my-twilio-auth-token');
String authorizationHeader = 'BASIC ' + EncodingUtil.base64Encode(headerValue);
req.setHeader('Authorization', authorizationHeader);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
String body = EncodingUtil.urlEncode('From=+from_number&To=+to_number&Body=Sample text from twilio', 'UTF-8');
req.setBody(body);
Http ht = new Http();
HttpResponse res = ht.send(req);
Response saying
Bad Request : A 'From' phone number is required.
The phone numbers don't go in the headers.
For the headers you will need
Content-Type: "application/x-www-form-urlencoded"
then you will need another header for authorization
Authorization: auth-string
where auth-string is a combination of the string Basic followed by a space followed by a base64 encoding of twilio-account-sid:twilio-auth-token (replace with your Twilio credentials joined by the colon) so the header will look something like
Authorization: "Basic ABCiDEdmFGHmIJKjLMN2OPQwR2S3TUVzABliCDE3FGc1HIo2JKL2MjNwOPcxQRSwTUc1Vzc0XmYhZAB3CDElFGH1Jw=="
The body of the POST request should contain key, value pairs of To, From and Body, something like
"From=" + twilio-phone-number + "&To=" + to-number + "&Body=" + message-body (replace with string values for phone numbers and message).
I hope this helps.

How to create multiple JmsQueueEndpoint object for different selector using Endpoint class

I have a queue where I want to process Selected messages and am able to achieve it using Java DSL as below:
from("amq:queue:{{jms.miscQueue}}?transacted=false&selector=DSBSelectorID %3D '" + SELECTOR_ID_TRANSACTION_A2A + "'")
from("amq:queue:{{jms.miscQueue}}?transacted=false&selector=DSBSelectorID %3D '" + SELECTOR_ID_TRANSACTION_A2P + "'")
Now I want to achieve the same by creating JMS Endpoints as below (Actually Java DSL works fine but it gets difficult to debug over time as more and more option is embedded to the URI) :
JmsQueueEndpoint activeMQEndpointA2A = endpoint("amq:queue:{{jms.miscQueue}}", JmsQueueEndpoint.class);
activeMQEndpointA2A.setTransacted(false);
activeMQEndpointA2A.setSelector("DSBSelectorID %3D 'Route-Transaction-A2A'");
JmsQueueEndpoint activeMQEndpointA2P = endpoint("amq:queue:{{jms.miscQueue}}", JmsQueueEndpoint.class);
activeMQEndpointA2P.setTransacted(false);
activeMQEndpointA2P.setSelector("DSBSelectorID %3D 'Route-Transaction-A2P'");
And when I use activeMQEndpointA2A and activeMQEndpointA2P in my Camel Routes it only works for the first Endpoint and second endpoint is unable to connect to the queue and keeps throwing error as below
Setup of JMS message listener invoker failed for destination
'io.az.jms' - trying to recover. Cause: DSBSelectorID %3D
'Route-Transaction-A2P'
Can someone suggest if it's achievable through Java Endpoint or not? Also if I have several routes and there are multiple options to be defined on the Endpoint URI what's the recommended approach?
You cannot do this, get an endpoint and then mutate its properties, and then get the endpoint again (you get the same instance as the first) eg A2A and A2P are the same endpoint instance.
You need to use unique endpoints and hence the selector should be in the uri

Camel jms listener not picking up right messages

Usecase: I would like to pick messages from an http end point and route them to a jms endpoint.
My route configuration looks like:
from("jetty:http://0.0.0.0:9080/quote")
.convertBodyTo(String.class)
.to("stream:out")
.to(InOut, "wmq:queue:" + requestQueue +
"?replyTo=" + responseQueue +
"&replyToType=" + replyToType +
"&useMessageIDAsCorrelationID=true");
My understanding is that this way I will get a request-response pattern for the JMS endpoint and correlationId would be the unique identifier to make the response to the corresponding request.
This works well when I have only 1 instance of the application running however when I have more than 1 instances running simultaneously, responses are picked up randomly and not only by the producer.
For example, A and B are 2 instances of the route (and listener) with exact same configuration listening for responses on a shared queue. At times A gets its response but at times it also picks up response for the message produced by B.
Appreciate any help/pointers on this. Thanks!
Basically the issue was with the use of replyToType which was set to Exclusive - this resulted in listener picking up everything instead of using jms selectors to match correlation ids.
I changed the value of replyToType to Shared and its all working fine now.
This is well defined in section "Request-Reply over JMS" at http://camel.apache.org/jms.html

Basic Auth Header appears to be lost

I am writing a Flask RESTful server and an AngularJS client, and am running into an issue where it appears the username and password information are being lost in transmission, so to speak.
In the Javascript console, I can tell that the client is sending the the Authorization header as expected: Authorization: Basic username:password. However, within the #auth.verify_password callback, they are both both empty.
I have a fair bit of unit tests around the server portion of the code, and the auth information appears to be present in all of them, so it is reassuring that I can at least get the username and password from within the header in some instances.
Additionally, I have added the CORS extension to the server code, and allow it server wide. It appears that an OPTIONS(which always returns 200) to the below url is always called immediately before the GET(returns 401, due to username and password issue) to the same url.
Reference code:
Server auth callback:
#app.route('/api/users/token')
#auth.login_required
def get_auth_token():
token = g.user.generate_auth_token()
return jsonify({ 'token': token.decode('ascii') })
#auth.verify_password
def verify_password(email_or_token, password):
print 'email_or_token: ' + email_or_token
print 'password: ' + password
...
Server Unit test code behaving as expected:
def _get_user_token(self, email=TEST_EMAIL, password=TEST_PASSWORD):
headers = {
'Authorization': 'Basic ' + b64encode("{0}:{1}".format(email, password))
}
response = self.app.get('/api/users/token', headers=headers)
return response
AngularJS code which yields appropriate header when inspected in browser but empty username and password in auth callback:
$http.get(silkyAppConstants.BASE_URL + '/api/users/token', {
headers: { "Authorization": "Basic " + username + ":" + password }
})
I suspect your problem is that you are sending an invalid Authorization header. The username + ":" + password portion of the header must be base64 encoded (see Section 2 of RFC 2617. When Flask receives the plain text credentials that you are sending it is trying pass it through a base64 decoder and that fails.

Resources