Giter Site home page Giter Site logo

michaelklishin / quartz-mongodb Goto Github PK

View Code? Open in Web Editor NEW
247.0 33.0 197.0 977 KB

A MongoDB-based store for the Quartz scheduler. This fork strives to be as feature complete as possible. Originally by MuleSoft.

License: Other

Java 65.63% Groovy 34.37%
mongodb quartz-scheduler quartz

quartz-mongodb's Introduction

A MongoDB-based store for Quartz

Be Aware of Abandonware

This project IS NO LONGER MAINTAINED. If you plan on using it, be ready to address any issues on your own and produce your own releases. Issues that ask others to do something will be ignored. Pull requests may be merged on a very random schedule.

What is This?

This is a MongoDB-backed job store for the Quartz scheduler.

Maven Artifacts

Artifacts are released to Bintray.

If you are using Maven, add the following repository definition to your pom.xml:

<repositories>
    <repository>
        <id>jcenter</id>
        <url>https://jcenter.bintray.com/</url>
    </repository>
</repositories>

With Gradle, add the following to your build.gradle:

repositories {
    maven {
        url "https://jcenter.bintray.com/"
    }
}

The Most Recent Release

With Maven:

<dependency>
    <groupId>com.novemberain</groupId>
    <artifactId>quartz-mongodb</artifactId>
    <version>2.2.0-rc2</version>
</dependency>

With Gradle:

compile "com.novemberain:quartz-mongodb:2.2.0-rc2"

Usage

Like most things in Quartz, this job store is configured via a property file, quartz.properties:

# Use the MongoDB store
org.quartz.jobStore.class=com.novemberain.quartz.mongodb.MongoDBJobStore
# MongoDB URI (optional if 'org.quartz.jobStore.addresses' is set)
org.quartz.jobStore.mongoUri=mongodb://localhost:27020
# comma separated list of mongodb hosts/replica set seeds (optional if 'org.quartz.jobStore.mongoUri' is set)
org.quartz.jobStore.addresses=host1,host2
# database name
org.quartz.jobStore.dbName=quartz
# Will be used to create collections like mycol_jobs, mycol_triggers, mycol_calendars, mycol_locks
org.quartz.jobStore.collectionPrefix=mycol
# thread count setting is ignored by the MongoDB store but Quartz requries it
org.quartz.threadPool.threadCount=1

Error Handling in Clustered Mode

When running in clustered mode, the store will periodically check in with the cluster. Should that operation fail, the store needs to decide what to do:

  • Shut down
  • Do nothing and optimistically proceed

Different strategies make sense in different scenarios. Pausing Quartz would be optimal but this job store currently doesn't have that option.

The org.quartz.jobStore.checkInErrorHandler.class property controls the error handler implementation.

To shut down the JVM (which is the default), add the following key to quartz.properties

org.quartz.jobStore.checkInErrorHandler.class=com.novemberain.quartz.mongodb.cluster.KamikazeErrorHandler

to ignore the failure:

org.quartz.jobStore.checkInErrorHandler.class=com.novemberain.quartz.mongodb.cluster.NoOpErrorHandler

Clojure and Quartzite

If you use Quartzite or want your job classes to be available to Clojure code, use:

org.quartz.jobStore.class=com.novemberain.quartz.mongodb.DynamicMongoDBJobStore

(this assumes Clojure jar is on classpath).

Job Data storage

By default you are allowed to pass any java.io.Serializable objects inside JobDataMap. It will be serialized and stored as a base64 string.

If your JobDataMap only contains simple types, it may be stored directly inside MongoDB to save some performance.

org.quartz.jobStore.jobDataAsBase64=false

Clustering

To enable clustering set the following property:

# turn clustering on:
org.quartz.jobStore.isClustered=true

# Must be unique for each node or AUTO to use autogenerated:
org.quartz.scheduler.instanceId=AUTO
# org.quartz.scheduler.instanceId=node1

# The same cluster name on each node:
org.quartz.scheduler.instanceName=clusterName

Each node in a cluster must have the same properties, except instanceId. To setup other clusters use different collection prefix:

org.quartz.scheduler.collectionPrefix=yourCluster

Different time settings for cluster operations:

# Frequency (in milliseconds) at which this instance checks-in to cluster.
# Affects the rate of detecting failed instances.
# Defaults to 7500 ms.
org.quartz.jobStore.clusterCheckinInterval=10000

# Time in millis after which a trigger can be considered as expired.
# Defaults to 10 minutes:
org.quartz.jobStore.triggerTimeoutMillis=1200000

# Time in millis after which a job can be considered as expired.
# Defaults to 10 minutes:
org.quartz.jobStore.jobTimeoutMillis=1200000

# Time limit in millis after which a trigger should be treated as misfired.
# Defaults to 5000 ms.
org.quartz.jobStore.misfireThreshold=10000

# WriteConcern timeout in millis when writing in Replica Set.
# Defaults to 5000 ms.
org.quartz.jobStore.mongoOptionWriteConcernTimeoutMillis=10000

Continuous Integration

Build Status

CI is hosted by Travis CI

Copyright & License

(c) Michael S. Klishin, Alex Petrov, 2011-2020.

Apache Public License 2.0

FAQ

Project Origins

The project was originally started by MuleSoft. It supports all Quartz trigger types and tries to be as feature complete as possible.

Why the Fork?

MuleSoft developers did not respond to attempts to submit pull requests for several months. As more and more functionality was added and implementation code refactored, I decided to completely separate this fork form GitHub forks network because the project is now too different from the original one. All changes were made with respect to the Apache Public License 2.0.

quartz-mongodb's People

Contributors

arulrajnet avatar benromberg avatar chevron94 avatar chrismyang avatar dandiep avatar daniel-h avatar dorogush avatar hofmeister avatar huang1900z avatar ifesdjeen avatar illyr avatar jacobsatelbrys avatar jochenberger avatar jwicks avatar killme2008 avatar korest avatar lordbuddha avatar michaelklishin avatar mixamlv avatar msimerson avatar parashivamurthykr avatar pwojnowski avatar sboekhoff avatar seanhoganskyhawk avatar sebastianlng avatar seilnacht avatar siscia avatar slawomirsepiol avatar vivganes avatar vladvoic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quartz-mongodb's Issues

Invalid CronTrigger can cause inability to delete / change job

When the MongDBJobStore deserializes a CronTrigger (which it does all the time), calling setEndTime will throw an IllegalArgumentException and prevent whatever operation was attempted (e.g. replace trigger, remove job)

Should probably have something like a try / catch around the setters in "MongoDBJobStore#toTrigger".

Could not find job with key ...

Hi,

I've been testing replacing Quartz DB clustering with yours and I am running into an issue when the scheduler starts:

org.quartz.JobPersistenceException: Could not find job with key DEFAULT.FeedLoader
    at com.novemberain.quartz.mongodb.MongoDBJobStore.storeTrigger(MongoDBJobStore.java:194) ~[quartz-mongodb-1.3.0-beta1.jar:na]
    at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:886) ~[quartz-2.1.7.jar:na]
    at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:259) ~[quartz-2.1.7.jar:na]
    at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:371) ~[spring-context-support-3.2.3.RELEASE.jar:3.2.3.RELEASE]

Note that I am using the following:

  • Spring Framework (3.2.3)
  • Quartz (2.1.7)
  • MongoDB (2.4.5)

Here is a extract from my Spring wiring:

<bean id="feedLoaderTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail">
            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
                <property name="name" value="FeedLoader" />
                <property name="jobClass" value="..my job class..." />
                <property name="durability" value="true" />
            </bean>
        </property>
        <property name="cronExpression" value="...my cron expression..." />
    </bean>
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="schedulerName" value="ClusteredScheduler" />
        <property name="triggers">
            <list>
                <ref bean="feedLoaderTrigger"/>
            </list>
        </property>
        <property name="applicationContextSchedulerContextKey">
            <value>applicationContext</value>
        </property>
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.scheduler.instanceName">ClusteredScheduler</prop>
                <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
                <prop key="org.quartz.threadPool.threadCount">4</prop>

                <prop key="org.quartz.jobStore.class">com.novemberain.quartz.mongodb.MongoDBJobStore</prop>
                <prop key="org.quartz.jobStore.addresses">...</prop>
                <prop key="org.quartz.jobStore.username">...</prop>
                <prop key="org.quartz.jobStore.password">...</prop>
                <prop key="org.quartz.jobStore.dbName">quartz</prop>
            </props>
        </property>
    </bean>

MongoDBJobStore.storeJobInMongo is invoked but it is invoked with replaceExisting set to true. Which causes an update on a non existing record. The flag is set to true in the Spring code ( org.springframework.scheduling.quartz.addJobToScheduler ). That said I think storeJobInMongo() should be creating the record regardless of replaceExisting if it doesn't exist.

What do you think? Happy to contribute a fix you think it makes sense.

Thanks,
Ludovic

Spring Test

This isn't an issue, but more of a request.
I'm some what new to Spring and quartz and notice when using your jar I get an incompatible class exception. Looking it up I see that Spring 3 doesn't play well with quartz 2, but I also have seen other examples online where people are using them together.

I'm politely asking if it's possible to create a full test suite using Java, Spring, and quartz-mongodb or a how-to Wiki?

Triggers are offset by one in MongoJobStore (MAJOR)

What am I trying to do?
I have a simple job, which prints context.getScheduledFireTime() every time it is triggered.

What error do I see?
What actually happens is that, scheduledFireTimes are offset by one. (This is a time-bomb indeed!)

To explain clearly, here is a comparison of actual and expected values of scheduledFireTime.

selection_060

and so on...!

Why is this a problem with quartz-mongodb and not the Quartz Scheduler?
Because,this issue happens only when using MongoDBJobStore. While using RAMJobStore, this problem does not happen.

Any more information, that might help?
I compared the code, particularly triggersFired() methods, of RAMJobStore (provided default by quartz-scheduler) and MongoJobStore (provided by quartz-mongodb).

The triggersFired() method does not call

trigger.triggered(cal); 

similar to the way RAMJobStore calls it. I hope this would fix the issue.

Regards,
Vivek

Error in setting org.quartz.jobStore.mongoUri in quartz.properties

I used the following quartz.properties file.

# Use the MongoDB store
org.quartz.jobStore.class=com.novemberain.quartz.mongodb.MongoDBJobStore
# MongoDB URI (optional if 'org.quartz.jobStore.addresses' is set)
org.quartz.jobStore.mongoUri=<<my Mongo URI?>>
# comma separated list of mongodb hosts/replica set seeds (optional if 'org.quartz.jobStore.mongoUri' is set)
#org.quartz.jobStore.addresses=localhost
org.quartz.jobStore.dbName=<<my db name>>
# Will be used to create collections like mycol_jobs, mycol_triggers, mycol_calendars, mycol_locks
org.quartz.jobStore.collectionPrefix=quartz
# thread count setting is ignored by the MongoDB store but Quartz requries it
org.quartz.threadPool.threadCount=10

I get the following exception during initialization of Quartz.

Stack trace:

Caused by: java.lang.NoSuchMethodException: No setter for property 'mongoUri'
at org.quartz.impl.StdSchedulerFactory.setBeanProps(StdSchedulerFactory.java:1372)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:848)
... 8 more

java.lang.RuntimeException: json can't serialize type : class org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean

I get following error when using this tool with Spring 3.2.4, Spring Data MongoDB 1.3.1, and Quartz 2.1.7

java.lang.RuntimeException: json can't serialize type : class org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
at com.mongodb.util.ClassMapBasedObjectSerializer.serialize(ClassMapBasedObjectSerializer.java:77)
at com.mongodb.util.JSONSerializers$MapSerializer.serialize(JSONSerializers.java:307)
at com.mongodb.util.ClassMapBasedObjectSerializer.serialize(ClassMapBasedObjectSerializer.java:79)

My quartz-job.xml looks like this

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<!-- The bean that does the actual work -->
<bean id="myTask" class="com.abcd.quartz.MyTask" />

<!-- Quartz uses Trigger, Job and JobDetail objects to realize scheduling of all kinds of jobs. 
        For the basic concepts behind Quartz, have a look at http://www.opensymphony.com/quartz.
        See: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/scheduling.html#scheduling-quartz -->
<bean id="jobAbcd" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="myTask" />
  <property name="targetMethod" value="doAbcd" />
  <property name="concurrent" value="false" />
</bean>

<!-- startDelay: Delay 10 seconds 
        cronExpression:Repeat every 5 minutes -->       
<bean id="cronTrigger4jobAbcd" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="jobAbcd" /> 
        <property name="startDelay" value="10000" />
        <property name="cronExpression" value="0 0/5 * * * ?" />
 </bean>

<!--  SCHEDULER FOR ALL JOBS -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="waitForJobsToCompleteOnShutdown" value="true" />
        <property name="triggers">
            <list>
                <ref bean="cronTrigger4jobAbcd" />
            </list>
        </property>
        <property name="quartzProperties"> 
            <props> 
                <prop key="org.quartz.jobStore.class">com.novemberain.quartz.mongodb.MongoDBJobStore</prop>
                <prop key="org.quartz.jobStore.mongoUri">mongodb://localhost:27017</prop> 
                <prop key="org.quartz.jobStore.dbName">mydb</prop>
                <prop key="org.quartz.jobStore.collectionPrefix">qrtz</prop>
                <prop key="org.quartz.threadPool.threadCount">4</prop><!-- thread count setting is ignored by the MongoDB store but Quartz requires it  -->
            </props>
        </property>
</bean>

MyTask.java

package com.abcd.quartz;
public class MyTask{    
    public void doAbcd() {      
            System.out  
            .println("Current Time : " + Calendar.getInstance().getTime());             
    }   
}

JobDetail orphaned after deleting only Trigger referencing it

Hello Michael,

We have continued our use of the MongoDb data store and we have found another behavior that we are not quite sure is the right one. Basically, when you schedule a Trigger (specifically, we are using Cron Triggers) along with a JobDetail and after the Trigger starts the Job execution, the trigger is removed from the quartz_triggers collection as expected. However, the Job detail remains in the corresponding collection, quartz_jobs.

We have made sure that the job detail is non-durable and that there are no other Triggers with a reference to a specific JobDetail. More precisely, our implementation has a one-to-one relationship between Triggers and Jobs. We have tracked down the conflict to the MongoDBJobStore#removeTrigger(TriggerKey triggerKey) method. Our current understanding of the problem is as follows:

Is there any other logic that we are missing that might be taking care of this orphaned job record (document) in the quartz_jobs collection? Any hints are appreciated. :)

Thanks for your reply,

P.S. We noticed that the storeJobInMongo method (https://github.com/michaelklishin/quartz-mongodb/blob/master/src/main/java/com/novemberain/quartz/mongodb/MongoDBJobStore.java#L905) is not putting the JOB_DURABILITY field in the job BasicDBObject instance. Although this does not affect our above points, we found that it might cause an unexpected behavior in other situations.

Stack overflow exception is thrown

Here's the exception:

Exception in thread "Executor_QuartzSchedulerThread" java.lang.StackOverflowError
    at sun.nio.cs.UTF_8$Decoder.decodeArrayLoop(UTF_8.java:241)
    at sun.nio.cs.UTF_8$Decoder.decodeLoop(UTF_8.java:305)
    at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:544)
    at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:140)
    at java.lang.StringCoding.decode(StringCoding.java:173)
    at java.lang.String.<init>(String.java:443)
    at org.bson.BasicBSONDecoder$BSONInput.readUTF8String(BasicBSONDecoder.java:455)
    at org.bson.BasicBSONDecoder.decodeElement(BasicBSONDecoder.java:155)
    at org.bson.BasicBSONDecoder._decode(BasicBSONDecoder.java:79)
    at org.bson.BasicBSONDecoder.decode(BasicBSONDecoder.java:57)
    at com.mongodb.DefaultDBDecoder.decode(DefaultDBDecoder.java:56)
    at com.mongodb.Response.<init>(Response.java:83)
    at com.mongodb.DBPort.go(DBPort.java:124)
    at com.mongodb.DBPort.go(DBPort.java:88)
    at com.mongodb.DBPort.findOne(DBPort.java:143)
    at com.mongodb.DBPort.runCommand(DBPort.java:148)
    at com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:140)
    at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:183)
    at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:155)
    at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:249)
    at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:205)
    at com.mongodb.DBCollection.insert(DBCollection.java:57)
    at com.mongodb.DBCollection.insert(DBCollection.java:100)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.acquireNextTriggers(MongoDBJobStore.java:496)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.acquireNextTriggers(MongoDBJobStore.java:517)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.acquireNextTriggers(MongoDBJobStore.java:517)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.acquireNextTriggers(MongoDBJobStore.java:517)

The problem is that the lock it tries to find really exists but it has a different "instanceId". In this case lock search will fail, and the method will be called again until the exception is thrown.

Jobs are triggered even after the trigger is paused

I see that jobs are triggered even after trigger is paused.

See a snippet of my code below.

 scheduler.scheduleJob(cronJob,cronTrigger);
 scheduler.pauseTrigger(cronTrigger.getKey());

Even after the trigger has been paused, I see the following lines printed in log.

2014-07-15 18:28:17 DEBUG - Storing job DEFAULT.firstCronJob and trigger DEFAULT.cronTrigger
2014-07-15 18:28:18 DEBUG - Finding up to 1 triggers which have time less than Tue Jul 15 18:28:48 IST 2014
2014-07-15 18:28:18 DEBUG - Found 0 triggers which are eligible to be run.
2014-07-15 18:28:18 DEBUG - batch acquisition of 0 triggers
2014-07-15 18:28:18 DEBUG - Finding up to 1 triggers which have time less than Tue Jul 15 18:28:48 IST 2014
2014-07-15 18:28:18 DEBUG - Found 0 triggers which are eligible to be run.
2014-07-15 18:28:19 DEBUG - batch acquisition of 0 triggers
2014-07-15 18:28:19 DEBUG - Finding up to 1 triggers which have time less than Tue Jul 15 18:28:49 IST 2014
2014-07-15 18:28:19 DEBUG - Found 0 triggers which are eligible to be run.
2014-07-15 18:28:19 DEBUG - batch acquisition of 0 triggers
2014-07-15 18:28:48 DEBUG - Finding up to 1 triggers which have time less than Tue Jul 15 18:29:18 IST 2014
2014-07-15 18:28:48 DEBUG - Found 1 triggers which are eligible to be run.
2014-07-15 18:28:48 DEBUG - Inserting lock for trigger DEFAULT.cronTrigger
2014-07-15 18:28:49 DEBUG - Aquired trigger DEFAULT.cronTrigger
2014-07-15 18:28:49 DEBUG - batch acquisition of 1 triggers
2014-07-15 18:29:00 DEBUG - Fired trigger DEFAULT.cronTrigger
2014-07-15 18:29:00 DEBUG - Producing instance of Job 'DEFAULT.firstCronJob', class=CronJob
2014-07-15 18:29:00 DEBUG - Calling execute on job DEFAULT.firstCronJob
Tue Jul 15 18:29:00 IST 2014: Scheduled Fire Time: Tue Jul 15 18:29:00 IST 2014, Next Fire Time: Tue Jul 15 18:30:00 IST 2014, Fired at : Tue Jul 15 18:29:00 IST 2014
2014-07-15 18:29:00 DEBUG - Trigger completed DEFAULT.cronTrigger
2014-07-15 18:29:02 DEBUG - Removing trigger lock DEFAULT.cronTrigger.NON_CLUSTERED
2014-07-15 18:29:02 DEBUG - Trigger lock DEFAULT.cronTrigger.NON_CLUSTERED removed.
2014-07-15 18:29:02 DEBUG - Finding up to 1 triggers which have time less than Tue Jul 15 18:29:32 IST 2014
2014-07-15 18:29:02 DEBUG - Found 0 triggers which are eligible to be run.
2014-07-15 18:29:02 DEBUG - batch acquisition of 0 triggers

Expectation is that the trigger should not be fired since it has been paused.

The same code works as expected while using RAMJobStore.

MongoDBJobStore should have constructors

If you want to initialize a MongoDBJobStore using a preconfigured Mongo(Client) instance, you have to override a static field of MongoDBJobStore. On the other hand, if you want to configure it using parameters like host name, user and password or uri, user and password, you have to call different setters on an "empty" MongoDBJobStore instance.
I think a better option would be a constructor that receives a Mongo(Client) instance and a set of other constructors that take different sets of required parameters and delegate to the former one.
Therefore, in my opinion, the setters for connection-related parameters should be deprecated and removed altogether in a later release. Besides, calling them after the connection has been initialized won't have any effect anyway.

Rescheduling Non-Durable Job Breaks

If you have a job with a single trigger, and during the execution of that job you make a call to reschedule that job, the code fails because replaceTrigger calls removeTrigger which also deletes the associated job if that was the only trigger associated with the job and it was not durable.

So, in replaceTrigger,

replace

removeTrigger(triggerKey);

With

BasicDBObject dbObject = Keys.keyToDBObject(triggerKey);
DBCursor triggers = triggerCollection.find(dbObject);
if (triggers.count() > 0) {
  triggerCollection.remove(dbObject);
}

It might be worth considering adding an appropriate lock around the remove of the old trigger and the adding of the new.

I am not able to get the schduler run, because there is a strange "open" exception

This is the Exception I get on start

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "default_QuartzSchedulerThread" org.quartz.JobPersistenceException: open [See nested exception: java.lang.IllegalStateException: open]
    at com.novemberain.quartz.mongodb.MongoDBJobStore.releaseAcquiredTrigger(MongoDBJobStore.java:640)
    at org.quartz.core.QuartzSchedulerThread.releaseIfScheduleChangedSignificantly(QuartzSchedulerThread.java:432)
    at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:316)
Caused by: java.lang.IllegalStateException: open
    at org.bson.util.Assertions.isTrue(Assertions.java:36)
    at com.mongodb.DBTCPConnector.releasePort(DBTCPConnector.java:421)
    at com.mongodb.DBCollectionImpl.remove(DBCollectionImpl.java:233)
    at com.mongodb.DBCollectionImpl.remove(DBCollectionImpl.java:201)
    at com.mongodb.DBCollection.remove(DBCollection.java:274)
    at com.mongodb.DBCollection.remove(DBCollection.java:301)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.removeTriggerLock(MongoDBJobStore.java:1202)
    at com.novemberain.quartz.mongodb.MongoDBJobStore.releaseAcquiredTrigger(MongoDBJobStore.java:638)
    ... 2 more

This is my properties file:

# Main Quartz configuration
org.quartz.scheduler.skipUpdateCheck = true
org.quartz.scheduler.instanceName = default
org.quartz.scheduler.jobFactory.class = org.quartz.simpl.SimpleJobFactory
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# org.quartz.threadPool.threadCount = 5

# Use the MongoDB store
org.quartz.jobStore.class=com.novemberain.quartz.mongodb.MongoDBJobStore
# MongoDB URI (optional if 'org.quartz.jobStore.addresses' is set)
org.quartz.jobStore.mongoUri=mongodb://localhost:27017
# comma separated list of mongodb hosts/replica set seeds (optional if 'org.quartz.jobStore.mongoUri' is set)
#org.quartz.jobStore.addresses=host1,host2
# database name
org.quartz.jobStore.dbName=quartz
# Will be used to create collections like mycol_jobs, mycol_triggers, mycol_calendars, mycol_locks
org.quartz.jobStore.collectionPrefix=
# thread count setting is ignored by the MongoDB store but Quartz requries it
org.quartz.threadPool.threadCount=5

And this is the class I try to run:

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Created by kic on 22.01.15.
 */
public class TestStore {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // define the job and tie it to our HelloJob class
        JobDetail job = newJob(FooJob.class)
                .withIdentity("job1", "group1")
                .build();

        // Trigger the job to run now, and then repeat every 40 seconds
        Trigger trigger = newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(3)
                        .repeatForever())
                .build();

        // Tell quartz to schedule the job using our trigger
        try {
            scheduler.scheduleJob(job, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }

        scheduler.start();
        Thread.sleep(6000);
        scheduler.shutdown();
    }

    public class FooJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            System.out.println("lala");
        }
    }
}

Cluster support?

The jdbc JobStore supports cluster, i found that the mongodb JobStore implementation isClustered always return true.Is it supports quartz cluster?

MongoDBJobStore#replaceTrigger does not work

replaceTrigger does not work. The implementation first removes an existing trigger and then stores the new one. When removing the existing one, the corresponding job is removed as well, so storing the new trigger throws the exception complaining that the job is not there.
See RAMJobStore as a reference for a correct implementation: first must be checked, that both triggers point to the same job and then the old one trigger must be replace with the new one without affecting the referenced job.

Can't have job with multiple triggers

Trying to schedule a job with a second trigger causes (line numbers not reflective of master branch).

org.quartz.ObjectAlreadyExistsException: {....}
at com.novemberain.quartz.mongodb.MongoDBJobStore.storeJobInMongo(MongoDBJobStore.java:1093)
at com.novemberain.quartz.mongodb.MongoDBJobStore.storeJobAndTrigger(MongoDBJobStore.java:118)
at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:840)
at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:250)

If the Job already exists, cool, ignore the error and just add the trigger.... There is supposed to be support for a one (job) to many (triggers) relationship here.

Was trying to add the second trigger to test out my DisallowConcurrentExecution support.

Can't connect to a mongo node with login/password

It's not possible to connect to a mongo using a credential.
It should be possible to add a user/password. The drivers should use MongoDbUtils.getDB if login/password is set.

Edit: Sorry, i read the code and i see that it is possible.

"Error retrieving expired lock from the database. Maybe it was deleted" debug message?

The scheduler behavior is fine with the exception of this error and I was wondering why is this logged as an error. I am trying to have a repeatable job:

        JobDetail job = newJob(QuartzPollTaskSimpleJob.class)
                .withIdentity("simpleJob", "pollTasks")
                .usingJobData("schedId", schedId)
                .requestRecovery( true )
                .build();
        SimpleTrigger trigger = newTrigger()
                .withIdentity("simpleTrigger", "pollTasks")
                .startNow()
                .withSchedule(simpleSchedule()
                    .withRepeatCount(10)
                    .withIntervalInSeconds(10))
                .build();

And besides the error message, the behavior is normal. And It also only triggers only some times.

Executed the Job: a-test.com1350568949614
Doodle 2012-10-18 16:13:00 [QuartzScheduler_QuartzSchedulerThread] ERROR c.n.quartz.mongodb.MongoDBJobStore -> Error retrieving expired lock from the database. Maybe it was deleted
Executed the Job: doodle-test.com1350568949614
Doodle 2012-10-18 16:13:10 [QuartzScheduler_QuartzSchedulerThread] ERROR c.n.quartz.mongodb.MongoDBJobStore -> Error retrieving expired lock from the database. Maybe it was deleted
Executed the Job: a-test.com1350568949614
Executed the Job: a-test.com1350568949614

Misfire can hang the main quart scheduler thread

When there is a misfire, depending of the misfire policy, the trigger may be updated to a new fire time that may well be some minute into the future and well base the time up to which triggers are supposed to be acquired.

This can result in a trigger being acquired and returned to the quart scheduler thread which then blocks until that trigger is due to fire. All jobs are blocked until that time.

RuntimeException no more

Seeing low (relatively) levels of this exception in production.

2013-12-13 14:01:27,147 53567111 ERROR [CJS] QuartzSchedulerThread - quartzSchedulerThreadLoop: RuntimeException no more
java.util.NoSuchElementException: no more
at com.mongodb.DBApiLayer$Result.next(DBApiLayer.java:384)
at com.mongodb.DBApiLayer$Result.next(DBApiLayer.java:346)
at com.mongodb.DBCursor._next(DBCursor.java:421)
at com.mongodb.DBCursor.next(DBCursor.java:494)
at com.novemberain.quartz.mongodb.MongoDBJobStore.removeTrigger(MongoDBJobStore.java:205)
at com.novemberain.quartz.mongodb.MongoDBJobStore.doAcquireNextTriggers(MongoDBJobStore.java:550)
at com.novemberain.quartz.mongodb.MongoDBJobStore.acquireNextTriggers(MongoDBJobStore.java:485)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:264)

Looking at the code, it seems that the following

  public boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException {
    BasicDBObject dbObject = Keys.keyToDBObject(triggerKey);
    DBCursor triggers = triggerCollection.find(dbObject);
    if (triggers.count() > 0) {
      DBObject trigger = triggers.next();
      if (trigger.containsField(TRIGGER_JOB_ID)) {
        // There is only 1 job per trigger so no need to look further.
        DBObject job = jobCollection.findOne(new BasicDBObject("_id", trigger.get(TRIGGER_JOB_ID)));
        // Remove the orphaned job if it's durable and has no other triggers associated with it,
        // remove it
        if (!job.containsField(JOB_DURABILITY) || job.get(JOB_DURABILITY).toString().equals("false")) {
          DBCursor referencedTriggers = triggerCollection.find(new BasicDBObject(TRIGGER_JOB_ID, job.get("_id")));
          if (referencedTriggers != null && referencedTriggers.count() <= 1) {
            jobCollection.remove(job);
          }
        }
      } else {
        log.debug("The trigger had no associated jobs");
      }
      triggerCollection.remove(dbObject);

      return true;
    }

    return false;
  }

should / could be changed to

  public boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException {
    BasicDBObject dbObject = Keys.keyToDBObject(triggerKey);
    List<DBObject> triggers = triggerCollection.find(dbObject).limit(2).toArray();
    if (triggers.size() > 0) {
      DBObject trigger = triggers.get(0);
      if (trigger.containsField(TRIGGER_JOB_ID)) {
        // There is only 1 job per trigger so no need to look further.
        DBObject job = jobCollection.findOne(new BasicDBObject("_id", trigger.get(TRIGGER_JOB_ID)));
        // Remove the orphaned job if it's durable and has no other triggers associated with it,
        // remove it
        if (job != null && (!job.containsField(JOB_DURABILITY) || job.get(JOB_DURABILITY).toString().equals("false"))) {
          List<DBObject> referencedTriggers = triggerCollection.find(new BasicDBObject(TRIGGER_JOB_ID, job.get("_id"))).limit(2).toArray();
          if (referencedTriggers.size() == 1) {
            jobCollection.remove(job);
          }
        }
      } else {
        log.debug("The trigger had no associated jobs");
      }
      triggerCollection.remove(dbObject);

      return true;
    }

    return false;
  }

The reason is that .count() does not indicate the number of results the cursor has but rather what the DB currently has... hence the possibility for this error to occur. Using .limit(2).toArray() and .size() operates solely on the cursor and thus hopefully preclude this from happening.

Can update my fork and issue a pull if this is agreed.

New triggers inserted, rather than updated

I'm running into an issue where, every time the trigger is updated and then persisted, a new copy of that trigger is inserted, rather than updating โ€“ so every time the main event runs, I get a new copy of all my triggers. This looks like it might be related to Issue #17 though not sure.

According to RamJobStore#storeTrigger, it looks like triggers get updated (or replaced rather), but I'm no expert on Quartz so forgive me if my understanding of the semantics is wrong.

If this is truly incorrect behavior, it looks like it's caused by two things:

  1. the index you add to the trigger collection includes nextFireTime and previousFireTime (https://github.com/michaelklishin/quartz-mongodb/blob/v1.2.0/src/main/java/com/novemberain/quartz/mongodb/MongoDBJobStore.java#L858)...
  2. ...which would be fine except that you rely on the uniqueness of that index to detect when an update should occur (https://github.com/michaelklishin/quartz-mongodb/blob/v1.2.0/src/main/java/com/novemberain/quartz/mongodb/MongoDBJobStore.java#L895)

Happy to submit a pull request if you can verify that what I'm saying makes sense - although it's such a small fix might be easier and safer for you to do yourself.

Thanks!

4-space identation for .java files

It's not a bug, but a proposal to use 4-space identation in .java files instead of 2-space.
I believe it will increase readability of the code.

Storing Trigger JobDetailMap

Hi,

Thank you for making all of this work available to the community. I started using the library and noticed that the 'JobDataMap' collection belonging to the Trigger objects is never persisted in the corresponding MongoDb collection. Just to be sure about whether this was Quartz's standard behavior, I looked at the implementation in the org.quartz.impl.jdbcjobstore.StdJDBCDelegate to see if it indeed persists the JobDataMap for both the JobDetail and Trigger objects. I found it does. See methods insertJobDetail(Connection conn, JobDetail job) and public int insertTrigger(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) here http://svn.terracotta.org/svn/quartz/trunk/quartz-core/src/main/java/org/quartz/impl/jdbcjobstore/StdJDBCDelegate.java.

I understand that it is difficult to map the each and every one of the features in a MongoDB-based implementation, but I was wondering if there had been any particular reason that had made you skip this field while persisting the trigger object into MongoDB.

Thanks for your reply.

NullPointerException in MongoDBJobStore.getTriggersForJob(jobKey) if jobKey has no associated Mongo document

How to reproduce

Call MongoDBJobStore.getTriggersForJob() with a fake jobKey

Expected behavior

should return an empty list (like in RamJobStore).

Code fix
public List<OperableTrigger> getTriggersForJob(JobKey jobKey) throws JobPersistenceException {
   final List<OperableTrigger> triggers = new ArrayList<OperableTrigger>();
   final DBObject dbObject = findJobDocumentByKey(jobKey);
   if(dbObject  == null) {
      return triggers;
   }
   final DBCursor cursor = triggerCollection.find(new BasicDBObject(TRIGGER_JOB_ID, dbObject.get("_id")));
   while (cursor.hasNext()) {
      triggers.add(toTrigger(cursor.next()));
   }
    return triggers;
}

Allow integration testing with mocked Mongo

I'd like to be able to mock the Mongo implementation (e.g. with a framework like Fongo) when working with the MongoDBJobStore.

This would allow us to write integration tests without requiring a local Mongo installation.

Not sure how an implementation of this could look like, as the MongoDBJobStore is created by Quartz itself. Injecting a Mongo mock could either work with yet another entry in the quartz.properties, or by statically overwriting the Mongo implementation to use. Both not very nice, maybe there's a cleaner way to do this.

Grails Support

Would it be possible to integrate quartz-mongodb with a Grails project?

I'm not sure what would be involved and perhaps its already possible in its current state?

I've attempted to integrate it with a grails 2.3.3 project but was unsuccessful. However this could be due to the fact that I have limited experience with Quartz and I've missed a piece of configuration.

.idea in the root of the project

Root of the project contains .idea folder in git.
IDE specific stuff is usually specified in git ignore global files, so the proposal is to remove it from git.

Even more info:
https://github.com/michaelklishin/quartz-mongodb/blob/master/.idea/modules.xml
contains references on non-existing files:

  1. src/main/Main.iml
  2. quartz-mongodb.iml

and trying to do as follows:

  1. git clone https://github.com/michaelklishin/quartz-mongodb.git
  2. Intellij IDEA 13 -> File -> Open produces an error (see screenshot in attachment)
    error

Documentation

Maybe is me, but I haven't find any documentation for the lib.

There is some ?

Batch Acquire Triggers Breaks

The scheduler can be configured to batch acquire triggers:-

"org.quartz.scheduler.batchTriggerAcquisitionMaxCount", "50"

However, this job store breaks badly when this is used. The issue is that the current recursive retry mechanism assumes that batch mode is never used and that only one trigger is to be acquired.

When aquiring multiple triggers, it must not forget the triggers it has already acquired when recursing to retry the acquires that have failed.

So, when recursing, add the result of the recursion to the triggers you already have:-

triggers.addAll(acquireNextTriggers(noLaterThan, maxCount - triggers.size(), timeWindow));

Clustering

Hi,
I need support for Quartz clustering - i.e., N > 1 servers, a repeating job runs exactly on one server for every repetition, possibly on a different server each time - using MongoDB as the datastore.

Are you working on adding support for clustering? Are there any technical issues that make the implementation difficult, or it's just a matter of having the time to write the code?

Thanks!

Null trigger when job is deleted

The function
toTrigger(TriggerKey triggerKey, DBObject dbObject) throws JobPersistenceException

could return a null if the job was deleted but this is not handled correctly in the acquireNextTriggers. The case when trigger is null is not handled at all and this causes a null pointer exception which causes quartz to crash even though the issue is minor (only 1 trigger is in a weird state).

If you have some suggestions on how we should handle this I will implement it.

One easy approach is just to add a simple if and test for null, then log it and continue. A good approach would probably include removing the trigger or marking it a certain way.

Looking forward to your suggestions,
Vlad.

Allow Jobdata for Jobs and store them as BSON

finally I got the mongo backend working, but I would like to submit some feature requests.

  1. Currently it is only possible to use jobdata on triggers (but not on jobs). And I think jobs will therefore not be updated after execution (annontation @PersistJobDataAfterExecution).

So it would be great to store jobdata on jobs too and persist them if requested.

  1. In the trigger the jobdata is just as base encoded string.

Since jobdatamap can only hold primitives anyway, why not store them as json/bson embedded object. this would have the big advandatege that the job data is searchable!

  1. A really nice feature would be if you can make a new implementation of org.quartz.plugins.history.LoggingJobHistoryPlugin and org.quartz.plugins.history.LoggingTriggerHistoryPlugin to store the result in a time to live constraint collection.

JobExecutionContext.getPreviousFireTime returns actual execution time

When getting the previous fire time in a Job implementation via JobExecutionContext.getPreviousFireTime() it actually returns the current time of execution.

It seems the previousFireTime is set to early in the process.

I would assume it should be set after the job has been completed,
when the lock is removed, taking the time from the lock entry as previousFireTime?

Best regards,
Daniel

Can't run Mongo with --notablescan

It is nice to be able to run mongo with the --notablescan.

However, that is not possible because some of the quartz queries are and will do table scans.

at com.mongodb.MongoException.parse(MongoException.java:82)
at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:292)
at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:273)
at com.mongodb.DBCursor._check(DBCursor.java:368)
at com.mongodb.DBCursor._hasNext(DBCursor.java:459)
at com.mongodb.DBCursor.hasNext(DBCursor.java:484)
at com.novemberain.quartz.mongodb.MongoDBJobStore.getJobKeys(MongoDBJobStore.java:336)
at org.quartz.core.QuartzScheduler.getJobKeys(QuartzScheduler.java:1411)
at org.quartz.impl.StdScheduler.getJobKeys(StdScheduler.java:461)

Am wondering if there is any way to make this play nice with --notablescan.

Use SLF4j properly

Don't do this:-

BasicDBObject query = new BasicDBObject();
query.put(TRIGGER_NEXT_FIRE_TIME, new BasicDBObject("$lte", new Date(noLaterThan)));

if (log.isDebugEnabled()) {
  log.debug("Finding up to " + maxCount + " triggers which have time less than " + new Date(noLaterThan));
}

Do this:-

Date noLaterThanDate = new Date();
BasicDBObject query = new BasicDBObject();
query.put(TRIGGER_NEXT_FIRE_TIME, new BasicDBObject("$lte", noLaterThanDate));

log.debug("Finding up to {} triggers which have time less than {}", maxCount, noLaterThanDate);

i.e. you don't really need those isDebugEnabled calls if you are using the SLF4j eith {} instead of string concatenation.

... And only create the noLaterThanDate once rather than twice as it was prior.

please support ssl

I need the quartz-mongodb support ssl,and now I rewrite the source code to support it.but if the version upgrade I need rewrite it to the new version again. so, I suggest that the ssl support will be added at the next version. Thank you!

Clustering supported?

Hi,
as far as I can see there is currently no support for clustering and failover.
Is that right?
If so is there any support intended?

regards

Job Durability

Quartz says that:

"Durability - if a job is non-durable, it is automatically deleted from the scheduler once there are no longer any active triggers associated with it. In other words, non-durable jobs have a life span bounded by the existence of its triggers."

Non durability is default, and durability is accomplished with a flag.

As I looked at the code (and tested it) I noticed that only triggers get deleted and not jobs. Am I missing something? Is this somehow the desired behavior?

Would you accept a pull request that also deletes the associated jobs when a trigger is deleted if the durability flag is not set?

Thanks,
Vlad.

Index on TRIGGER_NEXT_FIRE_TIME?

This is more like a feature request.
The mongodb job store doesn't work now if there are more than 64 MB of triggers because mongodb cannot handle that much that without an index. This only gets worse after you get over 64MB as data still accumulates.

I know this is probably not an common use case but still, if is possible there should be a way of having more than 64 MB of triggers.

Any thoughts?
Vlad.

Incorrect documentation?

In your usage section in the main readme you have the following:

# Use the MongoDB store
org.quartz.jobStore.class=com.mulesoft.quartz.mongo.MongoDBJobStore

Should this not be

# Use the MongoDB store
org.quartz.jobStore.class=com.novemberain.quartz.mongodb.MongoDBJobStore

java.lang.NullPointerException on MongoDBJobStore#shutdown

Hi,

I used MongoDBJobStore through Spring (3.2.2.RELEASE) with a null jobstore hostname.

"mongo.close()" fails because "mongo" is null.

java.lang.NullPointerException
at com.novemberain.quartz.mongodb.MongoDBJobStore.shutdown(MongoDBJobStore.java:91)
at org.quartz.core.QuartzScheduler.shutdown(QuartzScheduler.java:714)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1324)
at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1484)
at org.springframework.scheduling.quartz.SchedulerFactoryBean.createScheduler(SchedulerFactoryBean.java:598)
at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:479)
at com.visionobjects.myscript.backend.common.scheduler.MyScriptBackendSchedulerFactoryBean.afterPropertiesSet(MyScriptBackendSchedulerFactoryBean.java:18)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:608)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:735)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:233)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1233)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:678)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:475)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:224)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
at org.eclipse.jetty.server.Server.doStart(Server.java:270)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1260)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:1183)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.eclipse.jetty.start.Main.invokeMain(Main.java:462)
at org.eclipse.jetty.start.Main.start(Main.java:610)
at org.eclipse.jetty.start.Main.main(Main.java:86)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.