001package org.hl7.fhir.r5.terminologies.utilities;
002
003import java.util.List;
004
005import lombok.extern.slf4j.Slf4j;
006import org.hl7.fhir.exceptions.FHIRException;
007import org.hl7.fhir.exceptions.TerminologyServiceException;
008import org.hl7.fhir.r5.context.IWorkerContext;
009import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
010import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
011import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
012import org.hl7.fhir.utilities.Utilities;
013import org.hl7.fhir.utilities.i18n.I18nConstants;
014import org.hl7.fhir.utilities.validation.ValidationOptions;
015
016import java.util.ArrayList;
017
018@MarkedToMoveToAdjunctPackage
019@Slf4j
020public class TerminologyOperationContext {
021
022  public static class TerminologyServiceProtectionException extends FHIRException {
023
024    private TerminologyServiceErrorClass error;
025    private IssueType type;
026    private String diagnostics;
027
028    public TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) {
029      super(message);
030      this.error = error;
031      this.type = type;
032    }
033    public TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type, String diagnostics) {
034      super(message);
035      this.error = error;
036      this.type = type;
037      this.diagnostics = diagnostics;
038    }
039
040    public TerminologyServiceErrorClass getError() {
041      return error;
042    }
043
044    public IssueType getType() {
045      return type;
046    }
047    public String getDiagnostics() {
048      return diagnostics;
049    }
050
051  }
052
053  public static boolean debugging = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0;
054  private static final int EXPANSION_DEAD_TIME_SECS = 60;
055  private long deadTime;
056  private int nestCount = 0;
057  private long startTime;
058  private List<String> contexts = new ArrayList<>();
059  private IWorkerContext worker;
060  private boolean original;
061  private ValidationOptions options;
062  private String name;
063  private List<String> notes = new ArrayList<>();
064  
065  public TerminologyOperationContext(IWorkerContext worker, ValidationOptions options, String name) {
066    super();
067    this.worker = worker;
068    this.original = true;
069    this.options = options;
070    this.name = name;
071    this.startTime = System.currentTimeMillis();
072    
073    if (EXPANSION_DEAD_TIME_SECS == 0 || debugging) {
074      deadTime = 0;
075    } else {
076      deadTime = System.currentTimeMillis() + (EXPANSION_DEAD_TIME_SECS * 1000);      
077    }
078  }
079  
080  private TerminologyOperationContext(ValidationOptions options, String name) {
081    super();
082    this.options = options;
083    this.name = name;
084    this.startTime = System.currentTimeMillis();
085  }
086
087  public TerminologyOperationContext copy() {
088    TerminologyOperationContext ret = new TerminologyOperationContext(this.options, name);
089    ret.worker = worker;
090    ret.contexts.addAll(contexts);
091    ret.deadTime = deadTime;
092    ret.notes = notes;
093    ret.startTime = startTime;
094    ret.nestCount = nestCount + 1;
095    return ret;
096  }
097  
098  public void deadCheck(String note) {
099    note(note);
100    if (deadTime != 0 &&  System.currentTimeMillis() > deadTime) {
101     log.error("Operation took too long - longer than "+(deadTime - startTime)+"ms");
102      for (String s : notes) {
103        log.error(s);
104      }
105      throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS, name+" (local)"), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY);
106    }
107  }
108  
109  public void seeContext(String context) {
110    if (contexts.contains(context)) {
111      throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_CIRCULAR_REFERENCE, context, contexts.toString()), TerminologyServiceErrorClass.PROCESSING, IssueType.PROCESSING);
112    }
113    contexts.add(context);
114  }
115
116  public boolean isOriginal() {
117    return original;
118  }
119
120  public ValidationOptions getOptions() {
121    return options;
122  }
123
124  public void note(String s) {
125    s = Utilities.padLeft("", ' ', nestCount)+" "+(System.currentTimeMillis() - startTime)+" "+s;
126    notes.add(s);
127  }
128}