spring-projects / spring-integration

Spring Integration provides an extension of the Spring programming model to support the well-known Enterprise Integration Patterns (EIP)
http://projects.spring.io/spring-integration/
Apache License 2.0
1.54k stars 1.1k forks source link

Add support for dynamic Resource-backed SpEL expressions [INT-1382] #5380

Closed spring-operator closed 13 years ago

spring-operator commented 14 years ago

Mark Fisher opened INT-1382 and commented

Currently, we support "expression" attributes on all of the primary component types (e.g. transformer, filter, router, splitter, ...).

Those expression values are passed in the XML attribute at configuration time:

<transformer input-channel="in" expression="payload.address.city" output-channel="out"/>

Today, we do support externalization of the expression value by using yet another expression, or more commonly a property-placeholder:

<transformer input-channel="in" expression="${expressions.transformation.userToCity}" output-channel="out"/>

Even with the externalization however, the actual Expression instance is created at initial startup only.

Meanwhile, with Groovy support, we have the ability to change the backing script at runtime. We should provide the same option for SpEL expressions (with a configurable cache + refresh time setting). To do this, we should probably just add support for an inner "expression" sub-element that would look similar to using an inner bean in place of a "ref" attribute, but in this case, it would be an alternative to using an "expression" attribute. For example:

<transformer input-channel="in" output-channel="out">
    <expression location="file:///example/expressions.properties" key="transformation.userToCity" refresh-check-delay="60000"/>
</transformer>

The problem with the above example is that location would likely end up being duplicated throughout the configuration. A referenced Resource instance would be better, and in that case the Resource instance itself should have the refresh-check-delay (since it would apply at the level of Resource-loading). Perhaps something like this would be better:

<transformer input-channel="in" output-channel="out">
    <expression source="myExpressions" key="transformation.userToCity"/>
</transformer>

<expression-source id="myExpressions" location="file:///example/expressions.properties" refresh-check-delay="60000"/>

2 votes, 2 watchers

spring-operator commented 14 years ago

Dave Syer commented

It feels uncomfortable to tie the expression source to a properties file in the expression element itself. In the groovy case were you thinking it would be one expression per file (that's the only way it would work with Spring scripting support right now, I think)?

It would feel more natural to me to use a placeholder for the expression. Maybe a custom scope is better if a change to the expression source might invalidate many expressions all at once?

<transformer input-channel="in" output-channel="out">
    <expression value="${transformation.userToCity}" scope="refresh"/>
</transformer>

(or just refresh="true").

This is actually a special case of a fairly general pattern that could be applied to many Spring apps.

spring-operator commented 14 years ago

Mark Fisher commented

What about that last example above? There is an expression source with a refresh-check-delay. It can hold multiple expression key value pairs. Then in the actual \ element it only needs to reference the "source" and the "key".

spring-operator commented 14 years ago

Dave Syer commented

I still don't like it. For me this is not a production time use case, so it's questionable whether we should support it at all. Supposing it really is a production use case then restricting the source of expressions to a file is too restrictive, even if it is hidden behind a Resource. Maybe you can persuade me that the expression source can be a Properties instance, making it explicitly a dereference (as opposed to implicitly in the case of a custom scope). Then to make the expression refreshable I only need to implement a Properties instance that updates itself (eg from a database).

spring-operator commented 14 years ago

Mark Fisher commented

Please have a look at the prototype in the sandbox. ExpressionSource is in no way restricted to a File. It simply has a getExpression() method. The implementation possibilities are endless.

spring-operator commented 14 years ago

Mark Fisher commented

Also, I'm wondering why you don't think this would ever be a production use-case. I'm assuming you would not say the same for the Groovy script refresh?

Personally, I would be very careful with either SpEL or Groovy being modified at runtime, but people clearly want these dynamic options.

spring-operator commented 14 years ago

Mark Fisher commented

I'm moving this to RC1 so we can continue to evolve the example in the sandbox as well as explore alternative implementation ideas (e.g. generic scope options).

spring-operator commented 13 years ago

Dave Syer commented

I'm OK with what we have in the prototype, as long as there is an abstraction. Still not sure I like the whole feature, but if we have to do it in 2.0, this is a reasonable approach.

spring-operator commented 13 years ago

Mark Fisher commented

I guess you are suggesting that post 2.0, we might be able to rely upon a refreshable Scope?

spring-operator commented 13 years ago

Mark Fisher commented

I have committed the core functionality. For the namespace support, I'm planning the following:

<transformer input-channel="in" output-channel="out">
    <expression source="myExpressions" key="transformation.userToCity"/>
</transformer>

<expression-source id="myExpressions" location="file:///example/expressions.properties" cache-seconds="60"/>

The "source" attribute should probably be optional, since a single "expressionSource" bean would likely be a common configuration.

spring-operator commented 13 years ago

Mark Fisher commented

Everything is committed for this support on transformer, filter, router, splitter, and header-enricher. The 'expression' sub-element can be defined like so:

<transformer input-channel="in" output-channel="out">
    <expression source="myExpressions" key="transformation.userToCity"/>
</transformer>

The "source" attribute is optional. It will default to resolving a bean named "expressionSource".

We don't have namespace support for the "expressionSource" at this point, but it's simple to create as a bean. In fact the one implementation that is available out-of-the-box is exactly like a "messageSource" definition in Spring MVC, e.g.:

<bean id="expressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
    <property name="basename" value="/some/path/expressions"/>
</bean>

Any implementation of the ExpressionSource interface can be referenced.