I would like to develop a TCP Server with Spring Integration but I have a few difficulties...
My server should listen on a port for GPS devices which first identify themselves with their IMEI and needs an Acknowledgment from the server to go further and send the position data to the same TCP server.
I succeeded to get the IMEI but the acknowledgment doesn't reach the device... I'm not even sure if i send it the right way. I read the documentation and other posts and I found out that I had to use TcpInboundGateway instead of channel adapter because it's the same session. But do I have to use TcpOutboundGateway to send the response back to the device ?
Here is my code :
#EnableIntegration
#IntegrationComponentScan
#Configuration
public class TcpServerConfiguration {
private static final Logger logger = LogManager.getLogger(TcpServerConfiguration.class);
#Autowired
TcpServerProperties properties;
#Bean
public AbstractServerConnectionFactory serverFactory() {
TcpNetServerConnectionFactory tcpServerFactory = new TcpNetServerConnectionFactory(properties.getPort());
tcpServerFactory.setDeserializer(new ByteArrayRawSerializer());
return tcpServerFactory;
}
#Bean
public MessageChannel inputChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Bean
public MessageChannel outputChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Bean
public TcpInboundGateway tcpInGate(AbstractServerConnectionFactory connectionFactory) {
TcpInboundGateway inGateway = new TcpInboundGateway();
inGateway.setConnectionFactory(connectionFactory);
inGateway.setRequestChannel(inputChannel());
inGateway.setReplyChannel(outputChannel());
return inGateway;
}
}
The message handler class :
#MessageEndpoint
public class MessageHandler {
private static final Logger logger = LogManager.getLogger(MessageHandler.class);
#Transformer( inputChannel = "inputChannel", outputChannel = "outputChannel")
public byte[] consume(byte[] bytes) {
String message = new String(bytes);
String byteString = "";
for (byte b : bytes) {
byteString += Byte.toString(b) + ", ";
}
logger.info("Bytes : " + byteString);
logger.info("Message : " + message);
return new byte[]{ 01 };
}
}
Here I always send the Ack (01) to test if it's successfully sent which is not the case.
I added logging.level.org.springframework.integration=DEBUG and here is what I got :
2017-10-02 14:16:44.245 INFO 6340 --- [main] b.thingsplay.Fmb920TcpServerApplication : Started Fmb920TcpServerApplication in 3.885 seconds (JVM running for 5.38)
2017-10-02 14:16:44.250 INFO 6340 --- [pool-1-thread-1] .s.i.i.t.c.TcpNetServerConnectionFactory : serverFactory, port=7015 Listening
2017-10-02 14:17:50.161 DEBUG 6340 --- [pool-1-thread-1] .s.i.i.t.c.TcpNetServerConnectionFactory : Accepted connection from 1.1.1.1
2017-10-02 14:17:50.200 DEBUG 6340 --- [pool-1-thread-1] o.s.i.i.tcp.connection.TcpNetConnection : New connection ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912
2017-10-02 14:17:50.201 DEBUG 6340 --- [pool-1-thread-1] .s.i.i.t.c.TcpNetServerConnectionFactory : serverFactory: Added new connection: ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912
2017-10-02 14:17:50.204 DEBUG 6340 --- [pool-1-thread-2] o.s.i.i.tcp.connection.TcpNetConnection : ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912 Reading...
2017-10-02 14:17:50.207 DEBUG 6340 --- [pool-1-thread-2] o.s.i.i.t.s.ByteArrayRawSerializer : Available to read:17
2017-10-02 14:18:20.576 DEBUG 6340 --- [pool-1-thread-2] o.s.i.i.tcp.connection.TcpNetConnection : Message received GenericMessage [payload=byte[17], headers={ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=229435ad-6284-8136-40d4-4fdfe3ef462e, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700575}]
2017-10-02 14:18:20.585 INFO 6340 --- [pool-1-thread-2] o.s.i.endpoint.EventDrivenConsumer : Adding {bridge:null} as a subscriber to the 'outputChannel' channel
2017-10-02 14:18:20.587 INFO 6340 --- [pool-1-thread-2] o.s.integration.channel.DirectChannel : Channel 'application.outputChannel' has 1 subscriber(s).
2017-10-02 14:18:20.589 INFO 6340 --- [pool-1-thread-2] o.s.i.endpoint.EventDrivenConsumer : started org.springframework.integration.endpoint.EventDrivenConsumer#14c3d44a
2017-10-02 14:18:20.595 DEBUG 6340 --- [pool-1-thread-2] o.s.integration.channel.DirectChannel : preSend on channel 'inputChannel', message: GenericMessage [payload=byte[17], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=2cc3d793-dc24-705d-1438-d4b3e089d11b, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700594}]
2017-10-02 14:18:20.598 DEBUG 6340 --- [pool-1-thread-2] o.s.i.t.MessageTransformingHandler : messageHandler.consume.transformer.handler received message: GenericMessage [payload=byte[17], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=2cc3d793-dc24-705d-1438-d4b3e089d11b, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700594}]
2017-10-02 14:18:20.608 INFO 6340 --- [pool-1-thread-2] be.thingsplay.tcp.MessageHandler : Bytes : 0, 15, 51, 53, 50, 48, 57, 52, 48, 56, 51, 50, 54, 54, 52, 55, 53,
2017-10-02 14:18:20.611 INFO 6340 --- [pool-1-thread-2] be.thingsplay.tcp.MessageHandler : Message : 352094083266475
2017-10-02 14:18:20.618 DEBUG 6340 --- [pool-1-thread-2] o.s.integration.channel.DirectChannel : preSend on channel 'outputChannel', message: GenericMessage [payload=byte[1], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=0e5b6c37-ff73-5c46-4c37-0df48d3ba607, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700614}]
2017-10-02 14:18:20.620 DEBUG 6340 --- [pool-1-thread-2] o.s.integration.handler.BridgeHandler : org.springframework.integration.handler.BridgeHandler#5b393299 received message: GenericMessage [payload=byte[1], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=0e5b6c37-ff73-5c46-4c37-0df48d3ba607, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700614}]
2017-10-02 14:18:20.623 DEBUG 6340 --- [pool-1-thread-2] o.s.integration.channel.DirectChannel : postSend (sent=true) on channel 'outputChannel', message: GenericMessage [payload=byte[1], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=0e5b6c37-ff73-5c46-4c37-0df48d3ba607, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700614}]
2017-10-02 14:18:20.625 DEBUG 6340 --- [pool-1-thread-2] o.s.integration.channel.DirectChannel : postSend (sent=true) on channel 'inputChannel', message: GenericMessage [payload=byte[17], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#1a0438a2, ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=2cc3d793-dc24-705d-1438-d4b3e089d11b, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700594}]
2017-10-02 14:18:20.628 DEBUG 6340 --- [pool-1-thread-2] o.s.i.i.tcp.connection.TcpNetConnection : ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912 Message sent GenericMessage [payload=byte[1], headers={ip_tcp_remotePort=62138, ip_connectionId=ptr-x-x-x-x.dyn.mobistar.be:62138:7015:a7151dbf-63c1-4ff5-b158-5707797b8912, ip_localInetAddress=/0.0.0.0, ip_address=1.1.1.1, id=af87b495-fceb-dbdc-c768-a8a81391af75, ip_hostname=ptr-x-x-x-x.dyn.mobistar.be, timestamp=1506946700628}]
2017-10-02 14:18:20.631 DEBUG 6340 --- [pool-1-thread-2] o.s.i.i.t.s.ByteArrayRawSerializer : Available to read:0
I hope someone will be able to help me !
You use TcpInboundGateway correct way. Although you might not need setReplyChannel() option. In most cases it is fully enough to rely on the replyChannel in headers and jsut return from the #Transformer method will reach the proper "session" to reply.
I think you use wrong deserializer. See ByteArrayRawSerializer JavaDocs:
* A byte array (de)serializer that does nothing with the payload; sends it raw.
* Message termination for assembly purposes is signaled by the client closing the
* connection. The serializer does not, itself, close the connection after
* writing the bytes.
* <p>
* Because the socket must be closed to indicate message end, this (de)serializer
* can only be used by uni-directional (non-collaborating) channel adapters, and
* not by gateways.
So, since your client doesn't close the connection for end session, you don't get the reply there. More over this serializer isn't for gateways.
Consider to select the proper (de)serializer with appropriate terminator for you: https://docs.spring.io/spring-integration/docs/4.3.12.RELEASE/reference/html/ip.html#connection-factories
Related
I have a Tasklet and my problem is that the process inside the selectAndProcessData() method haven't finished running but the log have stated that this tasklet have been funished.
Inside the method there is a query that need to extract quite amount of data from DB (about 450++ records) but in the end, only 100+ records was being printed out.
And the line before the RepeatStatus.FINISHED do not printed out before the tasklet has been finished.
How can I make the process inside selectAndProcessData() method completely run before letting the tasklet return RepeatStatus.FINISHED?
My code:
public class ExtractCustomerInfoTasklet implements Tasklet {
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
selectAndProcessData();
return RepeatStatus.FINISHED;
}
private void selectAndProcessData(){
String customerQuery = "select * from customers where age > 21";
BeanPropertyRowMapper<Customer> beanPropertyRowMapper = new BeanPropertyRowMapper<>(Customer.class);
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
List<Customer> customerList = namedParameterJdbcTemplate.query(customerQuery, mapSqlParameterSource, beanPropertyRowMapper);
log.info("customerList's size: " + customerList.size());
if(CollectionUtils.isEmpty(customerList)) {
return;
}
Hashtable<String, Customer> customerHashtable = new Hashtable<>();
for(Customers customer : customerList){
if(!customerHashtable.containsKey(customer.getId())){
customerHashtable.put(customer.getId(), customer);
log.info("customer id: " + customer.getId());
}else{
log.info("customer id: " + customer.getId() + " not exist!");
}
}
log.info("customerHashtable's size: " + customerHashtable.size()); //this line do not printed out
}
}
Logs:
2022-10-05 21:25:25.987 INFO 4944 --- [nio-8082-exec-1]
o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob:
[name=ExtractCustomerInfoJob]] launched with the following parameters:
[{run.id=24}]
2022-10-05 21:25:26.103 INFO 4944 --- [nio-8082-exec-1]
c.f.b.j.c.listener.StartEndDateListener : [start Date]:Tue Oct 04
00:00:00 SGT 2022
2022-10-05 21:25:26.103 INFO 4944 --- [nio-8082-exec-1]
c.f.b.j.c.listener.StartEndDateListener : [end Date]:Wed Oct 05
00:00:00 SGT 2022
2022-10-05 21:25:26.103 INFO 4944 --- [nio-8082-exec-1]
c.f.batch.listener.ScheduleJobListener : Start Schedule Job History
Listener
2022-10-05 21:25:26.295 INFO 4944 --- [nio-8082-exec-1]
c.f.b.e.h.DefaultBatchEventHandler : Extract_Customer_Info_Job is
starting
2022-10-05 21:25:26.639 INFO 4944 --- [nio-8082-exec-1]
o.s.batch.core.job.SimpleStepHandler : Executing step: [[1] Extract
Customer Info from DB]
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customerList's size: 425
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0021011
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0084190
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0075917
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0068518
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0080279
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0073527
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0067744
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0078679
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0104133
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0043291
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0034689
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0097511
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0024483
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0059940
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0008263
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0132233
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0054388
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0108319
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0085711
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0136809
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0137462
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0038499
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0037966
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0137724
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0128406
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0137145
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0121859
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0111066
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0022478
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0046051
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0149903
2022-10-05 21:25:26.888 INFO 4944 --- [nio-8082-exec-1]
ExtractCustomerInfoTasklet : customer id: C0152974
2022-10-05 21:25:27.013 INFO 4944 --- [nio-8082-exec-1]
o.s.batch.core.step.AbstractStep : Step: [[1] Extract Customer
Info from DB] executed in 1s404ms
2022-10-05 21:25:27.365 INFO 4944 --- [nio-8082-exec-1]
c.f.b.e.h.DefaultBatchEventHandler : Extract_Customer_Info_Job has
completed
2022-10-05 21:25:28.738 INFO 4944 --- [nio-8082-exec-1]
o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob:
[name=Extract_Customer_Info_Job]] completed with the following
parameters: [{run.id=24}] and the following status: [COMPLETED] in
1s404ms
I have a use case where I am creating dynamic listeners based on API calls. After receiving a certain message, I am pausing the partition individually. For example, If I received a message with offset 100, then that partition will be paused. And this will be done for all partitions.
After all the partitions are paused, I want to stop the container.
This is my code -
public class OffsetBasedMessageListener implements ConsumerAwareMessageListener<String, String> {
#Override
public void onMessage(ConsumerRecord<String, String> consumerRecord, Consumer<?, ?> consumer) {
if (consumerRecord.offset() == 100) {
consumer.pause(Collections.singleton(new TopicPartition(consumerRecord.topic(),
consumerRecord.partition())));
}
}
Now, I have an EventListener configured which will be triggered when the container is Idle.
#EventListener
public void onIdle(ListenerContainerIdleEvent event) {
Collection<TopicPartition> collection = event.getContainer(ConcurrentMessageListenerContainer.class).getAssignedPartitions();
for (TopicPartition topicPartition: collection) {
System.out.println(
event.getContainer(ConcurrentMessageListenerContainer.class).isPartitionPaused(topicPartition));
}
}
So, what I am doing now is checking if all partitions are paused then I'll stop the container. But isPartitionPaused always returns false, even though it should return true.
I am using SpringBoot with SpringKafka.
Can someone tell me what I am doing wrong? or Is there another way to achieve this?
Thanks
You shouldn't pause the consumer directly - the container doesn't know that you have paused it; hence it returns false.
Call pausePartition on the container instead.
Note that either way, the consumer won't actually pause until all the records from the previous poll are processed. Set max.poll.records to 1 to pause immediately.
EDIT
This works just fine for me...
#SpringBootApplication
#RestController
public class So67430862Application {
private static final Logger LOG = LoggerFactory.getLogger(So67430862Application.class);
public static void main(String[] args) {
SpringApplication.run(So67430862Application.class, args);
}
Set<ConcurrentMessageListenerContainer<String, String>> containers = ConcurrentHashMap.newKeySet();
int id;
#Autowired
Creator creator;
#PostMapping(path = "/send/{topic}")
public void sendFoo(#PathVariable String topic) {
ConcurrentMessageListenerContainer<String, String> container = this.creator.create(topic);
container.getContainerProperties().setGroupId("group" + ++id);
container.getContainerProperties().setMessageListener(new Listener(container));
this.containers.add(container);
container.start();
}
#EventListener
public void idle(ListenerContainerIdleEvent event) {
ConcurrentMessageListenerContainer<String, String> container =
event.getContainer(ConcurrentMessageListenerContainer.class);
if (this.containers.contains(container)) {
boolean allPaused = container.getAssignedPartitions()
.stream()
.map(part -> container.isPartitionPaused(part))
.allMatch(paused -> paused);
LOG.info("All paused? {}", allPaused);
if (allPaused) {
container.stop(() -> { });
this.containers.remove(container);
}
}
}
// #Bean
// public NewTopic topic() {
// return TopicBuilder.name("so67430862").partitions(5).replicas(1).build();
// }
//
//
// #Bean
// public ApplicationRunner runner(KafkaTemplate<String, String> template) {
// return args -> {
// IntStream.range(0, 5).forEach(p -> {
// IntStream.range(0, 10).forEach(i ->template.send("so67430862", p, null, "test"));
// });
// };
// }
}
#Component
class Creator {
private final ConcurrentKafkaListenerContainerFactory<String, String> factory;
Creator(ConcurrentKafkaListenerContainerFactory<String, String> factory) {
this.factory = factory;
}
ConcurrentMessageListenerContainer<String, String> create(String topic) {
return this.factory.createContainer(topic);
}
}
class Listener extends AbstractConsumerSeekAware implements MessageListener<String, String> {
private static final Logger LOG = LoggerFactory.getLogger(Listener.class);
final ConcurrentMessageListenerContainer<String, String> container;
Listener(ConcurrentMessageListenerContainer<String, String> container) {
this.container = container;
}
#Override
public void onMessage(ConsumerRecord<String, String> data) {
LOG.info(ListenerUtils.recordToString(data, true));
if (data.offset() == 5) {
LOG.info("Pausing partition {}", data.partition());
this.container.pausePartition(new TopicPartition(data.topic(), data.partition()));
}
}
#Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
callback.seekToBeginning(assignments.keySet());
}
}
spring.kafka.consumer.max-poll-records=1
spring.kafka.listener.idle-event-interval=5000
2021-05-10 11:51:15.792 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#0
2021-05-10 11:51:15.797 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#1
2021-05-10 11:51:15.799 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#2
2021-05-10 11:51:15.801 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#3
2021-05-10 11:51:15.803 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#4
2021-05-10 11:51:15.809 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-0#5
2021-05-10 11:51:15.809 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : Pausing partition 0
2021-05-10 11:51:15.814 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#0
2021-05-10 11:51:15.816 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#1
2021-05-10 11:51:15.819 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#2
2021-05-10 11:51:15.823 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#3
2021-05-10 11:51:15.828 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#4
2021-05-10 11:51:15.831 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-1#5
2021-05-10 11:51:15.831 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : Pausing partition 1
2021-05-10 11:51:15.835 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#0
2021-05-10 11:51:15.838 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#1
2021-05-10 11:51:15.841 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#2
2021-05-10 11:51:15.844 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#3
2021-05-10 11:51:15.846 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#4
2021-05-10 11:51:15.849 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-4#5
2021-05-10 11:51:15.850 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : Pausing partition 4
2021-05-10 11:51:15.854 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#0
2021-05-10 11:51:15.858 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#1
2021-05-10 11:51:15.861 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#2
2021-05-10 11:51:15.864 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#3
2021-05-10 11:51:15.866 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#4
2021-05-10 11:51:15.871 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-2#5
2021-05-10 11:51:15.871 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : Pausing partition 2
2021-05-10 11:51:15.875 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#0
2021-05-10 11:51:15.877 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#1
2021-05-10 11:51:15.880 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#2
2021-05-10 11:51:15.883 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#3
2021-05-10 11:51:15.886 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#4
2021-05-10 11:51:15.889 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : so67430862-3#5
2021-05-10 11:51:15.889 INFO 58682 --- [ consumer-0-C-1] com.example.demo.Listener : Pausing partition 3
2021-05-10 11:51:20.893 INFO 58682 --- [ consumer-0-C-1] com.example.demo.So67430862Application : All paused? true
2021-05-10 11:51:20.895 INFO 58682 --- [ consumer-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-group1-1, groupId=group1] Revoke previously assigned partitions so67430862-0, so67430862-1, so67430862-4, so67430862-2, so67430862-3
2021-05-10 11:51:20.895 INFO 58682 --- [ consumer-0-C-1] o.s.k.l.KafkaMessageListenerContainer : group1: partitions revoked: [so67430862-0, so67430862-1, so67430862-4, so67430862-2, so67430862-3]
2021-05-10 11:51:20.895 INFO 58682 --- [ consumer-0-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-group1-1, groupId=group1] Member consumer-group1-1-be137c2c-2bbf-4235-a143-15d33a0cfe52 sending LeaveGroup request to coordinator localhost:9092 (id: 2147483647 rack: null) due to the consumer unsubscribed from all topics
2021-05-10 11:51:20.896 INFO 58682 --- [ consumer-0-C-1] o.a.k.clients.consumer.KafkaConsumer : [Consumer clientId=consumer-group1-1, groupId=group1] Unsubscribed all topics or patterns and assigned partitions
2021-05-10 11:51:20.896 INFO 58682 --- [ consumer-0-C-1] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService
2021-05-10 11:51:20.897 INFO 58682 --- [ consumer-0-C-1] org.apache.kafka.common.metrics.Metrics : Metrics scheduler closed
2021-05-10 11:51:20.897 INFO 58682 --- [ consumer-0-C-1] org.apache.kafka.common.metrics.Metrics : Closing reporter org.apache.kafka.common.metrics.JmxReporter
2021-05-10 11:51:20.898 INFO 58682 --- [ consumer-0-C-1] org.apache.kafka.common.metrics.Metrics : Metrics reporters closed
2021-05-10 11:51:20.901 INFO 58682 --- [ consumer-0-C-1] o.a.kafka.common.utils.AppInfoParser : App info kafka.consumer for consumer-group1-1 unregistered
2021-05-10 11:51:20.902 INFO 58682 --- [ consumer-0-C-1] essageListenerContainer$ListenerConsumer : group1: Consumer stopped
The commented out code creates a topic with 5 partitions and 10 records in each; the listener pauses each partition at offset 5. As you can see we don't get the rest of the records.
I'm attempting to create a simple Post request handler in Spring Boot that consumes JSON.
#RestController
#EnableWebMvc
public class WebFormController
{
#RequestMapping(path="/process-contact-form", method = RequestMethod.POST)
public ResponseEntity<String> processContact(#RequestBody Form form) throws Exception
{
System.out.println("TEST TEST: Ran");
return new ResponseEntity<String>("Thank you for contacting us, we'll respond soon.", HttpStatus.OK);
}
}
By default, my understanding is this will consume application/json.
However, when I make a request using curl or Postman, I get this response from Spring...
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
And in Postman I get '415 Unsupported Media Type'.
If I explicitly define the 'consumes' property...
#RequestMapping(path="/process-contact-form", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)
Spring Boot returns this...
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported
I've tried setting consumes to "application/json", x-www-form-urlencoded, and plain text and sending those types of requests from Postman, but I get the same results.
I built this off aws-serverless-java-container https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot and I'm running it from the SAM local CLI.
EDIT
I switched logging to DEBUG to get more details...
2020-05-08 17:53:40.692 DEBUG 1 --- [ main] s.b.w.s.f.OrderedCharacterEncodingFilter : Filter 'characterEncodingFilter' configured for use
2020-05-08 17:53:40.692 DEBUG 1 --- [ main] c.a.s.p.i.servlet.FilterChainHolder : Starting REQUEST: filter 0-characterEncodingFilter
2020-05-08 17:53:40.698 DEBUG 1 --- [ main] c.a.s.p.i.s.AwsProxyHttpServletRequest : Called set character encoding to UTF-8 on a request without a content type. Character encoding will not be set
2020-05-08 17:53:40.698 DEBUG 1 --- [ main] c.a.s.p.i.servlet.FilterChainHolder : Starting REQUEST: filter 2-com.amazonaws.serverless.proxy.internal.servlet.FilterChainManager$ServletExecutionFilter
2020-05-08 17:53:40.729 DEBUG 1 --- [ main] o.s.web.servlet.DispatcherServlet : POST "/process-contact-form", parameters={}
2020-05-08 17:53:40.732 DEBUG 1 --- [ main] c.a.s.p.i.servlet.AwsHttpServletRequest : Trying to access session. Lambda functions are stateless and should not rely on the session
2020-05-08 17:53:40.758 DEBUG 1 --- [ main] c.a.s.p.i.s.AwsHttpServletResponse : Response buffer flushed with 0 bytes, latch=1
2020-05-08 17:53:40.761 WARN 1 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported]
2020-05-08 17:53:40.761 DEBUG 1 --- [ main] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 415
2020-05-08 17:53:40.763 DEBUG 1 --- [ main] c.a.s.p.i.servlet.AwsHttpServletRequest : Trying to access session. Lambda functions are stateless and should not rely on the session
2020-05-08 17:53:40.768 DEBUG 1 --- [ main] c.a.s.p.i.servlet.FilterChainHolder : Executed ERROR: filter 3-com.amazonaws.serverless.proxy.internal.servlet.FilterChainManager$ServletExecutionFilter
2020-05-08 17:53:40.769 DEBUG 1 --- [ main] c.a.s.p.i.servlet.FilterChainHolder : Executed ERROR: filter 3-characterEncodingFilter
2020-05-08 17:53:40.779 INFO 1 --- [ main] c.a.s.p.internal.LambdaContainerHandler : 127.0.0.1:56893 null- null [08/05/2020:17:53:40Z] "POST /process-contact-form null" 415 - "-" "-" combined
2020-05-08 17:53:40.818 DEBUG 1 --- [ main] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader#c9d82f99 pairs: {POST /2018-06-01/runtime/invocation/807c7e06-86f6-1bd3-1055-78b464604573/response HTTP/1.1: null}{Docker-Lambda-Invoke-Wait: 1588960412475}{Docker-Lambda-Init-End: 1588960420587}{User-Agent: Java/1.8.0_201}{Host: 127.0.0.1:9001}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive}{Content-type: application/x-www-form-urlencoded}{Content-Length: 104}
2020-05-08 17:53:40.822 DEBUG 1 --- [ main] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader#6f0129144 pairs: {null: HTTP/1.1 202 Accepted}{Content-Type: application/json}{Date: Fri, 08 May 2020 17:53:40 GMT}{Transfer-Encoding: chunked}
2020-05-08 17:53:40.825 DEBUG 1 --- [ main] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader#18fdb6cf5 pairs: {GET /2018-06-01/runtime/invocation/next HTTP/1.1: null}{User-Agent: Java/1.8.0_201}{Host: 127.0.0.1:9001}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive}
EDIT
StreamLambdaHandler
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
System.out.println("TEST TEST " + inputStream.toString() + " " + outputStream.toString());
handler.proxyStream(inputStream, outputStream, context);
}
}
I had some assistance starting the app from scratch, and using different libraries. The annotation looks like this now..
#Path("/submit")
#Component
#Slf4j
public class SubmitController {
private JiraService jira;
private ConfigurationRepository configurations;
#Autowired
public SubmitController(JiraService jira, ConfigurationRepository configurations) {
this.jira = jira;
this.configurations = configurations;
}
#POST
#Path("/{id}")
#AnonymousAllowed
#Produces({APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response submitForm(#PathParam("id") String id, String body) {
// Stuff
}
I have this easy pipeline configuration. Just trying to tinker around with Spring Integration. However, the output is weird.
This is my code:
#Slf4j
#Configuration
#RequiredArgsConstructor
public class FileToConsoleIntegration {
#Bean public IntegrationFlow fileToConsoleIntegrationFlow() {
return IntegrationFlows.from(sameSourceDirectory(), spec -> spec.poller(this::getPollerSpec))
.log(LoggingHandler.Level.WARN, "before.sort", m -> m.getHeaders().get("file_name"))
.channel(MessageChannels.queue(5))
.log(LoggingHandler.Level.INFO, "after.sort", m -> m.getHeaders().get("file_name"))
.channel(alphabeticallyReversed())
.log(LoggingHandler.Level.ERROR, "after.sort", m -> m.getHeaders().get("file_name"))
.handle(logInfoMessageHandler())
.get();
}
private PollerSpec getPollerSpec(PollerFactory p) {
return p
.fixedRate(1000)
.maxMessagesPerPoll(10);
}
#Bean public MessageSource<File> sameSourceDirectory() {
FileReadingMessageSource messageSource = new FileReadingMessageSource();
messageSource.setDirectory(new File("input_dir"));
return messageSource;
}
#Bean public MessageHandler logInfoMessageHandler() {
return message ->
log.info("Handling message with headers: {} and payload: {}",
message.getHeaders(), message.getPayload());
}
#Bean
public PriorityChannel alphabeticallyReversed() {
return MessageChannels.priority()
.capacity(5)
.comparator(Comparator.comparing(getFilename(), Comparator.reverseOrder()))
.get();
}
private Function<Message<?>, String> getFilename() {
return a -> (String) a.getHeaders().get("file_name");
}
}
This is my input:
/input_dir
01_zhasdfha.txt
02_usfhahjf.txt
05_bsdfasdf.txt
06_asdfasdf.txt
This is my output:
2019-10-10 17:24:48.604 WARN 46024 --- [ask-scheduler-1] before.sort : 01_zhasdfha.txt
2019-10-10 17:24:48.605 INFO 46024 --- [ask-scheduler-1] after.sort : 01_zhasdfha.txt
2019-10-10 17:24:48.605 ERROR 46024 --- [ask-scheduler-2] after.sort : 01_zhasdfha.txt
2019-10-10 17:24:48.605 WARN 46024 --- [ask-scheduler-1] before.sort : 02_usfhahjf.txt
2019-10-10 17:24:48.605 INFO 46024 --- [ask-scheduler-1] after.sort : 02_usfhahjf.txt
2019-10-10 17:24:48.606 WARN 46024 --- [ask-scheduler-1] before.sort : 05_bsdfasdf.txt
2019-10-10 17:24:48.606 ERROR 46024 --- [ask-scheduler-2] after.sort : 02_usfhahjf.txt
2019-10-10 17:24:48.606 INFO 46024 --- [ask-scheduler-1] after.sort : 05_bsdfasdf.txt
2019-10-10 17:24:48.606 ERROR 46024 --- [ask-scheduler-2] after.sort : 05_bsdfasdf.txt
2019-10-10 17:24:48.606 WARN 46024 --- [ask-scheduler-1] before.sort : 06_asdfasdf.txt
2019-10-10 17:24:48.606 INFO 46024 --- [ask-scheduler-1] after.sort : 06_asdfasdf.txt
2019-10-10 17:24:48.606 ERROR 46024 --- [ask-scheduler-2] after.sort : 06_asdfasdf.txt
2019-10-10 17:24:48.605 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/01_zhasdfha.txt, id=5fcfb1cc-7187-86a8-dcf4-23e9b31b2376, file_name=01_zhasdfha.txt, file_relativePath=01_zhasdfha.txt, timestamp=1570728288603} and payload: input_dir/01_zhasdfha.txt
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/06_asdfasdf.txt, id=cf1a978b-c577-bb14-cf45-cf5b7ce8f2a7, file_name=06_asdfasdf.txt, file_relativePath=06_asdfasdf.txt, timestamp=1570728288606} and payload: input_dir/06_asdfasdf.txt
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/05_bsdfasdf.txt, id=c81dbcdc-6dd0-b549-4923-fe567ff6ca23, file_name=05_bsdfasdf.txt, file_relativePath=05_bsdfasdf.txt, timestamp=1570728288606} and payload: input_dir/05_bsdfasdf.txt
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/02_usfhahjf.txt, id=3371143b-a097-a9ee-e303-d4d359875188, file_name=02_usfhahjf.txt, file_relativePath=02_usfhahjf.txt, timestamp=1570728288605} and payload: input_dir/02_usfhahjf.txt
2019-10-10 17:24:48.609 INFO 46024 --- [ main] c.s.i.siexample.SiExampleApplication : Started SiExampleApplication in 0.743 seconds (JVM running for 1.232)
Desired output:
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/06_asdfasdf.txt, id=cf1a978b-c577-bb14-cf45-cf5b7ce8f2a7, file_name=06_asdfasdf.txt, file_relativePath=06_asdfasdf.txt, timestamp=1570728288606} and payload: input_dir/06_asdfasdf.txt
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/05_bsdfasdf.txt, id=c81dbcdc-6dd0-b549-4923-fe567ff6ca23, file_name=05_bsdfasdf.txt, file_relativePath=05_bsdfasdf.txt, timestamp=1570728288606} and payload: input_dir/05_bsdfasdf.txt
2019-10-10 17:24:48.607 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/02_usfhahjf.txt, id=3371143b-a097-a9ee-e303-d4d359875188, file_name=02_usfhahjf.txt, file_relativePath=02_usfhahjf.txt, timestamp=1570728288605} and payload: input_dir/02_usfhahjf.txt
2019-10-10 17:24:48.605 INFO 46024 --- [ask-scheduler-3] c.s.i.s.i.FileToConsoleIntegration : Handling message with headers: {file_originalFile=input_dir/01_zhasdfha.txt, id=5fcfb1cc-7187-86a8-dcf4-23e9b31b2376, file_name=01_zhasdfha.txt, file_relativePath=01_zhasdfha.txt, timestamp=1570728288603} and payload: input_dir/01_zhasdfha.txt
The difference between the desired output and current output is that the order is messed. How can I fix that?
Just because you have set the queue sizes to 5, doesn't mean the framework will wait for all 5 before the downstream poller pulls them. If there is a poller waiting for a message (the default is to wait up to 1 second on each poll), it will be received immediately. You could try setting the poller's receive timeout to 0 but there will still be a (small) race condition where messages can be fetched earlier than you expected.
It would be better to use an aggregator followed by a transformer to sort the List<> (or a custom output processor on the aggregator) and then a splitter.
I'm trying to write a basic application with rest api's connecting with a postgres database to perform couple of crud operations. When I run the application it starts successfully. But when I go to the url "http://localhost:8080/employees" or http://localhost:8080", it shows "This localhost page can’t be found. No webpage was found for the web address: http://localhost:8080/employees. HTTP ERROR 404".
Here is my TestApplication.java
package com.ranjana.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.http.HttpStatus;
#SpringBootApplication
#EntityScan(basePackages = {"com.ranjana.test.model"})
#ComponentScan(basePackageClasses = EmployeeController.class)
public class TestApplication implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
#Override
public void customize(ConfigurableServletWebServerFactory factory) {
factory.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/errors/403.html"));
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404.html"));
factory.addErrorPages(new ErrorPage("/errors/500.html"));
}
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
The controller class - EmployeeController.java :
package com.ranjana.test.controller;
import com.ranjana.test.repositoy.EmployeeRepo;
import com.ranjana.test.exception.ResourceNotFoundException;
import com.ranjana.test.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#RestController
#RequestMapping("/api/v1")
public class EmployeeController {
#Autowired
private EmployeeRepo empRepo;
#GetMapping("/employees")
public List<Employee> getAllEmployees(){
System.out.printf(empRepo.findAll().toString());
return empRepo.findAll();
}
#GetMapping("/employee/{id}")
public ResponseEntity<Employee> getUsersById(#PathVariable(value = "id") Long emplId)
throws ResourceNotFoundException {
Employee employee = empRepo.findById(emplId).orElseThrow(() -> new ResourceNotFoundException("Employee not found for id: " + emplId));
return ResponseEntity.ok().body(employee);
}
#PostMapping("/employees")
public Employee createEmployee(#Valid #RequestBody Employee employee){
System.out.println(employee.toString());
return empRepo.save(employee);
}
#PutMapping("/employee/{id}")
public ResponseEntity<Employee> updateEmployee(#PathVariable(value = "id") Long emplId, #Valid #RequestBody Employee empDetails)
throws ResourceNotFoundException{
Employee employee = empRepo.findById(emplId).orElseThrow(() -> new ResourceNotFoundException("Employee not found for id: " + emplId ));
employee.setFirstName(empDetails.getFirstName());
employee.setLastName(empDetails.getLastName());
employee.setEmailId(empDetails.getEmailId());
employee.setContactNumber(empDetails.getContactNumber());
employee.setUpdateDateTime(new Date());
final Employee updatedEmployee = empRepo.save(employee);
return ResponseEntity.ok(updatedEmployee);
}
#DeleteMapping("/employee/{id}")
public Map<String, Boolean> deleteEmployee(#PathVariable(value = "id") Long emplId)
throws Exception{
Employee employee = empRepo.findById(emplId).orElseThrow(() -> new ResourceNotFoundException("Employee not found for id: " + emplId));
empRepo.delete(employee);
Map <String, Boolean> response= new HashMap<>();
response.put("Deleted", Boolean.TRUE);
return response;
}
}
The model - Employee.java:
package com.ranjana.test.model;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "employee")
public class Employee {
//Employee Id
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
//Employee First Name
#Column(name = "first_name", nullable = false)
private String firstName;
//Employee Last Name
#Column(name = "last_name", nullable = false)
private String lastName;
//Employee Email Address
#Column(name = "email", nullable = true)
private String emailId;
//Employee Contact Number
#Column(name = "contact_number", nullable = true)
private String contactNumber;
//Creation Timestamp
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "create_date_time", nullable = false)
private Date createDateTime;
//Update Timestamp
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "update_date_time", nullable = true)
private Date updateDateTime;
//Getters and Setters
#Override
public String toString(){
return "Employee{"
+ "id = " + id + '\''
+ "firstName" + firstName + '\''
+ "lastName" + lastName + '\''
+ "email" + emailId + '\''
+ "phone" + contactNumber + '\''
+ "createDateTime" + createDateTime + '\''
+ "updateDateTime" + updateDateTime + '\''
+ "}";
}
}
The console looks like following:
2019-08-05 13:27:18.327 INFO 9943 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-08-05 13:27:18.332 INFO 9943 --- [ restartedMain] com.ranjana.test.TestApplication : Started TestApplication in 3.678 seconds (JVM running for 9.357)
2019-08-05 13:27:18.340 DEBUG 9943 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Creating new Restarter for thread Thread[main,5,main]
2019-08-05 13:27:18.340 DEBUG 9943 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Immediately restarting application
2019-08-05 13:27:18.340 DEBUG 9943 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader#51171eea
2019-08-05 13:27:18.340 DEBUG 9943 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Starting application com.ranjana.test.TestApplication with URLs [file:/Users/ranjanasinha/ransinha/test/target/classes/]
2019-08-05 13:27:18.714 DEBUG 9943 --- [2)-10.36.30.147] o.s.jdbc.core.JdbcTemplate : Executing SQL query [SELECT 1]
2019-08-05 13:27:18.714 INFO 9943 --- [3)-10.36.30.147] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-08-05 13:27:18.714 INFO 9943 --- [3)-10.36.30.147] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-08-05 13:27:18.715 DEBUG 9943 --- [3)-10.36.30.147] o.s.web.servlet.DispatcherServlet : Detected StandardServletMultipartResolver
2019-08-05 13:27:18.724 DEBUG 9943 --- [3)-10.36.30.147] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2019-08-05 13:27:18.724 INFO 9943 --- [3)-10.36.30.147] o.s.web.servlet.DispatcherServlet : Completed initialization in 9 ms
2019-08-05 13:27:31.581 DEBUG 9943 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/employees", parameters={}
2019-08-05 13:27:31.587 DEBUG 9943 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:31.591 DEBUG 9943 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:31.591 DEBUG 9943 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
2019-08-05 13:27:31.599 DEBUG 9943 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/errors/404.html", parameters={}
2019-08-05 13:27:31.601 DEBUG 9943 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:31.602 DEBUG 9943 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:31.602 DEBUG 9943 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404
2019-08-05 13:27:35.827 DEBUG 9943 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET "/", parameters={}
2019-08-05 13:27:35.830 DEBUG 9943 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:35.830 DEBUG 9943 --- [nio-8080-exec-2] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:35.830 DEBUG 9943 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
2019-08-05 13:27:35.831 DEBUG 9943 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/errors/404.html", parameters={}
2019-08-05 13:27:35.833 DEBUG 9943 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:35.834 DEBUG 9943 --- [nio-8080-exec-2] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:35.834 DEBUG 9943 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404
2019-08-05 13:27:38.056 DEBUG 9943 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET "/", parameters={}
2019-08-05 13:27:38.058 DEBUG 9943 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:38.058 DEBUG 9943 --- [nio-8080-exec-3] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:38.058 DEBUG 9943 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
2019-08-05 13:27:38.059 DEBUG 9943 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/errors/404.html", parameters={}
2019-08-05 13:27:38.060 DEBUG 9943 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
2019-08-05 13:27:38.062 DEBUG 9943 --- [nio-8080-exec-3] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2019-08-05 13:27:38.062 DEBUG 9943 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404
I'm new to spring boot. Please help me figure out what I'm missing.
Also the table got created. And I checked with manually putting data in the table, still the 404 not found error is showing. Thanks in advance.
You should call like http://localhost:8080/api/v1/employees
Because you have created requestmapping("API/v1")