001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.validation;
018
019 import java.io.IOException;
020
021 import javax.jbi.JBIException;
022 import javax.jbi.messaging.Fault;
023 import javax.jbi.messaging.MessageExchange;
024 import javax.jbi.messaging.MessagingException;
025 import javax.jbi.messaging.NormalizedMessage;
026 import javax.xml.parsers.ParserConfigurationException;
027 import javax.xml.transform.Source;
028 import javax.xml.transform.TransformerException;
029 import javax.xml.transform.dom.DOMResult;
030 import javax.xml.transform.dom.DOMSource;
031 import javax.xml.transform.stream.StreamSource;
032 import javax.xml.validation.Schema;
033 import javax.xml.validation.SchemaFactory;
034 import javax.xml.validation.Validator;
035
036 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
037 import org.apache.servicemix.common.util.MessageUtil;
038 import org.apache.servicemix.jbi.exception.FaultException;
039 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
040 import org.apache.servicemix.jbi.jaxp.StringSource;
041 import org.apache.servicemix.validation.handler.CountingErrorHandlerFactory;
042 import org.apache.servicemix.validation.handler.MessageAwareErrorHandler;
043 import org.apache.servicemix.validation.handler.MessageAwareErrorHandlerFactory;
044 import org.springframework.core.io.Resource;
045
046 import org.xml.sax.SAXException;
047
048 /**
049 * @org.apache.xbean.XBean element="endpoint"
050 * @author lhein
051 */
052 public class ValidationEndpoint extends ProviderEndpoint implements
053 ValidationEndpointType {
054
055 public static final String FAULT_FLOW = "FAULT_FLOW";
056
057 public static final String FAULT_JBI = "FAULT_JBI";
058
059 public static final String TAG_RESULT_START = "<result>";
060
061 public static final String TAG_RESULT_END = "</result>";
062
063 public static final String TAG_WARNING_START = "<warning>";
064
065 public static final String TAG_WARNING_END = "</warning>";
066
067 public static final String TAG_ERROR_START = "<error>";
068
069 public static final String TAG_ERROR_END = "</error>";
070
071 public static final String TAG_FATAL_START = "<fatalError>";
072
073 public static final String TAG_FATAL_END = "</fatalError>";
074
075 private String handlingErrorMethod = "FAULT_JBI";
076
077 private Schema schema;
078
079 private String schemaLanguage = "http://www.w3.org/2001/XMLSchema";
080
081 private Source schemaSource;
082
083 private Resource schemaResource;
084
085 private MessageAwareErrorHandlerFactory errorHandlerFactory = new CountingErrorHandlerFactory();
086
087 private SourceTransformer sourceTransformer = new SourceTransformer();
088
089 /*
090 * (non-Javadoc)
091 *
092 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
093 */
094 @Override
095 public void start() throws Exception {
096 super.start();
097
098 try {
099 if (schema == null) {
100 SchemaFactory factory = SchemaFactory
101 .newInstance(schemaLanguage);
102
103 if (schemaSource == null) {
104 if (schemaResource == null) {
105 throw new JBIException(
106 "You must specify a schema, schemaSource or schemaResource property");
107 }
108 if (schemaResource.getURL() == null) {
109 schemaSource = new StreamSource(schemaResource
110 .getInputStream());
111 } else {
112 schemaSource = new StreamSource(schemaResource
113 .getInputStream(), schemaResource.getURL()
114 .toExternalForm());
115 }
116 }
117 schema = factory.newSchema(schemaSource);
118 }
119 } catch (IOException e) {
120 throw new JBIException("Failed to load schema: " + e, e);
121 } catch (SAXException e) {
122 throw new JBIException("Failed to load schema: " + e, e);
123 }
124 }
125
126 /*
127 * (non-Javadoc)
128 *
129 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
130 */
131 @Override
132 public void stop() throws Exception {
133 super.stop();
134 }
135
136 /*
137 * (non-Javadoc)
138 *
139 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#processInOnly(javax.jbi.messaging.MessageExchange,
140 * javax.jbi.messaging.NormalizedMessage)
141 */
142 @Override
143 protected void processInOnly(MessageExchange exchange, NormalizedMessage in)
144 throws Exception {
145 NormalizedMessage out = exchange.createMessage();
146 Fault fault = exchange.createFault();
147 this.startValidation(exchange, in, out, fault);
148
149 if (fault.getContent() != null) {
150 throw new RuntimeException(sourceTransformer.contentToString(fault));
151 }
152 }
153
154 /*
155 * (non-Javadoc)
156 *
157 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#processInOut(javax.jbi.messaging.MessageExchange,
158 * javax.jbi.messaging.NormalizedMessage,
159 * javax.jbi.messaging.NormalizedMessage)
160 */
161 @Override
162 protected void processInOut(MessageExchange exchange, NormalizedMessage in,
163 NormalizedMessage out) throws Exception {
164 Fault fault = exchange.createFault();
165 this.startValidation(exchange, in, out, fault);
166 if (fault.getContent() != null) {
167 exchange.setFault(fault);
168 }
169 }
170
171 /**
172 * does the validation
173 *
174 * @param exchange
175 * @param in
176 * @param out
177 * @param fault
178 * @throws Exception
179 */
180 public void startValidation(MessageExchange exchange, NormalizedMessage in,
181 NormalizedMessage out, Fault fault) throws Exception {
182 Validator validator = schema.newValidator();
183
184 // create a new errorHandler and set it on the validator
185 MessageAwareErrorHandler errorHandler = errorHandlerFactory
186 .createMessageAwareErrorHandler();
187 validator.setErrorHandler(errorHandler);
188 DOMResult result = new DOMResult();
189
190 fault.setContent(null);
191
192 try {
193 // Only DOMSource and SAXSource are allowed for validating
194 // See
195 // http://java.sun.com/j2se/1.5.0/docs/api/javax/xml/validation/
196 // Validator.html#validate(javax.xml.transform.Source,%20javax.xml.transform.Result)
197 // As we expect a DOMResult as output, we must ensure that the input
198 // is a DOMSource
199 DOMSource src = sourceTransformer.toDOMSource(in.getContent());
200
201 // call the validation method
202 doValidation(validator, src, result);
203
204 // check if there were errors while validating
205 if (errorHandler.hasErrors()) {
206 /*
207 * check if this error handler supports the capturing of error
208 * messages.
209 */
210 if (errorHandler.capturesMessages()) {
211 /*
212 * In descending order of preference select a format to use.
213 * If neither DOMSource, StringSource or String are
214 * supported throw a messaging exception.
215 */
216 if (errorHandler.supportsMessageFormat(DOMSource.class)) {
217 fault.setContent((DOMSource) errorHandler
218 .getMessagesAs(DOMSource.class));
219 } else if (errorHandler
220 .supportsMessageFormat(StringSource.class)) {
221 fault.setContent(sourceTransformer
222 .toDOMSource((StringSource) errorHandler
223 .getMessagesAs(StringSource.class)));
224 } else if (errorHandler.supportsMessageFormat(String.class)) {
225 fault.setContent(sourceTransformer
226 .toDOMSource(new StringSource(
227 (String) errorHandler
228 .getMessagesAs(String.class))));
229 } else {
230 throw new MessagingException(
231 "MessageAwareErrorHandler implementation "
232 + errorHandler.getClass().getName()
233 + " does not support a compatible error message format.");
234 }
235 } else {
236 /*
237 * we can't do much here if the ErrorHandler implementation
238 * does not support capturing messages
239 */
240 StringBuffer resultString = new StringBuffer();
241 resultString.append(TAG_RESULT_START);
242 resultString.append('\n');
243 resultString.append(TAG_WARNING_START);
244 resultString.append(errorHandler.getWarningCount());
245 resultString.append(TAG_WARNING_END);
246 resultString.append('\n');
247 resultString.append(TAG_ERROR_START);
248 resultString.append(errorHandler.getErrorCount());
249 resultString.append(TAG_ERROR_END);
250 resultString.append('\n');
251 resultString.append(TAG_FATAL_START);
252 resultString.append(errorHandler.getFatalErrorCount());
253 resultString.append(TAG_FATAL_END);
254 resultString.append('\n');
255 resultString.append(TAG_RESULT_END);
256 resultString.append('\n');
257 fault.setContent(new StringSource(resultString.toString()));
258 }
259
260 if (!handlingErrorMethod.equalsIgnoreCase(FAULT_FLOW)) {
261 // HANDLE AS JBI FAULT
262 throw new FaultException("Failed to validate against schema: " + schema, exchange, fault);
263 } else {
264 MessageUtil.transfer(fault, out);
265 }
266 } else {
267 // Retrieve the ouput of the validation
268 // as it may have been changed by the validator
269 out.setContent(new DOMSource(result.getNode(), result
270 .getSystemId()));
271 }
272 } catch (SAXException e) {
273 throw new MessagingException(e);
274 } catch (IOException e) {
275 throw new MessagingException(e);
276 } catch (ParserConfigurationException e) {
277 throw new MessagingException(e);
278 } catch (TransformerException e) {
279 throw new MessagingException(e);
280 }
281 }
282
283 /**
284 * does the validation
285 *
286 * @param validator
287 * @param src
288 * @param result
289 * @throws SAXException
290 * @throws IOException
291 */
292 protected void doValidation(Validator validator, DOMSource src,
293 DOMResult result) throws SAXException, IOException {
294 validator.validate(src, result);
295 }
296
297 /*
298 * (non-Javadoc)
299 *
300 * @see org.apache.servicemix.common.endpoints.SimpleEndpoint#fail(javax.jbi.messaging.MessageExchange,
301 * java.lang.Exception)
302 */
303 protected void fail(MessageExchange messageExchange, Exception e)
304 throws MessagingException {
305 super.fail(messageExchange, e);
306 }
307
308 /*
309 * (non-Javadoc)
310 *
311 * @see org.apache.servicemix.common.endpoints.SimpleEndpoint#send(javax.jbi.messaging.MessageExchange)
312 */
313 protected void send(MessageExchange messageExchange)
314 throws MessagingException {
315 super.send(messageExchange);
316 }
317
318 /*
319 * (non-Javadoc)
320 *
321 * @see org.apache.servicemix.common.endpoints.SimpleEndpoint#sendSync(javax.jbi.messaging.MessageExchange)
322 */
323 protected void sendSync(MessageExchange messageExchange)
324 throws MessagingException {
325 super.sendSync(messageExchange);
326 }
327
328 /*
329 * (non-Javadoc)
330 *
331 * @see org.apache.servicemix.common.endpoints.SimpleEndpoint#done(javax.jbi.messaging.MessageExchange)
332 */
333 protected void done(MessageExchange messageExchange)
334 throws MessagingException {
335 super.done(messageExchange);
336 }
337
338 public String getHandlingErrorMethod() {
339 return handlingErrorMethod;
340 }
341
342 public void setHandlingErrorMethod(String handlingErrorMethod) {
343 this.handlingErrorMethod = handlingErrorMethod;
344 }
345
346 public Schema getSchema() {
347 return schema;
348 }
349
350 public void setSchema(Schema schema) {
351 this.schema = schema;
352 }
353
354 public String getSchemaLanguage() {
355 return schemaLanguage;
356 }
357
358 public void setSchemaLanguage(String schemaLanguage) {
359 this.schemaLanguage = schemaLanguage;
360 }
361
362 public Source getSchemaSource() {
363 return schemaSource;
364 }
365
366 public void setSchemaSource(Source schemaSource) {
367 this.schemaSource = schemaSource;
368 }
369
370 public Resource getSchemaResource() {
371 return schemaResource;
372 }
373
374 public void setSchemaResource(Resource schemaResource) {
375 this.schemaResource = schemaResource;
376 }
377
378 public MessageAwareErrorHandlerFactory getErrorHandlerFactory() {
379 return errorHandlerFactory;
380 }
381
382 public void setErrorHandlerFactory(
383 MessageAwareErrorHandlerFactory errorHandlerFactory) {
384 this.errorHandlerFactory = errorHandlerFactory;
385 }
386 }