Controlling log file location via JNDI in Spring? - java

I have a Java web application that uses the SLF4J logging facade. To date, we use the Log4J implementation underneath (though we are considering a switch to Logback). Log4J is currently configured via a log4j.xml configuration file that is placed in the root of our classpath.
In any event, we use JNDI to configure other aspects of our application so I am very familiar with how to set that up and pull a string from JNDI into a Spring configuration file.
However, I am at a loss to figure out how to create a Log4J appender from within a Spring configuration file. Better yet, can one completely configure Log4J via Spring and skip the log4j.xml configuration file altogether? I am hoping I don't have to do this programmatically.
I found a Spring class called Log4jWebConfigurer but this requires that the WAR run exploded (don't want that if I can help it) and also that the log file resides within the web-app directory (definitely don't want that).

First get the main directory via JNDI:
<jee:jndi-lookup id="myAppHome" jndi-name="myAppHome" />
Then use that bean in a Spring Expression Language statement in the the following way:
<bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>#{ myAppHome + '/conf/log4j.xml'}</value>
</list>
</property>
</bean>

Related

How to register Spring Cloud config server before any XML bean is declared?

I am trying to migrate a spring app who uses PropertyPlaceholderConfigurer to resolve all the XML placeholders in it's bean declarations to a spring cloud usage, I can check that the config server is contacted and responds with the respective data generated from a git repository, however, at server startup during the BeanFactoryPostProcessor registration the XML context fails to resolve the placeholders.
I assumed that by removing the bean definition:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="properties">
<bean class="org.apache.commons.configuration.ConfigurationConverter"
factory-method="getProperties">
<constructor-arg>
<ref bean="domainConfiguration" />
</constructor-arg>
</bean>
</property>
</bean>
And adding the POM dependency for config client and respective enviroment variables the placeholders should work but they dont.
Can I manually set the config server in a higher priority?
Or as an alternative, teach PropertyPlaceholderConfigurer to consume a config server?
If you are using spring-cloud-config, this should work out of the box. When spring will build/start the ApplicationContext, first it will create a bootstrap (parent) context which will happen before creating the main context. Getting the properties of the config server should happen in the bootstrap phase so that your beans which are created in the normal context should be able to get those properties.
Check out the Client Side Usage part of the documentation for an example and check out the usage of the bootstrap.properties file.
If you don't have spring-boot (it should work w/o it as well but the docs are spring-boot centric), check out this repo or this GitHub issue, you will need a ConfigServicePropertySourceLocator.

How to load system properties in Spring for properties file location

I am working on a Spring application where the properties file will be packaged inside the the .war file for deployment.
<context:property-placeholder location="classpath:application.properties" />
However, I would like to be able to override them with another file that can be specified in standalone.xml as a system property:
</extensions>
<system-properties>
<property name="CONFIG_FILE_LOCATION" value="/path/to/application.properties"/>
</system-properties>
This was my solution,
<context:property-placeholder location="classpath:application.properties,
file:///${CONFIG_FILE_LOCATION}" />
but apparently Spring is unable to find it
Caused by: java.io.FileNotFoundException: ${CONFIG_FILE_LOCATION} (The system cannot find the file specified)
Does anyone has any idea how I might fix it? Is there another way Spring accesses the system properties?
It is actually possible in Spring to override certain properties by specifying a system property location to another file using this solution:
<context:property-placeholder location="classpath:alarm_notification.properties, file:///${CONFIG_FILE_LOCATION}" />
If a certain property is not overridden inside the file located at CONFIG_FILE_LOCATION, the value from application.properties will be used instead.
Just make sure to have the following configuration in the standalone.bat file used to start the server:
</extensions>
<system-properties>
<property name="CONFIG_FILE_LOCATION" value="/path/to/application.properties"/>
</system-properties>
You need to file namespace for that as follows:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:/myFolder/folder/path/application.properites</value>
</list>
</property>
or as follows:
<context:property-placeholder locations="file:/myFolder/folder/path/application.properites"/>

Cloud properties not read by Spring application in Cloudfoundry

I am deploying a Spring application in Cloudfoundry which needs to access mysql database. Now as per the tutorial , if the spring version is higher than 3.1, i can use profiles for the cloud and the cloud properties will be available for the application. The mysql service is registered as p-mysql in my case so my spring config looks like this
<beans profile="cloud">
<context:property-placeholder location="classpath:/app.conf" />
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
p:location="file:#{systemProperties['app.conf']}" />
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- DB connection properties -->
<property name="driverClass" value="${db_driver:oracle.jdbc.OracleDriver}" />
<property name="jdbcUrl" value="${cloud.services.p-mysql.connection.jdbcUrl}" />
<property name="user" value="${cloud.services.p-mysql.connection.username}" />
<property name="password" value="${cloud.services.p-mysql.connection.password}" />
</bean>
</beans>
I need the app.conf for other keys not related to db services. When i deploy this i get the error
OUT org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'datasource' defined in URL [jar:file:/home/vcap/app/.java-buildpack/tomcat/webapps/ROOT/WEB-INF/lib/test-server-1.3.0.BUILD-SNAPSHOT.jar!/spring-bootstrap.xml]: Could not resolve placeholder 'cloud.services.p-mysql.connection.jdbcUrl'
Is there something i missed , when i look for the env for app, i do see the properties are available for the app (cf env app_name)
What i could have missed? do i have to include
cloudfoundry-runtime in the pom which i dont have now?
or include the cloud namespace in the spring app
Any documentation that mentions cloudfoundry-runtime is obsolete. The current documentation recommends the use of Spring Cloud Connectors for Spring applications on Cloud Foundry.
You should include Spring Cloud Connectors in your project, then you could do something as simple as this:
<cloud:data-source id="datasource" service-name="mysql-service"/>
You could also create the datasource bean yourself using property placeholders if you really need more control. Additional documentation on configuring Spring Cloud Connectors via XML is here: https://github.com/spring-cloud/spring-cloud-connectors/tree/master/spring-cloud-spring-service-connector#the-cloud-namespace

jndi look up for DefaultFtpSessionFactory

I have ftp connection properties in .properties file and following code for spring bean.
<bean id="ftpConnectionFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.host}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.username}"/>
<property name="password" value="${ftp.password}"/>
</bean>
Above method does work using properties file inside web app and placeholder configuration. But what I want is to keep these properties in server, let's say tomcat context.xml file.
I have spring integration which uses this factory.
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
remote-directory="${ftp.remoteDir}"
remote-file-separator="\"
session-factory="ftpConnectionFactory"
/>
Is there a way that I can externalize these properties in server and look up using jndi. For datasource I am currently doing it. But I don't know how to do it for session factory. The reason why I want to do this is to hide the password and other details.
If Tomcat can correctly bind the object to the JNDI from context.xml, there is no difference to get access to that object from JNDI lookup as you do it for DataSource.
Show, please, how you do it for DataSource from Spring, and how you configure ftpConnectionFactory, and I'll try to help you.
You could use a PropertyPlaceholderConfigurer as follows
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:external.properties</value>
</property>
</bean>
See more examples at 5.8.2 Customizing configuration metadata with a BeanFactoryPostProcessor and Spring PropertyPlaceholderConfigurer Example

Spring - Switch SchedulerFactoryBean To Be Used

I'm using Spring's SchedulerFactoryBean to run some Quartz jobs within a Spring based java application. At present, this is a single instance application in development, but as soon as we start horizontally scaling this we will want to use a jdbc based JobStore for Quartz so no more than one app will run a given job.
Right now, SchedulerFactoryBean is configured as follows:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="taskExecutor" ref="taskExecutor"/>
<property name="triggers">
<list>
<!-- a bunch of triggers here -->
</list>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
</bean>
and with using a jdbc based JobStore it will look like this
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="dataSource" ref="mysqlJobDataSource"/>
<property name="taskExecutor" ref="taskExecutor"/>
<property name="triggers">
<list>
<!-- a bunch of triggers here -->
</list>
</property>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<!-- and a bunch of other quartz props -->
</props>
</property>
</bean>
Ideally, I'd like to continue using the default RAMJobStore version (the first one) for developers, but use the jdbc version for deployed environments. However, there doesn't seem to be a very good way to switch between the two through something like a property, since the jdbc store involves lots more configuration and the mere existence of the dataSource property on SchedulerFactoryBean means it tries to a JDBC based job store.
Also, Since SchedulerFactoryBean is an initializing bean where the initializing basically starts running all of the jobs, so I can't have both of those beans defined in a config file loaded into the spring context either, which means I'll have parallel jobs running.
I've also read through this answer, but this situtation differs in that I'm dealing with two InitializingBeans that should never be in the same context at the same time.
What would be the simplest way to configure switching between these two configurations of SchedulerFactoryBean?
From Spring 3.1 you can use Spring profiles:
<bean name="schedulerFactoryBean" profile="dev" ...
<bean name="schedulerFactoryBean" profile="prd" ...
Then you can instruct Spring container which profile to use, see How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property and Spring autowire a stubbed service - duplicate bean.
If you can't use 3.1 or profiles, the old-school of solving such issues is to have two context files: schedulerContext-dev.xml and schedulerContext-prd.xml`. Then you can import them selectively:
<import resource="schedulerContext-${some.property}"/>
A better option would be using quartz properties file. As part of your release you can have different files per environment. The context that way is the same for all the environments, the only thing that changes is the configuration file. Using maven profiles you can solve it

Categories