Does anyone know how to test for different types of Collection in a route?
// This processor returns a Collection of 2 Sets
// 1. Set<GoodMessage>
// 2. Set<BadMessage>
.process(new MyGoodBadMessageProcessor())
// Split the result List
.split(body()).choice()
// How do you test for a Set<GoodMessage>??
.when(body().isInstanceOf(Set<GoodMessage>)
.to("direct:good")
.otherwise()
.to("direct:bad")
.endChoice()
Background: (In case someone can see a better way of doing this) I have a Processor that currently works as follows:
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Set<UnknownMessage> unknownMessages = message.getBody(Set.class);
Set<GoodMessage> goodMessages = new HashSet<GoodMessage>();
for(UnknownMessage msg: unknownMessages) {
// Simplified logic here
if (msg.isGood()) {
goodMessages.add(msg.getGoodMessage());
}
}
message.setBody(goodMessages);
}
I'd like to update this as to now include the BadMessage(s) for reporting:
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Set<UnknownMessage> unknownMessages = message.getBody(Set.class);
Set<GoodMessage> goodMessages = new HashSet<GoodMessage>();
Set<BadMessage> badMessages = new HashSet<BadMessage>();
List result = new ArrayList();
for(UnknownMessage msg: unknownMessages) {
// Simplified logic here
if (msg.isGood()) {
goodMessages.add(msg.getGoodMessage());
} else {
badMessages.add(msg.getBadMessage());
}
}
result.add(goodMessages)
result.add(badMessages)
message.setBody(result);
}
You cannot get the type of collection in this way (nothing to do with camel).
The way you've updated your process method does not need creating a different end point for bad messages.
One possible way to send this to a different end point based on message type is add a processor before the choice which inspects the type of the message and adds a header. Your choice statement can then work based on this header.
The following Predicate would work, although might give incorrect results when the Set is empty :/
Public class IsGoodMessage implements Predicate {
#Override
public boolean matches(Exchange exchange) {
Message message = exchange.getIn();
Set unknownSet = message.getBody(Set.class);
for (Object o : unknownSet) {
if (o instanceof GoodMessage) {
return true;
} else {
return false;
}
}
return false;
}
}
This helped:
How do I find out what type each object is in a ArrayList<Object>?
UPDATE: After some further reading, a better way to do this is to use a Header/Property to help distinguish the message type.
STEP 1: Update Processor to produce a Map that identifies different message types.
"GOOD_MSGS" -> List<GoodMessage>
"BAD_MSGS" -> List<BadMessage>
STEP 2: Create a splitter bean that splits this Map and then creates a header using the key of Map from the previous step.
(see "splitMessage" here http://camel.apache.org/splitter.html)
STEP 3: In the route use these headers to route the messages accordingly
Related
The class
public class Details {
private String name;
private String id;
//more attributes
}
I have a method which takes the http request and write an xml file based on the attribute the request wants (basically exporting specific fields that user wants).
Http Request body,
{
"isNameRequired": true,
"isIdRequired": true,
"isAttrRequired": false
// and other params to for filtering
}
Eg -> if the request wants only 'name' and 'id' values, the request will have booleans such as isNameRequired and IsIdRequired.
Based on this booleans my condition would look like
if (isNameRequired) {
// write name on file
}
if (isIdRequired) {
// write id on file
}
This is just an example. The actual attributes I want to write is more than 50 and I'm having that many booleans and multiple if checks to write.
Note : The entire process can happen n number of times.
There is list of Details (size > 100) which I need to loop through and write a xml based on users request.
If the user only needs name and id field, my xml should look like
<info>
<detail1>
<name>somename</name>
<id>someid</id>
</detail1>
<detail2>
<name>somename</name>
<id>someid</id>
</detail2>
</info>
like wise, if the request needs only name,
<info>
<detail1>
<name>somename</name>
</detail1>
<detail2>
<name>somename</name>
</detail2>
</info>
My question is, is this approach correct or is there a better way to handle this situation?
Well, it depends on your code but you could have some form of request handler that operates on a single requested element (e.g. "name"). If that fits your needs you could maintain a map of those handlers, get the ones you need and use them.
I'll add a simple example and assume your requests contain a number of strings for what's needed (if it's different I'll leave the conversion to you :) ):
interface RequestedElementHandler {
String getHandledElementName();
void handleElement(File xmlFile); //add anything else as needed
}
class NameHandler implements RequestedElementHandler {
public String getHandledElementName() { return "name"; }
public void handleElement(File xmlFile) {
//write name to file
}
}
And when handling your request:
Collection<String> requestedElements = ...;
//just an example, maintain a reusable map in a real world example
Map<String, RequestedElementHandler> handlers = new HashMap<>();
//you could use handlers.put("name", new NameHandler()) but doing it that way lets the handler define what elements it will work on
NameHandler nameHandler = new NameHandler();
handlers.put(nameHandler.getHandledElementName(), nameHandler());
//add other handlers as needed
for( String element : requestedElements ) {
RequestedElementHandler handler = handlers.get(element);
if( handler != null ) {
handler.handle(xmlFile);
}
}
Update:
Since your request body actually contains boolean properties you could modifiy the above approach as follows:
class NameHandler implements RequestedElementHandler {
//rename in interface
public String getPredicateName() { return "isNameRequired"; }
}
And when processing the request:
//extract the parameters from the request - how depends on whether you have
// - a ServletRequest: getParameterMap()
// - a Json object: try Jackon's ObjectMapper.readTree() and extract property names and values from the generic json object you get
// - anything else: can't tell without knowing but you should be able to do some research
Map<String, Boolean> parameters = ...
for( Map.Entry<String, Boolean> parameterEntry : parameters.entrySet() ) {
//reject FALSE and null
if( !Boolean.TRUE.equals(parameterEntry.getValue()) {
continue;
}
RequestedElementHandler handler = handlers.get(parameterEntry.getKey());
//use handler
}
Alternative:
Maintain a list of RequestHandler elements which all get the request and act on it if needed.
Example:
interface RequestHandler {
void handleRequest(Request r, File xmlFile);
}
class NameHandler implements RequestHandler {
void handleRequest(Request r, File xmlFile) {
if( !r.isNameRequired() ) {
return; //request doesn't require the name so skip
}
//add name to xml file
}
}
Usage:
Request request = ... //your request object
File xmlFile = ... //the file you want to write to
List<RequestHandler> handlers = ... //get from somewhere
handlers.forEach(handler -> handler.handleRequest(request, xmlFile));
I am coding a server application that will receive DFT_P03 messages with an added ZPM segment (which i have created a class for as per the HAPI documentation). Currently i am able to access this field as a generic segment when doing the following :
#Override
public Message processMessage(Message t, Map map) throws ReceivingApplicationException, HL7Exception
{
String encodedMessage = new DefaultHapiContext().getPipeParser().encode(t);
logEntryService.logDebug(LogEntry.CONNECTIVITY, "Received message:\n" + encodedMessage + "\n\n");
try
{
InboundMessage inboundMessage = new InboundMessage();
inboundMessage.setMessageTime(new Date());
inboundMessage.setMessageType("Usage");
DFT_P03 usageMessage = (DFT_P03) t;
Segment ZPMSegment = (Segment)usageMessage.get("ZPM");
inboundMessage.setMessage(usageMessage.toString());
Facility facility = facilityService.findByCode(usageMessage.getMSH().getReceivingFacility().getNamespaceID().getValue());
inboundMessage.setTargetFacility(facility);
String controlID = usageMessage.getMSH().getMessageControlID().encode();
controlID = controlID.substring(controlID.indexOf("^") + 1, controlID.length());
inboundMessage.setControlId(controlID);
Message response;
try
{
inboundMessageService.save(inboundMessage);
response = t.generateACK();
logEntryService.logDebug(LogEntry.CONNECTIVITY, "Message ACKed");
}
catch (Exception ex)
{
response = t.generateACK(AcknowledgmentCode.AE, new HL7Exception(ex));
logEntryService.logDebug(LogEntry.CONNECTIVITY, "Message NACKed");
}
return response;
}
catch (IOException e)
{
logEntryService.logDebug(LogEntry.CONNECTIVITY, "Message rejected");
throw new HL7Exception(e);
}
}
I have created a DFT_P03_Custom class as following :
public class DFT_P03_Custom extends DFT_P03
{
public DFT_P03_Custom() throws HL7Exception
{
this(new DefaultModelClassFactory());
}
public DFT_P03_Custom(ModelClassFactory factory) throws HL7Exception
{
super(factory);
String[] segmentNames = getNames();
int indexOfPid = Arrays.asList(segmentNames).indexOf("FT1");
int index = indexOfPid + 1;
Class<ZPM> type = ZPM.class;
boolean required = true;
boolean repeating = false;
this.add(type, required, repeating, index);
}
public ZPM getZPM()
{
return getTyped("ZPM", ZPM.class);
}
}
When trying to typecast the message to a DFT_P03_Custom instance i get a ClassCastException. As per their documentation, i did create the CustomModelClassFactory class but using this i just get tons of validation errors on the controlId field.
I am already using an identical logic to send custom MFN_M01 messages with an added ZFX segment and that works flawlessly. I understand there is some automatic typecasting being done by HAPI when it receives a DFT_P03 message and that is likely what i need to somehow override for it to be able to give me a DFT_P03_Custom instance instead.
If you have some insight on how i can achieve this without having to use a generic segment instance please help!
Thank you!
I finally figured this out. The only way i got this to work was to generate a conformance profile XML file (using an example message from our application as a base) with the messaging workbench on the HAPI site and use the maven plugin to generate the message and segment classes. Only with these classes am i able to correctly parse a message to my custom class. One thing to note is that it DOES NOT work if i try to use the MSH, PID, PV1 or FT1 classes provided by HAPI and use my Z-segment class. It only works if all the segments are the classes generated by the conformance plugin. This combined with a CustomModelClassFactory class (as shown on the HAPI website) and the proper package structure finally allowed me to access my Z-segment.
I have an app. I have a big button that allows the user to sync all their data at once to the cloud. A re-sync feature that allows them to send all their data again. (300+ entries)
I am using RXjava2 and retrofit2. I have my unit test working with a single call. However I need to make N network calls.
What I want to avoid is having the observable call the next item in a queue. I am at the point where I need to implement my runnable. I have seen a bit about Maps but I have not seen anyone use it as a queue. Also I want to avoid having one item fail and it report back as ALL items fail, like the Zip feature would do. Should I just do the nasty manager class that keeps track of a queue? Or is there a cleaner way to send several hundred items?
NOTE: SOLUTION CANNOT DEPEND ON JAVA8 / LAMBDAS. That has proved to be way more work than is justified.
Note all items are the same object.
#Test
public void test_Upload() {
TestSubscriber<Record> testSubscriber = new TestSubscriber<>();
ClientSecureDataToolKit clientSecureDataToolKit = ClientSecureDataToolKit.getClientSecureDataKit();
clientSecureDataToolKit.putUserDataToSDK(mPayloadSecureDataToolKit).subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
testSubscriber.assertValueCount(1);
testSubscriber.assertCompleted();
}
My helper to gather and send all my items
public class SecureDataToolKitHelper {
private final static String TAG = "SecureDataToolKitHelper";
private final static SimpleDateFormat timeStampSimpleDateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void uploadAll(Context context, RuntimeExceptionDao<EventModel, UUID> eventDao) {
List<EventModel> eventModels = eventDao.queryForAll();
QueryBuilder<EventModel, UUID> eventsQuery = eventDao.queryBuilder();
String[] columns = {...};
eventsQuery.selectColumns(columns);
try {
List<EventModel> models;
models = eventsQuery.orderBy("timeStamp", false).query();
if (models == null || models.size() == 0) {
return;
}
ArrayList<PayloadSecureDataToolKit> toSendList = new ArrayList<>();
for (EventModel eventModel : models) {
try {
PayloadSecureDataToolKit payloadSecureDataToolKit = new PayloadSecureDataToolKit();
if (eventModel != null) {
// map my items ... not shown
toSendList.add(payloadSecureDataToolKit);
}
} catch (Exception e) {
Log.e(TAG, "Error adding payload! " + e + " ..... Skipping entry");
}
}
doAllNetworkCalls(toSendList);
} catch (SQLException e) {
e.printStackTrace();
}
}
my Retrofit stuff
public class ClientSecureDataToolKit {
private static ClientSecureDataToolKit mClientSecureDataToolKit;
private static Retrofit mRetrofit;
private ClientSecureDataToolKit(){
mRetrofit = new Retrofit.Builder()
.baseUrl(Utilities.getSecureDataToolkitURL())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static ClientSecureDataToolKit getClientSecureDataKit(){
if(mClientSecureDataToolKit == null){
mClientSecureDataToolKit = new ClientSecureDataToolKit();
}
return mClientSecureDataToolKit;
}
public Observable<Record> putUserDataToSDK(PayloadSecureDataToolKit payloadSecureDataToolKit){
InterfaceSecureDataToolKit interfaceSecureDataToolKit = mRetrofit.create(InterfaceSecureDataToolKit.class);
Observable<Record> observable = interfaceSecureDataToolKit.putRecord(NetworkUtils.SECURE_DATA_TOOL_KIT_AUTH, payloadSecureDataToolKit);
return observable;
}
}
public interface InterfaceSecureDataToolKit {
#Headers({
"Content-Type: application/json"
})
#POST("/api/create")
Observable<Record> putRecord(#Query("api_token") String api_token, #Body PayloadSecureDataToolKit payloadSecureDataToolKit);
}
Update. I have been trying to apply this answer to not much luck. I am running out of steam for tonight. I am trying to implement this as a unit test, like I did for the original call for one item.. It looks like something is not right with use of lambda maybe..
public class RxJavaBatchTest {
Context context;
final static List<EventModel> models = new ArrayList<>();
#Before
public void before() throws Exception {
context = new MockContext();
EventModel eventModel = new EventModel();
//manually set all my eventmodel data here.. not shown
eventModel.setSampleId("SAMPLE0");
models.add(eventModel);
eventModel.setSampleId("SAMPLE1");
models.add(eventModel);
eventModel.setSampleId("SAMPLE3");
models.add(eventModel);
}
#Test
public void testSetupData() {
Assert.assertEquals(3, models.size());
}
#Test
public void testBatchSDK_Upload() {
Callable<List<EventModel> > callable = new Callable<List<EventModel> >() {
#Override
public List<EventModel> call() throws Exception {
return models;
}
};
Observable.fromCallable(callable)
.flatMapIterable(models -> models)
.flatMap(eventModel -> {
PayloadSecureDataToolKit payloadSecureDataToolKit = new PayloadSecureDataToolKit(eventModel);
return doNetworkCall(payloadSecureDataToolKit) // I assume this is just my normal network call.. I am getting incompatibility errors when I apply a testsubscriber...
.subscribeOn(Schedulers.io());
}, true, 1);
}
private Observable<Record> doNetworkCall(PayloadSecureDataToolKit payloadSecureDataToolKit) {
ClientSecureDataToolKit clientSecureDataToolKit = ClientSecureDataToolKit.getClientSecureDataKit();
Observable observable = clientSecureDataToolKit.putUserDataToSDK(payloadSecureDataToolKit);//.subscribe((Observer<? super Record>) testSubscriber);
return observable;
}
Result is..
An exception has occurred in the compiler (1.8.0_112-release). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for java.lang.invoke.MethodType not found
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compile<MyBuildFlavorhere>UnitTestJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
Edit. No longer trying Lambdas. Even after setting up the path on my mac, javahome to point to 1.8, etc. I could not get it to work. If this was a newer project I would push harder. However as this is an inherited android application written by web developers trying android, it is just not a great option. Nor is it worth the time sink to get it working. Already into the days of this assignment instead of the half day it should have taken.
I could not find a good non lambda flatmap example. I tried it myself and it was getting messy.
If I understand you correctly, you want to make your calls in parallel?
So rx-y way of doing this would be something like:
Observable.fromCallable(() -> eventsQuery.orderBy("timeStamp", false).query())
.flatMapIterable(models -> models)
.flatMap(model -> {
// map your model
//avoid throwing exceptions in a chain, just return Observable.error(e) if you really need to
//try to wrap your methods that throw exceptions in an Observable via Observable.fromCallable()
return doNetworkCall(someParameter)
.subscribeOn(Schedulers.io());
}, true /*because you don't want to terminate a stream if error occurs*/, maxConcurrent /* specify number of concurrent calls, typically available processors + 1 */)
.subscribe(result -> {/* handle result */}, error -> {/* handle error */});
In your ClientSecureDataToolKit move this part into constructor
InterfaceSecureDataToolKit interfaceSecureDataToolKit = mRetrofit.create(InterfaceSecureDataToolKit.class);
I'm newbie to Apache Camel. In hp nonstop there is a Receiver that receives events generated by event manager assume like a stream. My goal is to setup a consumer end point which receives the incoming message and process it through Camel.
Another end point I simply need to write it in logs. From my study I understood that for Consumer end point I need to create own component and configuration would be like
from("myComp:receive").to("log:net.javaforge.blog.camel?level=INFO")
Here is my code snippet which receives message from event system.
Receive receive = com.tandem.ext.guardian.Receive.getInstance();
byte[] maxMsg = new byte[500]; // holds largest possible request
short errorReturn = 0;
do { // read messages from $receive until last close
try {
countRead = receive.read(maxMsg, maxMsg.length);
String receivedMessage=new String(maxMsg, "UTF-8");
//Here I need to handover receivedMessage to camel
} catch (ReceiveNoOpeners ex) {
moreOpeners = false;
} catch(Exception e) {
moreOpeners = false;
}
} while (moreOpeners);
Can someone guide with some hints how to make this as a Consumer.
The 10'000 feet view is this:
You need to start out with implementing a component. The easiest way to get started is to extend org.apache.camel.impl.DefaultComponent. The only thing you have to do is override DefaultComponent::createEndpoint(..). Quite obviously what it does is create your endpoint.
So the next thing you need is to implement your endpoint. Extend org.apache.camel.impl.DefaultEndpoint for this. Override at the minimum DefaultEndpoint::createConsumer(Processor) to create your own consumer.
Last but not least you need to implement the consumer. Again, best ist to extend org.apache.camel.impl.DefaultConsumer. The consumer is where your code has to go that generates your messages. Through the constructor you receive a reference to your endpoint. Use the endpoint reference to create a new Exchange, populate it and send it on its way along the route. Something along the lines of
Exchange ex = endpoint.createExchange(ExchangePattern.InOnly);
setMyMessageHeaders(ex.getIn(), myMessagemetaData);
setMyMessageBody(ex.getIn(), myMessage);
getAsyncProcessor().process(ex, new AsyncCallback() {
#Override
public void done(boolean doneSync) {
LOG.debug("Mssage was processed " + (doneSync ? "synchronously" : "asynchronously"));
}
});
I recommend you pick a simple component (DirectComponent ?) as an example to follow.
Herewith adding my own consumer component may help someone.
public class MessageConsumer extends DefaultConsumer {
private final MessageEndpoint endpoint;
private boolean moreOpeners = true;
public MessageConsumer(MessageEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.endpoint = endpoint;
}
#Override
protected void doStart() throws Exception {
int countRead=0; // number of bytes read
do {
countRead++;
String msg = String.valueOf(countRead)+" "+System.currentTimeMillis();
Exchange ex = endpoint.createExchange(ExchangePattern.InOnly);
ex.getIn().setBody(msg);
getAsyncProcessor().process(ex, new AsyncCallback() {
#Override
public void done(boolean doneSync) {
log.info("Mssage was processed " + (doneSync ? "synchronously" : "asynchronously"));
}
});
// This is an echo server so echo request back to requester
} while (moreOpeners);
}
#Override
protected void doStop() throws Exception {
moreOpeners = false;
log.debug("Message processor is shutdown");
}
}
I'm looking for some ideas on implementing a basic message factory that reads a header from an input stream and creates the appropriate message type based on the type defined in the message header.
So I have something like (roughly.. and I'm willing to change the design if a better paradigm is presented here)
class MessageHeader {
public String type;
}
class MessageA extends Message {
public static final String MESSAGE_TYPE = "MSGA";
public MessageA (DataInputStream din) {
var1 = din.readInt ();
var2 = din.readInt ()
// etc
}
}
and I essentially want to do something like this:
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
return new MessageA (din);
} else if (header.type == MessageB.MESSAGE_TYPE) {
return new MessageB (din);
}
Although this scheme works I feel like there could be a better method using a Map and an Interface somehow...
public interface MessageCreator {
public Message create (DataInputStream);
}
Map <String, MessageCreater> factory = new Map <String, MessageCreator> ();
factory.put (MessageTypeA.MESSAGE_TYPE, new MessageCreator () {
public Message create (DataInputStream din) {
return new MessageA (din); }});
...
// Read message header
Message createdMessage = Map.get (header.type).create (din);
But then whenever I want to use the message I have to use instanceof and cast to the correct subclass.
Is there a 3rd (better?) option? Maybe there's a way to accomplish this using templates. Any help is appreciated. Thanks
Edit: I guess it's important to note I want to "dispatch" the message to a function. So essentially I really want to do this:
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
handleMessageA (new MessageA (din));
} else if (header.type == MessageB.MESSAGE_TYPE) {
handleMessageB (new MessageB (din))
}
So a pattern that incorporates the factory and a dispatch would be perfect
How about letting the guy who creates the messages actually dispatch to a handler.
So you'd add a handler interface like this:
public interface MessageHandler {
void handleTypeA(MessageA message);
void handleTypeB(MessageB message);
}
Then you'd have a dispatcher which is basically the same thing as your MessageCreator, except it calls the correct method on the handler instead of returning the message object.
public interface MessageDispatcher {
void createAndDispatch(DataInputStream input, MessageHandler handler);
}
The implementation is then almost identical to the first code snippet you posted:
public void createAndDispatch(DataInputStream input, MessageHandler handler) {
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
handler.handleTypeA(new MessageA (din));
} else if (header.type == MessageB.MESSAGE_TYPE) {
handler.handleTypeB(new MessageB (din));
}
}
Now you only have the one spot in the code where you have to do a switch or if/else if and after that everything is specifically typed and there's no more casting.