001    /*
002    // $Id: //open/mondrian/src/main/mondrian/xmla/impl/DefaultXmlaServlet.java#19 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2005-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.xmla.impl;
011    
012    import java.io.ByteArrayInputStream;
013    import java.io.ByteArrayOutputStream;
014    import java.io.IOException;
015    import java.io.InputStream;
016    import java.io.OutputStream;
017    import java.io.UnsupportedEncodingException;
018    import java.nio.ByteBuffer;
019    import java.nio.channels.Channels;
020    import java.nio.channels.ReadableByteChannel;
021    import java.nio.channels.WritableByteChannel;
022    import java.util.Map;
023    import java.util.List;
024    
025    import javax.servlet.ServletConfig;
026    import javax.servlet.ServletException;
027    import javax.servlet.http.HttpServletRequest;
028    import javax.servlet.http.HttpServletResponse;
029    import javax.xml.parsers.DocumentBuilder;
030    import javax.xml.parsers.DocumentBuilderFactory;
031    import javax.xml.parsers.ParserConfigurationException;
032    
033    import mondrian.xmla.SaxWriter;
034    import mondrian.xmla.XmlaRequest;
035    import mondrian.xmla.XmlaResponse;
036    import mondrian.xmla.XmlaServlet;
037    import mondrian.xmla.XmlaUtil;
038    import mondrian.olap.Util;
039    import mondrian.olap.Role;
040    import mondrian.xmla.XmlaRequestCallback;
041    import mondrian.xmla.XmlaException;
042    
043    import org.apache.log4j.Logger;
044    import org.w3c.dom.Attr;
045    import org.w3c.dom.Document;
046    import org.w3c.dom.Node;
047    import org.w3c.dom.NodeList;
048    import org.w3c.dom.Element;
049    import org.xml.sax.InputSource;
050    import org.xml.sax.SAXException;
051    
052    /**
053     * Default implementation of XML/A servlet.
054     *
055     * @author Gang Chen
056     */
057    public class DefaultXmlaServlet extends XmlaServlet {
058    
059        private static final Logger LOGGER = Logger.getLogger(DefaultXmlaServlet.class);
060        protected static final String nl = Util.nl;
061    
062        private DocumentBuilderFactory domFactory = null;
063    
064        public void init(ServletConfig servletConfig) throws ServletException {
065            super.init(servletConfig);
066            domFactory = getDocumentBuilderFactory();
067        }
068    
069        protected DocumentBuilderFactory getDocumentBuilderFactory() {
070            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
071            factory.setIgnoringComments(true);
072            factory.setIgnoringElementContentWhitespace(true);
073            factory.setNamespaceAware(true);
074            return factory;
075        }
076    
077        protected void unmarshallSoapMessage(
078                HttpServletRequest request,
079                Element[] requestSoapParts)
080                throws XmlaException {
081    
082            try {
083                InputStream inputStream;
084                try {
085                    inputStream = request.getInputStream();
086                } catch (IllegalStateException ex) {
087                    throw new XmlaException(
088                        SERVER_FAULT_FC,
089                        USM_REQUEST_STATE_CODE,
090                        USM_REQUEST_STATE_FAULT_FS,
091                        ex);
092                } catch (IOException ex) {
093                    // This is either Client or Server
094                    throw new XmlaException(
095                        SERVER_FAULT_FC,
096                        USM_REQUEST_INPUT_CODE,
097                        USM_REQUEST_INPUT_FAULT_FS,
098                        ex);
099                }
100    
101                DocumentBuilder domBuilder;
102                try {
103                    domBuilder = domFactory.newDocumentBuilder();
104                } catch (ParserConfigurationException ex) {
105                    throw new XmlaException(
106                        SERVER_FAULT_FC,
107                        USM_DOM_FACTORY_CODE,
108                        USM_DOM_FACTORY_FAULT_FS,
109                        ex);
110                }
111    
112                Document soapDoc;
113                try {
114                    soapDoc = domBuilder.parse(new InputSource(inputStream));
115                } catch (IOException ex) {
116                    // This is either Client or Server
117                    throw new XmlaException(
118                        SERVER_FAULT_FC,
119                        USM_DOM_PARSE_IO_CODE,
120                        USM_DOM_PARSE_IO_FAULT_FS,
121                        ex);
122                } catch (SAXException ex) {
123                    // Assume client passed bad xml
124                    throw new XmlaException(
125                        CLIENT_FAULT_FC,
126                        USM_DOM_PARSE_CODE,
127                        USM_DOM_PARSE_FAULT_FS,
128                        ex);
129                }
130    
131                /* Check SOAP message */
132                Element envElem = soapDoc.getDocumentElement();
133    
134                if (LOGGER.isDebugEnabled()) {
135                    StringBuilder buf = new StringBuilder(100);
136                    buf.append("XML/A request content").append(nl);
137                    buf.append(XmlaUtil.element2Text(envElem));
138                    LOGGER.debug(buf.toString());
139                }
140    
141                if ("Envelope".equals(envElem.getLocalName())) {
142                    if (!(NS_SOAP_ENV_1_1.equals(envElem.getNamespaceURI()))) {
143                        String msg = "Invalid SOAP message: " +
144                            "Envelope element not in SOAP namespace";
145                        throw new XmlaException(
146                            CLIENT_FAULT_FC,
147                            USM_DOM_PARSE_CODE,
148                            USM_DOM_PARSE_FAULT_FS,
149                            new SAXException(msg));
150                    }
151                } else {
152                    String msg = "Invalid SOAP message: " +
153                            "Top element not Envelope";
154                    throw new XmlaException(
155                        CLIENT_FAULT_FC,
156                        USM_DOM_PARSE_CODE,
157                        USM_DOM_PARSE_FAULT_FS,
158                        new SAXException(msg));
159                }
160    
161                Element[] childs =
162                    XmlaUtil.filterChildElements(envElem, NS_SOAP_ENV_1_1, "Header");
163                if (childs.length > 1) {
164                    String msg = "Invalid SOAP message: " +
165                            "More than one Header elements";
166                    throw new XmlaException(
167                        CLIENT_FAULT_FC,
168                        USM_DOM_PARSE_CODE,
169                        USM_DOM_PARSE_FAULT_FS,
170                        new SAXException(msg));
171                }
172                requestSoapParts[0] = childs.length == 1 ? childs[0] : null;
173    
174                childs = XmlaUtil.filterChildElements(envElem, NS_SOAP_ENV_1_1, "Body");
175                if (childs.length != 1) {
176                    String msg = "Invalid SOAP message: " +
177                            "Does not have one Body element";
178                    throw new XmlaException(
179                        CLIENT_FAULT_FC,
180                        USM_DOM_PARSE_CODE,
181                        USM_DOM_PARSE_FAULT_FS,
182                        new SAXException(msg));
183                }
184                requestSoapParts[1] = childs[0];
185    
186            } catch (XmlaException xex) {
187                throw xex;
188            } catch (Exception ex) {
189                throw new XmlaException(
190                    SERVER_FAULT_FC,
191                    USM_UNKNOWN_CODE,
192                    USM_UNKNOWN_FAULT_FS,
193                    ex);
194            }
195        }
196    
197        /**
198         * See if there is a "mustUnderstand" header element.
199         * If there is a BeginSession element, then generate a session id and
200         * add to context Map.
201         * <p>
202         * Excel 2000 and Excel XP generate both a BeginSession, Session and
203         * EndSession mustUnderstand==1
204         * in the "urn:schemas-microsoft-com:xml-analysis" namespace
205         * Header elements and a NamespaceCompatibility mustUnderstand==0
206         * in the "http://schemas.microsoft.com/analysisservices/2003/xmla"
207         * namespace. Here we handle only the session Header elements
208         *
209         */
210        protected void handleSoapHeader(
211                HttpServletResponse response,
212                Element[] requestSoapParts,
213                byte[][] responseSoapParts,
214                Map<String, Object> context) throws XmlaException {
215    
216            try {
217                Element hdrElem = requestSoapParts[0];
218                if ((hdrElem == null) || (! hdrElem.hasChildNodes())) {
219                    return;
220                }
221                String encoding = response.getCharacterEncoding();
222    
223                byte[] bytes = null;
224    
225                NodeList nlst = hdrElem.getChildNodes();
226                int nlen = nlst.getLength();
227                for (int i = 0; i < nlen; i++) {
228                    Node n = nlst.item(i);
229                    if (n instanceof Element) {
230                        Element e = (Element) n;
231    
232                        // does the Element have a mustUnderstand attribute
233                        Attr attr = e.getAttributeNode(SOAP_MUST_UNDERSTAND_ATTR);
234                        if (attr == null) {
235                            continue;
236                        }
237                        // Is its value "1"
238                        String mustUnderstandValue = attr.getValue();
239                        if ((mustUnderstandValue == null) ||
240                                (! mustUnderstandValue.equals("1"))) {
241                            continue;
242                        }
243    
244                        // We've got a mustUnderstand attribute
245    
246                        // Is it an XMLA element
247                        if (! NS_XMLA.equals(e.getNamespaceURI())) {
248                            continue;
249                        }
250                        // So, an XMLA mustUnderstand-er
251                        // Do we know what to do with it
252                        // We understand:
253                        //    BeginSession
254                        //    Session
255                        //    EndSession
256    
257                        String sessionIdStr;
258                        String localName = e.getLocalName();
259                        if (localName.equals(XMLA_BEGIN_SESSION)) {
260                            // generate SessionId
261    
262                            sessionIdStr = generateSessionId(context);
263    
264                            context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr);
265                            context.put(CONTEXT_XMLA_SESSION_STATE,
266                                        CONTEXT_XMLA_SESSION_STATE_BEGIN);
267    
268                        } else if (localName.equals(XMLA_SESSION)) {
269                            // extract the SessionId attrs value and put into context
270                            sessionIdStr = getSessionId(e, context);
271    
272                            context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr);
273                            context.put(CONTEXT_XMLA_SESSION_STATE,
274                                        CONTEXT_XMLA_SESSION_STATE_WITHIN);
275    
276                        } else if (localName.equals(XMLA_END_SESSION)) {
277                            // extract the SessionId attrs value and put into context
278                            sessionIdStr = getSessionId(e, context);
279    
280                            context.put(CONTEXT_XMLA_SESSION_ID, sessionIdStr);
281                            context.put(CONTEXT_XMLA_SESSION_STATE,
282                                        CONTEXT_XMLA_SESSION_STATE_END);
283    
284                        } else {
285                            // error
286                            String msg = "Invalid XML/A message: " +
287                                " Unknown \"mustUnderstand\" XMLA Header element \"" +
288                                localName +
289                                "\"";
290                            throw new XmlaException(
291                                MUST_UNDERSTAND_FAULT_FC,
292                                HSH_MUST_UNDERSTAND_CODE,
293                                HSH_MUST_UNDERSTAND_FAULT_FS,
294                                new RuntimeException(msg));
295                        }
296    
297                        StringBuilder buf = new StringBuilder(100);
298                        buf.append("<Session ");
299                        buf.append(XMLA_SESSION_ID);
300                        buf.append("=\"");
301                        buf.append(sessionIdStr);
302                        buf.append("\" ");
303                        buf.append("xmlns=\"");
304                        buf.append(NS_XMLA);
305                        buf.append("\" />");
306                        bytes = buf.toString().getBytes(encoding);
307    
308                    }
309                }
310                responseSoapParts[0] = bytes;
311    
312            } catch (XmlaException xex) {
313                throw xex;
314            } catch (Exception ex) {
315                throw new XmlaException(
316                    SERVER_FAULT_FC,
317                    HSH_UNKNOWN_CODE,
318                    HSH_UNKNOWN_FAULT_FS,
319                    ex);
320            }
321        }
322    
323        protected String generateSessionId(Map<String, Object> context) {
324            List<XmlaRequestCallback> callbacks = getCallbacks();
325            if (callbacks.size() > 0) {
326                // get only the first callback if it exists
327                XmlaRequestCallback callback = callbacks.get(0);
328                return (String) callback.generateSessionId(context);
329            } else {
330                // what to do here, should Mondrian generate a Session Id?
331                // TODO: Maybe Mondrian ought to generate all Session Ids and
332                // not the callback.
333                return "";
334            }
335        }
336    
337        protected String getSessionId(Element e, Map<String, Object> context)
338                throws Exception {
339            // extract the SessionId attrs value and put into context
340            Attr attr = e.getAttributeNode(XMLA_SESSION_ID);
341            if (attr == null) {
342                String msg = "Invalid XML/A message: " +
343                    XMLA_SESSION +
344                    " Header element with no " +
345                    XMLA_SESSION_ID +
346                    " attribute";
347                throw new SAXException(msg);
348            }
349            String value = attr.getValue();
350            if (value == null) {
351                String msg = "Invalid XML/A message: " +
352                    XMLA_SESSION +
353                    " Header element with " +
354                    XMLA_SESSION_ID +
355                    " attribute but no attribute value";
356                throw new SAXException(msg);
357            }
358            return value;
359        }
360    
361        protected void handleSoapBody(
362            HttpServletResponse response,
363            Element[] requestSoapParts,
364            byte[][] responseSoapParts,
365            Map<String, Object> context)
366            throws XmlaException
367        {
368            try {
369                String encoding = response.getCharacterEncoding();
370                Element hdrElem = requestSoapParts[0];
371                Element bodyElem = requestSoapParts[1];
372                Element[] dreqs = XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Discover");
373                Element[] ereqs = XmlaUtil.filterChildElements(bodyElem, NS_XMLA, "Execute");
374                if (dreqs.length + ereqs.length != 1) {
375                    String msg = "Invalid XML/A message: " +
376                        " Body has " +
377                        dreqs.length +
378                        " Discover Requests and " +
379                        ereqs.length +
380                        " Execute Requests";
381                    throw new XmlaException(
382                        CLIENT_FAULT_FC,
383                        HSB_BAD_SOAP_BODY_CODE,
384                        HSB_BAD_SOAP_BODY_FAULT_FS,
385                        new RuntimeException(msg));
386                }
387    
388                Element xmlaReqElem = (dreqs.length == 0 ? ereqs[0] : dreqs[0]);
389    
390                ByteArrayOutputStream osBuf = new ByteArrayOutputStream();
391    
392                // use context variable `role' as this request's XML/A role
393                String roleName = (String) context.get(CONTEXT_ROLE_NAME);
394                Role role = (Role) context.get(CONTEXT_ROLE);
395    
396                XmlaRequest xmlaReq = null;
397                if (role != null) {
398                    xmlaReq = new DefaultXmlaRequest(xmlaReqElem, role);
399                } else if (roleName != null) {
400                    xmlaReq = new DefaultXmlaRequest(xmlaReqElem, roleName);
401                } else {
402                    xmlaReq = new DefaultXmlaRequest(xmlaReqElem);
403                }
404    
405                XmlaResponse xmlaRes = new DefaultXmlaResponse(osBuf, encoding);
406    
407                try {
408                    getXmlaHandler().process(xmlaReq, xmlaRes);
409                } catch (XmlaException ex) {
410                    throw ex;
411                } catch (Exception ex) {
412                    throw new XmlaException(
413                        SERVER_FAULT_FC,
414                        HSB_PROCESS_CODE,
415                        HSB_PROCESS_FAULT_FS,
416                        ex);
417                }
418    
419                responseSoapParts[1] = osBuf.toByteArray();
420    
421            } catch (XmlaException xex) {
422                throw xex;
423            } catch (Exception ex) {
424                throw new XmlaException(
425                    SERVER_FAULT_FC,
426                    HSB_UNKNOWN_CODE,
427                    HSB_UNKNOWN_FAULT_FS,
428                    ex);
429            }
430        }
431    
432        protected void marshallSoapMessage(
433                HttpServletResponse response,
434                byte[][] responseSoapParts)
435                throws XmlaException {
436    
437            try {
438                // If CharacterEncoding was set in web.xml, use this value
439                String encoding = (charEncoding != null)
440                        ? charEncoding : response.getCharacterEncoding();
441    
442                /*
443                 * Since we just reset response, encoding and content-type were
444                 * reset too
445                 */
446                if (charEncoding != null) {
447                    response.setCharacterEncoding(charEncoding);
448                }
449                response.setContentType("text/xml");
450    
451                /*
452                 * The setCharacterEncoding, setContentType, or setLocale method
453                 * must be called BEFORE getWriter or getOutputStream and before
454                 * committing the response for the character encoding to be used.
455                 * http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletResponse.html
456                 */
457                OutputStream outputStream = response.getOutputStream();
458    
459    
460                byte[] soapHeader = responseSoapParts[0];
461                byte[] soapBody = responseSoapParts[1];
462    
463                Object[] byteChunks = new Object[5];
464    
465                try {
466                    StringBuilder buf = new StringBuilder(500);
467                    buf.append("<?xml version=\"1.0\" encoding=\"");
468                    buf.append(encoding);
469                    buf.append("\"?>");
470                    buf.append(nl);
471    
472                    buf.append("<");
473                    buf.append(SOAP_PREFIX);
474                    buf.append(":Envelope xmlns:");
475                    buf.append(SOAP_PREFIX);
476                    buf.append("=\"");
477                    buf.append(NS_SOAP_ENV_1_1);
478                    buf.append("\" ");
479                    buf.append(SOAP_PREFIX);
480                    buf.append(":encodingStyle=\"");
481                    buf.append(NS_SOAP_ENC_1_1);
482                    buf.append("\" >");
483                    buf.append(nl);
484                    buf.append("<");
485                    buf.append(SOAP_PREFIX);
486                    buf.append(":Header>");
487                    buf.append(nl);
488                    byteChunks[0] = buf.toString().getBytes(encoding);
489    
490                    byteChunks[1] = soapHeader;
491    
492                    buf.setLength(0);
493                    buf.append("</");
494                    buf.append(SOAP_PREFIX);
495                    buf.append(":Header>");
496                    buf.append(nl);
497                    buf.append("<");
498                    buf.append(SOAP_PREFIX);
499                    buf.append(":Body>");
500                    buf.append(nl);
501    
502                    byteChunks[2] = buf.toString().getBytes(encoding);
503    
504                    byteChunks[3] = soapBody;
505    
506                    buf.setLength(0);
507                    buf.append(nl);
508                    buf.append("</");
509                    buf.append(SOAP_PREFIX);
510                    buf.append(":Body>");
511                    buf.append(nl);
512                    buf.append("</");
513                    buf.append(SOAP_PREFIX);
514                    buf.append(":Envelope>");
515                    buf.append(nl);
516    
517                    byteChunks[4] = buf.toString().getBytes(encoding);
518    
519                } catch (UnsupportedEncodingException uee) {
520                    LOGGER.warn("This should be handled at begin of processing request", uee);
521                }
522    
523                if (LOGGER.isDebugEnabled()) {
524                    StringBuilder buf = new StringBuilder(100);
525                    buf.append("XML/A response content").append(nl);
526                    try {
527                        for (Object byteChunk : byteChunks) {
528                            byte[] chunk = (byte[]) byteChunk;
529                            if (chunk != null && chunk.length > 0) {
530                                buf.append(new String(chunk, encoding));
531                            }
532                        }
533                    } catch (UnsupportedEncodingException uee) {
534                        LOGGER.warn("This should be handled at begin of processing request", uee);
535                    }
536                    LOGGER.debug(buf.toString());
537                }
538    
539                if (LOGGER.isDebugEnabled()) {
540                    StringBuilder buf = new StringBuilder();
541                    buf.append("XML/A response content").append(nl);
542                }
543                try {
544                    int bufferSize = 4096;
545                    ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
546                    WritableByteChannel wch = Channels.newChannel(outputStream);
547                    ReadableByteChannel rch;
548                    for (Object byteChunk : byteChunks) {
549                        if (byteChunk == null || ((byte[]) byteChunk).length == 0) {
550                            continue;
551                        }
552                        rch = Channels
553                            .newChannel(new ByteArrayInputStream((byte[]) byteChunk));
554    
555                        int readSize;
556                        do {
557                            buffer.clear();
558                            readSize = rch.read(buffer);
559                            buffer.flip();
560    
561                            int writeSize = 0;
562                            while ((writeSize += wch.write(buffer)) < readSize) {
563                                ;
564                            }
565                        } while (readSize == bufferSize);
566                        rch.close();
567                    }
568                    outputStream.flush();
569                } catch (IOException ioe) {
570                    LOGGER.error("Damn exception when transferring bytes over sockets", ioe);
571                }
572            } catch (XmlaException xex) {
573                throw xex;
574            } catch (Exception ex) {
575                throw new XmlaException(
576                    SERVER_FAULT_FC,
577                    MSM_UNKNOWN_CODE,
578                    MSM_UNKNOWN_FAULT_FS,
579                    ex);
580            }
581        }
582    
583        /**
584         * This produces a SOAP 1.1 version Fault element - not a 1.2 version.
585         *
586         */
587        protected void handleFault(
588                        HttpServletResponse response,
589                        byte[][] responseSoapParts,
590                        Phase phase,
591                        Throwable t) {
592    
593            // Regardless of whats been put into the response so far, clear
594            // it out.
595            response.reset();
596    
597            // NOTE: if you can think of better/other status codes to use
598            // for the various phases, please make changes.
599            // I think that XMLA faults always returns OK.
600            switch (phase) {
601            case VALIDATE_HTTP_HEAD:
602                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
603                break;
604            case INITIAL_PARSE:
605            case CALLBACK_PRE_ACTION:
606            case PROCESS_HEADER:
607            case PROCESS_BODY:
608            case CALLBACK_POST_ACTION:
609            case SEND_RESPONSE:
610                response.setStatus(HttpServletResponse.SC_OK);
611                break;
612            }
613    
614            String code;
615            String faultCode;
616            String faultString;
617            String detail;
618            if (t instanceof XmlaException) {
619                XmlaException xex = (XmlaException) t;
620                code = xex.getCode();
621                faultString = xex.getFaultString();
622                faultCode = XmlaException.formatFaultCode(xex);
623                detail = XmlaException.formatDetail(xex.getDetail());
624    
625            } else {
626                // some unexpected Throwable
627                t = XmlaException.getRootCause(t);
628                code = UNKNOWN_ERROR_CODE;
629                faultString = UNKNOWN_ERROR_FAULT_FS;
630                faultCode = XmlaException.formatFaultCode(
631                                SERVER_FAULT_FC, code);
632                detail = XmlaException.formatDetail(t.getMessage());
633            }
634    
635            String encoding = response.getCharacterEncoding();
636    
637            ByteArrayOutputStream osBuf = new ByteArrayOutputStream();
638            try {
639                SaxWriter writer = new DefaultSaxWriter(osBuf, encoding);
640                writer.startDocument();
641                writer.startElement(SOAP_PREFIX + ":Fault");
642    
643                // The faultcode element is intended for use by software to provide
644                // an algorithmic mechanism for identifying the fault. The faultcode
645                // MUST be present in a SOAP Fault element and the faultcode value
646                // MUST be a qualified name
647                writer.startElement("faultcode");
648                writer.characters(faultCode);
649                writer.endElement();
650    
651                // The faultstring element is intended to provide a human readable
652                // explanation of the fault and is not intended for algorithmic
653                // processing.
654                writer.startElement("faultstring");
655                writer.characters(faultString);
656                writer.endElement();
657    
658                // The faultactor element is intended to provide information about
659                // who caused the fault to happen within the message path
660                writer.startElement("faultactor");
661                writer.characters(FAULT_ACTOR);
662                writer.endElement();
663    
664                // The detail element is intended for carrying application specific
665                // error information related to the Body element. It MUST be present
666                // if the contents of the Body element could not be successfully
667                // processed. It MUST NOT be used to carry information about error
668                // information belonging to header entries. Detailed error
669                // information belonging to header entries MUST be carried within
670                // header entries.
671                if (phase != Phase.PROCESS_HEADER) {
672                    writer.startElement("detail");
673                    writer.startElement(FAULT_NS_PREFIX + ":error", new String[] {
674                            "xmlns:" + FAULT_NS_PREFIX, MONDRIAN_NAMESPACE
675                    });
676                    writer.startElement("code");
677                    writer.characters(code);
678                    writer.endElement(); // code
679                    writer.startElement("desc");
680                    writer.characters(detail);
681                    writer.endElement(); // desc
682                    writer.endElement(); // error
683                    writer.endElement(); // detail
684                }
685    
686                writer.endElement();   // </Fault>
687                writer.endDocument();
688            } catch (UnsupportedEncodingException uee) {
689                LOGGER.warn("This should be handled at begin of processing request", uee);
690            } catch (Exception e) {
691                LOGGER.error("Unexcepted runimt exception when handing SOAP fault :(");
692            }
693    
694            responseSoapParts[1] = osBuf.toByteArray();
695        }
696    
697    }
698    
699    // End DefaultXmlaServlet.java