#Service
public Class SomeService {
private SomeServiceAsync someServiceAsync = new SomeServiceAsync();
...
public String DoAThing() {
CompletableFurute<String> future = someServiceAsync.GetAString();
return future.get();
}
}
#Service
#EnableAsync
public Class SomeServiceAsync {
#Value("${someProp1}")
private String someProp1;
#Autowired
private Environment env;
...
#Async
public CompletableFuture<String> GetAString() {
System.out.println(someProp1); // returns null
String someProp2 env.getProperty("someProp2"); // throws null pointer exception
return CompletableFurute.completedFuture("blablabla");
}
}
My problem is simply that I cannot access my application properties after making some of my methods run asynchronously. Nothing is failing before I try to execute the method and either get a null from #Value or env is null.
The method worked before making it async, and the async version works fine when I am not accessing the application properties.
Looks like the problem was the new SomeServiceAsync(), instead of an #Autowired.
Made the same mistake myself many times.
Related
I try to use Redis to set stock value.
#SpringBootTest
public class RedisDemoTest{
#Resource
private RedisService redisService;
#Test
public void stockTest() {
String value = redisService.setValue("stock:19",10L).getValue("stock:19");
System.out.println(value);
}
}
And here is my RedisService code
I tried both #Resource and #Autowired
#Service
public class RedisService {
#Resource
private JedisPool jedisPool;
public RedisService setValue(String key, Long value){
Jedis client = jedisPool.getResource();
client.set(key,value.toString());
client.close();
return this;
}
public String getValue(String key) {
Jedis client = jedisPool.getResource();
String value = client.get(key);
client.close();
return value;
}
it shows 1 usage with setValue method, However I had an java.lang.NullPointerException:Cannot invoke "service.RedisService.setValue(String, java.lang.Long)" because "this.redisService" is null
I hope to print out value 10 which is the set value.
the NullPointerExecption in the test code typically has a problem with improper DI. Make sure that your RedisService is properly injected into the RedisDemoTest class.
For instance, the target of the #Resource is based on the Field and Setter Method, so you would need to make a constructor for the setter method, otherwise, it will come out with an error.
I personally recommend using #Autowired, or #Inject, because #Resource is deprecated since Java 9 (jdk1.9)
This link helped me a lot: https://www.baeldung.com/spring-annotations-resource-inject-autowire
I am facing a strange problem while trying to unit test my code.
Here is my code :
public class ItemService {
private OfferService offerService;
#Inject
public ItemService (OfferService offerService){
this.offerService = offerService;
}
public List<Item> buildItems(ItemInfo itemInfo) {
List<Item> items = processItem(itemInfo);
Offers<Offer> offers = fetchItemInfo(items);
// based on the object offers, do some processing
}
private Offers<Offer> fetchItemInfo(List<Item> items) {
Offers<Offer> offers = new Offers<>();
// some processing here with offers.
// calling the db to fetch details
offerService.fetchInfoFromDB(offers);
return offers;
}
}
public class OfferService {
public void fetchInfoFromDB(Offers<Offer> offers) {
// fetching details from DB
// and updating the object **offers**
myDao.getDetailsById(id);
}
}
Now I have written junit to test the method buildItems()
UPDATE updating the mocks used and mock injection.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ItemService.class)
public class ItemServiceTest{
#Mock private MyDAO myDao;
#Mock private OfferService offerService;
#Before
public void setUp() throws Exception {
ItemService itemService = new ItemService (offerService, myDao);
}
public void testBuildItems(){
// some code -----
itemInfo = buildItemInfo();
offerDetail = buildOfferDetail();
when(myDao.getDetailsById(Mockito.anyLong())).thenReturn(offerDetail);
// some code -----
// I need to implement some code which will actually call
// offerService.fetchInfoFromDB(offers);
// and update the --offers-- object and return it.
List<Item> items = itemService.buildItems(itemInfo);
Assert.assertNotNull(items);
}
}
I am running with coverage and I can see that the below line got executed but the actual method is not getting called :
offerService.fetchInfoFromDB(offers);
I am getting null values in offers. Then I added the below line :
doCallRealMethod().when(offerService).fetchInfoFromDB(offers);
Still the same result. The offers object is passed by reference and is getting updated after the DB call which I am mocking already. But upto that call my code is not reaching. How can I update the offers object in my junit. Please help.
Your test is calling a zero arg ItemService() constructor, not the one arg #Inject constructor you posted. Either your code won't compile, or you haven't actually shown us the code in question.
Also, you say you are mocking offerService:
You call when on myDao and not offerService,
you do not pass your mock offerService into your ItemService constructor, as in new ItemService(offerService), and
your doCallRealMethod won't work because your mock offerService won't use your mock myDao; you'll need to mock the call on offerService directly with a thenAnswer that changes the passed List<Offer>, as on my question you linked.
doAnswer(invocation -> {
((List<Offer>) invocation.getArgument(0)).add(offerDetail);
return null;
}).when(offerService).fetchInfoFromDb(any());
If you fix those three you will be considerably closer to a working test.
CONTEXT:
I process reports with #Scheduled annotation and when invoke Component from Service property not getting initialized with #Value annotation even it physically exists in .properties and printed out in #PostConstruct.
DESCRIPTION:
ReportProcessor interface and InventoryReportProcessor implementation:
#FunctionalInterface
interface ReportProcessor {
public void process(OutputStream outputStream);
}
#Component
public class InventoryReportProcessor implement ReportProcessor {
#Value("${reportGenerator.path}")
private String destinationFileToSave;
/*
#PostConstruct
public void init() {
System.out.println(destinationFileToSave);
}
*/
#Override
public Map<String, Long> process(ByteArrayOutputStream outputStream) throws IOException {
System.out.println(destinationFileToSave);
// Some data processing in here
return null;
}
}
I use it from
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
My confusion comes from the fact that #Value in Service works fine but in #Component it returns null unless call in #PostConstruct. Also, if call #PostConstruct the value is still remains null in the rest of the class code.
I found similar Q&A and I did research in Srping docs but so far no single idea why it works this way and what can be a solution?
You need to Autowire the component to make your spring application aware of the component.
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Autowired
private ReportProcessor reportProcessor;
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
//InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
Field injection is done after objects are constructed since obviously the container cannot set a property of something which doesn't exist.
at the time System.out.println(destinationFileToSave); triggers values are not being injected;
if you want to see it working try something like this
#Autowired
InventoryReportProcessor pross;
pross.process(ByteArrayOutputStream outputStream);
#PostConstruct works as it is being called after the object creation.
Spring will only parse #Value annotations on beans it knows. The code you use creates an instance of the class outside the scope of Spring and as such Spring will do nothing with it.
One thing you can do is to create the instance explictly or use Autowire:
#Autowired
private ReportProcessor reportProcessor;
tl:dr If you have configured your application context correctly then a #Value cannot be null as that will stop the correct startup of your application.
Change your Code from
#Value("${reportGenerator.path}")
private String destinationFileToSave;
to
#Value("${reportGenerator.path}")
public void setDestinationFileToSave(String destinationFileToSave) {
SendMessageController.destinationFileToSave = destinationFileToSave;
}
I have a Singleton class in Java and I have a timer using the #Schedule annotation. I wish to change the property of the Schedule at runtime. Below is the code:
#Startup
#Singleton
public class Listener {
public void setProperty() {
Method[] methods = this.getClass().getDeclaredMethods();
Method method = methods[0];
Annotation[] annotations = method.getDeclaredAnnotations();
Annotation annotation = annotations[0];
if(annotation instanceof Schedule) {
Schedule schedule = (Schedule) annotation;
System.out.println(schedule.second());
}
}
#PostConstruct
public void runAtStartUp() {
setProperty();
}
#Schedule(second = "3")
public void run() {
// do something
}
}
I wish to change the value at runtime of Schedule second based on the information from a Property file. Is this actually possibe? The Property file contains the configuration information. I tried to do #Schedule(second = SOME_VARIABLE) where private static String SOME_VARIABLE = readFromConfigFile(); This does not work. It expects a constant meaning a final and I don't want to set final.
I also saw this post: Modifying annotation attribute value at runtime in java
It shows this is not possible to do.
Any ideas?
EDIT:
#Startup
#Singleton
public class Listener {
javax.annotation.#Resource // the issue is this
private javax.ejb.TimerService timerService;
private static String SOME_VARIABLE = null;
#PostConstruct
public void runAtStartUp() {
SOME_VARIABLE = readFromFile();
timerService.createTimer(new Date(), TimeUnit.SECONDS.toMillis(Long.parse(SOME_VARIABLE)), null);
}
#Timeout
public void check(Timer timer) {
// some code runs every SOME_VARIABLE as seconds
}
}
The issue is injecting using #Resource. How can this be fixed?
The Exception is shown below:
No EJBContainer provider available The following providers: org.glassfish.ejb.embedded.EJBContainerProviderImpl Returned null from createEJBContainer call
javax.ejb.EJBException
org.glassfish.ejb.embedded.EJBContainerProviderImpl
at javax.ejb.embeddable.EJBContainer.reportError(EJBContainer.java:186)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:121)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:78)
#BeforeClass
public void setUpClass() throws Exception {
Container container = EJBContainer.createEJBContainer();
}
This occurs during unit testing using the Embeddable EJB Container. Some of the Apache Maven code is located on this post: Java EJB JNDI Beans Lookup Failed
I think the solution you are looking for was discussed here.
TomasZ is right you should use programmatic timers with TimerService for the situations when you want dynamically change schedule in run time.
Maybe you could use the TimerService. I have written some code but on my Wildfly 8 it seems to run multiple times even if its a Singleton.
Documentation http://docs.oracle.com/javaee/6/tutorial/doc/bnboy.html
Hope this helps:
#javax.ejb.Singleton
#javax.ejb.Startup
public class VariableEjbTimer {
#javax.annotation.Resource
javax.ejb.TimerService timerService;
#javax.annotation.PostConstruct
public void runAtStartUp() {
createTimer(2000L);
}
private void createTimer(long millis) {
//timerService.createSingleActionTimer(millis, new javax.ejb.TimerConfig());
timerService.createTimer(millis, millis, null);
}
#javax.ejb.Timeout
public void run(javax.ejb.Timer timer) {
long timeout = readFromConfigFile();
System.out.println("Timeout in " + timeout);
createTimer(timeout);
}
private long readFromConfigFile() {
return new java.util.Random().nextInt(5) * 1000L;
}
}
I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.