1

I am migrating an application from Java 8 to Java 17, and javax dependencies changed to Jakarta ones. I also updated other dependencies which were dependent on javax.

The problem, we have used Quartz scheduler for scheduling jobs. QuartzInitializerListener is used to get Scheduler object. Also QuartzInitializerListener is added in web.xml as listener. QuartzInitializerListener uses javax library

Eclipse GlassFish 6.2.3

OpenJDK 17

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.2</version>
</dependency>

Scheduler Code

private Scheduler getScheduler() throws SchedulerException {
    StdSchedulerFactory factory = (StdSchedulerFactory) getServletContext().getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
    return factory.getScheduler();
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.quartz.ee.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QuartzInitializerListener implements ServletContextListener {
    public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY";
    private boolean performShutdown = true;
    private boolean waitOnShutdown = false;
    private Scheduler scheduler = null;
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    public QuartzInitializerListener() {
    }

    public void contextInitialized(ServletContextEvent sce) {
        this.log.info("Quartz Initializer Servlet loaded, initializing Scheduler...");
        ServletContext servletContext = sce.getServletContext();

        try {
            String configFile = servletContext.getInitParameter("quartz:config-file");
            if (configFile == null) {
                configFile = servletContext.getInitParameter("config-file");
            }

            String shutdownPref = servletContext.getInitParameter("quartz:shutdown-on-unload");
            if (shutdownPref == null) {
                shutdownPref = servletContext.getInitParameter("shutdown-on-unload");
            }

            if (shutdownPref != null) {
                this.performShutdown = Boolean.valueOf(shutdownPref);
            }

            String shutdownWaitPref = servletContext.getInitParameter("quartz:wait-on-shutdown");
            if (shutdownWaitPref != null) {
                this.waitOnShutdown = Boolean.valueOf(shutdownWaitPref);
            }

            StdSchedulerFactory factory = this.getSchedulerFactory(configFile);
            this.scheduler = factory.getScheduler();
            String startOnLoad = servletContext.getInitParameter("quartz:start-on-load");
            if (startOnLoad == null) {
                startOnLoad = servletContext.getInitParameter("start-scheduler-on-load");
            }

            int startDelay = 0;
            String startDelayS = servletContext.getInitParameter("quartz:start-delay-seconds");
            if (startDelayS == null) {
                startDelayS = servletContext.getInitParameter("start-delay-seconds");
            }

            try {
                if (startDelayS != null && startDelayS.trim().length() > 0) {
                    startDelay = Integer.parseInt(startDelayS);
                }
            } catch (Exception var12) {
                this.log.error("Cannot parse value of 'start-delay-seconds' to an integer: " + startDelayS + ", defaulting to 5 seconds.");
                startDelay = 5;
            }

            if (startOnLoad != null && !Boolean.valueOf(startOnLoad)) {
                this.log.info("Scheduler has not been started. Use scheduler.start()");
            } else if (startDelay <= 0) {
                this.scheduler.start();
                this.log.info("Scheduler has been started...");
            } else {
                this.scheduler.startDelayed(startDelay);
                this.log.info("Scheduler will start in " + startDelay + " seconds.");
            }

            String factoryKey = servletContext.getInitParameter("quartz:servlet-context-factory-key");
            if (factoryKey == null) {
                factoryKey = servletContext.getInitParameter("servlet-context-factory-key");
            }

            if (factoryKey == null) {
                factoryKey = "org.quartz.impl.StdSchedulerFactory.KEY";
            }

            this.log.info("Storing the Quartz Scheduler Factory in the servlet context at key: " + factoryKey);
            servletContext.setAttribute(factoryKey, factory);
            String servletCtxtKey = servletContext.getInitParameter("quartz:scheduler-context-servlet-context-key");
            if (servletCtxtKey == null) {
                servletCtxtKey = servletContext.getInitParameter("scheduler-context-servlet-context-key");
            }

            if (servletCtxtKey != null) {
                this.log.info("Storing the ServletContext in the scheduler context at key: " + servletCtxtKey);
                this.scheduler.getContext().put(servletCtxtKey, servletContext);
            }
        } catch (Exception var13) {
            this.log.error("Quartz Scheduler failed to initialize: " + var13.toString());
            var13.printStackTrace();
        }

    }

    protected StdSchedulerFactory getSchedulerFactory(String configFile) throws SchedulerException {
        StdSchedulerFactory factory;
        if (configFile != null) {
            factory = new StdSchedulerFactory(configFile);
        } else {
            factory = new StdSchedulerFactory();
        }

        return factory;
    }

    public void contextDestroyed(ServletContextEvent sce) {
        if (this.performShutdown) {
            try {
                if (this.scheduler != null) {
                    this.scheduler.shutdown(this.waitOnShutdown);
                }
            } catch (Exception var3) {
                this.log.error("Quartz Scheduler failed to shutdown cleanly: " + var3.toString());
                var3.printStackTrace();
            }

            this.log.info("Quartz Scheduler successful shutdown.");
        }
    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Things in `javax` have moved out of the JRE. Also Java EE stuff has moved to a new namespace. Have a look at https://stackoverflow.com/a/71529414/18619318. You will most likely need a newer version of Quartz and the latest Glassfish – user18619318 May 18 '22 at 12:07
  • @user18619318 I am using latest Glassfish compatible with java 17. I have also updated the namespace to jakarta(new). New version of quartz is using javax.servlet – WajahatAlvi May 18 '22 at 12:20
  • 1
    @user18619318 `javax.servlet` was never in the JRE. The problem is that GlassFish 6.2.3 is a Jakarta EE 9.1 implementation, and since Jakarta 9, the package names changed from `javax` to `jakarta`. – Mark Rotteveel May 18 '22 at 12:40
  • 1
    Given Quartz still uses the old Java EE/Jakarta 8 package names, you'll need to write your own equivalent listener using the `jakarta` package names (either as one-off code, or by forking Quartz and creating your own version with the fixes). Given the silence on issues like [this](https://github.com/quartz-scheduler/quartz/issues/737) and [this](https://github.com/quartz-scheduler/quartz/issues/794), I don't think you should hold your breath for the project to fix it for you. – Mark Rotteveel May 18 '22 at 12:46
  • @MarkRotteveel Thank you for the answer. I have created a Custom listener which is using Jakarta package names. – WajahatAlvi May 24 '22 at 06:51

1 Answers1

1

What I mention below, in general should never be done; however, since you are in a fix, you could experiment with the idea.

The idea in general is simple, 'wrapper':

  1. Create your own context listener, register in place of Quartz listener (as suggested above)
  2. Write a ServletContext wrapper which dispatches all method calls to the wrapped 'jakarta' servlet context
  3. ServletContextEvent is a class, with one method, use the javax class itself
  4. From your listener call the corresponding context initialized/destroyed
  5. Have the javax.servlet jar in the classpath

Then start the Glassfish ... who knows what happens next ... it might just work or fail spectacularly ... :D

Ironluca
  • 3,402
  • 4
  • 25
  • 32
  • I created a own listener of Quartz and added that into web.xml. The new listener is using only jakarta name packages. Before implementing this It was assumed that architecture of quartz keeps listener separate from internal functionality. my assumptions seems right for now. :D – WajahatAlvi May 24 '22 at 06:55