get all keys from spring ReloadableResourceBundleMessageSource - java

I am using ReloadableResourceBundleMessageSource to store value list of my system. So i can use the i18N functionality
i am using multiple resources in the basenames of ReloadableResourceBundleMessageSource.
I want to pass all the localized labels of the web UI to the client (front-end) in order to be cached locally at the client.
Is there a way to load the entire keys of my resource bundles?
here is my ReloadableResourceBundleMessageSource bean definition:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:resource1</value>
<value>classpath:resource2</value>
</list>
</property>
<property name="cacheSeconds" value="60"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="useCodeAsDefaultMessage" value="true"/>
</bean>
and this is my controller that pass all the keys:
#Component
#RequestMapping("/bundle")
public class ResourceBundleController {
#Autowired
private MessageSource messageSource;
#RequestMapping(value = "/locales.js")
public ModelAndView strings(HttpServletRequest request) {
// Call the string.jsp view
return new ModelAndView("/WEB-INF/includes/locales.jsp", "keys", ??? );// HERE IS MY PROBLEM. HOW SHOULD I GET ALL THE KEYS FROM MESSAGESOURCE
}
}
and here is my the resource bundle keys for the client:
<%#page contentType="text/javascript" pageEncoding="UTF-8"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%#taglib prefix="spring" uri="http://www.springframework.org/tags"%>
var messages = {};
<c:forEach var="key" items="${keys}">
messages["<spring:message text='${key}' javaScriptEscape='true'/>"] = "<spring:message code='${key}' javaScriptEscape='true' />";
</c:forEach>
Any help will be appreciated.

Updated information about spring configuration regards cache of resource bundle
You could make something like this:
Your own implementation of ReloadableResourceBundleMessageSource:
public class ExposedResourceMessageBundleSource extends ReloadableResourceBundleMessageSource {
private static final Logger LOGGER = Logger.getLogger(ExposedResourceMessageBundleSource.class);
#Override
protected Properties loadProperties(Resource resource, String fileName) throws IOException {
LOGGER.info("Load " + fileName);
return super.loadProperties(resource, fileName);
}
/**
* Gets all messages for presented Locale.
* #param locale user request's locale
* #return all messages
*/
public Properties getMessages(Locale locale){
return getMergedProperties(locale).getProperties();
}
}
Service definition to handle reasource operations:
public interface MessageResolveService extends MessageSourceAware{
String getMessage(String key, Object[] argumentsForKey, Locale locale);
Map<String,String> getMessages(Locale locale);
}
And implementation:
#Service
public class MessageResolverServiceImpl implements MessageResolveService{
private static final Logger LOGGER = Logger.getLogger(MessageResolverServiceImpl.class);
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
LOGGER.info("Messages i18n injected");
this.messageSource = messageSource;
}
public String getMessage(String key, Object[] arguments, Locale locale){
String message = "";
try{
message = messageSource.getMessage(key,arguments,locale);
} catch(NoSuchMessageException e){
message = key;
LOGGER.warn("No message found: "+key);
}
return message;
}
public Map<String,String> getMessages(Locale locale){
Properties properties = ((XmlWebApplicationContext)messageSource).getBean("messageSource",
ExposedResourceMessageBundleSource.class).getMessages(locale);
Map<String,String> messagesMap = new HashMap<String,String>();
for(Map.Entry<Object,Object> entry: properties.entrySet()){
if(entry.getKey() != null && entry.getValue() != null) {
messagesMap.put(entry.getKey().toString(), entry.getValue().toString());
}
}
return messagesMap;
}
}
Spring configuration:
<bean id="messageSource" class="your.package.ExposedResourceMessageBundleSource">
<property name="basenames">
<list>
<value>classpath:yourFileName</value>
<value>classpath:yourNextFileName</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="10"/> //If you want reload message every couple seconds. In this case every 10 seconds.
</bean>
And your #Controller(similar to this):
#Component
#RequestMapping("/bundle")
public class ResourceBundleController {
#Autowired
private MessageResolveService messageResolveService;
#RequestMapping(value = "/locales.js")
public ModelAndView strings(HttpServletRequest request) {
// Call the string.jsp view
return new ModelAndView("/WEB-INF/includes/locales.jsp", "keys", messageResolverService.getMessages(LocaleContextHolder.getLocale()));
}

Related

Spring Boot JSP page works but XmlViewResolver not working

I am developing a spring boot application, I am trying to use XmlViewResolver to view the pdf from predefined template using itext. Successfully getting the jsp page when I access the "/welcome" and "/invoice", but failed when I try "/pdf".
Please let me know what's wrong. Why the pdf bean is not working as expected?
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).
/WEB-INF/jsp/pdf.jsp
Below are the codes
#Controller
public class MainController {
#RequestMapping("/welcome")
public String hello(Model model) {
model.addAttribute("msg", "XmlViewResolver Demo");
return "success";
}
#RequestMapping("/invoice")
public String getInvoiceInfo() {
return "invoice";
}
#RequestMapping("/pdf")
public String getPdfInfo() {
return "pdf";
}
}
JspConfig.java
#Component
public class JspConfig {
#Bean
public ViewResolver internalResourceViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/jsp/");
bean.setSuffix(".jsp");
bean.setOrder(2);
return bean;
}
}
XmlConfig.java
#Configuration
public class XmlConfig {
#Bean
public XmlViewResolver xmlViewResolver() {
XmlViewResolver resolver = new XmlViewResolver();
Resource resource = new ClassPathResource("xml/user-view.xml");
resolver.setLocation(resource);
resolver.setOrder(1);
return resolver;
}
}
user-view.xml
<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-3.0.xsd">
<bean id="invoice" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="WEB-INF/template/invoice.jsp" />
</bean>
<bean id="pdf" class="com.example.config.PdfView">
<property name="url" value="WEB-INF/template/invoiceTemplate.pdf" />
</bean>
PdfView.java
import com.lowagie.text.pdf.PdfStamper;
public class PdfView extends AbstractPdfStamperView implements MessageSourceAware {
#Override
protected void mergePdfDocument(Map<String, Object> model, PdfStamper stamper, HttpServletRequest request, response) throws Exception {
stamper.setFormFlattening(true);
String customerName = (String) model.get("customerName");
stamper.getAcroFields().setField("name", customerName);
stamper.close();
}
}

Returning static page with url path parameters

I have the following controller
#Controller
#RequestMapping("/room/{roomId:^(?!main.html$).*}")
public class Rooms {
#RequestMapping
public String index(#PathVariable(value = "roomId") String id) {
// do some stuff...
return "main.html";
}
}
I have a main.html file in my resource/static folder. I would like when a user goes to /room/{something} to do some processing, return the static main.html page I have.
Everything I try results in a page not found exception or
javax.servlet.ServletException: Circular view path [main.html]:
would dispatch back to the current handler URL
[/room/main.html] again
what am I missing to allow this to work?
Thanks for the help
If you are doing configuration in class for view resolver
#Bean
public UrlBasedViewResolver urlBasedViewResolver()
{
UrlBasedViewResolver res = new InternalResourceViewResolver();
res.setViewClass(JstlView.class);
res.setPrefix("/WEB-INF/");
res.setSuffix(".jsp");
return res;
}
If using xml
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/jsp/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Also change your controller by below code
#RestController
#RequestMapping("/room/")
public class Rooms {
#RequestMapping(method = RequestMethod.GET)
public ModelAndView index(#PathVariable(value = "roomId") String id) {
// do some stuff...
ModelAndView model = new ModelAndView();
model.setViewName("main");
return model;
}
}

How to inject values from Java Class to Datasource Bean in spring

This might sound like a novice question. I want to inject datasource properties (which I am getting at runtime) and inject it to the bean..
I have a method in my javaclass...
public <String,String>map myMethod(Map<String, String> model) {
Map mapA = new HashMap();
mapA.put("username", "element 1");
mapA.put("password", "element 2");
mapA.put("host", "element 3");
return map;
}
I want to inject these values to my datasource bean in application-context.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value=""/> // inject values here
<property name="url" />
<property name="username" />
<property name="password" />
</bean>
I have seen numerous example on injecting values to beans using properties file but I could not figure out on how to inject a value from java class to the bean properties.
Thanks
You need to create a #Configuration class with a method annotated with #Bean returning an instance of org.apache.commons.dbcp.BasicDataSource.
#Configuration
public class DatasourceConfiguration {
#Bean
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(""); // you can call your code here
ds.setUrl(""); // to get these configuration values
ds.setUsername("");
ds.setPassword("");
return ds;
}
}
It can be a not so elegant solution, but what about this approach?
You can try to return a String from your method.
#Configuration
public class DatasourceConfiguration2 {
#Bean
public String getDataSourceSetting() {
Map<String, String> map = myMethod(model); //assuming that you are not able to edit the original method
StringBuilder sb = new StringBuilder();
for (Entry<String, String> e : map.entrySet()) {
sb.append(e.getKey()).append('=').append(e.getValue()).append(';');
}
}
}
In your xml you can define the property like:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="connectionProperties" value="dataSourceSetting"/>
</bean>
Based on dbcp api:
The "user" and "password" properties will be added explicitly, so they do not need to be included here.
Checking the source code you can see if user and password are null a message like log("DBCP DataSource configured without a 'username'"); will be printed. But the property will be available there.
Finally, in case of url property, there is no option, you need to set it up explicitly.

How do I load properties with Spring when also using Camel?

I have a HelloWorld Java project that uses Camel to obtain a Map, and print out its contents in JSON format. The Map currently has hardcoded values, but I really want to change my code so that it uses Spring to load a sensor.properties file of nested key,value pairs into this Map.
I have another Java project I wrote that only uses Spring, and can load the sensor.properties file just fine into an Arraylist object.
However, when I try to use code from that project to load the sensor.properties in my HelloWorld project I get the following Camel error with a NPE:
Returning Map
3310 [hello.world.request.timer] ERROR org.apache.camel.processor.DefaultErrorHandler - Failed delivery for exchangeId: 4e984884-df7f-4b82-a977-f5cf4c311814. Exhausted after delivery attempt: 1 caught: java.lang.NullPointerException
java.lang.NullPointerException
at sample.SensorGenerator.getSensors(SensorGenerator.java:17)
at sample.HelloWorld.returnMap(HelloWorld.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.camel.component.bean.MethodInfo.invoke(MethodInfo.java:231)
at org.apache.camel.component.bean.MethodInfo$1.proceed(MethodInfo.java:146)
at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:138)
org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:67)
at org.apache.camel.processor.DelegateProcessor.processNext(DelegateProcessor.java:53)
at org.apache.camel.processor.DelegateProcessor.proceed(DelegateProcessor.java:82)
at org.apache.camel.processor.interceptor.TraceInterceptor.process(TraceInterceptor.java:97)
at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:67)
at org.apache.camel.processor.RedeliveryErrorHandler.processExchange(RedeliveryErrorHandler.java:185)
at org.apache.camel.processor.RedeliveryErrorHandler.processErrorHandler(RedeliveryErrorHandler.java:151)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:89)
at org.apache.camel.processor.DefaultErrorHandler.process(DefaultErrorHandler.java:49)
at org.apache.camel.processor.DefaultChannel.process(DefaultChannel.java:228)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:75)
at org.apache.camel.processor.UnitOfWorkProcessor.processNext(UnitOfWorkProcessor.java:70)
at org.apache.camel.processor.DelegateProcessor.process(DelegateProcessor.java:48)
at org. apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:67)
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:102)
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:49)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Is there something I need to add to my applicationContext.xml to tell Camel that Spring will load my sensor.properties? Do I need to use the Spring integration component specified at http://camel.apache.org/springintegration.html ?
Here is my current ApplicationContext.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:camel="http://camel.apache.org/schema/spring"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<context:component-scan base-package="sample" />
<context:annotation-config />
<camel:camelContext id="HelloWorldContext">
<!-- Add Jackson library to render Java Map into JSON -->
<camel:dataFormats>
<camel:json id="jack" library="Jackson"/>
</camel:dataFormats>
<camel:route>
<!-- sends a request to the hello world JMS queue every 10 seconds -->
<camel:from
uri="timer://hello.world.request.timer?fixedRate=true&period=10000" />
<camel:to uri="log:hello.world.request?level=INFO?showAll=true" />
<camel:bean ref="helloWorld" />
<!-- now print out the map in JSON format -->
<camel:marshal ref ="jack"/>
<camel:convertBodyTo type="java.lang.String" />
<camel:log message="${body}"/>
<!-- now log the message -->
<camel:to uri="log:hello.world.response?level=INFO?showAll=true" />
</camel:route>
</camel:camelContext>
<bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="transacted" value="false" />
<property name="concurrentConsumers" value="1" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
<property name="prefetchPolicy" ref="prefetchPolicy" />
</bean>
<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="5" />
</bean>
<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="1" />
<property name="backOffMultiplier" value="2" />
<property name="initialRedeliveryDelay" value="2000" />
<property name="useExponentialBackOff" value="true" />
</bean>
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<util:properties id="sensorProperties" location="classpath:/sensor.properties"/>
<bean class="sample.SensorGenerator">
<property name="sourceProperties" ref="sensorProperties" />
</bean>
</beans>
Here are the four Java Classes I have (HelloWorldMain.java, HelloWorld.java, Sensor.java, and SensorGenerator.Java):
UPDATED: The issue was that I had a constructor in my HelloWorld.java calling SensorGenerator instead of using #Autowired to let Spring do it. The answer by Frederic at the bottom shows the old code Constructor. The #Autowired annotation is shown below in HelloWorld.java:
HelloWorldMain.java:
package sample;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloWorldMain {
// define context to load properties with Spring
public static void main(String[] args) throws Exception {
AbstractApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
Thread.currentThread().join();
}
}
HelloWorld.java:
package sample;
import java.util.*;
import org.apache.camel.Handler;
import org.springframework.stereotype.Service;
/**
* POJO that returns Hello World string
*
*/
#Service
public class HelloWorld {
#AutoWired
SensorGenerator sensorGenerator;
#Handler
public Map<?, ?> returnMap(){
System.out.println();
System.out.println("Returning Map");
// get the map of Sensors
Map<String,String> mySensorMap = sensorGenerator.getSensors();
// print out the Sensors in the map on the console
Set keys = mySensorMap.keySet();
for (Iterator i = keys.iterator(); i.hasNext();) {
String key = (String) i.next();
String value = (String) mySensorMap.get(key);
System.out.println("key= " + key + ", value= " + value);
}
return mySensorMap;
}
}
Sensor.java (which defines the fields I'm reading from sensor.properties):
package sample;
public class Sensor {
private String make;
private String makeDataType;
private String model;
private String modelDataType;
private String serialNumber;
private String serialNumberDataType;
private String sensorType;
private String sensorTypeDataType;
// getters and setters
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public String getMakeDataType() {
return makeDataType;
}
public void setMakeDataType(String makeDataType) {
this.makeDataType = makeDataType;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getModelDataType() {
return modelDataType;
}
public void setModelDataType(String modelDataType) {
this.modelDataType = modelDataType;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public String getSerialNumberDataType() {
return serialNumberDataType;
}
public void setSerialNumberDataType(String serialNumberDataType) {
this.serialNumberDataType = serialNumberDataType;
}
public String getSensorType() {
return sensorType;
}
public void setSensorType(String sensorType) {
this.sensorType = sensorType;
}
public String getSensorTypeDataType() {
return sensorTypeDataType;
}
public void setSensorTypeDataType(String sensorTypeDataType) {
this.sensorTypeDataType = sensorTypeDataType;
}
}
SensorGenerator.java (the class where I current hard-code the properties but want to have Spring load them from sensor.properties. If I comment out the For loop and any lines referencing sourceProperties I can get the map returned with the hard coded values just fine. That's why I suspect its some sort of Spring/Camel integration issue):
package sample;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class SensorGenerator {
private Properties sourceProperties;
// variable to increment key number for each sensor
int sensorNumber = 1;
// method to inject sensor.properties into a Map using Spring
Map<String, String> getSensors() {
Map<String, String> sensorMap = new HashMap<String, String>();
for (Object key : sourceProperties.keySet()) {
// Separate out each of the key,value pairs as an entry in the
// values array
String[] values = sourceProperties.getProperty((String) key).split(
",");
System.out.println("values array size= " + values.length);
// define string buffer that appends sensor number for each sensor's
// keys. Ex: sensor1 would have s1make, s1makeDataType, etc.
StringBuffer sensorNumberStringBuffer = new StringBuffer();
sensorNumberStringBuffer.append("s");
sensorNumberStringBuffer.append(sensorNumber);
// make and its data type (with sensor number prefix)
StringBuffer makeStringBuffer = new StringBuffer();
makeStringBuffer.append(sensorNumberStringBuffer);
makeStringBuffer.append("make");
StringBuffer makeDataTypeStringBuffer = new StringBuffer();
makeDataTypeStringBuffer.append(sensorNumberStringBuffer);
makeDataTypeStringBuffer.append("makeDataType");
// model and its data type (with sensor number prefix)
StringBuffer modelStringBuffer = new StringBuffer();
modelStringBuffer.append(sensorNumberStringBuffer);
modelStringBuffer.append("model");
StringBuffer modelDataTypeStringBuffer = new StringBuffer();
modelDataTypeStringBuffer.append(sensorNumberStringBuffer);
modelDataTypeStringBuffer.append("modelDataType");
// serialNumber and its data type (with sensor number prefix)
StringBuffer serialNumberStringBuffer = new StringBuffer();
serialNumberStringBuffer.append(sensorNumberStringBuffer);
serialNumberStringBuffer.append("serialNumber");
StringBuffer serialNumberDataTypeStringBuffer = new StringBuffer();
serialNumberDataTypeStringBuffer.append(sensorNumberStringBuffer);
serialNumberDataTypeStringBuffer.append("serialNumberDataType");
// sensorType and its data type (with sensor number prefix)
StringBuffer sensorTypeStringBuffer = new StringBuffer();
sensorTypeStringBuffer.append(sensorNumberStringBuffer);
sensorTypeStringBuffer.append("sensorType");
StringBuffer sensorTypeDataTypeStringBuffer = new StringBuffer();
sensorTypeDataTypeStringBuffer.append(sensorNumberStringBuffer);
sensorTypeDataTypeStringBuffer.append("sensorTypeDataType");
/*
put all the key,value pairs for this sensor in the sensorMap
*/
//TODO: Change all the hard coded values below to be elements
// from the values array once Spring can load spring.properties
// make and and its data type
sensorMap.put(makeStringBuffer.toString(), "DummyMake");
sensorMap.put(makeDataTypeStringBuffer.toString(), "String");
// model and and its data type
sensorMap.put(modelStringBuffer.toString(), "DummyModel");
sensorMap.put(modelDataTypeStringBuffer.toString(), "String");
// serialNumber and and its data type
sensorMap.put(serialNumberStringBuffer.toString(), "1234567890");
sensorMap.put(serialNumberDataTypeStringBuffer.toString(), "long");
// sensorType and its data type
sensorMap.put(sensorTypeStringBuffer.toString(), "DummyType");
sensorMap.put(sensorTypeDataTypeStringBuffer.toString(), "String");
// increment for next sensor
sensorNumber++;
}
return sensorMap;
}
public void setSourceProperties(Properties properties) {
this.sourceProperties = properties;
}
}
Btw: Line 17 of SensorGenerator.java as mentioned in the stack trace above is:
for (Object key : sourceProperties.keySet()) {
Here is an example sensor.properties file:
sensor1=DummySensor1:String,SensorModel1:String,1234567890:long,SensorType1:String
sensor2=DummySensor2:String,SensorModel2:String,8675309123:long,SensorType2:String
The problem is that SensorGenerator is not instantiated by spring but by your code, so the properties can never be set.
#Service
public class HelloWorld {
#Handler
public Map<?, ?> returnMap(){
SensorGenerator sensorGenerator = new SensorGenerator();
You should have the sensorGenerator be autowired in your HelloWorld service.

How to set timeout in Spring WebServiceTemplate

I am using org.springframework.ws.client.core.WebServiceTemplate for making Web Service calls. How can i configure timeout for the call.
If you are using Spring Webservices 2.1.0 version, You can set timeout using HttpComponentsMessageSender.
CommonsHttpMessageSender are deprecated and not recommended by Spring anymore.
The way I have it implemented, I define my WebServiceTemplate to use HttpComponentsMessageSender.
Values are in Milliseconds
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="defaultUri" value="${endpoint.url}" />
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="unmarshaller" />
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="connectionTimeout" value="1200000" />
<property name="readTimeout" value="1200000" />
</bean>
</property>
</bean>
Just Make sure you have in your pom file, you added the following
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.1</version>
<scope>compile</scope>
</dependency>
Same as Sathish answer, but programmatically (Spring 4+):
#Component
public class MyWebServiceGatewaySupport extends WebServiceGatewaySupport
{
#Value("${my.ws.readtimeout}")
private String readTimeout;
#Value("${my.ws.connectiontimeout}")
private String connectionTimeout;
Object marshalSendAndReceive(String endpoint, Object requestPayload)
{
WebServiceTemplate wsTemplate = this.getWebServiceTemplate();
WebServiceMessageSender[] senders = wsTemplate.getMessageSenders();
for (WebServiceMessageSender sender: senders)
{
try
{
int readTimeoutMsec = Integer.parseInt(readTimeout);
int connTimeoutMsec = Integer.parseInt(connectionTimeout);
HttpComponentsMessageSender httpSender = (HttpComponentsMessageSender) sender;
httpSender.setReadTimeout(readTimeoutMsec);
httpSender.setConnectionTimeout(connTimeoutMsec);
}
catch (ClassCastException|NumberFormatException cex)
{
logger.warn("Cannot set WS timeout: " + cex.getMessage());
}
}
return wsTemplate.marshalSendAndReceive(endpoint, requestPayload);
}
}
Since Spring Webservices 2.2, you can also use Spring's ClientHttpRequestMessageSender:
#Service
public class CustomWebServiceImpl extends WebServiceGatewaySupport implements CustomWebService {
private static final int CONNECTION_TIMEOUT = (10 * 1000);
private static final int READ_TIMEOUT = (10 * 1000);
public CustomWebServiceImpl() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(CONNECTION_TIMEOUT);
requestFactory.setReadTimeout(READ_TIMEOUT);
setMessageSender(new ClientHttpRequestMessageSender(requestFactory));
}
(...)
}
(no dependency to Apache HTTP Components required)
The below code worked for me.
#Bean
public YourClassImpl yourClassImpl(Jaxb2Marshaller marshaller, HttpComponentsMessageSender httpComponentsMessageSender) {
YourClassImpl client = new YourClassImpl();
client.setDefaultUri(PiiProperties.SOAP_ACTION.getValue());
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
client.setMessageSender(httpComponentsMessageSender);
return client;
}
#Bean
public HttpComponentsMessageSender httpComponentsMessageSender() {
HttpComponentsMessageSender sender = new HttpComponentsMessageSender();
sender.setReadTimeout(1000);
sender.setConnectionTimeout(1000);
return sender;
}
If you want that kind of control, you can
either switch to CommonsHttpMessageSender, which uses the Jakarta Commons
HttpClient
or subclass HttpUrlConnectionMessageSender and in the
prepareConnection(HttpURLConnection) method call
UrlConnection.setReadTimeOut(int)
That's how I did:
#Configuration
public class MunisServiceConfig {
#Value("${service.uri}")
private String soapUri;
#Bean
Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath(CheckStatePayment.class.getPackage().getName());
return jaxb2Marshaller;
}
#Bean
public WebServiceTemplate munisService() {
WebServiceTemplate template = new WebServiceTemplate();
template.setMarshaller(jaxb2Marshaller());
template.setUnmarshaller(jaxb2Marshaller());
template.setDefaultUri(soapUri);
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
httpComponentsMessageSender.setReadTimeout(3000);
httpComponentsMessageSender.setConnectionTimeout(5000);
template.setMessageSender(httpComponentsMessageSender);
return template;
}
}
This article will probably sort you out:
http://onebyteatatime.wordpress.com/2009/03/19/how-to-set-socket-timeout-using-spring-webservicetemplate/
The way I have it implemented, I define my WebServiceTemplate to use CommonsHttpMessageSender:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory" />
<property name="messageSender">
<bean
class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
</bean>
</property>
</bean>
Then, in code, I get the messageSender and set the timeout on it. You could equally do this in your xml.
CommonsHttpMessageSender messageSender = (CommonsHttpMessageSender)webServiceTemplate.getMessageSenders()[0];
messageSender.getHttpClient().getParams().setSoTimeout(timeoutMillis);
This code works with Spring Boot (verified on 2.1.5.RELEASE):
#Configuration
public class ExampleServiceClientConfiguration {
#Value("${example-service.uri}")
private String exampleServiceUri;
#Value("${example-service.timeout:120}")
private int exampleServiceTimeout;
#Bean
public ExampleServiceClient exampleServiceClient() {
ExampleServiceClient client = new ExampleServiceClient();
client.setMessageSender(httpUrlConnectionMessageSender());
client.setDefaultUri(exampleServiceUri);
client.setMarshaller(marshaller());
client.setUnmarshaller(marshaller());
return client;
}
#Bean
HttpUrlConnectionMessageSender httpUrlConnectionMessageSender() {
HttpUrlConnectionMessageSender sender = new HttpUrlConnectionMessageSender();
Duration timeout = Duration.ofSeconds(exampleServiceTimeout);
sender.setReadTimeout(timeout);
sender.setConnectionTimeout(timeout);
return sender;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(ObjectFactory.class.getPackageName());
return marshaller;
}
}
For the CommonsHttpMessageSender, we can set the timeout in the following way:
<bean id="httpParams" class="org.apache.commons.httpclient.params.HttpClientParams">
<!-- Timeout in milliseconds: in this case 1 minute -->
<property name="soTimeout" value="60000" />
</bean>
<bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
<property name="params" ref="httpParams" />
</bean>
<!-- Define the message sender used by all web service templates -->
<bean id="webServiceMessageSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<constructor-arg>
<ref bean="httpClient"/>
</constructor-arg>
</bean>
and ref the webServiceMessageSender as below:
<bean id="genericWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="messageSender" ref="webServiceMessageSender"/>
</bean>

Categories

Resources