This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 3 years ago.
I am creating a Netty UDP server using the spring framework. I have 3 classes and 1 interface.
UDPServer.java
package com.example.nettyUDPserver;
import java.net.InetAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.stereotype.Component;
import akka.actor.ActorRef;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
public class UDPServer {
private int port;
ActorRef serverActor = null;
public UDPServer(int port) {
this.port = port;
}
public void run() throws Exception {
final NioEventLoopGroup group = new NioEventLoopGroup();
try {
final Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<NioDatagramChannel>() {
#Override
public void initChannel(final NioDatagramChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new IncomingPacketHandler());
}
});
Integer pPort = port;
InetAddress address = InetAddress.getLocalHost();
//InetAddress address = InetAddress.getByName("192.168.1.53");
System.out.println("Localhost address is: " + address.toString());
b.bind(address, pPort).sync().channel().closeFuture().await();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
int port = 6001;
new UDPServer(port).run();
}
}
IncomingPacketHandler.java
package com.example.nettyUDPserver;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import com.example.dao.SensorRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
#ComponentScan("com.example.dao")
public class IncomingPacketHandler extends SimpleChannelInboundHandler<DatagramPacket> {
#Autowired
SensorRepository repo;
IncomingPacketHandler(){
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
final InetAddress srcAddr = packet.sender().getAddress();
ByteBuf buffer = packet.content();
packet.replace(buffer);
int len = buffer.readableBytes();
byte[] message = new byte[len];
buffer.readBytes(message);
String str = new String(message, StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree(str);
int id = actualObj.get("sensor_id").asInt();
String status = actualObj.get("status").asText();
System.out.println("==========================================================");
System.out.println("Source address of datagram received: " + srcAddr.toString());
System.out.println("String message received: " + str);
show();
}
public void show() {
System.out.println("In show function, we will perform our CRUD operations");
System.out.println(repo);
// try {
// this.repo.findAll().forEach(x -> System.out.println(x));
// } catch (NullPointerException e) {
// e.printStackTrace();
// }
}
}
Sensor.java
package com.example.models;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Sensor {
#Id
private int sensor_id;
private String status;
private double batLev;
public int getSensor_id() {
return sensor_id;
}
public void setSensor_id(int sensor_id) {
this.sensor_id = sensor_id;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public double getBatLev() {
return batLev;
}
public void setBatLev(double batLev) {
this.batLev = batLev;
}
}
SensorRepository.java
package com.example.dao;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.models.Sensor;
#Repository
public interface SensorRepository extends CrudRepository<Sensor, Integer> {
}
I am running my server in the class UDPServer.java and I can successfully get and decode datagrams. The problem is with the SensorRepository in the IncomingPacketHandler.java class. I am using the #Autowired notation in the variable and I am using the #Repository annotation in the interface, but when I print the value of the autowired repository, it is null, so I cannot make SQL queries. Any ideas?
UPDATE
Thank you for your answers guys, much appreciated. I am denoting the IncomingPacketHandler class as a component and I am autowiring it in the UDPServer class. When I run it I get this:
[nioEventLoopGroup-2-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message DatagramPacket(/192.168.61.64:59905 => /192.168.61.64:6001, PooledUnsafeDirectByteBuf(ridx: 0, widx: 38, cap: 2048)) that reached at the tail of the pipeline. Please check your pipeline configuration.
This is probably out of the scope of this question, but you maybe can show me tha direction. Thank you once again.
Your class IncomingPacketHandler is not managed by Spring, but created by you personally:
ChannelPipeline p = ch.pipeline();
p.addLast(new IncomingPacketHandler());
As such, even if you add a million Spring annotations, they won't do anything. What you want instead is to have Spring create this handler, and pass the Spring-created handler as argument to p.addLast
The IncomingPacketHandler class has been created manually and not by Spring and hence bean is not available.
Add #Component to IncomingPacketHandler class:
...
import org.springframework.stereotype.Component;
#Component
public class IncomingPacketHandler extends
...
And then in UDPServer.java:
...
import org.springframework.beans.factory.annotation.Autowired;
#Component
public class UDPServer {
#Autowired
private IncomingPacketHandler incomingPacketHandler;
...
Related
I'm getting an error with StreamIdentifier when trying to use MultiStreamTracker in a kinesis consumer application.
java.lang.IllegalArgumentException: Unable to deserialize StreamIdentifier from first-stream-name
What is causing this error? I can't find a good example of using the tracker with kinesis.
The stream name works when using a consumer with a single stream so I'm not sure what is happening. It looks like the consumer is trying to parse the accountId and streamCreationEpoch. But when I create the identifiers I am using the singleStreamInstance method. Is the stream name required to have these values? They appear to be optional from the code.
This test is part of a complete example on github.
package kinesis.localstack.example;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.amazonaws.services.kinesis.producer.KinesisProducer;
import com.amazonaws.services.kinesis.producer.KinesisProducerConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
import software.amazon.kinesis.common.ConfigsBuilder;
import software.amazon.kinesis.common.InitialPositionInStream;
import software.amazon.kinesis.common.InitialPositionInStreamExtended;
import software.amazon.kinesis.common.KinesisClientUtil;
import software.amazon.kinesis.common.StreamConfig;
import software.amazon.kinesis.common.StreamIdentifier;
import software.amazon.kinesis.coordinator.Scheduler;
import software.amazon.kinesis.exceptions.InvalidStateException;
import software.amazon.kinesis.exceptions.ShutdownException;
import software.amazon.kinesis.lifecycle.events.InitializationInput;
import software.amazon.kinesis.lifecycle.events.LeaseLostInput;
import software.amazon.kinesis.lifecycle.events.ProcessRecordsInput;
import software.amazon.kinesis.lifecycle.events.ShardEndedInput;
import software.amazon.kinesis.lifecycle.events.ShutdownRequestedInput;
import software.amazon.kinesis.processor.FormerStreamsLeasesDeletionStrategy;
import software.amazon.kinesis.processor.FormerStreamsLeasesDeletionStrategy.NoLeaseDeletionStrategy;
import software.amazon.kinesis.processor.MultiStreamTracker;
import software.amazon.kinesis.processor.ShardRecordProcessor;
import software.amazon.kinesis.processor.ShardRecordProcessorFactory;
import software.amazon.kinesis.retrieval.KinesisClientRecord;
import software.amazon.kinesis.retrieval.polling.PollingConfig;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCH;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.DYNAMODB;
import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KINESIS;
import static software.amazon.kinesis.common.InitialPositionInStream.TRIM_HORIZON;
import static software.amazon.kinesis.common.StreamIdentifier.singleStreamInstance;
#Testcontainers
public class KinesisMultiStreamTest {
static class TestProcessorFactory implements ShardRecordProcessorFactory {
private final TestKinesisRecordService service;
public TestProcessorFactory(TestKinesisRecordService service) {
this.service = service;
}
#Override
public ShardRecordProcessor shardRecordProcessor() {
throw new UnsupportedOperationException("must have streamIdentifier");
}
public ShardRecordProcessor shardRecordProcessor(StreamIdentifier streamIdentifier) {
return new TestRecordProcessor(service, streamIdentifier);
}
}
static class TestRecordProcessor implements ShardRecordProcessor {
public final TestKinesisRecordService service;
public final StreamIdentifier streamIdentifier;
public TestRecordProcessor(TestKinesisRecordService service, StreamIdentifier streamIdentifier) {
this.service = service;
this.streamIdentifier = streamIdentifier;
}
#Override
public void initialize(InitializationInput initializationInput) {
}
#Override
public void processRecords(ProcessRecordsInput processRecordsInput) {
service.addRecord(streamIdentifier, processRecordsInput);
}
#Override
public void leaseLost(LeaseLostInput leaseLostInput) {
}
#Override
public void shardEnded(ShardEndedInput shardEndedInput) {
try {
shardEndedInput.checkpointer().checkpoint();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
#Override
public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
}
}
static class TestKinesisRecordService {
private List<ProcessRecordsInput> firstStreamRecords = Collections.synchronizedList(new ArrayList<>());
private List<ProcessRecordsInput> secondStreamRecords = Collections.synchronizedList(new ArrayList<>());
public void addRecord(StreamIdentifier streamIdentifier, ProcessRecordsInput processRecordsInput) {
if(streamIdentifier.streamName().contains(firstStreamName)) {
firstStreamRecords.add(processRecordsInput);
} else if(streamIdentifier.streamName().contains(secondStreamName)) {
secondStreamRecords.add(processRecordsInput);
} else {
throw new IllegalStateException("no list for stream " + streamIdentifier);
}
}
public List<ProcessRecordsInput> getFirstStreamRecords() {
return Collections.unmodifiableList(firstStreamRecords);
}
public List<ProcessRecordsInput> getSecondStreamRecords() {
return Collections.unmodifiableList(secondStreamRecords);
}
}
public static final String firstStreamName = "first-stream-name";
public static final String secondStreamName = "second-stream-name";
public static final String partitionKey = "partition-key";
DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:latest");
#Container
public LocalStackContainer localstack = new LocalStackContainer(localstackImage)
.withServices(KINESIS, CLOUDWATCH)
.withEnv("KINESIS_INITIALIZE_STREAMS", firstStreamName + ":1," + secondStreamName + ":1");
public Scheduler scheduler;
public TestKinesisRecordService service = new TestKinesisRecordService();
public KinesisProducer producer;
#BeforeEach
void setup() {
KinesisAsyncClient kinesisClient = KinesisClientUtil.createKinesisAsyncClient(
KinesisAsyncClient.builder().endpointOverride(localstack.getEndpointOverride(KINESIS)).region(Region.of(localstack.getRegion()))
);
DynamoDbAsyncClient dynamoClient = DynamoDbAsyncClient.builder().region(Region.of(localstack.getRegion())).endpointOverride(localstack.getEndpointOverride(DYNAMODB)).build();
CloudWatchAsyncClient cloudWatchClient = CloudWatchAsyncClient.builder().region(Region.of(localstack.getRegion())).endpointOverride(localstack.getEndpointOverride(CLOUDWATCH)).build();
MultiStreamTracker tracker = new MultiStreamTracker() {
private List<StreamConfig> configs = List.of(
new StreamConfig(singleStreamInstance(firstStreamName), InitialPositionInStreamExtended.newInitialPosition(TRIM_HORIZON)),
new StreamConfig(singleStreamInstance(secondStreamName), InitialPositionInStreamExtended.newInitialPosition(TRIM_HORIZON)));
#Override
public List<StreamConfig> streamConfigList() {
return configs;
}
#Override
public FormerStreamsLeasesDeletionStrategy formerStreamsLeasesDeletionStrategy() {
return new NoLeaseDeletionStrategy();
}
};
ConfigsBuilder configsBuilder = new ConfigsBuilder(tracker, "KinesisPratTest", kinesisClient, dynamoClient, cloudWatchClient, UUID.randomUUID().toString(), new TestProcessorFactory(service));
scheduler = new Scheduler(
configsBuilder.checkpointConfig(),
configsBuilder.coordinatorConfig(),
configsBuilder.leaseManagementConfig(),
configsBuilder.lifecycleConfig(),
configsBuilder.metricsConfig(),
configsBuilder.processorConfig().callProcessRecordsEvenForEmptyRecordList(false),
configsBuilder.retrievalConfig()
);
new Thread(scheduler).start();
producer = producer();
}
#AfterEach
public void teardown() throws ExecutionException, InterruptedException, TimeoutException {
producer.destroy();
Future<Boolean> gracefulShutdownFuture = scheduler.startGracefulShutdown();
gracefulShutdownFuture.get(60, TimeUnit.SECONDS);
}
public KinesisProducer producer() {
var configuration = new KinesisProducerConfiguration()
.setVerifyCertificate(false)
.setCredentialsProvider(localstack.getDefaultCredentialsProvider())
.setMetricsCredentialsProvider(localstack.getDefaultCredentialsProvider())
.setRegion(localstack.getRegion())
.setCloudwatchEndpoint(localstack.getEndpointOverride(CLOUDWATCH).getHost())
.setCloudwatchPort(localstack.getEndpointOverride(CLOUDWATCH).getPort())
.setKinesisEndpoint(localstack.getEndpointOverride(KINESIS).getHost())
.setKinesisPort(localstack.getEndpointOverride(KINESIS).getPort());
return new KinesisProducer(configuration);
}
#Test
void testFirstStream() {
String expected = "Hello";
producer.addUserRecord(firstStreamName, partitionKey, ByteBuffer.wrap(expected.getBytes(StandardCharsets.UTF_8)));
var result = await().timeout(600, TimeUnit.SECONDS)
.until(() -> service.getFirstStreamRecords().stream()
.flatMap(r -> r.records().stream())
.map(KinesisClientRecord::data)
.map(r -> StandardCharsets.UTF_8.decode(r).toString())
.collect(toList()), records -> records.size() > 0);
assertThat(result).anyMatch(r -> r.equals(expected));
}
#Test
void testSecondStream() {
String expected = "Hello";
producer.addUserRecord(secondStreamName, partitionKey, ByteBuffer.wrap(expected.getBytes(StandardCharsets.UTF_8)));
var result = await().timeout(600, TimeUnit.SECONDS)
.until(() -> service.getSecondStreamRecords().stream()
.flatMap(r -> r.records().stream())
.map(KinesisClientRecord::data)
.map(r -> StandardCharsets.UTF_8.decode(r).toString())
.collect(toList()), records -> records.size() > 0);
assertThat(result).anyMatch(r -> r.equals(expected));
}
}
Here is the error I am getting.
[Thread-9] ERROR software.amazon.kinesis.coordinator.Scheduler - Worker.run caught exception, sleeping for 1000 milli seconds!
java.lang.IllegalArgumentException: Unable to deserialize StreamIdentifier from first-stream-name
at software.amazon.kinesis.common.StreamIdentifier.multiStreamInstance(StreamIdentifier.java:75)
at software.amazon.kinesis.coordinator.Scheduler.getStreamIdentifier(Scheduler.java:1001)
at software.amazon.kinesis.coordinator.Scheduler.buildConsumer(Scheduler.java:917)
at software.amazon.kinesis.coordinator.Scheduler.createOrGetShardConsumer(Scheduler.java:899)
at software.amazon.kinesis.coordinator.Scheduler.runProcessLoop(Scheduler.java:419)
at software.amazon.kinesis.coordinator.Scheduler.run(Scheduler.java:330)
at java.base/java.lang.Thread.run(Thread.java:829)
According to documentation:
The serialized stream identifier should be of the following format: account-id:StreamName:streamCreationTimestamp
So your code should be like this:
private List<StreamConfig> configs = List.of(
new StreamConfig(multiStreamInstance("111111111:multiStreamTest-1:12345"), InitialPositionInStreamExtended.newInitialPosition(TRIM_HORIZON)),
new StreamConfig(multiStreamInstance("111111111:multiStreamTest-2:12389"), InitialPositionInStreamExtended.newInitialPosition(TRIM_HORIZON)));
Note: this also will change leaseKey format to account-id:StreamName:streamCreationTimestamp:ShardId
I'm working on a small app that I can point to an Apache HTTP server log, follow the log (a la 'tail -f' in Linux), and write entries to an Oracle Database table.
I set up Spring Boot / Spring Data JPA application and created classes for my Entity, the CrudRepository interface, a service for the interface (though I believed this technically unnecessary for this implementation), and a runner to kick the process off. I also set up the TailerListenerAdapter to do the lifting for the log file parsing. I will post all of this code below.
The problem is that I can write a test record to the database successfully prior to starting the Tailer listener. However, when the listener is running, the Autowired Service in the TailerListenerAdapter, is null and throws an exception.
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 ...}
Here are the classes used (I probably have some unnecessary annotations in there left from experimentation) -
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();
}
}
}
And finally 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);
}
}
Side note, I noticed that if I manually instantiate the AccessService (instead of relying on Autowiring), I can invoke it, but then the NullPointerException happens on the Autowired AccessRepository interface. It's clear to me that it has to do with the Autowiring, I just am not understanding why.
I know that there are ways to follow and send logs via command line (this is going to run in a Linux environment) but I want to ensure that it is robust enough to, say, restart if it dies, handle log rollovers, etc. Also, I'm planning to write in some extra validation to ensure entries don't overlap (i.e. - in the event the application restarts and re-reads an entire file). But, I wanted to get it working first. I thought it would be straightforward since Tailer requires so little code, and I'm already comfortable with Spring.
I was able to get this working by passing the AccessService as a parameter for the AccessListener constructor. I then had to add the #Transactional annotation to the save method in the AccessService so that transactions would commit after each line was processed inside the thread.
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();
}
}
}
I'm working on a project in which I'm trying to inject dependency into a class but #Autowired annotating does not seem to work for some reason.
I have the class ApplicationServer
package project.eHealth;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import project.eHealth.bussiness.AppointmentDto;
import project.eHealth.bussiness.AppointmentService;
#SpringBootApplication
#Component
public class ApplicationServer {
private static final int PORT = 9001;
private static HashSet<String> names = new HashSet<String>();
#Autowired
private static AppointmentService appointments;
public static void main(String[] args) throws Exception {
SpringApplication.run(ApplicationServer.class, args);
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
ObjectMapper mapper = new ObjectMapper();
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
/*while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}*/
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
System.out.println("Aici este controlul!");
String input = in.readLine();
System.out.println(input);
Request request = mapper.readValue(input,Request.class);
if (request.getCommand().equals("getAllAppointments")){
if (appointments == null){
System.out.println("appointmentService is null");
}
else{
List<AppointmentDto> appointmentsList = appointments.getAll();
Response response = new Response();
response.setAction("RETURNED");
response.setData(mapper.writeValueAsString(appointmentsList));
String respObj = mapper.writeValueAsString(response);
out.println(respObj);
}
}
System.out.println(input);
if (input == null) {
return;
}
String[] command = input.split("&");
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
}
if (out != null) {
}
}
}
}
}
And the class AppointmentService
package project.serverSide.server.bussiness;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import project.serverSide.server.data.Appointment;
import project.serverSide.server.data.AppointmentRepository;
#Component
#Service
public class AppointmentService {
#Autowired
AppointmentRepository appointments;
public AppointmentService(){
}
public List<AppointmentDto> getAll(){
List<Appointment> appointmentsList = appointments.findAll();
List<AppointmentDto> appointmentsDto = new ArrayList<AppointmentDto>();
for (Appointment ap : appointmentsList){
AppointmentDto apDto = new AppointmentDto();
apDto.setDoctorName(new SimpleStringProperty(ap.getDoctorName()));
apDto.setPatientName(new SimpleStringProperty(ap.getPatientName()));
apDto.setDateIssued(new SimpleObjectProperty<LocalDate>(ap.getDateIssued()));
appointmentsDto.add(apDto);
}
return appointmentsDto;
}
}
And AppointmentRepository
package project.serverSide.server.data;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
#Component
public interface AppointmentRepository extends JpaRepository<Appointment,Integer> {
List<Appointment> findAll();
}
The issue is that ApplicationServer class does not inject the AppointmentService for some reason and I do not know why. I've been spending an important number of hours search for an answer but without any result.
Help is deeply appreciated!
You are missing #ComponentScan in your ApplicationServer class.
#SpringBootApplication
#ComponentScan(basePackages = {"project.serverSide"})
public class ApplicationServer {
}
And remove static from AppointmentService:
#Autowired
private AppointmentService appointments;
If you need static instance variable then use setter injection instead, as follows:
private static AppointmentService appointments;
#Autowired
public void setAppointments(AppointmentService appointments){
ApplicationServer.appointments = appointments;
}
you need to add base package in ApplicationContext.xml, u=you can use below code :-
<context:component-scan annotation-config="true"
base-package="com.amstech.mayal" />
below code use for add base package fot repositories:-
<context:annotation-config />
<jpa:repositories base-package="com.amstech.mayal.repository" />
and also add below code for Autowired:-
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
Hi i have an issue with spring websockets, this is the scenario:
a standalone application is sending (remote) some data like date Date, procedence String, and weight BigDecimal this data is sending via TCP to socket,
after that this data is saving into database at this point all is fine, but in the next step (websocket) i cannot show this information in a webpage, the weight data must be showed (live) in the screen
this is my websocket configuration:
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/indicator").withSockJS();
}
#Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
}
#Override
public void configureClientOutboundChannel(final ChannelRegistration registration) {
}
#Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration wstr) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> list) {
return Boolean.TRUE;
}
}
this is my another class that receive data form socket and process information and send to websocket:
import com.mcss.mcontrols.helper.ByteHelper;
import com.spc.basweb.Constants;
import com.spc.basweb.transmissor.dto.Transmission;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent;
import com.spc.basweb.service.BroadcastingService;
import com.spc.basweb.service.DataProcessorService;
import java.io.IOException;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Transformer;
#MessageEndpoint
public class BroadcastingServiceImpl implements BroadcastingService, ApplicationListener<BrokerAvailabilityEvent> {
private static final Logger LOGGER = Logger.getLogger(BroadcastingServiceImpl.class);
private final MessageSendingOperations<String> messagingTemplate;
private String processedData;
#Autowired
DataProcessorService dataProcessorService;
#Autowired
public BroadcastingServiceImpl(final MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#Override
public String getProcessedData() {
return processedData;
}
#Override
#ServiceActivator(inputChannel = "broadcaster")
public String broadcast(byte[] bytes) {
try {
Transmission t = (Transmission) ByteHelper.toObject(bytes);
LOGGER.debug(t.getProcedence() + " " + t.getDate() + " " + t.getWeight());
String rm = this.dataProcessorService.processData(t);
this.messagingTemplate.convertAndSend(Constants.END_POINT_READ, this.dataProcessorService.getWeighing().getWeight().toString());
return rm;
} catch (IOException | ClassNotFoundException ex) {
LOGGER.error("Error de transmision de objetos", ex);
}
return DataProcessorService.NOT_OK_RESPONSE;
}
#Override
public void onApplicationEvent(BrokerAvailabilityEvent e) {
LOGGER.debug("Application event");
}
#Transformer(outputChannel = "broadcaster")
public String convert(String response) {
return response;
}
}
in the debbuger i'm getting this information:
30-03-2016 15:07:20 DEBUG SimpleBrokerMessageHandler:277 - Processing MESSAGE destination=/read session=null payload=3003
in another class (Controller) i'm using the same method:
this.messagingTemplate.convertAndSend(Constants.END_POINT_READ, "3500");
and sending "manually" the information an is showing correctly. and i'm getting in debbuger this message:
30-03-2016 15:05:18 DEBUG SimpleBrokerMessageHandler:277 - Processing MESSAGE destination=/read session=dfR45V77 payload=3500
the difference is in session value but i don't know what this session is having null in the process, what am i doing wrong some clarification o help is welcome
First of all I don't see the configureMessageBroker implementation, so it isn't clear how that may work at all...
From other hand if you see such a difference, try to debug the code in the SimpMessagingTemplate.
I only see headerAccessor.setSessionId(sessionId); in the SimpleBrokerMessageHandler when it does
logger.debug("Broadcasting to " + subscriptions.size() + " sessions.");
I want to modify the client handler to use Foo instead of Datagram -- what changes are required in the client itself?
Surely it's not necessary to strictly keep to datagrams to send and receive with Netty? The Factorial example uses BigInteger, so, surely, it's possible to use POJO's.
Any and all attempts to create a class like:
class FooClientHandler extends SimpleChannelInboundHandler<Foo> are just non-starters for me, it literally won't send or receive with a server. (Yes, both client and server use similar handlers, generic classes with Foo.) So, I'm coming at this now from working code.
What's the key distinction between the factorial handler and the the datagram handler below? Or, is the primary distinction in how it's used in the client?
client:
package net.bounceme.dur.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
import java.util.logging.Logger;
import net.bounceme.dur.client.gui.MyProps;
public final class Client {
private static final Logger log = Logger.getLogger(Client.class.getName());
public void connect() throws InterruptedException {
MyProps p = new MyProps();
String host = p.getHost();
int port = p.getServerPort();
pingPongDatagram(host, port);
}
public void pingPongDatagram(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new DatagramClientHandler());
Channel ch = b.bind(0).sync().channel();
ch.writeAndFlush(new DatagramPacket(
Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
new InetSocketAddress(host, port))).sync();
log.info("wrote packet");
if (!ch.closeFuture().await(5000)) {
log.warning("server timed out");
}
} finally {
group.shutdownGracefully();
}
}
}
handler:
package net.bounceme.dur.netty;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
import java.util.logging.Logger;
public class DatagramClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private static final Logger log = Logger.getLogger(DatagramClientHandler.class.getName());
#Override
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
String response = msg.content().toString(CharsetUtil.UTF_8);
log.info(response);
DatagramPacket foo = new DatagramPacket(
Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
new InetSocketAddress("localhost", 4454));
ctx.writeAndFlush(foo);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.severe(cause.toString());
ctx.close();
}
}
I omitted the server code, it's almost exactly as in the Ghandi quote example.
What changes do I need to make to the client so that the handler can use Foo instead of DatagramPacket?
All I can say with certainty is that this client:
package net.bounceme.dur.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
import java.util.logging.Logger;
import net.bounceme.dur.client.gui.MyProps;
import net.bounceme.dur.client.jdbc.Title;
public final class Client {
private static final Logger log = Logger.getLogger(Client.class.getName());
public void connect() throws InterruptedException {
MyProps p = new MyProps();
String host = p.getHost();
int port = p.getServerPort();
pingPongDatagram(host, port);
}
public void pingPongDatagram(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new TitleClientHandler());
Channel ch = b.bind(0).sync().channel();
ch.writeAndFlush(new DatagramPacket(
Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
new InetSocketAddress(host, port))).sync();
ch.writeAndFlush(new Title());
log.info("wrote packets");
if (!ch.closeFuture().await(5000)) {
log.warning("server timed out");
}
} finally {
group.shutdownGracefully();
}
}
}
and handler:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.logging.Logger;
import net.bounceme.dur.client.jdbc.Title;
public class TitleClientHandler extends SimpleChannelInboundHandler<Title> {
private static final Logger log = Logger.getLogger(TitleClientHandler.class.getName());
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.severe(cause.toString());
ctx.close();
}
#Override
protected void channelRead0(ChannelHandlerContext chc, Title title) throws Exception {
log.info(title.toString());
}
}
don't, seemingly, communicate at all with the server -- even when the server has been modified accordingly.