View Javadoc

1   package org.apache.onami.scheduler;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static com.google.inject.Scopes.SINGLETON;
23  import static com.google.inject.multibindings.Multibinder.newSetBinder;
24  import static java.util.TimeZone.getTimeZone;
25  import static org.apache.onami.scheduler.Scheduled.DEFAULT;
26  
27  import java.util.TimeZone;
28  
29  import org.quartz.Job;
30  import org.quartz.JobListener;
31  import org.quartz.Scheduler;
32  import org.quartz.SchedulerListener;
33  import org.quartz.TriggerListener;
34  import org.quartz.spi.JobFactory;
35  
36  import com.google.inject.AbstractModule;
37  import com.google.inject.multibindings.Multibinder;
38  
39  /**
40   * Quartz (http://www.quartz-scheduler.org/) Module as Google-Guice extension.
41   */
42  public abstract class QuartzModule
43      extends AbstractModule
44  {
45  
46      private Multibinder<JobListener> jobListeners;
47  
48      private Multibinder<TriggerListener> triggerListeners;
49  
50      private Multibinder<SchedulerListener> schedulerListeners;
51  
52  	private SchedulerConfiguration schedulerConfiguration;
53  
54      /**
55       * {@inheritDoc}
56       */
57      @Override
58      protected final void configure()
59      {
60          checkState( jobListeners == null, "Re-entry is not allowed." );
61          checkState( triggerListeners == null, "Re-entry is not allowed." );
62          checkState( schedulerListeners == null, "Re-entry is not allowed." );
63          checkState( schedulerConfiguration == null, "Re-entry is not allowed." );
64  
65          jobListeners = newSetBinder( binder(), JobListener.class );
66          triggerListeners = newSetBinder( binder(), TriggerListener.class );
67          schedulerListeners = newSetBinder( binder(), SchedulerListener.class );
68          schedulerConfiguration = new SchedulerConfiguration();
69  
70          try
71          {
72              schedule();
73              bind( JobFactory.class ).to( InjectorJobFactory.class ).in( SINGLETON );
74              bind( Scheduler.class ).toProvider( SchedulerProvider.class ).asEagerSingleton();
75              bind( SchedulerConfiguration.class ).toInstance( schedulerConfiguration );
76          }
77          finally
78          {
79              jobListeners = null;
80              triggerListeners = null;
81              schedulerListeners = null;
82              schedulerConfiguration = null;
83          }
84      }
85  
86      /**
87       * Part of the EDSL builder language for configuring {@code Job}s.
88       * Here is a typical example of scheduling {@code Job}s when creating your Guice injector:
89       *
90       * <pre>
91       * Guice.createInjector(..., new QuartzModule() {
92       *
93       *     {@literal @}Override
94       *     protected void schedule() {
95       *       <b>scheduleJob(MyJobImpl.class).withCronExpression("0/2 * * * * ?");</b>
96       *     }
97       *
98       * });
99       * </pre>
100      *
101      * @see JobSchedulerBuilder
102      */
103     protected abstract void schedule();
104 
105     /**
106      * Allows to configure the scheduler.
107      *
108      * <pre>
109      * Guice.createInjector(..., new QuartzModule() {
110      *
111      *     {@literal @}Override
112      *     protected void schedule() {
113      *       configureScheduler().withManualStart().withProperties(...);
114      *     }
115      *
116      * });
117      * </pre>
118      *
119      * @since 1.1
120      */
121     protected final SchedulerConfigurationBuilder configureScheduler()
122     {
123         return schedulerConfiguration;
124     }
125 
126     /**
127      * Add the {@code JobListener} binding.
128      *
129      * @param jobListenerType The {@code JobListener} class has to be bound
130      */
131     protected final void addJobListener( Class<? extends JobListener> jobListenerType )
132     {
133         doBind( jobListeners, jobListenerType );
134     }
135 
136     /**
137      * Add the {@code TriggerListener} binding.
138      *
139      * @param triggerListenerType The {@code TriggerListener} class has to be bound
140      */
141     protected final void addTriggerListener( Class<? extends TriggerListener> triggerListenerType )
142     {
143         doBind( triggerListeners, triggerListenerType );
144     }
145 
146     /**
147      * Add the {@code SchedulerListener} binding.
148      *
149      * @param schedulerListenerType The {@code SchedulerListener} class has to be bound
150      */
151     protected final void addSchedulerListener( Class<? extends SchedulerListener> schedulerListenerType )
152     {
153         doBind( schedulerListeners, schedulerListenerType );
154     }
155 
156     /**
157      * Allows {@code Job} scheduling, delegating Guice create the {@code Job} instance
158      * and inject members.
159      *
160      * If given {@code Job} class is annotated with {@link Scheduled}, then {@code Job}
161      * and related {@code Trigger} values will be extracted from it.
162      *
163      * @param jobClass The {@code Job} has to be scheduled
164      * @return The {@code Job} builder
165      */
166     protected final JobSchedulerBuilder scheduleJob( Class<? extends Job> jobClass )
167     {
168         checkNotNull( jobClass, "Argument 'jobClass' must be not null." );
169 
170         JobSchedulerBuilder builder = new JobSchedulerBuilder( jobClass );
171 
172         if ( jobClass.isAnnotationPresent( Scheduled.class ) )
173         {
174             Scheduled scheduled = jobClass.getAnnotation( Scheduled.class );
175 
176             builder
177             // job
178             .withJobName( scheduled.jobName() )
179             .withJobGroup( scheduled.jobGroup() )
180             .withRequestRecovery( scheduled.requestRecovery() )
181             .withStoreDurably( scheduled.storeDurably() )
182             // trigger
183             .withCronExpression( scheduled.cronExpression() )
184             .withTriggerName( scheduled.triggerName() );
185 
186             if ( !DEFAULT.equals( scheduled.timeZoneId() ) )
187             {
188                 TimeZone timeZone = getTimeZone( scheduled.timeZoneId() );
189                 if ( timeZone != null )
190                 {
191                     builder.withTimeZone( timeZone );
192                 }
193             }
194         }
195 
196         requestInjection( builder );
197         return builder;
198     }
199 
200     /**
201      * Utility method to respect the DRY principle.
202      *
203      * @param <T>
204      * @param binder
205      * @param type
206      */
207     protected final <T> void doBind( Multibinder<T> binder, Class<? extends T> type )
208     {
209         checkNotNull( type, "Impossible to bind a null type" );
210         binder.addBinding().to( type );
211     }
212 
213     private static void checkState( boolean state, String message )
214     {
215         if ( !state )
216         {
217             throw new IllegalStateException( message );
218         }
219     }
220 
221     private static void checkNotNull( Object object, String message )
222     {
223         if ( object == null )
224         {
225             throw new IllegalArgumentException( message );
226         }
227     }
228 
229 }