-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
673 lines (534 loc) · 33.2 KB
/
atom.xml
File metadata and controls
673 lines (534 loc) · 33.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Ride the Camel!]]></title>
<link href="http://Waterback.github.com/atom.xml" rel="self"/>
<link href="http://Waterback.github.com/"/>
<updated>2014-03-21T10:20:38+01:00</updated>
<id>http://Waterback.github.com/</id>
<author>
<name><![CDATA[Martin Huber, innoq Deutschland GmbH]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Flexible Filebased-Testing in Apache Camel]]></title>
<link href="http://Waterback.github.com/blog/2014/03/18/flexible-filebased-testing-in-apache-camel/"/>
<updated>2014-03-18T16:56:00+01:00</updated>
<id>http://Waterback.github.com/blog/2014/03/18/flexible-filebased-testing-in-apache-camel</id>
<content type="html"><![CDATA[<h3>Introduction</h3>
<p>Well it’s been “long time no see”, but finally I got to do some cameleering again ;-).</p>
<p>In my most recent project, I used a Test-Setup for testing a Camel-Route, I came to think, it may be worth sharing:</p>
<p>So, Let’s Start:</p>
<p>The actual work of Camel-Route that is to be tested (from now on called: Working-Route) is actually not important to understand the blog.
But you need to know, that the route is servlet that wraps
SOAP-Service-Calls.
It transforms schemes and the Transformation-Result at the output-endpoint has to be validated.</p>
<p>For the Unittest of the Camel-Route I have some Testsamples organized in Files. The idea is now to read the files with another route and send them to
the Files as new Exchanges to the servlet-input-Endpoint the Working-Route exposes.
In the Test-Setup, the Working-Route sends it’s (transformed) Exchanges to a instantiated Validation-Route (configured to test for the transformed scheme),
filling in for the SOAP-Service behind.
The Response that goes through the initiating “file-reading”-Route wiretaps also to another instance of the Validation-Route testing for the backward-transformed Scheme.</p>
<h3>File-Route</h3>
<p>The File-Route looks like this: The “identifier” in the following code helps to the identify the Scheme to test for in this instance of the route.</p>
<pre><code> from("file:" + routeFolder + "?noop=true&readLock=rename&filter=#testfileFilter") // (*1)
.autoStartup(false) // (*2)
.routeId("fileread" + identifier)
.throttle(4) // (*3)
.convertBodyTo(String.class)
.wireTap("file://target/output/"+identifier) // (*4)
.choice() // (*5)
.when(simple("${in.body} not contains 'soap:Envelope'"))
.setBody().simple(TransformationConstants.soapStart + "${in.body}" +
TransformationConstants.soapEnd)
.end()
.to(transformationProxyIN) // (*6)
.to(validateendpoint) // (*7)
.setHeader("CamelFileName", simple("${file:name.noext}_TransformationResult.${file:name.ext}")) // (*8)
.to("file://target/output/"+identifier) // (*9)
</code></pre>
<p>Explanation: <br/>
(<em>1) The Fileroute is defined to read from a “routefolder” but doesn’t change or remove files (like the Camel-File-Consumer would in default) <br/>
A filter makes sure only Testfiles are read. If noop is on, the file-component automatically uses a idempotent-repository to ensure the same file
is not read more than once. <br/>
(</em>2) Any instance of this Route is not active from the get-go. Each Unit-Test uses its own folder, starts the correspondent Route, so only the folder with the associated
Testcases are read <br/>
(<em>3) For Performance-Reasons on the Test-Machine, the parallel-processing of files is throttled to 4 in parallel. <br/>
(</em>4) A Requirement for the test is to direct the input & the output of the whole processing to a specific-folder in target. (Mainly to be able to compare them if not rightfully converted).
This command-config directs a copy to the folder via the wiretap-EIP. <br/>
(<em>5) If the sample-File doesn’t contain a SOAP-Header (Well, some Files do, some don’t, you’ll never know ;-)) the sample gets embedded into
a soap-envelope <br/>
(</em>6) Sends the file to the Working-Route <br/>
(<em>7) Is feeded with the response of the Working-Route and sent to the Validation-Route <br/>
(</em>8) Changes the CamelFileName-Header, which is used if the file-component is ever used again, which in fact is in <em>9. <br/>
Isn’t the “simple”-language a cute little thing. See how easy it is to change a file based on an old one. <br/>
(</em>9) See *4.s.. Sends the result to the same folder like the original read file.</p>
<h3>Validation-Route</h3>
<p>The Validation-Route has two interesting aspect - the Route that validates against a Scheme. And the errorhandling
for validation-failures. Validation failures get send to the “mock:failure” endpoint. From this MockEndpoint the
Unittest is able to evaluate the testresult.</p>
<pre><code> /**
* The "validationroute" configured in this Method, strips the soap-parts off the message and validates it against the xsd
* if validation succeeds, the Message-Exchange is sent to MockEndpoint "success".
* If Validation fails, a ValidationException is thrown and handled by the configured ErrorHandling
* @throws Exception
*/
@Override
public void configure() throws Exception {
Namespaces soapNS = new Namespaces("s", "http://schemas.xmlsoap.org/soap/envelope/");
onException(ValidationException.class)
.maximumRedeliveries(0)
.handled(true)
.to("mock:failedValidation");
from(validateendpoint)
.routeId("validate" + identifier)
.setBody().xpath("/s:Envelope/s:Body/*[1]", soapNS)
.to("validator:" + schemaLocation)
.wireTap("mock:success")
.process(new ResponseFileProcessor())
.setBody().simple(TransformationConstants.soapStartTixBank + "${in.body}" + TransformationConstants.soapEnd);
}
}
</code></pre>
<h3>Unit-Test</h3>
<p>The test itself is relatively simple:
- Count the Files (so you know how many results to expect - another requirement: If there are new samples put into folder, tests
and assertions should work without changing code)
- Start the file-reading route.
- Assert the results from mock:success and mock:failedValidation
- and finally stop the file-reading route</p>
<pre><code> @Test
public void testBasicTransformationFunctionalityIN_10() throws Exception {
int files = countFiles("filereadV10");
camelContext.startRoute("filereadV10");
validationAsserts(files, 0);
camelContext.stopRoute("filereadV10");
}
</code></pre>
<p>It may look a bit tricky and strange not to use java.io.File for this.
It was just a try, but it worked, so i kept it, because it was the solution, that made the least work.
You can use a Endpoint-Definition (that I already had for the filereading-route) to look how many exchanges may wait there for me.
And then I used it to find out how many positive result have to be asserted for a successful test.</p>
<p>See the following piece of code:</p>
<pre><code> private int countFiles(String routeId) {
BrowsableEndpoint browsableEndpoint = (BrowsableEndpoint) camelContext.getEndpoint(
camelContext.getRoute(routeId).getConsumer().getEndpoint().getEndpointUri());
return browsableEndpoint.getExchanges().size();
}
</code></pre>
<p>The good thing about this implementation is also: it counts only the files the fileroute would also include. So let’s say you
have a filefilter in place that takes only the xml-files. The BrowsableEndpoint respects that.</p>
<p>That’s it folks, hope you find it useful.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Easy handmade SOAP-Webservice-Versioning with Apache Camel]]></title>
<link href="http://Waterback.github.com/blog/2012/03/02/easy-handmade-ws-addresssing-with-apache-camel/"/>
<updated>2012-03-02T22:38:00+01:00</updated>
<id>http://Waterback.github.com/blog/2012/03/02/easy-handmade-ws-addresssing-with-apache-camel</id>
<content type="html"><![CDATA[<h2>Introduction</h2>
<p>Have you ever wondered, how to create a lightweight versioning of your SOAP-Webservices?
Or a simple way to create a middle-tier for scheme-conversions
between a Webservice-Client and a Webservice-Server using different Schemes?</p>
<p>Then this post may be interesting to you.</p>
<p>A short while ago I was confronted with the exact problem I described as an Introduction. It was to create an integration help for a Business Process Management System.
I mulled over that problem somewhat and came up with an example, how you could do this very easily (as i find) with my favorite Integration Framework.</p>
<p>As you may have noticed I am talking about Apache Camel. In this Post I’d like to explain the solution i have found.</p>
<p>Before i get into the details, you could check and try (see readme)
that example for yourself: It’s located at github:</p>
<p><a href="https://github.com/Waterback/BPELCam">BPEL-Camel Example</a></p>
<p>Here in this post i will only repeat the interesting parts of it.</p>
<p>… Now let’s begin …</p>
<h2>Business Case</h2>
<p>Lets suppose, we have a Webservice for an specific business-purpose.
The business case is invented, so please don’t give too much on the details of it.</p>
<p>I had to find a way to service webservice-clients that for example, know a Version 1-Schema of our Webservice, but need to be
serviced by the Version 2-Webservice.</p>
<p>And vice-versa, meaning a V2-Scheme-Call should be able to be serviced by the V1-Webservice.</p>
<h2>Configuration</h2>
<p>I have two WSDLs in this example that differ only by the imported Xsd-Documents for V1 and V2 Schemas.</p>
<p>As Camel is well integrated with the CXF-Framework to provide Webservices, you only have to create some configuration. In my example
i use Spring for my configuration purposes.
Here’s a part of the <em>camel-context.xml</em> -Spring config:</p>
<p>For this simple example i use the the Jetty-Component to create a Http-Endpoint as a servlet for both webservices:</p>
<pre><code><bean id="jetty" class="org.apache.camel.component.jetty.JettyHttpComponent" />
<cxf:cxfEndpoint id="insuranceEndpoint-v1"
address="http://localhost:9000/process1"
wsdlURL="/wsdl/insuranceV1.wsdl">
<cxf:properties>
<entry key="dataFormat" value="MESSAGE" />
<entry key="publishedEndpointUrl" value="http://localhost:9080/insuranceservice" />
</cxf:properties>
</cxf:cxfEndpoint>
<cxf:cxfEndpoint id="insuranceEndpoint-v2"
address="http://localhost:9000/process2"
wsdlURL="/wsdl/insuranceV2.wsdl">
<cxf:properties>
<entry key="dataFormat" value="MESSAGE" />
<entry key="publishedEndpointUrl" value="http://localhost:9080/insuranceservice" />
</cxf:properties>
</cxf:cxfEndpoint>
</code></pre>
<p>The Endpoint-Address defined in the WSDL is overwritten with a configuration property: <em>publishedEndpointUrl</em> that points to that servlet i wanna use as a entrance to my camel-routes.
This configuration takes care
for the Endpoint that is printed into the wsdl when you call: <em>http://localhost:9000/process1?wsdl</em></p>
<p>With this i can create a unified entrance to both webservices.</p>
<h2>Camel-Routes: unified entrance for the webservices</h2>
<p>But how is this handled in the camel-routes? See yourself:</p>
<pre><code>package com.innoq.bpelcam;
public class SchemaEvalutionRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
Namespaces ns_v1 = new Namespaces("c1", "http://bpel.innoq.com/insurance/v1/types");
Namespaces ns_v2 = new Namespaces("c2", "http://bpel.innoq.com/insurance/v2/types");
from("jetty:http://localhost:9080/insuranceservice?minThreads=5")
.choice()
.when().xpath("//c1:CarInsuranceFindProcess", ns_v1)
.setHeader("schemaVersion", constant("1"))
.setHeader("targetVersion").xpath("//c1:targetVersion", String.class, ns_v1)
.to("seda:evaluateV1Scheme")
.when().xpath("//c2:CarInsuranceFindProcess", ns_v2)
.setHeader("schemaVersion", constant("2"))
.setHeader("targetVersion").xpath("//c2:targetVersion", String.class, ns_v2)
.to("seda:evaluateV2Scheme")
.otherwise()
.to("seda:fault");
...
</code></pre>
<p>So there’s a Jetty-Endpoint which starts the jetty-container implicitly.</p>
<p>If you’d like to use your average WebSphere-Web Container ;-) or a Tomcat you have to configure
it a little bit different in the Spring-config, but this should be the topic for another post (or the Camel-documentation).</p>
<p>We have here a WS-Addressing-like (correct me, when i’m wrong) determination which Schema the Webservice-Caller uses with
a simple XPath-Expression. The corresponding namespaces have to be defined at the beginning of the configure method.</p>
<p>For later evaluation this information is stored in the Camel-Exchange as Meta-Information (Header).
Also the <strong>targetedVersion</strong> is recognized and stored for later evalutaion as a Header.
Depending on the recognized Schema as V1 or V2, we incorporate a evaluation-route, which is build for each Webservice-Schema-Version:</p>
<pre><code> from("seda:evaluateV1Scheme")
.choice()
.when(header("targetVersion").isEqualTo("2"))
.pipeline("direct:transformV1V2")
.to("cxf:bean:orderEndpoint-v2")
.otherwise()
.to("cxf:bean:orderEndpoint-v1");
from("seda:evaluateV2Scheme")
.choice()
...
from("direct:transformV1V2")
.bean(new V1TransformerBean(), "transformV1toV2");
</code></pre>
<p>There the targetVersion comes in handy. So you don’t need no more XPath-evaluations to know where to go.
So in that matter, for each non-original schema-version (V2,V3 and so on), you need to expand this route
with another when()-Block.</p>
<p>The <strong>pipeline(“direct:transformV1V2”)</strong>-pattern, which enables to incorporate camel-routes (here: <em>“direct:transformV1V2”</em>)
for definition is used in this example to show,
that you can distribute your schema-conversion on different beans or/and routes as you wish. <br/>
The V1TransformerBean takes care in my case for the whole V1 to V2-conversion of the sent document.
This bean has grown in a not so easy component, so i’m gonna explain in one of my next posts.</p>
<p><strong>Last trick…</strong></p>
<p>The last trick is that the whole exchange is sent in the end to <strong>.to(“cxf:bean:insuranceEndpoint-v2”) </strong>.
This is a cxf-endpoint.
This could be your actual implementation of the versioned Webservice:</p>
<pre><code> package com.innoq.bpelcam;
public class WebservicesRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
DataFormat jaxb10 = new JaxbDataFormat("com.innoq.bpel.insurance.v1.types");
DataFormat jaxb11 = new JaxbDataFormat("com.innoq.bpel.insurance.v2.types");
Namespaces ns_v1 = new Namespaces("c1", "http://bpel.innoq.com/insurance/v1/types");
Namespaces ns_v2 = new Namespaces("c2", "http://bpel.innoq.com/insurance/v2/types");
from("cxf:bean:insuranceEndpoint-v1")
.id("handleProcess_v1")
.transform().xpath("//c1:CarInsuranceFindProcess", ns_v1)
.unmarshal(jaxb10)
.process(new OrderContentsProcessor())
.marshal(jaxb10)
.to("seda:save")
.process(new ResponseBuilderProcessor(10));
</code></pre>
<p>But you could also easily redirect the whole soap-document to another system (i.e. BPEL).
Therefore it only needs to be configured in your camel-spring-configuration as Endpoint pointing to another system.</p>
<p>A side note here is: I always use the Java-Fluent-API, because with a little discipline in formatting, you could have your routes
configured in a very transparent and clear matter. I’d prefer that over xml- or even graphical-configuration every time.</p>
<h2>That’s it</h2>
<p>So that’s it folks.
If something is not clear to you, i am happy for feedback/questions ;-)</p>
<p>Cheers,
Martin</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Camel: Inherit Errorhandling]]></title>
<link href="http://Waterback.github.com/blog/2011/12/09/camel-inherit-errorhandling/"/>
<updated>2011-12-09T00:14:00+01:00</updated>
<id>http://Waterback.github.com/blog/2011/12/09/camel-inherit-errorhandling</id>
<content type="html"><![CDATA[<p>Hello All,</p>
<p>today i want to write sth new from my ongoing understanding of my work with Apache Camel.
This time it’s not that of a big story as last time, but quite useful:
Up to this point (actually a little bit in the past now) i thought when building Routes with
Apache Camel in a org.apache.camel.RouteBuilder you have to write everything you want to configure
in that respective configure()-method and that’s it.</p>
<p>Well that’s not true…</p>
<!--more -->
<p>You may as well as call any method you like, i.e. from a class your RouteBuilder inherits from
and Camel interprets everything as a whole configuration.</p>
<p>Before some might ask: and why should that be useful - Well you could for example define some errorHandlers in that super class and make them accessible for every Camel-Route in which the errorhandling should be active, so you have to define only once but could use those errorhandling definitions in more than one RouteBuilder.</p>
<pre><code>public class InboundRouter extends ErrorHandlingRouteBuilder {
@Override
public void configure() throws Exception {
super.inheritConfigure();
from(fromEndpoint)
.noAutoStartup()
.transacted()
.policy(required)
.routeId(BasicRouteBuilder.stdfromRouteId(this.componentBaseName))
.process(neededHeadersProcessor)
.to(nextstepfromRoute);
}
}
</code></pre>
<p>And in ErrorhandlingRouteBuilder:</p>
<pre><code>inheritConfigure() {
errHdlRoute = "direct:errorfwd." + cpBase;
errorHandler(deadLetterChannel(getContext().getEndpoint(errHdlRoute))
.maximumRedeliveries(errHandlingParameters.getMaxRedeliver())
.useOriginalMessage()
.logStackTrace(false)
.maximumRedeliveryDelay(errHandlingParameters.getMaxRedeliverDelay())
.redeliveryDelay(errHandlingParameters.getRedeliverDelay())
.backOffMultiplier(errHandlingParameters.getBackoffMultiplier())
.useExponentialBackOff());
onException(PermanentError.class)
.maximumRedeliveries(0)
.useOriginalMessage()
.handled(true)
.to(errHdlRoute);
from(errHdlRoute)
.routeId("errorforwarder")
.process(new ErrorhandlingHeadersProcessor())
.process(new ConvertErrorToJPAProcesor())
.to("jpa:com.innoq.errhandling.SaveMessageStore?persistenceUnit);
}
</code></pre>
<p>Ahem, don’t read too much into that errorhandling stuff itself, the configurations shown are parts, taken from my most recent project.<br>
I promise i will come back with another post to explain my solution for errorhandling.
I only wanted to show that every camel-configuration aspect in the above example is also considered from camel in it’s configuration process.</p>
<p>As already mentioned you could build a big callstack in inherited classes or in others - it is only important to start your callstack inside the configure()-method of a RouteBuilder that gets loaded with the start of the CamelContext.</p>
<p>Now, if you might say: “Hello Mr. Obvious” - sorry that i wasted your precious time.</p>
<p>For me, from reading the available online docs and the Book “Camel in Action”
(which is, by the way, a very good one and a great way to start/work with Apache Camel)
this wasn’t so obvious to me.</p>
<p>Cheers… <br/>
Martin</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Camel: Application-Monitoring]]></title>
<link href="http://Waterback.github.com/blog/2011/12/08/application-monitoring-with-camel/"/>
<updated>2011-12-08T10:05:00+01:00</updated>
<id>http://Waterback.github.com/blog/2011/12/08/application-monitoring-with-camel</id>
<content type="html"><![CDATA[<p>I’ve been working with Apache Camel for a year now and i am still scraping at the top of all
the possibilities you have with Camel.</p>
<p>In this blog i wanna show something, that might be helpful when Camel is already used and someone want to keep
the statistics that your routes generate in a database for some sort of long time evaluations.
I think it’s quite easy to do so and definitely not limited to handling statistics of camel-routes.
If you have absolutely no idea what i could possibly mean with routes and statistics, i recommend some reading:
<a href="http://camel.apache.org">Camel WebSite</a> and for the jmx part itself: <a href="http://camel.apache.org/camel-jmx.html">Camel JMX</a></p>
<p>Because this example here is build around camel-routes you’ll see the statistics kept by
a ‘org.apache.camel.management.mbean.ManagedPerformanceCounter’<br/>
I want to persist those stats in a database using JPA. So i need a @Entity for this:</p>
<pre><code>@Entity(name = "CAMELSTATENTRY")
@Table(name = "MyStats")
public class CamelStatEntry {
private Long id;
private String nameIdentifier;
private Date statisticTimeStamp;
private Long totalExchanges;
private Long exchangesCompleted;
private Long exchangesFailed;
private Long minProcessingTime;
private Long maxProcessingTime;
private Long totalProcessingTime;
private Long lastProcessingTime;
private Long meanProcessingTime;
private Date firstExchangeCompletedTimestamp;
private Date firstExchangeFailureTimestamp;
private Date lastExchangeCompletedTimestamp;
private Date lastExchangeFailureTimestamp;
@Id
@Column(name = "ID")
public Long getId() {
return id;
}
...
</code></pre>
<p>I think it’s not too complicated to see, what those values in the CamelStatEntry-Entity stand for.</p>
<!--more -->
<p>This values get collected by Camel while exchanges “run through” your routes.
The trick is now to gather those statistics in a time-interval. The idea behind the time-interval is to be able to see, how your routes “behave” over time.
For this time-interval we’re going to use another route, a route defined in StatCollectionRouter-RouteBuilder.
This route is one, that get’s triggered at defined points in time while using the
<a href="http://camel.apache.org/quartz.html" >quartz component</a> (in my example every full quarter of an hour).</p>
<p>So there’s the route:</p>
<pre><code>public class StatCollectionRouter extends RouteBuilder implements InitializingBean {
/**
* Spring injected bean, that does all the collection work, and keeps the
* information which routes have to be checked.
*/
private StatCollectorBean statCollBean;
private String targetEndpoint = "jpa:?persistenceUnit=zifsdb&consumeDelete=false";
@Override
public void configure() throws Exception {
from(quartz://zs3vsl/statctr?cron=0+15+*+*+*+?+*)
.routeId("StatisticalRoute")
.bean(statCollBean, "collect")
.split(body())
.to("jpa:?persistenceUnit=statsdb")
.threads(1);
}
...
</code></pre>
<p>After the route got triggered it calls a bean, that does all the collection-work.
This bean returns a list of all CamelStatEntry-Objects. The split() divides the one trigger into so many message-exchanges
as you have CamelStatEntry-Objects in that List, returned by the collect-method of the statCollBean. Then every CamelStatEntry
gets stored, when sent to the jpa-component. Of course, the “statsdb”-persistenceUnit is defined in a persistence.xml-file.</p>
<p>The last thing you need to know how to collect the stats from the Camel-Routes-MBeans.</p>
<p>Well the following assumes, that you have a jmx-connector started with your CamelContext and know a little bit about
connecting to them and requesting MBeans. Explaining all that would be to much at this. Look @ the recommended reading above
to see how this is done.</p>
<p>I cut my example-code a little bit, because there’s a point in there that’s rather boring: How to select the Objectnames to check.
I need to connect to the MBeanServer of my “DefaultDomain”. My Jmx-Agent is configured in the spring-config like this:</p>
<pre><code> <camel:camelContext id="myCamelContext" >
<camel:jmxAgent id="com.innoq.jmxagent" createConnector="true"
mbeanObjectDomainName="com.innoq" mbeanServerDefaultDomain="WebSphere"
xmlns:camel="http://camel.apache.org/schema/spring" connectorPort="39099"
onlyRegisterProcessorWithCustomId="true" />
</camel:camelContext>
</code></pre>
<p>The “DefaultDomain” mentioned above is “com.innoq”.<br/>
You need to know the default-domain, because when you search for your MBeans with the Objectname, you have to set the right Domain.
The following is a part of the initialization of the StatCollectorBean. I search for Route-Types in my MBeanServer. Note that
i have an extra-class “MyJMXHelper” that finds the right MBeanServer for me, which i left out for space reasons. And also allow me
to say that this is just an example for creating the list: “allObjectNames2Check” - the ObjectNames, for which i want to check my Statistics.<br/>
Feel free to do this more elegantly.<br/>
The things you need to know gather around the build-up of the searchstring s
and that you maybe make a List of all MBeans that should be checked.</p>
<pre><code>public class StatCollectorBean implements JMXConstants {
/**
* Result-List of all Objectnames that have to be statistically checked.
*/
private Set<ObjectName> allObjectNames2Check;
...
@SuppressWarnings("unchecked")
private void addRoutes2CheckSet(List<String> searchStringList) {
MBeanServer mbs = MyJMXHelper.findDefaultDomainMBS();
ObjectName ojnsearch;
Set<ObjectName> routembeans = null;
Iterator<String> it = searchStringList.iterator();
while (it.hasNext()) {
String routeIdName = it.next();
try {
String s = "com.innoq:type=routes, name=\"" + routeIdName ""\", *";
ojnsearch = new ObjectName(s);
routembeans = mbs.queryNames(ojnsearch, null);
if (routembeans.size() != 0) {
allObjectNames2Check.addAll(routembeans);
}
}
catch (MalformedObjectNameException e) {
e.printStackTrace();
}
catch (NullPointerException e) {
e.printStackTrace();
}
}
}
</code></pre>
<p>The last piece of code i wanna show is the collect() method of the StatCollectorBean. It works with the Set “allObjectNames2Check”
initialized in the addRoutes2CheckSet-method</p>
<pre><code>public List<CamelStatEntry> collect() throws RuntimeException {
if (!initialized) {
synchronized (StatCollectorBean.class) {
init();
}
}
VirtualClock clock = new VirtualClock();
List<CamelStatEntry> list = new ArrayList<CamelStatEntry>(allOjn2Check.size());
try {
for (ObjectName ojn : allOjn2Check) {
list.add(createCSEfromMBean(ojn, clock.getDate()));
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
return list;
}
private CamelStatEntry createCSEfromMBean(ObjectName ojn, Date statisticTimeStamp)
throws Exception {
final String[] emptySig = new String[] {};
final Object[] emptyParms = new Object[] {};
CamelStatEntry cse = new CamelStatEntry();
MBeanServer mbs = MyJMXHelper.findDefaultDomainMBS();
String nameId = (String) mbs.invoke(ojn, "getRouteId", emptyParms, emptySig);
cse.setNameIdentifier(nameId);
cse.setExchangesCompleted((Long) mbs.invoke(ojn,
"getExchangesCompleted",
emptyParms, emptySig));
cse.setExchangesFailed((Long) mbs.invoke(ojn,
"getExchangesFailed",
emptyParms, emptySig));
cse.setFirstExchangeCompletedTimestamp((Date) mbs.invoke(ojn,
"getFirstExchangeCompletedTimestamp",
emptyParms, emptySig));
cse.setFirstExchangeFailureTimestamp((Date) mbs.invoke(ojn,
"getFirstExchangeFailureTimestamp",
emptyParms, emptySig));
cse.setLastExchangeCompletedTimestamp((Date) mbs.invoke(ojn,
"getLastExchangeCompletedTimestamp",
emptyParms, emptySig));
cse.setLastExchangeFailureTimestamp((Date) mbs.invoke(ojn,
"getLastExchangeFailureTimestamp",
emptyParms, emptySig));
cse.setLastProcessingTime((Long) mbs.invoke(ojn,
"getLastProcessingTime",
emptyParms, emptySig));
cse.setMaxProcessingTime((Long) mbs.invoke(ojn,
"getMaxProcessingTime",
emptyParms, emptySig));
cse.setMinProcessingTime((Long) mbs.invoke(ojn,
"getMinProcessingTime",
emptyParms, emptySig));
cse.setMeanProcessingTime((Long) mbs.invoke(ojn,
"getMeanProcessingTime",
emptyParms, emptySig));
cse.setStatisticTimeStamp(statisticTimeStamp);
cse.setTotalExchanges((Long) mbs.invoke(ojn,
"getExchangesTotal",
emptyParms, emptySig));
cse.setId(statisticTimeStamp.getTime() + nameId.hashCode() + servername.hashCode());
cse.setTotalProcessingTime((Long) mbs.invoke(ojn,
"getTotalProcessingTime",
emptyParms, emptySig));
mbs.invoke(ojn, "reset", emptyParms, emptySig);
return cse;
}
</code></pre>
<p>Note that it is important to reset the statistics, so i have for every time interval a fresh set of the statistics.</p>
<p>So that’s it.<br/>
Hope you’ll be able to use some of this.</p>
<p>Cheers,
Martin</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Welcome]]></title>
<link href="http://Waterback.github.com/blog/2011/12/07/welcome/"/>
<updated>2011-12-07T23:14:00+01:00</updated>
<id>http://Waterback.github.com/blog/2011/12/07/welcome</id>
<content type="html"><![CDATA[<p>Welcome to my random thoughts i find worthy of a writedown.
As i am about to start this here, blogs actually twittered out of any fashion, but i like them.
You can learn a lot by reading the blogs of others. I won’t presume you could necessarily do the same while reading my blog, but i’ll give it my best shot.</p>
<p>Mostly i am into riding the Apache Camel. Up to now i rode mostly the WebSphere Application Server in my professional Java-Life. In order to keep up a steady progress ;-), i am trying to combine both in my most recent project i am glad to be part of.</p>
<p>Next up: Learning some new language. I am targeting the likes of Erlang, Scala or Ruby</p>
<p>Hopefully you’ll enjoy some of my posts</p>
]]></content>
</entry>
</feed>