簡體   English   中英

HK2工廠為Quartz工作,而不是在執行后破壞服務

[英]HK2 factory for Quartz jobs, not destroying service after execution

我想在我的服務器應用程序中使用Quartz Scheduler ,它使用HK2進行依賴注入。 為了讓Quartz作業能夠訪問DI,他們需要自己進行DI管理。 結果,我寫了一個超級簡單的HK2感知工作工廠並將其注冊到調度程序。

它正常工作與服務的實例,觀察請求@Singleton@PerLookup范圍。 但是,它們在完成后無法destroy() 非單例服務(=作業)。

問題:如何讓HK2正確管理工作,包括再次拆除工作?

我是否需要通過serviceLocator.getServiceHandle()繼續創建服務的路徑,然后手動銷毀服務,可能來自JobListener (但是如何獲取ServiceHandle)?

Hk2JobFactory.java

@Service
public class Hk2JobFactory implements JobFactory {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @Inject
    ServiceLocator serviceLocator;

    @Override
    public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
        JobDetail jobDetail = bundle.getJobDetail();
        Class<? extends Job> jobClass = jobDetail.getJobClass();
        try {
            log.debug("Producing instance of Job '" + jobDetail.getKey() + "', class=" + jobClass.getName());

            Job job = serviceLocator.getService(jobClass);
            if (job == null) {
                log.debug("Unable to instantiate job via ServiceLocator, returning unmanaged instance.");
                return jobClass.newInstance();
            }
            return job;

        } catch (Exception e) {
            SchedulerException se = new SchedulerException(
                    "Problem instantiating class '"
                    + jobDetail.getJobClass().getName() + "'", e);
            throw se;
        }

    }

}

HelloWorldJob.java

@Service
@PerLookup
public class HelloWorldJob implements Job {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @PostConstruct
    public void setup() {
        log.info("I'm born!");
    }

    @PreDestroy
    public void shutdown() {
        // it's never called... :-(
        log.info("And I'm dead again");
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("Hello, world!");
    }

}

與@ jwells131313建議類似,我已經實現了一個JobListener,它在適當的時候destroy()的作業實例。 為了方便,我在作業的DataMap傳遞ServiceHandle

不同之處僅在於我對@PerLookup范圍非常滿意。

Hk2JobFactory.java:

@Service
public class Hk2JobFactory implements JobFactory {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @Inject
    ServiceLocator serviceLocator;

    @Override
    public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {

        JobDetail jobDetail = bundle.getJobDetail();
        Class<? extends Job> jobClass = jobDetail.getJobClass();
        try {
            log.debug("Producing instance of job {} (class {})", jobDetail.getKey(), jobClass.getName());

            ServiceHandle sh = serviceLocator.getServiceHandle(jobClass);
            if (sh != null) {
                Class scopeAnnotation = sh.getActiveDescriptor().getScopeAnnotation();
                if (log.isTraceEnabled()) log.trace("Service scope is {}", scopeAnnotation.getName());
                if (scopeAnnotation == PerLookup.class) {
                    // @PerLookup scope means: needs to be destroyed after execution
                    jobDetail.getJobDataMap().put(SERVICE_HANDLE_KEY, sh);
                }

                return jobClass.cast(sh.getService());
            }

            log.debug("Unable to instantiate job via ServiceLocator, returning unmanaged instance");
            return jobClass.newInstance();

        } catch (Exception e) {
            SchedulerException se = new SchedulerException(
                    "Problem instantiating class '"
                    + jobDetail.getJobClass().getName() + "'", e);
            throw se;
        }

    }

}

Hk2CleanupJobListener.java:

public class Hk2CleanupJobListener extends JobListenerSupport {
    public static final String SERVICE_HANDLE_KEY = "hk2_serviceHandle";
    private final Map<String, String> mdcCopy = MDC.getCopyOfContextMap();

    @Override
    public String getName() {
        return getClass().getSimpleName();
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        JobDetail jobDetail = context.getJobDetail();

        ServiceHandle sh = (ServiceHandle) jobDetail.getJobDataMap().get(SERVICE_HANDLE_KEY);
        if (sh == null) {
            if (getLog().isTraceEnabled()) getLog().trace("No serviceHandle found");
            return;
        }

        Class scopeAnnotation = sh.getActiveDescriptor().getScopeAnnotation();
        if (scopeAnnotation == PerLookup.class) {
            if (getLog().isTraceEnabled()) getLog().trace("Destroying job {} after it was executed (Class {})", 
                    jobDetail.getKey(), 
                    jobDetail.getJobClass().getName()
            );
            sh.destroy();
        }

    }

}

兩者都在Scheduler中注冊。

對於單身人士:

看起來像Singleton服務在作業完成時不會被銷毀,因為它是一個Singleton,對嗎? 如果期待的辛格爾頓在工作結束時被銷毀那么它似乎像服務更多的是一種“JobScope”,而不是一個真正的單身范圍。

工作范圍:

如果“作業”遵循某些規則,那么它可能是“操作”范圍的良好候選者(請參閱操作示例 )。 特別是在以下情況下,作業可以處於“操作”范圍:

  1. 可以同時進行許多並行作業
  2. 一次只能在一個線程上激活一個作業

請注意,上述規則還意味着Jobs可以在相同或不同時間存在於多個線程上。 最重要的規則是,在一個線程上,一次只能有一個Job可以處於活動狀態。

如果這兩個規則適用,那么我強烈建議編寫類似“JobScope”的操作范圍。

如果作業遵循上述規則,您可以定義JobScope:

@Scope
@Proxiable(proxyForSameScope = false)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface JobScope {
}

這將是相應上下文的整個實現:

@Singleton
public class JobScopeContext extends OperationContext<JobScope> {

    public Class<? extends Annotation> getScope() {
        return JobScope.class;
    }

}

然后,當您知道作業開始和停止時,您將使用OperationManager服務來啟動和停止作業。

即使喬布斯不遵守“操作”規則,您仍然可能希望使用“JobScope”范圍,當“作業”結束時,該范圍會破壞其服務。

PerLookup:

因此,如果您的問題是關於PerLookup范圍對象,您可能會遇到一些麻煩,因為您可能需要原始的ServiceHandle,這聽起來像您沒有。 在這種情況下,如果您至少可以在PerLookup范圍內找到原始服務WAS,則可以使用ServiceLocator.preDestroy來銷毀該對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM