Retrieve servletContext reference in quartz scheduler - servlets

I am using Quartz Scheduler with my spring 3.0 based application. I am successfully able to create new schedulers and they are working fine.
I have seen thus reference.
But.. I am not able to retrieve servletContext in my quartz job file. can anyone help me for How to retrieve servletContext reference in executeInternal() method ??

I had a similar need. I sorted it out in a similar fashion to the solution presented here.
In my servlet context listener I am setting the servlet context using the job data map object which then is set for a job:
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
//Create & start the scheduler.
StdSchedulerFactory factory = new StdSchedulerFactory();
factory.initialize(sce.getServletContext().getResourceAsStream("/WEB-INF/my_quartz.properties"));
scheduler = factory.getScheduler();
//pass the servlet context to the job
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("servletContext", sce.getServletContext());
// define the job and tie it to our job's class
JobDetail job = newJob(ImageCheckJob.class).withIdentity("job1", "group1").usingJobData(jobDataMap).build();
// Trigger the job to run now, and then repeat every 3 seconds
Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(simpleSchedule().withIntervalInMilliseconds(3000L).repeatForever()).build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
// and start it off
scheduler.start();
} catch (SchedulerException ex) {
log.error(null, ex);
}
}
Then inside my job I am doing this:
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ServletContext servletContext = (ServletContext) context.getMergedJobDataMap().get("servletContext");
//...
}
EDIT:
Also since you mention that you are using Spring I found this link, where in the last post a guy mentions to implement ServletContextAware. Personally, I would go with the JobDataMap, since that is its role.

Starting from Quartz 2.0, if you are starting the scheduler inside your webapp via QuartzInitializerServlet in your web.xml, you can store ServletContext in your SchedulerContext by setting scheduler-context-servlet-context-key as an init parameter as follows:
<!-- Quartz Scheduler Initializer Servlet -->
<servlet>
<servlet-name>QuartzInitializer</servlet-name>
<servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
<init-param>
<param-name>shutdown-on-unload</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>start-scheduler-on-load</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>scheduler-context-servlet-context-key</param-name>
<param-value>servletContext</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
See the following reference in code: https://github.com/elventear/quartz-scheduler/blob/quartz-2.0.0-rc/quartz/src/main/java/org/quartz/ee/servlet/QuartzInitializerServlet.java#L122

To get to your ServletContext from a QuartzJob configure like Kalman said, Then here is some code to get a actual "servletContext"
private void initContext(JobExecutionContext jobContext) {
Scheduler scheduler = jobContext.getScheduler();
SchedulerContext schedulerContext = null;
try {
schedulerContext = scheduler.getContext();
} catch (SchedulerException e) {
e.printStackTrace();
}
ServletContext servletContext = (ServletContext)schedulerContext.get("servletContext");
System.out.println("ServletContextName : "+ servletContext.getServletContextName());

Related

How do I make jcaptcha work with Spring Session?

We implemented Spring Session backed by Redis and have a cluster of Tomcat servers. When we turned sticky sessions off by not setting the jvmRoute we keep getting "Text verification failed" in the jcaptcha service. I assume this is because the jcaptcha servlet knows nothing about the Spring Dispatcher servlet, which has all of the Spring Session filters, and thus cannot read the session variable. How can we make jcaptcha work with Spring Session?
Here is our setup:
Web.xml
<servlet>
<servlet-name>my-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>my-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jcaptcha</servlet-name>
<servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jcaptcha</servlet-name>
<url-pattern>/jcaptcha/jcaptcha.jpg</url-pattern>
</servlet-mapping>
CustomHttpSessionAppInitializer.java
public class CustomHttpSessionAppInitializer extends AbstractHttpSessionApplicationInitializer {}
RedisSessionConfig.java
#Configuration
#EnableRedisHttpSession
public class RedisSessionConfig {
#Value("${spring.redis.host}")
private String redisServerName;
#Value("${spring.redis.port}")
private Integer redisServerPort;
#Value("${spring.redis.database}")
private Integer redisServerDatabase;
#Value("${spring.redis.password}")
private String redisServerPassword;
#Value("${spring.server.affinity}")
private Boolean isServerAffinity = Boolean.FALSE;
#Autowired
private SessionIdentifierService sessionIdentifierService;
#Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisServerName, redisServerPort);
config.setDatabase(redisServerDatabase);
config.setPassword(RedisPassword.of(redisServerPassword));
return new JedisConnectionFactory(config);
}
/*
* We need to register every HttpSessionListener as a bean to translate SessionDestroyedEvent and SessionCreatedEvent into
* HttpSessionEvent. Otherwise we will got a lot of warning messages about being Unable to publish Events for the session.
* See Spring Session Docs at:
* {#link} https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-httpsessionlistener
*/
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setUseBase64Encoding(false);
if (isServerAffinity) {
serializer.setJvmRoute(sessionIdentifierService.getJvmRoute());
}
return serializer;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
There shouldn't be a problem integrating jcaptcha with Spring Session. As long as there is a way of loading the session from Redis (via a SESSION cookie in this case) and the session exists, calling request.getSession() or request.getSession(false) will return the Redis-backed session.
This works in any filter and servlet that is called AFTER the springSessionRepositoryFilter. If you look at the source code of SessionRepositoryFilter, you will see that the HttpServletRequest is swapped with a SessionRepositoryRequestWrapper.
So your SimpleImageCaptchaServlet and whichever servlet you use to validate the user response will obtain a SessionRepositoryRequestWrapper that will seemlessly give you access to the Redis-backed session.
The problem then might be your configuration; the springSessionRepositoryFilter might not be registered with the container, especially since you’re using both web.xml and a Servlet 3.0+ WebApplicationInitializer. If your app works properly, then your web.xml is most likely working fine. Are you using a WebApplicationInitializer to load your web.xml? If not, then it might be that your Java Config is not loading. Make sure your web.xml loads your configuration somehow, perhaps by enabling component scanning (<context:component-scan/>) in the contextLoaderListener xml config file to load your Java Config along with:
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
to load the configuration that will create the filter, which you must then add to your web.xml:
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
Check out the Spring Session reference on XML config

Get servlet init params in servlet context listener

<listener>
<listener-class>config</listener-class>
</listener>
<servlet>
<servlet-name>ProcessReg</servlet-name>
<servlet-class>ProcessReg</servlet-class>
<init-param>
<param-name>text</param-name>
<param-value>HelloWorld1</param-value>
</init-param>
public class config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
String text1 = servletContext.getInitParameter("text");
In method contextInitialized(ServletContextEvent event) , If There would be two servlets , for example , Let's say name of the second servlet be Servlet2 and it let's say has also has init - param called text with value HelloWorld2 .
How does listener know to take ProcessReg servlet ?
How to get param from Servlet2 ??
You need to distinguish between servlet initialization parameters and context initialization parameters.
Context initialization parameters are:
context-wide;
declared in <context-param> elements directly under the <web-app> root;
typically accessed using the ServletContext.getInitParameter() method, e.g. from inside a listener's contextInitialized() method.
Servlet initialization parameters are:
servlet-specific;
declared in <init-param> elements inside a <servlet> element;
typically accessed using the ServletConfig.getInitParameter() method, e.g. from inside the servlet's init() method.

Which method is called just before the session gets invalidated in spring-mvc life cycle?

I'm trying to timeout an HttpSession in Spring-mvc.
When the session gets timed out I have to release the resources that were used.
For that I need to call some other methods in the application.
myService.releaseResources(id,name);
myService is an autowired object for the service class.
When the session gets timed out the sessionDestroyed method is called.
But in this method myService method value is null.
I want to know where should I call the above code.
Thanks in advance.
You need to implement HttpSessionListener.
public class SessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent sessionEvent) {
// TODO Auto-generated method stub
}
#Override
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
// TODO Auto-generated method stub
}
}
Add its entry to web.xml
<listener>
<listener-class>
yourpacakage.SessionListener
</listener-class>
</listener>
Then call your desired code inside sessionDestroyed method.And as far as service is concerned you can get the service object by accessing current applicationContext.
ServletContext ctx = event.getSession().getServletContext();
WebApplicationContext springContext=WebApplicationContextUtils.getWebApplicationContext(ctx);
springContext.getBean("yourService");
If you want to handle things local to a particular session bean, you can use #Predestroy to annotate your releaseResources method. That will inform Spring that you want that method to be invoked when the session ends.
Note that your MyService bean will have to be annotated to be session scoped.

Restart/ReInit a servlet

I want to restart a servlet (declared in web.xml, when JBoss is running) simply because its init-param points to a file which content has changed (i.e. providers.fac below has been modified).
If there is a way to reload the init-param without restarting the servlet, it will be good too.
I suppose I can modify the servlet to add a request param and function to restart itself ?
Is there any other option?
<servlet>
<servlet-name>coverage</servlet-name>
<servlet-class>coverageServlet</servlet-class>
<init-param>
<param-name>ConfigUrl</param-name>
<param-value>file:///C:/coverage/providers.fac</param-value>
</init-param>
<init-param>
<param-name>CacheDir</param-name>
<param-value>coverage</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Environment:
Servlet Api 2.4
JBoss 4.2
Spring Framework 2.5
If you are in jboss you can simply restart a servlet by altering the web.xml file if your servlet is exploded. On linux a touch would do.
Not sure what format your config file is but if you are trying to reload automatically a property configuration file I would have a look at the commons configuration lib that supports this out of the box(FileChangedReloadingStrategy)
If you are planning to restart your servlet automatically and many many times in a day/week you should make sure your permgen is good enough to handle the servlet reloads. There were instances where I had done this in production and burnt myself down with a lot of PermGen errors.
2 options:
Add an extra check on doGet() or doPost() which reloads the file when a certain request parameter is been set while an admin user is logged in and provide an admin screen which invokes that request.
Rewrite the servlet (or refactor the part to ServletContextListener) to store it in ServletContext instead of as an instance variable of the servlet and have some admin screen which reloads the file in ServletContext.
I would separate these concerns by pulling the file management out of the servlet and putting it into a JBoss JMX ServiceMBean. The MBean can take care of watching the file for changes and reloading when necessary, and can also expose the required operations [to the calling servlet]. This will allow you not to have to reload and re-init the servlet (or the WAR) which are fairly heavyweight operations.
I will invent a couple of operations for the FileManager:
public interface FileManagerMBean extends org.jboss.system.ServiceMBean {
public void setFileName(String fileName);
public void setCheckFrequency(long freq);
public String getCoverageData(......);
public String getProviderData(......);
}
The implementation might be (in the same package please :) )
public class FileManager extends org.jboss.system.ServiceMBeanSupport implements FileManagerMBean {
public void setFileName(String fileName) { .... }
public void setCheckFrequency(long freq) { .... }
public String getCoverageData(......) { /* impl */ }
public String getProviderData(......) { /* impl */ }
public void startService() throws Exception {
/* Start a file watcher thread */
}
public void stopService() throws Exception {
/* Stop the file watcher thread */
}
}
Your servlet might look like this:
// A ref to the MBean
FileManagerMBean fileMgr = null;
// The JMX MBean's ObjectName
ObjectName fileMgrOn = org.jboss.mx.util.ObjectNameFactory.create("portoalet.com:service=FileManager");
public void init() {
// Get the JBoss MBeanServer
MBeanServer server = org.jboss.mx.util.MBeanServerLocator.locateJBoss();
// Create an MBeanInvoker for the service
fileMgr = (FileManagerMBean)javax.management.MBeanServerInvocationHandler.newProxyInstance(server, fileMgrOn,FileManagerMBean.class, false);
}
Now you can use the fileMgr instance to make calls to your FileManager MBean, which should be thread safe unless you synchronize access to fileMgr.
I realize this looks a bit over-engineered, but you really should separate the functions of the servlet from the functions of managing the file.

Seam and ServletOutputStream - flush is not immediately visible

I am converting a 6 year old application to Seam 2.2.
The application is used to run in java 1.4 and weblogic 8.
It only uses jsp and servlet.
In one servlet I use:
public void doGet (HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException
{
//...
ServletOutputStream out = = res.getOutputStream();
// displaying a lot of messages
// after each println() I do a flush()
out.println("lots of messages.....");
out.flush();
out.close();
//...
}
When running the application the messages were immediately seen in the browser.
When I run this using Seam 2.2 in Weblogic 10 and Java 1.6 the messages are not immediately seen in the browser.
Only when the servlet is finished running.
Can I change something to fix this?
I do not want to change/convert the servlet into a Seam component. The servlet is running fine. The only thing is the flushing of messages to the browser window which only happens after the servlet has stopped running.
Could it be that the reason is that the servlet now goes through the Seam filter:
<filter>
<filter-name>Seam Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The reason is probably that the request goes through the SeamFilter, as you supposed.
I think it's not the SeamFilter itself that buffer the data stream from your servlet but the Ajax4Jsf filter that is invoked in the filter chain.
If you have RichFaces in the classpath there is a seam component that registers the Ajax4jsf filter in the chain. Namely, the Seam component is org.jboss.seam.web.ajax4jsfFilter.
If you don't need RichFaces try removing it from the classpath. If you need it, I suggest that you override org.jboss.seam.web.ajax4jsfFilter in order to skip the Ajax4Jsf filter for requests directed to your servlet.
Another possible solution is converting your servlet in a filter as a Seam component (see #Filter annotation) and positioning it at the beginning of the chain with the around attribute. Something like:
#Name("FormerServlet")
#Scope(STATELESS)
#BypassInterceptors
#Filter(around = "org.jboss.seam.web.ajax4jsfFilterInstantiator")
public class FormerServletFilter implements Filter
{
protected void init(FilterConfig filterConfig) throws Exception
{
}
protected void doDestroy()
{
}
/**
* Performs the filtering for a request.
*/
protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) throws Exception
{
if (thisRequestShoudBeManagedByMyServlet(request) )
{
// do here what you previously did in the servlet
} else
{
// go ahead with the Seam lifecycle
chain.doFilter(request, response);
}
}
You're running a servlet - there's nothing to do with Seam here. I suspect you need to re-evaluate your design, as there's not really an exact translation from servlet to Seam structure.

Resources