Spring JPA CrudRepository Автопроводной объект имеет значение null при использовании с Apache Commons Tailer - PullRequest
0 голосов
/ 07 февраля 2020

Я работаю над небольшим приложением, которое может указывать на журнал Apache HTTP-сервера, следить за журналом (как 'tail -f' в Linux) и записывать записи в базу данных Oracle таблица.

Я установил приложение JPA Spring Boot / Spring Data и создал классы для моей сущности, интерфейс CrudRepository, службу для интерфейса (хотя я считаю, что это технически не нужно для этой реализации), и бегун, чтобы начать процесс. Я также настроил TailerListenerAdapter, чтобы выполнить отбор для разбора файла журнала. Я опубликую весь этот код ниже.

Проблема в том, что я могу успешно записать тестовую запись в базу данных до до запуска прослушивателя Tailer. Однако, когда слушатель работает, служба Autowired в TailerListenerAdapter имеет значение NULL и выдает исключение.

java.lang.NullPointerException
    at sbx.demo.logauditor.util.AccessListener.handle(AccessListener.java:49)
    at org.apache.commons.io.input.Tailer.readLines(Tailer.java:525)
    at org.apache.commons.io.input.Tailer.run(Tailer.java:457)
    at sbx.demo.logauditor.LogAuditRunner.run(LogAuditRunner.java:40)
    {... more stack trace ...}

Вот используемые классы (у меня, вероятно, есть некоторые ненужные аннотации, оставленные после экспериментов ) -

LogAuditRunner. java

package sbx.demo.logauditor;

import java.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import javax.transaction.Transactional;

import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import lombok.var;
import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.service.AccessService;
import sbx.demo.logauditor.util.AccessListener;

@Component
public class LogAuditRunner implements CommandLineRunner {
    @Autowired
    AccessService accServ;
    final String datePattern = "dd/MMM/yyyy:HH:mm:ss Z";
    final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);

    @Override
    @Transactional
    public void run(String... args) throws Exception {

        // This test code works if uncommented
        //LocalDateTime TS = LocalDateTime.from(formatter.parse("31/Jan/2020:14:28:32 -0500"));     
        //var logTest = new AccessRecord("10.154.103.2",Timestamp.valueOf(TS),"/cs/resources/layouts/Top%20Menus/Oracle/tree_T_collection_closed.gif","304");
        //System.out.println("Testing repository with " + logTest.toString());
        //accServ.save(logTest);

        TailerListener listener = new AccessListener();    
        Tailer tailer = new Tailer(new File("D:\\access_log"), listener);        
        tailer.run();
    }

}

AccessService. java

package sbx.demo.logauditor.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Service;

import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.repository.AccessRepository;

@Service
@Configurable
public class AccessService {

    @Autowired(required = true)
    AccessRepository accessRepo;

    public void save(AccessRecord ar) {
        try {
            System.out.println("Writing record to database: " + ar.toString());
            accessRepo.save(ar);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public List<AccessRecord> findAll() {
        List<AccessRecord> recList = new ArrayList<AccessRecord>();
        try {
            System.out.println("Searching database for all access records");

            for(AccessRecord ar : accessRepo.findAll()) {
                recList.add(ar);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return recList;
    }
}

AccessRepository. java

package sbx.demo.logauditor.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import sbx.demo.logauditor.model.AccessRecord;

@Repository
public interface AccessRepository extends CrudRepository<AccessRecord, Long>{
}

AccessRecord. java

package sbx.demo.logauditor.model; 

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name="ACCESS_LOG")
@Data
public class AccessRecord {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
    @Column(name="PROXY_AGENT")
    private String agent;
    @Column(name="SOURCE_IP")
    private String sourceip;
    @Column(name="ACCESS_TS")
    private Timestamp reqts;
    @Column(name="URI")
    private String requri;
    @Column(name="HTTP_STATUS")
    private String respcode;

    public AccessRecord() {}

    public AccessRecord(String source, Timestamp ts, String uri, String status) {
        this.sourceip = source;
        this.reqts = ts;
        this.requri = uri;
        this.respcode = status;

        try {
            InetAddress ip = InetAddress.getLocalHost();
            String hostname = ip.getHostName();
            this.agent = hostname;
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        String record = "Record: [" +  agent + "] [" + sourceip + "] [" + reqts +  "] [" + requri +  "] [" + respcode + "]";

        return record;
    }
}

AccessListener. java

package sbx.demo.logauditor.util;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.transaction.Transactional;

import org.apache.commons.io.input.TailerListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.var;
import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.repository.AccessRepository;
import sbx.demo.logauditor.service.AccessService;

@Component
public class AccessListener extends TailerListenerAdapter {
    final String regex = "^(\\S+) (\\S+) (\\S+) " + 
            "\\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(\\S+)" + 
            " (\\S+)\\s*(\\S+)?\\s*\" (\\d{3}) (\\S+)"; 
    final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
    final String datePattern = "dd/MMM/yyyy:HH:mm:ss Z";
    final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);

    @Autowired
    AccessService accServ;

    @Override
    @Transactional
    public void handle(String line) {
        LogRecorder lr = new LogRecorder();
        try {
            final Matcher matcher = pattern.matcher(line);
            if (matcher.find()) {
                String IP = matcher.group(1);
                //String TS = matcher.group(4);
                String URL = matcher.group(6);
                String STATUS = matcher.group(8);

                LocalDateTime TS = LocalDateTime.from(formatter.parse(matcher.group(4)));


                var ar = new AccessRecord(IP,Timestamp.valueOf(TS),URL,STATUS);
                accServ.save(ar);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }       
    }
}

И наконец LogauditorApplication. java

package sbx.demo.logauditor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LogauditorApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogauditorApplication.class, args);
    }

}

Примечание, я заметил, что если я вручную создаю AccessService (вместо того, чтобы полагаться на Autowiring) ), Я могу вызвать его, но тогда на интерфейсе Autowired AccessRepository происходит NullPointerException. Мне ясно, что это связано с Autowiring, я просто не понимаю, почему.

Я знаю, что есть способы следить и отправлять журналы через командную строку (это будет работать в Linux окружающая среда), но я хочу убедиться, что он достаточно устойчив, чтобы, скажем, перезапустить его, если он умрет, обработать опрокидывания журнала и т. д. c. Кроме того, я планирую написать дополнительную проверку, чтобы убедиться, что записи не перекрываются (т. Е. В случае, если приложение перезапускается и перечитывает весь файл). Но я хотел, чтобы это сначала заработало. Я думал, что это будет просто, поскольку Tailer требует так мало кода, и я уже чувствую себя комфортно с Spring.

1 Ответ

1 голос
/ 08 февраля 2020

Я смог добиться этого, передав AccessService в качестве параметра для конструктора AccessListener. Затем мне пришлось добавить аннотацию @Transactional к методу save в AccessService, чтобы транзакции фиксировались после обработки каждой строки внутри потока.

New AccessListener . java

package sbx.demo.logauditor.util;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.input.TailerListenerAdapter;

import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.service.AccessService;

public class AccessListener extends TailerListenerAdapter {
    final String regex = "^(\\S+) (\\S+) (\\S+) " + 
            "\\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(\\S+)" + 
            " (\\S+)\\s*(\\S+)?\\s*\" (\\d{3}) (\\S+)"; 
    final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
    final String datePattern = "dd/MMM/yyyy:HH:mm:ss Z";
    final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
    private AccessService accServ;

    public AccessListener(AccessService as) {
        this.accServ = as;
    }

//  @Autowired
//  AccessService accServ;

    @Override
    public void handle(String line) {
        try {
            final Matcher matcher = pattern.matcher(line);
            if (matcher.find()) {
                String IP = matcher.group(1);
                String URL = matcher.group(6);
                String STATUS = matcher.group(8);

                LocalDateTime TS = LocalDateTime.from(formatter.parse(matcher.group(4)));


                AccessRecord ar = new AccessRecord(IP,Timestamp.valueOf(TS),URL,STATUS);
                accServ.save(ar);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }       
    }
}
...