I want to build a headless application which will query the DB in infinite loop and perform some operations in certain conditions (e.g. fetch records with specific values and when found launch e-mail sending procedure for each message).
I want to use Spring Boot as a base (especially because of Actuator to allow expose health-checks), but for now I used Spring Boot for building REST web-services.
Is there any best practices or patterns to follow when building infinite loop applications ? Does anyone tried to build it based on Spring Boot and can share with me his architecture for this case ?
Best regards.
Do not implement an infinite loop yourself. Let the framework handle it using its task execution capabilities:
#Service
public class RecordChecker{
//Executes each 500 ms
#Scheduled(fixedRate=500)
public void checkRecords() {
//Check states and send mails
}
}
Don't forget to enable scheduling for your application:
#SpringBootApplication
#EnableScheduling
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
See also:
Scheduling Tasks
What I'm using is a message broker and a consumer put at the spring boot application to do the job.
There are several options. My approach is to start a loop on an ApplicationReadyEvent, and abstract away the loop logic into an injectable service. In my case it was a game loop, but this pattern should work for you as well.
package com.ryanp102694.gameserver;
import com.ryanp102694.gameserver.service.GameProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class GameLauncher implements ApplicationListener<ApplicationReadyEvent> {
private static Logger logger = LoggerFactory.getLogger(GameLauncher.class);
private GameProcessor gameProcessor;
#Autowired
public GameLauncher(GameProcessor gameProcessor){
this.gameProcessor = gameProcessor;
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
logger.info("Starting game process.");
gameProcessor.start();
while(gameProcessor.isRunning()){
logger.debug("Collecting user input.");
gameProcessor.collectInput();
logger.debug("Calculating next game state.");
gameProcessor.nextGameState();
logger.debug("Updating clients.");
gameProcessor.updateClients();
}
logger.info("Stopping game process.");
gameProcessor.stop();
}
}
Related
A web application I've been working on recently the past like 2 weeks maybe for whatever reason when I finally tested it - won't seem to even enter the method that I have to return a JSON list of objects. I have included the Jackson library and Spring Boot Web, Tomcat, Data-JPA, Hibernate, MySQL, and a library to allow me to access JSP files. The index.jsp comes up but I almost feel like Spring Boot is giving me that free of charge as it's not even entering that method. I have been having the issue for a few days but trying to resolve it on my own - I found another answer that suggested to put a breakpoint inside one of the Spring classes but when I "debugged" it through Eclipse, it didn't even stop at that class - something about pattern matching - One answer suggested adding a context to the application.properties file - didn't help. I've reduced it to as simple as I think I can get it. Can anyone tell me what I might be doing wrong? Before my code, the project is on Github at: https://github.com/sfulmer/Scheduler.git
Here's my controller:
package net.draconia.schedule.controllers;
import java.util.List;
import net.draconia.schedule.beans.Event;
import net.draconia.schedule.dao.EventDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class ScheduleController
{
private static final Logger logger = LoggerFactory.getLogger(ScheduleController.class);
#Autowired
private EventDAO mObjDAO;
protected EventDAO getDAO()
{
return(mObjDAO);
}
//#GetMapping("/events")
#RequestMapping(value = "events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents()
{
logger.debug("I got here");
return(getDAO().getList());
}
#GetMapping("/")
public String index()
{
return("index");
}
}
Here is the DAO interface - I'll show the class if necessary but this is what the controller looks at:
package net.draconia.schedule.dao;
import java.util.List;
import javax.persistence.EntityNotFoundException;
import net.draconia.schedule.beans.Event;
public interface EventDAO
{
public Event getEventById(final long lId) throws EntityNotFoundException;
public List<Event> getList();
public void remove(final Event objEvent);
public void removeById(final long lId);
public Event save(final Event objEvent);
}
The Event class is so long but if I need to include it, I will. The application.properties file is here:
spring.datasource.url = jdbc:mysql://localhost:3306/schedule
spring.datasource.username = root
spring.datasource.password = R3g1n# M1lL$ 1$ My Qu3eN!
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
server.servlet.contextPath=/scheduler
and here is my Application class(with the SpringBootApplication annotation):
package net.draconia.schedule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
public class ScheduleApp implements WebMvcConfigurer
{
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return(builder.sources(ScheduleApp.class));
}
public static void main(String[] args)
{
SpringApplication.run(ScheduleApp.class, args);
}
}
I'm relatively new to Spring Boot but haven't ever ran into this problem ever before as I work with it at work and it works fine but we use entirely REST services there and I am using JSP files as well as sorta end-points that respond with JSON but you can't respond from REST services with JSP views so unfortunately I can't copy work's project to get that working or I would sigh Any thoughts on how I can get this working or what I am omitting?
My guess is that you're mixing things from Spring and Spring boot, and that's getting problems on loading beans, as you're probably changing the annotations load order or loading other beans rather than spring boot defaults as expected. For example, you implements WebMvcConfigurer, but you aren't providing any WebMvc Configuration, like a ViewResolver bean
My advice is to follow this guide: https://spring.io/guides/gs/spring-boot/
and use only the annotations from spring boot if using spring boot, or spring if using spring (they're similar, but not exactly the same, configuration is different).
Anyways, you can check loaded beans in Spring application context (Inject it in Application class) with ctx.getBeanDefinitionNames() method and see if your controller is there (i guess not).
By looking into code, my first impression is that, you have some typo in here:
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
Your controller class package name has net.draconia.schedule.controllers.
So can you please correct your scanBasePackages with proper package name.
If that is not the case, please update full stack trace along with GET request which you are submitting into application. Will take a look & update answer accordingly.
I have a Camel endpoint which is basically a Kafka Consumer reading from a topic and sending the information to a database. It is working fine, however, I am struggling to unit test it as I haven't been able to mock the Kafka endpoint. Can anyone help me in mocking a Kafka Consumer in a Camel Route?
#Override
public void configure() {
from(kafka:eph?brokers=localhost:9092...).routeId("KafkaConsumer")
.to(direct:updateDatabase)
}
To unit test your route, you may do that with a standard camel spring boot test.
During the test, the Kafka producer(in Camel's view) can be swapped in with a direct component and mock messages can be delivered there. To see if your routes are processing those messages properly, Mock endpoints can be used.
//Route definition
#Component
public class KafkaRoute extends RouteBuilder {
public static final String KAFKA_ROUTE_NAME = "kafka-route";
#Override
public void configure() throws Exception {
from("kafka:eph?brokers=localhost:9092").routeId(KAFKA_ROUTE_NAME)
.log(LoggingLevel.INFO, "Message: ${body} received on the topic: ${headers[kafka.TOPIC]} ")
.to("direct:updateDatabase");
from("direct:updateDatabase").log(LoggingLevel.INFO, "DB Updated.");
}
}
import java.util.HashMap;
import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.component.kafka.KafkaConstants;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.CamelSpringBootRunner;
import org.apache.camel.test.spring.MockEndpoints;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#MockEndpoints("direct:*")
public class KafkaRouteTest {
#Autowired
CamelContext camelContext;
#Produce
ProducerTemplate mockKafkaProducer;
#EndpointInject("mock:direct:updateDatabase")
MockEndpoint finalSink;
#Test
public void testKafkaRoute() throws Exception {
//Here we swap the FROM component in the KafkaRoute.KAFKA_ROUTE_NAME with a direct component, direct:kafka-from
AdviceWithRouteBuilder.adviceWith(camelContext, KafkaRoute.KAFKA_ROUTE_NAME, routeBuilder -> {
routeBuilder.replaceFromWith("direct:kafka-from");
});
Map<String, Object> headers = new HashMap<>();
headers.put(KafkaConstants.TOPIC, "testTopic");
//Send mock message to the route
mockKafkaProducer.sendBodyAndHeaders("direct:kafka-from", "test-body", headers);
//Assertions. You may do additional assertions with the likes of Mockito
finalSink.expectedBodiesReceived("test-body");
finalSink.expectedHeaderReceived(KafkaConstants.TOPIC, "testTopic");
finalSink.assertIsSatisfied();
}
}
Camel Kafka component is already unit tested, there is no point in replicating all those tests in your code base. However, if you really want to do testing against a real Kafka instance, you may use test containers. Here is a full blown example, from the Camel repository itself, using test containers.
Simply externalize the endpoint URI in a property (for example with Spring Property facility)
from(consumerEndpoint).routeId("KafkaConsumer")
Then in your production configuration, you use the real endpoint
consumerEndpoint=kafka:eph?brokers=localhost:9092...
Whereas in your test configuration, you use a direct endpoint
consumerEndpoint=direct:consumer
This one is easy to trigger from a Camel route test
producer.sendBody("direct:consumer", myMessageBody);
If starting from scratch, with only requirements of JVM and existing MySQL database of medium complexity, and with the goal of doing only REST, nothing else, what is a good example of components I should use?
Want to keep it as simple as possible.
A few simple options can be:
Spark Framework, Sinatra-inspired + uses nice Java 8 features. Quick start example:
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello World");
}
}
Spring Boot, a simple way to start in Spring ecosystem. Quick start:
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
#Controller
#EnableAutoConfiguration
public class SampleController {
#RequestMapping("/")
#ResponseBody
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleController.class, args);
}
}
Spring has Spring MVC module for building RETSful APIs, here's an example: http://spring.io/guides/gs/rest-service/
You can also take a look at other JVM languages, for example Play Framework in Scala or Grails in Groovy.
UPDATE
I forgot to mention Dropwizard. It uses JAX-RS for RESTful APIs, which can look very verbose, but it's very mature and stable. Here's quick start guide: http://www.dropwizard.io/0.9.2/docs/getting-started.html
I'm needing to make a service that schedules jobs that are basically get requests that hit some servlet. I tried to do this w/ a servlet context listener based on this post, Running a background Java program in Tomcat, but the web.xml changes that were defined are causing 404 errors on the Tomcat server. Does anyone have any other suggestions on how to accomplish this?
One idea I have at this point is to define a runnable servlet
public class Service extends HttpServlet implements Runnable {
//Does stuff
init() {
new Thread(this);
}
}
Is this a reasonable approach?
you can look into using quartz scheduler for jobs :
http://quartz-scheduler.org/
for instance (not specific to your task):
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SchedulerJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
Map dataMap = context.getJobDetail().getJobDataMap();
SchedulerTask task = (SchedulerTask)dataMap.get("schedulerTask");
task.printSchedulerMessage();
}
}
Another option (for a quick turaround) would be to just use a cron job or windows task manager depending on your OS.
I would like a class analogous to spring's ContextLoader/ContextLoaderListener/ContextLoadServlet. These classes are invoked when the application server initializes and puts your configured context into memory.
What is the analogy of this for an application that does not have a container wrappering it?
This would preclude multiple instantiations, provide a unified retrieval location, and not suffer Double Checked Locking lameness either.
An alternative solution can be found here:
Simple Spring, use of ClasspathApplicationContext for standalone apps, how to reuse?
for using SingletonBeanFactoryLocator.
The classic one is ClassPathXmlApplicationContext:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
See more here