In a rolling upgrade scenario, I would like to know when there are no active HTTP requests in a given Spring MVC container (starter by Spring Boot, running on Jetty).
That would allow the following flow:
Disable server in reverse proxy
Wait for all requests to finish
Perform upgrade
Is there any way to tell how many requests are currently being handled in Spring MVC or Jetty?
You can get information about the number of active requests using Jetty's StatisticsHandler and JMX.
If you're using Jetty as an embedded container (the recommended approach), you can use an EmbeddedServletContainerCustomizer to set this up:
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
MBeanContainer mbContainer = new MBeanContainer(
ManagementFactory.getPlatformMBeanServer());
server.addEventListener(mbContainer);
server.addBean(mbContainer);
StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(server.getHandler());
server.setHandler(statisticsHandler);
}
});
}
};
}
You'll need to add a dependency on org.eclipse.jetty:jetty-jmx to get access to MBeanContainer.
You can try with spring boot actuator. On endpoint /metrics you should have field
"httpsessions.active"
You can think of using AoP. That worked for me. Snippets of code from my project:
public class CommonJoinPointConfig {
#Pointcut("execution(* ru.outofrange.controller.ClaimController.searchClaims(..))")
public void searchClaims() {
}
}
#Aspect
public class CounterAspect {
public CounterAspect() {
}
public CounterAspect(MBeanService mBeanService) {
this.mBeanService = mBeanService;
}
#Before("ru.outofrange.aspect.CommonJoinPointConfig.searchClaims()")
public void beforeMethod() {
counterService.increaseNumberOfRunningRequests();
}
#After("ru.outofrange.aspect.CommonJoinPointConfig.searchClaims()")
public void afterMethod() {
counterService.decreaseNumberOfRunningRequests();
}
}
#Configuration
#ComponentScan(value = {"ru.outofrange.controller", "ru.outofrange.mbean"})
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class RestConfig extends RepositoryRestConfigurerAdapter {
#Autowired
private CounterService counterService;
#Bean
CounterAspect counterAspect(CounterService counterService){
return new CounterAspect(counterService);
}
...
}
Some notes. In the Config you should use #EnableAspectJAutoProxy(proxyTargetClass=true) and this annotation should be added into the Config, which scans package with controller and the service, i.e. the defined aspect, the controller and the service should be in the same Spring context.
Related
I am playing with a simple Spring Boot application and RabbitMQ.
However I cannot figure out how to run a method periodically.
Here is my Application class
#SpringBootApplication
public class SampleApp {
#Autowired
Sender sender;
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
sender.sendMessage();
}
}
And the sendMessage method is defined as below
#Scheduled(fixedRate = 3000L)
public void sendMessage() {
log.info("Sending message...");
rabbitTemplate.convertAndSend("my-exchange", "my-routing-key", "TEST MESSAGE");
}
However this method is called only once, I can see only a single line in the console.
What I missed in my code?
Thanks.
Looks like you are missing #EnableScheduling:
#EnableScheduling
#SpringBootApplication
public class SampleApp {
...
}
Quoting the documentation:
Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on #Configuration classes as follows:
#Configuration
#EnableScheduling
public class AppConfig {
// various #Bean definitions
}
This enables detection of #Scheduled annotations on any Spring-managed bean in the container.
I am usually using the Spring ThreadPoolTaskScheduler. You define it, as Bean for example then you wrap your method into a Runnable and you call it at intervals defined by a CronTrigger. The result can be retrieved using a ScheduledFuture
Check https://www.baeldung.com/spring-task-scheduler for a complete beginner tutorial.
Here's an alternative way if you don't want to rely on Spring's scheduler.
It's using rxjava2, here's an example:
#Component
public class MessagePublisher {
Sender sender;
Disposable d;
#Autowired
public Config(Sender sender) {
this.sender = sender;
this.d = doSomethingAfterStartup();
}
public Disposable doSomethingAfterStartup() {
return Observable.interval(3000, TimeUnit.MILLISECONDS).subscribe(tick -> {
sender.sendMessage();
});
}
}
I am midway through upgrading from Spring Boot 1.x to Spring Boot 2.0 and have noticed I have started getting class cast errors in my HandlerInterceptors.
For example, in one HandlerInterceptor I look if the controller method/endpoint is annotated with #AdminOnly to restrict access to certain endpoints.
#Component
public class AdminOnlyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Object handler) {
HandlerMethod hm = (HandlerMethod) handler;
Method method = hm.getMethod();
if (method.getDeclaringClass().isAnnotationPresent(RestController.class) && (method.isAnnotationPresent(AdminOnly.class) || method.getDeclaringClass().isAnnotationPresent(AdminOnly.class))) {
// Some Logic returning true or false
}
return true;
}
}
This worked in Spring Boot 1.5.x.
After upgrading I now get the following exception:
java.lang.ClassCastException: org.springframework.web.servlet.resource.ResourceHttpRequestHandler cannot be cast to org.springframework.web.method.HandlerMethod
I couldn't find anything relevant in the migration guide. How can I upgrade but keep the interceptor above working?
It appears Spring Boot 2.x Interceptors now also process Static Resource requests, so these now need to be manually excluded when registering the interceptor like below:
#Configuration
public class ControllerConfiguration implements WebMvcConfigurer {
private final AdminOnlyInterceptor adminInterceptor;
#Autowired
public ControllerConfiguration(AdminInterceptor adminInterceptor) {
this.adminInterceptor = adminInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor)
.addPathPatterns("/rest-api-root/**"); // White list paths
//.excludePathPatterns("/static-resource-root/**"); // Black list paths
}
}
After starting my spring boot application I want to start an customer process like creating required folders, files, etc. For that I'm using ApplicationListener<ApplicationReadyEvent>. This works like expected. But I'm building my spring application context with SpringApplicationBuilder. Every child notifies that the application is started correctly. So my customer post-process startes even more than one time.
#SpringBootApplication
#EnableConfigurationProperties(value = {StorageProperties.class})
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplicationBuilder parentBuilder
= new SpringApplicationBuilder(Application.class);
parentBuilder.child(Config1.class)
.properties("server.port:1443")
...
.run(args);
parentBuilder.child(Config2.class)
.properties("server.port:2443")
...
.run(args);
}
}
My first idea was, that I can create manuelly a new Bean with #Bean in Config1 for my Event-Listener. But I was not able to overhand the configuration file StorageProperties.class, which is necessary for this class.
Because the Listener has an constructor based dependency injection:
private final Path mPathTo;
public AfterStart(StorageProperties prop) {
this.mPathTo = Paths.get(prob.getPath());
}
How can I be able to start the listener just once per start?
For everyone who is interested in this question. This solution worked for me:
public void onApplicationEvent(ApplicationReadyEvent e) {
if (e.getApplicationContext().getParent == null) {
System.out.println("******************************");
System.out.println("Post-process begins.");
System.out.println("******************************");
}
}
I have a Spring MVC application and I'm trying to expose REST service using CXF (Jax-RS). I can't understand how to register my service and make it available. Here is my service:
#Path("/myservice/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public interface MyService {
#POST
#Path("start")
public String start(){
...........
}
}
So far I've managed to avoid any configuration file, only using annotations. So how do I expose my service without a config file? I couldn't find the information in the documentation.
Thanks
Here is the complete answer. Bits and parts can be found online, but nowhere had a complete answer that's easy to follow/implement.
I'll skip the maven part since it's pretty easy to figure it out.
Register CXF Servlet
#WebServlet(urlPatterns="/*")
public class CXFStartServlet extends CXFServlet {
}
Register all the Spring beans with #Path as a CXF service
#Configuration
public class MyCxfConfig {
#Autowired
private ApplicationContext ctx;
#Bean(name="cxf")
public SpringBus springBus(){
return new SpringBus();
}
#Bean
public Server jaxRsServer() {
LinkedList<ResourceProvider> resourceProviders = new LinkedList<ResourceProvider>();
for (String beanName : ctx.getBeanDefinitionNames()) {
if (ctx.findAnnotationOnBean(beanName, Path.class) != null) {
SpringResourceFactory factory = new SpringResourceFactory(beanName);
factory.setApplicationContext(ctx);
resourceProviders.add(factory);
}
}
JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setBus(ctx.getBean(SpringBus.class));
factory.setProviders(Arrays.asList(new JacksonJsonProvider()));
factory.setResourceProviders(resourceProviders);
return factory.create();
}
}
Don't forget to import "MyCxfconfig" in your Spring config. That's all :)
vaadin-spring introduces a couple of Spring scoped objects, the vaadin-session and the vaadin-ui scope. It is necessary to have these two scopes bound before referencing any Vaadin objects in your spring context if:
they are decorated with the #VaadinSessionScope or #UIScope annotations, or
they through some dependency chain reference any bean that is decorated this way.
All runs perfectly well when you start it up in a servlet container like jboss or tomcat. The question is:
If you would like to load a spring application context that contains any of the vaadin beans so decorated for unit testing purposes, how can you create a minimal test that allows the context to be loaded and accessed without starting up a web application container?
Spring MVC is very good at this but when you're using vaadin-spring it's not as straightforward - the relevant vaadin components are highly connected.
(The following example of how to construct a set of Vaadin components to allow access through the abovementioned scopes does not include configuration of the full container, just the minimum required to get a functioning application context.)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class)
#WebAppConfiguration
public class SpringConfigurationTest extends Assert {
#Configuration
#ComponentScan({ "org.example" }) // contains SomeClassReferencingASpringVaadinBean.class
public static class Config {
}
#Autowired
WebApplicationContext applicationContext;
class MyDeploymentConfiguration extends DefaultDeploymentConfiguration {
public MyDeploymentConfiguration(Class<?> servletClass, Properties initParameters) {
super(servletClass, initParameters);
initParameters.put(Constants.SERVLET_PARAMETER_UI_PROVIDER, DefaultUIProvider.class.getName());
}
}
class MyVaadinServlet extends VaadinServlet {
#Override
public String getServletName() {
return getClass().getSimpleName();
}
}
class MyUI extends UI {
#Override
protected void init(VaadinRequest request) {
}
}
#Before
public void setupVaadinScopes() throws Exception {
MyVaadinServlet vaadinServlet = new MyVaadinServlet();
MyDeploymentConfiguration deploymentConfiguration = new MyDeploymentConfiguration(MyVaadinServlet.class,
new Properties());
VaadinServletService vaadinService = new VaadinServletService(vaadinServlet, deploymentConfiguration);
VaadinServletRequest vaadinRequest = new VaadinServletRequest(new MockHttpServletRequest(), vaadinService);
// creates vaadin session and vaadin ui, binds them to thread
VaadinSession vaadinSession = vaadinService.findVaadinSession(vaadinRequest);
Integer uiId = Integer.valueOf(vaadinSession.getNextUIid());
UI ui = new MyUI();
ui.setSession(vaadinSession);
UI.setCurrent(ui);
ui.doInit(vaadinRequest, uiId, null);
vaadinSession.addUI(ui);
}
#Test
public void test0() {
try {
applicationContext.getBean(SomeClassReferencingASpringVaadinBean.class);
} catch (Exception e) {
fail("scopes were probably not set up correctly");
}
}
}