001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.entity;
021
022import ca.uhn.fhir.batch2.model.JobDefinition;
023import ca.uhn.fhir.batch2.model.StatusEnum;
024import jakarta.persistence.Basic;
025import jakarta.persistence.Column;
026import jakarta.persistence.Entity;
027import jakarta.persistence.EnumType;
028import jakarta.persistence.Enumerated;
029import jakarta.persistence.FetchType;
030import jakarta.persistence.Id;
031import jakarta.persistence.Index;
032import jakarta.persistence.Lob;
033import jakarta.persistence.Table;
034import jakarta.persistence.Temporal;
035import jakarta.persistence.TemporalType;
036import jakarta.persistence.Version;
037import org.apache.commons.lang3.builder.ToStringBuilder;
038import org.apache.commons.lang3.builder.ToStringStyle;
039import org.hibernate.Length;
040import org.hibernate.annotations.JdbcTypeCode;
041import org.hibernate.type.SqlTypes;
042
043import java.io.Serializable;
044import java.util.Date;
045
046import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH;
047import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH;
048import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.WARNING_MSG_MAX_LENGTH;
049import static org.apache.commons.lang3.StringUtils.left;
050
051@Entity
052@Table(
053                name = "BT2_JOB_INSTANCE",
054                indexes = {@Index(name = "IDX_BT2JI_CT", columnList = "CREATE_TIME")})
055public class Batch2JobInstanceEntity implements Serializable {
056
057        public static final int STATUS_MAX_LENGTH = 20;
058        public static final int TIME_REMAINING_LENGTH = 100;
059        public static final int PARAMS_JSON_MAX_LENGTH = 2000;
060        private static final long serialVersionUID = 8187134261799095422L;
061        public static final int INITIATING_USER_NAME_MAX_LENGTH = 200;
062        public static final int INITIATING_CLIENT_ID_MAX_LENGTH = 200;
063
064        @Id
065        @Column(name = "ID", length = JobDefinition.ID_MAX_LENGTH, nullable = false)
066        private String myId;
067
068        @Column(name = "CREATE_TIME", nullable = false)
069        @Temporal(TemporalType.TIMESTAMP)
070        private Date myCreateTime;
071
072        @Column(name = "START_TIME", nullable = true)
073        @Temporal(TemporalType.TIMESTAMP)
074        private Date myStartTime;
075
076        @Column(name = "END_TIME", nullable = true)
077        @Temporal(TemporalType.TIMESTAMP)
078        private Date myEndTime;
079
080        @Version
081        @Column(name = "UPDATE_TIME", nullable = true)
082        @Temporal(TemporalType.TIMESTAMP)
083        private Date myUpdateTime;
084
085        @Column(name = "DEFINITION_ID", length = JobDefinition.ID_MAX_LENGTH, nullable = false)
086        private String myDefinitionId;
087
088        @Column(name = "DEFINITION_VER", nullable = false)
089        private int myDefinitionVersion;
090
091        @Column(name = "STAT", length = STATUS_MAX_LENGTH, nullable = false)
092        @Enumerated(EnumType.STRING)
093        @JdbcTypeCode(SqlTypes.VARCHAR)
094        private StatusEnum myStatus;
095
096        @Column(name = "JOB_CANCELLED", nullable = false)
097        private boolean myCancelled;
098
099        @Column(name = "FAST_TRACKING", nullable = true)
100        private Boolean myFastTracking;
101
102        // TODO: VC column added in 7.2.0 - Remove non-VC column later
103        @Column(name = "PARAMS_JSON", length = PARAMS_JSON_MAX_LENGTH, nullable = true)
104        private String myParamsJson;
105
106        @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
107        @Column(name = "PARAMS_JSON_LOB", nullable = true)
108        private String myParamsJsonLob;
109
110        @Column(name = "PARAMS_JSON_VC", nullable = true, length = Length.LONG32)
111        private String myParamsJsonVc;
112
113        @Column(name = "CMB_RECS_PROCESSED", nullable = true)
114        private Integer myCombinedRecordsProcessed;
115
116        @Column(name = "CMB_RECS_PER_SEC", nullable = true)
117        private Double myCombinedRecordsProcessedPerSecond;
118
119        @Column(name = "TOT_ELAPSED_MILLIS", nullable = true)
120        private Integer myTotalElapsedMillis;
121
122        @Column(name = "WORK_CHUNKS_PURGED", nullable = false)
123        private boolean myWorkChunksPurged;
124
125        @Column(name = "PROGRESS_PCT", nullable = false)
126        private double myProgress;
127
128        @Column(name = "ERROR_MSG", length = ERROR_MSG_MAX_LENGTH, nullable = true)
129        private String myErrorMessage;
130
131        @Column(name = "ERROR_COUNT", nullable = false)
132        private int myErrorCount;
133
134        @Column(name = "EST_REMAINING", length = TIME_REMAINING_LENGTH, nullable = true)
135        private String myEstimatedTimeRemaining;
136
137        @Column(name = "CUR_GATED_STEP_ID", length = ID_MAX_LENGTH, nullable = true)
138        private String myCurrentGatedStepId;
139
140        @Column(name = "WARNING_MSG", length = WARNING_MSG_MAX_LENGTH, nullable = true)
141        private String myWarningMessages;
142
143        @Column(name = "USER_NAME", length = INITIATING_USER_NAME_MAX_LENGTH, nullable = true)
144        private String myTriggeringUsername;
145
146        @Column(name = "CLIENT_ID", length = INITIATING_CLIENT_ID_MAX_LENGTH, nullable = true)
147        private String myTriggeringClientId;
148
149        @Column(name = "USER_DATA_JSON", length = Length.LONG32, nullable = true)
150        private String myUserDataJson;
151
152        /**
153         * Any output from the job can be held in this column
154         * Even serialized json
155         */
156        @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
157        @Basic(fetch = FetchType.LAZY)
158        @Column(name = "REPORT", nullable = true, length = Integer.MAX_VALUE - 1)
159        private String myReport;
160
161        @Column(name = "REPORT_VC", nullable = true, length = Length.LONG32)
162        private String myReportVc;
163
164        public String getCurrentGatedStepId() {
165                return myCurrentGatedStepId;
166        }
167
168        public void setCurrentGatedStepId(String theCurrentGatedStepId) {
169                myCurrentGatedStepId = theCurrentGatedStepId;
170        }
171
172        public boolean isCancelled() {
173                return myCancelled;
174        }
175
176        public void setCancelled(boolean theCancelled) {
177                myCancelled = theCancelled;
178        }
179
180        public int getErrorCount() {
181                return myErrorCount;
182        }
183
184        public void setErrorCount(int theErrorCount) {
185                myErrorCount = theErrorCount;
186        }
187
188        public Integer getTotalElapsedMillis() {
189                return myTotalElapsedMillis;
190        }
191
192        public void setTotalElapsedMillis(Integer theTotalElapsedMillis) {
193                myTotalElapsedMillis = theTotalElapsedMillis;
194        }
195
196        public Integer getCombinedRecordsProcessed() {
197                return myCombinedRecordsProcessed;
198        }
199
200        public void setCombinedRecordsProcessed(Integer theCombinedRecordsProcessed) {
201                myCombinedRecordsProcessed = theCombinedRecordsProcessed;
202        }
203
204        public Double getCombinedRecordsProcessedPerSecond() {
205                return myCombinedRecordsProcessedPerSecond;
206        }
207
208        public void setCombinedRecordsProcessedPerSecond(Double theCombinedRecordsProcessedPerSecond) {
209                myCombinedRecordsProcessedPerSecond = theCombinedRecordsProcessedPerSecond;
210        }
211
212        public Date getCreateTime() {
213                return myCreateTime;
214        }
215
216        public void setCreateTime(Date theCreateTime) {
217                myCreateTime = theCreateTime;
218        }
219
220        public Date getStartTime() {
221                return myStartTime;
222        }
223
224        public void setStartTime(Date theStartTime) {
225                myStartTime = theStartTime;
226        }
227
228        public Date getEndTime() {
229                return myEndTime;
230        }
231
232        public void setEndTime(Date theEndTime) {
233                myEndTime = theEndTime;
234        }
235
236        public void setUpdateTime(Date theTime) {
237                myUpdateTime = theTime;
238        }
239
240        public Date getUpdateTime() {
241                return myUpdateTime;
242        }
243
244        public String getId() {
245                return myId;
246        }
247
248        public void setId(String theId) {
249                myId = theId;
250        }
251
252        public String getDefinitionId() {
253                return myDefinitionId;
254        }
255
256        public void setDefinitionId(String theDefinitionId) {
257                myDefinitionId = theDefinitionId;
258        }
259
260        public int getDefinitionVersion() {
261                return myDefinitionVersion;
262        }
263
264        public void setDefinitionVersion(int theDefinitionVersion) {
265                myDefinitionVersion = theDefinitionVersion;
266        }
267
268        public StatusEnum getStatus() {
269                return myStatus;
270        }
271
272        public void setStatus(StatusEnum theStatus) {
273                myStatus = theStatus;
274        }
275
276        public String getParams() {
277                if (myParamsJsonVc != null) {
278                        return myParamsJsonVc;
279                }
280                if (myParamsJsonLob != null) {
281                        return myParamsJsonLob;
282                }
283                return myParamsJson;
284        }
285
286        public void setParams(String theParams) {
287                myParamsJsonVc = theParams;
288                myParamsJsonLob = null;
289                myParamsJson = null;
290        }
291
292        public boolean getWorkChunksPurged() {
293                return myWorkChunksPurged;
294        }
295
296        public void setWorkChunksPurged(boolean theWorkChunksPurged) {
297                myWorkChunksPurged = theWorkChunksPurged;
298        }
299
300        public double getProgress() {
301                return myProgress;
302        }
303
304        public void setProgress(double theProgress) {
305                myProgress = theProgress;
306        }
307
308        public String getErrorMessage() {
309                return myErrorMessage;
310        }
311
312        public void setErrorMessage(String theErrorMessage) {
313                myErrorMessage = left(theErrorMessage, ERROR_MSG_MAX_LENGTH);
314        }
315
316        public String getEstimatedTimeRemaining() {
317                return myEstimatedTimeRemaining;
318        }
319
320        public void setEstimatedTimeRemaining(String theEstimatedTimeRemaining) {
321                myEstimatedTimeRemaining = left(theEstimatedTimeRemaining, TIME_REMAINING_LENGTH);
322        }
323
324        public String getReport() {
325                return myReportVc != null ? myReportVc : myReport;
326        }
327
328        public void setReport(String theReport) {
329                myReportVc = theReport;
330                myReport = null;
331        }
332
333        public String getWarningMessages() {
334                return myWarningMessages;
335        }
336
337        public void setWarningMessages(String theWarningMessages) {
338                myWarningMessages = theWarningMessages;
339        }
340
341        public String getTriggeringUsername() {
342                return myTriggeringUsername;
343        }
344
345        public Batch2JobInstanceEntity setTriggeringUsername(String theTriggeringUsername) {
346                myTriggeringUsername = theTriggeringUsername;
347                return this;
348        }
349
350        public String getTriggeringClientId() {
351                return myTriggeringClientId;
352        }
353
354        public Batch2JobInstanceEntity setTriggeringClientId(String theTriggeringClientId) {
355                myTriggeringClientId = theTriggeringClientId;
356                return this;
357        }
358
359        @Override
360        public String toString() {
361                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
362                                .append("id", myId)
363                                .append("definitionId", myDefinitionId)
364                                .append("definitionVersion", myDefinitionVersion)
365                                .append("errorCount", myErrorCount)
366                                .append("createTime", myCreateTime)
367                                .append("startTime", myStartTime)
368                                .append("endTime", myEndTime)
369                                .append("updateTime", myUpdateTime)
370                                .append("status", myStatus)
371                                .append("cancelled", myCancelled)
372                                .append("combinedRecordsProcessed", myCombinedRecordsProcessed)
373                                .append("combinedRecordsProcessedPerSecond", myCombinedRecordsProcessedPerSecond)
374                                .append("totalElapsedMillis", myTotalElapsedMillis)
375                                .append("workChunksPurged", myWorkChunksPurged)
376                                .append("progress", myProgress)
377                                .append("errorMessage", myErrorMessage)
378                                .append("estimatedTimeRemaining", myEstimatedTimeRemaining)
379                                .append("report", getReport())
380                                .append("warningMessages", myWarningMessages)
381                                .append("initiatingUsername", myTriggeringUsername)
382                                .append("initiatingclientId", myTriggeringClientId)
383                                .toString();
384        }
385
386        /**
387         * @return true if every step of the job has produced exactly 1 chunk.
388         */
389        public boolean isFastTracking() {
390                if (myFastTracking == null) {
391                        myFastTracking = false;
392                }
393                return myFastTracking;
394        }
395
396        public void setFastTracking(boolean theFastTracking) {
397                myFastTracking = theFastTracking;
398        }
399
400        public String getUserDataJson() {
401                return myUserDataJson;
402        }
403
404        public void setUserDataJson(String theUserDataJson) {
405                myUserDataJson = theUserDataJson;
406        }
407}