Retrieving the value of a property pom.xml - spring

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);
}
}

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 to use Spring boot AutoWired and ScheduledExecutorService?

I need to use autowired in more than one class with ScheduledExecutorService, what I have tried is shown in this code. logging size of User list in below example always shows 0, even after user added to arraylist. How to properly use Autowired and ScheduledExecutorService in spring boot?
#Component
public class AnotherClass {
List<User> users = new ArrayList();
public void addUser(User user){
users.add(user);
}
public void logUsers(){
logger.info("User size " + users.size()); <================= Always logs 0, when called from executor
}
}
#RestController
public class SecondClass {
#Autowired
private AnotherClass anotherClass;
#GetMapping(value="/user/test")
public void logUsers(){
anotherClass.addUser(new User());
}
}
Application Class
#Component
#SpringBootApplication
public class SpringBootDemoApplication {
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
#Autowired
private AnotherClass anotherClass;
#PostConstruct
public void init() {
logger();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
public void logger(){
exec.scheduleAtFixedRate(new Runnable(){
#Override
public void run(){
try {
anotherClass.logUsers();
}catch (Exception e){
}
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
The code works if you use the Spring #Autowired and not the #AutoWired Annotation.

Couldn't find PersistentEntity for type class when using #EnableMongoAuditing

I am getting "Couldn't find PersistentEntity for type class" error when I am using #EnableMongoAuditing features along with MongoRepository.
This happens when I save a document when collection isn't already present in database.
I tried whatever is mentioned in:
https://github.com/spring-projects/spring-boot/issues/12023
https://jira.spring.io/browse/DATAMONGO-1999
Spring boot mongodb auditing error
but nothing is working.
Mentioned things are:
Extend MongoConfig by AbstractMongoConfiguration and override all methods.
Here is my code which reproduced the same error:
MongoConfig class
#Configuration
public class MongoConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Override
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Override
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
#Override
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
#Override
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
#Override
protected String getDatabaseName() {
return mongoDB;
}
}
Person Collection class
#Document
public class Person {
#Id
private String id;
private String name;
#CreatedDate
private LocalDateTime createdAt;
#LastModifiedDate
private LocalDateTime lastModified;
// Getter Setters Constructors omitted for brevity
}
Main Application class
#EnableMongoAuditing
#EnableMongoRepositories ({"com.example.*", "org.apache.*"})
#SpringBootApplication
#ComponentScan({"com.example.*", "org.apache.*"})
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Expected Result is Person entity should be saved in database.
Actual Result is "Couldn't find PersistentEntity for type class Person" error
Looks like you ran into https://github.com/spring-projects/spring-boot/issues/12023
Extending AbstractMongoConfiguration will switch off Spring Boot's auto-configuration of various Mongo components and also customises the base packages that are used to scan for mappings. I would recommend that you don't use it in Spring Boot.
Update
I managed to get the example running with the configuration as simple as
#Configuration
public class MongoConfig {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Bean
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
}
and the app class
#EnableMongoAuditing
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Thread.sleep(2000);
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Notice that I followed my own advice and did't inherit from AbstractMongoConfiguration
Explanation
The problem lies in the initialization of
#Bean
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
You simply call MongoMappingContext constructor, without calling setInitialEntitySet. Compare that with MongoDataConfiguration auto-configuration class.
#Bean
#ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
Even worse, you don't register MongoMappingContext as a managed bean.
Due to this fact, auto-configuration class is still created. This leads to a race condition, I tried to run the original code and could easily reproduce the error, but with a breakpoint in AbstractMappingContext.addPersistentEntity the test always passed.
For me I resolved this issue by adding following method in MongoConfig if your class extends from AbstractMongoConfiguration
#Override
protected String getMappingBasePackage() {
return "com.companyName.modulename"
}
If MongoConfig extends from MongoConfigurationSupport then add below method
#Override
protected Collection<String> getMappingBasePackages() {
return Arrays.asList("com.companyName.module1","com.companyName.module2");
}
Note that in later case I can specify multiple package names as base packages.

Bean not getting overridden in Spring boot

I am trying to write and test an application that used spring-cloud with azure functions following this tutorial.
https://github.com/markusgulden/aws-tutorials/tree/master/spring-cloud-function/spring-cloud-function-azure/src/main/java/de/margul/awstutorials/springcloudfunction/azure
I am tryign to write a testcase and override the bean.
Here is the application class having function and handler Bean function.
#SpringBootApplication
#ComponentScan(basePackages = { "com.package" })
public class DataFunctions extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, Data> {
#FunctionName("addData")
public HttpResponseMessage addDataRun(
#HttpTrigger(name = "add", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws JsonParseException, JsonMappingException, IOException {
context.getLogger().info("Java HTTP trigger processed a POST request.");
try {
handleRequest(new GenericMessage<Optional<String>>(request.getBody()), context);
} catch (ServiceException ex) {
ErrorMessage em = new ErrorMessage();
return request.createResponseBuilder(handleException(ex, em)).body(em).build();
}
return request.createResponseBuilder(HttpStatus.CREATED).build();
}
#Autowired
MyService mService;
#Bean
public Consumer<GenericMessage<Optional<String>>> addData() {
ObjectMapper mapper = new ObjectMapper();
return req -> {
SomeModel fp = null;
try {
fp = mapper.readValue(req.getPayload().get(), SomeModel.class);
} catch (Exception e) {
throw new ServiceException(e);
}
mService.addData(fp);
};
}
}
I want to test by overriding the above bean.
Cosmosdb spring configuration
#Configuration
#EnableDocumentDbRepositories
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
#Value("${cosmosdb.collection.endpoint}")
private String uri;
#Value("${cosmosdb.collection.key}")
private String key;
#Value("${cosmosdb.collection.dbname}")
private String dbName;
#Value("${cosmosdb.connect.directly}")
private Boolean connectDirectly;
#Override
public DocumentDBConfig getConfig() {
ConnectionPolicy cp = ConnectionPolicy.GetDefault();
if (connectDirectly) {
cp.setConnectionMode(ConnectionMode.DirectHttps);
} else {
cp.setConnectionMode(ConnectionMode.Gateway);
}
return DocumentDBConfig.builder(uri, key, dbName).connectionPolicy(cp).build();
}
}
Here is the configuration
#TestConfiguration
#PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
#Profile("test")
#Import({DataFunctions.class})
public class TestConfig {
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
#Bean
#Primary
public DocumentDBConfig getConfig() {
return Mockito.mock(DocumentDBConfig.class);
}
}
Finally the test class
#RunWith(SpringRunner.class)
//#SpringBootTest //Enabling this gives initialization error.
#ActiveProfiles("test")
public class TempTest {
#InjectMocks
DataFunctions func;
#Mock
MyService mService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
private Optional<String> createRequestString(final String res) throws IOException {
InputStream iStream = TempTest.class.getResourceAsStream(res);
String charset="UTF-8";
try (BufferedReader br = new BufferedReader(new InputStreamReader(iStream, charset))) {
return Optional.of(br.lines().collect(Collectors.joining(System.lineSeparator())));
}
}
#Test
public void testHttpPostTriggerJava() throws Exception {
#SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
final Optional<String> queryBody = createRequestString("/test-data.json");
doNothing().when(mService).addData(Mockito.any(SomeModel.class));
doReturn(queryBody).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
#Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
doReturn("addData").when(context).getFunctionName();
// Invoke
final HttpResponseMessage ret = func.addDataRun(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.CREATED);
}
}
For this case instead of test configuration addData the actual bean is called from DataFunctions class. Also the database connection is also created when it should use the mocked bean from my test configuration. Can somebody please point out what is wrong in my test configuration?
I was able to resolve the first part of cosmos db config loading by marking it with
#Configuration
#EnableDocumentDbRepositories
#Profile("!test")
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
...
}
Also had to mark the repository bean as optional in the service.
public class MyService {
#Autowired(required = false)
private MyRepository myRepo;
}
Didn't use any spring boot configuration other than this.
#ActiveProfiles("test")
public class FunctionTest {
...
}
For the second part of providing mock version of Mock handlers, I simply made the test config file as spring application as below.
#SpringBootApplication
#ComponentScan(basePackages = { "com.boeing.da.helix.utm.traffic" })
#Profile("test")
public class TestConfiguration {
public static void main(final String[] args) {
SpringApplication.run(TestConfiguration.class, args);
}
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
}
and made use of this constructor from azure functions library in spring cloud in my constructor
public class AppFunctions
extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, List<Data>> {
public AppFunctions(Class<?> configurationClass) {
super(configurationClass);
}
}
public AzureSpringBootRequestHandler(Class<?> configurationClass) {
super(configurationClass);
}
Hope it helps someone.

Field created in spring component in not initialized with new keyword

I have spring component class annotated with #Component and in it I have field ConcurrentHashMap map, which is init in constructor of component and used in spring stream listener:
#Component
public class FooService {
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
fooMap = new ConcurrentHashMap<>();
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body);
}
}
Listener handle messages sent by rest controller. Can you tell me why I always got there fooMap.put(...) NullPointerException because fooMap is null and not initialzied.
EDIT:
After #OlegZhurakousky answer I find out problem is with async method. When I add #Async on some method and add #EnableAsync I can't anymore use private modificator for my #StreamListener method. Do you have idea why and how to fix it?
https://github.com/schwantner92/spring-cloud-stream-issue
Thanks.
Could you try using #PostConstruct instead of constructor?
#PostConstruct
public void init(){
this.fooMap = new ConcurrentHashMap<>();
}
#Denis Stephanov
When I say bare minimum, here is what I mean. So try this as a start, you'll see that the map is not null and start evolving your app from there.
#SpringBootApplication
#EnableBinding(Processor.class)
public class DemoApplication {
private final Map<String, String> map;
public static void main(String[] args) {
SpringApplication.run(DemoRabbit174Application.class, args);
}
public DemoApplication() {
this.map = new HashMap<>();
}
#StreamListener(Processor.INPUT)
public void sink(String string) {
System.out.println(string);
}
}
With Spring everything has to be injected.
You need to declare a #Bean for the ConcurrentHashMap, that will be injected in you Component. So create a Configuration class like:
#Configuration
public class FooMapConfiguration {
#Bean("myFooMap")
public ConcurrentHashMap<Long, String> myFooMap() {
return new ConcurrentHashMap<>();
}
}
Then modify your Component:
#Component
public class FooService {
#Autowired
#Qualifier("myFooMap")
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body); // <= No more NPE here
}
}

Resources