CacheBuilder Initialization in constructor do not use Spring annotation value - spring

I'm working on a functionality which works with google reCaptcha. During tests there is something wrong with CacheBuilder: expireAfterWrite value is always set = 0 (14400 is declared in anntoation and in application-test.properities as well). As i suppose something is wrong becouse it is initialized in constructor. Is there any other way to initialize it?
public class RecaptchaService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final RestTemplate restTemplate;
#Value("${recaptcha.url:}")
private String recaptchaUrl;
#Value("${recaptcha.secret:#{null}}")
private String recaptchaSecret;
#Value("${recaptcha.fail.silent:false}")
private boolean recaptchaFailSilent;
#Value("${recaptcha.failAttempts.expire:14400}")
private int recaptchaAttemptExpire;
#Value("${recaptcha.failAttempts.max:4}")
private int maxAttempts;
private LoadingCache<String, Integer> attemptsCache;
private int attempts;
public RecaptchaService(final RestTemplate restTemplate) {
this.restTemplate = restTemplate;
attemptsCache = CacheBuilder.newBuilder()
.expireAfterWrite(recaptchaAttemptExpire, TimeUnit.SECONDS)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(final String key) {
return 0;
}
});
}
}

Sounds like RecaptchaService isn't being managed by spring. I don't see a #Component annotation. I'm surpised the #Value annotations work at all in an un-managed bean!
If you let spring manage the life-cycle of RecaptchaService then those values will be available in the constructor.

We will assume that there is an #Component, or equivalent, annotation on your service.
In fact during the construction of your object, values are not yet injected. Then in your constructor recaptchaAttemptExpire equals to 0 (even if you set a default injection value).
As it seems to be a Service, then try to intialize your attemptsCache field during a #PostConstruct method maybe?

Related

How come the instance variable inside a Runnable is gone when it's executed?

#Autowired
ThreadPoolTaskScheduler taskScheduler;
#Autowired
SendUpdatesRunnable sendUpdatesRunnable;
private void createJob(String timezone) {
ZonedDateTime zoned = LocalDate.now().atTime(10, 11).atZone(ZoneId.of(timezone));
sendUpdatesRunnable.timezone = timezone;
taskScheduler.schedule(
sendUpdatesRunnable,
Date.from(zoned.toInstant())
);
}
and
#Component
public class SendUpdatesRunnable implements Runnable{
#Autowired
ProductRepository productRepository;
String timezone;
#Override
#Transactional
public void run() {
List<Product> newProds = productRepository.findProductByCreateDateTimeIsAfter(LocalDateTime.now().minusHours(24));
List<Product> updatedProds = productRepository.findProductByUpdateDateTimeIsAfter(LocalDateTime.now().minusHours(24));
//System print out timezone variable = null
}
}
Problem
When you inject a Spring Bean with AOP stuff (i.e. #Transcational) Spring Framework doesn't inject the real concrete class, but instead a proxy object. The proxy class holds the field timezone and it doesn't get delegated to your real class instance.
How Proxies in Spring Framework work:
source
The proxy exists because your run() method would look something like this:
public void run() {
transactionManager.begin();
realInstance.run();
transactionManager.commit();
}
So when you set timezone like this sendUpdatesRunnable.timezone = timezone; it is actually doing this proxy.timezone = timezone; and your sendUpdatesRunnable object still has timezone as null.
Solution
You need to use getters/setters.
When you use a setter method, the proxy class will have an implementation of it like this:
public void setTimezone(String timezone) {
realInstance.setTimezone(timezone);
}
Thus, when you call the set method, it will propagate properly to the real instance where the concrete implementation is.
With that said, you shouldn't be using instance level mutable variables at all in Spring Bean implementations. The only instance variables you should have are dependencies that are injected at bean initialization.

Spring Data Rest: #Autowire in Custom JsonDeserializer

I am trying to autowire a component into a custom JsonDeserializer but cannot get it right even with the following suggestions I found:
Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator
Right way to write JSON deserializer in Spring or extend it
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Spring Boot Autowiring of JsonDeserializer in Integration test
My final goal is to accept URLs to resources in different microservices and store only the ID of the resource locally. But I don't want to just extract the ID from the URL but also verify that the rest of the URL is correct.
I have tried many things and lost track a bit of what I tried but I believe I tried everything mentioned in the links above. I created tons of beans for SpringHandlerInstantiator, Jackson2ObjectMapperBuilder, MappingJackson2HttpMessageConverter, RestTemplate and others and also tried with setting the SpringHandlerInstantiator in RepositoryRestConfigurer#configureJacksonObjectMapper.
I am using Spring Boot 2.1.6.RELEASE which makes me think something might have changed since some of the linked threads are quite old.
Here's my last attempt:
#Configuration
public class JacksonConfig {
#Bean
public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
}
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
#Autowired
private Validator validator;
#Autowired
private HandlerInstantiator handlerInstantiator;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.setHandlerInstantiator(handlerInstantiator);
}
}
#Component
public class RestResourceURLSerializer extends JsonDeserializer<Long> {
#Autowired
private MyConfig config;
#Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ServiceConfig serviceConfig = config.getServices().get("identity");
URI serviceUri = serviceConfig.getExternalUrl();
String servicePath = serviceUri.getPath();
URL givenUrl = p.readValueAs(URL.class);
String givenPath = givenUrl.getPath();
if (servicePath.equals(givenPath)) {
return Long.parseLong(givenPath.substring(givenPath.lastIndexOf('/') + 1));
}
return null;
}
}
I keep getting a NullPointerException POSTing something to the API endpoint that is deserialized with the JsonDeserializer above.
I was able to solve a similar problem by marking my deserializer constructor accept a parameter (and therefore removing the empty constructor) and marking constructor as #Autowired.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
#Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
#JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
My entity is marked with annotation #JsonDeserialize so I don't have to explicitly register it with ObjectMapper.

GridFsTemplate NullPointerException in Service Unit Test Class (Tech Stack: Spring Data / Spring Boot / Micro Service / Mongodb )

I am developing a spring boot app.
The service method uploads a PDF into a mongodb repo using GridFsTemplate which is autowired in the service.
This file upload service method works as expected via postman rest client.
But, When I tried running a unit test; calling the same service method, the SpringData GridFsTemplate is not initialised (In MongoDB, you can use GridFS to store binary files). This results in the org.springframework.data.mongodb.gridfs.GridFsTemplate.store(...) throwing a NullPointerException.
Please, can you help, I have been stuck in this for a days.
Below is my service implementation:
#Service
public final class UploadServiceImpl implements UploadService {
#Autowired
private SequenceRepository sequenceDao;
#Autowired (required = true)
private GridFsTemplate gridFsTemplate;
#Override
public Long uploadFile(Invoice uploadedInvoice) {
ByteArrayInputStream byteArrayInputStream = null;
if (checkContentType(invoiceInfo.getContentType())) {
invoiceInfo.setPaymentID(sequenceDao.getNextSequenceId(INVOICE_UPLOAD_SEQ_KEY));
byteArrayInputStream = new ByteArrayInputStream(uploadedInvoice.getFileContent());
//Error thrown is java.lang.NullPointerException: null, where gridFsTemplate is null and basically autowire does not work when test is run.
GridFSFile gridFSUploadedFile= gridFsTemplate.store(byteArrayInputStream, invoiceInfo.getFileName(), invoiceInfo.getContentType(), invoiceInfo);
return 1l;
} else {
return 2l;
}
}
### Below is my Unit Test class for the service
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class UploadServiceTest {
#Mock
private SequenceRepository sequenceRepositoryMock;
#Autowired
private GridFsTemplate gridFsTemplateMock;
#Mock
private Invoice invoiceMock;
#InjectMocks
private static UploadService uploadService = new UploadServiceImpl();
DBObject fileMetaData = null;
DB db = null;
Jongo jongo = null;
#Before
public void setUp() throws Exception {
db = new Fongo("Test").getDB("Database");
jongo = new Jongo(db);
}
#Test
public void testUploadFile() {
//test 1
Long mockPaymentNo = new Long(1);
Mockito.when(sequenceRepositoryMock.getNextSequenceId(INVOICE_SEQUENCE)).thenReturn(mockPaymentNo);
assertEquals(mockPaymentNo, (Long) sequenceRepositoryMock.getNextSequenceId(INVOICE_SEQUENCE));
//test 2
Invoice dummyInvoice = getDummyInvoice();
InvoiceInfo dummyInvoiceInfo = dummyInvoice.getInvoiceInfo();
MongoCollection invoicesCollection = jongo.getCollection("invoices");
assertNotNull(invoicesCollection.save(dummyInvoiceInfo));
assertEquals(1, invoicesCollection.save(dummyInvoiceInfo).getN());
System.out.println("TEST 2 >>>>>>>>>>>>>>>>>> "+ uploadService);
//test 3 : The following line is the cause of the exception, the service method is called but the GridFsTemplate is not initialized when the test is run. But it works when the endpoint is invoked via postman
uploadService.uploadFile(dummyInvoice);
System.out.println("TEST 3 >>>>>>>>>>>>>>>>>> ");
}
}
The problem is because you use #InjectMocks to autowire your UploadService.
And UploadService autowire two other beans SequenceRepository and GridFsTemplate.
If you’re doing TDD or not (and we are able to change the test first)
– clients of this code don’t know about an additional dependency,
because it’s completely hidden
The Javadoc states:
Mockito will try to inject mocks only either by constructor injection,
setter injection, or property injection in order and as described
below. If any of the following strategy fail, then Mockito won’t
report failure; i.e. you will have to provide dependencies yourself.
The solution is to use UploadServiceImpl constructor to autowire beans:
#Service
public final class UploadServiceImpl implements UploadService {
private final SequenceRepository sequenceDao;
private final GridFsTemplate gridFsTemplate;
private final PlannerClient plannerClient;
#Autowired
public PlannerServiceImpl(PlannerClient plannerClient, GridFsTemplate gridFsTemplate, SequenceRepository sequenceDao) {
this.plannerClient = plannerClient;
}
...
}
When there are more dependencies needed, they’re clearly in sight because they are initialized in constructor
More detailed:
https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/

Correct scope for spring beans on Rest Service

I'm creating a REST service using RestEasy and Spring 4.
The service is basically an endpoint for a complex batch process. So clients call the service passing a bunch of parameters and then the processing is triggered.
As there are many parameters that are initially passed to the service, and those parameters are used pretty much everywhere in the system, I've chosen to create a 'helper' bean that will hold the parameters, then every other bean can autowire the 'parameter bean' and use it.
example:
the url called by the client would be: http://localhost/rest/service/execute?processType=A&initialDate=20141220&finalDate=20141231......
The REST Service endpoint would be something like:
#Path("/service")
public class RESTService {
#Autowired
private RequestParams params;
#Autowired
private ProcessOrchestrator orchestrator;
#POST
#Path("/execute")
#Produces(MediaType.TEXT_PLAIN)
public Response executa(
#NotNull #QueryParam("processType") String processType,
#NotNull #QueryParam("initialDate") String initialDate,
#NotNull #QueryParam("finalDate") String finalDate,
...
) {
params.setProcessType(processType);
params.setInitialDate(initialDate);
params.setFinalDate(finalDate);
orchestrator.triggerBatchProcess();
}
}
The RequestParams bean will only hold the parameters values:
#Component
public class RESTService {
private String processType;
private String initialDate;
private String finalDate;
...
// getters and setters
}
And the other beans would #Autowire the params bean and use its parameters:
#Component
public class DataProcessor {
#Autowire
private RequestParams params;
//...
}
#Component
public class DataConverter {
#Autowire
private RequestParams params;
//...
}
#Component
public class FileWritter {
#Autowire
private RequestParams params;
//...
}
The design looks correct, right? Now, my concern is: how do I make sure that a new RequestParams instance is created every time the service is called? Do I need to declare a scope ("request" for instance) for all my beans?

#Autowired in Spring MVC #Controller does not work on a private field

I have a Spring MVC Controller in a very XML-slimmed application, we use a lot of annotations and as little config as possible. The Controller is working and it also has a number of resource values injected. But I've experienced a really strange behavior with this controller; annotated private fields referencing other components will not be injected.
This will not work.
#Controller
public class EntranceUnitController {
#Value("${remote.baseUrl}")
private String baseUrl = "http://localhost";
#Value("${remote.port}")
private String pushPort = "8080";
#Autowired
private HttpClientFactory httpClientFactory;
...
It seems that the httpClientFactory isn't there yet when the private fields are set, if I set a break point to inspect the value there is of course null set when the controller is created.
BUT, if I make a setter for the component and annotate the set-method instead of the private field the controller works as expected.
#Controller
public class EntranceUnitController {
#Value("${remote.baseUrl}")
private String baseUrl = "http://localhost";
#Value("${remote.port}")
private String pushPort = "8080";
private HttpClientFactory httpClientFactory;
#Autowired
public void setHttpClientFactory(HttpClientFactory httpClientFactory) {
this.httpClientFactory = httpClientFactory;
}
...
To me this is really annoying. Isn't the auto wiring injection for annotated values happening at the same time regardless injection point? I.e. why does it matter that the object is injected with a setter? I thought that private field injections are directly followed by constructs and setters, me start to think I'm wrong in that case...
Seems like your dependencies are in fact injected, you are just putting a breakpoint in the wrong moment (too early) and the dependencies aren't injected yet, despite class being already created.
Remember that, unless you are using constructor injection, the first place where you can use injected dependencies is #PostConstruct method:
#Controller
public class EntranceUnitController {
#Autowired
private HttpClientFactory httpClientFactory;
#PostConstruct
public void init() {
httpClientFactory //should not be null
}

Resources