Use a file as input to unit tests - apache-camel

I think I have seen an elegant way to use a file as input for a unit test in apache camel but my google skills are failing me.
What I want is instead of:
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
<snip>...long real life xml that quickly fills up test files.</snip>";
template.sendBody("direct:create", xml);
What I think I have seen is something like
template.sendBody("direct:create", someCamelMetod("/src/data/someXmlFile.xml"));
Anybody knows where/if this is documented?
Edit:
Cross posted to http://camel.465427.n5.nabble.com/Use-a-file-as-input-to-unit-tests-td5722069.html
What I ended up doing was just creating a
private void readFile(String fileName) throws ... function
Still interested if anyone knows a nicer way though.

If I understand your question correctly you want to send an xml file as input to the route you want to test. My solution would be to use the adviseWith strategy which is a part of the Camel test support. Read about it here: http://camel.apache.org/testing.html
So, say that the route under test is something like this:
from("jms:myQueue")
.routeId("route-1")
.beanRef(myTransformationBean)
.to("file:outputDirectory");
in your test you can send xml into this route by replacing this from a file polling consumer.
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceRouteFromWith("route-1", "file:myInputDirectory");
}
});
context.start();
Then you can put your input xml file in the myInputDirectory and it will be picket up and used as input to the route.

Not really, you have to do some minor work yourself. You know, reading a text file isn't that simple since you might want to know the encoding. In your first case (inline string), you're always using UTF-16. A file can be whatever, and you have to know it, since it won't tell you what encoding it is. Given you have UTF-8, you can do something like this:
public String streamToString(InputStream str){
Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
if (scanner.hasNext())
return scanner.next();
return "";
}
// from classpath using ObjectHelper from Camel.
template.sendBody("direct:create", streamToString(ObjectHelper.loadResourceAsStream("/src/data/someXmlFile.xml")));

Related

Use string as Apache Camel endpoint?

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();

Camel: how to aggregate files based on exchange in pattern

I have a class to run my route; The input comes from a queue (which is filled by a route that does a query and inserts the rows as messages on the queue)
These messages each contain a few headers:
- pdu_id, basically a prefetch on the filename.
- pad: the path the files reside in
What is to happen: I want the files in the path named by their "pdu_id".* in a tar; After that a REST call is to be done to remove the documents source.
I know a route has a from; but basically I need a route with a dynamic "from", and as below code example shows, queueing froms doesn't do the trick.
The question is what to use instead; I could not find a similar thing, but it can be I didn't use the right google search; in which case I'm deeply sorry.
public class ToDeleteTarAndDeleteRoute extends RouteBuilder {
#Override
public void configure() throws Exception
{
from("broker1:todelete.message_ids.queue")
.from("file:///?fileName=${in.header.pad}${in.header.pdu_id}.*")
.aggregate(new TarAggregationStrategy())
.constant(true)
.completionFromBatchConsumer()
.eagerCheckCompletion()
.to("file:///?fileName=${in.header.pad}${in.header.pdu_id}.tar")
.log("${header.pdu_id} tarred")
.setHeader(Exchange.HTTP_METHOD, constant("DELETE"))
.setHeader("Connection", constant("Close"))
.enrich()
.simple("http:127.0.0.1/restfuldb${header.pdu_id}?httpClient.authenticationPreemptive=true")
.log("${header.pdu_id} tarred and deleted.");
}
}
Yes. Poll enrich can help you in doing it. You should use it something like this:
from("broker1:todelete.message_ids.queue")
.aggregationStrategy(new TarAggregationStrategy())
.pollEnrich()
.simple("file:///?fileName=${in.header.pad}/${in.header.pdu_id}.*")
.unmarshal().string()
.to("file:///?fileName=${in.header.pad}/${in.header.pdu_id}.tar")
.log("${header.pdu_id} tarred")
.setHeader(Exchange.HTTP_METHOD, constant("DELETE"))
.setHeader("Connection", constant("Close"))
.enrich()
.simple("http:127.0.0.1/restfuldb${header.pdu_id}?httpClient.authenticationPreemptive=true")
.log("${header.pdu_id} tarred and deleted.");
Currently the solution to the problem consisted of a few changes based on what #daBigBug answered.
pollEnrich's simple expression uses antInclude rather than fileName;
aggregate is put after pollenrich; as each batch is the set of files, rather than the input from the queue. The input from the queue only provides meta information based on which actions are to be taken.
aggregationStrategy() is not possible in a RouteBuilder; I used aggregate() instead.
I removed the unmarshal(); I don't see why this would be needed; the files can contain binary content.
from("broker1:todelete.message_ids.queue")
.pollEnrich()
.simple("file:${in.header.pad}?antInclude=${in.header.pdu_id}.*")
.aggregate(new TarAggregationStrategy())
.constant(true)
.completionFromBatchConsumer()
.eagerCheckCompletion()
.log("tarring to: ${header.pad}${header.pdu_id}.tar")
.setHeader(Exchange.FILE_NAME, simple("${header.pdu_id}.tar"))
.setHeader(Exchange.FILE_PATH, simple("${header.pad}"))
.to("file://ignored")
...(and the rest of the operations);
I now see the files are getting picked up and even placed in a tar; however, the filename of the tar is unexpected as is the location (it's placed in ./ignored); Also in the rest of the operation, it appears the exchange headers are lost.
If anyone can help figure out how to preserve the headers in a safe way... I'm much obliged. Should I use a new question for that, or should I rephrase the question.

Put modified xml back into the message?

I'm using CXF to send messages with SOAP over JMS.
I'm trying to write a CXF Interceptor in the POST_MARSHALL phase.
I want to change some attributes when the xml is generated.
I know i can get the content from the message via
message.getContent(java.io.Writer.class).
This happens to be in the form of JMSConduit$1. Which - I think - is a StringWriter (if I debug my code I can see a buf field).
I can get the xml in String format and make my changes, but the problems is putting it back in the message.
I can not change the JMSConduit$1 to something else, otherwise CXF won't send it to the JMS Endpoint. (it must be a JMSConduit).
I can't find a way to put the modified xml back in a JMSConduit, which i can get through
message.getExchange().getConduit();
So, how can I put my modified xml back into the message/JMSConduit?
Finally found an answer. I used a FilterWriter.
public void handleMessage(Message message) throws Fault {
final Writer writer = message.getContent(Writer.class);
message.setContent(Writer.class, new OutWriter(message, writer));
}
class OutWriter extends FilterWriter {
#Override
public void close() throws IOException {
// Modify String (in xml form).
message.setContent(Writer.class, out);
}
}

why can't i write this camel route?

from("e1")
.split()
.method("bean", "m1")
.to("e2")
.end()
.split()
.method("bean", "m2")
.to("e3");
The compiler complains about the 2nd to. The reason is that for some reason, it thinks the second split returns ExpressionCaluse rather than ExpressionClause<SplitDefinition>, which causes the following method return type to be Object rather than SplitDefinition.
I tried it in Eclipse, and first I got the same result as you (with the eclipse code completion showing an error). Then I rewrote the route (e.g. splitting it up with assignments to
ProcessorDefinition pd = from("e1")....
pd.split()...
Then, back to the original code, so finally Eclipse got the idea correct and the error marker disappeared. I don't know if you was trying eclipse too?
#Override
public void configure() throws Exception {
from("e1")
.split()
.method("bean", "m1")
.to("e2")
.end()
.split().method("bean", "m2")
.to("e3");
}
I mean, it should work. The signature of split() in ProcessorDefinition is correct:
public ExpressionClause<SplitDefinition> split()
I guess this is a glitch somewhere in my dev. env. and probably yours too.. or something. Odd, anyway.

Windows Phone 7 App Quits when I attempt to deserialize JSON

I'm developing my first windows phone 7 app, and I've hit a snag. basically it's just reading a json string of events and binding that to a list (using the list app starting point)
public void Load()
{
// form the URI
UriBuilder uri = new UriBuilder("http://mysite.com/events.json");
WebClient proxy = new WebClient();
proxy.OpenReadCompleted += new OpenReadCompletedEventHandler(OnReadCompleted);
proxy.OpenReadAsync(uri.Uri);
}
void OnReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
var serializer = new DataContractJsonSerializer(typeof(EventList));
var events = (EventList)serializer.ReadObject(e.Result);
foreach (var ev in events)
{
Items.Add(ev);
}
}
}
public ObservableCollection<EventDetails> Items { get; private set; }
EventDetails is my class that wraps the json string. this class has to be correct because it is an exact copy of the class used by that website internally from which the json is generated...
I get the json string correctly from the webclient call (I read the memorystream and the json is indeed there) but as soon as I attempt to deserialize the string, the application exits and the debugger stops.
I get no error message or any indication that anything happen, it just stops. This happens if I type the deserialize method into the watch window as well...
I have already tried using JSON.net in fact I thought maybe it was a problem with JSON.net so I converted it to use the native deserializer in the .net framework but the error is the same either way.
why would the application just quit? shouldn't it give me SOME kind of error message?
what could I be doing wrong?
many thanks!
Firstly, the fact that you have some string there that looks like JSON does not mean that you have a valid JSON. Try converting a simple one.
If your JSON is valid, it might be that your JSON implementation does not know how to convert a list to EventList. Give it a try with ArrayList instead and let me know.
The application closes because an unhandled exception happens. If check the App.xaml.cs file you will find the code that closes your app. What you need to do is try catch your deserialization process and handle it locally. So most likely you have some JSON the DataContractJsonSerializer does not like. I have been having issue with it deserializing WCF JSON and have had to go other routes.
You may want to check to ensure your JSON is valid, just because your website likes it does not mean it is actually valid, the code on your site may be helping to correct the issue. Drop a copy of your JSON object (the string) in http://jsonlint.com/ to see if it is valid or not. Crokford (the guy who created JSON) wrote this site to validate JSON, so I would rely on it more than your site ;) This little site has really helped me out of some issues over the past year.
I ran into this same kind of problem when trying to migrate some existing WM code to run on WP7. I believe that the WP7 app crashes whenever it loads an assembly (or class?) that references something that's not available in WP7. In my case, I think it was Assembly.Load or something in the System.IO namespace, related to file access via paths.
While your case might be something completely different, the symptoms were exactly the same.
The only thing I can recommend is to go through the JSON library and see if it's referencing base classes that are not allowed in WP7. Note that it doesn't even have to hit the line of code that's causing the issue - it'll crash as soon as it tries to hit the class that contains the bad reference.
If you can step into the JSON library, you can get a better idea of which class is causing the problem, because as soon as the code references it, the whole app will crash and the debugger will stop.

Resources