/**
* Copyright (c) 2009--2014 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.taskomatic.core;
import com.redhat.rhn.common.conf.Config;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.conf.ConfigException;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.common.messaging.MessageQueue;
import com.redhat.rhn.domain.org.Org;
import com.redhat.rhn.domain.org.OrgFactory;
import com.redhat.rhn.taskomatic.TaskoFactory;
import com.redhat.rhn.taskomatic.TaskoQuartzHelper;
import com.redhat.rhn.taskomatic.TaskoRun;
import com.redhat.rhn.taskomatic.TaskoSchedule;
import com.redhat.rhn.taskomatic.TaskoXmlRpcServer;
import com.redhat.rhn.taskomatic.TaskomaticApi;
import org.apache.log4j.Logger;
import org.hibernate.Transaction;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
/**
* Taskomatic Kernel.
*/
public class SchedulerKernel {
private static final String[] TASKOMATIC_PACKAGE_NAMES = {"com.redhat.rhn.taskomatic"};
private static Logger log = Logger.getLogger(SchedulerKernel.class);
private byte[] shutdownLock = new byte[0];
private static SchedulerFactory factory = null;
private static Scheduler scheduler = null;
private static TaskoXmlRpcServer xmlrpcServer = null;
private ChainedListener chainedTriggerListener = null;
private String dataSourceConfigPath = "org.quartz.jobStore.dataSource";
private String dataSourcePrefix = "org.quartz.dataSource";
private String defaultDataSource = "rhnDs";
/**
* Kernel main driver behind Taskomatic
* @throws InstantiationException thrown if this.scheduler can't be initialized.
* @throws UnknownHostException thrown if xmlrcp host is unknown
*/
public SchedulerKernel() throws InstantiationException, UnknownHostException {
Properties props = Config.get().getNamespaceProperties("org.quartz");
String dbUser = Config.get().getString(ConfigDefaults.DB_USER);
String dbPass = Config.get().getString(ConfigDefaults.DB_PASSWORD);
props.setProperty(dataSourceConfigPath, defaultDataSource);
String ds = dataSourcePrefix + "." + defaultDataSource;
props.setProperty(ds + ".user", dbUser);
props.setProperty(ds + ".password", dbPass);
// props.setProperty(ds + ".maxConnections", 30);
if (ConfigDefaults.get().isOracle()) {
props.setProperty("org.quartz.jobStore.driverDelegateClass",
"org.quartz.impl.jdbcjobstore.oracle.OracleDelegate");
String driver = Config.get().getString(ConfigDefaults.DB_CLASS,
"oracle.jdbc.driver.OracleDriver");
props.setProperty(ds + ".driver", driver);
props.setProperty(ds + ".URL", ConfigDefaults.get().getJdbcConnectionString());
}
else if (ConfigDefaults.get().isPostgresql()) {
props.setProperty("org.quartz.jobStore.driverDelegateClass",
"org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
String driver = Config.get().getString(ConfigDefaults.DB_CLASS,
"org.postgresql.Driver");
props.setProperty(ds + ".driver", driver);
props.setProperty(ds + ".URL", ConfigDefaults.get().getJdbcConnectionString());
}
else {
// This code should never get called as Exception would get
// thrown in getJdbcConnectionString.
throw new InstantiationException(
"Unknown db backend set, expecting oracle or postgresql");
}
try {
SchedulerKernel.factory = new StdSchedulerFactory(props);
SchedulerKernel.scheduler = SchedulerKernel.factory.getScheduler();
SchedulerKernel.scheduler.setJobFactory(new RhnJobFactory());
// Setup TriggerListener chain
this.chainedTriggerListener = new ChainedListener();
this.chainedTriggerListener.addListener(new TaskEnvironmentListener());
try {
SchedulerKernel.scheduler.addTriggerListener(this.chainedTriggerListener);
}
catch (SchedulerException e) {
throw new ConfigException(e.getLocalizedMessage(), e);
}
xmlrpcServer = new TaskoXmlRpcServer(Config.get());
xmlrpcServer.start();
}
catch (SchedulerException e) {
e.printStackTrace();
throw new InstantiationException("this.scheduler failed");
}
}
/**
* returns scheduler
* @return scheduler
*/
public static Scheduler getScheduler() {
return SchedulerKernel.scheduler;
}
/**
* Starts Taskomatic
* This method does not return until the this.scheduler is shutdown
* @throws TaskomaticException error occurred during Quartz or Hibernate startup
*/
public void startup() throws TaskomaticException {
HibernateFactory.createSessionFactory(TASKOMATIC_PACKAGE_NAMES);
if (!HibernateFactory.isInitialized()) {
throw new TaskomaticException("HibernateFactory failed to initialize");
}
MessageQueue.startMessaging();
MessageQueue.configureDefaultActions();
try {
SchedulerKernel.scheduler.start();
initializeAllSatSchedules();
synchronized (this.shutdownLock) {
try {
this.shutdownLock.wait();
}
catch (InterruptedException ignored) {
return;
}
}
}
catch (SchedulerException e) {
throw new TaskomaticException(e.getMessage(), e);
}
}
/**
* Initiates the shutdown process. Needs to happen in a
* separate thread to prevent Quartz scheduler errors.
*/
public void startShutdown() {
Runnable shutdownTask = new Runnable() {
public void run() {
shutdown();
}
};
Thread t = new Thread(shutdownTask);
t.setDaemon(true);
t.start();
}
/**
* Shutsdown the application
*/
protected void shutdown() {
try {
SchedulerKernel.scheduler.standby();
SchedulerKernel.scheduler.shutdown();
}
catch (SchedulerException e) {
// TODO Figure out what to do with this guy
e.printStackTrace();
}
finally {
MessageQueue.stopMessaging();
HibernateFactory.closeSessionFactory();
// Wake up thread waiting in startup() so it can exit
synchronized (this.shutdownLock) {
this.shutdownLock.notify();
}
}
}
/**
* load DB schedule configuration
*/
public void initializeAllSatSchedules() {
List jobNames;
Date now = new Date();
try {
jobNames = Arrays.asList(
SchedulerKernel.scheduler.getJobNames(
TaskoQuartzHelper.getGroupName(null)));
for (TaskoSchedule schedule : TaskoFactory.listActiveSchedulesByOrg(null)) {
if (!jobNames.contains(schedule.getJobLabel())) {
schedule.sanityCheckForPredefinedSchedules();
log.info("Initializing " + schedule.getJobLabel());
TaskoQuartzHelper.createJob(schedule);
}
else {
List<TaskoRun> runList =
TaskoFactory.listNewerRunsBySchedule(schedule.getId(), now);
if (!runList.isEmpty()) {
// there're runs in the future
// reinit the schedule
Transaction tx = TaskoFactory.getSession().beginTransaction();
log.warn("Reinitializing " + schedule.getJobLabel() + ", found " +
runList.size() + " runs in the future.");
TaskoFactory.reinitializeScheduleFromNow(schedule, now);
for (TaskoRun run : runList) {
TaskoFactory.deleteRun(run);
}
tx.commit();
}
}
}
// delete outdated reposync leftovers
TaskomaticApi tasko = new TaskomaticApi();
for (Org org : OrgFactory.lookupAllOrgs()) {
int removed = tasko.unscheduleInvalidRepoSyncSchedules(org);
if (removed > 0) {
log.warn("" + removed + " outdated repo-sync schedules detected and " +
"removed within org " + org.getId());
}
}
// close unfinished runs
int interrupted = 0;
for (TaskoRun run : TaskoFactory.listUnfinishedRuns()) {
run.setStatus(TaskoRun.STATUS_INTERRUPTED);
run.setEndTime(now);
TaskoFactory.save(run);
interrupted++;
}
if (interrupted > 0) {
log.warn("Number of interrupted runs: " + interrupted);
}
TaskoFactory.closeSession();
}
catch (Exception e) {
e.printStackTrace();
return;
}
}
}