I want to create a session-scoped bean to monitor activations and passivations of HTTP sessions. The bean is very simple:
package my.log;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import org.apache.log4j.Logger;
public class SessionLoggingListenerBean implements HttpSessionActivationListener {
private final Logger LOG = Logger.getLogger(this.getClass());
public SessionLoggingListenerBean() {
LOG.info("SessionLoggingListenerBean starting");
}
public void init() {
LOG.info("SessionLoggingListenerBean init");
}
public void sessionDidActivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " activated");
}
public void sessionWillPassivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " will passivate");
}
}
Bean definition in application context:
<bean id="sessionLoggingListenerBean" class="my.log.SessionLoggingListenerBean" scope="session" init-method="init" lazy-init="false"/>
With this configuration there is no logs from this class, even from the constructor or init() method. Apparently, Spring does not create this bean.
By trial and error I checked that Spring instantiates such a bean when it is needed by another bean, e.g. used by UI. Is there any other (better) way? Is it a bug in Spring?
Spring version used: 2.0.8.
HttpSessionActivationListener is part of the javax.servlet.http package. That should give you a hint that it should be managed by the Servlet container. In your case, you aren't registering the Listener with your ServletContext, neither through the web.xml or a SerlvetContainerInitializer.
Through web.xml you wouldn't be able to make it both a Spring and Servlet container managed object so instead these workarounds exist, first, second.
If you are using a WebApplicationInitializer, you can instantiate your AnnotationConfigWebApplicationContext, have the SessionLoggingListenerBean bean created, retrieve it and use it with
SessionLoggingListenerBean yourBean = context.getBean(SessionLoggingListenerBean.class);
servletContext.addListener(yourBean);
After some experimenting I think it is better not to use Spring for this purpose. I've modified the class to implement also HttpSessionListener and Serializable:
public class SessionLoggingListener implements HttpSessionListener,
HttpSessionActivationListener, Serializable {
private static final long serialVersionUID = -763785365219658010L;
private static final Logger LOG = Logger.getLogger(SessionLoggingListener.class);
public SessionLoggingListener() {
LOG.info("SessionLoggingListener created");
}
public void sessionDidActivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " activated");
}
public void sessionWillPassivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " will passivate");
}
public void sessionCreated(HttpSessionEvent event) {
final HttpSession session = event.getSession();
LOG.info("Session " + session.getId() + " created. MaxInactiveInterval: " + session.getMaxInactiveInterval() + " s");
session.setAttribute(this.getClass().getName(), this);
}
public void sessionDestroyed(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " destroyed");
event.getSession().removeAttribute(this.getClass().getName());
}
}
and added it to web.xml:
<listener>
<listener-class>
evo.log.SessionLoggingListener
</listener-class>
</listener>
From now on, whenever a new session is created, the listener binds to it (session.setAttribute(...)). This is necessary to make the container notice the listener about session activation or passivation.
In case of Spring and sessions - according to this forum thread Spring does not load session beans until they are requested:
Session bean is treated as a special form of "prototype". That means it will follow prototype symantics in creating an instance.
For me this is an unintuitive and not well documented behavior.
Related
I am implementing a batch application using springboot 2.4.3 + jsr352. There is a simple batchlet class(sleepybatchlet) defined. I am trying to reference it in the JSL. but It fails saying classnotfound exception when the job is started using joboperator.
sleepy-batchlet.xml:
<job xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd" restartable="true" version="1.0" id="sleepy-batchlet">
<step id="step1">
<batchlet ref="sleepyBatchlet">
<properties>
<property name="sleep.time.seconds" value="#{jobParameters['sleep.time.seconds']}" />
</properties>
</batchlet>
</step>
</job>
Below is my batchlet class which is annotated with #Named
#Named
public class SleepyBatchlet extends AbstractBatchlet{
private final static Logger logger = Logger.getLogger(SleepyBatchlet.class.getName());
private Map<ReportMetaData,byte[]> pdfMetadataMap;
/**
* Logging helper.
*/
protected static void log(String method, Object msg) {
System.out.println("SleepyBatchlet: " + method + ": " + msg);
// logger.info("SleepyBatchlet: " + method + ": " + String.valueOf(msg));
}
/**
* This flag gets set if the batchlet is stopped. This will break the batchlet
* out of its sleepy loop.
*/
private volatile boolean stopRequested = false;
/**
* The total sleep time, in seconds.
*/
#Inject
#BatchProperty(name = "sleep.time.seconds")
String sleepTimeSecondsProperty;
private int sleepTime_s = 3;
#Inject
private JschFileUtil jschFileUtil;
#Override
public String process() throws Exception {
log("process", "entry");
System.out.println("Test");
return "exitStatus";
}
/**
* Called if the batchlet is stopped by the container.
*/
#Override
public void stop() throws Exception {
log("stop:", "");
stopRequested = true;
}
}
Defined the bean in java configuration class as well.
#Autowired
private SleepyBatchlet sleepyBatchlet;
#Bean
public Batchlet fooBatchlet() {
return sleepyBatchlet;
}
But for some reason, Its not getting referenced in the JSL. Can someone please suggest what needs to be done to use the bean created already ?
I am trying to reference it in the JSL. but It fails saying classnotfound exception when the job is started using joboperator.
This is because you are referring to the class by its name and not its fully qualified name.
I added sample springboot+jsr352 application here . github.com/MekalaJ/demo
In your example, you need to update your step definition as follows:
<batchlet ref="com.example.demo.batch.SleepyBatchlet">
<properties>
<property name="sleep.time.seconds" value="#{jobParameters['sleep.time.seconds']}" />
</properties>
</batchlet>
Intellij show red underline.
and When I mouseover to red underline, that show this message.
Methods annotated with '#Async' must be overridable
Reports the cases when your code prevents a class from being
subclassed by some framework (e.g. Spring or Hibernate) at runtime
What I should do for remove this error?
And It show red underline. but It still working without compile error.
I'm using Intellij 2017.2.5.
#Async
private void deleteFile(String fileName, String path) {
BasicAWSCredentials credentials = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
AmazonS3 s3client = AmazonS3ClientBuilder.standard().withRegion("ap-northeast-2").withCredentials(new AWSStaticCredentialsProvider(credentials)).build();
try {
s3client.deleteObject(new DeleteObjectRequest(AWS_BUCKET_NAME, path + fileName));
} catch (AmazonServiceException ase) {
System.out.println("Caught an AmazonServiceException.");
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException.");
System.out.println("Error Message: " + ace.getMessage());
}
}
#Async is an indication to Spring to execute this method asynchronously. So it can only work on several conditions :
The class must be managed by Spring
The method has to be public
The method has to be called using Spring
For the latter, it seems like you are calling this method directly in your class, so Spring has no way to know that you called this method, it's not htat magic.
You should refactor your code so the method is called on a bean managed by Spring as following code :
#Service
public class AsyncService {
#Async
public void executeThisAsync() {...}
}
#Service
public class MainService {
#Inject
private AsyncService asyncService;
public mainMethod() {
...
// This will be called asynchronusly
asyncService.executeThisAsync();
}
...
}
The error indicates that private must be protected cq. public, for the asynchronicity.
Then it is not seen that this method is used by the Async tooling. Simply add a SuppressWarnings, stating actually that you know what you are doing.
#Async
#SuppressWarnings("WeakerAccess")
protected void deleteFile(String fileName, String path) {
You might drop a hint to the IntelliJ team.
I am beginner in Spring framework.
In my case Session can be expire by following way
--> Success Log-out (Explicit Log-out )
--> Session Timeout (Implicit Log-out )
I have do DML(record insertion) in database whenever some user log in and I want to perform DML(record deletion) in database whenever user session timeout (Implicit Log-out).
My Question is that is there any way in Spring that tell us before the expiry of session.
So I can do perform my custom event before session expiry.
Thanks in advance
Yes, you can do that with SessionDestroyedEvent.
#Component
public class SessionEndedListener implements ApplicationListener<SessionDestroyedEvent> {
#Override
public void onApplicationEvent(SessionDestroyedEvent event)
{
for (SecurityContext securityContext : event.getSecurityContexts())
{
Authentication authentication = securityContext.getAuthentication();
YourPrincipalClass user = (YourPrincipalClass) authentication.getPrincipal();
// do something
}
}
}
And in web.xml:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
This event will be fired for both the regular logout as well as the session timeout.
I have solved my problem by following way similar #Codo answer
#Component
public class SessionCreatedListenerService implements ApplicationListener<ApplicationEvent> {
private static final Logger logger = LoggerFactory
.getLogger(SessionCreatedListenerService.class);
#Autowired
HttpSession httpSession;
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if(applicationEvent instanceof HttpSessionCreatedEvent){ //If event is a session created event
}else if(applicationEvent instanceof HttpSessionDestroyedEvent){ //If event is a session destroy event
// handler.expireCart();
logger.debug(""+(Long)httpSession.getAttribute("userId"));
logger.debug(" Session is destory :" ); //log data
}else if(applicationEvent instanceof AuthenticationSuccessEvent){ //If event is a session destroy event
logger.debug(" athentication is success :" ); //log data
}else{
/*logger.debug(" unknown event occur : " Source: " + ); //log data
}
}
}
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.stereotype.Component;
#Component
public class LogoutSuccessListener implements ApplicationListener<LogoutSuccessEvent>{
#Override
public void onApplicationEvent(LogoutSuccessEvent evt) {
String login = evt.getAuthentication().getName();
System.out.println(login + " has just logged out");
}
}
When I shut down Tomcat, I observe a correct shutdown and cleanup of the Spring WebApplicationContext. However, when I redeploy my Spring-based WAR (by copying the new WAR to webapps), normal shutdown does not occur. This is a problem for me due to all the ensuing resource leaks:
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named [hz.hazelcast-swipe-instance.scheduled] but has failed to stop it. This is very likely to create a memory leak.
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named [hz.hazelcast-swipe-instance.operation.thread-0] but has failed to stop it. This is very likely to create a memory leak.
... and many more. I am using XML-less configuration, this is my WebApplicationInitializer:
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
}
#Override protected Class<?>[] getServletConfigClasses() { return null; }
#Override protected String[] getServletMappings() { return new String[] { "/" }; }
#Override public void onStartup(ServletContext ctx) throws ServletException {
ctx.setInitParameter("spring.profiles.active", "production");
super.onStartup(ctx);
}
}
There is no configuration specific to controlling the behavior upon servlet context reload, and I assume this should have worked out of the box.
Is there a way to make the WebApplicationContext close properly before continuing the servlet context reloading procedure?
I am on Spring 4.0.5, Tomcat 7.0.54, Hazelcast 3.2.1, Hibernate 4.3.4.Final.
Update
I have added a Spring application listener for the ContextClosedEvent and printed the stack trace of its invocation:
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:333) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:880) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:841) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.destroy(FrameworkServlet.java:819) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.apache.catalina.core.StandardWrapper.unload(StandardWrapper.java:1486) [catalina.jar:7.0.54]
at org.apache.catalina.core.StandardWrapper.stopInternal(StandardWrapper.java:1847) [catalina.jar:7.0.54]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232) [catalina.jar:7.0.54]
at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5647) [catalina.jar:7.0.54]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232) [catalina.jar:7.0.54]
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1575) [catalina.jar:7.0.54]
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1564) [catalina.jar:7.0.54]
This indicates that the Spring shutdown occurs in its Servlet#destroy method. This is the relevant snippet from AbstractApplicationContext#close():
if (logger.isInfoEnabled()) {
logger.info("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
try {
getLifecycleProcessor().onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
synchronized (this.activeMonitor) {
this.active = false;
}
I see the log entry from the start of this snippet, and I get my ContextClosedEvent. I also see an entry DefaultLifecycleProcessor - Stopping beans in phase 2147483647, which probably comes from the getLifecycleProcessor.onClose() line. It seems that some error occurs downstream from that. Some exception may be swallowed.
Update 2
As requested, this is how I configure Hazelcast:
#Bean(destroyMethod="shutdown") public HazelcastInstance hazelcast() {
final Config c = hzConfig();
final JoinConfig join = c.getNetworkConfig().getJoin();
join.getMulticastConfig().setEnabled(false);
join.getTcpIpConfig().setEnabled(true);
return getOrCreateHazelcastInstance(c);
}
hzConfig() is a method where instance name, group name and password, map names, and map indices are configured, so I don't think it is of interest here.
And this is my Hibernate SessionFactory config:
#Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean b = new LocalSessionFactoryBean();
b.setDataSource(dataSource);
b.setHibernateProperties(props(
"hibernate.connection.release_mode", "on_close",
"hibernate.id.new_generator_mappings", "true",
"hibernate.hbm2ddl.auto", "update",
"hibernate.order_inserts", "true",
"hibernate.order_updates", "true",
"hibernate.max_fetch_depth", "0",
"hibernate.jdbc.fetch_size", "200",
"hibernate.jdbc.batch_size", "50",
"hibernate.jdbc.batch_versioned_data", "true",
"hibernate.jdbc.use_streams_for_binary", "true",
"hibernate.use_sql_comments", "true"
));
return b;
}
At some point, you mentioned that there was a NoClassDefFoundError for Logback. You got this fixed by removing this dependency, but then the problem moved to a another class - one of Spring's own classes.
This can mean that either one of the libraries you have does something buggy with class loaders or maybe Tomcat needs instructions not to keep locks on some resources. See here more about Tomcat resources being locked and the <Context> setting to try: in your Tomcat's conf/context.xml place a antiResourceLocking="true" to the element.
Have you tried upping unloadDelay (defaults to 2000ms) for Tomcat contexts? See http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
UPDATE: I see that you are having issues with logback as well, it might be worth the shot to try and register this listener as well:
class LogbackShutdownListener implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent event) {
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
System.out.println("Shutting down Logback context '" + loggerContext.getName() + "' for " + contextRootFor(event));
loggerContext.stop();
}
#Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("Logback context shutdown listener registered for " + contextRootFor(event));
}
private String contextRootFor(ServletContextEvent event) {
return event.getServletContext().getContextPath();
}
}
Be sure to declare this listener before the spring context loader listener so that it is invoked after the context listener upon shutdown.
UPDATE 2: Also it might be worth the try to register another bean to handle closing of the Hazelcast stuff manually (be sure to also remove destroyMethod from the hazelcast bean):
#Component
class HazelcastDestructor {
#Autowired
private HazelcastInstance instance;
#PreDestroy
public void shutdown() {
try {
instance.shutdown();
} catch (Exception e) {
System.out.println("Hazelcast failed to shutdown(): " + e);
throw e;
}
}
}
UPDATE 3: Just out of curiosity, have you tried parallel deployment: http://www.javacodegeeks.com/2011/06/zero-downtime-deployment-and-rollback.html. It might behave differently than reloading the very same context. At the very least you should be able to undeploy the old version lazily and see if that makes a difference.
There is a similar issue on the dangling threads while container restarting here.
Of all the answers, one particular answer of interest was by Howard - which shows the way these threads are cleared.
There is some good discussion and reasoning as to how this can terminate the threads here.
Now implement ServletContextListener and take care of these threads in the contextDestroyed() method as:
public class YourListener implements ServletContextListener{
....
#Override
public void contextDestroyed(ServletContextEvent event) {
//Call the immolate method here
}
}
Register this listener in WebApplicationInitilizer as:
ctx.addListener(new YourListener());
So when server is restarted - contextDestroyed method is called and this takes care of all these threads.
From Web App development point of view, ServletContainer can only notify the before started and before end process of app.
It is using ServletContextListener.
Config ServletContextListener in web.xml
<listener>
<listener-class>com.var.YourListener</listener-class>
</listener>
YourListener.java
public class YourListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
//initialization process
}
public void contextDestroyed(ServletContextEvent sce) {
//destory process
}
}
Update -XML Less
Programmatically
#Override
public void onStartup(ServletContext ctx) throws ServletException {
ctx.addListener(new YourContextListener());
ctx.setInitParameter("spring.profiles.active", "production");
super.onStartup(ctx);
}
Annotation
#WebListener / #WebServletContextListener
public class YourContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
Update- ShoutDown Hook In Spring
I never use it before my app development, we can register shoutdownhook event into AbstractApplicationContext of Spring.
I am not sure it will be ok or not for you.
AbstractApplicationContext context = ...
context.registerShutdownHook();
Reference 1 Reference 2
I want to build a RESTful API with Java and Cassandra 2.x (on Jersey framework). I'm new to both technologies so I would like to ask you is that the correct way to integrate and share Cassandra driver.
0. Get the driver though Maven
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>2.0.3</version>
</dependency>
1. Wrap driver's functionality with a Client class:
package com.example.cassandra;
import com.datastax.driver.core.*;
public class Client {
private Cluster cluster;
private Session session;
public Client(String node) {
connect( node );
}
private void connect(String node) {
cluster = Cluster.builder()
.addContactPoint(node)
.build();
session = cluster.connect();
}
public ResultSet execute( String cql3 ) {
return session.execute( cql3 );
}
public void close() {
cluster.close();
}
}
2. I insatiate the client in ContextListener and share it though context attribute
package com.example.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.example.cassandra.Client;
public class ExampleContextListener implements ServletContextListener {
Client cassandraClient;
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext ctx = servletContextEvent.getServletContext();
cassandraClient = new Client( ctx.getInitParameter( "DBHost" ) );
ctx.setAttribute( "DB", cassandraClient );
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
cassandraClient.close();
}
}
3. Now I get the client from servlet's context and use it
Client client = (Client) context.getAttribute("DB");
client.execute("USE testspace;");
ResultSet rs = client.execute("SELECT * from users;");
for (Row row : rs ) {
output += row.getString("lname") + "|";
}
Is that the correct way to do it (both from performance and architectural point of view)?
Full example available on: https://github.com/lukaszkujawa/jersey-cassandra
I just developed what you are going to develop. What you wrote works but it's not my favourite approach. I'd rather create a Singleton (since 1 session is enough for an application). Following Joshua Bloch enum's singleton's pattern here is what I did
public enum Cassandra {
DB;
private Session session;
private Cluster cluster;
private static final Logger LOGGER = LoggerFactory.getLogger(Cassandra.class);
/**
* Connect to the cassandra database based on the connection configuration provided.
* Multiple call to this method will have no effects if a connection is already established
* #param conf the configuration for the connection
*/
public void connect(ConnectionCfg conf) {
if (cluster == null && session == null) {
cluster = Cluster.builder().withPort(conf.getPort()).withCredentials(conf.getUsername(), conf.getPassword()).addContactPoints(conf.getSeeds()).build();
session = cluster.connect(conf.getKeyspace());
}
Metadata metadata = cluster.getMetadata();
LOGGER.info("Connected to cluster: " + metadata.getClusterName() + " with partitioner: " + metadata.getPartitioner());
metadata.getAllHosts().stream().forEach((host) -> {
LOGGER.info("Cassandra datacenter: " + host.getDatacenter() + " | address: " + host.getAddress() + " | rack: " + host.getRack());
});
}
/**
* Invalidate and close the session and connection to the cassandra database
*/
public void shutdown() {
LOGGER.info("Shutting down the whole cassandra cluster");
if (null != session) {
session.close();
}
if (null != cluster) {
cluster.close();
}
}
public Session getSession() {
if (session == null) {
throw new IllegalStateException("No connection initialized");
}
return session;
}
}
And in the context listener I call connect or shutdown.
Since all exceptions in new driver are unchecked my tip for you is to create your own implementation of the Jersey ExceptionMapper mapping DriverException. One more thing, think about working with PreparedStatements rather than Strings so that Cassandra parse the query only once. In my application I followed the above patterns also for the queries (an enum singleton that prepare statements when loaded first time and then expose methods to use these statements).
HTH,
Carlo