From d373fd02f334031b682ea51f71b13d46becb974b Mon Sep 17 00:00:00 2001 From: Bruno Medeiros Date: Fri, 22 Feb 2013 13:48:48 -0300 Subject: [PATCH] adding support for scoping - creating QuartzJobScope --- pom.xml | 6 ++ .../guice/guartz/InjectorJobFactory.java | 36 +++++++- .../nnsoft/guice/guartz/QuartzJobScope.java | 91 +++++++++++++++++++ .../nnsoft/guice/guartz/QuartzJobScoped.java | 21 +++++ .../org/nnsoft/guice/guartz/QuartzModule.java | 2 + .../org/nnsoft/guice/guartz/QuartzScopes.java | 11 +++ 6 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/nnsoft/guice/guartz/QuartzJobScope.java create mode 100644 src/main/java/org/nnsoft/guice/guartz/QuartzJobScoped.java create mode 100644 src/main/java/org/nnsoft/guice/guartz/QuartzScopes.java diff --git a/pom.xml b/pom.xml index 9e8c8b3..c86861c 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,12 @@ 3.0 compile + + com.google.guava + guava + r09 + compile + junit junit diff --git a/src/main/java/org/nnsoft/guice/guartz/InjectorJobFactory.java b/src/main/java/org/nnsoft/guice/guartz/InjectorJobFactory.java index be5e3bc..028fd98 100644 --- a/src/main/java/org/nnsoft/guice/guartz/InjectorJobFactory.java +++ b/src/main/java/org/nnsoft/guice/guartz/InjectorJobFactory.java @@ -19,6 +19,8 @@ import javax.inject.Inject; import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; @@ -33,7 +35,7 @@ final class InjectorJobFactory implements JobFactory { - /** + /** * The delegated {@link Injector}. */ @Inject @@ -55,8 +57,36 @@ public void setInjector( Injector injector ) public Job newJob( TriggerFiredBundle bundle, Scheduler scheduler ) throws SchedulerException { - Class jobClass = bundle.getJobDetail().getJobClass(); - return this.injector.getInstance( jobClass ); + final Class jobClass = bundle.getJobDetail().getJobClass(); + + Job decorator = new JobScopeDecorator(jobClass); + + return decorator; + } + + private final class JobScopeDecorator implements Job { + + private final Class decoratedJobClass; + + Job decorated; + + private JobScopeDecorator(Class jobClass) { + this.decoratedJobClass = jobClass; + } + + public void execute(JobExecutionContext context) throws JobExecutionException + { + // See http://code.google.com/p/google-guice/wiki/CustomScopes#Triggering_the_Scope + final QuartzJobScope scope = InjectorJobFactory.this.injector.getInstance( QuartzJobScope.class ); + scope.enter(); + try + { + decorated = InjectorJobFactory.this.injector.getInstance( decoratedJobClass ); + decorated.execute( context ); + } finally { + scope.exit(); + } + } } } diff --git a/src/main/java/org/nnsoft/guice/guartz/QuartzJobScope.java b/src/main/java/org/nnsoft/guice/guartz/QuartzJobScope.java new file mode 100644 index 0000000..5fcb31a --- /dev/null +++ b/src/main/java/org/nnsoft/guice/guartz/QuartzJobScope.java @@ -0,0 +1,91 @@ +package org.nnsoft.guice.guartz; + +import static com.google.common.base.Preconditions.checkState; +import com.google.common.collect.Maps; +import com.google.inject.Key; +import com.google.inject.OutOfScopeException; +import com.google.inject.Provider; +import com.google.inject.Scope; +import java.util.Map; + +/** + * Scopes a Quartz Job Execution. + * + * Based on: http://code.google.com/p/google-guice/wiki/CustomScopes#Implementing_Scope + * + * @author Bruno Medeiros + */ +public class QuartzJobScope implements Scope +{ + + private static final Provider SEEDED_KEY_PROVIDER = + new Provider() { + public Object get() { + throw new IllegalStateException("If you got here then it means that" + + " your code asked for scoped object which should have been" + + " explicitly seeded in this scope by calling" + + " SimpleScope.seed(), but was not."); + } + }; + + private final ThreadLocal, Object>> values + = new ThreadLocal, Object>>(); + + public void enter() { + checkState(values.get() == null, "A scoping block is already in progress"); + values.set(Maps., Object>newHashMap()); + } + + public void exit() { + checkState(values.get() != null, "No scoping block in progress"); + values.remove(); + } + + public void seed(Key key, T value) { + Map, Object> scopedObjects = getScopedObjectMap(key); + checkState(!scopedObjects.containsKey(key), "A value for the key %s was " + + "already seeded in this scope. Old value: %s New value: %s", key, + scopedObjects.get(key), value); + scopedObjects.put(key, value); + } + + public void seed(Class clazz, T value) { + seed(Key.get(clazz), value); + } + + public Provider scope(final Key key, final Provider unscoped) { + return new Provider() { + public T get() { + Map, Object> scopedObjects = getScopedObjectMap(key); + + @SuppressWarnings("unchecked") + T current = (T) scopedObjects.get(key); + if (current == null && !scopedObjects.containsKey(key)) { + current = unscoped.get(); + scopedObjects.put(key, current); + } + return current; + } + }; + } + + private Map, Object> getScopedObjectMap(Key key) { + Map, Object> scopedObjects = values.get(); + if (scopedObjects == null) { + throw new OutOfScopeException("Cannot access " + key + + " outside of a scoping block"); + } + return scopedObjects; + } + + /** + * Returns a provider that always throws exception complaining that the object + * in question must be seeded before it can be injected. + * + * @return typed provider + */ + @SuppressWarnings({"unchecked"}) + public static Provider seededKeyProvider() { + return (Provider) SEEDED_KEY_PROVIDER; + } +} diff --git a/src/main/java/org/nnsoft/guice/guartz/QuartzJobScoped.java b/src/main/java/org/nnsoft/guice/guartz/QuartzJobScoped.java new file mode 100644 index 0000000..ac6e826 --- /dev/null +++ b/src/main/java/org/nnsoft/guice/guartz/QuartzJobScoped.java @@ -0,0 +1,21 @@ +package org.nnsoft.guice.guartz; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.google.inject.ScopeAnnotation; + +/** + * Apply this to implementation classes when you want one instance per quartz job. + * + * @author Bruno Medeiros + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ScopeAnnotation +public @interface QuartzJobScoped +{ + //nothing +} diff --git a/src/main/java/org/nnsoft/guice/guartz/QuartzModule.java b/src/main/java/org/nnsoft/guice/guartz/QuartzModule.java index 7df5d88..441e84d 100644 --- a/src/main/java/org/nnsoft/guice/guartz/QuartzModule.java +++ b/src/main/java/org/nnsoft/guice/guartz/QuartzModule.java @@ -68,6 +68,8 @@ protected final void configure() try { + bindScope(QuartzJobScoped.class, QuartzScopes.QUARTZ_JOB); + bind(QuartzJobScope.class).toInstance(QuartzScopes.QUARTZ_JOB); schedule(); bind( JobFactory.class ).to( InjectorJobFactory.class ).in( SINGLETON ); bind( Scheduler.class ).toProvider( SchedulerProvider.class ).asEagerSingleton(); diff --git a/src/main/java/org/nnsoft/guice/guartz/QuartzScopes.java b/src/main/java/org/nnsoft/guice/guartz/QuartzScopes.java new file mode 100644 index 0000000..3206601 --- /dev/null +++ b/src/main/java/org/nnsoft/guice/guartz/QuartzScopes.java @@ -0,0 +1,11 @@ +package org.nnsoft.guice.guartz; + + +public class QuartzScopes { + + /** + * Quartz Job scope. + */ + public static final QuartzJobScope QUARTZ_JOB = new QuartzJobScope(); + +}