how to extend a list in spring config - spring

I have defined a with some "common" values. How can I extend the common list by additional values to various new beans?
<util:list id="myCommonList" list-class="java.util.LinkedList">
<bean .../>
<bean .../>
<bean .../>
<bean .../>
</util:list>
<bean id="extension" parent="myCommonList">
<bean ref="additionalValue"/>
</bean>
Will this overwrite the list or extend it?

You can do it, but not using <util:list>, which is just a convenience syntax. The container does provide a "collection merging" function, but you have to use the "old" style:
<bean id="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>X</value>
</list>
</property>
</bean>
<bean id="child" parent="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<value>Y</value>
</list>
</property>
</bean>

Based on skaffman's answer, you can achive this way:
<util:list id="parent">
<value>X</value>
</util:list>
<bean id="child" parent="parent" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<value>Y</value>
</list>
</property>
</bean>

To append list2's elements to list1 use the following spring config.
<util:list id="list1">
<value>1</value>
<util:list>
<util:list id="list2">
<value>3</value>
<value>5</value>
<util:list>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="list1" />
<property name="targetMethod" value="addAll" />
<property name="arguments" ref="list2" />
</bean>

According to this JIRA; there is no trivial solution for this (currently, but hopefully in 3.1 there will be), though there are several workarounds; e.g. this one.

Today I was having this issue too. For me the acceptable solution was to use SpEL and while SpEL doesn't supports multiple statements - create two auxiliary classes that appends lists.
Context might be implemented like this:
<util:list id="list1">
<value>str1</value>
<value>str2</value>
</util:list>
<util:list id="list2">
<value>str3</value>
<value>str4</value>
</util:list>
<bean ...>
<property name="propertyThatRequiresMergedList"
value="#{ new x.y.springs.StringListsMerger().merge(#list1, #list2) }" />
</bean>
And classes:
package x.y.springs;
import java.util.List;
public abstract class ListsMerger<T extends List> {
public T merge(T ... lists) {
T container = createContainer();
for (T list : lists) {
container.addAll(list);
}
return container;
}
public abstract T createContainer();
}
package x.y.springs;
import java.util.ArrayList;
import java.util.List;
public class StringListsMerger extends ListsMerger<List<String>> {
#Override
public List<String> createContainer() {
return new ArrayList<String>();
}
}

You can overwrite or add any other bean in this list by following the below snippet.
For example: this is your bean which needs to be injected in the AAA util:list mentioned below.
<bean id = "BBB" class=""/>
<util:list id="AAA"/>
<bean id="AAAListMergeDirective" depends-on="AAA"
parent="listMergeDirective" >
<property name="add" ref="BBB" />
</bean>

Related

How to provide reference bean to XmlViewResolver

i am generating Excel Sheet using Spring MVC , for this reason i have the following in my appcontext.xml file
appcontext.xml
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
views.xml
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
​
Instead of having the bean reference excelView in a separate views.xml file .can we mention the below in the same appcontext.xml
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
Sure, you can.
Just add BeanNameViewResolver to your ViewResolver chain.
Of course, you should remove excelView from views.xml in this case.
Here is an example:
appcontext.xml:
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="0"/>
</bean>
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
<bean id="excelView" class="com.xxxx.xx.util.MyListExcelView" />
YourController.java
#Controller
public class YourController {
// ...
#RequestMapping(value = "/test")
public String test() {
// populate your model here
return "excelView";
}
// ...
}

Spring MVC - The #Controller annotation does not get autodetected

I am new in spring, therefore maybe my question is stupid! I am trying to add a controller to Spring's Petclinic example. However, the mappings in this controller do not work and I get 404 error. I added the same mapping to the other existing controllers and it works. I want to see what I am doing wrong. Here is my controller:
package org.springframework.samples.petclinic.web;
import java.util.Map;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
#Controller
public class TwitterController {
#RequestMapping(value = "/twitter/searchtweets", method = RequestMethod.GET)
public String initFindForm(Map<String, Object> model) {
...
return "twitter/searchtweets";
}
}
and here is the component scan definition in my MVC config file:
<context:component-scan
base-package="org.springframework.samples.petclinic.web"/>
And:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="cnManager"/>
<property name="viewResolvers">
<list>
<!-- Default viewClass: JSTL view (JSP with html output) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- Example: a logical view name of 'vets' is mapped to '/WEB-INF/jsp/vets.jsp' -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- Used here for 'xml' and 'atom' views -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
</list>
</property>
</bean>
<!-- Simple strategy: only path extension is taken into account -->
<bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true"/>
<property name="defaultContentType" value="text/html"/>
<property name="mediaTypes">
<map>
<entry key="html" value="text/html" />
<entry key="xml" value="application/xml" />
<entry key="atom" value="application/atom+xml" />
</map>
</property>
</bean>
The address: localhost:8081/petclinic/twitter/searchtweets.html returns 404 Error. It's worth mentioning that the jsp file here in: webapp/WEB_INF/jsp/twitter/searchtweets.jsp
Am I missing something?

XStream JavaBeanConverter Spring config to serialize using getter

I would like to register XStream JavaBeanConverter in Spring config. I see test examples where JavaBeanConverter is registered as below.
xstream.registerConverter(new JavaBeanConverter(xstream.getClassMapper(), "class"), -20);
But how do I set it up in my spring config.
Currently my spring config is setup as below
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" >
<property name="renderedAttributes" value="document" />
</bean>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller" p:autodetectAnnotations="true">
<property name="encoding" value="UTF-8" />
</bean>
</property>
<property name="contentType" value="application/xml;charset=UTF-8" />
<property name="modelKey" value="person" />
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
I am using xstream annotation for Alias and Converter.
I tried extending my custom converter from JavaBeanConverter. It serializes normal properties fine but I want it to serialize getXXX method.
public class MyCustomConverter extends JavaBeanConverter {
public MyCustomConverter(Mapper mapper) {
super(mapper);
}
#Override... marshal.. unmarshal... canConvert methods...
}
Thanks a lot!
Add this to your marshaller bean definition:
<property name="converters">
<util:list>
<bean class="com.MyBeanConverter">
<constructor-arg value="com.MyBean" index="0" />
</bean>
</util:list>
</property>
And then, define the following class that will implement the conversion
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.javabean.JavaBeanConverter;
public class MyBeanConverter extends JavaBeanConverter {
public MyBeanConverter(Class<?> theClass) {
super(new XStream().getMapper(), theClass);
}
}

How does one make Spring read from a file to string property?

I have a bean that has a string property that I would like to set, but I would like it set from a file without changing the bean code. The bean is something like this.
public class SomeBean {
public void setSomeProperty(String string) { ... }
}
I was looking for something like this in the beans.xml file
<beans>
<bean class="SomeBean">
<property name="someProperty">
<util:string src="classpath:foo.txt" />
</property>
</bean>
</beans>
Try using the PropertyPlaceholderConfigurer to load a value from a properties file:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:foo.properties</value>
</list>
</property>
</bean>
<bean class="SomeBean">
<property name="someProperty" value="${myBean.someProperty}" />
Then, in the foo.properties file, you set the property to whatever value you want:
myBean.someProperty = value
Hope this helps
I found a way using the Guava classes though it looks really bad.
(The value attribute is all in one line)
<property name="someProperty"
value="#{ T(com.google.common.io.Resources).toString(
T(com.google.common.io.Resources).getResource('foo.txt'),
T(java.nio.charset.Charset).forName('UTF-8')) }"/>
Hopefully someone can find a better answer.

Spring: How to inject a property with a non-setter method?

Is it possible to inject a property bean through a method with a signature doesn't start with set?
Specifically, I'm trying to use Spring to configure an embedded Jetty instance and I need to be able to inject a servlet bean via an addServlet() method.
I am looking at Jetty/Tutorial/Embedding Jetty documentation. I guess you mean calling ServletContextHandler.addServlet(). You have few choices:
#Configuration (since 3.0)
My favourite approach. You can configure everything using Java!
#Configuration
public class Jetty {
#Bean(initMethod = "start")
public Server server() {
Server server = new Server(8080);
server.setHandler(context());
return server;
}
#Bean
public ServletContextHandler context() {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addServlet(servlet(), "/*");
return context;
}
#Bean
public ServletHolder servletHolder() {
return new ServletHolder(helloServlet());
}
#Bean
public HelloServlet helloServlet() {
return new HelloServlet();
}
}
Inheritance/decorating
You can inherit from or wrap original ServletContextHandler class to follow Java bean naming conventions. Of course it requires an extra class, but makes Jetty class Spring-friendly. You can even publish such wrapper or maybe someone already did that?
MethodInvokingFactoryBean
I don't like this approach as it seems too low level. Basically you create a bean that calls arbitrary method with arbitrary arguments:
<bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="servletContextHandler"/>
<property name="targetMethod" value="addServlet"/>
<property name="arguments">
<list>
<ref bean="yourServlet"/>
</list>
</property>
</bean>
just the spring file adapted to Jetty 7. It's possible to add yours contextHandlers...
<bean id="contexts"
class="org.eclipse.jetty.server.handler.ContextHandlerCollection" />
<context:property-placeholder location="src/main/resources/ws.properties" />
<!-- Manually start server after setting parent context. (init-method="start") -->
<bean id="jettyServer" class="org.eclipse.jetty.server.Server"
destroy-method="stop">
<property name="threadPool">
<bean id="ThreadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<property name="minThreads" value="10" />
<property name="maxThreads" value="50" />
</bean>
</property>
<property name="connectors">
<list>
<bean id="Connector" class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<property name="port" value="8181" />
</bean>
</list>
</property>
<property name="handler">
<bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<property name="handlers">
<list>
<ref bean="contexts" />
<bean id="defaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler" />
<bean class="org.eclipse.jetty.servlet.ServletContextHandler"
p:contextPath="/${ws.context.path}">
<property name="sessionHandler">
<bean class="org.eclipse.jetty.server.session.SessionHandler" />
</property>
<property name="servletHandler">
<bean class="org.eclipse.jetty.servlet.ServletHandler">
<property name="servlets">
<list>
<bean class="org.eclipse.jetty.servlet.ServletHolder"
p:name="spring-ws">
<property name="servlet">
<bean
class="org.springframework.ws.transport.http.MessageDispatcherServlet" />
</property>
<property name="initParameters">
<map>
<entry key="contextConfigLocation" value="classpath:/spring-ws-context.xml" />
</map>
</property>
</bean>
</list>
</property>
<property name="servletMappings">
<list>
<bean class="org.eclipse.jetty.servlet.ServletMapping"
p:servletName="spring-ws" p:pathSpec="/*" />
</list>
</property>
</bean>
</property>
</bean>
<bean class="org.eclipse.jetty.server.handler.RequestLogHandler" />
</list>
</property>
</bean>
</property>
</bean>

Resources