0

I have an API in Java Sprint that needs to execute a task daily to import some data from an external system that is generating a txt in an FTP server. The issue I have is that the Autowired fields are not being autowired.... I mean, they are null.

I'm using @PostConstruct to execute a task every time the app started, so I can schedule the action with a Timer.

Attempt 1

Here is the code (first the PostContruct method)

@Override
    @PostConstruct
    @Transactional
    public Response importdata(){
        Response response = new Response();
        try {
            System.out.println("*** Setting Import ****");
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR_OF_DAY, 21);
            calendar.set(Calendar.MINUTE, 5);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);

            Timer time = new Timer(); // Instantiate Timer Object
            time.schedule(new ImportServiceImpl(), calendar.getTime(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
        } catch (Exception e) {
            e.printStackTrace();
            response.setCode(CodeList.EXCEPTION);
            response.setSuccess(false);
        }
        return response;
    }

So here, I'm scheduling the function daily at 21.05.

Here you have ImportServiceImpl

@Component
public class ImportServiceImpl extends TimerTask  implements ImportService{

    @Autowired
    InvoiceDao invoiceDao;

    @Autowired
    ClientDao clientDao;

    @Override
    @Transactional
    public void run() {
        System.out.println("*** Running **** " + new Date());
        startImport();
    }

    @Override
    @Transactional
    public void startImport() {
        Path dir = Paths.get(ResourcesLocation.IMPORT_ROUTE);
        Boolean success = true;
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for (Path entry : stream) {
                if (!Files.isDirectory(entry)) {
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(new FileInputStream(ResourcesLocation.IMPORT_ROUTE + entry.getFileName().toString())));
                    System.out.println("*** Importing file **** " + ResourcesLocation.IMPORT_ROUTE + entry.getFileName().toString());
                    try {
                        String line;
                        int i = 0;
                        while ((line = br.readLine()) != null) {

                            final String[] parts = line.split("\\|");
                            System.out.println("Line: " + i++ + " Text: " + line);
                            System.out.println("Factura: " + parts[1]);
                            Client client = (Client) this.clientDao.get(parts[0]);
                            String invoiceNumber = this.generateInvoiceNumber(parts[1].substring(1).replace("-", ""));
                            Invoice inv = (Invoice) this.invoiceDao.getByNumber(invoiceNumber);
                            if(inv == null){
                                inv = new Invoice();
                                inv.setClient(client);
                                inv.setNumber(invoiceNumber);
                                inv.setDate(this.convertDate(parts[2]));
                                inv.setTotal(this.convertFloat(parts[3]));
                                inv = (Invoice) this.invoiceDao.addOrUpdate(inv);
                            }
                        }
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        success = false;
                    }
                    finally {
                        try {
                            br.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        if(success){
                            try {
                                Files.delete(entry);
                            } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }

The issue here is that this.clientDao should be autowired, but it's null.... So I tried to do

if(this.clientDao == null)
    this.clientDao = new ClientDaoImpl();

But then, in the get method in ClientDaoImpl I have

@SuppressWarnings("unchecked")
    public Object get(String name) throws Exception
    {
        Query q = sessionFactory.getCurrentSession()
                .createQuery("from " + this.entity + " WHERE name = '" + name + "'");
        return q.uniqueResult();
    }

And there sessionFactory was null as it was not autowired.. And I don't think that the solution is to keep initializing every class manually...

Attemp 2

Then I tried to Autowired the class ImportServiceImpl instead of initializing manually and changed my code with:

@Autowired
    ImportService importService;

and

time.schedule(this.importService, calendar.getTime(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));

But there I get an error because the this.importService is not ImportServiceImpl, it's ImportService which is the interface and the interface cannot extend TimerTask.

3 Attempt

Change the Autowired class to autowired the Implementation and not the Interface. Like this:

@Autowired
    ImportServiceImpl importService;

So I get the following error:

java.lang.IllegalArgumentException: Can not set com.app.services.ImportServiceImpl field com.app.services.InvoiceServiceImpl.importService to com.sun.proxy.$Proxy184

I checked the answers in Why is my Spring @Autowired field null?

But the manual solution is not working as the context is never setted. Also I tried with the @Configure annotation, that was suggested here too and or I don;t know how to use it or it is not working.

To simplify the example: I have a class InvoiceServiceImpl which has a method importdata with the annotation @PostConstruct, so it's called after the app start (that part is ok) the importdata method, schedule a timertask for the class ImportServiceImpl (so far so good). But then when it's the right time, the method is executed but the @Autowired properties inside the method in the timertask class are null.

Faabass
  • 1,394
  • 8
  • 29
  • 58

1 Answers1

1

Updated

I have to suggest a small modification of how code is organized.

First of all ImportService is defined.

public interface ImportService {
    public void startImport();
}

And the related implementation.

@Service
public class ImportServiceImpl implements ImportService {

    @Autowired
    private InvoiceDao invoiceDao;

    @Autowired
    private ClientDao clientDao;

    @Override
    @Transactional
    public void startImport() {
        // Process...
    }

Then, you have your TimerTask implementation.

@Component
public class ImportTimerTask extends TimerTask {

    @Autowired
    private ImportService importService;

    @Override
    public void run() {
        importService.startImport();
    }
}

And finally, you have the @PostConstruct method in any Class.

@Autowired
private ImportTimerTask importTimerTask;

@PostConstruct
@Transactional
public void importData() {
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 9);
    calendar.set(Calendar.MINUTE, 2);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    Timer time = new Timer();
    time.schedule(importTimerTask, calendar.getTime(),
            TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
}

With a such implementation, my simple tests were fine, invoiceDao and cliendDao were successfully autowired.


You can try adding @EnableAspectJAutoProxy(proxyTargetClass=true) in a @Configuration class in order to implement Attempt 3.

More reference you can find here.

lzagkaretos
  • 2,842
  • 2
  • 16
  • 26
  • I added that to my WebConfig but it didn't work: @ Configuration @ EnableWebMvc @ EnableAspectJAutoProxy(proxyTargetClass=true) public class WebConfig extends WebMvcConfigurerAdapter {.. Now I'm getting $Proxy174 error(I don't know if the number matters) – Faabass Sep 21 '17 at 19:37
  • Updated answer with a code modification suggestion. Hope that helps. – lzagkaretos Sep 22 '17 at 06:07