I would like to use Spring Event to "speak" with my beans in my web application.
So, for example, my bean which fires event is like this:
#Controller
#Scope("request")
#KeepAlive
public class Controller extends InitializingBean, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void test() {
applicationEventPublisher.publishEvent(new TestEvent(this));
}
}
And my listener event is like this:
#Component
#Scope("request")
#KeepAlive
public class Module implements ApplicationListener<TestEvent> {
#Override
public void onApplicationEvent(TestEvent event) {
}
}
The most important point is these beans are scope request because they need to be initialized at every time the page is called.
But in startup, I get this message:
Caused by: java.lang.IllegalStateException: No thread-bound request
found: Are you referring to request attributes outside of an actual
web request, or processing a request outside of the originally
receiving thread? If you are actually operating within a web request
and still receive this message, your code is probably running outside
of DispatcherServlet/DispatcherPortlet: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request.
Like if Spring try to instantiate my Module bean in startup and as the bean is scope request, it can't do this (the context request is not instantiate)
If I delete event management, everything works fine.
So, my question is:
Is it possible to have event listener is scope request ? And how to do this ?
Thanks
Try to inject a scoped proxy in a Singleton ApplicationListener to handle the TestEvent.
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS, value="request")
public class TestEventHandler {
public void onTestEvent(TestEvent event)
// ...
}
}
public class TestEventApplicationListener implements ApplicationListener<TestEvent> {
#Autowired
private TestEventHandler handler;
#Override
public void onApplicationEvent(TestEvent event) {
handler.onTestEvent(event);
}
}
Related
I am developing a set of reusable stuff like filters, global exception handlers and other spring webflux components, that would be used by reactive spring apps (webflux)
Since, this is a library (a jar), that will be consumed by apps, I dont want to annotate the global exception handler class with #Configuration and instead would like to let the applications programmatically configure the exception handler as needed. I see from the docs, that the way to configure the WebExceptionHandler is through the HttpHandler.
#Component
#AllArgsConstructor
public class TestApplicationConfig {
private ApplicationContext applicationContext;
#Bean
public HttpHandler routeHandler() {
return WebHttpHandlerBuilder.applicationContext(applicationContext)
.exceptionHandler(new ServiceExceptionHandler())
.build();
}
}
//exception handler
#NoArgsConstructor
#Order(-2)
#Slf4j
public class ServiceExceptionHandler implements ErrorWebExceptionHandler {
#SuppressWarnings("NullableProblems")
#Override
public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable err) {
}
}
The routeHandler bean gets called by the framework, but the exception handler is not called for any exception. If I have #Configuration in the exception handler class, then it is getting called. Wondering if I making a mistake in the way I am exposing the HttpHandler as a bean
The Spring event mechanism supports publishing application events and listening to these events. as it is explained in this question: Scoped Spring events possible? spring event notify only the current request session listeners. so my question is that possible to notify all existent scoped beans.
code example:
#Controller
public class FooController {
#Autowired
private ApplicationEventPublisher publisher;
#GetMapping("/fireEvent")
public void notifyAllScopedBeans() {
publisher.publishEvent(new FooEvent(this));
}
}
#SessionScope
#Component
public class FooListener {
private String username = "this bean username"
#EventListener(FooEvent.class)
public void listen() {
System.out.println("I'm listening. PS : I am
"+this.username);
}
}
I'm new with jax-rs and have build a web service with jersey and glassfish.
What I need is a method, which is called once the service is started. In this method I want to load a custom config file, set some properties, write a log, and so on ...
I tried to use the constructor of the servlet but the constructor is called every time a GET or POST method is called.
what options I have to realize that?
Please tell, if some dependencies are needed, give me an idea how to add it to the pom.xml (or else)
There are multiple ways to achieve it, depending on what you have available in your application:
Using ServletContextListener from the Servlet API
Once JAX-RS is built on the top of the Servlet API, the following piece of code will do the trick:
#WebListener
public class StartupListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// Perform action during application's startup
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// Perform action during application's shutdown
}
}
Using #ApplicationScoped and #Observes from CDI
When using JAX-RS with CDI, you can have the following:
#ApplicationScoped
public class StartupListener {
public void init(#Observes
#Initialized(ApplicationScoped.class) ServletContext context) {
// Perform action during application's startup
}
public void destroy(#Observes
#Destroyed(ApplicationScoped.class) ServletContext context) {
// Perform action during application's shutdown
}
}
In this approach, you must use #ApplicationScoped from the javax.enterprise.context package and not #ApplicationScoped from the javax.faces.bean package.
Using #Startup and #Singleton from EJB
When using JAX-RS with EJB, you can try:
#Startup
#Singleton
public class StartupListener {
#PostConstruct
public void init() {
// Perform action during application's startup
}
#PreDestroy
public void destroy() {
// Perform action during application's shutdown
}
}
If you are interested in reading a properties file, check this question. If you are using CDI and you are open to add Apache DeltaSpike dependencies to your project, considering having a look at this answer.
I have the following route configuration:
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:in").to("direct:out");
}
}
When I try to test it:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpoints
public class MyRouteTest {
#EndpointInject(uri = "mock:direct:out")
private MockEndpoint mockEndpoint;
#Produce(uri = "direct:in")
private ProducerTemplate producerTemplate;
#Configuration
public static class TestConfig extends SingleRouteCamelConfiguration {
#Bean
#Override
public RouteBuilder route() {
return new MyRoute();
}
}
#Test
public void testRoute() throws Exception {
mockEndpoint.expectedBodiesReceived("Test Message");
producerTemplate.sendBody("Test Message");
mockEndpoint.assertIsSatisfied();
}
}
I get this exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint[direct://out].
Exchange[Message: Test Message]
It looks like the Mock is not picking up the message from the endpoint.
What am I doing wrong?
The problem is that mock endpoints just intercept the message before delegating to the actual endpoint. Quoted from the docs:
Important: The endpoints are still in action. What happens differently
is that a Mock endpoint is injected and receives the message first and
then delegates the message to the target endpoint. You can view this
as a kind of intercept and delegate or endpoint listener.
The solution to your problem is to tell certain endpoints (the ones that expect a consumer in your case) not to delegate to the actual endpoint. This can easily be done using #MockEndpointsAndSkip instead of #MockEndpoints:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpointsAndSkip("direct:out") // <-- turns unit test from red to green ;)
public class MyRouteTest {
// ....
}
This issue because, in your route configuration, there is no route with "direct:out" consumer endpoint.
add a line like some thing below,
from("direct:out").("Anything you want to log");
So that direct:out will consume the exchange and In your test, mock will be able check the received text without any issues. Hope this helps !!
I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.