I am using mean bean 'http://meanbean.sourceforge.net' to validate/test my beans. It works fine for most of the beans. But when a bean has arrays in it, it is failing with following error.
SEVERE: getFactory: Failed to find suitable Factory for property=[hostNames] of type=[class [I]. Please register a custom Factory. Throw NoSuchFactoryException.
org.meanbean.factories.ObjectCreationException: Failed to instantiate object of type [[I] due to NoSuchMethodException.
Following is my sample code.
public class Machine {
private String[] hostNames;
public String[] getHostNames() {
return hostNames;
}
public void setHostNames(String[] hostNames) {
this.hostNames = hostNames;
}
}
import org.junit.Test;
import org.meanbean.test.BeanTester;
public class TestBeanUtil {
#Test
public void test1(){
new BeanTester().testBean(Machine.class);
}
}
Any help on how to get rid of this error. I found one way by ignoring specific fields like below.
Configuration configuration = new ConfigurationBuilder().ignoreProperty("hostNames").build();
new BeanTester().testBean(Machine.class, configuration);
But My concern is is there any way that i can test without ignoring specific proper (or) ignore all the arrays in one shot ?
You can create a custom factory for your field:
class HostNamesFactory implements Factory<String[]> {
#Override
public String[] create() {
return new String[] {"host1", "host2", "host3"};
}
}
Then use this factory when you create your custom configuration and pass it to the bean tester:
Configuration configuration = new ConfigurationBuilder().overrideFactory("hostNames", new HostNamesFactory()).build();
new BeanTester().testBean(Machine.class, configuration);
I am agree this is not the perfect solution but at least the property getter and setter will be tested.
Maybe too late, but you can include in the AbstractJavaBeanTest the factories of each property that cannot be created.
Here is a sample when you need a factory for LocalDateTime and UUID
#Test
public void getterAndSetterCorrectness() throws Exception
{
final BeanTester beanTester = new BeanTester();
beanTester.getFactoryCollection().addFactory(LocalDateTime.class,
new LocalDateTimeFactory());
beanTester.getFactoryCollection().addFactory(UUID.class,
new ExecutionUUIDFactory());
beanTester.testBean(getBeanInstance().getClass());
}
Then you just define the custom factories that you want (in this example)
/**
* Concrete Factory that creates a LocalDateTime.
*/
class LocalDateTimeFactory implements Factory<LocalDateTime>
{
#Override
public LocalDateTime create()
{
return LocalDateTime.now();
}
}
/**
* Concrete Factory that creates a UUID.
*/
class ExecutionUUIDFactory implements Factory<UUID>
{
#Override
public UUID create()
{
return UUID.randomUUID();
}
}
Related
I have following class, which publishes the spring event.
#Component
public class ApplicationReadyEventListener {
Boolean isHit = false;
#EventListener
public void handle(final ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getSpringApplication().getClass().toGenericString()));
isHit = true; // This needs to be replaced with CustomLoggerComponent
}
}
As I need to publish the event and need to check failure and success event, I have following test:
#ExtendWith({SpringExtension.class})
class ApplicationReadyEventListenerTest {
private final ApplicationContextRunner runner = new ApplicationContextRunner();
//Success Test
#Test
void loggerShouldLogWhenApplicationIsReady() {
SpringApplciation application = new SpringApplication(ApplicationReadyEventListener.class);
application.setWebApplicationType(WebApplicationType.NONE);
final ApplicationReadyEvent event = new ApplicationReadyEvent(application, null, mock(ConfigurableApplicationContext.class));
runner.withBean(ApplicationReadyEventListener.class)
.run(context -> {
context.publishEvent(event);
final ApplicationReadyEventListener applicationStartedListener = context.getBean(ApplicationReadyEventListener.class);
MatcherAssert.assertThat(applicationStartedListener.isHit(), is(true));
});
}
//FailureTest
#Test
void shouldNotCallApplicationStarted() {
SpringApplciation application = new SpringApplication(ApplicationReadyEventListener.class);
application.setWebApplicationType(WebApplicationType.NONE);
final RuntimeException runtimeException = new RuntimeException("Some Error Occurred");
final ApplicationEvent event = new ApplicationFailedEvent(application, null, mock(ConfigurableApplicationContext.class), runtimeException);
runner.withBean(ApplicationReadyEventListener.class)
.run(context -> {
context.publishEvent(event);
final ApplicationReadyEventListener applicationStartedListener = context.getBean(ApplicationReadyEventListener.class);
MatcherAssert.assertThat(applicationStartedListener.isHit(), is(false));
});
}
}
This is working fine as of now as the class (ApplicationReadyEventListener) does not have any bean. I want to have a custom logger for this one, and instead of isHit, I would be checking the side effect of custom logger's method getting called.
However, I could not add any dependency, so I tried to isolate the problem by creating a separate application which contains the subject under test ApplicationReadyEvent and to have CustomLoggerBean created, used following one:
#Configuration
public class CustomLogMockProvider {
#Bean
public Logger logger() {
return Mockito.mock(Logger.class);
}
}
And when I write this test for the same:
#Test
void tesCustomLoggerBeanPresence() {
SpringApplciation application = new SpringApplication(CustomLogger.class);
application.setWebApplicationType(WebApplicationType.NONE);
runner.withBean(CustomLogMockProvider.class)
.run(context -> {
String[] beanNamesForType = context.getBeanNamesForType(Logger.class);
Arrays.stream(beanNamesForType).forEach(System.out::println);
});
}
Getting UnsatisfiedDependencyException for the above one.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationReadyEventListener': Unsatisfied dependency expressed through constructor parameter 0: Could not convert argument value of type [java.lang.Class] to required type [com.priti.com.common.config.CustomLogger]: Failed to convert value of type 'java.lang.Class' to required type 'com.priti.com.common.config.CustomLogger'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.Class' to required type 'com.priti.com.common.config.CustomLogger': no matching editors or conversion strategy found
Any lead on this would be helpful.
I think your test setup is the problem. You're doing far to much things by hand instead of using the proper Spring tooling.
If you want to test your ApplicationReadyEventListener the test should look like this:
#ExtendWith({SpringExtension.class})
#ContextConfiguration(classes = ApplicationReadyEventListener.class)
class ApplicationReadyEventListenerTest {
#MockBean
private SpringApplication springApplicationMock;
#MockBean
private CustomLogger customLoggerMock;
#Autowired
private ApplicationEventPublisher publisher;
#Test
void name() {
publisher.publishEvent(new ApplicationReadyEvent(springApplicationMock, null, null));
verify(customLoggerMock).doSomething();
}
}
You run a Spring test by using SpringExtension.class. You mock your CustomLogger as well as the SpringApplication. Now you can publish a ApplicationReadyEvent and verify that your listener has invoked the CustomLogger.
I have an old code base that I need to refactor using Java 8, so I have an interface, which tells whether my current site supports the platform.
public interface PlatformSupportHandler {
public abstract boolean isPaltformSupported(String platform);
}
and I have multiple classes implementing it and each class supports a different platform.
A few of the implementing classes are:
#Component("bsafePlatformSupportHandler")
public class BsafePlatoformSupportHandler implements PlatformSupportHandler {
String[] supportedPlatform = {"iPad", "Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
Another implementation:
#Component("discountPlatformSupportHandler")
public class DiscountPlatoformSupportHandler implements PlatformSupportHandler{
String[] supportedPlatform = {"Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
At runtime in my filter, I get the required bean which I want:
platformSupportHandler = (PlatformSupportHandler) ApplicationContextUtil
.getBean(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
and call isPlatformSupported to get whether my current site supports the following platform or not.
I am new to Java 8, so is there any way I can refactor this code without creating multiple classes? As the interface only contains one method, can I somehow use lambda to refactor it?
If you want to stick to the current design, you could do something like this:
public class MyGeneralPurposeSupportHandler implements PlatformSupportHandler {
private final Set<String> supportedPlatforms;
public MyGeneralPurposeSupportHandler(Set<String> supportedPlatforms) {
this.supportedPlatforms = supportedPlatforms;
}
public boolean isPlatformSupported(String platform) {
return supportedPlatforms.contains(platform);
}
}
// now in configuration:
#Configuration
class MySpringConfig {
#Bean
#Qualifier("discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone"})); // yeah its not a java syntax, but you get the idea
}
#Bean
#Qualifier("bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone", "iPad"}));
}
}
This method has an advantage of not creating class per type (discount, bsafe, etc), so this answers the question.
Going step further, what happens if there no type that was requested, currently it will fail because the bean does not exist in the application context - not a really good approach.
So you could create a map of type to the set of supported platforms, maintain the map in the configuration or something an let spring do its magic.
You'll end up with something like this:
public class SupportHandler {
private final Map<String, Set<String>> platformTypeToSuportedPlatforms;
public SupportHandler(Map<String, Set<String>> map) {
this.platformTypeToSupportedPlatforms = map;
}
public boolean isPaltformSupported(String type) {
Set<String> supportedPlatforms = platformTypeToSupportedPlatforms.get(type);
if(supportedPlatforms == null) {
return false; // or maybe throw an exception, the point is that you don't deal with spring here which is good since spring shouldn't interfere with your business code
}
return supportedPlatforms.contains(type);
}
}
#Configuration
public class MyConfiguration {
// Configuration conf is supposed to be your own way to read configurations in the project - so you'll have to implement it somehow
#Bean
public SupportHandler supportHandler(Configuration conf) {
return new SupportHandler(conf.getRequiredMap());
}
}
Now if you follow this approach, adding a new supported types becomes codeless at all, you only add a configuration, by far its the best method I can offer.
Both methods however lack the java 8 features though ;)
You can use the following in your config class where you can create beans:
#Configuration
public class AppConfiguration {
#Bean(name = "discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
String[] supportedPlatforms = {"Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
#Bean(name = "bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
String[] supportedPlatforms = {"iPad", "Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
private PlatformSupportHandler getPlatformSupportHandler(String[] supportedPlatforms) {
return platform -> Arrays.asList(supportedPlatforms).contains(platform);
}
}
Also, when you want to use the bean, it is again very easy:
#Component
class PlatformSupport {
// map of bean name vs bean, automatically created by Spring for you
private final Map<String, PlatformSupportHandler> platformSupportHandlers;
#Autowired // Constructor injection
public PlatformSupport(Map<String, PlatformSupportHandler> platformSupportHandlers) {
this.platformSupportHandlers = platformSupportHandlers;
}
public void method1(String subProductType) {
PlatformSupportHandler platformSupportHandler = platformSupportHandlers.get(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
}
}
As it was written in Mark Bramnik's answer you can move this to configuration.
Suppose that it would be in yaml in that way:
platforms:
bsafePlatformSupportHandler: ["iPad", "Android", "iPhone"]
discountPlatformSupportHandler: ["Android", "iPhone"]
Then you can create config class to read this:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class Config {
private Map<String, List<String>> platforms = new HashMap<String, List<String>>();
// getters and setters
You can than create handler with checking code.
Or place it in your filter like below:
#Autowired
private Config config;
...
public boolean isPlatformSupported(String subProductType, String platform) {
String key = subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND;
return config.getPlatforms()
.getOrDefault(key, Collections.emptyList())
.contains(platform);
}
I am new in Spring framework. I develop a standalone console application. App will get several files of different format ( CSV, JSP, XML) as arguments. I want inject a certain implementation of parser according to file format.
my service and parsers
These is my service:
#Service
public class ParsingService {
private final Parser parser;
#Autowired
public ParsingService(Parser parser) {
this.parser = parser;
}
public List<Order> parse(String filePath) {
try {
return parser.parse(filePath);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
My main class:
public class Main {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);
for (String arg : args) {
ParsingService service = context.getBean(ParsingService.class);
List<Order> listOfParsedObjects = service.parse(arg);
listOfParsedObjects.forEach(System.out::println);
}
}
}
I will pass to command line several file paths and i need Spring to inject necessary implementation depending on file format.
Assuming that Parser is your own interface you can add a method telling the format it's able to parse:
public interface Parser {
List<Order> parse(String filePath);
String getFormat();
}
Then override it in all the implementations:
#Component
public class CsvParser implements Parser {
public static final String FORMAT = "csv";
public String getFormat(){
return FORMAT;
}
// ...
}
Configure your parser beans either by annotating the classes with #Bean/#Component or by creating instances in your config class. (If you're using SpringBoot I would suggest using #ConditionalOn... annotations in order to avoid creation of unnecessary beans)
Now you can inject all of your Parser instances into ParserService.
#Service
public class ParsingService {
private final Map<String, Parser> parsers;
#Autowired
public ParsingService(List<Parser> allParsers) {
this.parsers = allParsers
.stream()
.collect(Collectors.toMap(Parser::getFormat, p -> p));
}
public List<Order> parse(String filePath) {
try {
String format = getFormat(filePath);
Parser parser = parsers.get(format);
if(parser == null) {
// Replace this exception by a more appropriate one
throw new RuntimeException("No parsers found for format : " + format);
} else {
return parser.parse(filePath);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String getFormat(String filePath){
int i = filePath.lastIndexOf('.');
if (i > 0) {
return filePath.substring(i+1).toLowerCase();
} else {
// Replace this exception by a more appropriate one
throw new RuntimeException("Cannot determine the file format!");
}
}
}
This way neither your ParserService nor Main classes will depend from your custom Parser implementations. Once you need a new parser you can simply define a new class implementing the interface. No more changes needed.
UPDATE
Adding Main and AppConfig classes
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);
ParsingService service = context.getBean(ParsingService.class);
for (String arg : args) {
List<Order> listOfParsedObjects = service.parse(arg);
listOfParsedObjects.forEach(System.out::println);
}
}
}
#Configuration
#ComponentScan(basePackages = "your.root.package")
public class AppConf {
// Do something here
}
For parallel processing try replacing your for-loop in Main with the following code:
Arrays.stream(args)
.parallel()
.map(service::parse)
.flatMap(List::stream)
.forEach(System.out::println);
Or you can use an ExecutorService:
int poolSize = 3;
ExecutorService executorService = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
for (String arg : args) {
executorService.submit(() -> {
service.parse(arg).forEach(System.out::println);
});
}
My recommendation would be to consider using Spring Boot and the #ConditionalOnProperty annotation. In the code example below, there will only ever be a bean called csvParserImpl if the property of my.parser has the value of csv. By changing the property value from csv to json, jsonParserImpl will be created instead of csvParserImpl. If my.parser is not defined or set to a value which doesn't include neither csv nor json, then there will be no instance of Parser.
#Configuration
public class MyAutoconfiguration {
#Bean
#ConditionalOnProperty(name="my.parser", havingValue="csv")
CsvParserImpl csvParserImpl() {
return new CsvParserImpl();
}
#Bean
#ConditionalOnProperty(name="my.parser", havingValue="json")
JsonParserImpl jsonParserImpl() {
return new JsonParserImpl();
}
}
When I'm referring to "property", that has a specific meaning within spring boot. Externalized Configuration in spring boot can pull in property values from a multiple of sources, including environment variables, system variables, and command line variables.
You may want to inject a collection of Parsers
#Autowired
private List<Parser> parsers;
And then choose the correct parser from that list.
Also, that is possible to do through a Map
Spring Annotations - Injecting Map of Objects
You can define the method in the parser interface, that returns a collection of extensions, like this
public interface Parser {
List<String> getExtensions();
}
Then you can utilize Java 8 streams for looking for correct parser:
parsers.stream().filter(p->p.getExtensions().contains(extension)).findFirst();
This will return the optional which may contain the needed parser
When you add a parser, what you need is to add a parser and define the extensions. No need to change the code in main
I want implement strategy design pattern in spring boot application. I create BeanPostProcessor for construct strategy resolver:
#Component
public class HandlerInAnnotationBeanPostProcessor implements BeanPostProcessor {
private final UnpHandlersResolver unpHandlersResolver;
public HandlerInAnnotationBeanPostProcessor(UnpHandlersResolver unpHandlersResolver) {
this.unpHandlersResolver = unpHandlersResolver;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof HandlerIn) {
if (bean.getClass() != UnpHandler.class)
throw new RuntimeException("Not UnpHandler bean annotated by HandlerIn");
SmevMessageType[] type = ((HandlerIn) annotation).type();
for (SmevMessageType smevMessageType : type) {
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
}
}
}
return bean;
}
}
And I create resolver:
#Slf4j
#Component
public class UnpHandlersResolverImpl implements UnpHandlersResolver {
private Map<SmevMessageType, UnpHandler> map = new HashMap<>();
#Override
public void setHandler(SmevMessageType messageType, UnpHandler unpHandler) {
map.put(messageType, unpHandler);
}
#Override
public UnpHandler getUnpHandler(SmevMessageType type) {
UnpHandler sendRequestHandler = map.get(type);
if (sendRequestHandler == null)
throw new IllegalArgumentException("Invalid SendRequestHandler type: " + type);
return sendRequestHandler;
}
}
My BeanPostProcessor scan all beans with annotation HandlerIn and add to resolver's mup. I think it's wrong to do that:
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
But I not understand how can I add find beans to resolver. Before this implementation I faind beans in #Postconstruct method of resolver like:
context.getBeansWithAnnotation(HandlerIn.class);
But in this solution I have context in resolver and I think is bad.
Tell me how to properly implement what I want? In short, I want to have a set of classes that implement different behaviors. And the class that controls them. Give the class a parameter so that he chooses the right strategy and gives it to me. Like this:
Handler handler = handlersResolver.getHandler(messageType);
Result result = handler.somthing(param);
I'm going to try to make a simple example.
Interface Greeting {
void sayHello();
String getSupportedLanguage();
}
Then you have X number of implementations and you can loop through them in your "resolver"'s constructor to build the map. (I've seen this called a Proxy or a Decorator in code though, i.e. GreetingProxy or GreetingDecorator)
#Service
public GreetingResolver {
private Map<String, Greeting> languageToGreetingMap = new HashMap<>();
#Autowired
public GreetingResolver(List<Greeting> greetings) {
for (Greeting greeting : greetings) {
languageToGreetingMap.put(greeting.getSupportedLanguage(), greeting);
}
}
public void sayGreetingForLanguage(String language) {
languageToGreetingMap.get(language).sayHello();
}
}
This is a basic example of how one can do the strategy pattern in Spring. Every interface implementation of "Greeting" only knows about itself and what it can support. We then autowire all implementations in a list and loop through to create the map once and then during runtime only the relevant entry from the map in retrieved and used.
Note: this was typed "free hand" directly in the web page so please forgive any typos in the code.
I am currently working on a Spring Boot application that allows users to save categories into a database. I can get my code to "work", however, I think it limits the amount of testing I can do, hence my question.
The controller receives a list of categories. The controller iterates over these categories, validates them and depending on whether they are valid, they are saved to a database. The controller finally returns a list of messages, such that the recipient can identify which category has been accepted or rejected, etc.
I have a list of model messages (List), which upon each iteration, the controller instantiates a new model message (new ModelMessage()) and eventually adds it to the List. Is there a way to inject a new ModelMessage upon each iteration or do I need to use the new keyword? If I do use the new keyword, I feel like this is limiting my testability/tightly coupling my controller to the model message.
The controller:
#PostMapping("/category")
public String saveCategoryModelToDatabase(#RequestBody CategoryModelWrapper categoryModelWrapper){
List<CategoryModel> categoryModelList = categoryModelWrapper.getCategoryModelList();
modelMessageList.clear();
for(CategoryModel categoryModel : categoryModelList){
//Resetting model
modelMessage = new ModelMessage(); //This tightly couples my method to the ModelMessage class, which is bad for testing?
//#Autowired modelMessage; <-- something like this? Inject a new ModelMessage with each iteration.
modelMessage.setName(categoryModel.getName());
//Resetting categoryModelErrors
Errors categoryModelErrors = new BeanPropertyBindingResult(categoryModel, "categoryModel");
categoryModelValidator.validate(categoryModel, categoryModelErrors);
if(categoryModelErrors.hasErrors()){
modelMessage.setStatus(ModelMessageStatusEnum.REJECTED);
modelMessage.setReason(MODEL_MESSAGE_0004);
}
if(categoryModelService.save(categoryModel)){
modelMessage.setStatus(ModelMessageStatusEnum.ACCEPTED);
}
else{
modelMessage.setStatus(ModelMessageStatusEnum.REJECTED);
modelMessage.setReason(MODEL_MESSAGE_0005);
}
modelMessageList.add(modelMessage);
}
return gson.toJson(modelMessageList);
}
An example of the response to the recipient:
[{"name":"Arts","status":"ACCEPTED"},{"name":"Business","status":"ACCEPTED"},{"name":"Gaming","status":"ACCEPTED"},{"name":"Deals","status":"REJECTED","reason":"Category rejected because of an unexpected exception, i.e. possibly due to duplicate keys."}]
Thanks for any help :)
You could use the ApplicationContext, assuming you have access to it, as a factory for ModelMessage. But, is that really necessary?
I think you can create new ModelMessages in your Controller, it's only a data object and not a service bean.
A junit can check the result of the method.
But if you really want to use Spring, I would look at the FactoryBean...
Example:
public class ModelMessage {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Component
public class ModelMessageFactory implements FactoryBean<ModelMessage> {
#Override
public ModelMessage getObject() throws Exception {
return new ModelMessage();
}
#Override
public Class<?> getObjectType() {
return ModelMessage.class;
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
#SpringJUnitConfig
public class ModelMessageFactoryTest {
#Autowired
private ModelMessageFactory messageFactory;
#Test
public void testGetObject() throws Exception {
assertNotNull("Factory is null", messageFactory);
IModelMessage modelMessage1 = messageFactory.getObject();
IModelMessage modelMessage2 = messageFactory.getObject();
assertNotEquals("error object is equal", System.identityHashCode(modelMessage1),
System.identityHashCode(modelMessage2));
}
#Test
public void testGetObjectType() throws Exception {
assertEquals(ModelMessage.class, messageFactory.getObjectType());
}
}