Loading an xml file only once in spring - java

In spring, how would I go about loading an XML file once such that I can reference the file in my controller actions and not have to load it up again and again.

Create a bean whose sole purpose is to read your XML file and has the desired accessor methods, and inject it into your controller.

In Spring , all beans are initialized as Singleton unless specified . You could inject the properties from your XML file as follows
The xml file format for the properties is as below.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="key1">Value 1</entry>
<entry key="key2">Value 2</entry>
</properties>
you can use
<context:property-placeholder
location="classpath:/com/myProject/spring_prop.xml" />
<bean id="bean" class="org.MyBean">
<property name="key1" value="${key1}" />
</bean>
Or read the XML files in an #PostConstruct method via a bean as #Jens has mentioned .

Related

Conditionally load a bean in spring boot using xml configuration?

I am using Spring Boot 2.1.
I have some mixed configuration in my project : XML files and java classes with annotations.
We have this current configuration which works :
application.properties :
spring.profiles.active=dev, component1, component2
applicationContext-file.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
profile="component1">
<beans>
<bean id="myserviceimpl"
class="org.blabla.MyServiceImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
We want to extract the component values from the spring.profiles.active property since they have nothing to do with the environment :
application.properties :
spring.profiles.active=dev
component1=true
component2=true
How can i condition the instantiation of the myserviceimpl bean inside the applicationContext-file.xml ?
I can no longer rely on the profile attribute since the spring.profiles.active property no longer includes
the values of the components.
Thanks for helping.
I haven't tried it by myself but I suggest to check the following:
Step 1
Create one XML file per profile (say, dev and prod for the sake of example):
app-config-dev.xml:
------------------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
profile="dev">
<beans>
<!-- here define only beans that you want to load in __dev__ environment -->
<bean id="myserviceimpl"
class="org.blabla.MyServiceImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
app-config-prod.xml:
------------------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
profile="prod">
<beans>
<!-- here define only beans that you want to load in __production__ environment -->
<bean id="myserviceimpl"
class="org.blabla.MyServiceProdImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
Step 2
Now in your "primary" applicationContext-file.xml instead of defining beans directly, include all the xml files that you've created during the step 1:
<import resource="classpath:app-config-dev.xml" />
<import resource="classpath:app-config-prod.xml" />
Read this thread for more details on this step if needed
Step 3
Remove the component1=true and component2=true from aplication properties, you don't need it anymore, the profile itself defines which beans should be loaded.
Update 1
Based OP's first comment:
You probably can try another option but I consider it a "low-level" solution and it requires deep understanding of how does spring loading process work under the hood.
So when spring starts it finds the definitions of beans (in xml, java config, annotations like #Component and so forth) and creates out of all this information a BeanDefinition - a metadata object that aggregates all the information about the bean (for example whether its singleton or prototype). At this point no beans are not created yet. Lets call this point in time a "Bean Definitions Done" point" (for the sake of explanations, its my term out of my head...
Then spring starts to build a graph of dependencies and starts creating beans, initializing them (Autowiring, post-construct methods, and so on) and placing them onto application context (if they're singletons).
Now, in spring there is an option to provide a hook called BeanFactoryPostProcessor that is invoked exactly at the "Bean Definitions Done" point. This is basically a java code that implements some interface and by itself is a spring bean that spring treats in a special way.
So you can implement this interface and manipulate the results of Bean factory.
Namely you can access every bean definition that spring has opened and if you think that the bean of this definition should not be created (here comes your custom logic that would check properties, whatever) - remove the bean definition from the registry:
For technical method of removing bean definitions see this thread
Here is an example of Bean Factory Post processor that actually adds a new bean although it wasn't registered

How to create beans by condition in xml?

I'm creating the following bean using xml config:
<int-ip:tcp-outbound-gateway id="gate"
request-channel="input"
reply-channel="clientBytes2StringChannel"
connection-factory="factory"/>
How can I configure this based on a condition, eg a value included in application.properties?
I spring 4 I could use #ConditionalOnExpression("SpEL"), but how could I achieve the same in xml?
You can use Spring Profiles to conditionally create beans. For e.g.
<beans profile="dev">
<bean id="devDatasourceConfig" class="" />
</beans>
<beans profile="production">
<bean id="productionDatasourceConfig" class=" " />
</beans>
You can activate profiles as follows:
JVM system parameter:
-Dspring.profiles.active=dev
or
Environment variable:
export spring_profiles_active=dev

Inject property into non managed Spring xml

I am having xml. (Ehcache.xml)
I want to inject into it property from property file(on the classpath).
However since this xml is not a Spring managed bean I am not able to do so.
Any recommendations how to overcome this?
the xml:
...
properties="peerDiscovery=manual,rmiUrls=//**${other.node.hostname}**:41001/org.jasig.cas.ticket.ServiceTicket|//**${other.node.hostname}**:41001/org.jasig.cas.ticket.TicketGrantingTicket"
propertySeparator="," />
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="port=41001,socketTimeoutMillis=5000" />
</ehcache>
I want to inject ${other.node.hostname} from another properties file.
but I get this on runtime:
Caused by: java.net.URISyntaxException: Illegal character in authority at index 2: //${other.node.hostname}:41001/org.jasig.cas.ticket.TicketGrantingTicket
thanks,
ray.
in the ehcache.xml it won't work because it's not spring configuration. But if you define the equivalent configuration in spring instead of the ehcache file it should work:
<context:property-placeholder location="classpath:config.properties"/>
<bean id="myCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="maxElementsInMemory" value="${cache.maxMemoryElements}"/>
</bean>
the best is to use the ehcache.xml file the least possible and configure everything in spring, as most of the options in the file have a spring equivalent.

Reading properties file in Spring 3.2

spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="meassageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="resource\message">
</property>
</bean>
</beans>
Main.java class file
public class Main {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml");
System.out.println(context.getMessage("emp", null, Locale.US));
}
}
My properties file is in src/resource folder. File name is mesaage_en_US.properties.
I have also tried with different file names like message.property, message_en.property and with different locales like Locale.English, Locale.UK but no luck.
I moved the property file to src folder but getting same exception.
I am getting following exception.
Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'emp' for locale 'en_US'.
at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:65)
at org.springframework.context.support.AbstractApplicationContext.getMessage(AbstractApplicationContext.java:1234)
at org.beans.Main.main(Main.java:14)
Please help.
message_en_US.properties
emp=Hello Employee.
I like to use a PropertySourcesPlaceholderConfigurer for that. Here's a great tutorial to get you started.
Basically, you'll want to add:
<context:property-placeholder location="classpath:foo.properties" />
to your spring xml config file, where "foo.properties" is a resource's absolute path within the class path.
Then you can inject them into fields like this:
#Value( "${jdbc.url}" )
private String jdbcUrl;
where "jdbc.url" is the reference name in your properties file.
Of course, the #Value won't work inside your static void main, but I really doubt static void main is where you want to use your properties anyway. You ought to be accessing them from a Spring Bean.
I think this is duplicated from this question. Basically, it has to do with a mismatch between your bundle and the locale specified in code.
Instead of getting message from ApllicationContext I am getting message from MeassageSource itself. I changed my spring.xml like this
<bean id="employee" class="org.bean.Employee" >
<property name="id" value="1"/>
<property name="name" value=""/>
<property name="dept" value=""/>
<property name="messages" ref="messageSource"/>
</bean>
Now I am calling messages.getMessage(this.messages.getMessage("emp", null, Locale.UK)) from Employee class. Its working.
Change to
<property name="basename" value="message" />
with message_en_US.properties in the same folder as your spring.xml.
EDIT : You have a typo in your bean name when defining the MessageSource. It's name should have been exactly messageSource. Because of that extra a in meassageSource ApplicationContext failed to load it.
I've reproduced your error and found the problem. It has to do with Spring not finding the bundle. I think you should be getting a warning before the exception with the following message:
WARNING: ResourceBundle [resource\message] not found for MessageSource: Can't find bundle for base name resource\message, locale en_US
This has been the hint. The problem is related to your project structure and how the bundles are searched when specifying setBasename property. Please take a look at this.
Anyway I think you should put your bundles in the more standard location src/main/resources. If you follow this convention, your messageSource bean should be defined like this:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message" />
</bean>
With this approach your example should produce the desired line:
Hello Employee.

Spring mvc ResourceBundleMessageSource for different themes

I configures my appstrings.properties file which contains strings related to theme used by my application. There are two themes for my application. Both have their own appstrings.properties file located at WEB-INF/strings/theme1/appstrings and WEB-INF/strings/theme2/appstrings. I specified property themeName in config.properties file.
Here is my spring config file:
<context:property-placeholder location="file:///${config.properties}" />
...
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basenames="WEB-INF/strings/theme1/appstrings" />
Right now all my app strings are coming from WEB-INF/strings/theme1/appstrings.properties file. How can I make it dynamic. i.e. when I change themeName propperty to theme2 it should get string from WEB-INF/strings/theme2/appstrings.properties
You need to use a ResourceBundleThemeSource
<!-- resolves localized <theme_name>.properties files in the classpath to
allow for theme support -->
<bean
class="org.springframework.ui.context.support.ResourceBundleThemeSource"
id="themeSource" />
<mvc:interceptors>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
</mvc:interceptors>
See Spring Reference Chapter 15.7 Using Themes
For an running example you can quickly create a small Spring Roo application. It uses the theme support to change the css files.
If config.properties contains a property say: themeName = propertyValue. If you want to use this themeName's value in spring mvc configuration servlet xml file then you can use ${themeName}
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basenames="WEB-INF/strings/${themeName}/appstrings" />
reference

Categories

Resources