Spring Bean - Constructor with arguments - java

I see an error in the constructor which says
"String queueName" cannot be autowired.
I have AmazonSQSAsync component defined in another class but not
queueName. Why is the constructor trying to autowire the parameters
and how can I resolve this?
#Configuration
public class SqsQueueHealthIndicator extends AbstractHealthIndicator {
private final AmazonSQSAsync amazonSQSAsync;
private final String queueName;
public SqsQueueHealthIndicator(AmazonSQSAsync amazonSQSAsync, String queueName) {
this.amazonSQSAsync = amazonSQSAsync;
this.queueName = queueName;
}
#Override
protected void doHealthCheck(Health.Builder builder) {
try {
amazonSQSAsync.getQueueUrl(queueName);
builder.up();
} catch (QueueDoesNotExistException e) {
builder.down(e);
}
}
#Bean
SqsQueueHealthIndicator queueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${url}") String queueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, queueName);
}
#Bean
SqsQueueHealthIndicator deadLetterQueueHealthIndicator(#Autowired AmazonSQSAsync amazonSQSAsync, #Value("${dlqurl}") String deadLetterQueueName) {
return new SqsQueueHealthIndicator(amazonSQSAsync, deadLetterQueueName);
}
}

Because you're declaring it as final so its value must be initialized.
You did not provide default value so Spring understand its value will be injected.
So you have 2 options:
Remove the final modifier. Use #Value to inject your value from config file.
Create a bean type of String and inject it. You should name it:
#Bean("queueName")
public String getQueueName {return "xyz";}
And inject like:
#Autowire
#Qualifier("queueName")
private final String queueName;
In normal circumstance, the option 1 is the go-to.

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
}

How to Autowire conditionally in spring boot?

I have created one scheduler class
public class TestSchedulderNew {
#Scheduled(fixedDelay = 3000)
public void fixedRateJob1() {
System.out.println("Job 1 running");
}
#Scheduled(fixedDelay = 3000)
public void fixedRateJob2() {
System.out.println("Job 2 running");
}
}
In configuration i have put #ConditionalOnProperty annotation to enable this on conditional purpose.
#Bean
#ConditionalOnProperty(value = "jobs.enabled")
public TestSchedulderNew testSchedulderNew() {
return new TestSchedulderNew();
}
Now in controller, i have created "stopScheduler" method to stop those scheduler , in this controller i have autowired
TestSchedulderNew class
#RestController
#RequestMapping("/api")
public class TestCont {
private static final String SCHEDULED_TASKS = "testSchedulderNew";
#Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor; /]
#Autowired
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule(){
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
}
Now the problem is if conditional property is false then i get below exception
Field testSchedulderNew in com.sbill.app.web.rest.TestCont required a bean of type 'com.sbill.app.schedulerJob.TestSchedulderNew
In case of true everything works fine,
Do we have any option to solve this ?
You can use #Autowired(required=false) and null check in stopScheduler method.
#Autowired(required=false)
private TestSchedulderNew testSchedulderNew;
#GetMapping(value = "/stopScheduler")
public String stopSchedule() {
if (testSchedulderNew != null) {
postProcessor.postProcessBeforeDestruction(testSchedulderNew,
SCHEDULED_TASKS);
return "OK";
}
return "NOT_OK";
}

Is it the right way to implements spring InitializingBean like this?

I want to assign value for host and port field in class IdGenerator,which is the best way to achieve this?
Note:IdGenerator is best not be managed by spring,
the client class can call genId() as a static method.
#Component
public class IdGenerator implements InitializingBean{
private static final Logger LOGGER = LoggerFactory.getLogger(IdGenerator.class);
#Value("${vesta.host}")
private String host;
#Value("${vesta.port}")
private Integer port;
static VestaHttpClient client;
#Override
public void afterPropertiesSet() {
Assert.hasText(host);
Assert.notNull(port);
LOGGER.info("about to initial IdGenerator.");
try {
client = new VestaHttpClient(host, port);
}catch(Exception e){
LOGGER.info("IdGenerator initialize failed .");
throw new RuntimeException("----------VestaHttpClient initialize failed--------");
}
LOGGER.info("IdGenerator was successfully initialized.");
}
public static String genId(){
return client.genId()+"";
}
}
You could simply build VestaHttpClient as a bean in a #Configuration class, using a PropertyPlaceholderConfigurer to grab the property values and inject them into fields annotated with #Value. Here is some code I mocked to describe my recommendation (it may require some adjustments):
#Configuration
#PropertySource("classpath:example.properties")
public class MyConfiguration {
#Value("${vesta.host}")
private String host;
#Value("${vesta.port}")
private Integer port;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public VestaHttpClient get httpClient(){
VestaHttpClient httpClient = null;
try {
client = new VestaHttpClient(host, port);
}catch(Exception e){
/*Omitted*/
}
return httpClient;
}
}
If you want to inject the property values using field injection, so called “clean” configuration bean, then InitializingBean is the right way to validate your properties.
However another way is to use just constructor injection and get rid of InitializingBean
#Component
public class IdGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(IdGenerator.class);
private String host;
private Integer port;
static VestaHttpClient client;
#Autowired
public WebProperties(#Value("${vesta.host}") String protocol,
#Value("${vesta.port}") Integer port) {
// Validate properties and initialize VestaHttpClient
}
}

#Autowire field has Null Pointer Exception ['new' keyword not used]

I had already tried solutions mentioned in Why is my Spring #Autowired field null? yet the problem persists. I have tried annotating the class DevicePojo(code below) with #Configurable #Service.
Here are my beans
DistributionConfig.java
#Component
#Configuration
public class DistributionConfig {
#Qualifier("exponentialDistribution")
#Bean
#Scope("prototype")
public DistributionService exponentialDistribution() {
return new ExponentiallyDistribute();
}
#Qualifier("normalDistribution")
#Bean
#Scope("prototype")
public DistributionService normalDistribution() {
return new NormallyDistribute();
}
#Qualifier("uniformDistribution")
#Bean
#Scope("prototype")
public DistributionService uniformDistribution() {
return new UniformlyDistribute();
}
}
JsonFileConfig.java
#Configuration
public class JsonFileConfig {
private static ObjectMapper mapper=new ObjectMapper();
#Qualifier("devicesPojo")
#Bean
public DevicesPojo[] devicesPojo() throws Exception {
DevicesPojo[] devicePojo=mapper.readValue(new File(getClass().getClassLoader().getResource("Topo/esnet-devices.json").getFile()),DevicesPojo[].class);
return devicePojo;
}
#Qualifier("linksPojo")
#Bean
public LinksPojo[] linksPojo() throws Exception {
LinksPojo[] linksPojo=mapper.readValue(new File(getClass().getClassLoader().getResource("Topo/esnet-adjcies.json").getFile()),LinksPojo[].class);
return linksPojo;
}
}
Here is my DevicePojo where i get the null pointer exception.
#JsonDeserialize(using = DeviceDeserializer.class)
#Component
public class DevicesPojo {
private String device;
private List<String> ports;
private List<Integer> bandwidth;
#Autowired
#Qualifier("uniformDistribution")
private DistributionService uniformDistribution; // Here uniformDistribution is null
public DevicesPojo(String device, List<String> port, List<Integer> bandwidth) {
this.device = device;
this.ports= port;
this.bandwidth=bandwidth;
this.uniformDistribution.createUniformDistribution(1000,0,ports.size());
}
public String getDevice(){
return device;
}
public String getRandomPortForDevice()
{
return ports.get((int)uniformDistribution.getSample());
}
public List<String> getAllPorts(){
return ports;
}
public int getBandwidthForPort(String port){
return bandwidth.get(ports.indexOf(port));
}
}
However, if i replace private DistributionService uniformDistribution;with private DistributionService uniformDistribution=new UniformDistribution() the code is working fine.
There is a mix of problems here.
1. You create your DevicesPojo objects using JSON deserializer. Spring has no chance to interfere and inject the DistributionService.
2. Even if it could interfere, it would fail, since you are trying to use the 'distributionService' object in the constructor. Field injection would work only after an object is constructed.
Now regarding fixing the problems.
Long answer short - don't expect auto-injection in your POJOs.
Normally, dependencies like 'distributionService' in objects that are created on the fly, like your DevicesPojo are avoided altogether.
If you insist on having them, inject them manually at construction time:
class DevicesPojoFactory {
#Autowired #Qualifier("uniformDistribution")
private DistributionService uniformDistribution;
ObjectMapper mapper = new ObjectMapper();
DevicesPojo[] readFromFile(String path) {
DevicesPojo[] devicePojoArr = mapper.readValue(...);
for (DevicesPojo dp: devicePojoArr) {
dp.setDistribution(uniformDistribution);
}
}
}

Categories