/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.vertical.work.quartz;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import com.enonic.vertical.work.WorkEntry;
import com.enonic.vertical.work.WorkException;
import com.enonic.vertical.work.WorkService;
import com.enonic.cms.api.plugin.ext.TaskHandler;
import com.enonic.cms.core.plugin.ext.TaskHandlerExtensions;
public final class QuartzWorkService
implements WorkService
{
private final static String DEFAULT_GROUP = "default";
private TaskHandlerExtensions extensions;
private QuartzSchedulerManager schedulerManager;
@Autowired
public void setExtensions( final TaskHandlerExtensions extensions )
{
this.extensions = extensions;
}
public boolean isEnabled()
{
return this.schedulerManager.isEnabled();
}
@Autowired
public void setSchedulerManager( final QuartzSchedulerManager schedulerManager )
{
this.schedulerManager = schedulerManager;
}
private Scheduler getScheduler()
throws WorkException
{
if ( isEnabled() )
{
return this.schedulerManager.getScheduler();
}
else
{
throw new WorkException( "Scheduler is not enabled" );
}
}
private JobDetail createJobDetail( WorkEntry from )
{
JobDetail to = new JobDetail();
to.setName( from.getKey() );
to.setGroup( DEFAULT_GROUP );
to.setDescription( from.getName() );
to.setDurability( false );
to.setJobClass( WorkJobAdapter.class );
to.setJobDataMap( createJobDataMap( from ) );
return to;
}
private Trigger createTrigger( WorkEntry from )
{
Trigger trigger = (Trigger) from.getTriggerPeer();
trigger.setName( from.getKey() );
trigger.setGroup( DEFAULT_GROUP );
return trigger;
}
private WorkEntry createEntry( JobDetail job, Trigger trigger )
{
WorkEntry to = new WorkEntry();
to.setKey( job.getName() );
to.setName( job.getDescription() );
JobDataMap map = job.getJobDataMap();
to.setWorkClass( map.getString( WorkJobAdapter.CLASS_KEY ) );
for ( Iterator i = map.keySet().iterator(); i.hasNext(); )
{
String key = (String) i.next();
if ( isPublicProperty( key ) )
{
to.setProperty( key, map.getString( key ) );
}
}
to.setTriggerPeer( trigger );
return to;
}
private boolean isPublicProperty( String key )
{
if ( key.equals( WorkJobAdapter.CLASS_KEY ) )
{
return false;
}
else if ( key.equals( WorkEntry.PROP_USERNAME ) )
{
return false;
}
else
{
return true;
}
}
private JobDataMap createJobDataMap( WorkEntry entry )
{
JobDataMap map = new JobDataMap();
map.put( WorkJobAdapter.CLASS_KEY, entry.getWorkClass() );
String[] props = entry.getPropertyNames();
for ( int i = 0; i < props.length; i++ )
{
map.put( props[i], entry.getProperty( props[i] ) );
}
return map;
}
public void addEntry( WorkEntry task )
throws WorkException
{
addEntry( task, false );
}
public void addEntry( WorkEntry work, boolean replace )
throws WorkException
{
if ( work != null )
{
validateEntry( work );
WorkEntry oldEntry = null;
if ( replace )
{
oldEntry = getEntry( work.getKey() );
deleteEntry( work );
}
JobDetail job = createJobDetail( work );
Trigger trigger = createTrigger( work );
try
{
Scheduler scheduler = getScheduler();
scheduler.scheduleJob( job, trigger );
}
catch ( SchedulerException e )
{
addEntry( oldEntry, false );
throw new WorkException( "Failed to add work entry", e );
}
}
}
public void deleteEntry( WorkEntry work )
throws WorkException
{
deleteEntry( work.getKey() );
}
public void deleteEntry( String key )
throws WorkException
{
try
{
Scheduler scheduler = getScheduler();
scheduler.deleteJob( key, DEFAULT_GROUP );
}
catch ( SchedulerException e )
{
throw new WorkException( "Failed to delete work entry", e );
}
}
public WorkEntry[] getEntries()
throws WorkException
{
List<WorkEntry> entries = new LinkedList<WorkEntry>();
List<JobDetail> jobDetailList = getJobDetails();
for ( JobDetail detail : jobDetailList )
{
Trigger trigger = getTrigger( detail );
entries.add( createEntry( detail, trigger ) );
}
return entries.toArray( new WorkEntry[entries.size()] );
}
private JobDetail getJobDetail( String key )
throws WorkException
{
try
{
Scheduler scheduler = getScheduler();
return scheduler.getJobDetail( key, DEFAULT_GROUP );
}
catch ( SchedulerException e )
{
throw new WorkException( e.getMessage() );
}
}
private Trigger getTrigger( JobDetail job )
throws WorkException
{
try
{
Scheduler scheduler = getScheduler();
Trigger[] triggers = scheduler.getTriggersOfJob( job.getName(), job.getGroup() );
if ( triggers.length > 0 )
{
return triggers[0];
}
}
catch ( SchedulerException e )
{
throw new WorkException( e.getMessage(), e );
}
return null;
}
public String[] getEntryKeys()
throws WorkException
{
try
{
Scheduler scheduler = getScheduler();
return scheduler.getJobNames( DEFAULT_GROUP );
}
catch ( SchedulerException e )
{
throw new WorkException( e.getMessage() );
}
}
private List<JobDetail> getJobDetails()
throws WorkException
{
try
{
Scheduler scheduler = getScheduler();
LinkedList<JobDetail> list = new LinkedList<JobDetail>();
String[] names = scheduler.getJobNames( DEFAULT_GROUP );
for ( int i = 0; i < names.length; i++ )
{
list.add( scheduler.getJobDetail( names[i], DEFAULT_GROUP ) );
}
return list;
}
catch ( SchedulerException e )
{
throw new WorkException( e.getMessage(), e );
}
}
/**
* Return a single entry.
*/
public WorkEntry getEntry( String key )
throws WorkException
{
JobDetail job = getJobDetail( key );
if ( job != null )
{
Trigger trigger = getTrigger( job );
return createEntry( job, trigger );
}
else
{
return null;
}
}
/**
* Checks the entry and makes sure there's a valid TaskPlugin class, backing this work entry.
*
* @param entry The class specifying the task.
* @throws WorkException If no TaskPlugin is found.
*/
private void validateEntry( WorkEntry entry )
throws WorkException
{
String clzName = entry.getWorkClass().replaceAll( ".*\\.", "" );
TaskHandler taskPlugin = this.extensions.getByName( clzName );
if ( taskPlugin == null )
{
throw new WorkException( "Work class " + clzName + " not valid" );
}
}
}