001package com.identityworksllc.iiq.common.vo; 002 003import com.fasterxml.jackson.annotation.JsonAutoDetect; 004import com.fasterxml.jackson.annotation.JsonInclude; 005import com.google.common.base.Objects; 006import org.apache.commons.lang3.builder.ToStringBuilder; 007import sailpoint.tools.Message; 008 009import java.io.Serializable; 010import java.util.ArrayList; 011import java.util.List; 012import java.util.StringJoiner; 013 014/** 015 * A generic class to represent (and perhaps log) some operation outcome. 016 * It contains fields for generic start/stop tracking, as well as fields 017 * for a slew of output and logging indicators. These are not intended 018 * to be used in any particular way. 019 */ 020@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 021public class Outcome implements AutoCloseable, Serializable { 022 023 /** 024 * Create and start a new Outcome object. This is intended to be used 025 * in conjunction with the try-with-resources and AutoClosable function 026 * to start/stop your Outcome along with its operation. 027 * @return A started outcome 028 */ 029 public static Outcome start() { 030 Outcome outcome = new Outcome(); 031 outcome.startTimeMillis = outcome.created; 032 return outcome; 033 } 034 035 /** 036 * The name of an application associated with this outcome 037 */ 038 private String applicationName; 039 040 /** 041 * The name of an attribute associated with this outcome 042 */ 043 private String attribute; 044 045 /** 046 * The millisecond timestamp at which this object was created 047 */ 048 private final long created; 049 050 /** 051 * The name of an identity associated with this outcome 052 */ 053 private String identityName; 054 055 /** 056 * The list of timestampped messages logged for this outcome 057 */ 058 private final List<StampedMessage> messages; 059 060 /** 061 * A native identity associated with this outcome 062 */ 063 private String nativeIdentity; 064 065 /** 066 * An arbitrary object ID associated with this outcome 067 */ 068 private String objectId; 069 070 /** 071 * An arbitrary object name associated with this outcome 072 */ 073 private String objectName; 074 075 /** 076 * The type of the object ID or name above 077 */ 078 private String objectType; 079 080 /** 081 * The provisioning transaction associated with this outcome 082 */ 083 private String provisioningTransaction; 084 085 /** 086 * An indicator that something has been refreshed 087 */ 088 private Boolean refreshed; 089 090 /** 091 * A response code, intended when this is used for reporting an HTTP response 092 */ 093 @JsonInclude(JsonInclude.Include.NON_DEFAULT) 094 private Integer responseCode; 095 096 /** 097 * The start time in milliseconds 098 */ 099 @JsonInclude(JsonInclude.Include.NON_DEFAULT) 100 private long startTimeMillis; 101 102 /** 103 * The status of this operation 104 */ 105 private OutcomeType status; 106 107 /** 108 * The stop time in milliseconds 109 */ 110 @JsonInclude(JsonInclude.Include.NON_DEFAULT) 111 private long stopTimeMillis; 112 113 /** 114 * Arbitrary text associated with this operation 115 */ 116 private String text; 117 118 /** 119 * A flag indicating that something was updated 120 */ 121 private Boolean updated; 122 123 /** 124 * Arbitrary value associated with this operation (presumably of the named attribute) 125 */ 126 private String value; 127 128 /** 129 * Basic constructor, also used by Jackson to figure out what the 'default' for each 130 * item in the class is. 131 */ 132 public Outcome() { 133 this.created = System.currentTimeMillis(); 134 this.stopTimeMillis = -1L; 135 this.startTimeMillis = -1L; 136 this.messages = new ArrayList<>(); 137 } 138 139 /** 140 * Adds a timestamped error to this outcome 141 * @param input A string message to use with the error 142 * @param err The error itself 143 */ 144 public void addError(String input, Throwable err) { 145 this.messages.add(new StampedMessage(input, err)); 146 } 147 148 /** 149 * Adds a timestamped IIQ message to this outcome 150 * @param input The IIQ outcome 151 */ 152 public void addMessage(Message input) { 153 this.messages.add(new StampedMessage(input)); 154 } 155 156 /** 157 * Adds a timestamped log message of INFO level to this outcome 158 * @param input The log message 159 */ 160 public void addMessage(String input) { 161 this.messages.add(new StampedMessage(input)); 162 } 163 164 /** 165 * If the outcome has not yet had its stop timestamp set, sets it to the current time 166 */ 167 @Override 168 public void close() { 169 if (this.stopTimeMillis < 0) { 170 this.stopTimeMillis = System.currentTimeMillis(); 171 } 172 } 173 174 public String getApplicationName() { 175 return applicationName; 176 } 177 178 public String getAttribute() { 179 return attribute; 180 } 181 182 public long getCreated() { 183 return created; 184 } 185 186 public String getIdentityName() { 187 return identityName; 188 } 189 190 public List<StampedMessage> getMessages() { 191 return messages; 192 } 193 194 public String getNativeIdentity() { 195 return nativeIdentity; 196 } 197 198 public String getObjectId() { 199 return objectId; 200 } 201 202 public String getObjectName() { 203 return objectName; 204 } 205 206 public String getObjectType() { 207 return objectType; 208 } 209 210 public String getProvisioningTransaction() { 211 return provisioningTransaction; 212 } 213 214 public int getResponseCode() { 215 return responseCode != null ? responseCode : 0; 216 } 217 218 public long getStartTimeMillis() { 219 return startTimeMillis; 220 } 221 222 /** 223 * Gets the status, or calculates it based on other fields. 224 * 225 * If there is no status explicitly set and the stop time is not set, returns null. 226 * 227 * @return The status, or null if the outcome is not yet stopped 228 */ 229 public OutcomeType getStatus() { 230 if (status != null) { 231 return status; 232 } else if (this.stopTimeMillis < 0) { 233 return null; 234 } else { 235 boolean hasError = this.messages.stream().anyMatch(msg -> msg.getLevel() == LogLevel.ERROR); 236 if (hasError) { 237 return OutcomeType.Failure; 238 } 239 boolean hasWarning = this.messages.stream().anyMatch(msg -> msg.getLevel() == LogLevel.WARN); 240 if (hasWarning) { 241 return OutcomeType.Warning; 242 } 243 return OutcomeType.Success; 244 } 245 } 246 247 public long getStopTimeMillis() { 248 return stopTimeMillis; 249 } 250 251 public String getText() { 252 return text; 253 } 254 255 public String getValue() { 256 return value; 257 } 258 259 public boolean isRefreshed() { 260 return refreshed != null && refreshed; 261 } 262 263 public boolean isUpdated() { 264 return updated != null && updated; 265 } 266 267 public void setApplicationName(String applicationName) { 268 this.applicationName = applicationName; 269 } 270 271 public void setAttribute(String attribute) { 272 this.attribute = attribute; 273 } 274 275 public void setIdentityName(String identityName) { 276 this.identityName = identityName; 277 } 278 279 public void setNativeIdentity(String nativeIdentity) { 280 this.nativeIdentity = nativeIdentity; 281 } 282 283 public void setObjectId(String objectId) { 284 this.objectId = objectId; 285 } 286 287 public void setObjectName(String objectName) { 288 this.objectName = objectName; 289 } 290 291 public void setObjectType(String objectType) { 292 this.objectType = objectType; 293 } 294 295 public void setProvisioningTransaction(String provisioningTransaction) { 296 this.provisioningTransaction = provisioningTransaction; 297 } 298 299 public void setRefreshed(Boolean refreshed) { 300 this.refreshed = refreshed; 301 } 302 303 public void setResponseCode(Integer responseCode) { 304 this.responseCode = responseCode; 305 } 306 307 public void setStartTimeMillis(long startTimeMillis) { 308 this.startTimeMillis = startTimeMillis; 309 } 310 311 public void setStatus(OutcomeType status) { 312 this.status = status; 313 } 314 315 public void setStopTimeMillis(long stopTimeMillis) { 316 this.stopTimeMillis = stopTimeMillis; 317 } 318 319 public void setText(String text) { 320 this.text = text; 321 } 322 323 public void setUpdated(Boolean updated) { 324 this.updated = updated; 325 } 326 327 public void setValue(String value) { 328 this.value = value; 329 } 330 331 /** 332 * Sets the status to Terminated and the stop time to the current timestamp 333 */ 334 public void terminate() { 335 this.status = OutcomeType.Terminated; 336 this.stopTimeMillis = System.currentTimeMillis(); 337 } 338 339 @Override 340 public String toString() { 341 StringJoiner joiner = new StringJoiner(", ", Outcome.class.getSimpleName() + "[", "]"); 342 if ((applicationName) != null) { 343 joiner.add("applicationName='" + applicationName + "'"); 344 } 345 if ((attribute) != null) { 346 joiner.add("attribute='" + attribute + "'"); 347 } 348 joiner.add("created=" + created); 349 if ((identityName) != null) { 350 joiner.add("identityName='" + identityName + "'"); 351 } 352 if ((messages) != null) { 353 joiner.add("messages=" + messages); 354 } 355 if ((nativeIdentity) != null) { 356 joiner.add("nativeIdentity='" + nativeIdentity + "'"); 357 } 358 if ((objectId) != null) { 359 joiner.add("objectId='" + objectId + "'"); 360 } 361 if ((objectName) != null) { 362 joiner.add("objectName='" + objectName + "'"); 363 } 364 if ((objectType) != null) { 365 joiner.add("objectType='" + objectType + "'"); 366 } 367 if ((provisioningTransaction) != null) { 368 joiner.add("provisioningTransaction='" + provisioningTransaction + "'"); 369 } 370 joiner.add("refreshed=" + refreshed); 371 if (responseCode != null && responseCode > 0) { 372 joiner.add("responseCode=" + responseCode); 373 } 374 joiner.add("startTimeMillis=" + startTimeMillis); 375 if ((status) != null) { 376 joiner.add("status=" + status); 377 } 378 joiner.add("stopTimeMillis=" + stopTimeMillis); 379 if ((text) != null) { 380 joiner.add("text='" + text + "'"); 381 } 382 joiner.add("updated=" + updated); 383 if ((value) != null) { 384 joiner.add("value='" + value + "'"); 385 } 386 return joiner.toString(); 387 } 388}