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        /**
150         * Any output from the job can be held in this column
151         * Even serialized json
152         */
153        @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
154        @Basic(fetch = FetchType.LAZY)
155        @Column(name = "REPORT", nullable = true, length = Integer.MAX_VALUE - 1)
156        private String myReport;
157
158        @Column(name = "REPORT_VC", nullable = true, length = Length.LONG32)
159        private String myReportVc;
160
161        public String getCurrentGatedStepId() {
162                return myCurrentGatedStepId;
163        }
164
165        public void setCurrentGatedStepId(String theCurrentGatedStepId) {
166                myCurrentGatedStepId = theCurrentGatedStepId;
167        }
168
169        public boolean isCancelled() {
170                return myCancelled;
171        }
172
173        public void setCancelled(boolean theCancelled) {
174                myCancelled = theCancelled;
175        }
176
177        public int getErrorCount() {
178                return myErrorCount;
179        }
180
181        public void setErrorCount(int theErrorCount) {
182                myErrorCount = theErrorCount;
183        }
184
185        public Integer getTotalElapsedMillis() {
186                return myTotalElapsedMillis;
187        }
188
189        public void setTotalElapsedMillis(Integer theTotalElapsedMillis) {
190                myTotalElapsedMillis = theTotalElapsedMillis;
191        }
192
193        public Integer getCombinedRecordsProcessed() {
194                return myCombinedRecordsProcessed;
195        }
196
197        public void setCombinedRecordsProcessed(Integer theCombinedRecordsProcessed) {
198                myCombinedRecordsProcessed = theCombinedRecordsProcessed;
199        }
200
201        public Double getCombinedRecordsProcessedPerSecond() {
202                return myCombinedRecordsProcessedPerSecond;
203        }
204
205        public void setCombinedRecordsProcessedPerSecond(Double theCombinedRecordsProcessedPerSecond) {
206                myCombinedRecordsProcessedPerSecond = theCombinedRecordsProcessedPerSecond;
207        }
208
209        public Date getCreateTime() {
210                return myCreateTime;
211        }
212
213        public void setCreateTime(Date theCreateTime) {
214                myCreateTime = theCreateTime;
215        }
216
217        public Date getStartTime() {
218                return myStartTime;
219        }
220
221        public void setStartTime(Date theStartTime) {
222                myStartTime = theStartTime;
223        }
224
225        public Date getEndTime() {
226                return myEndTime;
227        }
228
229        public void setEndTime(Date theEndTime) {
230                myEndTime = theEndTime;
231        }
232
233        public void setUpdateTime(Date theTime) {
234                myUpdateTime = theTime;
235        }
236
237        public Date getUpdateTime() {
238                return myUpdateTime;
239        }
240
241        public String getId() {
242                return myId;
243        }
244
245        public void setId(String theId) {
246                myId = theId;
247        }
248
249        public String getDefinitionId() {
250                return myDefinitionId;
251        }
252
253        public void setDefinitionId(String theDefinitionId) {
254                myDefinitionId = theDefinitionId;
255        }
256
257        public int getDefinitionVersion() {
258                return myDefinitionVersion;
259        }
260
261        public void setDefinitionVersion(int theDefinitionVersion) {
262                myDefinitionVersion = theDefinitionVersion;
263        }
264
265        public StatusEnum getStatus() {
266                return myStatus;
267        }
268
269        public void setStatus(StatusEnum theStatus) {
270                myStatus = theStatus;
271        }
272
273        public String getParams() {
274                if (myParamsJsonVc != null) {
275                        return myParamsJsonVc;
276                }
277                if (myParamsJsonLob != null) {
278                        return myParamsJsonLob;
279                }
280                return myParamsJson;
281        }
282
283        public void setParams(String theParams) {
284                myParamsJsonVc = theParams;
285                myParamsJsonLob = null;
286                myParamsJson = null;
287        }
288
289        public boolean getWorkChunksPurged() {
290                return myWorkChunksPurged;
291        }
292
293        public void setWorkChunksPurged(boolean theWorkChunksPurged) {
294                myWorkChunksPurged = theWorkChunksPurged;
295        }
296
297        public double getProgress() {
298                return myProgress;
299        }
300
301        public void setProgress(double theProgress) {
302                myProgress = theProgress;
303        }
304
305        public String getErrorMessage() {
306                return myErrorMessage;
307        }
308
309        public void setErrorMessage(String theErrorMessage) {
310                myErrorMessage = left(theErrorMessage, ERROR_MSG_MAX_LENGTH);
311        }
312
313        public String getEstimatedTimeRemaining() {
314                return myEstimatedTimeRemaining;
315        }
316
317        public void setEstimatedTimeRemaining(String theEstimatedTimeRemaining) {
318                myEstimatedTimeRemaining = left(theEstimatedTimeRemaining, TIME_REMAINING_LENGTH);
319        }
320
321        public String getReport() {
322                return myReportVc != null ? myReportVc : myReport;
323        }
324
325        public void setReport(String theReport) {
326                myReportVc = theReport;
327                myReport = null;
328        }
329
330        public String getWarningMessages() {
331                return myWarningMessages;
332        }
333
334        public void setWarningMessages(String theWarningMessages) {
335                myWarningMessages = theWarningMessages;
336        }
337
338        public String getTriggeringUsername() {
339                return myTriggeringUsername;
340        }
341
342        public Batch2JobInstanceEntity setTriggeringUsername(String theTriggeringUsername) {
343                myTriggeringUsername = theTriggeringUsername;
344                return this;
345        }
346
347        public String getTriggeringClientId() {
348                return myTriggeringClientId;
349        }
350
351        public Batch2JobInstanceEntity setTriggeringClientId(String theTriggeringClientId) {
352                myTriggeringClientId = theTriggeringClientId;
353                return this;
354        }
355
356        @Override
357        public String toString() {
358                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
359                                .append("id", myId)
360                                .append("definitionId", myDefinitionId)
361                                .append("definitionVersion", myDefinitionVersion)
362                                .append("errorCount", myErrorCount)
363                                .append("createTime", myCreateTime)
364                                .append("startTime", myStartTime)
365                                .append("endTime", myEndTime)
366                                .append("updateTime", myUpdateTime)
367                                .append("status", myStatus)
368                                .append("cancelled", myCancelled)
369                                .append("combinedRecordsProcessed", myCombinedRecordsProcessed)
370                                .append("combinedRecordsProcessedPerSecond", myCombinedRecordsProcessedPerSecond)
371                                .append("totalElapsedMillis", myTotalElapsedMillis)
372                                .append("workChunksPurged", myWorkChunksPurged)
373                                .append("progress", myProgress)
374                                .append("errorMessage", myErrorMessage)
375                                .append("estimatedTimeRemaining", myEstimatedTimeRemaining)
376                                .append("report", getReport())
377                                .append("warningMessages", myWarningMessages)
378                                .append("initiatingUsername", myTriggeringUsername)
379                                .append("initiatingclientId", myTriggeringClientId)
380                                .toString();
381        }
382
383        /**
384         * @return true if every step of the job has produced exactly 1 chunk.
385         */
386        public boolean isFastTracking() {
387                if (myFastTracking == null) {
388                        myFastTracking = false;
389                }
390                return myFastTracking;
391        }
392
393        public void setFastTracking(boolean theFastTracking) {
394                myFastTracking = theFastTracking;
395        }
396}