java test custom annotation that load message from properties file - spring-boot

I have create custom annotation that load default error message from properties file
Java hibernate-validator #interface load from properties
now i want to test it, that when invalid value is fill, the default error is show up
here is my test class
public class CreditCardTest {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
String[] inValidValues = new String[] {
"??",
"##",
"]]]"
};
#Test
public void ccInValid() {
for (String value : inValidValues) {
AnnotatedBean bean = new AnnotatedBean();
bean.value = value;
Set<ConstraintViolation<AnnotatedBeanCustom>> constraintViolationCustom = validator.validate(bean);
constraintViolation.stream().forEach( v -> assertEquals(v.getMessage(), "contains invalid character"));
}
}
private class AnnotatedBean {
#AccountNumber
String value;
}
}
when i run above test class the error is like
org.junit.ComparisonFailure: expected:<[{pacakge.cc.message}]> but was:<[contains invalid character]>
how do make the annotation on test class load the properties file?

Related

Argument 1: cannot convert from 'Callbacks.OnConsoleLogHandler' to 'Kotlin.Jvm.Functions.IFunction2'

I created a binding library, via a .arr file.
And in one of the methods I need to pass a callback.
What I was trying to do:
this is my delegate:
public class Callbacks
{
public delegate void OnConsoleLogHandler(string message, Sometype type);
}
this is my Configuration class:
public class Configuration
{
public Callbacks.OnConsoleLogHandler onConsoleLog;
public Configuration()
{
}
public Configuration(Action<Configuration> action)
{
var config = new Configuration();
action?.Invoke(config);
onConsoleLog = config.onConsoleLog;
}
}
here's how I'm trying to call a native method:
public static void Start(Configuration configuration)
{
var onConsoleLog = configuration.onConsoleLog;
Package.Name.Configuration config = new Package.Name.Configuration();
config.SetOnConsoleLog(onConsoleLog);
}
But on this row config.SetOnConsoleLog(onConsoleLog);
I get error:
Argument 1: cannot convert from 'Callbacks.OnConsoleLogHandler' to 'Kotlin.Jvm.Functions.IFunction2'
If I try something like this:
config.SetOnConsoleLog(() => { });
I get:
Cannot convert lambda expression to type 'IFunction2' because it is not a delegate type
How can I put the delegate as a callback? Any suggestions?

How to override jna.tmpdir and java.io.tmpdir properties in testcontainers?

TestContainers have Native.java class, where it gets JNA props from "jna.tmpdir" key. How to override value of that key? like:
jna.tmpdir=/data/builds/compose-tmp
java.io.tmpdir=/data/builds/compose-tmp
Part of code where it gets jna prop: 
package com.sun.jna;
public final class Native implements Version {
...
static File getTempDir() throws IOException {
File jnatmp;
String prop = System.getProperty("jna.tmpdir");
if (prop != null) {
jnatmp = new File(prop);
jnatmp.mkdirs();
}
}

How to read all properties values from the source properties file in Spring-cloud-config client

i have this spring-cloud-config client class and i can access the individual properties using the #Value annotation just fine. However, i am interested to know how to read ALL the properties values from a properties file without binding each of the property's key to a #Value annotation. Basically the idea is that i would like to read all the properties value from the properties file without even knowing anything about the properties defined in the file. Any idea how i can do that?
Client Class
#EnableAutoConfiguration
#ComponentScan
#RestController
#RefreshScope
public class ConfigDemoClientApplication
{
#Value("${special}")
String special;
#RequestMapping("/restaurant")
public String hello()
{
return "Hello " + special;
}
public static void main(String[] args) {
SpringApplication.run(ConfigDemoClientApplication.class, args);
}
}
Sample Properties file
special: bargain!
amount: 200
city: New York
In this example, i would like to read all the 3 properties without defining a #Value annotation for each of them in my class. Is that possible?
Thanks for your help.
I just solved you problem creating this applicationProps bean, that is a java.util.Properties object containing all the properties of the application.
The only think needed is an autowired Environment object.
Here's the code:
#Autowired
Environment env;
//Load all the properties of the server and put them into a java Properties obj
#Bean(name = "applicationProps")
public Properties applicationProperties() {
final Properties properties = new Properties();
for(Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
PropertySource propertySource = (PropertySource) it.next();
if (propertySource instanceof PropertiesPropertySource) {
log.info("Adding all properties contained in " + propertySource.getName());
properties.putAll(((MapPropertySource) propertySource).getSource());
}
if (propertySource instanceof CompositePropertySource){
properties.putAll(getPropertiesInCompositePropertySource((CompositePropertySource) propertySource));
}
}
return properties;
}
private Properties getPropertiesInCompositePropertySource(CompositePropertySource compositePropertySource){
final Properties properties = new Properties();
compositePropertySource.getPropertySources().forEach(propertySource -> {
if (propertySource instanceof MapPropertySource) {
log.info("Adding all properties contained in " + propertySource.getName());
properties.putAll(((MapPropertySource) propertySource).getSource());
}
if (propertySource instanceof CompositePropertySource)
properties.putAll(getPropertiesInCompositePropertySource((CompositePropertySource) propertySource));
});
return properties;
}
#Autowired
#Qualifier("applicationProps")
Properties applicationProperties;
The recursive step in getPropertiesInCompositePropertySource method is needed because the properties fetched from the config server are recursively nested in a CompositePropertySource
Hope it helps
Greetings
Try this : Its all Spring, you can maybe use it with a PostConstruct Method
Map<String,String> someMap = new HashMap<>();
Resource resource = new ClassPathResource("some.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
for(Object key : props.keySet()) {
someMap.put(key.toString(),props.getProperty(key.toString()));
}

#MessageMapping with placeholders

I am working with Spring-websocket and I have the following problem:
I am trying to put a placeholder inside a #MessageMapping annotation in order to get the url from properties. It works with #RequestMapping but not with #MessageMapping.
If I use this placeholder, the URL is null. Any idea or suggestion?
Example:
#RequestMapping(value= "${myProperty}")
#MessageMapping("${myProperty}")
Rossen Stoyanchev added placeholder support for #MessageMapping and #SubscribeMapping methods.
See Jira issue: https://jira.spring.io/browse/SPR-13271
Spring allows you to use property placeholders in #RequestMapping, but not in #MessageMapping. This is 'cause the MessageHandler. So, we need to override the default MessageHandler to do this.
WebSocketAnnotationMethodMessageHandler does not support placeholders and you need add this support yourself.
For simplicity I just created another WebSocketAnnotationMethodMessageHandler class in my project at the same package of the original, org.springframework.web.socket.messaging, and override getMappingForMethod method from SimpAnnotationMethodMessageHandler with same content, changing only how SimpMessageMappingInfo is contructed using this with this methods (private in WebSocketAnnotationMethodMessageHandler):
private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
These methods now will resolve value considering properties (calling resolveAnnotationValues method), so we need use something like this:
private String[] resolveAnnotationValues(final String[] destinationNames) {
final int length = destinationNames.length;
final String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = this.resolveAnnotationValue(destinationNames[i]);
}
return result;
}
private String resolveAnnotationValue(final String name) {
if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
return name;
}
final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();
final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return name;
}
final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
return result != null ? result.toString() : name;
}
You still need to define a PropertySourcesPlaceholderConfigurer bean in your configuration.
If you are using XML based configuration, include something like this:
<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />
If you are using Java based configuration, you can try in this way:
#Configuration
#PropertySources(value = #PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Obs.: in this case, url-mapping-config.properties file are in a gradle/maven project in src\main\resources\META-INF\spring folder and content look like this:
myPropertyWS=urlvaluews
This is my sample controller:
#Controller
public class WebSocketController {
#SendTo("/topic/test")
#MessageMapping("${myPropertyWS}")
public String test() throws Exception {
Thread.sleep(4000); // simulated delay
return "OK";
}
}
With default MessageHandler startup log will print something like this:
INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
And with our MessageHandler now print this:
INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
See in this gist the full WebSocketAnnotationMethodMessageHandler implementation.
EDIT: this solution resolves the problem for versions before 4.2 GA. For more information, see this jira.
Update :
Now I understood what you mean, but I think that is not possible(yet).
Documentation does not mention anything related to Path mapping URIs.
Old answer
Use
#MessageMapping("/handler/{myProperty}")
instead of
#MessageMapping("/handler/${myProperty}")
And use it like this:
#MessageMapping("/myHandler/{username}")
public void handleTextMessage(#DestinationVariable String username,Message message) {
//do something
}
#MessageMapping("/chat/{roomId}")
public Message handleMessages(#DestinationVariable("roomId") String roomId, #Payload Message message, Traveler traveler) throws Exception {
System.out.println("Message received for room: " + roomId);
System.out.println("User: " + traveler.toString());
// store message in database
message.setAuthor(traveler);
message.setChatRoomId(Integer.parseInt(roomId));
int id = MessageRepository.getInstance().save(message);
message.setId(id);
return message;
}

How do I override a scoped bean for tests?

I have this bean in my Spring Java config:
#Bean
#Scope( proxyMode=ScopedProxyMode.TARGET_CLASS, value=SpringScopes.DESKTOP )
public BirtSession birtSession() {
return new BirtSession();
}
For tests, I need a mock without a scope (there is no "Desktop" scope in the test). But when I create a configuration for my test which imports the above configuration and contains:
#Bean
public BirtSession birtSession() {
return new MockSession();
}
I get a "Desktop" scoped mocked bean :-(
How do I make Spring "forget" the #Scope annotation?
PS: It works when I don't use #Import and use copy&paste but I don't want to do that.
The problem seems to be in ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod() that uses ScopedProxyCreator.createScopedProxy() static method to create the scoped bean definition:
// replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = proxyDef.getBeanDefinition();
}
As the BeanDefinitionHolder returns a RootBeanDefinition instead of ConfiguratioClassBeanDenition the scoped proxy bean definition (ie, the ScopedProxyFactoryBean) cannot be overriden by another Java Configuration class.
A workaround could be declaring the scoped beans to override in a xml configuration file and importing it with #ImportResource.
The problem isn't Spring keeping the annotation, the problem is that Spring first tries to parse the "productive" config and in order to do that, it checks whether the scope is available. Spring checks scopes eagerly. So it never gets to the second/overriding bean definition.
Create a dummy scope:
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
public class MockSpringScope implements org.springframework.beans.factory.config.Scope {
private Map<String, Object> objects = new HashMap<String, Object>();
#Override
public Object get( String name, ObjectFactory<?> objectFactory ) {
Object result = objects.get( name );
if( null == result ) {
result = objectFactory.getObject();
objects.put( name, result );
}
return result;
}
#Override
public Object remove( String name ) {
return objects.remove( name );
}
#Override
public void registerDestructionCallback( String name, Runnable callback ) {
// NOP
}
#Override
public Object resolveContextualObject( String key ) {
// NOP
return null;
}
#Override
public String getConversationId() {
// NOP
return null;
}
}
and register that under as "Desktop" scope. That will Spring allow to successfully parse the production config.

Resources