Code review comment: I believe that we should always rollback if something goes wrong, ie. right before `throw new IllegalStateException("Unable to create Scheduler", e)`. Actually, we could change the catch block below to `catch Throwable`...

Code before review:
public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Config config) {
if (!quartzSupport.getRuntimeConfig().forceStart && context.getScheduledMethods().isEmpty()) {
LOGGER.infof("No scheduled business methods found - Quartz scheduler will not be started");
this.triggerNameSequence = null;
this.scheduler = null;
this.invokers = null;
} else {
this.triggerNameSequence = new AtomicInteger();
this.invokers = new HashMap<>();
try {
Properties props = getSchedulerConfigurationProperties(quartzSupport);
SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);
scheduler = schedulerFactory.getScheduler();
scheduler.setJobFactory(new InvokerJobFactory(invokers));
CronType cronType = context.getCronType();
CronDefinition def = CronDefinitionBuilder.instanceDefinitionFor(cronType);
CronParser parser = new CronParser(def);
InstanceHandle<UserTransaction> handle = Arc.container().instance(UserTransaction.class);
boolean nonManagedTx = quartzSupport.getBuildTimeConfig().getStoreClass()
.equals("org.quartz.impl.jdbcjobstore.JobStoreCMT");
if (nonManagedTx) {
if (handle.isAvailable()) {
handle.get().begin();
} else {
LOGGER.debugf("Unable to start transaction for JobStoreCMT job store class. " +
"No %s bean found.", UserTransaction.class);
}
}
for (ScheduledMethodMetadata method : context.getScheduledMethods()) {
invokers.put(method.getInvokerClassName(), context.createInvoker(method.getInvokerClassName()));
for (Scheduled scheduled : method.getSchedules()) {
String name = triggerNameSequence.getAndIncrement() + "_" + method.getInvokerClassName();
JobBuilder jobBuilder = JobBuilder.newJob(InvokerJob.class)
.withIdentity(name, Scheduler.class.getName())
.usingJobData(INVOKER_KEY, method.getInvokerClassName())
.requestRecovery();
ScheduleBuilder<?> scheduleBuilder;
String cron = scheduled.cron().trim();
if (!cron.isEmpty()) {
if (SchedulerContext.isConfigValue(cron)) {
cron = config.getValue(SchedulerContext.getConfigProperty(cron), String.class);
}
if (!CronType.QUARTZ.equals(cronType)) {
Cron cronExpr = parser.parse(cron);
switch (cronType) {
case UNIX:
cron = CronMapper.fromUnixToQuartz().map(cronExpr).asString();
break;
case CRON4J:
cron = CronMapper.fromCron4jToQuartz().map(cronExpr).asString();
break;
default:
break;
}
}
scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
} else if (!scheduled.every().isEmpty()) {
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(
SimpleScheduler.parseDuration(scheduled, scheduled.every(), "every").toMillis())
.repeatForever();
} else {
throw new IllegalArgumentException("Invalid schedule configuration: " + scheduled);
}
TriggerBuilder<?> triggerBuilder = TriggerBuilder.newTrigger()
.withIdentity(name + "_trigger", Scheduler.class.getName())
.withSchedule(scheduleBuilder);
Long millisToAdd = null;
if (scheduled.delay() > 0) {
millisToAdd = scheduled.delayUnit().toMillis(scheduled.delay());
} else if (!scheduled.delayed().isEmpty()) {
millisToAdd = Math
.abs(SimpleScheduler.parseDuration(scheduled, scheduled.delayed(), "delayed").toMillis());
}
if (millisToAdd != null) {
triggerBuilder.startAt(new Date(Instant.now()
.plusMillis(millisToAdd).toEpochMilli()));
}
JobDetail job = jobBuilder.build();
if (scheduler.checkExists(job.getKey())) {
scheduler.deleteJob(job.getKey());
}
scheduler.scheduleJob(job, triggerBuilder.build());
LOGGER.debugf("Scheduled business method %s with config %s", method.getMethodDescription(),
scheduled);
}
}
if (nonManagedTx) {
if (handle.isAvailable()) {
try {
handle.get().commit();
} catch (HeuristicMixedException | HeuristicRollbackException | RollbackException e) {
handle.get().rollback();
}
}
handle.close();
}
} catch (SchedulerException | NotSupportedException | SystemException e) {
throw new IllegalStateException("Unable to create Scheduler", e);
}
}
}

Line under review: handle.get().rollback();

Code after addressing the review:
public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Config config) {
if (!quartzSupport.getRuntimeConfig().forceStart && context.getScheduledMethods().isEmpty()) {
LOGGER.infof("No scheduled business methods found - Quartz scheduler will not be started");
this.triggerNameSequence = null;
this.scheduler = null;
this.invokers = null;
} else {
this.triggerNameSequence = new AtomicInteger();
this.invokers = new HashMap<>();
UserTransaction transaction = null;
try (InstanceHandle<UserTransaction> handle = Arc.container().instance(UserTransaction.class)) {
boolean manageTx = quartzSupport.getBuildTimeConfig().storeType.isNonManagedTxJobStore();
if (manageTx && handle.isAvailable()) {
transaction = handle.get();
}
Properties props = getSchedulerConfigurationProperties(quartzSupport);
SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);
scheduler = schedulerFactory.getScheduler();
scheduler.setJobFactory(new InvokerJobFactory(invokers));
CronType cronType = context.getCronType();
CronDefinition def = CronDefinitionBuilder.instanceDefinitionFor(cronType);
CronParser parser = new CronParser(def);
if (transaction != null) {
transaction.begin();
}
for (ScheduledMethodMetadata method : context.getScheduledMethods()) {
invokers.put(method.getInvokerClassName(), context.createInvoker(method.getInvokerClassName()));
for (Scheduled scheduled : method.getSchedules()) {
String name = triggerNameSequence.getAndIncrement() + "_" + method.getInvokerClassName();
JobBuilder jobBuilder = JobBuilder.newJob(InvokerJob.class)
.withIdentity(name, Scheduler.class.getName())
.usingJobData(INVOKER_KEY, method.getInvokerClassName())
.requestRecovery();
ScheduleBuilder<?> scheduleBuilder;
String cron = scheduled.cron().trim();
if (!cron.isEmpty()) {
if (SchedulerContext.isConfigValue(cron)) {
cron = config.getValue(SchedulerContext.getConfigProperty(cron), String.class);
}
if (!CronType.QUARTZ.equals(cronType)) {
Cron cronExpr = parser.parse(cron);
switch (cronType) {
case UNIX:
cron = CronMapper.fromUnixToQuartz().map(cronExpr).asString();
break;
case CRON4J:
cron = CronMapper.fromCron4jToQuartz().map(cronExpr).asString();
break;
default:
break;
}
}
scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
} else if (!scheduled.every().isEmpty()) {
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(
SimpleScheduler.parseDuration(scheduled, scheduled.every(), "every").toMillis())
.repeatForever();
} else {
throw new IllegalArgumentException("Invalid schedule configuration: " + scheduled);
}
TriggerBuilder<?> triggerBuilder = TriggerBuilder.newTrigger()
.withIdentity(name + "_trigger", Scheduler.class.getName())
.withSchedule(scheduleBuilder);
Long millisToAdd = null;
if (scheduled.delay() > 0) {
millisToAdd = scheduled.delayUnit().toMillis(scheduled.delay());
} else if (!scheduled.delayed().isEmpty()) {
millisToAdd = Math
.abs(SimpleScheduler.parseDuration(scheduled, scheduled.delayed(), "delayed").toMillis());
}
if (millisToAdd != null) {
triggerBuilder.startAt(new Date(Instant.now()
.plusMillis(millisToAdd).toEpochMilli()));
}
JobDetail job = jobBuilder.build();
if (scheduler.checkExists(job.getKey())) {
scheduler.deleteJob(job.getKey());
}
scheduler.scheduleJob(job, triggerBuilder.build());
LOGGER.debugf("Scheduled business method %s with config %s", method.getMethodDescription(),
scheduled);
}
}
if (transaction != null) {
transaction.commit();
}
} catch (Throwable e) {
if (transaction != null) {
try {
transaction.rollback();
} catch (SystemException ex) {
LOGGER.error("Unable to rollback transaction", ex);
}
}
throw new IllegalStateException("Unable to create Scheduler", e);
}
}
}