View Javadoc

1   /*
2    *  Copyright (c) 2008 Rodrigo Ruiz
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.apache.axis.message.addressing.handler;
17  
18  import org.apache.axis.AxisFault;
19  import org.apache.axis.Message;
20  import org.apache.axis.MessageContext;
21  import org.apache.axis.AxisEngine;
22  import org.apache.axis.client.Call;
23  import org.apache.axis.client.Service;
24  import org.apache.axis.message.SOAPEnvelope;
25  import org.apache.axis.message.SOAPBodyElement;
26  import org.apache.axis.message.RPCElement;
27  import org.apache.axis.message.SOAPFault;
28  import org.apache.axis.description.OperationDesc;
29  import org.apache.axis.components.uuid.UUIDGen;
30  import org.apache.axis.components.uuid.UUIDGenFactory;
31  import org.apache.axis.handlers.BasicHandler;
32  import org.apache.axis.message.addressing.Action;
33  import org.apache.axis.message.addressing.AddressingVersion;
34  import org.apache.axis.message.addressing.AttributedURI;
35  import org.apache.axis.message.addressing.AddressingHeaders;
36  import org.apache.axis.message.addressing.Constants;
37  import org.apache.axis.message.addressing.EndpointReference;
38  import org.apache.axis.message.addressing.EndpointReferenceType;
39  import org.apache.axis.message.addressing.MessageID;
40  import org.apache.axis.message.addressing.To;
41  import org.apache.axis.message.addressing.AddressingHeaderFault;
42  import org.apache.axis.message.addressing.util.AddressingUtils;
43  import org.apache.axis.types.URI;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  
48  import javax.xml.namespace.QName;
49  import java.util.ArrayList;
50  import java.util.List;
51  import java.util.StringTokenizer;
52  
53  /**
54   * Class AddressingHandler.
55   *
56   * @author Davanum Srinivas
57   * @version $Revision: 14 $
58   */
59  public class AddressingHandler extends BasicHandler implements Constants {
60  
61    /**
62     * <code>serialVersionUID</code> attribute.
63     */
64    private static final long serialVersionUID = 831310304473973743L;
65  
66    /**
67     * Class logger.
68     */
69    protected static final Log LOG = LogFactory.getLog(AddressingHandler.class);
70  
71    /**
72     * UUID generator.
73     */
74    private static UUIDGen uuidGen = UUIDGenFactory.getUUIDGen();
75  
76    /**
77     * A list of QNames of reference properties specified by the
78     * deployer that need to be handled.
79     * <p>
80     * If the ignore flag (above) is set to false, and this list is
81     * null - we process all headers
82     */
83    private List<QName> refPropQNames;
84  
85    /**
86     * Actor URI.
87     */
88    private String actor;
89  
90    /**
91     * Whether to remove headers once processed.
92     */
93    private boolean removeHeaders = false;
94  
95    /**
96     * Whether to allow relative Action URIs or not.
97     */
98    private boolean allowNonSpecificActions = false;
99  
100   /**
101    * This is defined in newer Axis builds in Handler.java, but replicated
102    * here for backwards compatibility.  Note that some faults will NOT be
103    * correctly processed using older (pre-Feb-2006) versions of Axis.
104    */
105   private static final String OPT_CATCH_OWN_FAULTS = "handler.catchOwnFaults";
106 
107   /**
108    * Creates a new instance.
109    */
110   public AddressingHandler() {
111   }
112 
113   /**
114    * {@inheritDoc}
115    */
116   @Override
117   public void init() {
118     super.init();
119     initializeReferencePropertyNames();
120     initializeRemoveHeaders();
121     initializeActor();
122     initializeAllowNonSpecificActions();
123 
124     // Default to calling onFault() for our own faults
125     Boolean catchOwnFaults = (Boolean)getOption(OPT_CATCH_OWN_FAULTS);
126     if (catchOwnFaults == null) {
127       setOption(OPT_CATCH_OWN_FAULTS, Boolean.TRUE);
128     }
129   }
130 
131   /**
132    * Initializes the "actor" URI field.
133    */
134   protected void initializeActor() {
135     actor = (String) getOption("actor");
136   }
137 
138   /**
139    * Initializes the "removeHeaders" configuration flag.
140    */
141   private void initializeRemoveHeaders() {
142     String property = (String) getOption("removeHeaders");
143     this.removeHeaders = "true".equalsIgnoreCase(property);
144   }
145 
146   /**
147    * Initializes the "allowNonSpecificActions" configuration flag.
148    */
149   private void initializeAllowNonSpecificActions() {
150     String property = (String) getOption("allowNonSpecificActions");
151     this.allowNonSpecificActions = "true".equalsIgnoreCase(property);
152   }
153 
154   /**
155    * {@inheritDoc}
156    */
157   public void invoke(MessageContext msgContext) throws AxisFault {
158     boolean setMustUnderstand =
159       msgContext.isPropertyTrue(ENV_ADDRESSING_SET_MUST_UNDERSTAND);
160 
161     try {
162       if (msgContext.isClient()) {
163         if (!msgContext.getPastPivot()) {
164           processClientRequest(msgContext, setMustUnderstand);
165         } else {
166           processClientResponse(msgContext);
167         }
168       } else {
169         if (!msgContext.getPastPivot()) {
170           processServerRequest(msgContext);
171         } else {
172           processServerResponse(msgContext, setMustUnderstand);
173         }
174       }
175     } catch (Exception e) {
176       LOG.error("Exception in AddressingHandler", e);
177       throw AxisFault.makeFault(e);
178     }
179   }
180 
181   /**
182    * {@inheritDoc}
183    */
184   public void onFault(MessageContext msgContext) {
185     if (msgContext.isClient()) {
186       return;
187     }
188     try {
189       processFault(msgContext);
190     } catch (Exception e) {
191       LOG.error("Exception in AddressingHandler", e);
192     }
193   }
194 
195   /**
196    * Processes a fault.
197    *
198    * @param ctx Context information
199    * @throws Exception If an error occurs
200    */
201   protected void processFault(MessageContext ctx) throws Exception {
202     Message msg = ctx.getResponseMessage();
203     if (msg == null) {
204       return;
205     }
206 
207     AddressingVersion version = AddressingUtils.getAddressingVersion(ctx);
208 
209     SOAPFault el = (SOAPFault)msg.getSOAPEnvelope().getFirstBody();
210     if (el.getFault().getFaultCode().equals(
211       ctx.getSOAPConstants().getMustunderstandFaultQName())) {
212       return;
213     }
214 
215     AddressingHeaders reqHeaders =
216       (AddressingHeaders) ctx.getProperty(ENV_ADDRESSING_REQUEST_HEADERS);
217 
218     if (reqHeaders == null) {
219       // error?
220       return;
221     }
222 
223     AddressingHeaders resHeaders = AddressingUtils.getResponseHeaders(ctx);
224 
225     // set From
226     EndpointReference fromEPR = resHeaders.getFrom();
227     if (fromEPR == null) {
228       To toURI = reqHeaders.getTo();
229       if (toURI != null) {
230         fromEPR = new EndpointReference(toURI);
231         fromEPR.setProperties(reqHeaders.getReferenceProperties());
232         resHeaders.setFrom(fromEPR);
233       }
234     }
235     URI actionURI = new URI(version.getFaultActionURI(), allowNonSpecificActions);
236     resHeaders.setAction(actionURI);
237 
238     // process RelatesTo
239     MessageID msgID = reqHeaders.getMessageID();
240     if (msgID != null) {
241       resHeaders.addRelatesTo(msgID.toString(), version.getResponseRelationshipType());
242     }
243 
244     // process MessageID
245     msgID = new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
246     resHeaders.setMessageID(msgID);
247 
248     // now put all headers into soap env.
249     EndpointReferenceType faultTo = reqHeaders.getFaultTo();
250     if (faultTo == null) {
251       faultTo = reqHeaders.getReplyTo();
252     }
253 
254     if (faultTo != null) {
255       resHeaders.setReferenceProperties(faultTo.getProperties());
256       resHeaders.setReferenceParameters(faultTo.getParameters());
257 
258       // resHeaders.toEnvelope(msg.getSOAPEnvelope());
259 
260       // process FaultTo
261       AttributedURI address = faultTo.getAddress();
262       if (address != null) {
263         String uri = address.toString();
264         /*
265          * Fix Sonic00027851, WS Addressing does not properly examine and
266          * deliver to an anonymous FaultTo address
267          */
268         if (uri != null
269             && !uri.equals(version.getAnonymousRoleURI())) {
270           // send the msg to fault to
271           if (!uri.equals(URI_NONE_W3C_CR)) {
272             forwardMessage(faultTo, msg);
273           }
274           // Somehow make the response empty, or create a new empty
275           // response
276           /*
277            * Fix Sonic00027691 avoid NullPointerException with
278            * msgContext.setCurrentMessage(null)
279            */
280           ctx.setResponseMessage(null);
281           ctx.setProperty("axis.DontEmitFault", Boolean.TRUE);
282           return;
283         }
284       }
285     }
286 
287     // TODO : Default to ReplyTo, and then From?
288 
289     resHeaders.toEnvelope(msg.getSOAPEnvelope());
290   }
291 
292   /**
293    * Method processClientRequest.
294    *
295    * @param ctx Context
296    * @param setMustUnderstand Must-Understand attribute value
297    */
298   protected void processClientRequest(MessageContext ctx,  boolean setMustUnderstand)
299     throws Exception {
300 
301     boolean sendDefaultFrom = ctx.isPropertyTrue(SEND_DEFAULT_FROM, true);
302     boolean sendDefaultMessageID = ctx.isPropertyTrue(SEND_DEFAULT_MESSAGEID, true);
303     boolean sendDefaultTo = ctx.isPropertyTrue(SEND_DEFAULT_TO, true);
304 
305     AddressingHeaders sharedHeaders = (AddressingHeaders) ctx
306     .getProperty(ENV_ADDRESSING_SHARED_HEADERS);
307 
308     AddressingHeaders headers = AddressingUtils.getRequestHeaders(ctx);
309 
310     headers.setSetMustUnderstand(setMustUnderstand);
311 
312     // set MessageID
313     if (headers.getMessageID() == null && sendDefaultMessageID) {
314       MessageID id = new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
315       headers.setMessageID(id);
316     }
317 
318     // set To
319     To to = headers.getTo();
320     if (to == null && sendDefaultTo) {
321       if (sharedHeaders != null) {
322         // To is always set in shared headers
323         headers.setTo(sharedHeaders.getTo());
324       } else {
325         headers.setTo(new To(ctx.getStrProp(MessageContext.TRANS_URL)));
326       }
327     }
328 
329     // set Action
330     String action = ctx.getSOAPActionURI();
331     if (action != null) {
332       URI actionURI = new URI(action, allowNonSpecificActions);
333       headers.setAction(new Action(actionURI));
334     } else {
335       Action act = headers.getAction();
336       if (act == null) {
337         URI actionURI = new URI("http://apache.org/axis/defaultAction");
338         headers.setAction(new Action(actionURI));
339       }
340 
341       ctx.setUseSOAPAction(true);
342       // Make SOAP action match
343       ctx.setSOAPActionURI(headers.getAction().toString());
344     }
345 
346     // set From
347     if (headers.getFrom() == null && sendDefaultFrom) {
348       String from = ctx.getStrProp(ENV_ADDRESSING_FROM_URI);
349       if (from == null) {
350         from = AddressingUtils.getAddressingVersion().getAnonymousRoleURI();
351       }
352       headers.setFrom(new EndpointReference(from));
353     }
354 
355     // set ReplyTo
356     if (ctx.isPropertyTrue(ENV_ADDRESSING_SEND_REPLYTO) && headers.getReplyTo() == null) {
357       String replyTo = ctx.getStrProp(ENV_ADDRESSING_REPLYTO_URI);
358       if (replyTo != null) {
359         headers.setReplyTo(new EndpointReference(replyTo));
360       } else {
361         headers.setReplyTo(headers.getFrom());
362       }
363     }
364 
365     // set FaultTo
366     if (headers.getFaultTo() == null) {
367       String faultTo = ctx.getStrProp(ENV_ADDRESSING_FAULTTO_URI);
368       if (faultTo != null) {
369         headers.setFaultTo(new EndpointReference(faultTo));
370       }
371     }
372 
373     if (sharedHeaders != null) {
374       headers.setReferenceProperties(sharedHeaders.getReferenceProperties());
375     }
376     Message msg = ctx.getRequestMessage();
377     headers.toEnvelope(msg.getSOAPEnvelope(), this.actor);
378   }
379 
380   /**
381    * Method processClientResponse.
382    *
383    * @param ctx Context information
384    * @throws Exception If an error occurs
385    */
386   protected void processClientResponse(MessageContext ctx) throws Exception {
387     // if no response - do nothing
388     Message msg = ctx.getResponseMessage();
389     if (msg == null) {
390       return;
391     }
392 
393     AddressingHeaders headers = new AddressingHeaders(
394       msg.getSOAPEnvelope(), this.actor,
395       true, this.removeHeaders, false, this.allowNonSpecificActions,
396       this.refPropQNames);
397     ctx.setProperty(ENV_ADDRESSING_RESPONSE_HEADERS, headers);
398   }
399 
400   /**
401    * Method processServerRequest.
402    *
403    * @param ctx Context information
404    * @throws Exception If an error occurs
405    */
406   protected void processServerRequest(MessageContext ctx) throws Exception {
407 
408     boolean isAddrRequired = ctx.isPropertyTrue(ENV_ADDRESSING_REQUIRED, true);
409 
410     Message msg = ctx.getRequestMessage();
411     if (msg == null) {
412       return;
413     }
414     AddressingHeaders headers = new AddressingHeaders(
415       msg.getSOAPEnvelope(), this.actor,
416       true, this.removeHeaders, false, this.allowNonSpecificActions,
417       this.refPropQNames);
418 
419     AxisFault fault = headers.getAddrHeaderFault();
420 
421     if (isAddrRequired && headers.getAction() == null) {
422       if (ctx.getProperty(ENV_ADDRESSING_NAMESPACE_URI) == null) {
423         ctx.setProperty(ENV_ADDRESSING_NAMESPACE_URI, NS_URI_ADDRESSING_2005_08);
424       }
425       fault = new AddressingHeaderFault("No Action header",
426           AddressingHeaderFault.HEADER_REQUIRED, "Action");
427       LOG.debug("WS-A request to endpoint " + headers.getTo()
428           + " is missing the required wsa:Action header.");
429     }
430 
431     ctx.setProperty(ENV_ADDRESSING_REQUEST_HEADERS, headers);
432 
433     if (fault != null) {
434       throw fault;
435     }
436 
437     // set the target service based on To header if it hasn't already
438     // been determined.  NOTE: May want to add an option to override
439     // later.
440     if (ctx.getService() == null && ctx.getTargetService() == null) {
441       setTargetService(ctx, headers);
442 
443       if (ctx.getService() != null) {
444         resetOperations(ctx);
445       }
446     }
447   }
448 
449   /**
450    * Reset the context operations.
451    *
452    * @param ctx Context information
453    * @throws AxisFault If an error occurs
454    */
455   protected void resetOperations(MessageContext ctx) throws AxisFault {
456     resetContextOperations(ctx);
457   }
458 
459   /**
460    * Reinitializes the RPCElement with right operations
461    * so that things are nicely deserialized.
462    *
463    * @param ctx Context information
464    * @throws AxisFault If an error occurs
465    */
466   public static void resetContextOperations(MessageContext ctx) throws AxisFault {
467     Message msg = ctx.getCurrentMessage();
468     if (msg == null) {
469       return;
470     }
471     SOAPEnvelope env = msg.getSOAPEnvelope();
472     if (env == null) {
473       return;
474     }
475     SOAPBodyElement bodyElement = env.getFirstBody();
476     if (bodyElement != null && bodyElement instanceof RPCElement) {
477       RPCElement element = (RPCElement) bodyElement;
478       // update the operations in RPCElement
479       element.updateOperationsByQName();
480       OperationDesc[] operations = element.getOperations();
481       // and set operation if appropriate
482       if (operations == null) {
483         element.updateOperationsByName();
484       } else if (operations.length == 1) {
485         ctx.setOperation(operations[0]);
486       }
487     } else {
488       msg.getSOAPPartAsString();
489     }
490   }
491 
492   /**
493    * Can be overridden by subclasses to customize
494    * how the wsa:to header is interpreted.
495    *
496    * @param ctx     Context information
497    * @param headers Header container
498    */
499   protected void setTargetService(MessageContext ctx, AddressingHeaders headers)
500     throws Exception {
501     To toURI = headers.getTo();
502     if (toURI == null) {
503       return;
504     }
505     String to = toURI.getPath();
506     if (to == null) {
507       return;
508     }
509     // set the target service
510     int i = to.lastIndexOf('/');
511     ctx.setTargetService(to.substring(i + 1));
512   }
513 
514   /**
515    * Method processServerResponse.
516    *
517    * @param ctx Context information
518    * @param setMustUnderstand Whether to set the attribute
519    * @throws Exception If an error occurs
520    */
521   protected void processServerResponse(MessageContext ctx,
522     boolean setMustUnderstand) throws Exception {
523     // if no response - do nothing
524     Message msg = ctx.getResponseMessage();
525     if (msg == null) {
526       return;
527     }
528 
529     AddressingVersion version = AddressingUtils.getAddressingVersion();
530 
531     AddressingHeaders reqHeaders = (AddressingHeaders) ctx
532     .getProperty(ENV_ADDRESSING_REQUEST_HEADERS);
533 
534     if (reqHeaders == null) {
535       // error?
536       return;
537     }
538 
539     AddressingHeaders resHeaders = AddressingUtils.getResponseHeaders(ctx);
540 
541     resHeaders.setSetMustUnderstand(setMustUnderstand);
542 
543     // set From
544     EndpointReference fromEPR = resHeaders.getFrom();
545     if (fromEPR == null) {
546       To toURI = reqHeaders.getTo();
547       if (toURI != null) {
548         fromEPR = new EndpointReference(toURI);
549         fromEPR.setProperties(reqHeaders.getReferenceProperties());
550         resHeaders.setFrom(fromEPR);
551       }
552     }
553 
554     // set Action
555     Action action = resHeaders.getAction();
556     if (action == null) {
557       String respAction = ctx.getStrProp(ENV_ADDRESSING_RESP_ACTION);
558       if (respAction == null) {
559         // not set - try request headers
560         action = reqHeaders.getAction();
561         if (action != null) {
562           URI actionURI = new URI(action.toString() + "Response",
563               allowNonSpecificActions);
564           resHeaders.setAction(new Action(actionURI));
565         }
566       } else {
567         resHeaders.setAction(new Action(new URI(respAction)));
568       }
569     }
570 
571     if (resHeaders.getFrom() == null && reqHeaders.getFrom() != null) {
572       resHeaders.setTo(reqHeaders.getFrom().getAddress());
573     } else {
574       resHeaders.setTo(new To(version.getAnonymousRoleURI()));
575     }
576 
577     // process RelatesTo
578     MessageID msgID = reqHeaders.getMessageID();
579     if (msgID != null) {
580       resHeaders.addRelatesTo(msgID.toString(), version.getResponseRelationshipType());
581     }
582 
583     // process MessageID
584     msgID = new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
585     resHeaders.setMessageID(msgID);
586 
587     // process ReplyTo
588     EndpointReferenceType replyTo = reqHeaders.getReplyTo();
589     if (replyTo != null) {
590       resHeaders.setReferenceProperties(replyTo.getProperties());
591       resHeaders.setReferenceParameters(replyTo.getParameters());
592 
593       AttributedURI address = replyTo.getAddress();
594       if (address != null) {
595         String uri = address.toString();
596         if (uri != null && !uri.equals(version.getAnonymousRoleURI())) {
597           if (!uri.equals(URI_NONE_W3C_CR)) {
598             // send the msg to reply to
599             forwardMessage(replyTo, msg);
600           }
601           // Nothing goes back out the HTTP connection.
602           ctx.setResponseMessage(null);
603         } else {
604           resHeaders.toEnvelope(msg.getSOAPEnvelope(), this.actor);
605         }
606       }
607     } else {
608       resHeaders.toEnvelope(msg.getSOAPEnvelope(), this.actor);
609     }
610   }
611 
612   /**
613    * Forwards the message to the given EPR.
614    *
615    * @param epr Destination EPR
616    * @param msg Message to forward
617    * @throws Exception If an error occurs
618    */
619   protected void forwardMessage(EndpointReferenceType epr, Message msg)
620     throws Exception {
621     AttributedURI address = epr.getAddress();
622 
623     AddressingHeaders headers = null;
624     MessageContext ctx = msg.getMessageContext();
625     if (ctx != null) {
626       headers = (AddressingHeaders) ctx
627       .getProperty(ENV_ADDRESSING_RESPONSE_HEADERS);
628     }
629     if (headers == null) {
630       headers = new AddressingHeaders();
631       headers.setTo(address);
632       headers.setReferenceProperties(epr.getProperties());
633       headers.setReferenceParameters(epr.getParameters());
634     }
635 
636     Service service = getService(ctx);
637 
638     Call c = (Call) service.createCall();
639     c.setSOAPVersion((ctx == null)
640       ? org.apache.axis.Constants.DEFAULT_SOAP_VERSION
641       : ctx.getSOAPConstants());
642     c.setTargetEndpointAddress(address.toString());
643     c.setRequestMessage(msg);
644     c.setProperty(ENV_ADDRESSING_REQUEST_HEADERS, headers);
645     c.setProperty(ENV_ADDRESSING_NAMESPACE_URI, AddressingUtils
646         .getAddressingNamespaceURI(ctx));
647     if (ctx != null) {
648       c.setProperty(AxisEngine.PROP_SEND_XSI, ctx
649           .getProperty(AxisEngine.PROP_SEND_XSI));
650       c.setOperationStyle(ctx.getOperationStyle());
651       c.setOperationUse(ctx.getOperationUse());
652     }
653     configureCall(c, ctx);
654     try {
655       c.invoke();
656     } catch (AxisFault axisFault) {
657       // This fault shouldn't necessarily become the response of
658       // the original interaction, should it?  Should we be doing
659       // this on a separate thread?
660       LOG.error(axisFault.dumpToString());
661     }
662   }
663 
664   /**
665    * Override this method if you need something other than the default Service.<br>
666    * The service returned by this method is used in creating the new Call object.
667    * Something like: <br>
668    * <pre>
669    * Service service = getService(msgContext);
670    * Call call = service.createCall()
671    * call.setTargetEndpointAddress(toEndPointReference.getAddress().toString());
672    * </pre>
673    *
674    * @param ctx Context information
675    * @return A service instance
676    */
677   protected Service getService(MessageContext ctx) {
678     return new Service();
679   }
680 
681   /**
682    * Override this method to prepare the new call, for instance to add
683    * properties from the old MessageContext that may be needed by other
684    * handlers.
685    *
686    * @param call Call object about to be invoked
687    * @param oldContext MessageContext of the original request/response.
688    */
689   protected void configureCall(Call call, MessageContext oldContext) {
690   }
691 
692   /**
693    * Retrieve QNames for reference properties from deployment
694    * and initialize the refPropQNames list which is the list
695    * of reference properties that this handler should care
696    * about.
697    */
698   private void initializeReferencePropertyNames() {
699     // check if the user wants to process all headers, this means
700     // the user wants to treat non ws-addr headers as reference props
701     // If this option is not enabled, the non ws-addr headers will not
702     // be deserialized.
703     String refPropNames = (String) getOption("referencePropertyNames");
704     if (refPropNames == null) {
705       this.refPropQNames = new ArrayList<QName>();
706     } else if (refPropNames.equals("*")) {
707       this.refPropQNames = null;
708     } else {
709       this.refPropQNames = new ArrayList<QName>();
710       StringTokenizer tkn = new StringTokenizer(refPropNames, ",");
711       while (tkn.hasMoreTokens()) {
712         String qnameString = tkn.nextToken().trim();
713         try {
714           QName qname = QName.valueOf(qnameString);
715           refPropQNames.add(qname);
716         } catch (Exception e) {
717           // Ignore QNames which were written incorrectly
718           // if their parsing results in errors
719           LOG.debug("Invalid QName ignored: " + qnameString);
720         }
721       }
722     }
723   }
724 
725 }