blob: 429e7fb0891db67ef661a7eb9add76f2d8117857 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.deltaspike.scheduler.impl;
import org.apache.deltaspike.core.api.provider.BeanProvider;
import org.apache.deltaspike.core.spi.activation.Deactivatable;
import org.apache.deltaspike.core.util.ClassDeactivationUtils;
import org.apache.deltaspike.core.util.ClassUtils;
import org.apache.deltaspike.core.util.ServiceUtils;
import org.apache.deltaspike.scheduler.api.Scheduled;
import org.apache.deltaspike.scheduler.spi.SchedulerControl;
import org.apache.deltaspike.scheduler.spi.Scheduler;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
public class SchedulerExtension implements Extension, Deactivatable
{
private static final Logger LOG = Logger.getLogger(SchedulerExtension.class.getName());
//keep it as a string (needed by some containers - due to the imports)
private static Set<String> classNamesToVeto = new HashSet<String>();
private Boolean isActivated = true;
private Set<Class> foundManagedJobClasses = new HashSet<Class>();
private Scheduler scheduler;
private Class jobClass;
public SchedulerExtension()
{
classNamesToVeto.add("org.apache.deltaspike.scheduler.impl.DynamicExpressionObserverJob");
}
protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery)
{
this.isActivated = ClassDeactivationUtils.isActivated(getClass());
if (this.isActivated)
{
String jobClassName = SchedulerBaseConfig.JobCustomization.JOB_CLASS_NAME;
this.jobClass = ClassUtils.tryToLoadClassForName(jobClassName);
if (this.jobClass == null)
{
this.isActivated = false;
}
}
}
public <X> void findScheduledJobs(@Observes ProcessAnnotatedType<X> pat, BeanManager beanManager)
{
if (!this.isActivated)
{
return;
}
Class<X> beanClass = pat.getAnnotatedType().getJavaClass();
//see SchedulerProducer
if (Scheduler.class.isAssignableFrom(beanClass) || isInternalUnmanagedClass(beanClass))
{
pat.veto();
return;
}
if (!jobClass.isAssignableFrom(beanClass) && !Runnable.class.isAssignableFrom(beanClass))
{
return;
}
Scheduled scheduled = pat.getAnnotatedType().getAnnotation(Scheduled.class);
if (scheduled != null && scheduled.onStartup())
{
this.foundManagedJobClasses.add(beanClass);
}
}
private <X> boolean isInternalUnmanagedClass(Class<X> beanClass)
{
return classNamesToVeto.contains(beanClass.getName());
}
public <X> void validateJobs(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager)
{
if (!this.isActivated)
{
return;
}
Class configuredJobClass = this.jobClass;
this.jobClass = resolveFinalJobType();
if (this.jobClass == null)
{
afterBeanDiscovery.addDefinitionError(new IllegalStateException("Please only annotate classes with @" +
Scheduled.class.getName() + " of type " +
configuredJobClass.getName() + " or of type " + Runnable.class.getName() + ", but not both!"));
return;
}
}
public <X> void scheduleJobs(@Observes AfterDeploymentValidation afterDeploymentValidation, BeanManager beanManager)
{
SchedulerControl schedulerControl = BeanProvider.getContextualReference(SchedulerControl.class, true);
this.isActivated &= schedulerControl == null || schedulerControl.isSchedulerEnabled();
if (!this.isActivated)
{
return;
}
initScheduler(afterDeploymentValidation);
if (this.scheduler == null)
{
return;
}
List<String> foundJobNames = new ArrayList<String>();
for (Class jobClass : this.foundManagedJobClasses)
{
if (foundJobNames.contains(jobClass.getSimpleName()))
{
afterDeploymentValidation.addDeploymentProblem(
new IllegalStateException("Multiple Job-Classes found with name " + jobClass.getSimpleName()));
}
foundJobNames.add(jobClass.getSimpleName());
this.scheduler.registerNewJob(jobClass);
}
}
/**
* Allows to support implementations of {@link java.lang.Runnable}
* annotated with {@link Scheduled} >without< explicit config.
* @return the job-type which will be used to select the scheduler
*/
protected Class resolveFinalJobType()
{
Set<Class> foundTypes = new HashSet<Class>();
for (Class foundJobClass : this.foundManagedJobClasses)
{
if (jobClass.isAssignableFrom(foundJobClass))
{
foundTypes.add(jobClass);
}
else if (Runnable.class.isAssignableFrom(foundJobClass))
{
foundTypes.add(Runnable.class);
}
}
if (foundTypes.size() > 1)
{
return null;
}
else if (foundTypes.size() == 1)
{
return foundTypes.iterator().next();
}
else
{
//use the configured type
//it's still useful if there is no annotated job-class, but a dyn. usage of the scheduler is still possible
return jobClass;
}
}
public <X> void stopScheduler(@Observes BeforeShutdown beforeShutdown)
{
if (!this.isActivated)
{
return;
}
if (this.scheduler != null)
{
this.scheduler.stop();
this.scheduler = null;
}
}
private void initScheduler(AfterDeploymentValidation afterDeploymentValidation)
{
List<Scheduler> availableSchedulers = ServiceUtils.loadServiceImplementations(Scheduler.class, true);
this.scheduler = findScheduler(availableSchedulers, this.jobClass);
if (this.scheduler != null)
{
try
{
this.scheduler.start();
}
catch (Throwable t)
{
afterDeploymentValidation.addDeploymentProblem(t);
}
}
else if (!this.foundManagedJobClasses.isEmpty())
{
LOG.warning(
this.foundManagedJobClasses.size() + " scheduling-jobs found, but there is no configured scheduler");
}
}
private static Scheduler findScheduler(List<Scheduler> availableSchedulers, Class jobClass)
{
for (Scheduler scheduler : availableSchedulers)
{
//in case of implementing the Scheduler interface directly
for (Type interfaceClass : scheduler.getClass().getGenericInterfaces())
{
if (!(interfaceClass instanceof ParameterizedType) ||
!Scheduler.class.isAssignableFrom((Class)((ParameterizedType)interfaceClass).getRawType()))
{
continue;
}
if (jobClass.isAssignableFrom(((Class)((ParameterizedType)interfaceClass).getActualTypeArguments()[0])))
{
return scheduler;
}
}
//in case of extending e.g. AbstractQuartzScheduler
if (scheduler.getClass().getGenericSuperclass() instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) scheduler.getClass().getGenericSuperclass();
for (Type typeArgument : parameterizedType.getActualTypeArguments())
{
if (jobClass.isAssignableFrom((Class)typeArgument))
{
return scheduler;
}
}
}
}
return null;
}
Scheduler getScheduler()
{
return scheduler;
}
}