/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package controllers.catalog; import static com.emc.vipr.client.core.util.ResourceUtils.uri; import java.net.URI; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import play.data.validation.Max; import play.data.validation.MaxSize; import play.data.validation.Min; import play.data.validation.MinSize; import play.data.validation.Required; import play.data.validation.Validation; import play.i18n.Messages; import play.mvc.Controller; import play.mvc.Util; import play.mvc.With; import util.Event; import util.ExecutionWindowUtils; import util.MessagesUtils; import util.OrderUtils; import util.TimeUtils; import util.TimeUtils.Duration; import com.emc.vipr.model.catalog.ExecutionWindowCommonParam; import com.emc.vipr.model.catalog.ExecutionWindowCreateParam; import com.emc.vipr.model.catalog.ExecutionWindowRestRep; import com.emc.vipr.model.catalog.ExecutionWindowUpdateParam; import com.emc.vipr.model.catalog.OrderRestRep; import com.google.common.collect.Maps; import controllers.Common; import controllers.deadbolt.Restrict; import controllers.deadbolt.Restrictions; import controllers.tenant.TenantSelector; import controllers.util.AbstractRestRepForm; import controllers.util.Models; /** * @author Chris Dail */ @With(Common.class) @Restrictions({ @Restrict("TENANT_ADMIN") }) public class ExecutionWindows extends Controller { public static void show() { Date serverTime = new Date(System.currentTimeMillis()); TenantSelector.addRenderArgs(); render(serverTime); } public static void create(Long start, Long end, @Required Integer timezoneOffsetInMinutes) { if (start == null) { badRequest("Start not specified"); } String tenantId = Models.currentAdminTenant(); ExecutionWindowForm executionWindowForm = new ExecutionWindowForm(); executionWindowForm.tenantId = tenantId; executionWindowForm.timezoneOffsetInMinutes = timezoneOffsetInMinutes == null ? 0 : timezoneOffsetInMinutes; executionWindowForm.type = ExecutionWindowRestRep.WEEKLY; executionWindowForm.updateTimes(start, end); addDateTimeRenderArgs(); render("@edit", executionWindowForm); } public static void edit(@Required String id, @Required Integer timezoneOffsetInMinutes) { ExecutionWindowRestRep executionWindow = ExecutionWindowUtils.getExecutionWindow(uri(id)); if (executionWindow == null) { notFound(); } ExecutionWindowForm executionWindowForm = new ExecutionWindowForm(); executionWindowForm.timezoneOffsetInMinutes = timezoneOffsetInMinutes == null ? 0 : timezoneOffsetInMinutes; executionWindowForm.readFrom(executionWindow); addDateTimeRenderArgs(); render(executionWindowForm); } public static void save(ExecutionWindowForm executionWindowForm) { executionWindowForm.validate("executionWindowForm"); boolean isOverlapping = isOverlapping(executionWindowForm); if (Validation.hasErrors() || isOverlapping) { if (isOverlapping) { renderArgs.put("error", MessagesUtils.get("executionWindow.overlapping")); } response.status = 400; addDateTimeRenderArgs(); params.flash(); render("@edit", executionWindowForm); } else { ExecutionWindowRestRep executionWindow = executionWindowForm.save(); renderText(MessagesUtils.get("executionWindow.saved.success", executionWindow.getName())); } } public static void move(@Required String id, @Required Long start, @Required Integer timezoneOffsetInMinutes) { ExecutionWindowRestRep executionWindow = ExecutionWindowUtils.getExecutionWindow(uri(id)); if (executionWindow == null) { renderJSON(new CalResponse(false, MessagesUtils.get("executionWindow.notfound", id))); } ExecutionWindowForm executionWindowForm = new ExecutionWindowForm(); executionWindowForm.timezoneOffsetInMinutes = timezoneOffsetInMinutes == null ? 0 : timezoneOffsetInMinutes; executionWindowForm.readFrom(executionWindow); executionWindowForm.updateTimes(start, null); boolean isOverlapping = isOverlapping(executionWindowForm); if (isOverlapping) { renderJSON(new CalResponse(false, MessagesUtils.get("executionWindow.overlapping", executionWindow.getName()))); } executionWindowForm.save(); renderJSON(new CalResponse(true, MessagesUtils.get("executionWindow.saved.success", executionWindow.getName()))); } public static void resize(@Required String id, @Required Long start, @Required Long end, @Required Integer timezoneOffsetInMinutes) { ExecutionWindowRestRep executionWindow = ExecutionWindowUtils.getExecutionWindow(uri(id)); if (executionWindow == null) { renderJSON(new CalResponse(false, MessagesUtils.get("executionWindow.notfound", id))); } ExecutionWindowForm executionWindowForm = new ExecutionWindowForm(); executionWindowForm.timezoneOffsetInMinutes = timezoneOffsetInMinutes == null ? 0 : timezoneOffsetInMinutes; executionWindowForm.readFrom(executionWindow); executionWindowForm.updateTimes(start, end); boolean isOverlapping = isOverlapping(executionWindowForm); if (isOverlapping) { renderJSON(new CalResponse(false, MessagesUtils.get("executionWindow.overlapping", executionWindow.getName()))); } executionWindowForm.save(); renderJSON(new CalResponse(true, MessagesUtils.get("executionWindow.saved.success", executionWindow.getName()))); } public static void delete(ExecutionWindowForm executionWindowForm) { ExecutionWindowRestRep executionWindow = ExecutionWindowUtils.getExecutionWindow(uri(executionWindowForm.id)); if (executionWindow == null) { notFound(); } List<OrderRestRep> scheduledOrders = OrderUtils.getScheduledOrdersByExecutionWindow(uri(executionWindowForm.id)); if (Validation.hasErrors() || !scheduledOrders.isEmpty()) { if (!scheduledOrders.isEmpty()) { renderArgs.put("error", MessagesUtils.get("executionWindow.deleted.containsScheduledOrders", scheduledOrders.size())); } response.status = 400; executionWindowForm.readFrom(executionWindow); addDateTimeRenderArgs(); render("@edit", executionWindowForm); } ExecutionWindowUtils.deleteExecutionWindow(executionWindow); renderText(MessagesUtils.get("executionWindow.deleted.success", executionWindow.getName())); } public static void events(int timezoneOffsetInMinutes, String start, String end) { List<ExecutionWindowRestRep> executionWindows = ExecutionWindowUtils.getExecutionWindows(uri(Models.currentAdminTenant())); // NOSONAR // ("Suppressing Sonar violation of Method invoking inefficient number constructor. Method events is invoked with int which is not inefficient") DateTimeZone tz = TimeUtils.getLocalTimeZone(timezoneOffsetInMinutes); DateTimeFormatter formatter = ISODateTimeFormat.date().withZone(tz); DateTime startDateTime = DateTime.parse(start, formatter); DateTime endDateTime = DateTime.parse(end, formatter); List<Event> events = ExecutionWindowUtils.asEvents(executionWindows, startDateTime, endDateTime, tz); renderJSON(events); } @Util public static boolean isOverlapping(ExecutionWindowForm executionWindowForm) { if (executionWindowForm.length == null) { return false; } ExecutionWindowRestRep tempExecutionWindow = new ExecutionWindowRestRep(); tempExecutionWindow.setName(Messages.get("ExecutionWindows.tempWindowLabel")); executionWindowForm.writeTo(tempExecutionWindow); return ExecutionWindowUtils.isOverlapping(tempExecutionWindow); } // Adds choices for dropdowns to the renderArgs private static void addDateTimeRenderArgs() { // Days of the Week Map<String, String> daysOfWeek = Maps.newLinkedHashMap(); for (int i = 1; i <= 7; i++) { String num = String.valueOf(i); daysOfWeek.put(num, MessagesUtils.get("datetime.daysOfWeek." + num)); } renderArgs.put("daysOfWeek", daysOfWeek); // Days of the Month Map<String, String> daysOfMonth = Maps.newLinkedHashMap(); for (int i = 1; i <= 31; i++) { String num = String.valueOf(i); daysOfMonth.put(num, num); } daysOfMonth.put(ExecutionWindowForm.LAST_DAY_OF_MONTH, MessagesUtils.get("datetime.dayOfMonth.L")); renderArgs.put("daysOfMonth", daysOfMonth); // Hours Map<String, String> hoursOfDay = Maps.newLinkedHashMap(); for (int i = 0; i <= 23; i++) { String num = String.valueOf(i); hoursOfDay.put(num, MessagesUtils.get("datetime.hours." + num)); } renderArgs.put("hoursOfDay", hoursOfDay); // Time Durations Map<String, String> timeDuration = Maps.newLinkedHashMap(); timeDuration.put(ExecutionWindowRestRep.MINUTES, MessagesUtils.get("datetime.units.min")); timeDuration.put(ExecutionWindowRestRep.HOURS, MessagesUtils.get("datetime.units.h")); timeDuration.put(ExecutionWindowRestRep.DAYS, MessagesUtils.get("datetime.units.d")); renderArgs.put("timeDuration", timeDuration); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); TimeZone tz = c.getTimeZone(); renderArgs.put("serverTimeZone", tz.getDisplayName(tz.inDaylightTime(new Date()), TimeZone.SHORT)); renderArgs.put("serverTimeZoneOffset", tz.getOffset(new Date().getTime()) / (60 * 1000)); } // public static class CalResponse { public boolean success; public String message; public CalResponse(boolean success, String message) { this.success = success; this.message = message; } public CalResponse() { } } public static class ExecutionWindowForm extends AbstractRestRepForm<ExecutionWindowRestRep> { private static final int MAX_DAYS = 1; private static final int MAX_HOURS = 23; private static final int MIN_MINUTES = 30; private static final int MAX_MINUTES = (23 * 60) + 59; public static final String LAST_DAY_OF_MONTH = "L"; @Required @MaxSize(128) @MinSize(2) public String name; public String tenantId; @Required public Integer hourOfDay; @Required @Min(1) public Integer length; @Required public String lengthType; @Required public String type; @Min(1) @Max(7) public Integer dayOfWeek; public String dayOfMonth; @Required public Integer timezoneOffsetInMinutes; public ExecutionWindowForm() { } @Override protected void doReadFrom(ExecutionWindowRestRep model) { name = model.getName(); tenantId = model.getTenant().getId().toString(); length = model.getExecutionWindowLength(); lengthType = model.getExecutionWindowLengthType(); type = model.getExecutionWindowType(); if (model.getHourOfDayInUTC() != null) { hourOfDay = TimeUtils.getLocalHourOfDay(model.getHourOfDayInUTC(), timezoneOffsetInMinutes); if (model.getDayOfWeek() != null) { dayOfWeek = TimeUtils.getLocalDayOfWeek(model.getDayOfWeek(), model.getHourOfDayInUTC(), timezoneOffsetInMinutes); } } if (model.getLastDayOfMonth() != null && model.getLastDayOfMonth().booleanValue() == true) { dayOfMonth = LAST_DAY_OF_MONTH; } else if (model.getDayOfMonth() != null) { dayOfMonth = Integer.toString(TimeUtils.getLocalDayOfMonth( model.getDayOfMonth(), model.getHourOfDayInUTC(), timezoneOffsetInMinutes)); } } @Override public void validate(String fieldName) { super.validate(fieldName); // Execution Window Name Unique Check if (name != null) { name = name.trim(); if (StringUtils.isNotBlank(name)) { ExecutionWindowRestRep existingExecutionWindow = ExecutionWindowUtils.getExecutionWindow(name, uri(Models.currentAdminTenant())); if (existingExecutionWindow != null && (StringUtils.isBlank(id) || (StringUtils.isNotBlank(id) && existingExecutionWindow.getId().equals( URI.create(id))) == false)) { Validation.addError(fieldName + ".name", MessagesUtils.get("execWindow.name.notUnique")); } } } if (!dayOfMonth.equals(LAST_DAY_OF_MONTH)) { if (!StringUtils.isNumeric(dayOfMonth)) { Validation.addError("executionWindow.dayOfMonth", MessagesUtils.get("execWindow.dayOfMonthNumeric")); } else { int dayOfMonthLocal = Integer.valueOf(dayOfMonth); Validation.min("executionWindow.dayOfMonth", dayOfMonthLocal, 1); Validation.max("executionWindow.dayOfMonth", dayOfMonthLocal, 31); } } if (StringUtils.isNotBlank(params.get("executionWindowForm.length")) && !StringUtils.isNumeric(params.get("executionWindowForm.length"))) { Validation.addError(fieldName + ".length", MessagesUtils.get("validation.invalid")); } else { if (this.length != null) { if (ExecutionWindowRestRep.MINUTES.equals(lengthType)) { if (length.intValue() < MIN_MINUTES) { Validation.addError(fieldName + ".length", MessagesUtils.get("execWindow.length.min", MIN_MINUTES)); } if (length.intValue() > MAX_MINUTES) { Validation.addError(fieldName + ".length", MessagesUtils.get("execWindow.length.max", MAX_MINUTES)); } } else if (ExecutionWindowRestRep.HOURS.equals(lengthType)) { if (length.intValue() > MAX_HOURS) { Validation.addError(fieldName + ".length", MessagesUtils.get("execWindow.length.max", MAX_HOURS)); } } else if (ExecutionWindowRestRep.DAYS.equals(lengthType)) { if (length.intValue() > MAX_DAYS) { Validation.addError(fieldName + ".length", MessagesUtils.get("execWindow.length.max", MAX_DAYS)); } } else { Validation.addError("execWindow.lengthType", MessagesUtils.get("execWindow.lengthType")); } } } } public void setDurationMillis(String durationMillis) { setDurationMillis(Long.parseLong(durationMillis)); } public void setDurationMillis(long durationMillis) { Duration d = TimeUtils.toDuration(durationMillis); length = (int) d.duration; lengthType = d.unit.name(); } public String getDurationMillis() { return String.valueOf(TimeUtils.toMillis(length, lengthType)); } public void updateTimes(Long start, Long end) { DateTime startDateTime = new DateTime(start); startDateTime = startDateTime.withZone(TimeUtils.getLocalTimeZone(timezoneOffsetInMinutes)); hourOfDay = startDateTime.getHourOfDay(); dayOfWeek = startDateTime.getDayOfWeek(); dayOfMonth = String.valueOf(startDateTime.getDayOfMonth()); if (end != null) { long durationMillis = (end - start); setDurationMillis(durationMillis); } } @Override protected ExecutionWindowRestRep doCreate() { ExecutionWindowRestRep executionWindow = null; try { ExecutionWindowCreateParam createParam = new ExecutionWindowCreateParam(); createParam.setTenant(uri(this.tenantId)); writeCommon(createParam); executionWindow = ExecutionWindowUtils.createExecutionWindow(createParam); } catch (Exception e) { flash.error(e.getMessage()); Common.handleError(); } return executionWindow; } @Override protected ExecutionWindowRestRep doUpdate() { ExecutionWindowRestRep executionWindow = null; try { ExecutionWindowUpdateParam updateParam = new ExecutionWindowUpdateParam(); writeCommon(updateParam); executionWindow = ExecutionWindowUtils.updateExecutionWindow(uri(this.id), updateParam); } catch (Exception e) { flash.error(e.getMessage()); Common.handleError(); } return executionWindow; } private void writeCommon(ExecutionWindowCommonParam commonParam) { commonParam.setName(name); commonParam.setExecutionWindowLength(length); commonParam.setExecutionWindowLengthType(lengthType); commonParam.setExecutionWindowType(type); commonParam.setHourOfDayInUTC(TimeUtils.getUTCHourOfDay(hourOfDay, timezoneOffsetInMinutes)); commonParam.setDayOfWeek(TimeUtils.getUTCDayOfWeek(dayOfWeek, hourOfDay, timezoneOffsetInMinutes)); if (StringUtils.isNotBlank(dayOfMonth)) { if (dayOfMonth.equals(LAST_DAY_OF_MONTH)) { commonParam.setLastDayOfMonth(Boolean.TRUE); commonParam.setDayOfMonth(null); } else { commonParam.setLastDayOfMonth(Boolean.FALSE); Integer dayOfMonthInLocal = (Integer) ConvertUtils.convert(dayOfMonth, Integer.class); if (dayOfMonthInLocal != null) { commonParam.setDayOfMonth(TimeUtils.getUTCDayOfMonth(dayOfMonthInLocal, hourOfDay, timezoneOffsetInMinutes)); } } } else { commonParam.setLastDayOfMonth(Boolean.FALSE); commonParam.setDayOfMonth(null); } } public void writeTo(ExecutionWindowRestRep executionWindowRestRep) { executionWindowRestRep.setId(uri(this.id)); executionWindowRestRep.setName(name); executionWindowRestRep.setExecutionWindowLength(length); executionWindowRestRep.setExecutionWindowLengthType(lengthType); executionWindowRestRep.setExecutionWindowType(type); executionWindowRestRep.setHourOfDayInUTC(TimeUtils.getUTCHourOfDay(hourOfDay, timezoneOffsetInMinutes)); executionWindowRestRep.setDayOfWeek(TimeUtils.getUTCDayOfWeek(dayOfWeek, hourOfDay, timezoneOffsetInMinutes)); if (StringUtils.isNotBlank(dayOfMonth)) { if (dayOfMonth.equals(LAST_DAY_OF_MONTH)) { executionWindowRestRep.setLastDayOfMonth(Boolean.TRUE); executionWindowRestRep.setDayOfMonth(null); } else { executionWindowRestRep.setLastDayOfMonth(Boolean.FALSE); Integer dayOfMonthInLocal = (Integer) ConvertUtils.convert(dayOfMonth, Integer.class); if (dayOfMonthInLocal != null) { executionWindowRestRep.setDayOfMonth(TimeUtils.getUTCDayOfMonth(dayOfMonthInLocal, hourOfDay, timezoneOffsetInMinutes)); } } } else { executionWindowRestRep.setLastDayOfMonth(Boolean.FALSE); executionWindowRestRep.setDayOfMonth(null); } } } }