View Javadoc
1   /*
2    * Copyright 2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.platform.camel.hl7.validation;
17  
18  import ca.uhn.hl7v2.DefaultHapiContext;
19  import ca.uhn.hl7v2.HL7Exception;
20  import ca.uhn.hl7v2.HapiContext;
21  import ca.uhn.hl7v2.Severity;
22  import ca.uhn.hl7v2.model.Message;
23  import ca.uhn.hl7v2.parser.GenericParser;
24  import ca.uhn.hl7v2.validation.MessageRule;
25  import ca.uhn.hl7v2.validation.ValidationContext;
26  import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
27  import org.apache.camel.Exchange;
28  import org.apache.camel.Processor;
29  import org.openehealth.ipf.commons.core.modules.api.ValidationException;
30  import org.openehealth.ipf.gazelle.validation.core.CachingGazelleProfileRule;
31  import org.openehealth.ipf.gazelle.validation.profile.ConformanceProfile;
32  import org.openehealth.ipf.gazelle.validation.profile.HL7v2Transactions;
33  import org.openehealth.ipf.gazelle.validation.profile.store.GazelleProfileStore;
34  import org.openehealth.ipf.modules.hl7.parser.DefaultEscaping;
35  import org.openehealth.ipf.platform.camel.core.adapter.ValidatorAdapter;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  import static java.util.Objects.requireNonNull;
41  
42  /**
43   * Factory for manually triggering a validation of a message depending on a profile or a defined
44   * IHETransaction. In general this should not be necessary when the HapiContext of the parsed
45   * message contains a ValidationContext that includes the corresponding
46   * {@link org.openehealth.ipf.gazelle.validation.core.GazelleProfileRule GazelleProfileRule}
47   * instance as one of its message rules. In that case it would be sufficient to use
48   * {@link org.openehealth.ipf.platform.camel.hl7.HL7v2#validatingProcessor()} or
49   * {@link org.apache.camel.component.hl7.HL7#messageConforms()}.
50   *
51   * @author Boris Stanojevic
52   * @author Christian Ohr
53   */
54  public final class ConformanceProfileValidators {
55  
56      private static final HapiContext FALLBACK_HAPI_CONTEXT;
57  
58      static {
59          FALLBACK_HAPI_CONTEXT = new DefaultHapiContext();
60          FALLBACK_HAPI_CONTEXT.setProfileStore(new GazelleProfileStore());
61          FALLBACK_HAPI_CONTEXT.setValidationContext((ValidationContext) ValidationContextFactory.noValidation());
62          FALLBACK_HAPI_CONTEXT.getParserConfiguration().setEscaping(DefaultEscaping.INSTANCE);
63      }
64  
65      private ConformanceProfileValidators() {
66      }
67  
68      /**
69       * Returns a validating Camel processor for a dedicated profile
70       *
71       * @param conformanceProfile HL7 conformance profile
72       * @return a validating Camel processor for a dedicated profile
73       */
74      public static Processor validatingProcessor(final ConformanceProfile conformanceProfile) {
75          return new Processor() {
76  
77              private CachingGazelleProfileRule rule = new CachingGazelleProfileRule(conformanceProfile);
78  
79              @Override
80              public void process(Exchange exchange) throws Exception {
81                  if (ValidatorAdapter.validationEnabled(exchange)) {
82                      doValidate(bodyMessage(exchange), rule);
83                  }
84              }
85          };
86      }
87  
88      /**
89       * Returns a validating Camel processor for a message in a IHE transaction. The actual profile
90       * to be used is guessed from the message's event type and version
91       *
92       * @param iheTransaction IHE transaction
93       * @return a validating Camel processor for a message in a IHE transaction
94       */
95      public static Processor validatingProcessor(final HL7v2Transactions iheTransaction) {
96          return new Processor() {
97  
98              private CachingGazelleProfileRule rule = new CachingGazelleProfileRule(iheTransaction);
99  
100             @Override
101             public void process(Exchange exchange) throws Exception {
102                 if (ValidatorAdapter.validationEnabled(exchange)) {
103                     doValidate(bodyMessage(exchange), rule);
104                 }
105             }
106         };
107     }
108 
109     private static void doValidate(Message message, final MessageRule validator) {
110         throwIPFValidationException(validator.apply(message));
111     }
112 
113     private static void throwIPFValidationException(ca.uhn.hl7v2.validation.ValidationException... exceptions) {
114         List<ca.uhn.hl7v2.validation.ValidationException> fatalExceptions = new ArrayList<>();
115         if (exceptions != null) {
116             for (ca.uhn.hl7v2.validation.ValidationException exception : exceptions) {
117                 if (exception.getSeverity().equals(Severity.ERROR)) {
118                     fatalExceptions.add(exception);
119                 }
120             }
121         }
122         if (!fatalExceptions.isEmpty()) {
123             throw new ValidationException("Message validation failed", fatalExceptions);
124         }
125     }
126 
127     /**
128      * Returns the HAPI Message from the message body. If the body is a string, it is parsed on the fly
129      * using
130      *
131      * @param exchange
132      * @return HAPI message
133      * @throws HL7Exception
134      */
135     private static Message bodyMessage(Exchange exchange) throws HL7Exception {
136         Object body = exchange.getIn().getBody();
137         Message message;
138 
139         if (body instanceof Message) {
140             message = (Message) body;
141         } else if (body instanceof String) {
142             HapiContext context = exchange.getIn().getHeader("CamelHL7Context", HapiContext.class);
143             context = context != null ? context : FALLBACK_HAPI_CONTEXT;
144             message = new GenericParser(context).parse((String) body);
145         } else {
146             // try type conversion
147             message = exchange.getIn().getBody(Message.class);
148         }
149         requireNonNull(message, "Exchange does not contain or can be converted to the required 'ca.uhn.hl7v2.model.Message' type");
150         return message;
151     }
152 
153 }