Is there a way to have a function run when a session is created or expired? - spring-boot

I am currently planning an application that requires a function to run whenever a session is created and expires. I'm planning on using something like redis but I am open to other ideas. What i am looking for is a n annotation such as #whenexpires and #whencreated. I know that most of the annotations for sessions are at the class, and notthemethod Thanks in regards.

As of Servlet specification 2.3, Java Servlet containers like Apache Tomcat provide the HttpSessionListener interface in order to execute custom logic in the event of created or destroyed sessions. Basic usage:
package com.example;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MySessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
}
}
Add MySessionListener to your web.xml or - in case of Spring - declare a Spring bean for it that is detected by Spring. However, Spring is not required as HttpSessionListener is part of the Java Servlet spec.
If you go for Spring Session with Redis, you can continue using your HttpSessionListener by adding it to the Spring configuration as described in the official docs.
#EnableRedisHttpSession
public class Config {
#Bean
public MySessionListener mySessionListener() {
return new MySessionListener();
}
// more Redis configuration comes here...
}
Moreover, Spring Session comes with support for the "Spring-native" way of event subscription and publishing: ApplicationEvent. Depending on the session persistence approach, there are currently up to three events that can be catched by your application: SessionExpiredEvent, SessionCreatedEvent, SessionDestroyedEvent.
Implement an EventListener in order to subscribe to Spring Session events, for example:
package com.example;
import org.springframework.context.event.EventListener;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.stereotype.Component;
#Component
public class MySessionEventListener {
#EventListener
public void sessionDestroyed(SessionDestroyedEvent event) {
}
#EventListener
public void sessionCreated(SessionCreatedEvent event) {
}
#EventListener
public void sessionExired(SessionExpiredEvent event) {
}
}

Related

How Do I Unit Test A Jersey REST API Without Running A Server?

I am working with a REST API that is using Jersey with Spring Boot (so no specific application context in XML or Java) and Spring Data JPA.
I want to write unit tests on the GET and POST endpoints, however, I don't want to start a web server as it takes too long.
If I use JerseyTest my Spring Beans don't get loaded into the context.
public class InMemoryContainerPackageTest extends
JerseyTestNg.ContainerPerClassTest {
#Override
protected TestContainerFactory getTestContainerFactory() {
return new InMemoryTestContainerFactory();
}
#Override
public Application configure() {
ResourceConfig config = new ResourceConfig()
.register(SpringLifecycleListener.class)
.register(RequestContextFilter.class)
.register(this)
.register(MyController.class)
.packages("com.my.service");
return config;
}
If I use SpringBootTest it starts up a web server which takes about 30 seconds and ideally I want all my tests to complete in under 5 seconds otherwise developers won't run them.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestNGClass1 extends AbstractTestNGSpringContextTests {
I don't think MockMvc works with Jersey endpoints.
If I use JerseyTest my Spring Beans don't get loaded into the context.
What you can do is set the property "contextConfig" in your ResourceConfig. The value will be a Spring ApplicationContext instance. So if you are using Java configuration, you would just use an AnnotationConfigApplicationContext.
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.property("contextConfig",
new AnnotationConfigApplicationContext(SpringConfig.class));
}
Here, SpringConfig is an arbitrary Spring #Configuration class. Below is a complete example.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
public class SpringTest extends JerseyTest {
public static class MessageService {
public String getMessage() {
return "Hello World";
}
}
#Configuration
public static class SpringConfig {
#Bean
public MessageService service() {
return new MessageService();
}
}
#Path("test")
public static class TestResource {
#Autowired
private MessageService service;
#GET
public String get() {
return service.getMessage();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.property("contextConfig",
new AnnotationConfigApplicationContext(SpringConfig.class));
}
#Override
public TestContainerFactory getTestContainerFactory() {
return new InMemoryTestContainerFactory();
}
#Test
public void testIt() {
Response res = target("test")
.request()
.get();
String msg = res.readEntity(String.class);
System.out.println(msg);
assertThat(msg).isEqualTo("Hello World");
}
}
As far as the JPA, you are going to have to configure that yourself. When using Spring Boot, all of the JPA bootstrapping is taken care of. If you are going to use Jersey Test Framework, then you are ignoring all Spring Boot configuration.
It's really not that hard to configure JPA yourself. It basically consists of configuring a DataSource, a TransactionManager, a JpaVendorAdaptor, and a LocalContainerEntityManagerFactoryBean. And to enable the Spring Data repositories, you just need to use the #EnableJpaRepositories.
Have a look at this complete example configuration.
Another thing to be wary of is that when we use the Jersey Test Framework, we will not have the test scoped transactions (and rollbacks) that you will get when working with Spring Test. So when you write your tests, you need to take this into consideration.

Spring Boot: Capture event on context load time and on property change

I wanted to execute a custom logic as soon as the application started and also whenever properties change in Spring Cloud config repo/server. So I have written something like this:
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfiguration implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// Custom logic goes here. It should be executed on both app context load time
// and on any property change time
}
}
This above code was working few months back during application load time and when property change. But this code stopped working recently with, I guess, Spring boot / cloud version updates.
Currently I am using Sprig boot 1.5.10 and Cloud Edgware.SR3
Found a way to run the custom logic to run it on both load time and property change time.
Basically changed to above code to be called upon any event and then inside the overridden methood onApplicationEvent checking only for below event
ContextRefreshedEvent - Event raised when an application context gets initialized or refreshed.
EnvironmentChangeEvent - Event published to signal a change in the environment such as properties in config repo.
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class AppConfiguration implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof EnvironmentChangeEvent || event instanceof ContextRefreshedEvent) {
// Custom logic goes here. It should be executed on both app context load time
// and on any property change time
}
}
}
Updated
We can also use #EventListener annotation to do the similar thing, which is very simple and easy use. Refer below example:
#Configuration
public class AppConfiguration {
#EventListener({EnvironmentChangeEvent.class, ContextRefreshedEvent.class})
public void onRefresh() {
// Your code goes here...
}
}

init method in jersey jax-rs web service

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.

Changing default welcome-page for spring-boot application deployed as a war

I was trying to find a way to change the default welcome-page for a spring-boot application that is being deployed as a war in production but I can't find a way to do it without a web.xml file.
According to the documentation we can do it using the EmbeddedServletContainerFactory with this code:
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
TomcatContextCustomizer contextCustomizer = new TomcatContextCustomizer() {
#Override
public void customize(Context context) {
context.addWelcomeFile("/<new welcome file>");
}
};
factory.addContextCustomizers(contextCustomizer);
return factory;
}
Although, as we're creating a war file and deploying it to tomcat and not using the Embedded Tomcat, this isn't doing anything.
Any idea? If we really need to add a web.xml file, how can we do it and still using spring boot? Should we specify the Application bean(with the main method) as the application context for DispatcherServlet? The documentation isn't very clear about that.
Older Servlet containers don’t have support for the ServletContextInitializer bootstrap process used in Servlet 3.0. You can still use Spring and Spring Boot in these containers but you are going to need to add a web.xml to your application and configure it to load an ApplicationContext via a DispatcherServlet.
Thanks in advance!
Pedro
It's not too hard to do... you just need to forward the default mapping...
#Configuration
public class DefaultView extends WebMvcConfigurerAdapter{
#Override
public void addViewControllers( ViewControllerRegistry registry ) {
registry.addViewController( "/" ).setViewName( "forward:/yourpage.html" );
registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
super.addViewControllers( registry );
}
}
Well, a few years passed since the last answer - and code evolves..
This won't work on Spring 5 / Java 8+, you should implement the interface and override the default method.
import org.springframework.core.Ordered;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class DefaultViewConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("/homepage.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Following Michael's tutorial, I was able to just map / to my index.gsp file.
#Controller
class Routes {
#RequestMapping({
"/",
"/bikes",
"/milages",
"/gallery",
"/tracks",
"/tracks/{id:\\w+}",
"/location",
"/about"
})
public String index() {
return "forward:/index.gsp";
}
}
I am doing it as follows.
package org.gwtproject.tutorial.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Configure the welcome page
*
*/
#Configuration
public class SpringBootWelcomePageConfiguration extends WebMvcConfigurerAdapter implements WebMvcConfigurer {
/**
* redirect a user to the welcome page when he visits tha app without a
* destination url.
*/
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/ForExampleAGwtEntrypoint.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}
}

Spring Pre/Post method security annotations not working

I can't seem to get Spring Pre/Post method security annotations to work. I've read every related stackoverflow question on the topic, and the main suggestion is to ensure that global-method-security is enabled in the same context as the beans which you wish to secure. I have the following my dispatcher-servlet.xml:
<context:component-scan base-package="com.package.path" />
<context:annotation-config />
<security:global-method-security pre-post-annotations="enabled" />
The beans in question are in "com.package.path". I know that Spring is creating instances of them correctly, as injection is working just fine and requests are being serviced by the intended classes.
So, here's an example service class in "com.package.path":
#Controller
#RequestMapping("/article")
public class ArticleServiceImpl extends GWTController implements ArticleService {
#Autowired
public ArticleServiceImpl(DataSource ds) {
}
#Override
#PreAuthorize("hasRole('ROLE_BASIC_USER')")
public Article save(Article article) {
}
}
The annotation on the save method does not work. A few important notes:
I'm using GWT, though from what I've read, that shouldn't matter much.
I have method security working perfectly well in another, similar project. The only difference is that there is a DAO layer in the other project, which is not present in this one. It's in this layer that I have annotation security working. However, it shouldn't matter what "layer" this is, as long as Spring is responsible for creation of the beans, right?
The interface "ArticleService" above is a GWT service interface. I've tried putting the annotation there, but that doesn't work either.
Here's my GWTController class, referenced above, if needed:
package com.areahomeschoolers.baconbits.server.spring;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.areahomeschoolers.baconbits.server.util.ServerContext;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* Spring controller class that handles all requests and passes them on to GWT. Also initializes server context.
*/
public class GWTController extends RemoteServiceServlet implements ServletConfigAware, ServletContextAware, Controller, RemoteService {
private static final long serialVersionUID = 1L;
protected ServletContext servletContext;
#Override
public ServletContext getServletContext() {
return servletContext;
}
// Call GWT's RemoteService doPost() method and return null.
#Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// load our ServerContext with current request, response, session, user, appContext, etc.
ServerContext.loadContext(request, response, servletContext);
try {
doPost(request, response);
} finally {
ServerContext.unloadContext();
}
return null; // response handled by GWT RPC over XmlHttpRequest
}
#Override
public void setServletConfig(ServletConfig conf) {
try {
super.init(conf);
} catch (ServletException e) {
e.printStackTrace();
}
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
protected void checkPermutationStrongName() throws SecurityException {
return;
}
#Override
protected void doUnexpectedFailure(Throwable e) {
e.printStackTrace();
super.doUnexpectedFailure(e);
}
}
Security aspect provided by Spring Security inherits all limitations of Spring Framework proxy-based AOP support. In particular, aspects are not applied to calls that happen "inside" the objects (unless you use AspectJ weaving), see 7.6.1 Understanding AOP proxies.
So, if you want to use security aspect this way, you need to use GWT integration mechanism that make calls to your service from the outside, i.e. a mechanism that doesn't require your services to extend RemoteServiceServlet.
Something like spring4gwt should work fine.

Resources