001: /*******************************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *******************************************************************************/package org.ofbiz.base.util;
019:
020: import java.io.*;
021: import java.net.URL;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Set;
025:
026: import javax.xml.parsers.DocumentBuilder;
027: import javax.xml.parsers.DocumentBuilderFactory;
028: import javax.xml.parsers.ParserConfigurationException;
029:
030: import javolution.util.FastList;
031:
032: import org.apache.xml.serialize.OutputFormat;
033: import org.apache.xml.serialize.XMLSerializer;
034: import org.w3c.dom.Document;
035: import org.w3c.dom.DocumentFragment;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.Node;
038: import org.xml.sax.EntityResolver;
039: import org.xml.sax.ErrorHandler;
040: import org.xml.sax.InputSource;
041: import org.xml.sax.SAXException;
042: import org.xml.sax.SAXParseException;
043: import org.xml.sax.helpers.DefaultHandler;
044:
045: /**
046: * Utilities methods to simplify dealing with JAXP & DOM XML parsing
047: *
048: */
049: public class UtilXml {
050:
051: public static final String module = UtilXml.class.getName();
052:
053: public static String writeXmlDocument(Document document)
054: throws java.io.IOException {
055: if (document == null) {
056: Debug
057: .logWarning(
058: "[UtilXml.writeXmlDocument] Document was null, doing nothing",
059: module);
060: return null;
061: }
062: return writeXmlDocument(document.getDocumentElement());
063: }
064:
065: public static String writeXmlDocument(Element element)
066: throws java.io.IOException {
067: if (element == null) {
068: Debug
069: .logWarning(
070: "[UtilXml.writeXmlDocument] Element was null, doing nothing",
071: module);
072: return null;
073: }
074:
075: ByteArrayOutputStream bos = new ByteArrayOutputStream();
076: writeXmlDocument(bos, element);
077: String outString = bos.toString("UTF-8");
078:
079: if (bos != null)
080: bos.close();
081: return outString;
082: }
083:
084: public static String writeXmlDocument(DocumentFragment fragment)
085: throws java.io.IOException {
086: if (fragment == null) {
087: Debug
088: .logWarning(
089: "[UtilXml.writeXmlDocument] DocumentFragment was null, doing nothing",
090: module);
091: return null;
092: }
093:
094: ByteArrayOutputStream bos = new ByteArrayOutputStream();
095: List elementList = UtilXml.childElementList(fragment);
096: Iterator elementIter = elementList.iterator();
097: while (elementIter.hasNext()) {
098: Element element = (Element) elementIter.next();
099: writeXmlDocument(bos, element);
100: }
101: String outString = bos.toString("UTF-8");
102:
103: if (bos != null)
104: bos.close();
105: return outString;
106: }
107:
108: public static void writeXmlDocument(String filename,
109: Document document) throws java.io.FileNotFoundException,
110: java.io.IOException {
111: if (document == null) {
112: Debug
113: .logWarning(
114: "[UtilXml.writeXmlDocument] Document was null, doing nothing",
115: module);
116: return;
117: }
118: writeXmlDocument(filename, document.getDocumentElement());
119: }
120:
121: public static void writeXmlDocument(String filename, Element element)
122: throws java.io.FileNotFoundException, java.io.IOException {
123: if (element == null) {
124: Debug
125: .logWarning(
126: "[UtilXml.writeXmlDocument] Element was null, doing nothing",
127: module);
128: return;
129: }
130: if (filename == null) {
131: Debug
132: .logWarning(
133: "[UtilXml.writeXmlDocument] Filename was null, doing nothing",
134: module);
135: return;
136: }
137:
138: File outFile = new File(filename);
139: FileOutputStream fos = null;
140: fos = new FileOutputStream(outFile);
141:
142: try {
143: writeXmlDocument(fos, element);
144: } finally {
145: if (fos != null)
146: fos.close();
147: }
148: }
149:
150: public static void writeXmlDocument(OutputStream os,
151: Document document) throws java.io.IOException {
152: if (document == null) {
153: Debug
154: .logWarning(
155: "[UtilXml.writeXmlDocument] Document was null, doing nothing",
156: module);
157: return;
158: }
159: writeXmlDocument(os, document.getDocumentElement());
160: }
161:
162: public static void writeXmlDocument(OutputStream os, Element element)
163: throws java.io.IOException {
164: OutputFormat format = new OutputFormat(element
165: .getOwnerDocument());
166: writeXmlDocument(os, element, format);
167: }
168:
169: public static void writeXmlDocument(OutputStream os,
170: Element element, OutputFormat format)
171: throws java.io.IOException {
172: if (element == null) {
173: Debug
174: .logWarning(
175: "[UtilXml.writeXmlDocument] Element was null, doing nothing",
176: module);
177: return;
178: }
179: if (os == null) {
180: Debug
181: .logWarning(
182: "[UtilXml.writeXmlDocument] OutputStream was null, doing nothing",
183: module);
184: return;
185: }
186:
187: XMLSerializer serializer = new XMLSerializer(os, format);
188: serializer.asDOMSerializer();
189: serializer.serialize(element);
190: }
191:
192: public static Document readXmlDocument(String content)
193: throws SAXException, ParserConfigurationException,
194: java.io.IOException {
195: return readXmlDocument(content, true);
196: }
197:
198: public static Document readXmlDocument(String content,
199: boolean validate) throws SAXException,
200: ParserConfigurationException, java.io.IOException {
201: if (content == null) {
202: Debug
203: .logWarning(
204: "[UtilXml.readXmlDocument] content was null, doing nothing",
205: module);
206: return null;
207: }
208: ByteArrayInputStream bis = new ByteArrayInputStream(content
209: .getBytes("UTF-8"));
210: return readXmlDocument(bis, validate, "Internal Content");
211: }
212:
213: public static Document readXmlDocument(URL url)
214: throws SAXException, ParserConfigurationException,
215: java.io.IOException {
216: return readXmlDocument(url, true);
217: }
218:
219: public static Document readXmlDocument(URL url, boolean validate)
220: throws SAXException, ParserConfigurationException,
221: java.io.IOException {
222: if (url == null) {
223: Debug
224: .logWarning(
225: "[UtilXml.readXmlDocument] URL was null, doing nothing",
226: module);
227: return null;
228: }
229: return readXmlDocument(url.openStream(), validate, url
230: .toString());
231: }
232:
233: /**
234: * @deprecated
235: */
236: public static Document readXmlDocument(InputStream is)
237: throws SAXException, ParserConfigurationException,
238: java.io.IOException {
239: return readXmlDocument(is, true, null);
240: }
241:
242: public static Document readXmlDocument(InputStream is,
243: String docDescription) throws SAXException,
244: ParserConfigurationException, java.io.IOException {
245: return readXmlDocument(is, true, docDescription);
246: }
247:
248: public static Document readXmlDocument(InputStream is,
249: boolean validate, String docDescription)
250: throws SAXException, ParserConfigurationException,
251: java.io.IOException {
252: if (is == null) {
253: Debug
254: .logWarning(
255: "[UtilXml.readXmlDocument] InputStream was null, doing nothing",
256: module);
257: return null;
258: }
259:
260: long startTime = System.currentTimeMillis();
261:
262: // DON'T do this: seems to be causing problems with Catalina/Tomcat, maybe it is expecting a different parser?
263: //System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
264:
265: Document document = null;
266:
267: /* Xerces DOMParser direct interaction; the other seems to be working better than this, so we'll stay with the standard JAXP stuff
268: DOMParser parser = new DOMParser();
269: try {
270: parser.setFeature("http://xml.org/sax/features/validation", true);
271: parser.setFeature("http://apache.org/xml/features/validation/schema", true);
272: } catch (SAXException e) {
273: Debug.logWarning("Could not set parser feature: " + e.toString(), module);
274: }
275: parser.parse(new InputSource(is));
276: document = parser.getDocument();
277: */
278:
279: /* Standard JAXP (mostly), but doesn't seem to be doing XML Schema validation, so making sure that is on... */
280: DocumentBuilderFactory factory = new org.apache.xerces.jaxp.DocumentBuilderFactoryImpl();
281: factory.setValidating(validate);
282: factory.setNamespaceAware(true);
283:
284: factory.setAttribute("http://xml.org/sax/features/validation",
285: new Boolean(validate));
286: factory.setAttribute(
287: "http://apache.org/xml/features/validation/schema",
288: new Boolean(validate));
289:
290: // with a SchemaUrl, a URL object
291: //factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
292: //factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", SchemaUrl);
293: DocumentBuilder builder = factory.newDocumentBuilder();
294: if (validate) {
295: LocalResolver lr = new LocalResolver(new DefaultHandler());
296: ErrorHandler eh = new LocalErrorHandler(docDescription, lr);
297:
298: builder.setEntityResolver(lr);
299: builder.setErrorHandler(eh);
300: }
301: document = builder.parse(is);
302:
303: double totalSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
304: if (Debug.timingOn())
305: Debug.logTiming("XML Read " + totalSeconds + "s: "
306: + docDescription, module);
307: return document;
308: }
309:
310: public static Document makeEmptyXmlDocument() {
311: return makeEmptyXmlDocument(null);
312: }
313:
314: public static Document makeEmptyXmlDocument(String rootElementName) {
315: Document document = null;
316: DocumentBuilderFactory factory = DocumentBuilderFactory
317: .newInstance();
318:
319: factory.setValidating(true);
320: // factory.setNamespaceAware(true);
321: try {
322: DocumentBuilder builder = factory.newDocumentBuilder();
323:
324: document = builder.newDocument();
325: } catch (Exception e) {
326: Debug.logError(e, module);
327: }
328:
329: if (document == null)
330: return null;
331:
332: if (rootElementName != null) {
333: Element rootElement = document
334: .createElement(rootElementName);
335: document.appendChild(rootElement);
336: }
337:
338: return document;
339: }
340:
341: /** Creates a child element with the given name and appends it to the element child node list. */
342: public static Element addChildElement(Element element,
343: String childElementName, Document document) {
344: Element newElement = document.createElement(childElementName);
345:
346: element.appendChild(newElement);
347: return newElement;
348: }
349:
350: /** Creates a child element with the given name and appends it to the element child node list.
351: * Also creates a Text node with the given value and appends it to the new elements child node list.
352: */
353: public static Element addChildElementValue(Element element,
354: String childElementName, String childElementValue,
355: Document document) {
356: Element newElement = addChildElement(element, childElementName,
357: document);
358:
359: newElement.appendChild(document
360: .createTextNode(childElementValue));
361: return newElement;
362: }
363:
364: /** Creates a child element with the given name and appends it to the element child node list.
365: * Also creates a CDATASection node with the given value and appends it to the new elements child node list.
366: */
367: public static Element addChildElementCDATAValue(Element element,
368: String childElementName, String childElementValue,
369: Document document) {
370: Element newElement = addChildElement(element, childElementName,
371: document);
372:
373: newElement.appendChild(document
374: .createCDATASection(childElementValue));
375: return newElement;
376: }
377:
378: /** Return a List of Element objects that are children of the given element */
379: public static List childElementList(Element element) {
380: if (element == null)
381: return null;
382:
383: List elements = FastList.newInstance();
384: Node node = element.getFirstChild();
385:
386: if (node != null) {
387: do {
388: if (node.getNodeType() == Node.ELEMENT_NODE) {
389: Element childElement = (Element) node;
390: elements.add(childElement);
391: }
392: } while ((node = node.getNextSibling()) != null);
393: }
394: return elements;
395: }
396:
397: /** Return a List of Element objects that have the given name and are
398: * immediate children of the given element; if name is null, all child
399: * elements will be included. */
400: public static List childElementList(Element element,
401: String childElementName) {
402: if (element == null)
403: return null;
404:
405: List elements = FastList.newInstance();
406: Node node = element.getFirstChild();
407:
408: if (node != null) {
409: do {
410: if (node.getNodeType() == Node.ELEMENT_NODE
411: && (childElementName == null || childElementName
412: .equals(node.getNodeName()))) {
413: Element childElement = (Element) node;
414:
415: elements.add(childElement);
416: }
417: } while ((node = node.getNextSibling()) != null);
418: }
419: return elements;
420: }
421:
422: /** Return a List of Element objects that have the given name and are
423: * immediate children of the given element; if name is null, all child
424: * elements will be included. */
425: public static List childElementList(Element element,
426: Set childElementNames) {
427: if (element == null)
428: return null;
429:
430: List elements = FastList.newInstance();
431: if (childElementNames == null)
432: return elements;
433: Node node = element.getFirstChild();
434:
435: if (node != null) {
436: do {
437: if (node.getNodeType() == Node.ELEMENT_NODE
438: && childElementNames.contains(node
439: .getNodeName())) {
440: Element childElement = (Element) node;
441: elements.add(childElement);
442: }
443: } while ((node = node.getNextSibling()) != null);
444: }
445: return elements;
446: }
447:
448: /** Return a List of Element objects that are children of the given DocumentFragment */
449: public static List childElementList(DocumentFragment fragment) {
450: if (fragment == null)
451: return null;
452: List elements = FastList.newInstance();
453: Node node = fragment.getFirstChild();
454: if (node != null) {
455: do {
456: if (node.getNodeType() == Node.ELEMENT_NODE) {
457: Element childElement = (Element) node;
458: elements.add(childElement);
459: }
460: } while ((node = node.getNextSibling()) != null);
461: }
462: return elements;
463: }
464:
465: /** Return the first child Element
466: * returns the first element. */
467: public static Element firstChildElement(Element element,
468: Set childElementNames) {
469: if (element == null)
470: return null;
471: // get the first element with the given name
472: Node node = element.getFirstChild();
473:
474: if (node != null) {
475: do {
476: if (node.getNodeType() == Node.ELEMENT_NODE
477: && childElementNames.contains(node
478: .getNodeName())) {
479: Element childElement = (Element) node;
480:
481: return childElement;
482: }
483: } while ((node = node.getNextSibling()) != null);
484: }
485: return null;
486: }
487:
488: /** Return the first child Element
489: * returns the first element. */
490: public static Element firstChildElement(Element element) {
491: if (element == null)
492: return null;
493: // get the first element with the given name
494: Node node = element.getFirstChild();
495:
496: if (node != null) {
497: do {
498: if (node.getNodeType() == Node.ELEMENT_NODE) {
499: Element childElement = (Element) node;
500:
501: return childElement;
502: }
503: } while ((node = node.getNextSibling()) != null);
504: }
505: return null;
506: }
507:
508: /** Return the first child Element with the given name; if name is null
509: * returns the first element. */
510: public static Element firstChildElement(Element element,
511: String childElementName) {
512: if (element == null)
513: return null;
514: // get the first element with the given name
515: Node node = element.getFirstChild();
516:
517: if (node != null) {
518: do {
519: if (node.getNodeType() == Node.ELEMENT_NODE
520: && (childElementName == null || childElementName
521: .equals(node.getNodeName()))) {
522: Element childElement = (Element) node;
523:
524: return childElement;
525: }
526: } while ((node = node.getNextSibling()) != null);
527: }
528: return null;
529: }
530:
531: /** Return the first child Element with the given name; if name is null
532: * returns the first element. */
533: public static Element firstChildElement(Element element,
534: String childElementName, String attrName, String attrValue) {
535: if (element == null)
536: return null;
537: // get the first element with the given name
538: Node node = element.getFirstChild();
539:
540: if (node != null) {
541: do {
542: if (node.getNodeType() == Node.ELEMENT_NODE
543: && (childElementName == null || childElementName
544: .equals(node.getNodeName()))) {
545: Element childElement = (Element) node;
546:
547: String value = childElement.getAttribute(attrName);
548:
549: if (value != null && value.equals(attrValue)) {
550: return childElement;
551: }
552: }
553: } while ((node = node.getNextSibling()) != null);
554: }
555: return null;
556: }
557:
558: /** Return the text (node value) contained by the named child node. */
559: public static String childElementValue(Element element,
560: String childElementName) {
561: if (element == null)
562: return null;
563: // get the value of the first element with the given name
564: Element childElement = firstChildElement(element,
565: childElementName);
566:
567: return elementValue(childElement);
568: }
569:
570: /** Return the text (node value) contained by the named child node or a default value if null. */
571: public static String childElementValue(Element element,
572: String childElementName, String defaultValue) {
573: if (element == null)
574: return defaultValue;
575: // get the value of the first element with the given name
576: Element childElement = firstChildElement(element,
577: childElementName);
578: String elementValue = elementValue(childElement);
579:
580: if (elementValue == null || elementValue.length() == 0)
581: return defaultValue;
582: else
583: return elementValue;
584: }
585:
586: /** Return the text (node value) of the first node under this, works best if normalized. */
587: public static String elementValue(Element element) {
588: if (element == null)
589: return null;
590: // make sure we get all the text there...
591: element.normalize();
592: Node textNode = element.getFirstChild();
593:
594: if (textNode == null)
595: return null;
596:
597: StringBuffer valueBuffer = new StringBuffer();
598: do {
599: if (textNode.getNodeType() == Node.CDATA_SECTION_NODE
600: || textNode.getNodeType() == Node.TEXT_NODE) {
601: valueBuffer.append(textNode.getNodeValue());
602: }
603: } while ((textNode = textNode.getNextSibling()) != null);
604: return valueBuffer.toString();
605: }
606:
607: public static String checkEmpty(String string) {
608: if (string != null && string.length() > 0)
609: return string;
610: else
611: return "";
612: }
613:
614: public static String checkEmpty(String string1, String string2) {
615: if (string1 != null && string1.length() > 0)
616: return string1;
617: else if (string2 != null && string2.length() > 0)
618: return string2;
619: else
620: return "";
621: }
622:
623: public static String checkEmpty(String string1, String string2,
624: String string3) {
625: if (string1 != null && string1.length() > 0)
626: return string1;
627: else if (string2 != null && string2.length() > 0)
628: return string2;
629: else if (string3 != null && string3.length() > 0)
630: return string3;
631: else
632: return "";
633: }
634:
635: public static boolean checkBoolean(String str) {
636: return checkBoolean(str, false);
637: }
638:
639: public static boolean checkBoolean(String str, boolean defaultValue) {
640: if (defaultValue) {
641: //default to true, ie anything but false is true
642: return !"false".equals(str);
643: } else {
644: //default to false, ie anything but true is false
645: return "true".equals(str);
646: }
647: }
648:
649: /**
650: * Local entity resolver to handle J2EE DTDs. With this a http connection
651: * to sun is not needed during deployment.
652: * Function boolean hadDTD() is here to avoid validation errors in
653: * descriptors that do not have a DOCTYPE declaration.
654: */
655: public static class LocalResolver implements EntityResolver {
656:
657: private boolean hasDTD = false;
658: private EntityResolver defaultResolver;
659:
660: public LocalResolver(EntityResolver defaultResolver) {
661: this .defaultResolver = defaultResolver;
662: }
663:
664: /**
665: * Returns DTD inputSource. If DTD was found in the dtds Map and inputSource was created
666: * flag hasDTD is set to true.
667: * @param publicId - Public ID of DTD
668: * @param systemId - System ID of DTD
669: * @return InputSource of DTD
670: */
671: public InputSource resolveEntity(String publicId,
672: String systemId) throws SAXException, IOException {
673: //Debug.logInfo("resolving XML entity with publicId [" + publicId + "], systemId [" + systemId + "]", module);
674: hasDTD = false;
675: String dtd = UtilProperties.getSplitPropertyValue(UtilURL
676: .fromResource("localdtds.properties"), publicId);
677: if (UtilValidate.isNotEmpty(dtd)) {
678: if (Debug.verboseOn())
679: Debug.logVerbose(
680: "[UtilXml.LocalResolver.resolveEntity] resolving DTD with publicId ["
681: + publicId + "], systemId ["
682: + systemId
683: + "] and the dtd file is [" + dtd
684: + "]", module);
685: try {
686: URL dtdURL = UtilURL.fromResource(dtd);
687: if (dtdURL == null) {
688: throw new GeneralException(
689: "Local DTD not found - " + dtd);
690: }
691: InputStream dtdStream = dtdURL.openStream();
692: InputSource inputSource = new InputSource(dtdStream);
693:
694: inputSource.setPublicId(publicId);
695: hasDTD = true;
696: if (Debug.verboseOn())
697: Debug
698: .logVerbose(
699: "[UtilXml.LocalResolver.resolveEntity] got LOCAL DTD input source with publicId ["
700: + publicId
701: + "] and the dtd file is ["
702: + dtd + "]", module);
703: return inputSource;
704: } catch (Exception e) {
705: Debug.logWarning(e, module);
706: }
707: } else {
708: // nothing found by the public ID, try looking at the systemId, or at least the filename part of it and look for that on the classpath
709: int lastSlash = systemId.lastIndexOf("/");
710: String filename = null;
711: if (lastSlash == -1) {
712: filename = systemId;
713: } else {
714: filename = systemId.substring(lastSlash + 1);
715: }
716:
717: URL resourceUrl = UtilURL.fromResource(filename);
718:
719: if (resourceUrl != null) {
720: InputStream resStream = resourceUrl.openStream();
721: InputSource inputSource = new InputSource(resStream);
722:
723: if (UtilValidate.isNotEmpty(publicId)) {
724: inputSource.setPublicId(publicId);
725: }
726: hasDTD = true;
727: if (Debug.verboseOn())
728: Debug
729: .logVerbose(
730: "[UtilXml.LocalResolver.resolveEntity] got LOCAL DTD/Schema input source with publicId ["
731: + publicId
732: + "] and the file/resource is ["
733: + filename + "]",
734: module);
735: return inputSource;
736: } else {
737: Debug
738: .logWarning(
739: "[UtilXml.LocalResolver.resolveEntity] could not find LOCAL DTD/Schema with publicId ["
740: + publicId
741: + "] and the file/resource is ["
742: + filename + "]", module);
743: return null;
744: }
745: }
746: //Debug.logInfo("[UtilXml.LocalResolver.resolveEntity] local resolve failed for DTD with publicId [" +
747: // publicId + "] and the dtd file is [" + dtd + "], trying defaultResolver", module);
748: return defaultResolver.resolveEntity(publicId, systemId);
749: }
750:
751: /**
752: * Returns the boolean value to inform id DTD was found in the XML file or not
753: * @return boolean - true if DTD was found in XML
754: */
755: public boolean hasDTD() {
756: return hasDTD;
757: }
758: }
759:
760: /** Local error handler for entity resolver to DocumentBuilder parser.
761: * Error is printed to output just if DTD was detected in the XML file.
762: */
763: public static class LocalErrorHandler implements ErrorHandler {
764:
765: private String docDescription;
766: private LocalResolver localResolver;
767:
768: public LocalErrorHandler(String docDescription,
769: LocalResolver localResolver) {
770: this .docDescription = docDescription;
771: this .localResolver = localResolver;
772: }
773:
774: public void error(SAXParseException exception) {
775: if (localResolver.hasDTD()) {
776: Debug.logError("XmlFileLoader: File " + docDescription
777: + " process error. Line: "
778: + String.valueOf(exception.getLineNumber())
779: + ". Error message: " + exception.getMessage(),
780: module);
781: }
782: }
783:
784: public void fatalError(SAXParseException exception) {
785: if (localResolver.hasDTD()) {
786: Debug.logError("XmlFileLoader: File " + docDescription
787: + " process fatal error. Line: "
788: + String.valueOf(exception.getLineNumber())
789: + ". Error message: " + exception.getMessage(),
790: module);
791: }
792: }
793:
794: public void warning(SAXParseException exception) {
795: if (localResolver.hasDTD()) {
796: Debug.logError("XmlFileLoader: File " + docDescription
797: + " process warning. Line: "
798: + String.valueOf(exception.getLineNumber())
799: + ". Error message: " + exception.getMessage(),
800: module);
801: }
802: }
803: }
804: }
|