/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.plugin.pc.content; import java.util.Date; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.Trigger; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.content.ContentSource; import org.rhq.core.domain.content.PackageVersion; import org.rhq.core.domain.content.Repo; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.content.ContentSourceManagerLocal; import org.rhq.enterprise.server.plugin.pc.AbstractTypeServerPluginContainer; import org.rhq.enterprise.server.plugin.pc.MasterServerPluginContainer; import org.rhq.enterprise.server.plugin.pc.ServerPluginManager; import org.rhq.enterprise.server.plugin.pc.ServerPluginType; import org.rhq.enterprise.server.scheduler.SchedulerLocal; import org.rhq.enterprise.server.scheduler.jobs.ContentProviderSyncJob; import org.rhq.enterprise.server.scheduler.jobs.RepoSyncJob; import org.rhq.enterprise.server.util.LookupUtil; import org.rhq.enterprise.server.xmlschema.generated.serverplugin.content.ContentPluginDescriptorType; /** * The container responsible for managing the lifecycle of content server-side plugins. * * @author John Mazzitelli */ public class ContentServerPluginContainer extends AbstractTypeServerPluginContainer { private static final String CONTENT_SRC_SYNC_JOB_GROUP_NAME = "syncContentSource"; private static final String REPO_SYNC_JOB_GROUP_NAME = "syncRepo"; private static final String REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME = "syncRepoImmediate"; private ContentProviderManager adapterManager; public ContentServerPluginContainer(MasterServerPluginContainer master) { super(master); } @Override public void initialize() throws Exception { getLog().debug("Content server plugin container initializing..."); super.initialize(); this.adapterManager = createAdapterManager(); getLog().debug("Content server plugin container initialized."); } @Override public void start() { super.start(); this.adapterManager.initialize((ContentServerPluginManager) getPluginManager()); } @Override public void shutdown() { getLog().debug("Content server plugin container is shutting down..."); this.adapterManager.shutdown(); super.shutdown(); getLog().debug("Content server plugin container is shutdown."); } @Override public void scheduleAllPluginJobs() throws Exception { super.scheduleAllPluginJobs(); scheduleSyncJobs(); return; } @Override public ServerPluginType getSupportedServerPluginType() { return new ServerPluginType(ContentPluginDescriptorType.class); } /** * Returns the object that is responsible for managing all {@link ContentProvider adapters} which are the * things that know how to download content from a specific {@link ContentSource}. * * @return adapter manager */ public ContentProviderManager getAdapterManager() { return this.adapterManager; } /** * This will syncronize the given content source, meaning its {@link PackageVersion}s will be updated and, if not * lazy-loading, will load the package versions that are not loaded yet. * * <p>Note that this will perform the sync asynchronously in a separate thread. This is because this sync operation * can potentially run for hours and we do not want to block the calling thread.</p> * * @param contentSource the content source to sync * * @throws SchedulerException if failed to schedule the job for immediate execution */ public void syncProviderNow(final ContentSource contentSource) throws SchedulerException { // Create our job with a trigger that fires immediately and doesn't repeat. // Make the name unique - we may already have our cron job schedules. // What happens if this is triggered when our cron job is triggered? the job will abort and let the current job finish JobDetail job = new JobDetail(ContentProviderSyncJob.createUniqueJobName(contentSource), CONTENT_SRC_SYNC_JOB_GROUP_NAME, ContentProviderSyncJob.class, false, false, false); JobDataMap jobDataMap = ContentProviderSyncJob.createJobDataMap(contentSource, job); jobDataMap.putAsString(ContentProviderSyncJob.DATAMAP_SYNC_IMPORTED_REPOS, true); SimpleTrigger trigger = new SimpleTrigger(job.getName(), job.getGroup()); trigger.setVolatility(false); SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); Date next = scheduler.scheduleJob(job, trigger); getLog().info( "Scheduled content source sync job [" + job.getName() + ':' + job.getGroup() + "] to fire now at [" + next + "] for [" + contentSource + "]."); } /** * Causes the given repo to be scheduled for an immediate sync. The sync will take place through the scheduler, * causing this call to be asynchronous and return before the sync itself takes place. * * @param repo cannot be <code>null</code> * @throws SchedulerException if the job cannot be scheduled */ public void syncRepoNow(Repo repo) throws SchedulerException { String jobName = RepoSyncJob.createJobName(repo); JobDetail job = new JobDetail(jobName, REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME, RepoSyncJob.class, false, false, false); RepoSyncJob.createJobDataMap(job, repo); Date nextExecution; SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); Trigger trigger = scheduler.getTrigger(jobName, REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME); if (trigger == null) { trigger = new SimpleTrigger(jobName, job.getGroup()); trigger.setVolatility(false); nextExecution = scheduler.scheduleJob(job, trigger); } else { nextExecution = scheduler.rescheduleJob(jobName, REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME, trigger); } getLog().info( "Scheduled repo sync job [" + job.getName() + ':' + job.getGroup() + "] to fire now at [" + nextExecution + "] for [" + repo + "]."); } public void cancelRepoSync(Subject subject, Repo repo) throws SchedulerException { JobDetail jobDetail = new JobDetail(RepoSyncJob.createJobName(repo), REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME, RepoSyncJob.class, false, false, false); RepoSyncJob.createJobDataMap(jobDetail, repo); SimpleTrigger trigger = new SimpleTrigger(jobDetail.getName(), jobDetail.getGroup()); trigger.setVolatility(false); SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); boolean cancelled = scheduler.interrupt(RepoSyncJob.createJobName(repo), REPO_SYNC_JOB_IMMEDIATE_GROUP_NAME); getLog().info("Cancelled repo sync job [" + jobDetail.getName() + ':' + jobDetail.getGroup() + "]."); } /** * This will schedule the sync job for the given content source. Once the scheduling is complete, the content * source's adapter will be asked to synchronize with the remote content source according to the time(s) specified * in the {@link ContentSource#getSyncSchedule() schedule}. * * <p>If the content source's sync schedule is empty, this method assumes it should not be automatically sync'ed, so * no schedule will be created, and this method simply returns.</p> * * @param contentSource provider to sync * * @throws SchedulerException if failed to schedule the job */ public void scheduleProviderSyncJob(ContentSource contentSource) throws SchedulerException { String syncSchedule = contentSource.getSyncSchedule(); if ((syncSchedule == null) || (syncSchedule.trim().length() == 0)) { getLog().debug(contentSource.toString() + " does not define a sync schedule - not scheduling."); return; } SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); scheduler.scheduleCronJob(ContentProviderSyncJob.createJobName(contentSource), CONTENT_SRC_SYNC_JOB_GROUP_NAME, ContentProviderSyncJob.createJobDataMap(contentSource, null), ContentProviderSyncJob.class, true, false, syncSchedule); } /** * This will schedule the sync job for the given repository. Once the scheduling is complete, the repository's * adapter will be asked to synchronize with the remote repository according to the time(s) specified * in the {@link Repo#getSyncSchedule() schedule}. * * <p>If the repository's sync schedule is empty, this method assumes it should not be automatically sync'ed, so * no schedule will be created, and this method simply returns.</p> * * @param repo repository to sync * * @throws SchedulerException if failed to schedule the job */ public void scheduleRepoSyncJob(Repo repo) throws SchedulerException { String syncSchedule = repo.getSyncSchedule(); if ((syncSchedule == null) || (syncSchedule.trim().length() == 0)) { getLog().warn(repo.toString() + " does not define a sync schedule - not scheduling."); return; } SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); scheduler.scheduleCronJob(RepoSyncJob.createJobName(repo), REPO_SYNC_JOB_GROUP_NAME, RepoSyncJob .createJobDataMap(null, repo), RepoSyncJob.class, true, false, syncSchedule); } /** * It will schedule one job per adapter such that each adapter is scheduled to be synchronized as per its defined * sync schedule. This must only be called when all content source adapters have been initialized. */ public void scheduleSyncJobs() { if (this.adapterManager != null) { for (ContentSource contentSource : this.adapterManager.getAllContentSources()) { try { getLog().debug("scheduleSyncJobs :: Scheduling CP job: " + contentSource.getName()); scheduleProviderSyncJob(contentSource); ContentSourceManagerLocal contentSourceManager = LookupUtil.getContentSourceManager(); PageList<Repo> repos = contentSourceManager.getAssociatedRepos(LookupUtil.getSubjectManager() .getOverlord(), contentSource.getId(), PageControl.getUnlimitedInstance()); if (repos != null) { for (Repo repo : repos) { getLog().debug("scheduleSyncJobs :: Scheduling REPO job: " + repo.getName()); scheduleRepoSyncJob(repo); } } } catch (Exception e) { getLog().warn("Could not schedule sync job for content source [" + contentSource + "].", e); } } } } /** * This will unschedule the sync job for the given content source. Once unscheduled, the content source's adapter * will not be asked to synchronize with the remote content source. * * @param contentSource cannot be <code>null</code> * * @throws SchedulerException if failed to unschedule the job */ public void unscheduleProviderSyncJob(ContentSource contentSource) throws SchedulerException { SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); scheduler.deleteJob(ContentProviderSyncJob.createJobName(contentSource), CONTENT_SRC_SYNC_JOB_GROUP_NAME); } /** * This will unschedule the sync job for the given Repo. * * @param repo cannot be <code>null</code> * * @throws SchedulerException if failed to unschedule the job */ public void unscheduleRepoSyncJob(Repo repo) throws SchedulerException { SchedulerLocal scheduler = LookupUtil.getSchedulerBean(); scheduler.deleteJob(RepoSyncJob.createJobName(repo), REPO_SYNC_JOB_GROUP_NAME); } protected ServerPluginManager createPluginManager() { return new ContentServerPluginManager(this); } /** * Creates the adapter manager that the PC will use. * * <p>This is protected scope so subclasses can define their own adapter manager to use. This is mainly to support * tests.</p> * * @return the new adapter manager */ protected ContentProviderManager createAdapterManager() { ContentProviderManager am = new ContentProviderManager(); return am; } }