The project I am working at the moment uses camel as the routing framework.
When configuring camel context in spring we pass a property file that contains a bunch of global properties needed when configuring camel routes or for controlling run time behavior:
<camel:camelContext xmlns="http://camel.apache.org/schema/spring" id="my-id">
<camel:propertyPlaceholder location="my-system.properties" id="global-properties"/>
...
</camel:camelContext>
and say my-system.properties has an entry like below:
my-system.properties
# Global properties that control my-system configuration and run time
...
foo={{bar}}
...
When configuring the routes I can access foo property using the {{foo}} notation. It is also available to other beans using #PropertyInject annotation. However there is one use case in my design when a plain POJO not created by spring (an enum instead but this is not relevant) needs to access my foo property. Because this POJO it is passed the CamelContext as a method argument I find it natural to think I should be able to get the value of foo from there. However I spent a bit of time and could not figure out by myself how.
I know I can load the properties file again or even get the system property System.getProperty("bar") and everything will work but it looks like cheating to me.
There is an api on CamelContext to resolve property placeholders - its the resolvePropertyPlaceholders method:
http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html#resolvePropertyPlaceholders(java.lang.String)
If your POJO is not being managed by the SpringContext I don't see any way you can automatically inject the property. Although your approach may not seem the most fancy or elegant, it has the advantage of not giving you any overhead you could have by using another injection tool.
Related
I am trying to outhouse central beans of my OSGi bundles into a central bundle, which provides them as a service. This works fine with the ErrorHanlders and Processors, but not with the ShutdownStrategy and RedeliveryPolicy. The Error Message I receive is
org.osgi.service.blueprint.container.ComponentDefinitionException: A class org.apache.camel.processor.RedeliveryPolicy was found in the interfaces list, but class proxying is not allowed by default. The ext:proxy-method='classes' attribute needs to be added to this service reference.
I could try to follow the instrutction and add the ext:proxy-method, but first I want to understand the clue here. Maybe it's not a good idea to centralize strategies and policies?
[EDIT] The mistake here was to use the class in the service instead of an interface. So interface="org.apache.camel.spi.ShutdownStrategy" should be the correct Interface here (for the ShutdownStrategy). The bundle with my camel route references this service so:
<reference
id="shutdownStrategy"
interface="org.apache.camel.spi.ShutdownStrategy"
component-name="shutdownStrategy" />
But now I get the following error:
java.lang.IllegalArgumentException: CamelContext must be specified
[EDIT] I want to confine this question to the ShutdownStrategy, because the RedeliveryPolicy works fine when I referenc it in the ErrorHandlers inside my central bundle.
So is it possible to outhouse the ShutdownStrategy, too? Maybe not, because it needs a CamelContext.
When using Spring XML you then just define a spring bean which implements the org.apache.camel.spi.ShutdownStrategy and Camel will look it up at startup and use it instead of its default.
I found the answer in the Camel documentation
You can implement your own strategy to control the shutdown by implementing the org.apache.camel.spi.ShutdownStrategy and the set it on the CamelContext using the setShutdownStrategy method.
When using Spring XML you then just define a spring bean which implements the org.apache.camel.spi.ShutdownStrategy and Camel will look it up at startup and use it instead of its default.
So if you have your own implementation of the ShutdownStrategy you can use it as a bean.
<bean id="shutdownStrategy"
class="org.apache.camel.spi.ShutdownStrategy">
</bean>
I have a simple Web MVC application using Spring Boot that communicates with a database; the DB is H2 and has been in memory until now. I want to change that, and thus use a jdbc:h2:file:... URL.
Up until now, I have not needed to add any XML to configure my application, and I'd prefer it to stay that way if possible. But I can't figure out how to specify a different JDBC URL. I obtained and inspected the data source by passing it to an #Bean method:
org.apache.tomcat.jdbc.pool.DataSource#745e6f01{ConnectionPool[
defaultAutoCommit=null;
defaultReadOnly=null;
defaultTransactionIsolation=-1;
defaultCatalog=null;
driverClassName=org.h2.Driver;
maxActive=100;
maxIdle=100;
minIdle=10;
initialSize=10;
maxWait=30000;
testOnBorrow=false;
testOnReturn=false;
timeBetweenEvictionRunsMillis=5000;
numTestsPerEvictionRun=0;
minEvictableIdleTimeMillis=60000;
testWhileIdle=false;
testOnConnect=false;
password=********;
url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;
username=sa;
validationQuery=null;
validationQueryTimeout=-1;
validatorClassName=null;
validationInterval=30000;
accessToUnderlyingConnectionAllowed=true;
removeAbandoned=false;
removeAbandonedTimeout=60;
logAbandoned=false;
connectionProperties=null;
initSQL=null;
jdbcInterceptors=null;
jmxEnabled=true;
fairQueue=true;
useEquals=true;
abandonWhenPercentageFull=0;
maxAge=0;
useLock=false;
dataSource=null;
dataSourceJNDI=null;
suspectTimeout=0;
alternateUsernameAllowed=false;
commitOnReturn=false;
rollbackOnReturn=false;
useDisposableConnectionFacade=true;
logValidationErrors=false;
propagateInterruptState=false;
ignoreExceptionOnPreLoad=false;
}
(newlines mine)
The setup of that bean seems rather intricate, so I want to interfere with it as little as possible - just replace the default JDBC URL.
How can I configure individual properties for Spring to create the datasource? Preferably in Java, but if there is a concise XML way I'm happy as well. I just want to avoid adding 100 lines of boilerplate for something equivalent to url=...
A DataSource is auto configured by Spring Boot for you. To influence how and what there are several properties you can set. Those are prefixed with spring.datasource, for a list take a look at the Spring Boot Reference Guide for a full list.
In your case simply add the following to the application.properties file
spring.datasource.url=jdbc:h2:file:...
This will tell Spring Boot to use this URL instead of the default.
As H2 is considered an in-memory database and not a regular database, when using JPA this will lead to your database to be dropped when the application is stopped. To fix this simply add the following
spring.jpa.hibernate.ddl-auto=update
To specify a dialect simply add the following
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
or even simpler
spring.jpa.database=H2
What is the best approach for creating services that load a property set at runtime (bean is passed "xyz" and loads xyz.properties)? These properties files need to be able to be dropped into a folder outside the classpath before a command is entered to start the service (edit: this could happen at any time while the program is running).
I already have a system to do this that we've been using for over a year, but I'm migrating to spring to make the code more modular (customize services more easily through DI) and easier to maintain. My current method of creating an environment and then passing it with "this" to the dependencies just seems upside down from an IoC standpoint.
Is there a way to use a PropertyPlaceholderConfigurer without hardcoding the name of the property file? Maybe just a reference to a variable I pass into the constructor of the service that its dependencies can load? So far, it looks like I will have to create a service and inject its dependencies without any config and then call a separate load method for each to pass in the properties, but that just seems like I'm not really using spring.
USE CASE: The app will pool client connections to various servers and will forward requests from other applications to these servers. New profiles must be able to be added by non-programmers without taking down or restarting the app. The profiles will include basic things like host, port, and login info, but also more complex things like whether to use tcp/http, ssl/https (which will determine which client type to use), and timeouts and pool min/max/etc (which will need default values).
I tried with PropertyPlaceholderConfigurer and frankly, I couldn't wrap my head around it, somehow. It's easy enough to use when you use the existing options but I couldn't extend the framework.
So my approach was much more simple:
Create an annotation #InjectConfig which takes a config key as parameter.
In your beans/services, annotate fields or public setters with this annotation.
Write a BeanPostProcessor which takes options from a "config provider" and injects them into the fields / setters.
Now all you need is a config provider. Inject that into the post processor and you're done.
Note: I prefer annotating setters because that means you can easily configure your services from tests (just call the setters) without having to come up with smart names for 238576 config files.
EDIT If you have many configs, then a config factory might be a better choice:
Create a key to describe a config bundle (I usually use an enum or a new type here to prevent typos)
Put this key into the service when you create it (manually or via Spring)
Write a config factory that can return Properties or a Map for a config key.
Inject this factory into your service
In the init code of your service, use the key to lookup your config via the factory.
Using this approach, you can have a dummy factory that always returns the same thing in tests and a more complex factory for production.
The real factory can then be configured via spring so it knows where to look for configuration files. One approach is to register a java.io.File per config key. Now your concerns (configuring a service and loading configs) are completely separated.
PropertyPlaceholderConfigurer reads and initialize files on application context initialization and only once. So most probably you cannot configure it at runtime.
But you can have variables. For example, for my case I have default properties and user specific properties. So PropertyPlaceholderConfigurer loads properties from classpath first and after that is trying to find additional properties at defined location (user home folder). I user's property file exists so configurer loads it and override properties.
Here is my example:
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/> <!-- do not throw exception if file not found -->
<property name="locations">
<list>
<value>classpath:server.properties</value>
<value>file:${user.home}/user.properties</value>
</list>
</property>
</bean>
I'm not sure that this answer is what you're exactly need. But I'm trying to guess what is your actual task. So if you need re-read properties runtime each time you access them you have to do it manually like you did before because spring application context helps you configure your application initial configuration.
It seems like the best approach may be to use a ServiceManager that contains the main ApplicationContext and then have each Service initialize its own FileSystemXmlApplicationContext with the main context as the parent like so:
public class ServiceManager {
ApplicationContext appContext;
String APP_HOME = System.getProperty("user.home") + File.separator;
public void init() {
//set main spring context
appContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
public void start(String serviceName) throws Exception {
ApplicationContext serviceContext = new FileSystemXmlApplicationContext(
new String[]{APP_HOME + serviceName + ".xml"}, //path to child ctx
appContext); //reference to parent ctx to build hierarchy
Service service = (Service) serviceContext.getBean("service");
service.start();
}
}
The ApplicationContext is a bit heavy to be duplicating, but memory is pretty cheap these days and this provides total separation of concerns. I still have shared logging and an event system managed by the parent context, and each service is now simplified in its own config. I built a proof of concept using two services, and it seems to work fine so far. I'll add another comment once I finish the other services and finish testing.
reference:
http://techo-ecco.com/blog/spring-application-context-hierarchy-and-contextsingletonbeanfactorylocator/
predhme
You came up with a way to read environment variable into a Spring Bean then "was able to then pass that into the FileSystemResource".
Here:
Can I use an Environment variable based location for Spring FileSystemResource?
Could you please explain how you passed the variable to FileSystemResource?
Thanks!
Charlie
You want to use a PropertyPlaceholderConfigurer.
Once you have that set up, you should be able to pull any environment variable with the:
${variableName}
syntax anywhere in your configuration file.
I don't like the idea of having system properties for individual variables like these. Sooner you might turn into hundreds of system properties that might need to be set. Instead as others suggest, have the property file outside of the build and specify the location of the property file as a system property.
Having the properties that change with environment, in a properties file outside of the build (JAR / WAR..) helps a lot in making good builds.
This blog post might help.
Or something similar as below:
Using a property placeholder configurer
<bean id="myConfigPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="${my.sys.variable}/fooModule/foo.properties"
p:placeholderPrefix="#foo{"
p:placeholderSuffix="}" />
Injecting in beans
#Value("#foo{some.key.defined.in.fooPropertiesFile}")
String myFileSystemResourcePath;
(or)
Using util:properties
<util:properties id="fooProps"
location="file:///${my.sys.variable}/fooModule.properties/>
Injecting in beans
#Value("#fooProps[some.key.defined.in.fooPropertiesFile]")
String myFileSystemResourcePath;
Note: When defining property-placeholder-configurer bean, there are different syntactic variations that are supported. You might choose any. Also instead of injecting with #Value, you can reference the property in the xml file with ${variableName} as dlawrence suggested.
Setting the environment variable
for one project:
Setting the envVariable, in this example my.sys.variable can be just done with
project-->(right click)-->run as-->run as configurations-->vm arguments
and there: -Dmy.sys.variable=file:///D:/myBaseDir
globally as a preference:
If this envProp is to be shared with different projects in eclipse, then you can do
Windows->Preferences->Java->Installed JREs->(select the jre)->edit->default vm arguments->and set it with something like -Dmy.sys.variable=file:///D:/myBaseDir
To be exact, how can I get the value of the <display-name> tag under the <web-app> tag stored in an application's web.xml in a Spring application context configuration XML file.
Ideally I would like something like the following...
<bean><property value="${servletContext.servletContextName}/></bean>
It seem like ServletContext.getServletContextName() does what I want but I can't seem to figure out how to get a handle on that in the Spring application context file.
Ok, the answer is trivial in Spring 3.0.x. Per the documentation for ServletContextFactory
Deprecated. as of Spring 3.0, since "servletContext" is now available as a default bean in every WebApplicationContext
So I decided to try the following and it worked!
<bean><property value="#{servletContext.servletContextName}/></bean>
Since servletContext object is implicitly defined we can access it via Spring EL using the #{} syntax.
I don't think you can do this via the XML config.
You can autowire a bean to receive the ServletContext object (or implement ServletContextAware), and fetch it from that programmatically, but I don't think the XML expressions have any visibility of it.
Maybe try the Expression Language?
<bean>
<property value="#{T(javax.servlet.ServletContext).getServletContextName()}"/>
</bean>
I suspect that would print null if it works though, since there is no context yet.