Camel - extend Java DSL? - apache-camel

I've got a repeating pattern in my routes - a certain Processor needs the same 3 Headers set every time I call it, so I've got the following code in my routes about 10+ times:
.whatever()
.setHeader("foo1", "bar1")
.setHeader("foo2", "bar2")
.setHeader("foo3", "bar3")
.processRef("processorBazThatNeedsHeaders")
.whatever()
The headers are populated differently every time, so abstracting this out into a subroute doesn't really buy me anything.
What I love to be able to do is subclass RouteDefinition to have another method in my DSL that would allow me to do this:
.whatever()
.bazProcessor("bar1", "bar2", "bar3")
.whatever()
and in 'bazProcessor', set the headers and call the processor.
I've tried to do this but it seems that it's only possible with some serious probably-not-future-proof surgery, and it seems that others have had similar luck.
I need them to be set as headers as opposed to passing them as parameters directly to the processor because the values are also used after the processor for routing.
Is there some hidden facility to achieve something like this?

By subclassing the RouteDefinition your extension will only be visible direct after from(...). This could be a limitation if you would like to use the DSL extension for example after the filter(...) DSL.
A simpler approach would be to encapsulate the logic somewhere, and use it in a class that implements the org.apache.camel.Processor interface, and then call an overload of .process(...), or bean(...) in the route to use the logic. You will be actually very closed to a DSL extension if you use a meaningful name for the Processor instance or a method, that returns that Processor instance. Here is an example of the suggested approach. At the end, your code could look like:
.whatever()
.process(setTheHeadersForBaz)
.whatever()
Just for reference: if you really need to do a DSL, there is a project that extends the Camel DSL based on Groovy. I guess a Scala way based on the Camel Scala DSL could be also an option.

Though slightly irrelevant, following is an example of extending Scala DSL.
We can create an implicit methods to DSL trait via an implicit class.
object DSLImplicits {
implicit class RichDSL(val dsl: DSL) {
def get = dsl.setHeader(Exchange.HTTP_METHOD, _ => HttpMethods.GET.name)
def post = dsl.setHeader(Exchange.HTTP_METHOD, _ => HttpMethods.POST.name)
}
}
And use it like this.
import DSLImplicits.RichDSL
//----------------------------
from("someWhere")
//Do some processing
.get.to("http://somewhere.com")
More details #
http://siliconsenthil.in/blog/2013/07/11/apache-camel-with-scala-extending-dsl/

So you only set the headers because you want the Processor to have access to those values?
If so then a simple example using a Factory could look like this:
whatever()
.process(BazProcessorFactory.instance("bar1", "bar2", "bar3"))
.whatever()
Where the BazProcessorFactory is just a wrapper around your Processor:
public class BazProcessorFactory {
public Processor instance(final String...vals) {
return new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
//access your array of values here
System.out.println("Foo1 = "+vals[0]);
}
}
}
}

Related

State Schema Evolution with POJOs

I'm using flink 1.11 with Scala and i have a question regarding the schema evolution using a POJO.
In the documentation is written, that POJOs are supported for state schema evolution (with some limitations).
Are Scala case clases also considered as POJO and therefore supported?
case class WordCount(word: String, count: Int)
Or have i to write something like this:
class WordCount(var word: String, var count: Int) {
def this() {
this(null, -1)
}
}
Case classes are not POJOs. In particular, they do not satisfy:
The class has a public no-argument constructor
All non-static, non-transient fields in the class (and all superclasses) are either public (and non-final) or have a public getter- and a setter- method that follows the Java beans naming conventions for getters and setters. (afaik case classes have final fields with getters in the generated JVM class)
You can implement all required things in a normal scala class but your IDE might not support you well. An option is to create your class in Java, let your IDE beanify it and convert it to scala (or use it directly).
There is also the option to create evolution support for case classes with a custom serializer. That will eventually be available by Flink. (You could also go ahead and contribute it).

Apache Camel - java DSL - transform body to one of its fields

First, I'm fairly new to Camel so if what (or how) I'm trying to do here is dumb, let me know.
CODE:
from("direct:one")
.to("mock:two")
.process(new Processor(){
#Override
public void process(Exchange exchange)throws Exception{
MyCustomObject obj = exchange.getIn().getBody(MyCustomObject.class);
exchange.getOut().setBody(obj.getOneOfTheFields());
}
})
.to("mock:three");
QUESTION:
This processor transforms an object to one of it's fields. I know that I could replace it with simple expression but that would require me to put 'oneOfTheFields' in a string and I don't want to do that.
Is there a shorter way to do this using java code only?
This can be easily achieved using setBody and Camel simple:
from("direct:one")
.to("mock:two")
.setBody(simple("${body.fieldName}"))
.to("mock:three");
You specify the name of the field, and Camel will use the standard accessor mechanism to set the body appropriately.
Can you not simply do this:
from("direct:one")
.to("mock:two")
.setBody(body().getOneOfTheFields())
.to("mock:three");
Let me know if this works.

ApiTransformer for parametrized, unavailable type

I'm using Objectify and wish to have its Key<> type passed around in my API. I've created an ApiTransformer, but my questions is where to declare it, since the serialized Key<> class is not available, hence I cannot declare its transformer as a class annotation. I tried declaring it in the #Api annotation, but it doesn't work, I still get the error:
There was a problem generating the API metadata for your Cloud Endpoints classes: java.lang.IllegalArgumentException: Parameterized type com.googlecode.objectify.Key<[my package].User> not supported.
The ApiTransformer looks like:
public class KeyTransformer implements Transformer<Key<?>, String> {
public String transformTo(Key<?> in) {
return in.getString();
}
public Key<?> transformFrom(String in) {
return Key.valueOf(in);
}
}
And in my #Api I have:
#Api(name = "users", version = "v1",transformers = {KeyTransformer.class})
Unfortunately you can't. As you said you need to declare it on the Key class, your only chances to make this work are either.
1) Recompile the Key class for objectify with the #transformer annotation.
2) Extend the Key class with your own implementation and define the transformer there.
I don't really like any of those options so the way i usually resolve this is to hide the key object getter (by using #ApiResourceProperty(ignored=AnnotationBoolean.TRUE)) and only expose the id from that key.
That way you get a Endpoints frendly object, the only downside is you'll have to reconstitute the key using Key.create(YourClass.class, longId) manually whenever you need it.
You can add transforms to 3rd party classes by listing the transform in #Api annotation. I'm not dead sure it'll work parameterized class, but I don't see why not.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Api#transformers()

solr: read stopword.txt in Custom Handler

I want to read stopword.txt in my custom handler. How to do that ? I know that this is used in Filtering and can be done from there. But I need to read that list in my Custom UpdateRequestProcessorFactory. Also can I read any other custom file created by me.
I was aware that limitation. I overlooked that you are using about update processor.
I looked into the code, here is an existing code you can use as example. SolrCoreAware is the interface you are after.
public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcessorFactory implements SolrCoreAware
#Override
public void inform(SolrCore core) {
resourceLoader = core.getResourceLoader();
}
Classes that implement org.apache.lucene.analysis.util.ResourceLoaderAware can read files under conf directory. However what it your use case anyway?
looks like xy problem

apache-camel file2 antInclude ignore-case

I need to poll a directory and narrow the files with a case insentive expression.
With version 2.10 camel adds support for antInclude which is what I look into, unfortunately antInclude is case sensitive, so are other filtering expressions. Implementing GenericFileFilter is not an option, since the filtering patterns are not known at compile time as I read them from database at runtime and I have multiple file rules each with a different pattern.
I programmatically create several routes in a loop, where each file route has a different case insensitive filtering pattern. I would appreciate if camel file component supports case insensitive expressions, or is there any other way without creating myself a new file component in camel?
public class MyRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
Vector<FileTransferEntity> list = TransferDAO.getTransferList();
for(FileTransferEntity t : list) {
fromF("ftp://ftpuser#ftpserver/some-directory?antInclude=%s", t.getFileMask()).
toF("mock:result");//depending on t, action will change.
}
}
should be able to use a custom filter instead...see camel-file2 for information or see this example...
https://svn.apache.org/repos/asf/camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileConsumerFileFilterTest.java

Resources