Why are we Injecting on constructor/method - spring

I am new to Spring. Please help me in understanding the below logic.
I have gone through Google and Stack Overflow search, but nowhere I was able to find a clear and straightforward explanation.
Exporter.java
public class Exporter {
#NonNull
public final Subscription subscription;
#Inject
public Exporter(#Named(DOWNLOAD) final boolean download,
final Subscription subscription){
this.subscription = subscription;
}
}
Subscription.java
#Getter
#singleton
#AllArgsConstructor(onConstructor = #__(Inject))
public class Subscription {
//some logic
}
Why are we using #Inject here for a constructor(Exporter)? what exactly it does?
When does it get initialized?
Why are we required to use #AllArgsConstructor for Subscription class?

Related

mongock #BeforeExecution is not executed

I need to initialize admin from env variables, but #BeforeExecution method is not launched, execution directly starts in initAdmin method. Any suggestions?
#ChangeUnit(id = "init", order = "001", author = "me")
#RequiredArgsConstructor
#Component
public class InitChangeLog {
private final MyService service;
private final MongoTemplate template;
private final ConfigurableEnvironment env;
private String admin;
#BeforeExecution
private void setAdmin() {
this.admin = env.getProperty("admin");
}
#Execution
public void initAdmin() {
service.create(...);
}
}
I can see multiple errors there that can be the reason of your issue:
#Component shouldn't be used for a changeUnit. The way Mongock retrieves the changeUnits is by specifying the packages or directly the class. You can see it in the documentation
Mongock offers two ways to inject your beans, in the constructor and directly in the method. Please see this section in the documentation for more information
#BeforeExecution shouldn't be private.
Try correcting those points and I am confident it will work just fine :)

I can do PUT but not POST with Spring Data Rest?

I have two simple entity like this:
public class Agent extends BasedEntity {
private String firstname;
private String lastname;
#ManyToOne
#JoinColumn(name="agency_id", nullable=true)
Agency agency;
}
and
public class Agency extends BasedEntity {
private String name;
private String address;
#OneToMany(mappedBy="agency")
private Set<Agent> agents;
}
#RepositoryRestResource
public interface AgencyRespository extends JpaRepository<Agency, Long> {
}
#RepositoryRestResource
public interface AgentsRespository extends JpaRepository<Agent, Long> {
}
When I do a PUT with
https://localhost:8080/api/v1/agents/64/agency
body:https://localhost:8080/api/v1/agencies/50
it goes through but if I do a POST to
https://localhost:8080/api/v1/agents/64/agency
body:https://localhost:8080/api/v1/agencies/50
I get a
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
You are using an old version of Spring Data Rest. POST is allowed from 2.3.x.
The latest version is 3.2.x. You should definetely ugrade to a newer version...
----------- Edit
I've just realized that the exception is NOT the inner HttpRequestMethodNotSupportedException from the RepositoryPropertyReferenceController class, but the 'default' org.springframework.web.HttpRequestMethodNotSupportedException.
This exception is never raised directly from the SRD package.
Maybe you have a filter which deny POST request or some kind of security settings.

How to load a compacted topic in memory before starting the context

I'm using a compacted topic in kafka which I load into a HashMap at the application startup.
Then I'm listening to a normal topic for messages, and processing them using the HashMap constructed from the compacted topic.
How can I make sure the compacted topic is fully read and the HashMap fully initialized before starting to listen to the other topics ?
(Same for RestControllers)
Implement SmartLifecycle and load the map in start(). Make sure the phase is earlier than any other object that needs the map.
This is an old question, I know, but I wanted to provide a more complete code sample of a solution that I ended up with when I struggled with this very problem myself.
The idea is that, like Gary has mentioned in the comments of his own answer, a listener isn't the correct thing to use during initialization - that comes afterwards. An alternative to Garry's SmartLifecycle idea, however, is InitializingBean, which I find less complicated to implement, since it's only one method: afterPropertiesSet():
#Slf4j
#Configuration
#RequiredArgsConstructor
public class MyCacheInitializer implements InitializingBean {
private final ApplicationProperties applicationProperties; // A custom ConfigurationProperties-class
private final KafkaProperties kafkaProperties;
private final ConsumerFactory<String, Bytes> consumerFactory;
private final MyKafkaMessageProcessor messageProcessor;
#Override
public void afterPropertiesSet() {
String topicName = applicationProperties.getKafka().getConsumer().get("my-consumer").getTopic();
Duration pollTimeout = kafkaProperties.getListener().getPollTimeout();
try (Consumer<String, Bytes> consumer = consumerFactory.createConsumer()) {
consumer.subscribe(List.of(topicName));
log.info("Starting to cache the contents of {}", topicName);
ConsumerRecords<String, Bytes> records;
do {
records = consumer.poll(pollTimeout);
records.forEach(messageProcessor::process);
} while (!records.isEmpty());
}
log.info("Completed caching {}", topicName);
}
}
For brevity's sake I'm using Lombok's #Slf4j and #RequiredArgsConstructor annotations, but those can be easily replaced. The ApplicationProperties class is just my way of getting the topic name I'm interested in. It can be replaced with something else, but my implementation uses Lombok's #Data annotation, and looks something like this:
#Data
#Configuration
#ConfigurationProperties(prefix = "app")
public class ApplicationProperties {
private Kafka kafka = new Kafka();
#Data
public static class Kafka {
private Map<String, KafkaConsumer> consumer = new HashMap<>();
}
#Data
public static class KafkaConsumer {
private String topic;
}
}

#Cacheable() returning id null

I have a method findAll() that returns all the speciality from BD, in this method i put the annotation #Cacheable to get the data from the cache, the problem is when i execute the /specialities api, the first time i get the correct data, when i execute the api the second time i get the data with null ids
#Service
#CacheConfig(cacheNames = ServiceConstant.SPECIALITY)
public class SpecialityServiceImpl implements SpecialityService {
#Autowired
private SpecialitySearchRepository specialitySearchRepository;
#Autowired
private SpecialtyMapper specialityMapper;
#Override
#Cacheable
public List<SpecialityDTO> findAll() {
return specialitySearchRepository.findAll().stream().map(specialityMapper::toDto)
.collect(Collectors.toCollection(LinkedList::new));
}
}
#RestController
public class SpecialityResource {
#Autowired
private SpecialityService specialityService;
#GetMapping("/specialities")
public List<SpecialityDTO> getAllSpecialitys() {
return specialityService.findAll();
}
}
Config
#Configuration
#EnableCaching
public class CacheConfiguration {
private static final String HAZELCAST_LOGGING_TYPE = "hazelcast.logging.type";
#Bean
public Config hazelCastConfig() {
return new Config().setInstanceName("cache")
.addMapConfig(
new MapConfig().setName(ServiceConstant.SPECIALITY)
.setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU).setTimeToLiveSeconds(100))
.setProperty(HAZELCAST_LOGGING_TYPE, "none");
}
}
#Aymen Kanzari, please see: https://gist.github.com/gokhanoner/766a1a807744d1a69c6a7799c3f34d73
I tried to replicate the issue but it seems working as expected. I can see inside findAll method for the first call but next ones just hit the Hazelcast cache. Can you describe the issue a bit more?

Is it possible to have a constant valued through a Spring Service?

We have a web service that one of its parameters is called origin and this origin is always validated against a code in the database.
For each one of our services I have to validate this code. This code does not change so I want to keep it in a constant, but I still have to validate it to prevent clients from sending a wrong code.
Basically what I want is this:
#Service
public class Service {
#Autowired
private LogBS logBS;
// I know this cannot be used in a static context.
public static final Long CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
I know something similar can be done with Spring caching, but is it possible to do it with a constant?
I would rather go with this:
#Service
public class CodeValidatorService {
private LogBS logBS;
private Long CODE;
#Autowired
public CodeValidatorService(LogBS logBS){
this.logBS = logBS;
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
if (CODE == null){
throw new ServiceException("Code cannot be read from DB!");
}
}
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
Just as a code review, I prefer injecting dependencies in the constructor rather than using #Autowired in the field directly, it makes the service testable. You could also try to read the code in a #PostConstruct method, but I think it's better to do it in the constructor so you always have the service in a ready-to-go state.
For using it in the rest of your services, inject the CodeValidatorService instance on them:
#Service
public class OtherService {
private CodeValidatorService codeValidatorService;
#Autowired
public OtherService(CodeValidatorService codeValidatorService){
this.codeValidatorService = codeValidatorService;
}
public void performAction(final Long origin) {
codeValidatorService.validateOriginCode(origin);
//do the rest of your logic here
}
}
See also:
Spring Beans and dependency injection
Setter injection versus constructor injection
You can have a constantsProvider class
#Component
public class ConstantsProvider {
#Autowired
private LogBS logBS;
private String CODE;
#PostConstruct
public void init() {
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
}
public String getCode() {
return CODE;
}
}
Add this snippet of code to Service class
#Autowired
private ConstantsProvider constantsProvider;
You can use constantsProvider.getCode() in your services. This way CODE is going to be immutable and not defined in a static context.
Note: If you have more constants similar to this, there is a better way to define the ConstantsProvider class. If there is only one, I would stick to the above implementation.
Edit 1:
If you need it in all the service classes, make the constantsProvider a spring bean and initialize the CODE there itself. Updated the answer

Resources