Spring boot application that run service in sequential order by passing the sequence at the run time - spring-boot

I want to implement a spring boot application that have 3 services . Now I want to run 3 services from my controller sequentially . I want to pass the the name of service and method as well as input parameters and sequence through controller and want to get the result of each of service . Please let me know how I can achieve these with a simple spring boot application.

Very very high level example. General idea that you need to use "reflection" for create class instance by name and/or call methods dynamically without "if". Pay attention that code can be different if your "service" is spring bean.
interface Service {
void method();
}
class Service1 implements Service {
#Override
public void method() {
// do magic
}
}
class Service2 implements Service {
#Override
public void method() {
// do magic
}
}
class Test {
public void executeMe(String className, String methodName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// you may need to have full class name with package
Service service = (Service) Class.forName(className).newInstance();
if ("method".equalsIgnoreCase(methodName)) {
service.method();
}
}
}
Update
class Service1 {
public void method1() {
// do magic
}
public void method2() {
// do magic
}
}
class Service2 {
public void method3() {
// do magic
}
}
class Test {
public void executeMe(String className, String methodName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// you may need to have full class name with package
Service1 service = (Service1) Class.forName(className).newInstance();
if ("method1".equalsIgnoreCase(methodName)) {
service.method1();
}
}
}
with Spring
class Test {
#Autowired
private Service1 service1;
#Autowired
private Service2 service2;
public void executeMe(String className, String methodName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Map<String, Object> map = new HashMap<>();
map.put("service1", service1);
map.put("service2", service2);
// you may need to have full class name with package
if ("service1".equalsIgnoreCase(className)) {
Service1 service1 = (Service1) map.get(className);
if ("method1".equalsIgnoreCase(methodName)) {
service1.method1();
}
}
}
}
Again, this is very very high level meta code.

Related

Injecting spring bean (service layer class) into ResourceBundle

I created a class using ResourceBundle interface as shown below. This class is dependent on another class. The implementation class for ResourceBundle (QuestionnaireSource as shown below) always has null as dependencies. No matter if I use setter or constructor injection.
Could someone please help me with this issue. I am I missing some configuration here.
#Component
public class QuestionnaireSource extends ResourceBundle {
private final QuestionnaireCache questionnaireCache;
private static final Object lockObject = new Object();
#Override
protected Object handleGetObject(String key) {
// Gets an object for the given key from this resource bundle.
// Returns null if this resource bundle does not contain an object for the given key 0
Object value = null;
try {
value = getString(key, LocaleContextHolder.getLocale());
} catch (Exception ignored) {
}
return value;
}
public Questionnaire getString(String key, Locale locale) {
Locale l = safeLocale(locale);
return getResources(l).get(key);
}
private Locale safeLocale(Locale l) {
if (l.getLanguage().equalsIgnoreCase("DE")) {
return Locale.GERMAN;
} else {
return Locale.ENGLISH;
}
}
protected Map<String, Questionnaire> getResources(Locale locale) {
synchronized (lockObject) {
return questionnaireCache.getQuestionnaireCache().get(locale.getLanguage().toUpperCase());
}
}
#Override
public Enumeration<String> getKeys() {
return null;
}
public QuestionnaireSource(QuestionnaireCache questionnaireCache) {
super();
this.questionnaireCache = questionnaireCache;
}
}
Update:
I found that even simple dependency injection in resourceBundle is failing.
UPdate2:
The way I am using in the main class is as follows:
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.QuestionnaireSource", locale);
System.out.println(bundle.getString("some.test.string"));
Update3
I am writing a simple example to convey the scenario:
Some service class
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
Some example implementation of resource bundle
#Component
public class MyResourceBundle extends ResourceBundle {
private final SomeServiceTest someServiceTest;
#Autowired
public MyResourceBundle(SomeServiceTest someServiceTest) {
this.someServiceTest = someServiceTest;
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return "test";
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
Main.java
main(){
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.MyResourceBundle", locale);
System.out.println(bundle.getString("test"));
}
Update4:
I changed the annotation on classes as mentioned by on this post https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
but still I have the null dependency injection for SomeServiceTest class. The changes are as shown below.
SomeServiceTest.java
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
MyResourceBundle.java
#Configurable
public class MyResourceBundle extends ResourceBundle {
#Autowired
private SomeServiceTest someServiceTest;
public MyResourceBundle() {
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return someServiceTest.testMethod();
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
still SomeServiceTest class is null.
Can you please post an example on how you are using this class? Is it you (your code) or spring who instanciate it (on startup)?
#Component only works for beans which Spring instanciate. If you want to inject stuff in classes you instanciate in you code you can annotate the class with #Configurable.
Please see https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects for some examples.
Make sure you have initialized the spring context
If you are using spring boot
You can get the application context after it starts and use it to get the bean you want
For example
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(YouApplication.class, args);
MyResourceBundle resConfig = run.getBean("myResourceBundle", MyResourceBundle .class);
resConfig.handleGetObject("test");
}
Unfortunately ResourceBundle.getBundle does not initialize the spring application context

how can application yaml value inject at runtime in spring boot?

I want to change the value of application.yaml at loading time.
ex) application.yaml
user.name: ${name}
Here, I want to put this value by calling an external API such as a vault, rather than a program argument when the jar is executed with the name value.
First of all, I think I need to write code that implements EnvironmentPostProcessor and calls external API, but I don't know how to inject that value. can I get help?
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// API CAll
// how can inject yaml value??
}
}
I don't know which way to orient myself.
OPTION 1: doing it via EnvironmentPostProcessor:
assuming you have registered you EnvironmentPostProcessor in /resources/META-INF/spring.factories file:
org.springframework.boot.env.EnvironmentPostProcessor=package.to.environment.config.EnvironmentConfig
all you need is to add your custom PropertySource:
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
environment.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
}
}
public class CustomPropertySource extends PropertySource<String> {
public CustomPropertySource(String name) {
super(name);
}
#Override
public Object getProperty(String name) {
if (name.equals("name")) {
return "MY CUSTOM RUNTIME VALUE";
}
return null;
}
}
OPTION 2: doing it via PropertySourcesPlaceholderConfigurer:
A class that is responsible for resolving these palceholders is a BeanPostProcessor called PropertySourcesPlaceholderConfigurer (see here).
So you could override it and provide you custom PropertySource that would resolve your needed property like so:
#Component
public class CustomConfigurer extends PropertySourcesPlaceholderConfigurer {
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, ConfigurablePropertyResolver propertyResolver) throws BeansException {
((ConfigurableEnvironment) beanFactoryToProcess.getBean("environment"))
.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
super.processProperties(beanFactoryToProcess, propertyResolver);
}
}
use ConfigurationProperties for your properties and change it via an api like this:
#Component
#ConfigurationProperties(prefix = "user")
public class AppProperties {
private String name;
//getter and setter
}
#RestController
public class AppPropertiesController {
#Autowire
AppProperties prop;
#PostMapping("/changeProp/{name}")
public void change(#PathVariable String name){
prop.setName(name);
}
}

How do I use a different Spring datasource depending on the name of the unit test?

)
I have a rather unusual requirement for a series of unit tests I'm fixing. Basically, a different datasource/transaction manager needs to be used depending on the method name of the unit test.
For example, if the test name ends with UseDB2, then we use the DB2 data source, if it's UseH2 then we use the H2 datasource.
I thought the way to go about this was to use the AbstractRoutingDatasource provided by the Spring framework.
public class TestRoutingDatasSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
Using a context holder to select the required datasource:
public class DatabaseContextHolder {
private static final ThreadLocal<DType> contextHolder = new ThreadLocal<DType>();
public static void setDatabaseType(DType databaseType) {
contextHolder.set(databaseType);
}
public static DType getDatabaseType() {
return (DType) contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
I was then going to use the name of the test to set the context; something like this:
public class MyDBUnitTestCase extends
AbstractTransactionalDataSourceSpringContextTests {
protected DataSource dataSource;
protected String schemaName;
public void setDataSource(DataSource aDataSource) {
this.dataSource = aDataSource;
}
public void setSchemaName(String aSchemaName) {
this.schemaName = aSchemaName;
}
protected void onSetUp() throws Exception {
super.onSetUp();
if (getName().endsWith("UsingDB2")) {
DatabaseContextHolder.setDatabaseType(DType.DB2);
}
else {
DatabaseContextHolder.setDatabaseType(DType.H2);
}
}
But of course, this won't work because by the time I've come to check the name of the test, Spring has already configured all the beans (doh!).
Is there some other mechanism I can use to get this to work?
Thanks very much for your time.

Retrieving the value of a property pom.xml

I would like to retrieve the value of a property in file application.properties in my service layer of my application, the value of setVersion is null
version=5.4.3
and the function for recovery the version
#Override
public ProductDto getVersionApp() {
ProductDto dto = new ProductDto();
Properties prop = new Properties();
try {
prop.load(new FileInputStream("/concerto-rest-api/src/main/resources/application.properties"));
dto.setVersion(prop.getProperty("version"));
LOG.info("version ",prop.getProperty("version"));
} catch (IOException ex) {}
return dto;
}
You can use #Value("${version}") in you service, provided you service is a spring bean.
If you are using the spring-boot framework, there are several ways you can get that property.
First:
#SpringBootApplication
public class SpringBoot01Application {
public static void main(String[] args) {
ConfigurableApplicationContext context=SpringApplication.run(SpringBoot01Application.class, args);
String str1=context.getEnvironment().getProperty("version");
System.out.println(str1);
}
}
Second:
#Component
public class Student {
#Autowired
private Environment env;
public void speak() {
System.out.println("=========>" + env.getProperty("version"));
}
}
Third:
#Component
#PropertySource("classpath:jdbc.properties")//if is application.properties,then you don't need to write #PropertyScource("application.properties")
public class Jdbc {
#Value("${jdbc.user}")
private String user;
#Value("${jdbc.password}")
private String password;
public void speack(){
System.out.println("username:"+user+"------"+"password:"+password);
}
}

Spring-Boot Access object created in main from RequestMapping

I am using spring-boot for implementing a REST server. Inside a function for request mapping, I have to create an object which is heavyweight, so for every REST call I have do it and it is slowing things down. Is it possible to create the object in main and access from the function?
#SpringBootApplication
public class MyClass{
public static void main(String[] args) {
/* I want to initialize the object here */
SpringApplication.run(MyClass.class, args);
}
}
#RestController
public class MyController {
#RequestMapping("/go/to/user/summary")
public Users[] getUsers(#RequestParam(value="length", defaultValue="10") int length) {
/* I want to use the object here */
}
You can create a bean in MyClass and then consume that bean in MyController. Spring will only create a single instance of the bean so you'll avoid the cost of creating it multiple times. Something like this:
#SpringBootApplication
public class MyClass {
public static void main(String[] args) {
SpringApplication.run(MyClass.class, args);
}
#Bean
public Heavyweight heavyweight() {
// Initialize and return heavyweight object here
}
}
#RestController
public class MyController {
private final Heavyweight heavyweight;
#Autowired
public MyController(Heavyweight heavyweight) {
this.heavyweight = heavyweight;
}
#RequestMapping("/go/to/user/summary")
public Users[] getUsers(#RequestParam(value="length", defaultValue="10") int length) {
// Use heavyweight here
this.heavyweight.foo();
// ...
return users;
}
}
I think You can use #Service for this approach. Service class is Singleton so if You create object inside it on startup application then You can use it requests in Your controllers class.
Example service:
#Service
public class MyService{
private MyLargeObject largeObject;
public MyLargeObject set(MyLargeObject largeObject){
this.largeObject = largeObject;
}
public MyLargeObject get(){
return largeObject;
}
}
Example controller:
#RestController
public class MyController{
#Inject
private MyService myService;
#RequestMapping("/go/to/user/summary")
public Users[] getUsers(#RequestParam(value="length", defaultValue="10") int length) {
MyLargeObject o = myService.get();
}
}
EDIT1:
If You want init Your largeObject directly in service You can use #PostConstruct annotation. For example:
#PostConstruct
public void init() {
// initialization Your object here
}

Resources