Apache Camel -- Websphere MQ integration - apache-camel

I have an application using apache-camel solution, and would like to send message to Websphere MQ Server through jms, convert jms property JMS_IBM_MQMD_MsgId to MQMD field MQMD.MsgId, so that I set this value on message through camel
exchange.getIn().setHeader(WMQConstants.JMS_IBM_MQMD_MSGID, "XXXXXXXXXXXXXXXXXXXXXXXX".getBytes());
According to Apache Camel - IBM MQ integration, seems we need another properties setting on destination object. Reference Setting JMS Provider Options on the Destination on http://camel.apache.org/jms.html, I provide a custom DestinationResolver for camel jms component, set mdWriteEnabled, mdReadEnabled for destination object.
<bean id="ibmMQServer01" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="ibmMQCredentialConnectionFactory01" />
<property name="destinationResolver" ref="wmqDestinationResolver" />
</bean>
and
public class WMQDestinationResolver implements DestinationResolver {
#Override
public Destination resolveDestinationName(Session session, String destinationName,
boolean pubSubDomain) throws JMSException {
MQSession mqSession = (MQSession) session;
MQQueue queue = (MQQueue) mqSession.createQueue("queue:///" + destinationName);
queue.setBooleanProperty(WMQConstants.WMQ_MQMD_WRITE_ENABLED, true);
queue.setBooleanProperty(WMQConstants.WMQ_MQMD_READ_ENABLED, true);
queue.setIntProperty(WMQConstants.WMQ_MQMD_MESSAGE_CONTEXT, WMQConstants.WMQ_MDCTX_SET_ALL_CONTEXT);
return queue;
}
}
I can get JMS_IBM_MQMD_MsgId on receiver while setting mdReadEnabled equals true. However, mdWriteEnabled seems not works for me, and I get JMS_IBM_MQMD_MsgId as an unexpected value AMQ CS.QA.CBSA.Q�Y�b (been parsed from byte[], totally 24 bytes).
The received JMSMessageID is ID:414d512043532e51412e434253412e511987055902cc6222, which can be parsed to the messy string above.

I drill down camel code and find the root casue
While setting jms property, it will run method getValidJMSHeaderValue of org.apache.camel.component.jms.JmsBinding
protected Object getValidJMSHeaderValue(String headerName, Object headerValue) {
if (headerValue instanceof String) {
return headerValue;
} else if (headerValue instanceof BigInteger) {
return headerValue.toString();
} else if (headerValue instanceof BigDecimal) {
return headerValue.toString();
} else if (headerValue instanceof Number) {
return headerValue;
} else if (headerValue instanceof Character) {
return headerValue;
} else if (headerValue instanceof CharSequence) {
return headerValue.toString();
} else if (headerValue instanceof Boolean) {
return headerValue;
} else if (headerValue instanceof Date) {
return headerValue.toString();
}
return null;
}
Seems camel reject byte array value and return null, so that jms provider cannot apply property of JMS_IBM_MQMD_MsgId. I override this method to sovle it.
Note: I simply create the same class org.apache.camel.component.jms.JmsBinding in source folder src/main/java, class loader would default load this class instead of the class from maven library.

I can get JMS_IBM_MQMD_MsgId on receiver while setting mdReadEnabled
equals true. However, mdWriteEnabled seems not works for me, and I get
JMS_IBM_MQMD_MsgId as an unexpected value "AMQ CS.QA.CBSA.Q�Y�b""
(been parsed from byte[], totally 24 bytes).
The received JMSMessageID is
"ID:414d512043532e51412e434253412e511987055902cc6222", which can be
parsed to the messy string above.
What you are seeing is correct. The MsgId is a byte array of 24 bytes. It is made up of BOTH string and binary values. Hence, you CANNOT use it as a String.

Related

Why does my apache camel split/aggregate route return no results?

I'm trying to read a binary file, convert it into a pojo format and then output as CSV. The unmarshalling (and marshalling) seems to be fine, but I'm having trouble optimising the converting the relevant records to Foo.class. The attempt below returns no results.
from(String.format("file://%s?move=%s", INPUT_DIRECTORY, MOVE_DIRECTORY))
.unmarshal(unmarshaller)
.split(bodyAs(Iterator.class), new ListAggregationStrategy())
.choice()
.when(not(predicate)).stop()
.otherwise().convertBodyTo(Foo.class)
.end()
.end()
.marshal(csv)
.to(String.format("file://%s?fileName=${header.CamelFileName}.csv", OUTPUT_DIRECTORY));
I was able to get it to work like this, but it feels like there has to be a better way - This will be need to be efficient, and having a 1s timeout feels like it goes against that, which is why I was attempting to use the built in split aggregation. Alternatively some way of using completionFromBatchConsumer, but I was struggling to make that work either!.
from(String.format("file://%s?move=%s", INPUT_DIRECTORY, MOVE_DIRECTORY))
.unmarshal(unmarshaller)
.split(bodyAs(Iterator.class))
.streaming()
.filter(predicate)
.convertBodyTo(Foo.class)
.aggregate(header("CamelFileName"), new ListAggregationStrategy())
.completionTimeout(1000)
.marshal(csv)
.to(String.format("file://%s?fileName=${header.CamelFileName}.csv", OUTPUT_DIRECTORY));
You could create your own AggregationStrategy in your first solution.
Instead of calling stop() in your choice statement, put a simple header like "skipMerge" to true.
In your strategy, test if this header exists and if so, skip it.
class ArrayListAggregationStrategy implements AggregationStrategy {
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
Object newBody = newExchange.getIn().getBody();
Boolean skipMerge = newExchange.getIn().getHeader("skipMerge", Boolean.class);
if (!skipMerge) { return oldExchange; }
ArrayList<Object> list = null;
if (oldExchange == null) {
list = new ArrayList<Object>();
list.add(newBody);
newExchange.getIn().setBody(list);
return newExchange;
} else {
list = oldExchange.getIn().getBody(ArrayList.class);
list.add(newBody);
return oldExchange;
}
}
}
Currently, your code never goes to marshal(csv) because the aggregator does not receive all the splitted parts.

jose4j: how to set full serialization input?

Is there a way to set a JWE full serialization input with jose4j? For example, what goes in the TODO below?
public String decryptJWE(PrivateKey privateKey, String payload, boolean compact) throws JoseException {
JsonWebEncryption jwe = new JsonWebEncryption();
if (compact) {
jwe.setCompactSerialization(payload);
} else {
// TODO: what goes here? expecting something like jwe.setFullSerialization(payload)
}
jwe.setKey(privateKey);
return jwe.getPayload();
}
No, only the JWE compact serialization is supported. The general and flattened JWE JSON serializations aren't directly supported.

I can't unmarshal basic string from akka http stream

I am trying to consume a stream of json objects in akka-http. ( akka http version "10.0.9", akka-http-play-json version 1.10.1)
I follow examples on web but, for String I am getting:
could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromByteStringUnmarshaller[String]
and for my user defined Foo case class (for which I provided the json protocol):
could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromByteStringUnmarshaller[server.Foo]
This is the code that is simplified. I provide a EntityStreamingSupport.json() and for the Foo object a Json Format. I don't think I need one for String. If I don't put the asSourceOf and read a simple String object or a Foo case class object the code works. What am I missing?
package server
import akka.http.scaladsl.common.EntityStreamingSupport
import akka.http.scaladsl.server.{ Directives, Route }
import akka.http.scaladsl.model.StatusCodes
import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._
import play.api.libs.json._
case class Foo(bar: String)
class StreamingMarketDataUpload extends Directives {
implicit val jsonStreamingSupport = EntityStreamingSupport.json()
implicit val jsonFooFormat = Json.format[Foo]
lazy val routes: Route =
path("upload1") {
post {
entity(as[String]) { input =>
complete(StatusCodes.OK)
}
}
} ~ path("upload2") {
post {
// Compile error here
entity(asSourceOf[String]) { marks =>
complete(StatusCodes.OK)
}
}
} ~ path("upload3") {
post {
entity(as[Foo]) { input =>
complete(StatusCodes.OK)
}
}
} ~ path("upload4") {
post {
// Compile error here
entity(asSourceOf[Foo]) { marks =>
complete(StatusCodes.OK)
}
}
}
}
asSourceOf[T] tries to consume the incoming data as a Source[T, _]. As its method signature indicates, asSourceOf[T] takes an implicit FromByteStringUnmarshaller[T] parameter. The de.heikoseeberger.akkahttpplayjson.PlayJsonSupport utility doesn't provide an implementation of this unmarshaller (as of version 1.19.0), but it does provide the unmarshallers necessary for consuming a simple String or Foo. This is why your code works when it's not using asSourceOf.
The examples in the documentation use SprayJsonSupport, which is shipped as part of Akka HTTP. If you don't want to use SprayJsonSupport, you'll have to implement a FromByteStringUnmarshaller to use asSourceOf: the linked source code can give you an idea of how to do this.

Aggregation and filtering through the consumer template

This is more a general, whats the best practice question...
I have a few processes where the consumer template has been used to read a directory (or a MQ queue) for whatever is available and then stop itself, the entire route-set it calls is created programmatically based of a few parameters
So using the consumer template method below... Is there a way to assign
A filter operation programmatically (ie, if i want to filter out certain files from the below, its easy if its through a standard route... (through .filter) but at the moment, i have no predefined beans, so adding #filter=filter to the EIP is not really an option).
An aggregation function from inside my while loop. (while still using the template).
#Override
public void process(Exchange exchange) throws Exception {
getConsumer().start();
int exchangeCount = 0;
while (true) {
String consumerEp = "file:d://directory?delete=true&sendEmptyMessageWhenIdle=true&idempotent=false";
Exchange fileExchange = getConsumer().receive(consumerEp);
if (fileExchange == null || fileExchange.getIn()==null || fileExchange.getIn().getHeader(CAMEL_FILE_NAME)==null) {
break;
}
exchangeCount++;
Boolean batchStatus = (Boolean) fileExchange.getProperty(PROP_CAMEL_BATCH_COMPLETE);
LOG.info("---PROCESSING : " + fileExchange.getIn().getHeader(CAMEL_FILE_NAME));
getProducer().send("direct:some-other-process", fileExchange);
//Get the CamelBatchComplete Property to establish the end of the batch, and not cycle through twice.
if(batchStatus!=null && batchStatus==true){
break;
}
}
// Stop the consumer service
getConsumer().stop();
LOG.info("End Group Operation : Total Exchanges=" + exchangeCount);
}

How to extract the web service method name in mule <cxf:proxy-service>

I am working with mule <cxf:proxy-service> and need to extract the web service method name to attach to message for later use.
We've a service proxy class implementing Callable interface. Initially we tried to get operation name like this:
public Object onCall(MuleEventContext eventContext) throws Exception {
try {
MuleMessage inboundMessage = eventContext.getMessage();
Set<String> props = inboundMessage.getInvocationPropertyNames();
System.out.println("CXF invocation properties ==> " + props);
System.out.println("CXF invocation property ==> " + inboundMessage.getInvocationProperty("cxf_operation"));
but the above code gives incorrect operation name. (We've 4 operations in service and it always give the 2nd operation name). Below is the mule flow used for this:
<flow name="proxyService">
<http:inbound-endpoint address="${some.address}"
exchange-pattern="request-response">
<cxf:proxy-service wsdlLocation="classpath:abc.wsdl"
namespace="http://namespace"
service="MyService">
</cxf:proxy-service>
</http:inbound-endpoint>
<component class="com.services.MyServiceProxy" />
So, I resorted to write an inbound cxf interceptor to extract the operation name. I wrote below interceptor which works fine with <cxf:jaxws-service> but not with <cxf:proxy-service> element.
Here is my interceptor:
public class GetCXFOperation extends AbstractPhaseInterceptor<Message> {
public GetCXFOperation() {
super(Phase.PRE_INVOKE);
}
#Override
public void handleMessage(Message message) throws Fault {
Exchange exchange = message.getExchange();
Endpoint ep = exchange.get(Endpoint.class);
OperationInfo op = exchange.get(OperationInfo.class);
if(op != null){
System.out.println("Operation Name: " + op.getName().getLocalPart());
} else{
Object nameProperty = exchange.get("org.apache.cxf.resource.operation.name");
if(nameProperty != null)
System.out.println(nameProperty.toString());
}
}
}
Seeking guidance as to how to extract operation name in <cxf:proxy-service>? Is there an easy mule way of getting correct answer? Or is there a different phase in which I should be invoking my interceptor? What phases work with <cxf:proxy-service>

Resources