001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.api.model.pipelines; 015 016import org.gbif.api.jackson.LocalDateTimeSerDe; 017 018import java.io.Serializable; 019import java.time.LocalDateTime; 020import java.util.Comparator; 021import java.util.Objects; 022import java.util.Optional; 023import java.util.Set; 024import java.util.StringJoiner; 025import java.util.TreeSet; 026import java.util.UUID; 027 028import com.fasterxml.jackson.annotation.JsonInclude; 029import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 030import com.fasterxml.jackson.databind.annotation.JsonSerialize; 031 032import static org.gbif.api.model.pipelines.PipelineStep.Status.RUNNING; 033 034/** 035 * Models a pipeline process for a specific dataset and attempt. 036 */ 037public class PipelineProcess implements Serializable { 038 039 private static final long serialVersionUID = -3992826055732414678L; 040 041 @JsonInclude(JsonInclude.Include.NON_DEFAULT) 042 private long key; 043 044 private UUID datasetKey; 045 private String datasetTitle; 046 private int attempt; 047 048 @JsonSerialize(using = LocalDateTimeSerDe.LocalDateTimeSerializer.class) 049 @JsonDeserialize(using = LocalDateTimeSerDe.LocalDateTimeDeserializer.class) 050 private LocalDateTime created; 051 052 private String createdBy; 053 private Set<PipelineExecution> executions = 054 new TreeSet<>(PIPELINE_EXECUTION_BY_CREATED_ASC.reversed()); 055 056 public static final Comparator<PipelineExecution> PIPELINE_EXECUTION_BY_CREATED_ASC = 057 (e1, e2) -> { 058 LocalDateTime created1 = e1 != null ? e1.getCreated() : null; 059 LocalDateTime created2 = e2 != null ? e2.getCreated() : null; 060 061 if (created1 == null) { 062 return created2 == null ? 0 : 1; 063 } else if (created2 == null) { 064 return -1; 065 } 066 067 return created1.compareTo(created2); 068 }; 069 070 /** 071 * Comparator that sorts the pipeline processes by the created date of the latest execution. 072 */ 073 public static final Comparator<PipelineProcess> PIPELINE_PROCESS_BY_LATEST_EXEUCTION_ASC = 074 (p1, p2) -> { 075 if (p1 == null) { 076 return p2 == null ? 0 : 1; 077 } else if (p2 == null) { 078 return -1; 079 } 080 081 PipelineExecution latestExecution1 = 082 p1.getExecutions().isEmpty() ? null : p1.getExecutions().iterator().next(); 083 PipelineExecution latestExecution2 = 084 p2.getExecutions().isEmpty() ? null : p2.getExecutions().iterator().next(); 085 086 return Objects.compare( 087 latestExecution1, latestExecution2, PIPELINE_EXECUTION_BY_CREATED_ASC); 088 }; 089 090 /** 091 * Comparator that sorts pipeline processes by the start date of their latest step in ascending 092 * order. The steps that are running have preference, and we take into account only the steps of 093 * the latest execution. 094 */ 095 public static final Comparator<PipelineProcess> PIPELINE_PROCESS_BY_LATEST_STEP_RUNNING_ASC = 096 (p1, p2) -> { 097 if (p1 == null) { 098 return p2 == null ? 0 : 1; 099 } else if (p2 == null) { 100 return -1; 101 } 102 103 PipelineExecution latestExecution1 = 104 p1.getExecutions().isEmpty() ? null : p1.getExecutions().iterator().next(); 105 PipelineExecution latestExecution2 = 106 p2.getExecutions().isEmpty() ? null : p2.getExecutions().iterator().next(); 107 108 Optional<PipelineStep> lastStepOpt1 = Optional.empty(); 109 if (latestExecution1 != null && latestExecution1.getSteps() != null) { 110 lastStepOpt1 = 111 latestExecution1.getSteps().stream() 112 .filter(p -> p.getState() != null) 113 .max(Comparator.comparing(PipelineStep::getStarted)); 114 } 115 116 Optional<PipelineStep> lastStepOpt2 = Optional.empty(); 117 if (latestExecution2 != null && latestExecution2.getSteps() != null) { 118 lastStepOpt2 = 119 latestExecution2.getSteps().stream() 120 .filter(p -> p.getState() != null) 121 .max(Comparator.comparing(PipelineStep::getStarted)); 122 } 123 124 if (!lastStepOpt1.isPresent()) { 125 return !lastStepOpt2.isPresent() ? 0 : 1; 126 } else if (!lastStepOpt2.isPresent()) { 127 return -1; 128 } 129 130 PipelineStep step1 = lastStepOpt1.get(); 131 PipelineStep step2 = lastStepOpt2.get(); 132 133 if (step1.getStarted() == null) { 134 return step2.getStarted() == null ? 0 : 1; 135 } else if (step2.getStarted() == null) { 136 return -1; 137 } 138 139 // steps that are running have preference 140 if (step1.getState() == RUNNING) { 141 return step2.getState() == RUNNING ? step1.getStarted().compareTo(step2.getStarted()) : 1; 142 } else if (step2.getState() == RUNNING) { 143 return -1; 144 } else { 145 return step1.getStarted().compareTo(step2.getStarted()); 146 } 147 }; 148 149 public long getKey() { 150 return key; 151 } 152 153 public UUID getDatasetKey() { 154 return datasetKey; 155 } 156 157 public PipelineProcess setDatasetKey(UUID datasetKey) { 158 this.datasetKey = datasetKey; 159 return this; 160 } 161 162 public String getDatasetTitle() { 163 return datasetTitle; 164 } 165 166 public void setDatasetTitle(String datasetTitle) { 167 this.datasetTitle = datasetTitle; 168 } 169 170 public int getAttempt() { 171 return attempt; 172 } 173 174 public PipelineProcess setAttempt(int attempt) { 175 this.attempt = attempt; 176 return this; 177 } 178 179 public LocalDateTime getCreated() { 180 return created; 181 } 182 183 public PipelineProcess setCreated(LocalDateTime created) { 184 this.created = created; 185 return this; 186 } 187 188 public String getCreatedBy() { 189 return createdBy; 190 } 191 192 public PipelineProcess setCreatedBy(String createdBy) { 193 this.createdBy = createdBy; 194 return this; 195 } 196 197 public Set<PipelineExecution> getExecutions() { 198 return executions; 199 } 200 201 public void setExecutions(Set<PipelineExecution> executions) { 202 this.executions.clear(); 203 this.executions.addAll(executions); 204 } 205 206 public PipelineProcess addExecution(PipelineExecution execution) { 207 executions.add(execution); 208 return this; 209 } 210 211 @Override 212 public String toString() { 213 return new StringJoiner(", ", PipelineProcess.class.getSimpleName() + "[", "]") 214 .add("key=" + key) 215 .add("datasetKey=" + datasetKey) 216 .add("datasetTitle=" + datasetTitle) 217 .add("attempt=" + attempt) 218 .add("created=" + created) 219 .add("createdBy='" + createdBy + "'") 220 .add("executions=" + executions) 221 .toString(); 222 } 223 224 @Override 225 public boolean equals(Object o) { 226 if (this == o) return true; 227 if (o == null || getClass() != o.getClass()) return false; 228 PipelineProcess process = (PipelineProcess) o; 229 return attempt == process.attempt && Objects.equals(datasetKey, process.datasetKey); 230 } 231 232 @Override 233 public int hashCode() { 234 return Objects.hash(datasetKey, attempt); 235 } 236}