Apache Camel: Set 'description' for Processor using Java DSL - apache-camel

I can add a custom id and description to a <log> or most other types of processor in XML like this:
<log message="Hello" id="logId">
<description>Description of logging</description>
</log>
I've tried to do a similar thing with Java DSL:
from("direct:1")
.log("Hello")
.id("logId")
.description("Description of logging")
But the description is applied to the route, not the processor. (The routeId() method is provided to apply the id to the route instead of the processor.)
Looking through the Camel source code, ProcessorDefinition's definition of id() has a bunch of code to apply it to the last block or output, preceded by this comment:
// set it on last output as this is what the user means to do
// for Block(s) with non empty getOutputs() the id probably refers
// to the last definition in the current Block
There is no similar definition of description() in ProcessorDefinition, so this simpler method is inherited from OptionalIdentifiedDefinition:
public T description(String text) {
if (text != null) {
if (description == null) {
description = new DescriptionDefinition();
}
description.setText(text);
}
return (T) this;
}
It seems to me that the Java DSL does not provide a way to set the description for log or other processors. Is this true, or did I miss something?
(If it is true, should I work on a patch to improve the DSL?)

Related

How do I (continue to, post-Kotlin Android Extensions) access widgets as array elements (which are doubly-included)?

I am "modernizing" some 15-month-old code to take advantage of
Kotlin Extension View Binding (migrating away from the deprecated
Kotlin Android Extensions compiler plugin).
The issue I'm having is related to the practice of using vars of
type Array<ConstraintLayout> throughout my code. This is
exemplified by charKeys throughout this posting.
I'm using nested includes within the XML.
I am struggling to figure the correct new syntax or approach. I
cannot get this code to compile yet.
NOTE: All Kotlin & XML has been reduced to relevant sections only.
First, the "old" way - which is working perfectly.
PuzzleFragment.kt
import kotlinx.android.synthetic.main.fragment_puzzle.*
import kotlinx.android.synthetic.main.item_keyboard.*
import kotlinx.android.synthetic.main.item_keyboard.view.*
import kotlinx.android.synthetic.main.item_kkey.view.*
:
class PuzzleFragment : Fragment() {
lateinit var charKeys: Array<ConstraintLayout>
charKeys = arrayOf(
kbd_char_0,
kbd_char_1
:
)
fragment_puzzle.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/puzzle_fragment"
>
<include layout="#layout/item_keyboard" />
</androidx.constraintlayout.widget.ConstraintLayout>
item_keyboard.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/keyboard"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/kbd_char_0"
>
<include layout="#layout/item_kkey" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/kbd_char_1"
>
<include layout="#layout/item_kkey" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
item_kkey.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android"
>
<TextView
android:id="#+id/kkey"
/>
</merge>
Again, all of that is (was) working (using Kotlin Android
Extensions). And this code allows (allowed) me to do things
like:
PuzzleFragment.kt
for (x in 0 until someNumber) {
val shape = charKeys[x].background as GradientDrawable
shape.setStroke(...)
charKeys[x].kkey.setTextColor(...)
for (key in charKeys)
key.isEnabled = false
for ((kx, key) in charKeys.withIndex())
key.elevation = ... //using kx
for (cx in 0 until maxGuessLength)
makeKeyRed(charKeys[cx], true)
private fun makeKeyRed(key: ConstraintLayout, doRed: Boolean) {
when {
doRed -> key.kkey.setTextColor(...)
key.kkey.text != "#" -> key.kkey.setTextColor(...)
else -> key.kkey.setTextColor(...)
}
}
So - that's the old way. Everything's cool. Now I'm converting this code. I have:
PuzzleFragment.kt
import com.zazzem.thats.databinding.FragmentPuzzleBinding
class PuzzleFragment : Fragment(R.layout.fragment_puzzle) {
private var _binding: FragmentPuzzleBinding? = null
private val binding get() = _binding!!
The following code seems ok (no "highlighted" errors in the IDE):
lateinit var charKeys: Array<ConstraintLayout>
charKeys = arrayOf(
binding.item_keyboard.kbd_char_0,
binding.item_keyboard.kbd_char_1,
:
)
for (x in 0 until someNumber) {
val shape = charKeys[x].background as GradientDrawable
shape.setStroke(...)
But it "falls apart" with the next line of code:
charKeys[x].kkey.setTextColor(...)
It doesn't like "kkey" here ("Unresolved reference"). And, in fact,
Basic code completion (Ctrl + Space) shows none of the widgets which
are part of item_kkey.
I don't know if I'm simply missing something obvious? Or if this
whole "array of 'vaguely-typed' ConstraintLayouts" approach isn't
valid (any longer)? Or if it's something in between?
Your .kkey is another use of a synthetic property, so you can't do that without Kotlin Android Extensions. The synthetic property is under the hood using findViewById on whatever ConstraintLayout you call it on to find the child view.
With view binding, no such properties of Views exist. But the generated class does have properties for include blocks. These properties return the generated binding classes of the layout files that you're including. However, you have not used android:id elements on your include elements, so they won't be accessible via your binding class.
Even if you did, that wouldn't be compatible with your ConstraintLayout Array strategy here.
I suggest for ease of converting your code, you create a direct replacement for the synthetic property, like this:
val ConstraintLayout.kkey: TextView get() = findViewById(R.id.kkey)
One downside to this compared to the synthetic property is that it has to find the view every time you use it, which is slightly slower than synthetic properties when they are repeatedly used. But I don't think it will be very significant here, since this particular view is always the sole child of the layout you are searching in.
Note, this strategy has the same weakness that they deprecated Android Extensions for. It's not safe to call this on any arbitrary ConstraintLayout.

Sql Component :Consume multiple rows and mark them all as processed using onConsume

I configured camel sql component to read data from from database table . I have "onConsume" parameter working when i read one row at a time , but doesn't work when i try to read multiple rows at a time using "maxMessagesPerPoll". Here is what i tried ...
Working : When i read one row at a time and update the row using onConsume .
My consumer endpoint uri looks like :
sql:select * from REPORT where IS_VIOLATED != 'N' and TYPE = 'Provisioning'?consumer.delay=1000&consumer.onConsume=update REPORT set IS_VIOLATED = 'N' where REPORT_ID =:#REPORT_ID
Not working : When I configured camel's sql component to read configurable rows(using "maxMessagesPerPoll") . It reads multiple rows at a time but onConsume doesn't seem to work . I tried to tell camel to use IN operator and setting header value(REPORT_ID) with a array of values for IN clause.
My consumer endpoint uri now looks like :
sql:select * from REPORT where IS_VIOLATED != 'N' and TYPE = 'Provisioning'?consumer.delay=1000&maxMessagesPerPoll=3&consumer.useIterator=false&consumer.onConsume=update REPORT set IS_VIOLATED = 'N' where REPORT_ID in(:#REPORT_ID)
I might be doing something wrong here. I did enough searching on this already and found related post1, post2 . But it doesn't put me on correct path.
I need to be able to mark all the consumed rows to IS_VIOLATED = 'N' .
Thanks for your help.
I noticed that you set consumer.useIterator=false, and the doc says:
If true each row returned when polling will be processed individually. If false the entire java.util.List of data is set as the IN body.
So I think that because of this option, the :#REPORT_ID is no more understood, since it would be from the entire list and no more from each row.
Maybe removing this option would already be enough.
I also didn't understand why you changed the where clause from where REPORT_ID =:#REPORT_ID to where REPORT_ID in(:#REPORT_ID).
By carefully looking at the apache sql component doc :
I tried implementing custom processing stratergy, using attribute "processingStrategy"`.
public class ReportProcessingStratergy implements SqlProcessingStrategy {
#Override
public int commit(DefaultSqlEndpoint defaultSqlEndpoint, Exchange exchange, Object o, JdbcTemplate jdbcTemplate, String s) throws Exception {
s = s.replace("?","5066834,5066835,5066832");
return jdbcTemplate.update(s);
}
#Override
public int commitBatchComplete(DefaultSqlEndpoint defaultSqlEndpoint, JdbcTemplate jdbcTemplate, String s) throws Exception {
return 0;
}
}
configure spring bean :
<bean class="go.ga.ns.reconc.sl.ReportProcessingStratergy" id="reportProcessingStratergy">
now my sql consumer endpoint uri looks like :
sql:select * from REPORT where IS_VIOLATED != 'N' and TYPE = 'Provisioning'?consumer.delay=1000&maxMessagesPerPoll=3&consumer.useIterator=false&&processingStrategy=#reportProcessingStratergy&consumer.onConsume=update REPORT set IS_VIOLATED = 'N' where REPORT_ID in(?)
note :processingStrategy=#reportProcessingStratergy(# has significance as explained here, it did not work with out it)

Setting multiple headers in camel route

Is it possible to set multiple exchange headers in camel route using single call to setHeader.
Something like this
<from uri="file://inputdir/?delete=true" />
<!-- need to set multiple headers at once(as a comma separated list)-->
<setHeader headerName="headername">
<constant>headerval</constant>
</setHeader>
<to uri="mock:end"/>
Or should I create a custom processor for this?
No you cant set multiple headers at once as far as I am aware. In your case a custom processor will probably be more effective.
You might want to look at the simple expression language here here is that you can OGNL notation in your camel route xml file. OGNL will allow you to specify a chain of methods in the expression.
For example suppose you have a message that contains an Employee object that has a getSalaryGrade() method you could set a header to this value by using the following syntax:
<setHeader headerName="SalaryGrade">
<simple>${body.getSalaryGrade()}</simple>
</setHeader>
You could for example create a simple class that returns a list and store the list in the header and then access the list via simple in the route. The following code is untested but should give you a idea.
public class ListCity {
public List<String> ListCities()
{
ArrayList< String> list = new ArrayList<String>();
list.add("New York");
list.add("JOhannesburg");
list.add("HoChiMinh");
return list;
}
}
Declare the list city bean in your xml. You can then set this list into a header by using something like this:
<setHeader headerName="CityList">
<simple>${listCity.ListCities()}</simple>
</setHeader>

Camel check file's last modified date frequently using scheduling service

I want to use camel in my project to check a file's last modified date every xx minutes using camel's scheduling/timer service.
I read the document for file component it seems there is a polling function, however there is also a timer component for camel.
Anyone has some code example if i want to do with the requirement?
I would use the file consumer end point.
Something like this:
file:c:/foldername?delay=5000
This will scan the folder every 5 seconds for files and for each file send a message on the route.
You would probably need to store the previous times somewhere such as a text file or database and then compare the modified variable passed in the message to the modified one stored in the database or file.
A rough example of this would look like follows:
<route id="CheckFileRoute">
<from uri="file:d:/RMSInbox?delay=5000&readLock=changed/>
<log message="${ file:modified }/>
<bean ref="CompareDates"/>
</route>
The file consumer will place a lot of information regarding the file in the header such as modified date. Go read this link for more details on the variables in the header http://camel.apache.org/file2.html
The compare dates bean would be java class that acts like a processor which would have a structure like this:
public class CompareDates {
#Handler
public void CheckDates
(
#Body Object msgbody
, #Headers Map hdr
)
{
Date newDate = (Date)hdr.get("CamelFileLastModified");
Date oldDate = readfromfileorDatabase
if(newDate>oldDate)
{
//the date has changed look busy
}
}
Hope this gets you going.

Replace GString tags in a file

i've got a word document saved in xml format. In this document, there are some GString Tag like $name.
In my groovy code, i load the xml file to replace this GString tag like this:
def file = new File ('myDocInXml.xml')
def name = 'myName'
file.eachLine { line ->
println line
}
But it doesn't works. The GString Tag doesn't be replaced by my variable 'name'.
Could anyone help me ?
THX
Better to use a templating here. Load the xmml file as a template and create a binding to replace the placeholders. A simple example could be like
def xml='''
<books>
<% names.each { %>
<book>
$it
</book>
<%}%>
</books>
'''
def engine=new groovy.text.SimpleTemplateEngine()
def template=engine.createTemplate(xml)
def binding=[names:['john','joe']]
template.make(binding)
Currently templating is the approach. But you might want to keep an eye on this issue in JIRA GROOVY-2505. It is a feature request to convert a String to a GString in cases when the string is read from an external source:
Several times it has been asked on the
mailing list on how to either convert
a String to a GString or to evaluate a
String as a GString. The need arises
when a String comes in from an
external source and contains a GString
expression, for example an XML file or
a Configuration file. Currently one
needs to either invoke the GroovyShell
or the SimpleTemplateEngine to
accomplish the task. In both cases
this takes several lines of code and
is not intuitively obvious. One could
ether add a GDK method to String such
as "evaluate" (which in my humble
opinion would be the nicest) or
provide a conversion of the form
"String as GString"
Pretty old question, however, issue http://jira.codehaus.org/browse/GROOVY-2505 still not solved...
There is a nice workaround, which behaves almost like GString substitution, by using Apache StrSubstitutor class. For me it is more comfortable than creating templates - you can use GStrings in XML files:
import org.apache.commons.lang.text.StrSubstitutor
strResTpl = new File(filePath + "example.xml").text
def extraText = "MY EXTRA TEXT"
map = new HashMap();
map.put("text_to_substitute", "example text - ${extraText}")
def result = new StrSubstitutor(map).replace(strResTpl);
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<eample>
<text_to_substitute>${text_to_substitute}</text_to_substitute>
</example>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<eample>
<text_to_substitute>example text - MY EXTRA TEXT</text_to_substitute>
</example>

Resources