Search in sources :

Example 31 with Scheduler

use of org.quartz.Scheduler in project opennms by OpenNMS.

the class PollerTest method testSchedule.

public void testSchedule(boolean reschedule, OnmsMonitoredService svc) throws Exception {
    Scheduler scheduler = createMock(Scheduler.class);
    PollService pollService = createNiceMock(PollService.class);
    PollerFrontEnd pollerFrontEnd = createMock(PollerFrontEnd.class);
    svc.setId(7);
    PollConfiguration pollConfig = new PollConfiguration(svc, new HashMap<String, Object>(), 300000);
    PolledService polledService = new PolledService(pollConfig.getMonitoredService(), pollConfig.getMonitorConfiguration(), pollConfig.getPollModel());
    Set<PolledService> polledServices = Collections.singleton(polledService);
    Poller poller = new Poller();
    pollerFrontEnd.addConfigurationChangedListener(poller);
    pollerFrontEnd.addPropertyChangeListener(poller);
    expect(pollerFrontEnd.getPolledServices()).andReturn(polledServices);
    expect(pollerFrontEnd.isStarted()).andReturn(true);
    expect(scheduler.deleteJob(new JobKey(polledService.toString(), PollJobDetail.GROUP))).andReturn(reschedule);
    pollerFrontEnd.setInitialPollTime(eq(svc.getId()), isA(Date.class));
    expect(scheduler.scheduleJob(isA(PollJobDetail.class), isA(Trigger.class))).andReturn(new Date());
    replay(scheduler, pollService, pollerFrontEnd);
    poller.setScheduler(scheduler);
    poller.setPollerFrontEnd(pollerFrontEnd);
    poller.afterPropertiesSet();
    verify(scheduler, pollService, pollerFrontEnd);
}
Also used : Scheduler(org.quartz.Scheduler) Date(java.util.Date) JobKey(org.quartz.JobKey) Trigger(org.quartz.Trigger)

Example 32 with Scheduler

use of org.quartz.Scheduler in project ddf by codice.

the class TestWorkspaceQueryService method testRun.

@SuppressWarnings("unchecked")
@Test
public void testRun() throws SchedulerException, UnsupportedQueryException, SourceUnavailableException, FederationException {
    String workspaceId = "3";
    QueryUpdateSubscriber queryUpdateSubscriber = mock(QueryUpdateSubscriber.class);
    WorkspaceService workspaceService = mock(WorkspaceService.class);
    CatalogFramework catalogFramework = mock(CatalogFramework.class);
    FilterBuilder filterBuilder = mock(FilterBuilder.class);
    Scheduler scheduler = mock(Scheduler.class);
    when(scheduler.getContext()).thenReturn(mock(SchedulerContext.class));
    Supplier<Optional<Scheduler>> schedulerSupplier = () -> Optional.of(scheduler);
    SecurityService securityService = new SecurityService() {

        @Override
        public Subject getSystemSubject() {
            return mock(Subject.class);
        }

        @Override
        public Map<String, Serializable> addSystemSubject(Map<String, Serializable> properties) {
            return properties;
        }
    };
    FilterService filterService = mock(FilterService.class);
    when(filterService.getModifiedDateFilter(any())).thenReturn(mock(Filter.class));
    when(filterBuilder.anyOf(Mockito.any(Filter.class))).thenReturn(mock(Or.class));
    when(filterBuilder.allOf(Mockito.<Filter>anyVararg())).thenReturn(mock(And.class));
    WorkspaceQueryServiceImpl workspaceQueryServiceImpl = new WorkspaceQueryServiceImpl(queryUpdateSubscriber, workspaceService, catalogFramework, filterBuilder, schedulerSupplier, securityService, filterService);
    workspaceQueryServiceImpl.setQueryTimeInterval(60);
    String ecql = "area( Polygon((10 10, 20 10, 20 20, 10 10)) ) BETWEEN 10000 AND 30000";
    WorkspaceMetacardImpl workspaceMetacard = mock(WorkspaceMetacardImpl.class);
    when(workspaceMetacard.getId()).thenReturn(workspaceId);
    QueryMetacardImpl queryMetacardWithSource = mock(QueryMetacardImpl.class);
    when(queryMetacardWithSource.getSources()).thenReturn(Collections.singletonList("SomeSource"));
    when(queryMetacardWithSource.getCql()).thenReturn(ecql);
    Attribute id1 = mock(Attribute.class);
    when(id1.getValue()).thenReturn("1");
    when(queryMetacardWithSource.getAttribute(Metacard.ID)).thenReturn(id1);
    QueryMetacardImpl queryMetacardWithoutSource = mock(QueryMetacardImpl.class);
    when(queryMetacardWithoutSource.getSources()).thenReturn(Collections.emptyList());
    when(queryMetacardWithoutSource.getCql()).thenReturn(ecql);
    Attribute id2 = mock(Attribute.class);
    when(id2.getValue()).thenReturn("2");
    when(queryMetacardWithoutSource.getAttribute(Metacard.ID)).thenReturn(id2);
    Map<String, Pair<WorkspaceMetacardImpl, List<QueryMetacardImpl>>> queryMetacards = Collections.singletonMap(id2.getValue().toString(), new ImmutablePair<>(workspaceMetacard, Arrays.asList(queryMetacardWithSource, queryMetacardWithoutSource)));
    when(workspaceService.getQueryMetacards()).thenReturn(queryMetacards);
    long hitCount1 = 10;
    long hitCount2 = 20;
    QueryResponse queryResponse = mock(QueryResponse.class);
    when(queryResponse.getHits()).thenReturn(hitCount1).thenReturn(hitCount2);
    when(catalogFramework.query(any())).thenReturn(queryResponse);
    workspaceQueryServiceImpl.setSubject(new Subject() {

        @Override
        public boolean isGuest() {
            return false;
        }

        @Override
        public Object getPrincipal() {
            return null;
        }

        @Override
        public PrincipalCollection getPrincipals() {
            return null;
        }

        @Override
        public boolean isPermitted(String s) {
            return false;
        }

        @Override
        public boolean isPermitted(Permission permission) {
            return false;
        }

        @Override
        public boolean[] isPermitted(String... strings) {
            return new boolean[0];
        }

        @Override
        public boolean[] isPermitted(List<Permission> list) {
            return new boolean[0];
        }

        @Override
        public boolean isPermittedAll(String... strings) {
            return false;
        }

        @Override
        public boolean isPermittedAll(Collection<Permission> collection) {
            return false;
        }

        @Override
        public void checkPermission(String s) throws AuthorizationException {
        }

        @Override
        public void checkPermission(Permission permission) throws AuthorizationException {
        }

        @Override
        public void checkPermissions(String... strings) throws AuthorizationException {
        }

        @Override
        public void checkPermissions(Collection<Permission> collection) throws AuthorizationException {
        }

        @Override
        public boolean hasRole(String s) {
            return false;
        }

        @Override
        public boolean[] hasRoles(List<String> list) {
            return new boolean[0];
        }

        @Override
        public boolean hasAllRoles(Collection<String> collection) {
            return false;
        }

        @Override
        public void checkRole(String s) throws AuthorizationException {
        }

        @Override
        public void checkRoles(Collection<String> collection) throws AuthorizationException {
        }

        @Override
        public void checkRoles(String... strings) throws AuthorizationException {
        }

        @Override
        public void login(AuthenticationToken authenticationToken) throws AuthenticationException {
        }

        @Override
        public boolean isAuthenticated() {
            return false;
        }

        @Override
        public boolean isRemembered() {
            return false;
        }

        @Override
        public Session getSession() {
            return null;
        }

        @Override
        public Session getSession(boolean b) {
            return null;
        }

        @Override
        public void logout() {
        }

        @Override
        public <V> V execute(Callable<V> callable) throws ExecutionException {
            try {
                return callable.call();
            } catch (Exception e) {
                throw new ExecutionException(e);
            }
        }

        @Override
        public void execute(Runnable runnable) {
        }

        @Override
        public <V> Callable<V> associateWith(Callable<V> callable) {
            return null;
        }

        @Override
        public Runnable associateWith(Runnable runnable) {
            return null;
        }

        @Override
        public void runAs(PrincipalCollection principalCollection) throws NullPointerException, IllegalStateException {
        }

        @Override
        public boolean isRunAs() {
            return false;
        }

        @Override
        public PrincipalCollection getPreviousPrincipals() {
            return null;
        }

        @Override
        public PrincipalCollection releaseRunAs() {
            return null;
        }
    });
    workspaceQueryServiceImpl.setCronString("0 0 0 * * ?");
    workspaceQueryServiceImpl.setQueryTimeoutMinutes(5L);
    workspaceQueryServiceImpl.run();
    ArgumentCaptor<Map> argumentCaptor = ArgumentCaptor.forClass(Map.class);
    verify(queryUpdateSubscriber).notify(argumentCaptor.capture());
    Map queryUpdateSubscriberArgumentRaw = argumentCaptor.getValue();
    Map<String, Pair<WorkspaceMetacardImpl, Long>> queryUpdateSubscriberArgument = (Map<String, Pair<WorkspaceMetacardImpl, Long>>) queryUpdateSubscriberArgumentRaw;
    assertThat(queryUpdateSubscriberArgument.get(workspaceId).getRight(), is(hitCount1 + hitCount2));
}
Also used : Serializable(java.io.Serializable) Or(org.opengis.filter.Or) AuthenticationToken(org.apache.shiro.authc.AuthenticationToken) AuthorizationException(org.apache.shiro.authz.AuthorizationException) AuthenticationException(org.apache.shiro.authc.AuthenticationException) QueryMetacardImpl(org.codice.ddf.catalog.ui.metacard.workspace.QueryMetacardImpl) CatalogFramework(ddf.catalog.CatalogFramework) SchedulerContext(org.quartz.SchedulerContext) Permission(org.apache.shiro.authz.Permission) Optional(java.util.Optional) WorkspaceService(org.codice.ddf.catalog.ui.query.monitor.api.WorkspaceService) And(org.opengis.filter.And) QueryUpdateSubscriber(org.codice.ddf.catalog.ui.query.monitor.api.QueryUpdateSubscriber) Map(java.util.Map) Attribute(ddf.catalog.data.Attribute) Scheduler(org.quartz.Scheduler) FilterService(org.codice.ddf.catalog.ui.query.monitor.api.FilterService) PrincipalCollection(org.apache.shiro.subject.PrincipalCollection) Callable(java.util.concurrent.Callable) FilterBuilder(ddf.catalog.filter.FilterBuilder) SecurityService(org.codice.ddf.catalog.ui.query.monitor.api.SecurityService) ExecutionException(org.apache.shiro.subject.ExecutionException) Pair(org.apache.commons.lang3.tuple.Pair) ImmutablePair(org.apache.commons.lang3.tuple.ImmutablePair) Subject(ddf.security.Subject) SourceUnavailableException(ddf.catalog.source.SourceUnavailableException) UnsupportedQueryException(ddf.catalog.source.UnsupportedQueryException) SchedulerException(org.quartz.SchedulerException) FederationException(ddf.catalog.federation.FederationException) AuthorizationException(org.apache.shiro.authz.AuthorizationException) ExecutionException(org.apache.shiro.subject.ExecutionException) AuthenticationException(org.apache.shiro.authc.AuthenticationException) Filter(org.opengis.filter.Filter) QueryResponse(ddf.catalog.operation.QueryResponse) WorkspaceMetacardImpl(org.codice.ddf.catalog.ui.metacard.workspace.WorkspaceMetacardImpl) Session(org.apache.shiro.session.Session) Test(org.junit.Test)

Example 33 with Scheduler

use of org.quartz.Scheduler in project midpoint by Evolveum.

the class TaskSynchronizer method synchronizeTask.

/**
     * Task should be refreshed when entering this method.
     *
     * @return true if task info in Quartz was updated
     */
public boolean synchronizeTask(TaskQuartzImpl task, OperationResult parentResult) {
    if (!task.isPersistent()) {
        // transient tasks are not scheduled via Quartz!
        return false;
    }
    boolean changed = false;
    String message = "";
    OperationResult result = parentResult.createSubresult(TaskSynchronizer.class.getName() + ".synchronizeTask");
    result.addArbitraryObjectAsParam("task", task);
    try {
        LOGGER.trace("Synchronizing task {}; isRecreateQuartzTrigger = {}", task, task.isRecreateQuartzTrigger());
        Scheduler scheduler = taskManager.getExecutionManager().getQuartzScheduler();
        String oid = task.getOid();
        JobKey jobKey = TaskQuartzImplUtil.createJobKeyForTask(task);
        TriggerKey standardTriggerKey = TaskQuartzImplUtil.createTriggerKeyForTask(task);
        boolean waitingOrClosedOrSuspended = task.getExecutionStatus() == TaskExecutionStatus.WAITING || task.getExecutionStatus() == TaskExecutionStatus.CLOSED || task.getExecutionStatus() == TaskExecutionStatus.SUSPENDED;
        if (!scheduler.checkExists(jobKey) && !waitingOrClosedOrSuspended) {
            String m1 = "Quartz job does not exist for a task, adding it. Task = " + task;
            message += "[" + m1 + "] ";
            LOGGER.trace(" - " + m1);
            scheduler.addJob(TaskQuartzImplUtil.createJobDetailForTask(task), false);
            changed = true;
        }
        // WAITING and CLOSED tasks should have no triggers; SUSPENDED tasks should have no extra triggers
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        boolean standardTriggerExists = triggers.stream().anyMatch(t -> t.getKey().equals(standardTriggerKey));
        if (waitingOrClosedOrSuspended) {
            for (Trigger trigger : triggers) {
                if (task.getExecutionStatus() != TaskExecutionStatus.SUSPENDED || !trigger.getKey().equals(standardTriggerKey)) {
                    String m1 = "Removing Quartz trigger " + trigger.getKey() + " for WAITING/CLOSED/SUSPENDED task " + task;
                    message += "[" + m1 + "] ";
                    LOGGER.trace(" - " + m1);
                    scheduler.unscheduleJob(trigger.getKey());
                    changed = true;
                } else {
                // For SUSPENDED tasks, we keep the standard trigger untouched. We want to preserve original
                // scheduled time. (This might or might not be what the user wants ... but it has been so for so
                // many years, so let's not change it now.)
                //
                // It's harmless to keep the standard trigger, because:
                // 1) If a trigger is mistakenly alive, JobExecutor will take care of it.
                // 2) If a trigger has wrong parameters, this will be corrected on task resume.
                }
            }
        } else if (task.getExecutionStatus() == TaskExecutionStatus.RUNNABLE) {
            Trigger triggerToBe;
            try {
                triggerToBe = TaskQuartzImplUtil.createTriggerForTask(task);
            } catch (ParseException e) {
                String message2 = "Cannot create a trigger for a task " + this + " because of a cron expression parsing exception";
                LoggingUtils.logUnexpectedException(LOGGER, message2, e);
                result.recordFatalError(message2, e);
                // TODO: implement error handling correctly
                throw new SystemException("Cannot a trigger for a task because of a cron expression parsing exception", e);
            }
            if (triggerToBe == null) {
                if (standardTriggerExists) {
                    // TODO what about non-standard triggers?
                    // These may be legal here (e.g. for a manually-run recurring task waiting to get a chance to run)
                    String m1 = "Removing standard Quartz trigger for RUNNABLE task that should not have it; task = " + task;
                    message += "[" + m1 + "] ";
                    LOGGER.trace(" - " + m1);
                    scheduler.unscheduleJob(TriggerKey.triggerKey(oid));
                    changed = true;
                }
            } else {
                // if the trigger should exist and it does not...
                if (!standardTriggerExists) {
                    String m1 = "Creating standard trigger for a RUNNABLE task " + task;
                    LOGGER.trace(" - " + m1);
                    message += "[" + m1 + "] ";
                    scheduler.scheduleJob(triggerToBe);
                    changed = true;
                } else {
                    // we have to compare trigger parameters with the task's ones
                    Trigger triggerAsIs = scheduler.getTrigger(standardTriggerKey);
                    if (task.isRecreateQuartzTrigger() || TaskQuartzImplUtil.triggerDataMapsDiffer(triggerAsIs, triggerToBe)) {
                        String m1 = "Existing trigger has incompatible parameters or was explicitly requested to be recreated; recreating it. Task = " + task;
                        LOGGER.trace(" - " + m1);
                        message += "[" + m1 + "] ";
                        scheduler.rescheduleJob(standardTriggerKey, triggerToBe);
                        changed = true;
                    } else {
                        String m1 = "Existing trigger is OK, leaving it as is; task = " + task;
                        LOGGER.trace(" - " + m1);
                        message += "[" + m1 + "] ";
                        Trigger.TriggerState state = scheduler.getTriggerState(standardTriggerKey);
                        if (state == Trigger.TriggerState.PAUSED) {
                            String m2 = "However, the trigger is paused, resuming it; task = " + task;
                            LOGGER.trace(" - " + m2);
                            message += "[" + m2 + "] ";
                            scheduler.resumeTrigger(standardTriggerKey);
                            changed = true;
                        }
                    }
                }
            }
        }
    } catch (Exception e) {
        // todo make this more specific (originally here was SchedulerException but e.g. for negative repeat intervals here we get unchecked IllegalArgumentException...)
        String message2 = "Cannot synchronize repository/Quartz Job Store information for task " + task;
        LoggingUtils.logUnexpectedException(LOGGER, message2, e);
        result.recordFatalError(message2, e);
    }
    if (result.isUnknown()) {
        result.computeStatus();
        result.recordStatus(result.getStatus(), message);
    }
    return changed;
}
Also used : TriggerKey(org.quartz.TriggerKey) JobKey(org.quartz.JobKey) Trigger(org.quartz.Trigger) SystemException(com.evolveum.midpoint.util.exception.SystemException) Scheduler(org.quartz.Scheduler) OperationResult(com.evolveum.midpoint.schema.result.OperationResult) ParseException(java.text.ParseException) SchemaException(com.evolveum.midpoint.util.exception.SchemaException) SchedulerException(org.quartz.SchedulerException) ParseException(java.text.ParseException) ObjectNotFoundException(com.evolveum.midpoint.util.exception.ObjectNotFoundException) SystemException(com.evolveum.midpoint.util.exception.SystemException)

Example 34 with Scheduler

use of org.quartz.Scheduler in project midpoint by Evolveum.

the class TaskSynchronizer method synchronizeJobStores.

/**
     * Checks for consistency between Quartz job store and midPoint repository.
     * In case of conflict, the latter is taken as authoritative source.
     *
     * (For RAM Job Store, running this method at startup effectively means that tasks from midPoint repo are imported into Quartz job store.)
     *
     */
boolean synchronizeJobStores(OperationResult parentResult) {
    OperationResult result = parentResult.createSubresult(this.getClass().getName() + ".synchronizeJobStores");
    Scheduler scheduler = taskManager.getExecutionManager().getQuartzScheduler();
    LOGGER.info("Synchronizing Quartz job store with midPoint repository.");
    List<PrismObject<TaskType>> tasks;
    try {
        tasks = getRepositoryService().searchObjects(TaskType.class, new ObjectQuery(), null, result);
    } catch (SchemaException | RuntimeException e) {
        LoggingUtils.logUnexpectedException(LOGGER, "Synchronization cannot be done, because tasks cannot be listed from the repository.", e);
        return false;
    }
    LOGGER.trace("There are {} task(s) in repository", tasks.size());
    // check consistency of tasks present in repo
    Set<String> oidsInRepo = new HashSet<>();
    int processed = 0;
    int changed = 0;
    int errors = 0;
    for (PrismObject<TaskType> taskPrism : tasks) {
        if (taskPrism.getOid() == null) {
            LOGGER.error("Skipping task with no OID: {}", taskPrism);
            errors++;
            continue;
        }
        oidsInRepo.add(taskPrism.getOid());
        TaskQuartzImpl task;
        try {
            // in order for the task to be "fresh"
            task = (TaskQuartzImpl) taskManager.getTask(taskPrism.getOid(), result);
            if (synchronizeTask(task, result)) {
                // todo are we sure that we increment this counter only for successfully processed tasks? we hope so :)
                changed++;
            }
        } catch (SchemaException e) {
            LoggingUtils.logUnexpectedException(LOGGER, "Task Manager cannot synchronize task {} due to schema exception.", e, taskPrism.getOid());
        } catch (ObjectNotFoundException e) {
            LoggingUtils.logException(LOGGER, "Task Manager cannot synchronize task {} because it does not exist", e, taskPrism.getOid());
        }
        if (result.getLastSubresultStatus() == OperationResultStatus.SUCCESS) {
            processed++;
        } else {
            errors++;
        }
    }
    // remove non-existing tasks
    int removed = 0;
    Set<JobKey> jobs = null;
    try {
        jobs = new HashSet<>(scheduler.getJobKeys(jobGroupEquals(JobKey.DEFAULT_GROUP)));
    } catch (SchedulerException e) {
        String message = "Cannot list jobs from Quartz scheduler, skipping second part of synchronization procedure.";
        LoggingUtils.logUnexpectedException(LOGGER, message, e);
        result.recordPartialError(message, e);
    }
    if (jobs != null) {
        LOGGER.trace("There are {} job(s) in Quartz job store", jobs.size());
        for (JobKey job : jobs) {
            if (!oidsInRepo.contains(job.getName()) && !RemoteNodesManager.STARTER_JOB_KEY.equals(job)) {
                LOGGER.info("Task " + job.getName() + " is not in repository, removing from Quartz job store.");
                try {
                    scheduler.deleteJob(job);
                    removed++;
                } catch (SchedulerException e) {
                    String message = "Cannot remove job " + job.getName() + " from Quartz job store";
                    LoggingUtils.logUnexpectedException(LOGGER, message, e);
                    result.createSubresult("deleteQuartzJob").recordPartialError(message, e);
                    errors++;
                }
            }
        }
    }
    String resultMessage = "Synchronization of midpoint and Quartz task store finished. " + processed + " task(s) existing in midPoint repository successfully processed, resulting in " + changed + " updated Quartz job(s). " + removed + " task(s) removed from Quartz job store. Processing of " + errors + " task(s) failed.";
    LOGGER.info(resultMessage);
    if (result.isUnknown()) {
        result.recordStatus(OperationResultStatus.SUCCESS, resultMessage);
    }
    return true;
}
Also used : SchemaException(com.evolveum.midpoint.util.exception.SchemaException) SchedulerException(org.quartz.SchedulerException) Scheduler(org.quartz.Scheduler) OperationResult(com.evolveum.midpoint.schema.result.OperationResult) ObjectQuery(com.evolveum.midpoint.prism.query.ObjectQuery) PrismObject(com.evolveum.midpoint.prism.PrismObject) JobKey(org.quartz.JobKey) TaskType(com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType) TaskQuartzImpl(com.evolveum.midpoint.task.quartzimpl.TaskQuartzImpl) ObjectNotFoundException(com.evolveum.midpoint.util.exception.ObjectNotFoundException) HashSet(java.util.HashSet)

Example 35 with Scheduler

use of org.quartz.Scheduler in project searchcode-server by boyter.

the class JobService method startIndexerJob.

private void startIndexerJob() throws SchedulerException {
    Scheduler scheduler = Singleton.getScheduler();
    // Setup the indexer which runs forever indexing
    JobDetail job = newJob(IndexDocumentsJob.class).withIdentity("indexerjob").build();
    SimpleTrigger trigger = newTrigger().withIdentity("indexerjob").withSchedule(simpleSchedule().withIntervalInSeconds(this.INDEXTIME).repeatForever()).build();
    scheduler.scheduleJob(job, trigger);
    scheduler.start();
}
Also used : JobDetail(org.quartz.JobDetail) Scheduler(org.quartz.Scheduler) SimpleTrigger(org.quartz.SimpleTrigger)

Aggregations

Scheduler (org.quartz.Scheduler)74 SchedulerException (org.quartz.SchedulerException)38 JobDetail (org.quartz.JobDetail)34 Trigger (org.quartz.Trigger)23 Test (org.junit.Test)19 JobKey (org.quartz.JobKey)17 SimpleTrigger (org.quartz.SimpleTrigger)16 CronTrigger (org.quartz.CronTrigger)15 JobDataMap (org.quartz.JobDataMap)10 TriggerKey (org.quartz.TriggerKey)10 TriggerBuilder.newTrigger (org.quartz.TriggerBuilder.newTrigger)9 ArrayList (java.util.ArrayList)7 Date (java.util.Date)6 Command (org.openhab.core.types.Command)5 InetSocketAddress (java.net.InetSocketAddress)4 SocketChannel (java.nio.channels.SocketChannel)4 IOException (java.io.IOException)3 RouteBuilder (org.apache.camel.builder.RouteBuilder)3 DefaultCamelContext (org.apache.camel.impl.DefaultCamelContext)3 StdSchedulerFactory (org.quartz.impl.StdSchedulerFactory)3