/* * Copyright © 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.deploy.pipeline; import co.cask.cdap.api.app.ApplicationSpecification; import co.cask.cdap.api.schedule.ScheduleSpecification; import co.cask.cdap.internal.app.runtime.schedule.Scheduler; import co.cask.cdap.pipeline.AbstractStage; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramType; import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; import java.util.Map; /** * This {@link co.cask.cdap.pipeline.Stage} is responsible for automatic creation of any new schedules * specified by the application. If the schedules already exist, it will update them. */ public class CreateSchedulesStage extends AbstractStage<ApplicationWithPrograms> { private final Scheduler scheduler; public CreateSchedulesStage(Scheduler scheduler) { super(TypeToken.of(ApplicationWithPrograms.class)); this.scheduler = scheduler; } @Override public void process(ApplicationWithPrograms input) throws Exception { ApplicationSpecification existingAppSpec = input.getExistingAppSpecification(); Map<String, ScheduleSpecification> existingSchedulesMap; if (existingAppSpec == null) { existingSchedulesMap = ImmutableMap.of(); } else { existingSchedulesMap = existingAppSpec.getSchedules(); } MapDifference<String, ScheduleSpecification> mapDiff = Maps.difference(existingSchedulesMap, input.getSpecification().getSchedules()); for (Map.Entry<String, ScheduleSpecification> entry : mapDiff.entriesOnlyOnLeft().entrySet()) { // delete schedules that existed in the old app spec, but don't anymore ScheduleSpecification scheduleSpec = entry.getValue(); ProgramType programType = ProgramType.valueOfSchedulableType(scheduleSpec.getProgram().getProgramType()); scheduler.deleteSchedule(Id.Program.from(input.getId(), programType, scheduleSpec.getProgram().getProgramName()), scheduleSpec.getProgram().getProgramType(), scheduleSpec.getSchedule().getName()); } for (Map.Entry<String, MapDifference.ValueDifference<ScheduleSpecification>> entry : mapDiff.entriesDiffering().entrySet()) { // Update those schedules - the new schedules have the same IDs but different specs ScheduleSpecification newScheduleSpec = entry.getValue().rightValue(); ScheduleSpecification oldScheduleSpec = entry.getValue().leftValue(); if (newScheduleSpec.getSchedule().equals(oldScheduleSpec.getSchedule())) { // The schedules are exactly the same - the difference in spec might come from the properties map - // hence it is useless to update the schedule continue; } ProgramType programType = ProgramType.valueOfSchedulableType(newScheduleSpec.getProgram().getProgramType()); scheduler.updateSchedule(Id.Program.from(input.getId(), programType, newScheduleSpec.getProgram().getProgramName()), newScheduleSpec.getProgram().getProgramType(), newScheduleSpec.getSchedule()); } for (Map.Entry<String, ScheduleSpecification> entry : mapDiff.entriesOnlyOnRight().entrySet()) { ScheduleSpecification scheduleSpec = entry.getValue(); ProgramType programType = ProgramType.valueOfSchedulableType(scheduleSpec.getProgram().getProgramType()); scheduler.schedule(Id.Program.from(input.getId(), programType, scheduleSpec.getProgram().getProgramName()), scheduleSpec.getProgram().getProgramType(), scheduleSpec.getSchedule()); } // Note: the mapDiff also has a entriesInCommon method returning all entries in left and right maps // which have exactly the same keys and values. In that case, we don't need to do anything, not // even to update the schedule // Emit the input to next stage. emit(input); } }