I have several maps defined in my context file. Is there a way to combine those maps into one map that contains all of their entries, without writing Java code (and without using nested maps)? I'm looking for the equivalent of Map m = new HashMap(); m.putAll(carMap); m.putAll(bikeMap);
Seems like there should be a way to do this in Spring context file, but the Spring 3.0 reference doc section on util:map doesn't cover this use case.
<!-- I want to create a map with id "vehicles" that contains all entries of the other maps -->
<util:map id="cars">
<entry key="groceryGetter" value-ref="impreza"/>
</util:map>
<util:map id="bicycles">
<entry key="commuterBike" value-ref="schwinn"/>
</util:map>
Using collection merging concept in Spring, multiple beans like these can be merged incrementally. I used this in my project to merge lists , but can be extended to merge maps as well.
E.g.
<bean id="commonMap"
class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="1" value="one"/>
<entry key="2" value="two"/>
</map>
</property>
</bean>
<bean id="firstMap"
parent="commonMap"
class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map merge="true">
<entry key="3" value="three"/>
<entry key="4" value="four"/>
</map>
</property>
</bean>
The association of the second map definition with the first one is done through the parent attribute on the <bean> node and the entries in the first map are merged with those in the second using the merge attribute on the <map> node.
I bet there is no direct support for this feature in Spring.
However, writing a factory bean to use in Spring is not that difficult (haven't tried to compile that)
public class MapMerger <K,V> implements FactoryBean {
private Map<K,V> result = new HashMap<K,V>();
#Override
public Object getObject() {
return result;
}
#Override
public boolean isSingleton(){
return true;
}
#Override
public Class getObjectType(){
return Map.class;
}
public void setSourceMaps(List<Map<K,V>> maps) {
for (Map<K,V> m : maps) {
this.result.putAll(m);
}
}
}
In spring config, just do something like:
<bean id="yourResultMap" class="foo.MapMerger">
<property name="sourceMaps">
<util:list>
<ref bean="carMap" />
<ref bean="bikeMap" />
<ref bean="motorBikeMap" />
</util:list>
</property>
</bean>
Related
I have a Property Mapping (colorMap.xml):
<bean id="B_colorMapping" class="com.project.color.colorMap">
<property name="map">
<map>
<entry key="1" value="blue" />
<entry key="2" value="blue-green" />
<entry key-ref="10" value-ref="B_colorMapping" />
</map>
</property>
</bean>
<bean id="R_colorMapping" class="com.project.color.colorMap">
<property name="map">
<map>
<entry key="1" value="red" />
<entry key="2" value="red-yellow" />
<entry key-ref="10" value-ref="R_colorMapping" />
</map>
</property>
</bean>
And the com.project.color.colorMap codes look like this:
public class colorMap {
private Map<Object, Object> map;
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> Cmap) {
map = Cmap;
}
}
Right now, what is happening, I manually update the colorMap.xml by adding entries and values. What I'm planning to do is to create a JSP page to handle the adding of the values in my xml.
for now, I want to display the values of my xml on the JSP page.
For example: (on the JSP page...)
B_colorMapping:
Color Key: 1 Value: blue
Color Key: 2 Value: blue-green
R_colorMapping:
Color Key:1 Value: red
Color Key:2 Value: red-yellow
Can someone offer a help or tip on how to do this? THis is spring MVC (Object, Map, ModelAndView).
I'm stuck on this one.
I am using JMX in Spring application with XML configuration:
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=bean1" value-ref="bean1"/>
<entry key="bean:name=bean2" value-ref="bean2"/>
<entry key="bean:name=bean3" value-ref="bean3"/>
</map>
</property>
<property name="notificationListenerMappings">
<map>
<entry key="*">
<bean class="com.test.listener"/>
</entry>
</map>
</property>
</bean>
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1099" />
</bean>
<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
<property name="objectName" value="connector:name=rmi" />
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi" />
</bean>
I understand from various documents like instead of this XML configuration we could annotate it with #EnableMBeanExport and with #ManagedResource for the beans.
But i doubt how ConnectorServerFactoryBean gets configured with these annotations. Or is there any annotation available to configure RMI and connectorServerFactoryBean?
Also i need to know how to annotate, notificationListenerMappings configured?
P.S:
I have the code working for publisher and listener under XML configuration. I am planning to move it completely on annotation based as i do not want to disturb XML configuration already in PROD.
Edited
Found the following piece of code: planning to try it:
#Bean
public RmiRegistryFactoryBean registry() {
return new RmiRegistryFactoryBean();
}
#Bean
#DependsOn("registry")
public ConnectorServerFactoryBean connectorServer() throws MalformedObjectNameException {
ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/connector");
return connectorServerFactoryBean;
}
Edit 2:
I am proceeding on above mentioned approach and I am able to configure MBeans and able to publish notifications. But unfortunately I am stuck up with configuring NotificationListener through Annotation.
I tried adding the following:
#Bean
#DependsOn("registry")
public ConnectorServerFactoryBean connectorServer() throws MalformedObjectNameException {
ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/connector");
//TestListener is my NotificationListener class
ObjectName objectName = new ObjectName("bean:name=bean1");
connectorServerFactoryBean.getServer().addNotificationListener(objectName,
new TestListener(), null,null);
return connectorServerFactoryBean;
}
I am getting instanceNotFoundException stating bean:name=bean1 is not found. But I have configured like, #ManagedResource(objectName="bean:name=bean1") on my bean1.
Any help please on what i am missing?
#EnableMBeanExport has a server property, which reference the bean name of a server object.
see for example the test of this component, which use this server property : https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java
I am using spring integration 4.2.4.RELEASE and I came across a bug. I am trying to create a inbound-channel using a custom directory based on "WatchServiceDirectoryScanner". When I try to inject this scanner I get the error.
"The 'filter' and 'locker' options must be present on the provided external 'scanner': ". No matter what combination of properties I tried it does not work. The reason is because even I supply a locker and filters to my custom scanner the "FileReadingMessageSource" in spring creates their own. Therefore when the it asserts
Assert.state(!(this.scannerExplicitlySet && (this.filter != null || this.locker != null)),
"The 'filter' and 'locker' options must be present on the provided external 'scanner': "
+ this.scanner);
it fails. There is a filter being "FileListFilterFactoryBean.initializeFileListFilter" no matter what combination has been set the code below will create one and the whole things just fails.
// no filters are provided
else if (Boolean.FALSE.equals(this.preventDuplicates)) {
filtersNeeded.add(new AcceptAllFileListFilter<File>());
}
else { // preventDuplicates is either TRUE or NULL
filtersNeeded.add(new AcceptOnceFileListFilter<File>());
}
I have read the post: How to skip the settings of filter and locker
but it does not work for me.
Has anybody find a solution for this?
Here is a sample XML configuration of creating the inbound channel.
<bean id="inboundChannel_3522_filter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^Test_(20160216).TXT$" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="inboundChannel_3522_nio_locker" class="org.springframework.integration.file.locking.NioFileLocker" />
<bean id="inboundChannel_3522_scanner" class="com.test.spring.integraton.file.NonRecursiveWatchServiceDirectoryScanner"
>
<constructor-arg value="e:\data\incoming\Test-V2" />
<property name="filter" ref="inboundChannel_3522_filter"/>
<property name="locker" ref="inboundChannel_3522_nio_locker"/>
</bean>
<file:inbound-channel-adapter id="inboundChannel_3522" auto-create-directory="true" scanner="inboundChannel_3522_scanner"
directory="file:/c:/data/incoming/TEST/" prevent-duplicates="false" ignore-hidden="false" >
<integration:poller fixed-rate="1000"/>
</file:inbound-channel-adapter>
Basically I want to know if there is any way to override the bean FileReadingMessageSource so that I can change the Assert?
I have figure out how to get around this bug. So I create a scanner and I inject the filters and locker to the scanner. I create a custom FileReadingMessageSourceFactoryBean that skips creating filter and locker if a scanner has been injected. The CustomFileReadingMessageSourceFactoryBean with the changes is shown below. The changes occurs at line 172. The rest it is the same.
if (this.scanner != null)
{
this.source.setScanner(this.scanner);
}
else
{
if (this.filter != null)
{
if (this.locker == null)
{
this.source.setFilter(this.filter);
}
else
{
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(this.filter);
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(locker);
}
}
else if (this.locker != null)
{
CompositeFileListFilter<File> compositeFileListFilter = new CompositeFileListFilter<File>();
compositeFileListFilter.addFilter(new FileListFilterFactoryBean().getObject());
compositeFileListFilter.addFilter(this.locker);
this.source.setFilter(compositeFileListFilter);
this.source.setLocker(locker);
}
}
The new Spring XML looks as below. The difference is that we add the bean inboundChanel_3522.adapter.source at the end. What this does is when the inbound channel looks for a bean of type FileReadingMessageSourceFactoryBean it will append to its id the ".adpater.source" and if one exist it will use that otherwise it will create a default. In this case it will use our CustomFileReadingMessageSourceFactoryBean which has the logic to skip creating additional filters. It is very important that the custom bean comes after inbound-channel-adapter otherwise the bean factory will override it. Finally here is how the XML should look like.
<bean id="inboundChannel_3522_filter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.RegexPatternFileListFilter">
<constructor-arg value="^Test_(20160216).TXT$" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="inboundChannel_3522_nio_locker" class="org.springframework.integration.file.locking.NioFileLocker" />
<bean id="inboundChannel_3522_scanner" class="com.test.spring.integraton.file.NonRecursiveWatchServiceDirectoryScanner">
<constructor-arg value="e:\data\incoming\Test-V2" />
<property name="filter" ref="inboundChannel_3522_filter"/>
<property name="locker" ref="inboundChannel_3522_nio_locker"/>
</bean>
<file:inbound-channel-adapter id="inboundChannel_3522" auto-create-directory="true"
scanner="inboundChannel_3522_scanner"
directory="file:/c:/data/incoming/TEST/" prevent-duplicates="false" ignore-hidden="false" >
<integration:poller fixed-rate="1000"/>
</file:inbound-channel-adapter>
<bean id="inboundChannel_3522.adapter.source" class="com.test.spring.integraton.file.CustomFileReadingMessageSourceFactoryBean">
<property name="scanner" ref="inboundChannel_3522_scanner"/>
<property name="directory" value="e:\data\incoming\Test-V2"/>
<property name="autoCreateDirectory" value="true"/>
</bean>
I'm trying to add a simple redirect into a web application built in Restlets, and it's proving non-trivial. The task is a simple one: I want to actually redirect all missing files from a web application to the same static file.
I'm using org.restlet.routing.Redirector with the following values (I'm using Spring injection):
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
<entry key="/" value-ref="staticsRedirector" />
</map>
</property>
</bean>
<bean id="staticsRedirector" class="ca.uhnresearch.pughlab.tracker.restlets.CustomRedirector">
<constructor-arg ref="trackerComponentChildContext" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
I can play with the file hierarchy relatively simply, but I just want to send anything that doesn't match either /api or /statics to /statics/index.html within the same application.
Restlet is almost getting it, and it does seem now to pick up the reference to the correct file, it just doesn't quite serve it.
I've put a working copy of the whole thing (including Thierry's suggestions below) at: https://github.com/morungos/restlet-spring-static-files. What I'd like to happen is something like the equivalent sequential attempts below:
curl http://localhost:8080/statics/**/* to hit the corresponding /statics/**/*
curl http://localhost:8080 to hit the main /statics/index.html
curl http://localhost:8080/**/* to hit the main /statics/index.html
I made some tests regarding your issue and I can't figure out how to have your message :-(. Perhaps it's because I haven't the whole code.
In fact, I saw a problem at the level of the SpringRouter itself. I would like to attach the redirector with an attachDefault and not an attach("/", ...) / attach("", ...). The method setDefaultAttachment actually does an attach("", ...).
So I made work something with the following updates:
Create a custom SpringRouter
public class CustomSpringRouter extends SpringRouter {
public void setDefaultAttachment(Object route) {
if (route instanceof Redirector) {
this.attachDefault((Restlet) route);
} else {
super.setDefaultAttachment(route);
}
}
}
Create a custom Redirector. I got the context from the component instead of a child context.
public class CustomRedirector extends Redirector {
public CustomRedirector(Component component, String targetPattern, int mode) {
super(component.getContext(), targetPattern, mode);
}
}
I then use the following Spring configuration:
<?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="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="test.CustomSpringRouter">
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
</map>
</property>
<property name="defaultAttachment" ref="staticsRedirector" />
</bean>
<bean id="staticsRedirector" class="test.CustomRedirector">
<constructor-arg ref="myComponent" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
<bean name="apiRouter" class="org.restlet.ext.spring.SpringRouter">
(...)
</bean>
(...)
</beans>
Hope it helps you,
Thierry
So I currently am working on a project that uses Spring to inject a schema location for the xsd when calling an xsl, something like:
<bean id="transformer" class="com.mypackage.Transformer">
<property name="xsl" value="sample.xsl" />
<property name="params">
<map>
<entry key="schemaLocation" value-ref="schema" />
</map>
</property>
</bean>
<bean id="schema" class="java.lang.String">
<constructor-arg value="http://www.sample.com/schema/sampleSchema.xsd" />
</bean>
This works fine when you use a url as the schema location, but say for example you want to refer to a schema that is brought in on the classpath as a maven dependency. I've found that using something like 'classpath:sampleSchema.xsd' doesn't work. I would've thought this kind of behaviour was fairly common, is there an accepted workaround to this? Create a custom class that looks up the schema on the classpath and returns its path as a string?
OK so I thought I would share how I fixed this.
Two spring beans:
<bean id="schema" factory-method="getSchemaLocation" factory-bean="schemaFinder" />
<bean id="schemaFinder" class="com.mypackage.SchemaFinder">
<constructor-arg value="NAME_OF_SCHEMA" />
</bean>
And a class:
public class SchemaFinder
{
private String schemaLocation;
public SchemaFinder(String schemaName) throws Exception
{
try
{
URL absoluteSchemaUrl = getClass().getClassLoader().getResource(schemaName);
setSchemaLocation(absoluteSchemaUrl.toString());
}
catch (Exception e)
{
//whatever you want here
}
}
public void setSchemaLocation(String schemaLocation)
{
this.schemaLocation = schemaLocation;
}
public String getSchemaLocation()
{
return this.schemaLocation;
}
}
Quite easy when you think about it...