Annotation based ServiceLocatorFactoryBean? - java

I would like to implement Factory pattern in my project..i have gone through online resources and I came to know that spring ServiceLocatorFactoryBean should be implemented instead of normal java factory pattern....
i have followed this link but it is explained in xml based....can any one tell me how to do it using annotations based Factory pattern??

Spring Java Configuration ref guide #Configuration
Interface Parser.class
public interface Parser {
void parse(String str);
}
Implementation for above interface.
JsonParser.java
public class JsonParser implements Parser {
#Override
public void parse(String str) {
System.out.println("JsonParser.parse::" + str);
}
}
XMLParser.java
public class XMLParser implements Parser{
#Override
public void parse(String str) {
System.out.println("XMLParser.parse :: " + str);
}
}
ParserFactory.java actual Factory interface.
public interface ParserFactory {
public Parser getParser(ParserType parserType);
}
ParseType.java enum to specify parsing types(avoid typos and safe)
public enum ParserType {
JSON("jsonParser"), XML("xmlParser");
private final String value;
ParserType(String input) {
this.value = input;
}
public String getValue() {
return this.value;
}
#Override
public String toString() {
return this.value;
}
}
ParseService.java , where Business logic implemeted.
#Service
public class ParserService {
#Autowired
private ParserFactory parserFactory;
public void doParse(String parseString, ParserType parseType) {
Parser parser = parserFactory.getParser(parseType);
System.out.println("ParserService.doParse.." + parser);
parser.parse(parseString);
}
}
Finally AppConfig.java Spring java configuration class, where all of my beans registered as container managed beans.
#Configuration
#ComponentScan(basePackages = {"<Your Package Name>"})
public class AppConfig {
#Bean
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
#Bean(name = "jsonParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JsonParser jsonParser() {
return new JsonParser();
}
#Bean(name = "xmlParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public XMLParser xmlParser() {
return new XMLParser();
}
}
Now autowire ParserService bean in either controller or test classs, and invoke parese(String, ParseType) method to test.
Here is my test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
public class ServiceLocatorFactoryExample {
#Autowired
private ParserService parserService;
#Test
public void testParserFactory() {
parserService.doParse("Srilekha", ParserType.JSON);
parserService.doParse("Srilekha", ParserType.XML);
}
}

Look this complete example: Serevice Locator factory
It helps me to understand how it works using spring boot.

Related

Spring custom Scope with timed refresh of beans

I am working within an environment that changes credentials every several minutes. In order for beans that implement clients who depend on these credentials to work, the beans need to be refreshed. I decided that a good approach for that would be implementing a custom scope for it.
After looking around a bit on the documentation I found that the main method for a scope to be implemented is the get method:
public class CyberArkScope implements Scope {
private Map<String, Pair<LocalDateTime, Object>> scopedObjects = new ConcurrentHashMap<>();
private Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();
private Integer scopeRefresh;
public CyberArkScope(Integer scopeRefresh) {
this.scopeRefresh = scopeRefresh;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
.isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
}
return scopedObjects.get(name).getValue();
}
#Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable runnable) {
destructionCallbacks.put(name, runnable);
}
#Override
public Object resolveContextualObject(String name) {
return null;
}
#Override
public String getConversationId() {
return "CyberArk";
}
}
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk")
public String dateString(){
return LocalDateTime.now().toString();
}
}
#RestController
public class HelloWorld {
#Autowired
private String dateString;
#RequestMapping("/")
public String index() {
return dateString;
}
}
When I debug this implemetation with a simple String scope autowired in a controller I see that the get method is only called once in the startup and never again. So this means that the bean is never again refreshed. Is there something wrong in this behaviour or is that how the get method is supposed to work?
It seems you need to also define the proxyMode which injects an AOP proxy instead of a static reference to a string. Note that the bean class cant be final. This solved it:
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk", proxyMode=ScopedProxyMode.TARGET_CLASS)
public NonFinalString dateString(){
return new NonFinalString(LocalDateTime.now());
}
}

In Spring Boot which class would be initialize first #Component or #Configuration

I have a class annotated with #Component which is use to initialze application.yml config properties. Service classe is using configuration property. But sometime my Service class instance created before the Configuration class and I get null property value in service class, Its random not specific pattern.
Configuration Initializer class..
#Component
public class ConfigInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(ConfigInitializer.class);
#Autowired
ProxyConfig proxyConfig;
/*#PostConstruct
public void postConstruct(){
setProperties();
}
*/
#Override
public void afterPropertiesSet() {
setProperties();
}
private void setSystemProperties(){
log.debug("Setting properties...");
Properties props = new Properties();
props.put("PROXY_URL", proxyConfig.getProxyUrl());
props.put("PROXY_PORT", proxyConfig.getProxyPort());
System.getProperties().putAll(props);
}
}
#Component
#ConfigurationProperties(prefix = "proxy-config")
public static class ProxyConfig {
private String proxyUrl;
private String proxyPort;
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public String getProxyPort() {
return proxyPort;
}
public void setProxyPort(String proxyPort) {
this.proxyPort = proxyPort;
}
}
Service Class..
#Service("receiverService")
public class ReceiverService {
private static final Logger logger = LoggerFactory.getLogger(ReceiverService.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
public ReceiverService() {
initClient();
}
private void initClient() {
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
String value = System.getProperty("PROXY_URL"); **//Here I am getting null**
logger.info("Values : " + value);
}
});
System.out.println("future.get() = " + future.get());
}
}
Above Service class get null values String value = System.getProperty("PROXY_URL")
When I use #DependsOn annotation on Service class, it works fine.
In my little knowledge, I know Spring does not have specific order of bean creation.
I want to know If I use #Configuration instead of #Component on ConfigInitializer class like below, Will spring initialize ConfigInitializer
class before other beans ?.
#Configuration
public class ConfigInitializer implements InitializingBean {
//code here
}

#PropertySource java.io.FileNotFoundException

I am testing Spring's #Conditional in which I load the bean depending on the value present in the .properties file. So I created a .properties file in src/main/resources/application-config.properties and I have the configuration class as:
#Configuration
#PropertySource(value = {"classpath:application-config.properties"}, ignoreResourceNotFound = false)
#ComponentScan(basePackages = {"com.app.test"})
public class ApplicationContextConfig {...}
I have 2 Condition implementations as below:
public class ConditionalBeanOne implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String name= conditionContext.getEnvironment().getProperty("condition.name");
return name.equalsIgnoreCase("condition_one");
}
}
public class ConditionalBeanTwo implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String name= conditionContext.getEnvironment().getProperty("condition.name");
return name.equalsIgnoreCase("condition_two");
}
}
I have respective POJO classes as:
#Component
#Conditional(value = ConditionalBeanOne.class)
public class BeanOne implements ServiceBean {}
#Component
#Conditional(value = ConditionalBeanTwo.class)
public class BeanTwo implements ServiceBean {}
When I run the application, I get following exception:
Caused by: java.io.FileNotFoundException: class path resource [application-config.properties] cannot be opened because it does not exist
I am running this through main method as following:
public class ConditionalMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
.....
}
}
I couldn't reproduce your problem so I created a complete working example based on your use case, which is also available on my GitHub. I noticed that your conditions are really the same, only the values are different, so you don't really need to duplicate the code there. Other than that, it's pretty much what you did.
I'd say that you're reinventing the wheel here. Spring Boot already has a ConditionalOnProperty which does this.
Application.java:
public class Application {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfig.class)) {
ApplicationConfig.GreeterService greeterService =
applicationContext.getBean(ApplicationConfig.GreeterService.class);
String actual = greeterService.greeting();
System.out.printf("Greeting: %s.\n", actual);
}
}
}
ApplicationConfig.java:
#Configuration
// The / doesn't matter, but I prefer being explicit
#PropertySource("classpath:/application.properties")
#ComponentScan
public class ApplicationConfig {
#FunctionalInterface
public static interface GreeterService {
String greeting();
}
#Bean
#ConditionalOnProperty("hello")
public GreeterService helloService() {
return () -> "hello";
}
#Bean
#ConditionalOnProperty("hi")
public GreeterService hiService() {
return () -> "hi";
}
}
ConditionalOnProperty.java:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(OnPropertyCondition.class)
public #interface ConditionalOnProperty {
String value() default "";
}
OnPropertyCondition.java:
public class OnPropertyCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> attributes = annotatedTypeMetadata
.getAnnotationAttributes(ConditionalOnProperty.class.getName());
String value = (String) attributes.get("value");
String name = conditionContext.getEnvironment().getProperty("greeting");
return !isEmpty(name) && name.equalsIgnoreCase(value);
}
}
application.properties:
greeting=hello
Run normally:
Output:
Greeting: hello.
Run with -Dgreeting=hi:
Output:
Greeting: hi.

Spring PropertySourcesPlaceholderConfigurer does not use custom PropertySource for #Value

I have been trying to get a very basic example of a custom PropertySource running in a Spring Application.
This is my PropertySource:
public class RemotePropertySource extends PropertySource{
public RemotePropertySource(String name, Object source) {
super(name, source);
}
public RemotePropertySource(String name) {
super(name);
}
public Object getProperty(String s) {
return "foo"+s;
}
}
It gets added to the ApplicationContext via an ApplicationContextInitializer:
public class RemotePropertyApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext ctx) {
RemotePropertySource remotePropertySource = new RemotePropertySource("remote");
ctx.getEnvironment().getPropertySources().addFirst(remotePropertySource);
System.out.println("Initializer registered PropertySource");
}
}
Now I created a simple Unit-Test to see if the PropertySource is used correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RemotePropertySourceTest.ContextConfig.class, initializers = RemotePropertyApplicationContextInitializer.class)
public class RemotePropertySourceTest {
#Autowired
private UnderTest underTest;
#Autowired
Environment env;
#Test
public void testContext() {
assertEquals(env.getProperty("bar"),"foobar");
assertEquals(underTest.getFoo(),"footest");
}
#Component
protected static class UnderTest {
private String foo;
#Autowired
public void setFoo(#Value("test")String value){
foo=value;
}
public String getFoo(){
return foo;
}
}
#Configuration
#ComponentScan(basePackages = {"test.property"})
protected static class ContextConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
return configurer;
}
}
}
Accessing the value via the Environment gives me the correct result ("foobar"), but using the #Value-Annotation fails. As far as I have read in the documentation the PropertySourcesPlaceholderConfigurer in my Configuration should automatically pick up my PropertySource from the environment but apparently it does not. Is there something I am missing?
I know that accessing properties explicitly via the environment is preferrable but the existing application uses #Value-Annotations a lot.
Any help is greatly appreciated. Thanks!
To get value from property source with #Value you have to use ${} syntax:
#Autowired
public void setFoo(#Value("${test}")String value){
foo=value;
}
Take a look at official documentation.

Is it possible to use #Valid annotation in console Spring-based application?

I have AnnotationConfigApplicationContext created with #Configuration annotated class as a param:
#Configuration
class Config {
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
#Bean
public A aBean() {
return new A();
}
#Bean
public B aBean() {
return new B();
}
}
Where A and B are:
class A {
#Min(1)
public int myInt;
}
class B {
#Autowire(required = true)
#Valid
public A aBean;
}
Q: Is it possible to make Spring to process #Valid annotation in this case?
PS: Currently I have following working implementation of B:
class B {
public A aBean;
public void setABean(A aBean, Validator validator) {
if (validator.validate(aBean).size() > 0) {
throw new ValidationException();
}
this.aBean = aBean;
}
}
This impl seems a bit clumsy to me and I want to replace it. Please help :)
It looks like you want to validate your bean in the process of injection.
You can read
here.
Here is an example:
public class BeanValidator implements org.springframework.validation.Validator, InitializingBean {
private Validator validator;
public void afterPropertiesSet() throws Exception {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.usingContext().getValidator();
}
public boolean supports(Class clazz) {
return true;
}
public void validate(Object target, Errors errors) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(target);
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
String propertyPath = constraintViolation.getPropertyPath().toString();
String message = constraintViolation.getMessage();
errors.rejectValue(propertyPath, "", message);
}
}
}
You will need to implement InitializingBean, to be able to validate the bean after it was set.

Categories