Java Spring Boot Configurations & Components - java

I'm trying to use a component to load up my configuration yml file.
However, it throws a Null Pointer exception and System.out shows 'null' for application.
However, when the same pattern is used to code up a #RestController, everything works fine. Why is #component not seeing my configurations??
#Component
#ConfigurationProperties(prefix = "myconf")
public class AppConfigJSON {
private String application;
private final String applicationConfigJSON;
Logger logger = LoggerFactory.getLogger(this.getClass());
private final ReadContext acRcJson;
public AppConfigJSON(){
String json = "";
try {
System.out.println("Application: " + this.application);
json = IOUtils.toString(this.getClass().getResourceAsStream("/myconfs/"+this.application+".json"));
} catch (IOException e) {
logger.error("Error reading JSON or app YML {}", e.getStackTrace());
}
this.applicationConfigJSON = json;
this.acRcJson = JsonPath.parse(this.getApplicationConfigJSON());
}
// These functions below set by configuration
public String getApplication()
{
return application;
}
public void setApplication(String application)
{
this.application = application;
}
}

In your constructor the application variable hasn't been initialized yet, in other words, Spring needs an instance first so it can apply it's magic. You need to move your constructor logic to a method annotated with #PostContruct so the application variable is set with the property value at that point.

Related

Bean failiing to initialize in a not deterministic way

I have a Spring Boot application that has the following bean:
#Configuration
public class ConfigStatus {
#Value("${olt.id}")
private int olt_id;
#Bean
Status createStatus() {
return new Status(olt_id);
}
}
I launch several containers with this application (20 to be exact) and in some of them the bean fails to be initialized and I don't understand why. The bean is successfuly initialized consistently in 18 of 20 containers. Memory is not an issued as the system I am using has 125gb of ram and at the time of this issue is has 90gb of free memory.
This is the class that makes use of the bean:
#DependsOn("createStatus")
#Service
public class ReceiveMessageHandler {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private static final Gson converter = new Gson();
#Autowired private Status currentStatus;
public void handleMessage(String body) {
OltRequest request = converter.fromJson(body, OltRequest.class);
request.setStartedBeingProcessedAtOlt(new Date().getTime());
Response r = new Response(request.getId(), 200, new Date().getTime());
r.setRequestEnqueuedAtOlt(currentStatus.getEnqueuedAtWorkerTimes().get(request.getId())); // line that fails due to the bean being equal to null
r.setRequestDequeuedAtOlt(new Date().getTime());
log.info("Processing request: " + request.getId());
try {
Thread.sleep(request.getDuration());
} catch(InterruptedException e) {
e.printStackTrace();
}
request.setEndedBeingProcessedAtOlt(new Date().getTime());
log.info("Finished processing request: " + request.getId());
r.setEndedHandling(new Date().getTime());
Worker.send_response(r, request.getOriginMessage().getWorker());
}
}
The error messages sais that when the bean was accessed it was null.
In the environment I have another Spring Boot app with 30 running docker containers in which this problem does not occurr, ence my disbelief :D.
I believe you should do:
#Configuration
public class ConfigStatus {
#Bean
Status currentStatus(#Value("${olt.id}") int olt_id) {
return new Status(olt_id);
}
}
I'm not 100% confident if Spring prioritises #Value over #Bean in its instantiation order, so doing this guarantees the ordering. It may be that some of your instances are trying to load the bean before the value is there, so bails out on some NPE.
Also, you should be able to drop the line: #DependsOn("createStatus") in ReceiveMessageHandler.java. Spring runs #Configuration classes before #Service classes, and #Autowired matches on type.

Spring Boot - Assign value to Class Member from YML file - Null Pointer Exception

I am using below annotations in my config class to get the values from properties file(yml).
Configuration
EnableConfigurationProperties
ConfigurationProperties (prefix = "notification")
I am able to get the values inside public methods without problem using the class . But I am getting 'Error Creating bean' Error when I try to assign value instance variable of the class using config class.
Below is my code. Can someone please throw some light.
This is my config class
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties (prefix = "notification")
public class NotifyYaml {
private String subscriptionId;
public String getSubscriptionId() {
return subscriptionId;
}
public void setSubscriptionId(String subscriptionId) {
this.subscriptionId = subscriptionId;
}
Below is the class where I am getting error during startup.
#Component
public class PubSubController {
#Autowired
private NotifyYaml notify;
public PubSubController() {
// TODO Auto-generated constructor stub
}
String projectId = "ccc-g-pre-proj-cacdate";
//Error in this line
String subscriptionId = notify.getSubscriptionId();
The #Autowired object only gets filled in after the object is created.
This means that while the object is being created, it tries to call a method from a null object.
I would suggest using something like a #PostConstruct method. (Note: you will need to include javax.annotations into your project somehow.)
String subscriptions; // remove the value for now...
#PostConstruct
private void init() {
subscriptions = notify.getSubscriptionId(); // ...and add it back in here.
}

Microprofile #Fallback not working in native image mode

Having the following code running on Quarkus:
#Singleton
#RegisterForReflection
public class StoreService {
private static final Logger LOGGER = Logger.getLogger(StoreService.class);
#Inject
#RestClient
StoresApiClient client;
#CacheResult(cacheName = "stores")
#Fallback(fallbackMethod = "allFallbackStores")
public List<Store> allStores() {
// call REST API using client
}
#SuppressWarnings("unused")
public List<Store> allFallbackStores() {
try {
LOGGER.info("Falling back to internal stores list");
...
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
the fallback mechanism is working properly in regular JDK mode. On the other hand in native image mode, #Fallback annotation is not being respected and an exception is thrown after unsuccessful API call. What might be a reason for that if #RegisterForReflection annotation is in place?

Spring boot cron expression scheduling, calling onApplicationEvent of ApplicationListener

I have written a StartupListener by implementing ApplicationListener and overriding the method: onApplicationEvent(ContextRefreshedEvent event).
#Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger logger = LoggerFactory.getLogger(StartupListener.class);
#Value("${create.file.some.merchant}")
private boolean createMerchantAFile;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
logger.info("Application context started");
if (createMerchantAFile) {
EmbeddedResponse emb = null;
file = ReportConstants.MERCHANT_A+ ReportConstants.FILE_FORMAT;
try {
emb = genericServices.readJsonFile("merA.json");
generateReport.generateExcelFile(emb, file, "MerchantA");
} catch (IOException ioe) {
logger.error("IO Exception while reading JSON file. Message: ", ioe);
} catch (Exception e) {
logger.error("Exception while reading JSON file. Message", e);
}
createMerchantAFile= false;
}
}
}
Inside this method, I am trying to create some files based on whether a boolean value corresponding to file is true or not.
This boolean value is being read from "application.properties" file by using #Value annotation.
This StartupListener works fine.
Now I wish to generate these files by scheduling them, so I added #EnableScheduling to my main class file, and created a new java file with a method:
#Component
public class FileGenerationScheduler {
#Autowired
StartupListener startupListener;
#Scheduled(cron = "${file.gen.cron}")
public void generateFileWithCron() {
startupListener.onApplicationEvent(null); //passing null here
}
}
This method gets called on the specified cron expression but all the #Value boolean values are not read from "application.properties". So by default these values will be false (instance variables)
#Value("${create.file.some.merchant}")
private boolean createMerchantAFile;
This is present in StartupListener and is now false. So nothing is created.
How can I make sure that these values are read from application.prop even when called via scheduler ?
Create a service and add all the logic in it including the fetching of the value from the property find . Inject that bean inside the listener and the scheduler class.

AWS Lambda + Spring, how to load application.yml

I have problem with customizing API gateway domain, for my restful app deployed on AWS lambda. Customized domain, works this way, that depending on basePath it chooses different APIs which finally touches Lambda. For example:
api.mycustomdomain.com/view/ping -> goes to application view with path /view/ping
api.mycustomdomain.com/admin/ping -> goes to application admin with path /admin/ping
I am using this example as boilerplate: https://github.com/awslabs/aws-serverless-java-container/tree/master/samples/spring/pet-store
What I would like to achieve is handler which depending on Host header strips prefix from request path.
I have prepared following application.yml file:
server:
contextPath: "/view"
productionHost: "api.mycustomdomain.com"
The problem/question is. How can I now load those into my Lambda function? Here is my naive try:
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
boolean isinitialized = false;
#Value("${server.contextPath}")
private String prefix;
#Value("${server.productionHost}")
private String productionHost;
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
if(awsProxyRequest.getHeaders().get("Host").equals(productionHost))
awsProxyRequest.setPath(awsProxyRequest.getPath().substring(prefix.length()));
if (!isinitialized) {
isinitialized = true;
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PingPongApp.class);
} catch (ContainerInitializationException e) {
e.printStackTrace();
return null;
}
}
return handler.proxy(awsProxyRequest, context);
}
}
Obviously this doesn't work, LambdaHandler is working out of Spring context.
Any ideas how can I deal with that?
It seems you can not load those properties. Follow either of the 2 options given below.
1> You can add following bean in your configuration and that way you can autowire strings and use the way you are already using
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
2>
public AwsProxyResponse..{
#Autowired
private Environment env;
..
public AwsProxyResponse handleRequest{
..
String contextPath = env.getRequiredProperty(“server.contextPath”));
...
}
}

Categories