/* * Copyright © 2014-2015 Cask Data, Inc. * * Licensed 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 co.cask.cdap.internal.app.runtime.schedule; import co.cask.cdap.api.app.ApplicationSpecification; import co.cask.cdap.api.schedule.SchedulableProgramType; import co.cask.cdap.api.schedule.Schedule; import co.cask.cdap.api.schedule.ScheduleSpecification; import co.cask.cdap.app.store.Store; import co.cask.cdap.common.ApplicationNotFoundException; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.internal.schedule.StreamSizeSchedule; import co.cask.cdap.internal.schedule.TimeSchedule; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramType; import co.cask.cdap.proto.ScheduledRuntime; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.AbstractIdleService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; /** * Abstract scheduler service common scheduling functionality. For each {@link Schedule} implementation, there is * a scheduler that this class will delegate the work to. * The extending classes should implement prestart and poststop hooks to perform any action before starting all * underlying schedulers and after stopping them. */ public abstract class AbstractSchedulerService extends AbstractIdleService implements SchedulerService { private static final Logger LOG = LoggerFactory.getLogger(AbstractSchedulerService.class); private final TimeScheduler timeScheduler; private final StreamSizeScheduler streamSizeScheduler; private final Store store; public AbstractSchedulerService(TimeScheduler timeScheduler, StreamSizeScheduler streamSizeScheduler, Store store) { this.timeScheduler = timeScheduler; this.streamSizeScheduler = streamSizeScheduler; this.store = store; } /** * Start the scheduler services, by initializing them and starting them */ protected final void startSchedulers() throws SchedulerException { try { timeScheduler.init(); timeScheduler.start(); LOG.info("Started time scheduler"); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SchedulerException.class); throw new SchedulerException(t); } try { streamSizeScheduler.init(); streamSizeScheduler.start(); LOG.info("Started stream size scheduler"); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, SchedulerException.class); throw new SchedulerException(t); } } private boolean isStarted(Scheduler scheduler) { if (scheduler instanceof TimeScheduler) { return ((TimeScheduler) scheduler).isStarted(); } else if (scheduler instanceof StreamSizeScheduler) { return ((StreamSizeScheduler) scheduler).isStarted(); } throw new IllegalArgumentException("Unrecognized type of scheduler for " + scheduler.getClass().toString()); } /** * Stop the quartz scheduler service. */ protected final void stopScheduler() throws SchedulerException { try { streamSizeScheduler.stop(); LOG.info("Stopped stream size scheduler"); } catch (Throwable t) { LOG.error("Error stopping stream size scheduler", t); Throwables.propagateIfInstanceOf(t, SchedulerException.class); throw new SchedulerException(t); } finally { try { timeScheduler.stop(); LOG.info("Stopped time scheduler"); } catch (Throwable t) { LOG.error("Error stopping time scheduler", t); Throwables.propagateIfInstanceOf(t, SchedulerException.class); throw new SchedulerException(t); } } } @Override public void schedule(Id.Program programId, SchedulableProgramType programType, Schedule schedule) throws SchedulerException { schedule(programId, programType, schedule, ImmutableMap.<String, String>of()); } @Override public void schedule(Id.Program programId, SchedulableProgramType programType, Schedule schedule, Map<String, String> properties) throws SchedulerException { Scheduler scheduler; scheduler = getScheduler(schedule); scheduler.schedule(programId, programType, schedule, properties); } @Override public void schedule(Id.Program programId, SchedulableProgramType programType, Iterable<Schedule> schedules) throws SchedulerException { schedule(programId, programType, schedules, ImmutableMap.<String, String>of()); } @Override public void schedule(Id.Program programId, SchedulableProgramType programType, Iterable<Schedule> schedules, Map<String, String> properties) throws SchedulerException { for (Schedule schedule : schedules) { schedule(programId, programType, schedule, properties); } } @Override public List<ScheduledRuntime> previousScheduledRuntime(Id.Program program, SchedulableProgramType programType) throws SchedulerException { return timeScheduler.previousScheduledRuntime(program, programType); } @Override public List<ScheduledRuntime> nextScheduledRuntime(Id.Program program, SchedulableProgramType programType) throws SchedulerException { return timeScheduler.nextScheduledRuntime(program, programType); } @Override public List<String> getScheduleIds(Id.Program program, SchedulableProgramType programType) throws SchedulerException { return ImmutableList.<String>builder() .addAll(timeScheduler.getScheduleIds(program, programType)) .addAll(streamSizeScheduler.getScheduleIds(program, programType)) .build(); } @Override public void suspendSchedule(Id.Program program, SchedulableProgramType programType, String scheduleName) throws NotFoundException, SchedulerException { Scheduler scheduler = getSchedulerForSchedule(program, scheduleName); scheduler.suspendSchedule(program, programType, scheduleName); } @Override public void resumeSchedule(Id.Program program, SchedulableProgramType programType, String scheduleName) throws NotFoundException, SchedulerException { Scheduler scheduler = getSchedulerForSchedule(program, scheduleName); scheduler.resumeSchedule(program, programType, scheduleName); } @Override public void updateSchedule(Id.Program program, SchedulableProgramType programType, Schedule schedule) throws NotFoundException, SchedulerException { updateSchedule(program, programType, schedule, ImmutableMap.<String, String>of()); } @Override public void updateSchedule(Id.Program program, SchedulableProgramType programType, Schedule schedule, Map<String, String> properties) throws NotFoundException, SchedulerException { Scheduler scheduler = getSchedulerForSchedule(program, schedule.getName()); ScheduleState scheduleState = scheduleState(program, programType, schedule.getName()); scheduler.updateSchedule(program, programType, schedule, properties); // the update of schedule will delete and a create new one so we have to suspend it if it was suspended if (scheduleState == ScheduleState.SUSPENDED) { suspendSchedule(program, programType, schedule.getName()); } } @Override public void deleteSchedule(Id.Program program, SchedulableProgramType programType, String scheduleName) throws NotFoundException, SchedulerException { Scheduler scheduler = getSchedulerForSchedule(program, scheduleName); scheduler.deleteSchedule(program, programType, scheduleName); } @Override public void deleteSchedules(Id.Program program, SchedulableProgramType programType) throws SchedulerException { timeScheduler.deleteSchedules(program, programType); streamSizeScheduler.deleteSchedules(program, programType); } @Override public void deleteAllSchedules(Id.Namespace namespaceId) throws SchedulerException { for (ApplicationSpecification appSpec : store.getAllApplications(namespaceId)) { deleteAllSchedules(namespaceId, appSpec); } } private void deleteAllSchedules(Id.Namespace namespaceId, ApplicationSpecification appSpec) throws SchedulerException { for (ScheduleSpecification scheduleSpec : appSpec.getSchedules().values()) { Id.Application appId = Id.Application.from(namespaceId.getId(), appSpec.getName()); ProgramType programType = ProgramType.valueOfSchedulableType(scheduleSpec.getProgram().getProgramType()); Id.Program programId = Id.Program.from(appId, programType, scheduleSpec.getProgram().getProgramName()); deleteSchedules(programId, scheduleSpec.getProgram().getProgramType()); } } @Override public ScheduleState scheduleState(Id.Program program, SchedulableProgramType programType, String scheduleName) throws SchedulerException { try { Scheduler scheduler = getSchedulerForSchedule(program, scheduleName); return scheduler.scheduleState(program, programType, scheduleName); } catch (NotFoundException e) { return ScheduleState.NOT_FOUND; } } public static String scheduleIdFor(Id.Program program, SchedulableProgramType programType, String scheduleName) { return String.format("%s:%s", programIdFor(program, programType), scheduleName); } public static String programIdFor(Id.Program program, SchedulableProgramType programType) { return String.format("%s:%s:%s:%s", program.getNamespaceId(), program.getApplicationId(), programType.name(), program.getId()); } private Scheduler getSchedulerForSchedule(Id.Program program, String scheduleName) throws NotFoundException { ApplicationSpecification appSpec = store.getApplication(program.getApplication()); if (appSpec == null) { throw new ApplicationNotFoundException(program.getApplication()); } Map<String, ScheduleSpecification> schedules = appSpec.getSchedules(); if (schedules == null || !schedules.containsKey(scheduleName)) { throw new ScheduleNotFoundException(Id.Schedule.from(program.getApplication(), scheduleName)); } ScheduleSpecification scheduleSpec = schedules.get(scheduleName); Schedule schedule = scheduleSpec.getSchedule(); return getScheduler(schedule); } private Scheduler getScheduler(Schedule schedule) { if (schedule instanceof TimeSchedule) { return timeScheduler; } else if (schedule instanceof StreamSizeSchedule) { return streamSizeScheduler; } else { throw new IllegalArgumentException("Unhandled type of schedule: " + schedule.getClass()); } } }