Overriding Properties

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

Overriding Properties

Matt Raible
Hello all,

In my Camel app, I recently integrated Spring Boot to use its external configuration feature. Basically, I can put default properties in an src/main/resources/application.properties file. Then I can override them on different servers using various mechanisms, from using a config/application.properties on the classpath to using environment variables. It's really slick in that you can have a single WAR that can be configured for dev, test, staging and prod.

To read these properties in my Camel routes, I'm using the following setupCamelContext() method to read this file in. Problem is, it doesn't hook into Spring Boot's external override mechanism. Is there anyway to make Camel aware of Spring Boot's override feature?

@Configuration
@ImportResource("classpath:META-INF/cxf/cxf.xml")
@ComponentScan("com.company.app")
public class CamelConfig extends CamelConfiguration {
        @Value("${logging.trace.enabled}")
        private Boolean tracingEnabled;

        @Override
        protected void setupCamelContext(CamelContext camelContext) throws Exception {
                PropertiesComponent pc = new PropertiesComponent();
                pc.setLocation("classpath:application.properties");
                camelContext.addComponent("properties", pc);
                // see if trace logging is turned on
                if (tracingEnabled) {
                        camelContext.setTracing(true);
                }
                super.setupCamelContext(camelContext);
        }

        @Bean
        public Tracer camelTracer() {
                Tracer tracer = new Tracer();
                tracer.setTraceExceptions(false);
                tracer.setTraceInterceptors(true);
                tracer.setLogName("com.company.app.trace");
                return tracer;
        }
}

If not, I'm able to workaround this issue by changing the properties from being inlined to being separate Strings. For example, here's an onException handler that doesn't pick up overridden properties:

public abstract class AbstractRouteBuilder extends RouteBuilder {

        /**
         * Default exception handling for all routes.
         *
         * @throws Exception
         */
        @Override
        public void configure() throws Exception {
                onException(Exception.class)
                                .setHeader("routeId", property(Exchange.FAILURE_ROUTE_ID))
                                .setHeader("endpoint", property(Exchange.FAILURE_ENDPOINT))
                                .setHeader("exception", property(Exchange.EXCEPTION_CAUGHT))
                                .setHeader("subject", simple("Message Broker Error ({{esb.env}}) - ${exception.class.simpleName}"))
                                .transform(simple("${exception.message}\n\nStacktrace Details:\n\n${exception.stacktrace}"))
                                .to("freemarker:/templates/mail/error.ftl")
                                .to("smtp://{{mail.host}}?contentType=text/plain&to={{esb.alert.email}}" +
                                                "&from={{mail.from}}&subject=${headers.subject})");
        }
}

If I change it to the following, things work as expected:

public abstract class AbstractRouteBuilder extends RouteBuilder {

        @Value("${esb.env}")
        private String esbEnv;

        @Value("${esb.alert.email}")
        private String esbAlertEmail;

        /**
         * Default exception handling for all routes.
         *
         * @throws Exception
         */
        @Override
        public void configure() throws Exception {
                onException(Exception.class)
                                .setHeader("routeId", property(Exchange.FAILURE_ROUTE_ID))
                                .setHeader("endpoint", property(Exchange.FAILURE_ENDPOINT))
                                .setHeader("exception", property(Exchange.EXCEPTION_CAUGHT))
                                .setHeader("subject", simple("Message Broker Error (" + esbEnv + ") - ${exception.class.simpleName}"))
                                .transform(simple("${exception.message}\n\nStacktrace Details:\n\n${exception.stacktrace}"))
                                .to("freemarker:/templates/mail/error.ftl")
                                .to("smtp://{{mail.host}}?contentType=text/plain&to=" + esbAlertEmail +
                                                "&from={{mail.from}}&subject=${headers.subject})");
        }
}

However, I'd rather not have to do this for each route.

Please let me know if it's possible to make Camel aware of Spring Boot's external properties configuration.

Thanks,

Matt
Reply | Threaded
Open this post in threaded view
|

Re: Overriding Properties

Claus Ibsen-2
Hi

Yeah not sure if Spring Boot does stuff differently than regular
spring framework in term of the property placeholder stuff.

Have you tried that spring -> camel bridge, there is a note about it
on the top of this page that points you to it
http://camel.apache.org/using-propertyplaceholder.html

And yeah if some code is missing to make this easier, then maybe we
can do something in camel-spring, or we could introduce a new
camel-spring-boot module that integrates the spring boot with Camel.
Maybe there is other spring boot stuff we need to take a look at.




On Fri, Aug 8, 2014 at 4:15 AM, Matt Raible <[hidden email]> wrote:

> Hello all,
>
> In my Camel app, I recently integrated Spring Boot to use its external configuration feature. Basically, I can put default properties in an src/main/resources/application.properties file. Then I can override them on different servers using various mechanisms, from using a config/application.properties on the classpath to using environment variables. It's really slick in that you can have a single WAR that can be configured for dev, test, staging and prod.
>
> To read these properties in my Camel routes, I'm using the following setupCamelContext() method to read this file in. Problem is, it doesn't hook into Spring Boot's external override mechanism. Is there anyway to make Camel aware of Spring Boot's override feature?
>
> @Configuration
> @ImportResource("classpath:META-INF/cxf/cxf.xml")
> @ComponentScan("com.company.app")
> public class CamelConfig extends CamelConfiguration {
>         @Value("${logging.trace.enabled}")
>         private Boolean tracingEnabled;
>
>         @Override
>         protected void setupCamelContext(CamelContext camelContext) throws Exception {
>                 PropertiesComponent pc = new PropertiesComponent();
>                 pc.setLocation("classpath:application.properties");
>                 camelContext.addComponent("properties", pc);
>                 // see if trace logging is turned on
>                 if (tracingEnabled) {
>                         camelContext.setTracing(true);
>                 }
>                 super.setupCamelContext(camelContext);
>         }
>
>         @Bean
>         public Tracer camelTracer() {
>                 Tracer tracer = new Tracer();
>                 tracer.setTraceExceptions(false);
>                 tracer.setTraceInterceptors(true);
>                 tracer.setLogName("com.company.app.trace");
>                 return tracer;
>         }
> }
>
> If not, I'm able to workaround this issue by changing the properties from being inlined to being separate Strings. For example, here's an onException handler that doesn't pick up overridden properties:
>
> public abstract class AbstractRouteBuilder extends RouteBuilder {
>
>         /**
>          * Default exception handling for all routes.
>          *
>          * @throws Exception
>          */
>         @Override
>         public void configure() throws Exception {
>                 onException(Exception.class)
>                                 .setHeader("routeId", property(Exchange.FAILURE_ROUTE_ID))
>                                 .setHeader("endpoint", property(Exchange.FAILURE_ENDPOINT))
>                                 .setHeader("exception", property(Exchange.EXCEPTION_CAUGHT))
>                                 .setHeader("subject", simple("Message Broker Error ({{esb.env}}) - ${exception.class.simpleName}"))
>                                 .transform(simple("${exception.message}\n\nStacktrace Details:\n\n${exception.stacktrace}"))
>                                 .to("freemarker:/templates/mail/error.ftl")
>                                 .to("smtp://{{mail.host}}?contentType=text/plain&to={{esb.alert.email}}" +
>                                                 "&from={{mail.from}}&subject=${headers.subject})");
>         }
> }
>
> If I change it to the following, things work as expected:
>
> public abstract class AbstractRouteBuilder extends RouteBuilder {
>
>         @Value("${esb.env}")
>         private String esbEnv;
>
>         @Value("${esb.alert.email}")
>         private String esbAlertEmail;
>
>         /**
>          * Default exception handling for all routes.
>          *
>          * @throws Exception
>          */
>         @Override
>         public void configure() throws Exception {
>                 onException(Exception.class)
>                                 .setHeader("routeId", property(Exchange.FAILURE_ROUTE_ID))
>                                 .setHeader("endpoint", property(Exchange.FAILURE_ENDPOINT))
>                                 .setHeader("exception", property(Exchange.EXCEPTION_CAUGHT))
>                                 .setHeader("subject", simple("Message Broker Error (" + esbEnv + ") - ${exception.class.simpleName}"))
>                                 .transform(simple("${exception.message}\n\nStacktrace Details:\n\n${exception.stacktrace}"))
>                                 .to("freemarker:/templates/mail/error.ftl")
>                                 .to("smtp://{{mail.host}}?contentType=text/plain&to=" + esbAlertEmail +
>                                                 "&from={{mail.from}}&subject=${headers.subject})");
>         }
> }
>
> However, I'd rather not have to do this for each route.
>
> Please let me know if it's possible to make Camel aware of Spring Boot's external properties configuration.
>
> Thanks,
>
> Matt



--
Claus Ibsen
-----------------
Red Hat, Inc.
Email: [hidden email]
Twitter: davsclaus
Blog: http://davsclaus.com
Author of Camel in Action: http://www.manning.com/ibsen
hawtio: http://hawt.io/
fabric8: http://fabric8.io/
Reply | Threaded
Open this post in threaded view
|

Re: Overriding Properties

Matt Raible
On Sat, Aug 9, 2014 at 2:47 AM, Claus Ibsen <[hidden email]> wrote:

> Hi
>
> Yeah not sure if Spring Boot does stuff differently than regular
> spring framework in term of the property placeholder stuff.
>

Yes, it does. From
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
:

Spring Boot uses a very particular PropertySource order that is designed to
allow sensible overriding of values, properties are considered in the the
following order:

Command line arguments.
Java System properties (System.getProperties()).
OS environment variables.
JNDI attributes from java:comp/env
A RandomValuePropertySource that only has properties in random.*.
Application properties outside of your packaged jar (application.properties
including YAML and profile variants).
Application properties packaged inside your jar (application.properties
including YAML and profile variants).
@PropertySource annotations on your @Configuration classes.
Default properties (specified using SpringApplication.setDefaultProperties).

It's also smart enough to know that a "ESB_ENV" environment variable should
override a "esb.env" property.


> Have you tried that spring -> camel bridge, there is a note about it
> on the top of this page that points you to it
> http://camel.apache.org/using-propertyplaceholder.html


Yep, I have this in my CamelConfig.java:

@Override
protected void setupCamelContext(CamelContext camelContext) throws
Exception {
PropertiesComponent pc = new PropertiesComponent();
 pc.setLocation("classpath:application.properties");
camelContext.addComponent("properties", pc);
 super.setupCamelContext(camelContext);
}


>
>
> And yeah if some code is missing to make this easier, then maybe we
> can do something in camel-spring, or we could introduce a new
> camel-spring-boot module that integrates the spring boot with Camel.
> Maybe there is other spring boot stuff we need to take a look at.
>
>
>
I have a workaround, so this isn't urgent. However, it would be pretty
sweet if Camel was smart enough to know about these overrides.

This seems to be one of the main classes for making this work in Spring
Boot:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java


>
>
> On Fri, Aug 8, 2014 at 4:15 AM, Matt Raible <[hidden email]>
> wrote:
> > Hello all,
> >
> > In my Camel app, I recently integrated Spring Boot to use its external
> configuration feature. Basically, I can put default properties in an
> src/main/resources/application.properties file. Then I can override them on
> different servers using various mechanisms, from using a
> config/application.properties on the classpath to using environment
> variables. It's really slick in that you can have a single WAR that can be
> configured for dev, test, staging and prod.
> >
> > To read these properties in my Camel routes, I'm using the following
> setupCamelContext() method to read this file in. Problem is, it doesn't
> hook into Spring Boot's external override mechanism. Is there anyway to
> make Camel aware of Spring Boot's override feature?
> >
> > @Configuration
> > @ImportResource("classpath:META-INF/cxf/cxf.xml")
> > @ComponentScan("com.company.app")
> > public class CamelConfig extends CamelConfiguration {
> >         @Value("${logging.trace.enabled}")
> >         private Boolean tracingEnabled;
> >
> >         @Override
> >         protected void setupCamelContext(CamelContext camelContext)
> throws Exception {
> >                 PropertiesComponent pc = new PropertiesComponent();
> >                 pc.setLocation("classpath:application.properties");
> >                 camelContext.addComponent("properties", pc);
> >                 // see if trace logging is turned on
> >                 if (tracingEnabled) {
> >                         camelContext.setTracing(true);
> >                 }
> >                 super.setupCamelContext(camelContext);
> >         }
> >
> >         @Bean
> >         public Tracer camelTracer() {
> >                 Tracer tracer = new Tracer();
> >                 tracer.setTraceExceptions(false);
> >                 tracer.setTraceInterceptors(true);
> >                 tracer.setLogName("com.company.app.trace");
> >                 return tracer;
> >         }
> > }
> >
> > If not, I'm able to workaround this issue by changing the properties
> from being inlined to being separate Strings. For example, here's an
> onException handler that doesn't pick up overridden properties:
> >
> > public abstract class AbstractRouteBuilder extends RouteBuilder {
> >
> >         /**
> >          * Default exception handling for all routes.
> >          *
> >          * @throws Exception
> >          */
> >         @Override
> >         public void configure() throws Exception {
> >                 onException(Exception.class)
> >                                 .setHeader("routeId",
> property(Exchange.FAILURE_ROUTE_ID))
> >                                 .setHeader("endpoint",
> property(Exchange.FAILURE_ENDPOINT))
> >                                 .setHeader("exception",
> property(Exchange.EXCEPTION_CAUGHT))
> >                                 .setHeader("subject", simple("Message
> Broker Error ({{esb.env}}) - ${exception.class.simpleName}"))
> >
> .transform(simple("${exception.message}\n\nStacktrace
> Details:\n\n${exception.stacktrace}"))
> >
> .to("freemarker:/templates/mail/error.ftl")
> >
> .to("smtp://{{mail.host}}?contentType=text/plain&to={{esb.alert.email}}" +
> >
> "&from={{mail.from}}&subject=${headers.subject})");
> >         }
> > }
> >
> > If I change it to the following, things work as expected:
> >
> > public abstract class AbstractRouteBuilder extends RouteBuilder {
> >
> >         @Value("${esb.env}")
> >         private String esbEnv;
> >
> >         @Value("${esb.alert.email}")
> >         private String esbAlertEmail;
> >
> >         /**
> >          * Default exception handling for all routes.
> >          *
> >          * @throws Exception
> >          */
> >         @Override
> >         public void configure() throws Exception {
> >                 onException(Exception.class)
> >                                 .setHeader("routeId",
> property(Exchange.FAILURE_ROUTE_ID))
> >                                 .setHeader("endpoint",
> property(Exchange.FAILURE_ENDPOINT))
> >                                 .setHeader("exception",
> property(Exchange.EXCEPTION_CAUGHT))
> >                                 .setHeader("subject", simple("Message
> Broker Error (" + esbEnv + ") - ${exception.class.simpleName}"))
> >
> .transform(simple("${exception.message}\n\nStacktrace
> Details:\n\n${exception.stacktrace}"))
> >
> .to("freemarker:/templates/mail/error.ftl")
> >
> .to("smtp://{{mail.host}}?contentType=text/plain&to=" + esbAlertEmail +
> >
> "&from={{mail.from}}&subject=${headers.subject})");
> >         }
> > }
> >
> > However, I'd rather not have to do this for each route.
> >
> > Please let me know if it's possible to make Camel aware of Spring Boot's
> external properties configuration.
> >
> > Thanks,
> >
> > Matt
>
>
>
> --
> Claus Ibsen
> -----------------
> Red Hat, Inc.
> Email: [hidden email]
> Twitter: davsclaus
> Blog: http://davsclaus.com
> Author of Camel in Action: http://www.manning.com/ibsen
> hawtio: http://hawt.io/
> fabric8: http://fabric8.io/
>
Reply | Threaded
Open this post in threaded view
|

Re: Overriding Properties

Claus Ibsen-2
Hi

I logged a ticket to not forget about this
https://issues.apache.org/jira/browse/CAMEL-7699



On Mon, Aug 11, 2014 at 4:56 PM, Matt Raible <[hidden email]> wrote:

> On Sat, Aug 9, 2014 at 2:47 AM, Claus Ibsen <[hidden email]> wrote:
>
>> Hi
>>
>> Yeah not sure if Spring Boot does stuff differently than regular
>> spring framework in term of the property placeholder stuff.
>>
>
> Yes, it does. From
> http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
> :
>
> Spring Boot uses a very particular PropertySource order that is designed to
> allow sensible overriding of values, properties are considered in the the
> following order:
>
> Command line arguments.
> Java System properties (System.getProperties()).
> OS environment variables.
> JNDI attributes from java:comp/env
> A RandomValuePropertySource that only has properties in random.*.
> Application properties outside of your packaged jar (application.properties
> including YAML and profile variants).
> Application properties packaged inside your jar (application.properties
> including YAML and profile variants).
> @PropertySource annotations on your @Configuration classes.
> Default properties (specified using SpringApplication.setDefaultProperties).
>
> It's also smart enough to know that a "ESB_ENV" environment variable should
> override a "esb.env" property.
>
>
>> Have you tried that spring -> camel bridge, there is a note about it
>> on the top of this page that points you to it
>> http://camel.apache.org/using-propertyplaceholder.html
>
>
> Yep, I have this in my CamelConfig.java:
>
> @Override
> protected void setupCamelContext(CamelContext camelContext) throws
> Exception {
> PropertiesComponent pc = new PropertiesComponent();
>  pc.setLocation("classpath:application.properties");
> camelContext.addComponent("properties", pc);
>  super.setupCamelContext(camelContext);
> }
>
>
>>
>>
>> And yeah if some code is missing to make this easier, then maybe we
>> can do something in camel-spring, or we could introduce a new
>> camel-spring-boot module that integrates the spring boot with Camel.
>> Maybe there is other spring boot stuff we need to take a look at.
>>
>>
>>
> I have a workaround, so this isn't urgent. However, it would be pretty
> sweet if Camel was smart enough to know about these overrides.
>
> This seems to be one of the main classes for making this work in Spring
> Boot:
>
> https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
>
>
>>
>>
>> On Fri, Aug 8, 2014 at 4:15 AM, Matt Raible <[hidden email]>
>> wrote:
>> > Hello all,
>> >
>> > In my Camel app, I recently integrated Spring Boot to use its external
>> configuration feature. Basically, I can put default properties in an
>> src/main/resources/application.properties file. Then I can override them on
>> different servers using various mechanisms, from using a
>> config/application.properties on the classpath to using environment
>> variables. It's really slick in that you can have a single WAR that can be
>> configured for dev, test, staging and prod.
>> >
>> > To read these properties in my Camel routes, I'm using the following
>> setupCamelContext() method to read this file in. Problem is, it doesn't
>> hook into Spring Boot's external override mechanism. Is there anyway to
>> make Camel aware of Spring Boot's override feature?
>> >
>> > @Configuration
>> > @ImportResource("classpath:META-INF/cxf/cxf.xml")
>> > @ComponentScan("com.company.app")
>> > public class CamelConfig extends CamelConfiguration {
>> >         @Value("${logging.trace.enabled}")
>> >         private Boolean tracingEnabled;
>> >
>> >         @Override
>> >         protected void setupCamelContext(CamelContext camelContext)
>> throws Exception {
>> >                 PropertiesComponent pc = new PropertiesComponent();
>> >                 pc.setLocation("classpath:application.properties");
>> >                 camelContext.addComponent("properties", pc);
>> >                 // see if trace logging is turned on
>> >                 if (tracingEnabled) {
>> >                         camelContext.setTracing(true);
>> >                 }
>> >                 super.setupCamelContext(camelContext);
>> >         }
>> >
>> >         @Bean
>> >         public Tracer camelTracer() {
>> >                 Tracer tracer = new Tracer();
>> >                 tracer.setTraceExceptions(false);
>> >                 tracer.setTraceInterceptors(true);
>> >                 tracer.setLogName("com.company.app.trace");
>> >                 return tracer;
>> >         }
>> > }
>> >
>> > If not, I'm able to workaround this issue by changing the properties
>> from being inlined to being separate Strings. For example, here's an
>> onException handler that doesn't pick up overridden properties:
>> >
>> > public abstract class AbstractRouteBuilder extends RouteBuilder {
>> >
>> >         /**
>> >          * Default exception handling for all routes.
>> >          *
>> >          * @throws Exception
>> >          */
>> >         @Override
>> >         public void configure() throws Exception {
>> >                 onException(Exception.class)
>> >                                 .setHeader("routeId",
>> property(Exchange.FAILURE_ROUTE_ID))
>> >                                 .setHeader("endpoint",
>> property(Exchange.FAILURE_ENDPOINT))
>> >                                 .setHeader("exception",
>> property(Exchange.EXCEPTION_CAUGHT))
>> >                                 .setHeader("subject", simple("Message
>> Broker Error ({{esb.env}}) - ${exception.class.simpleName}"))
>> >
>> .transform(simple("${exception.message}\n\nStacktrace
>> Details:\n\n${exception.stacktrace}"))
>> >
>> .to("freemarker:/templates/mail/error.ftl")
>> >
>> .to("smtp://{{mail.host}}?contentType=text/plain&to={{esb.alert.email}}" +
>> >
>> "&from={{mail.from}}&subject=${headers.subject})");
>> >         }
>> > }
>> >
>> > If I change it to the following, things work as expected:
>> >
>> > public abstract class AbstractRouteBuilder extends RouteBuilder {
>> >
>> >         @Value("${esb.env}")
>> >         private String esbEnv;
>> >
>> >         @Value("${esb.alert.email}")
>> >         private String esbAlertEmail;
>> >
>> >         /**
>> >          * Default exception handling for all routes.
>> >          *
>> >          * @throws Exception
>> >          */
>> >         @Override
>> >         public void configure() throws Exception {
>> >                 onException(Exception.class)
>> >                                 .setHeader("routeId",
>> property(Exchange.FAILURE_ROUTE_ID))
>> >                                 .setHeader("endpoint",
>> property(Exchange.FAILURE_ENDPOINT))
>> >                                 .setHeader("exception",
>> property(Exchange.EXCEPTION_CAUGHT))
>> >                                 .setHeader("subject", simple("Message
>> Broker Error (" + esbEnv + ") - ${exception.class.simpleName}"))
>> >
>> .transform(simple("${exception.message}\n\nStacktrace
>> Details:\n\n${exception.stacktrace}"))
>> >
>> .to("freemarker:/templates/mail/error.ftl")
>> >
>> .to("smtp://{{mail.host}}?contentType=text/plain&to=" + esbAlertEmail +
>> >
>> "&from={{mail.from}}&subject=${headers.subject})");
>> >         }
>> > }
>> >
>> > However, I'd rather not have to do this for each route.
>> >
>> > Please let me know if it's possible to make Camel aware of Spring Boot's
>> external properties configuration.
>> >
>> > Thanks,
>> >
>> > Matt
>>
>>
>>
>> --
>> Claus Ibsen
>> -----------------
>> Red Hat, Inc.
>> Email: [hidden email]
>> Twitter: davsclaus
>> Blog: http://davsclaus.com
>> Author of Camel in Action: http://www.manning.com/ibsen
>> hawtio: http://hawt.io/
>> fabric8: http://fabric8.io/
>>



--
Claus Ibsen
-----------------
Red Hat, Inc.
Email: [hidden email]
Twitter: davsclaus
Blog: http://davsclaus.com
Author of Camel in Action: http://www.manning.com/ibsen
hawtio: http://hawt.io/
fabric8: http://fabric8.io/