Spring Boot + Apache CXF. Publish endpoints with annotations only - spring

According to official Apache CXF documentation we create SOAP services in spring in such way:
Configuration class
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
#Configuration
public class WebServiceConfiguration {
private static final String BINDING_URI = "http://www.w3.org/2003/05/soap/bindings/HTTP/";
#Bean
public ServletRegistrationBean<CXFServlet> disServlet() {
return new ServletRegistrationBean<>(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus bus = new SpringBus();
bus.setProperty("org.apache.cxf.stax.maxTextLength", 1024 * 1024 * 1024);
return bus;
}
#Bean
public Endpoint userServiceEndpoint(UserService userService) {
EndpointImpl endpoint = new EndpointImpl(springBus(), userService);
endpoint.setBindingUri(BINDING_URI);
endpoint.publish("/users");
return endpoint;
}
}
Generated from WSDL SOAP interface:
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.3.2
* Generated source version: 2.2
*
*/
#WebService(name = "UserServiceSoap", targetNamespace = "http://User.no/webservices/")
#XmlSeeAlso({ObjectFactory.class})
public interface UserServiceSoap {
#WebMethod(operationName = "GetUser", action = "http://User.no/webservices/GetUser")
#WebResult(name = "GetUserResult", targetNamespace = "http://User.no/webservices/")
#RequestWrapper(localName = "UserServiceSoap", targetNamespace = "http://User.no/webservices/", className = "no.user.webservices.generated.UserServiceSoap")
#ResponseWrapper(localName = "UserServiceSoapResponse", targetNamespace = "http://User.no/webservices/", className = "no.user.webservices.generated.UserServiceSoapResponse")
public String getUser(#WebParam(name = "username", targetNamespace = "http://User.no/webservices/") String username);
}
Service implementation:
#Service
#WebService(
endpointInterface = "no.altinn.webservices.generated.UserServiceSoap",
serviceName = "UserServiceSoapService",
targetNamespace = "http://User.no/webservices/",
portName = "UserServiceSoapPort"
)
#BindingType("http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/")
public class UserService implements UserServiceSoap {
#Override
public String getUser(String requestUsername) {
//logic
}
}
What do I want?
Is there any already implemented way to publish SOAP endpoint without creating bean in the spring configuration. I want to do it with annotation on service implementation, for example (#SoapEndpoint):
#Service
#SoapEndpoint(bindingUri = "http://www.w3.org/2003/05/soap/bindings/HTTP/",
publish = "/users")
#WebService(
endpointInterface = "no.altinn.webservices.generated.UserServiceSoap",
serviceName = "UserServiceSoapService",
targetNamespace = "http://User.no/webservices/",
portName = "UserServiceSoapPort"
)
#BindingType("http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/")
public class UserService implements UserServiceSoap {
#Override
public String getUser(String requestUsername) {
//logic
}
}

Workaround:
Create empty interface named SoapService and #SoapEndpoint and initialize endpoints manually
SoapService interface:
public interface SoapService {
}
#SoapEndpoint interface:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
public #interface SoapEndpoint {
String bindingUri() default "http://www.w3.org/2003/05/soap/bindings/HTTP/";
String publish();
}
Initialize endpoints manually
#Configuration
public class WebServiceConfiguration {
private static final String BINDING_URI = "http://www.w3.org/2003/05/soap/bindings/HTTP/";
#Autowired
private List<SoapService> endpoints;
#Bean
public ServletRegistrationBean<CXFServlet> disServlet() {
return new ServletRegistrationBean<>(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus bus = new SpringBus();
bus.setProperty("org.apache.cxf.stax.maxTextLength", 1024 * 1024 * 1024);
return bus;
}
#PostConstruct
public void init() {
for (SoapService bean : endpoints) {
if (bean.getClass().getAnnotation(SoapEndpoint.class) == null) {
throw new IllegalArgumentException("Missed #SoapEndpoint for " + bean.getClass().getName());
}
EndpointImpl endpoint = new EndpointImpl(springBus(), bean);
endpoint.setBindingUri(BINDING_URI);
endpoint.publish(bean.getClass().getAnnotation(SoapEndpoint.class).publish());
}
}
}
Add SoapService into you implementation classes
#Service
#SoapEndpoint(publish = "/users")
#WebService(endpointInterface = "no.altinn.webservices.generated.UserServiceSoap", serviceName = "UserServiceSoapService", targetNamespace = "http://User.no/webservices/", portName = "UserServiceSoapPort")
#BindingType("http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/")
public class UserService implements UserServiceSoap, SoapService {
#Override
public String getUser(String requestUsername) {
//logic
}
}
--- Result (I've added OrganitionService which implements OrganizationServiceSoap) ---

Related

Programmitacally generate multiple beans

I have multiple merchants and a WSDL with different configurations is needed for each merchant. Right now, I'm copying and pasting the following method and changing the configs. But it creates some difficulties (requires code change & deployment). I want to initialize it programmatically at the run time. I've tried these methods but it din't work.
Is it possible?
#Bean(name = "marchant-1")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema commonSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("Marchant1WSPort");
wsdl11Definition.setLocationUri("/Marchant1WebService");
wsdl11Definition.setTargetNamespace("http://.../");
wsdl11Definition.setSchema(commonSchema);
return wsdl11Definition;
}
I solved similar kind of problem by specifying scope of my bean as prototype. Below example explains my implementation:
Create your DefaultWsdl11Definition class as shown below:
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("prototype")
#Getter
#Setter
public class DefaultWsdl11Definition {
private String portTypeName;
private String locationUri;
private String targetNamespace;
private XsdSchema schema;
}
Implement ApplicationContextAware to generate beans programmatically:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
#Service
public class ApplicationContextAwareImpl implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextAwareImpl.initApplicationContext(applicationContext);
}
private static void initApplicationContext(ApplicationContext applicationContext) {
ApplicationContextAwareImpl.context = applicationContext;
}
/**
* #param requiredType Bean class
*
* #return Bean of required type
*/
public static <T> T getBean(Class<T> requiredType) {
return context.getBean(requiredType);
}
}
Use ApplicationContextAwareImpl.getBean() method to generate beans programmatically:
DefaultWsdl11Definition wsdl11Definition = ApplicationContextAwareImpl.getBean(DefaultWsdl11Definition.class);
wsdl11Definition.setPortTypeName("Marchant1WSPort");
wsdl11Definition.setLocationUri("/Marchant1WebService");
wsdl11Definition.setTargetNamespace("http://.../");
wsdl11Definition.setSchema(commonSchema);
Also you can make use of Qualifier and Bean annotations to generate multiple beans of same type:
#Configuration
public class BeanConfigurations {
#Qualifier("marchant-1")
#Bean(name = "marchant-1")
public DefaultWsdl11Definition defaultWsdl11Definition1(XsdSchema commonSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("Marchant1WSPort");
wsdl11Definition.setLocationUri("/Marchant1WebService");
wsdl11Definition.setTargetNamespace("http://.../");
wsdl11Definition.setSchema(commonSchema);
return wsdl11Definition;
}
#Qualifier("marchant-2")
#Bean(name = "marchant-2")
public DefaultWsdl11Definition defaultWsdl11Definition2(XsdSchema commonSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("Marchant2WSPort");
wsdl11Definition.setLocationUri("/Marchant2WebService");
wsdl11Definition.setTargetNamespace("http://.../");
wsdl11Definition.setSchema(commonSchema);
return wsdl11Definition;
}
}
This solution worked for me:
#Configuration
public class WsBeanLoader implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
SimpleXsdSchema xsd = new SimpleXsdSchema(new ClassPathResource("xsd/sample.xsd"));
xsd.afterPropertiesSet();
BeanDefinitionBuilder builder = builder(name, xsd);
registry.registerBeanDefinition(name, builder.getBeanDefinition());
}
private BeanDefinitionBuilder builder(String name, SimpleXsdSchema xsd) {
name = StringUtils.capitalize(name);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DefaultWsdl11Definition.class);
builder.addPropertyValue("serviceName", name + "WebService");
builder.addPropertyValue("portTypeName", name + "WSPortBinding");
builder.addPropertyValue("locationUri", "/WS/" + name + "WS");
builder.addPropertyValue("targetNamespace", "url..");
builder.addPropertyValue("schema", xsd);
return builder;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}

Spring Boot: Add a specific HTTP header in a SOAP request based on Web Service Security (WS-Security, WSS) username

I am exposing a SOAP web service using Spring Boot. This web service is secured using Web Service Security (WSS) which is configured with this security_policy.xml:
<xwss:SecurityConfiguration
xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireUsernameToken
passwordDigestRequired="true" nonceRequired="true" />
</xwss:SecurityConfiguration>
Until this point, the application is working just fine. It is able to authenticate successfully.
Now, I need to add a specific HTTP header based on the WSS username. It is, adds the HTTP header "x-auth-type" with the values:
"test-auth-type" when the username is "test"
"production-auth-type" when the username is "production"
"undefined-auth-type" otherwise
I thought it was easy to add an EndpointInterceptor in which I can set the HTTP header based on the user, but is not been possible to me until now.
My Web Service Configuration class looks like this:
package com.godev.soapwebserviceswithspring;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static final String WS_SCHEMA_PATH = "godev_contract.xsd";
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
#Bean(name = "xml_message")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema billsSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("XmlMessagePort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace(NAMESPACE_URI);
wsdl11Definition.setSchema(billsSchema);
return wsdl11Definition;
}
#Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource(WS_SCHEMA_PATH));
}
#Bean
PayloadLoggingInterceptor payloadLoggingInterceptor() {
return new PayloadLoggingInterceptor();
}
#Bean
PayloadValidatingInterceptor payloadValidatingInterceptor() {
final PayloadValidatingInterceptor payloadValidatingInterceptor = new PayloadValidatingInterceptor();
payloadValidatingInterceptor.setSchema(new ClassPathResource(WS_SCHEMA_PATH));
return payloadValidatingInterceptor;
}
#Bean
XwsSecurityInterceptor securityInterceptor() {
XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
securityInterceptor.setCallbackHandler(callbackHandler());
securityInterceptor.setPolicyConfiguration(new ClassPathResource("security_policy.xml"));
return securityInterceptor;
}
#Bean
SimplePasswordValidationCallbackHandler callbackHandler() {
SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
callbackHandler.setUsersMap(Collections.singletonMap("admin", "pwd123"));
return callbackHandler;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(payloadLoggingInterceptor());
interceptors.add(payloadValidatingInterceptor());
interceptors.add(securityInterceptor());
}
}
My Web Service Endpoint class looks like this:
package com.godev.soapwebserviceswithspring;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import com.godev.soap.webservices.demo.GetXmlMessageRequest;
import com.godev.soap.webservices.demo.GetXmlMessageResponse;
#Endpoint
public class XmlMessageEndpoint {
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getXmlMessageRequest")
#ResponsePayload
public GetXmlMessageResponse getXmlDocument(#RequestPayload GetXmlMessageRequest request) {
GetXmlMessageResponse response = new GetXmlMessageResponse();
response.setXmlMessage("<xml>empty document</xml>");
return response;
}
}
Any advice will be very appreciated!
It works for me:
Inject the Security element present in the SOAP header in the Endpoint:
#Endpoint
public class XmlMessageEndpoint {
private static final String NAMESPACE_URI = "http://godev.com/soap/webservices/demo";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getXmlMessageRequest")
#ResponsePayload
public GetXmlMessageResponse getXmlDocument(#RequestPayload GetXmlMessageRequest request, #SoapHeader("{" + Security.SECURITY_NAMESPACE + "}Security") SoapHeaderElement securityHeader) {
GetXmlMessageResponse response = new GetXmlMessageResponse();
response.setXmlMessage("<xml>empty document</xml>");
return response;
}
In order to parse the securityHeader into something usable, you need to define a couple of POJOs. In my case, I only need the username
POJO for Security element:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NAMESPACE, name = "Security")
#Getter
#Setter
public class Security {
public static final String SECURITY_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
#XmlElement(namespace = Security.SECURITY_NAMESPACE, name = "UsernameToken")
private UsernameToken usernameToken;
}
POJO for UsernameToken element:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = Security.SECURITY_NAMESPACE, name = "UsernameToken")
#Getter
#Setter
public class UsernameToken {
#XmlElement(namespace = Security.SECURITY_NAMESPACE, name = "Username")
private String username;
}
And finally, you can parse the securityHeader using something like this:
public class SoapParser {
public static Security parseSecurityElement(SoapHeaderElement soapHeaderElement) {
Security securityElement = null;
try {
JAXBContext context = JAXBContext.newInstance(Security.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
securityElement = (Security) unmarshaller.unmarshal(soapHeaderElement.getSource());
} catch (JAXBException e) {
e.printStackTrace();
}
return securityElement;
}
}
I hope, it helps!

Spring context doesn't load my class in unittest

I can't get my unit test to load my configuration class.
My test class is annotated:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles(profiles = "test")
#ContextConfiguration (classes = ClientConfiguration.class)
public class ClientConfigurationTest { ...
ClientConfiguration.class
#ConditionalOnProperty(value = "dirt.security.oauth2client", matchIfMissing = false)
#Configuration
#PropertySource(value = "classpath:oauth2client-${spring.profiles.active:local}.properties", ignoreResourceNotFound=true)
public class ClientConfiguration {
static {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
#Bean
#ConfigurationProperties(prefix = "dirt.security.oauth2client.client")
ClientCredentialsResourceDetails clientConfig() {
return new ClientCredentialsResourceDetails() {
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
};
}
#Bean
#ConfigurationProperties(prefix = "dirt")
protected DirtClientConfig dirtClientConfig() {
return new DirtClientConfig();
}
#Bean
DirtRestTemplate restTemplate() {
DirtRestTemplate dirtRestTemplate =
new DirtRestTemplate(clientConfig(),
new DefaultOAuth2ClientContext(), dirtClientConfig());
dirtRestTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
return dirtRestTemplate;
}
}
None of the 3 beans get instantiated, and when I call this, it gets a dependecy error on one of the other beans
#Test
public void clientConfig() {
DirtRestTemplate results =
(DirtRestTemplate)context
.getAutowireCapableBeanFactory()
.createBean(DirtRestTemplate.class,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
true);
assertNotNull(results);
}

Set Spring SolrDocument Collection name based on PropertyValue

I want to set values Spring SolrDocument Collection based on application.yml value.
#Data
#SolrDocument(collection = #Value("${solr.core}"))
public class SearchableProduct {
}
Hoi Michela,
Ok, I had the same Problem and I found a solution: SpEL
it is described in details here:Spring Data for Apache Solr
you have to add the EL-expression to the Annotation
#SolrDocument(collection = "#{#serverSolrContext.getCollectionName()}")
public class SOLREntity implements Serializable {
.....
}
you have to provide a the serverSolrContext Bean with the method getCollectionName().
#Value("${solr.core}")
private String core;
public String getCollectionName() {
return core;
}
you have to write in our application.properties the following core entry.
solr.core=myOwnCoreName
That's it actually, BUT
if you get the following Exception, so as I did:
org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean
You have to have the following in your Configuration Bean
#Configuration
#EnableSolrRepositories(basePackages = { "de.solr.db" })
#Profile("default")
#PropertySource("classpath:application.properties")
public class ServerSolrContext extends AbstractSolrConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ServerSolrContext.class);
#Resource
private Environment environment;
#Value("${solr.core}")
private String core;
public String getCollectionName() {
return core;
}
#PostConstruct
public void init() {
System.out.println(core);
}
#Bean
public SolrClient solrClient() {
String url = environment.getProperty("solr.server.url");
String user = environment.getProperty("solr.server.user");
String password = environment.getProperty("solr.server.password");
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new UsernamePasswordCredentials(user, password));
SSLContext sslContext = null;
try {
sslContext = ReportConfiguration.getTrustAllContext();
}
catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClientBuilder.create().setSSLSocketFactory(sslSocketFactory)
.addInterceptorFirst(new PreemptiveAuthInterceptor()).setDefaultCredentialsProvider(credentialsProvider)
.build();
SolrClient client = new HttpSolrClient.Builder().withHttpClient(httpClient).withBaseSolrUrl(url).build();
return client;
}
#Bean
#ConditionalOnMissingBean(name = "solrTemplate")
public SolrTemplate solrTemplate(#Qualifier("mySolrTemplate") SolrTemplate solrTemplate) {
return solrTemplate;
}
#Bean("mySolrTemplate")
public SolrTemplate mySolrTemplate(SolrClient solrClient, SolrConverter solrConverter) {
return new SolrTemplate(new HttpSolrClientFactory(solrClient), solrConverter);
}
#Override
public SolrClientFactory solrClientFactory() {
return new HttpSolrClientFactory(solrClient());
}
}
The last 3 Methods are doing the Trick, that cost me a while to find the right solution:
it is here, so actually I was lucky to find this:
Allow PropertyPlaceholders in #SolrDocument solrCoreName

Spring boot configure MessageInterpolator #Bean

I am using Spring boot v1.4 and hibernate v4.3.5.finall in my application
I have writen my own ResourceBundle and MessageInterpolator to save messages in database and have configured them as bean in my project. It seems ResourceBundle works fine and returns my custom message but parameters don't pass,for example for this validation :
#Size(min=5,max = 10)
private String lastName;
I expect : size must be between 5 and 10 bla bla.....
but the result is : size must be between {min} and {max} bla bla.....
any Idea? Thanks..
my ResourceBundle class:
package ir.pt.core.bundles;
import ir.pt.common.bean.ResourceEntity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.io.IOException;
import java.util.*;
public class DatabaseResourceBundle extends ResourceBundle {
#PersistenceContext
protected EntityManager em;
private Map<String, String> cache = new HashMap<String, String>();
protected final static String BUNDLE_NAME = "ir.pt.core.bundles";
protected Control DB_CONTROL = new DBControl();
public DatabaseResourceBundle() {
setParent(ResourceBundle.getBundle(BUNDLE_NAME, DB_CONTROL));
}
public DatabaseResourceBundle(Locale locale) {
setParent(ResourceBundle.getBundle(BUNDLE_NAME, locale, DB_CONTROL));
}
#Override
protected Object handleGetObject(String key) {
return cache != null ? cache.get(key) : parent.getObject(key);
}
#Override
public Enumeration<String> getKeys() {
return parent.getKeys();
}
protected class DBControl extends Control {
#Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
return new CustomizedLocaleResources(locale);
}
protected class CustomizedLocaleResources extends ListResourceBundle {
private Locale locale;
public CustomizedLocaleResources(Locale locale) {
this.locale = locale;
}
#Override
protected Object[][] getContents() {
String sql = "FROM ResourceEntity re WHERE re.locale = '"+locale.getLanguage()+"'";
TypedQuery<ResourceEntity> query =
em.createQuery(sql, ResourceEntity.class);
List<ResourceEntity> resources = query.getResultList();
Object[][] all = new Object[resources.size()][2];
int i = 0;
for (Iterator<ResourceEntity> it = resources.iterator(); it.hasNext();) {
ResourceEntity resource = it.next();
all[i] = new Object[]{resource.getKey(), resource.getMessage()};
cache.put(resource.getKey(), resource.getMessage());
i++;
}
return all;
}
}
}
}
my MessageInterpolator class:
package ir.pt.core.bundles;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.MessageInterpolator;
import java.util.Locale;
import java.util.Map;
public class DatabaseMessageInterpolator extends ResourceBundleMessageInterpolator implements MessageInterpolator{
protected final String BRACE_OPEN = "\\{";
protected final String BRACE_CLOSE = "\\}";
#Autowired
DatabaseResourceBundle databaseResourceBundle;
#Override
public String interpolate(String message, Context context) {
return interpolate(message, context, databaseResourceBundle.getLocale());
}
#Override
public String interpolate(String message, Context context, Locale locale) {
String messageKey = context.getConstraintDescriptor().getAttributes().get("message").toString();
message = databaseResourceBundle.getString(messageKey.replaceAll(BRACE_OPEN, "").replaceAll(BRACE_CLOSE, ""));
Map<String, Object> attributes = context.getConstraintDescriptor().getAttributes();
for (String key : attributes.keySet()) {
String value = attributes.get(key).toString();
key = BRACE_OPEN + key + BRACE_CLOSE;
message = message.replaceAll(key, value);
}
return message;
}
}
my bean configuration:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {
#Override
public Validator getValidator() {
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setMessageInterpolator(messageInterpolator());
return factory;
}
#Bean
public MessageInterpolator messageInterpolator() {
return new DatabaseMessageInterpolator();
}
#Bean
ResourceBundle resourceBundle() {
return new DatabaseResourceBundle(new Locale("fa"));
}
}

Resources