DSL progress

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

DSL progress

Vadim Chekan
He he.
I've just run successfully this route:

   <bean id="dsl" class="org.mycompany.test.Main">
    <property name="DSLRoute"><value>
    route {
    from 'timer://kickoff?period=10000&amp;delay=1'
    to 'mock:vadim'
    }
        </value></property>
   </bean>

At the moment I use *Type classes but I'm working on injecting routes
directly into camel context now.

Vadim.
Reply | Threaded
Open this post in threaded view
|

Re: DSL progress

jstrachan
2008/9/5 Vadim Chekan <[hidden email]>:

> He he.
> I've just run successfully this route:
>
>  <bean id="dsl" class="org.mycompany.test.Main">
>        <property name="DSLRoute"><value>
>                route {
>                        from 'timer://kickoff?period=10000&amp;delay=1'
>                        to 'mock:vadim'
>                }
>        </value></property>
>  </bean>

Awesome! :)

Did you try out the xtext stuff to generate a nice context
highlighting editor as well?

> At the moment I use *Type classes but I'm working on injecting routes
> directly into camel context now.

Creating the *Type classes sounds good btw - the classes in
org.apache.camel.model; then we can just add the RoutesType to the
CamelContext and share the same code to take the AST and turn it into
the actual running routes. Is that kinda what you meant?

--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com
Reply | Threaded
Open this post in threaded view
|

Re: DSL progress

Vadim Chekan
James Strachan wrote:

> Awesome! :)
>
> Did you try out the xtext stuff to generate a nice context
> highlighting editor as well?
>
>> At the moment I use *Type classes but I'm working on injecting routes
>> directly into camel context now.
>
> Creating the *Type classes sounds good btw - the classes in
> org.apache.camel.model; then we can just add the RoutesType to the
> CamelContext and share the same code to take the AST and turn it into
> the actual running routes. Is that kinda what you meant?
>

No I haven't look at xtext yet. The next task on my plate is When it's
done I'll start hacking eclipse.

Type classes. Well, my idea was to avoid their usage and inject concrete
route objects into the camel context directly. Just for sake of avoiding
extra dependencies.
But after some more code reading I discovered that a lot of business is
done in Type classes. Wrapping routes into exception handlers, hooking
up interceptors, etc.
So I decided I will use Type classes. I could experiment with
implementing this logic in parser itself, but why would I do something
that already is implemented? BTW, it is not rhetorical question so if
there is a reason, let me know :)

Overall it turned out to be much simpler task then I anticipated. Kudos
to antlrworks. This tool changed my opinion about LL parsers
(top-to-down parser). I am ready to trade LR parser for what this tool
gives to you. Ability to debug your *grammar* even before you compile it
with the application saves tons of time and is just fun to watch and
play with.
And when it shows a visual graph with conflicting choices in the
grammar... man, I was just speechless when I discovered it.

Questions:
Here's what I'm considering as I'm doing it. First of all some kind of
code reusage support. What if you have the same route definition in
several places? Do we want to support some kind of macro?
If we do, then the next thing users will ask is "I have a macro, but in
each application I need to set a parameter in it, for example queue
name". As I'm trying to keep domain of this DSL small and avoid yet
another interpreter. So I think it'd be beneficial to integrate this DSL
with scripting languages which we already have in camel.
Another thing is that we need external calls in order to implement
conditions. I'd rather delegate expressions evaluation and keep DSL a
routing only language.

For example dsl:

route from {
        // must return list of strings or null
        // TODO: where getMySourceList is declared???
        juel:getMySourceList('InputQueue', 'Integration')
} to {
        'mock:a1'
        if(javascript('headers.header["a"] == "b"')) {
                'a2'
        } else {
                multicast {
                        'a3'
                        'a4'
                }
        }
}


Comments are more than welcome,
Vadim.

P.S. Here is the current parser. There is a decision conflict around
"else" and I'm working on it.
=====================================================================
grammar Camel;
options { output = AST; ASTLabelType = CommonTree;}

tokens {
        ROUTES; PROCESS;
}

@header { package org.mycompany.test; }
@lexer::header { package org.mycompany.test; }

routes :
        route+ -> ^(ROUTES route*)
        ;

route :
        ROUTE from (to)?
        ;
       
from :
        FROM endpoints
        ;

to :
        TO endpoints
        ;

endpoints :
        endpoint
        | '{' endpoint+ '}'
        ;


endpoint:
        SINGLE_Q_STRING
        | IF '(' function ')' endpoints (ELSE endpoints )? // TODO: make sure
that "sliding else" works correctly
        ;

function:
        lang ':' LITERAL params?
        ;
       
params :
        '(' (SINGLE_Q_STRING (',' SINGLE_Q_STRING)* )* ')'
        ;

/*process :
        BEAN SINGLE_Q_STRING -> ^(BEAN SINGLE_Q_STRING)
        | MULTICAST '{' process* '}' -> ^(MULTICAST process*)
        //| PIPELINE '{' process_list '}'
        | SPLIT SINGLE_Q_STRING
        //| AGGREGATE expression
        | AGGREGATE
        | resequence
        ;

resequence :
        RESEQUENCE BATCH
        //| BATCH RESEQUENCE expression
        | BATCH RESEQUENCE NUMBER NUMBER
        //| BATCH RESEQUENCE NUMBER NUMBER '(' expression_list ')'
        //| STREAM RESEQUENCE resequence_options
        //| STREAM RESEQUENCE resequence_options '(' expression ')'
        ;
*/
       
lang :
        'el'
        |'xpath'
        |'ognl'
        |'beanshell'
        |'javascript'
        |'groovy'
        |'python'
        |'php'
        |'ruby'
        ;

ROUTE : 'route';
FROM : 'from';
TO : 'to';
BEAN : 'bean';
MULTICAST: 'multicast';
PIPELINE: 'pipeline';
SPLIT : 'split';
AGGREGATE: 'aggregate';
RESEQUENCE: 'resequence';
BATCH : 'batch';
STREAM : 'stream';

IF : 'if';
ELSE : 'else';

NUMBER : '0'..'9'+;
SINGLE_Q_STRING : '\'' ~('\'')* '\'' {setText(getText().substring(1,
getText().length()-1));} ;
LITERAL : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;

WS  : (' '|'\r'|'\n'|'\t')+ {$channel = HIDDEN;} ;
COMMENT
     :   '/*' .* '*/' {$channel=HIDDEN;}
     ;
LINE_COMMENT
     : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
     ;

Reply | Threaded
Open this post in threaded view
|

Re: DSL progress

jstrachan
2008/9/6 Vadim Chekan <[hidden email]>:

> James Strachan wrote:
>>
>> Awesome! :)
>>
>> Did you try out the xtext stuff to generate a nice context
>> highlighting editor as well?
>>
>>> At the moment I use *Type classes but I'm working on injecting routes
>>> directly into camel context now.
>>
>> Creating the *Type classes sounds good btw - the classes in
>> org.apache.camel.model; then we can just add the RoutesType to the
>> CamelContext and share the same code to take the AST and turn it into
>> the actual running routes. Is that kinda what you meant?
>>
>
> No I haven't look at xtext yet. The next task on my plate is When it's done
> I'll start hacking eclipse.
>
> Type classes. Well, my idea was to avoid their usage and inject concrete
> route objects into the camel context directly. Just for sake of avoiding
> extra dependencies.
> But after some more code reading I discovered that a lot of business is done
> in Type classes. Wrapping routes into exception handlers, hooking up
> interceptors, etc.
> So I decided I will use Type classes.

Great stuff; if nothing else it should hopefully help you get started
and not have to worry about the internal details on how the patterns
work etc.


> I could experiment with implementing
> this logic in parser itself, but why would I do something that already is
> implemented? BTW, it is not rhetorical question so if there is a reason, let
> me know :)

It'd certainly help if all the DSLs shared the same AST - the model
objects; rather than having lots of different ways to turn the AST
into the actual runtime objects. I'm sure the AST could be improved
some; its a tad complex to read as its also implemented as JAXB beans
so that the AST can be faithfully round tripped to and from XML.


> Overall it turned out to be much simpler task then I anticipated. Kudos to
> antlrworks. This tool changed my opinion about LL parsers (top-to-down
> parser). I am ready to trade LR parser for what this tool gives to you.
> Ability to debug your *grammar* even before you compile it with the
> application saves tons of time and is just fun to watch and play with.
> And when it shows a visual graph with conflicting choices in the grammar...
> man, I was just speechless when I discovered it.

:)

The eclipse plugin is pretty good too btw!

> Questions:
> Here's what I'm considering as I'm doing it. First of all some kind of code
> reusage support. What if you have the same route definition in several
> places? Do we want to support some kind of macro?
> If we do, then the next thing users will ask is "I have a macro, but in each
> application I need to set a parameter in it, for example queue name". As I'm
> trying to keep domain of this DSL small and avoid yet another interpreter.
> So I think it'd be beneficial to integrate this DSL with scripting languages
> which we already have in camel.

Having a macro sounds good; i guess a macro with defined parameters
then folks can instantiate them.

Maybe something that looks a bit like a method call?

def foo(a, b) {
  if (xpath("/foo/bar")) {
    to a
  else {
    to b
  }
}

// then later on in some route

route from "activemq:Foo"
  foo("activemq:Cheese", "file://something")
  foo("activemq:Bar", "file://whatnot")

> Another thing is that we need external calls in order to implement
> conditions. I'd rather delegate expressions evaluation and keep DSL a
> routing only language.
>
> For example dsl:
>
> route from {
>        // must return list of strings or null
>        // TODO: where getMySourceList is declared???
>        juel:getMySourceList('InputQueue', 'Integration')
> } to {
>        'mock:a1'
>        if(javascript('headers.header["a"] == "b"')) {
>                'a2'
>        } else {
>                multicast {
>                        'a3'
>                        'a4'
>                }
>        }
> }
>
>
> Comments are more than welcome,
> Vadim.
>
> P.S. Here is the current parser. There is a decision conflict around "else"
> and I'm working on it.

Looks great! :)
--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com
Reply | Threaded
Open this post in threaded view
|

Re: DSL progress

jstrachan
BTW once the eclipse xtext thing is done; I'm sure it'll be really
easy to link it into the visualisation plugin...
http://code.google.com/p/camel-route-viewer/

i.e. so you edit the DSL then at any time can see a graphical picture
of the route.

--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com