How do I "inject" structured configuration information with Spring? - java

I have the following scenario:
There will be a Java language testbed system consisting of a number of "services" that collaborate by passing messages to one another. The service implementation is intended to be generic, i.e. there is no specific "business logic" contained within. For the purposes of the testbed it is important to be able to create various collections of services, configured externally (if possible).
The services themselves are unaware of the existence of any other service. Each service simply subscribes to the topics where it expects to receive information and publishes on topics where it sends information out to any waiting subscribers. With careful configuration it then would be possible to simulate a data flow graph.
The plan is to configure a given service instance by providing configuration information that describes the information needed to set up subscribers (readers) and publishers (writers). The configuration information may include other properties not related to publish/subscribe.
Below is a possible example:
Note: XML was chosen for the example simply because it's easy enough to read and allows for structured data.
<service>
<name>Service A</name>
<service-id>service ID</service-id>
<publish>
<per-second>5</per-second>
<topic>
<name>Topic 1</name>
<class>org.xyz.Topic1</class>
<!-- override 5/sec rate -->
<per-second>10</per-second>
</topic>
<topic>
<name>Topic 2</name>
<class>org.xyz.Topic2</class>
</topic>
</publish>
<subscribe>
<topic>
<name>Topic 3</name>
<class>org.xyz.Topic3</class>
</topic>
</subscribe>
</service>
<service>
<name>Service B</name>
<service-id>service ID</service-id>
<publish>
<per-second>30</per-second>
<topic>
<name>Topic 3</name>
<class>org.xyz.Topic3</class>
</topic>
</publish>
<subscribe>
<topic>
<name>Topic 2</name>
<class>org.xyz.Topic2</class>
</topic>
</subscribe>
</service>
...
I would like to use the Spring framework to help with the configuration of these services. Note: I am very new to Spring and am currently reading Spring in Action (and other sources) to educate myself.
What I would like to know is: How could I "inject" the configuration information to some sort of controller or factory that would then use it to create the collection of services and provide them with the necessary information to create the readers and writers they will use to receive and send messages?
From what I've read so far, Spring appears to be pretty powerful WRT to dependency injection and "bean wiring", but I don't know enough about what can (and cannot) be done, nor how to do it.
I'm not partial to whether Spring is configured by Java or XML. I just used XML because it easy to put together, allows for data structuring and seems to be used everywhere. If it makes more sense to specify the configuration a different way, just let me know.
How a given service would handle an event (i.e. receive a specific message) to possibly send out a message "response", or take some other action, is a topic outside the scope of this question. I am researching how that could be done - mainly at rules based processing. If anyone has suggestions, I will gladly take a look at them.

Make a config file like this:
some.paremeter=cool
some.other.parameter=awesome
named myconfig.properties. Make sure the file is in your classpath, then include -Dspring.config.name=myconfig in your vm args; then in the xml you can use ${some.parameter} etc. In particular, putting the config file in <project-root>/config/ will work.
For example, here is a simple MongoClient from one of my projects that uses a spring config:
<!--Mongo-->
<bean id="mongoClient" class="com.mongodb.MongoClient">
<constructor-arg>
<bean class="com.mongodb.MongoClientURI">
<constructor-arg type="java.lang.String"
value="mongodb://${mongo.db.user}:${mongo.db.password}#${mongo.db.host}:${mongo.db.port}/${mongo.db.database}"/>
</bean>
</constructor-arg>
</bean>

Related

Is it possible to outhouse ShutdownStrategy for CamelContext into a central OSGi bundle?

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>

Spring XD Using custom TCP serializer

I extended AbstractByteArraySerializerand now I want to use this serializer like the rest of the available TCP serializers (LF, NULL, L1, ...).
I found the profiles in tcp-encdec.xml and registered my own profile:
...
<beans profile="use-custom">
<bean id="CUSTOM"
class="custom.tcp.serializer.ByteArrayCustomSerializer">
<property name="maxMessageSize" value="${bufferSize}" />
</bean>
</beans>
...
Spring uses EncoderDecoderMixins.Encoding to convert Encoding to a specific profile.
EncoderDecoderMixins.Encoding is an enum in a final class. Spring converts the decoder TCP property to a specific profile based on this enum. My CUSTOM serializer won't work since it isn't in the specified Encodings.
Is there a way to register a new Encoding or do I have to write a new Source module to use my serializer?
Unfortunately, you will need a custom source; we could probably add another enumeration, such as CUSTOM, where you provide the classname of the deserializer but that will need a change to the standard source.
A quick and dirty work-around would be to modify the source locally:
<int-ip:tcp-connection-factory id="connectionFactory"
...
deserializer="myDeserializer"/>
<bean id="myDeserializer" class="foo.Deser" />
i.e. change the ${decoder} placeholder to point to your bean.

can't set a different filter in file:inbound-channel-adapter and scanner?

Below is part of my Spring Integration config :
<bean id="recursiveScanner" class="org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner" >
<property name="filter" ref="skipTmpFileFilter" />
</bean>
<bean id="skipTmpFileFilter" class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^[^~].*"/>
</bean>
<file:inbound-channel-adapter directory="${inbound.folder}"
scanner="recursiveScanner"
id="fileChannel"
filter="fileNameFilter">
<integration:poller id="poller" fixed-delay="10000" />
</file:inbound-channel-adapter>
As you can see, I'd like to define 2 different filters :
one to skip temp files, in the recursiveScanner
one more advanced in which I have defined some other patterns, fileNameFilter (details of which are not relevant, so I'm not providing it)
What I see when I launch this in debug mode is that first, skipTmpFileFilter is set in recursiveScanner, but it is overwritten by fileNameFilter a bit after, making skipTmpFileFilter ineffective.
Is it the intended behavior or a bug ? I think it would make sense to be able to configure 2 different filters, one generic (in scanner) and one more specific (in the inbound adapter). Here, I'm kind of forced to use a composite filter.
Thanks
Vincent
If we take a look to the source code of FileReadingMessageSource, we'll see:
public void setFilter(FileListFilter<File> filter) {
Assert.notNull(filter, "'filter' must not be null");
this.scanner.setFilter(filter);
}
And there is no more stuff around the filter for the FileReadingMessageSource. Everything is delegated to the DirectoryScanner.
So, there is no any choice, unless provide only one filter option: or for the DirectoryScanner bean, or <file:inbound-channel-adapter>.
And yes: to have several filters in place you should use CompositeFileListFilter.
However I think we can protect that point of the override case.
Feel free to raise JIRA issue on the matter.

jsr 303 how to force it to use my resourcebundle?

I have validator class which uses
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages" />
</bean>
Besides validator which does more complex validation I also use JSR303 like
#NotNull(message="null value is not allowed")
protected String postCode;
I want for JSR to use same file source messages.
#NotNull(message=<SOMEHOW GO TO MY messages_en_US.properties AND EXTRACT SOMETHING LIKE : user.poscode.null>)
Thanks!
By default JSR303 validator looks for a file called ValidationMessages.properties, which is bundled with the implementation you use.
If you want to add your own constraint validation messages you have to provide your own ValidationMessages.properties, and add to it the message.
You can look at Message interpolation, Hibernate validator docs.
But what I've just describe its Java EE standard behaviour I don't know if Spring behaves in the same way
I resolved the situation with help of another more focused question of mine on simillar topic. Anyone interested : javax.validation how to target different locale?
Please be aware however that there will be a need in two sets of files including resourcebundle and properties.

Multiple dependencies with Spring

Can anyone advice me how can I inject multiple dependencies for a same bean in the spring framework?
I will try to explain the scenario very briefly, in case anyone can suggest me a better solution.
I have a data repository and it has to go through certain filters to filter out unwanted data. The criteria for filtering change and are not fixed to one filter. So, I created a filter handler which filters data based on filters. I want to use IoC and inject the filter dependencies. Its straight forward till here, only that there can be multiple filters. How do I inject multiple dependencies. If I can create a List of filters, how do I declare a list in the xml file?
Thanks in advance,
You can do it like this (filter1 and filter2 are ids of beans defined elsewhere):
<property name="propertyName">
<list>
<ref bean="filter1"/>
<ref bean="filter2"/>
</list>
</property>
If your filters all implement the same interface, the most elegant way (in my opinion) is like this:
#Autowired
private List<YourFilterInterface> filters;
This will wire a list containing all registered beans implementing YourFilterInterface. It's available in Spring version 2.5 and up.
The Spring docs tell you how to create a list.
Example taken from above link...
<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
<value>pechorin#hero.org</value>
<value>raskolnikov#slums.org</value>
<value>stavrogin#gov.org</value>
<value>porfiry#gov.org</value>
</util:list>

Categories