src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/unmarshaller/UnmarshallingContext.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -33,10 +33,12 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.xml.XMLConstants;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.UnmarshalException;
 import javax.xml.bind.Unmarshaller;

@@ -49,18 +51,21 @@
 
 import com.sun.istack.internal.NotNull;
 import com.sun.istack.internal.Nullable;
 import com.sun.istack.internal.SAXParseException2;
 import com.sun.xml.internal.bind.IDResolver;
+import com.sun.xml.internal.bind.Util;
 import com.sun.xml.internal.bind.api.AccessorException;
 import com.sun.xml.internal.bind.api.ClassResolver;
 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
 import com.sun.xml.internal.bind.v2.ClassFactory;
 import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
 import com.sun.xml.internal.bind.v2.runtime.Coordinator;
 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.LocatorImpl;
 

@@ -74,10 +79,12 @@
  * @author Kohsuke Kawaguchi
  */
 public final class UnmarshallingContext extends Coordinator
     implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor {
 
+    private static final Logger logger = Logger.getLogger(UnmarshallingContext.class.getName());
+
     /**
      * Root state.
      */
     private final State root;
 

@@ -175,10 +182,18 @@
      * For backward compatibility, when null, use thread context classloader.
      */
     public @Nullable ClassLoader classLoader;
 
     /**
+     * The variable introduced to avoid reporting n^10 similar errors.
+     * After error is reported counter is decremented. When it became 0 - errors should not be reported any more.
+     *
+     * volatile is required to ensure that concurrent threads will see changed value
+     */
+    private static volatile int errorsCounter = 10;
+
+    /**
      * State information for each element.
      */
     public final class State {
         /**
          * Loader that owns this element.

@@ -258,25 +273,36 @@
          */
         public UnmarshallingContext getContext() {
             return UnmarshallingContext.this;
         }
 
+        @SuppressWarnings("LeakingThisInConstructor")
         private State(State prev) {
             this.prev = prev;
-            if(prev!=null)
+            if (prev!=null) {
                 prev.next = this;
         }
+        }
 
         private void push() {
-            if(next==null)
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST, "State.push");
+            }
+            if (next==null) {
+                assert current == this;
                 allocateMoreStates();
+            }
+            nil = false;
             State n = next;
             n.numNsDecl = nsLen;
             current = n;
         }
 
         private void pop() {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST, "State.pop");
+            }
             assert prev!=null;
             loader = null;
             nil = false;
             receiver = null;
             intercepter = null;

@@ -379,13 +405,14 @@
     private void allocateMoreStates() {
         // this method should be used only when we run out of a state.
         assert current.next==null;
 
         State s = current;
-        for( int i=0; i<8; i++ )
+        for (int i=0; i<8; i++) {
             s = new State(s);
     }
+    }
 
     public void clearStates() {
         State last = current;
         while (last.next != null) last = last.next;
         while (last.prev != null) {

@@ -434,10 +461,11 @@
 
             factories.put(type,new Factory(factory,m));
         }
     }
 
+    @Override
     public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException {
         if(locator!=null)
             this.locator = locator;
         this.environmentNamespaceContext = nsContext;
         // reset the object

@@ -447,20 +475,19 @@
         patchersLen=0;
         aborted = false;
         isUnmarshalInProgress = true;
         nsLen=0;
 
-        setThreadAffinity();
-
         if(expectedType!=null)
             root.loader = EXPECTED_TYPE_ROOT_LOADER;
         else
             root.loader = DEFAULT_ROOT_LOADER;
 
         idResolver.startDocument(this);
     }
 
+    @Override
     public void startElement(TagName tagName) throws SAXException {
         pushCoordinator();
         try {
             _startElement(tagName);
         } finally {

@@ -484,10 +511,11 @@
         assert current.loader!=null;   // the childElement should register this
         // and tell the new child that you are activated
         current.loader.startElement(current,tagName);
     }
 
+    @Override
     public void text(CharSequence pcdata) throws SAXException {
         State cur = current;
         pushCoordinator();
         try {
             if(cur.elementDefaultValue!=null) {

@@ -500,10 +528,11 @@
         } finally {
             popCoordinator();
         }
     }
 
+    @Override
     public final void endElement(TagName tagName) throws SAXException {
         pushCoordinator();
         try {
             State child = current;
 

@@ -524,10 +553,11 @@
         } finally {
             popCoordinator();
         }
     }
 
+    @Override
     public void endDocument() throws SAXException {
         runPatchers();
         idResolver.endDocument();
 
         isUnmarshalInProgress = false;

@@ -535,30 +565,31 @@
         locator = DUMMY_INSTANCE;
         environmentNamespaceContext = null;
 
         // at the successful completion, scope must be all closed
         assert root==current;
-
-        resetThreadAffinity();
     }
 
     /**
      * You should be always calling this through {@link TextPredictor}.
      */
     @Deprecated
+    @Override
     public boolean expectText() {
         return current.loader.expectText;
     }
 
     /**
      * You should be always getting {@link TextPredictor} from {@link XmlVisitor}.
      */
     @Deprecated
+    @Override
     public TextPredictor getPredictor() {
         return this;
     }
 
+    @Override
     public UnmarshallingContext getContext() {
         return this;
     }
 
     /**

@@ -648,10 +679,11 @@
                 new UnmarshalException(
                     event.getMessage(),
                     event.getLinkedException() ) );
     }
 
+    @Override
     public boolean handleEvent(ValidationEvent event) {
         try {
             // if the handler says "abort", we will not return the object.
             boolean recover = parent.getEventHandler().handleEvent(event);
             if(!recover)    aborted = true;

@@ -678,10 +710,11 @@
 
     public void handleError(String msg) {
         handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation()));
     }
 
+    @Override
     protected ValidationEventLocator getLocation() {
         return locator.getLocation();
     }
 
     /**

@@ -799,20 +832,22 @@
 //
 //
     private String[] nsBind = new String[16];
     private int nsLen=0;
 
+    @Override
     public void startPrefixMapping( String prefix, String uri ) {
         if(nsBind.length==nsLen) {
             // expand the buffer
             String[] n = new String[nsLen*2];
             System.arraycopy(nsBind,0,n,0,nsLen);
             nsBind=n;
         }
         nsBind[nsLen++] = prefix;
         nsBind[nsLen++] = uri;
     }
+    @Override
     public void endPrefixMapping( String prefix ) {
         nsLen-=2;
     }
     private String resolveNamespacePrefix( String prefix ) {
         if(prefix.equals("xml"))

@@ -866,10 +901,11 @@
         return r;
     }
 
     //  NamespaceContext2 implementation
     //
+    @Override
     public Iterator<String> getPrefixes(String uri) {
         // TODO: could be implemented much faster
         // wrap it into unmodifiable list so that the remove method
         // will throw UnsupportedOperationException.
         return Collections.unmodifiableList(

@@ -897,10 +933,11 @@
                     a.add(nsBind[i]);
 
         return a;
     }
 
+    @Override
     public String getPrefix(String uri) {
         if( uri==null )
             throw new IllegalArgumentException();
         if( uri.equals(XMLConstants.XML_NS_URI) )
             return XMLConstants.XML_NS_PREFIX;

@@ -917,10 +954,11 @@
             return environmentNamespaceContext.getPrefix(uri);
 
         return null;
     }
 
+    @Override
     public String getNamespaceURI(String prefix) {
         if (prefix == null)
             throw new IllegalArgumentException();
         if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE))
             return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;

@@ -1057,10 +1095,11 @@
         @Override
         public Collection<QName> getExpectedChildElements() {
             return getInstance().getJAXBContext().getValidRootNames();
         }
 
+        @Override
         public void receive(State state, Object o) {
              if(state.backup!=null) {
                 ((JAXBElement<Object>)state.backup).setValue(o);
                 o = state.backup;
             }

@@ -1093,10 +1132,11 @@
             // which adds the resident memory footprint. Since XsiNilLoader is small,
             // I intentionally allocate a new instance freshly.
             state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true));
         }
 
+        @Override
         public void receive(State state, Object o) {
             JAXBElement e = (JAXBElement)state.target;
             e.setValue(o);
             state.getContext().recordOuterPeer(e);
             state.getContext().result = e;

@@ -1231,6 +1271,29 @@
             return (StructureLoader)current.loader;
 
         return null;
     }
 
+    /**
+     * Based on current {@link Logger} {@link Level} and errorCounter value determines if error should be reported.
+     *
+     * If the method called and return true it is expected that error will be reported. And that's why
+     * errorCounter is automatically decremented during the check.
+     *
+     * NOT THREAD SAFE!!! In case of heave concurrency access several additional errors could be reported. It's not expected to be the
+     * problem. Otherwise add synchronization here.
+     *
+     * @return true in case if {@link Level#FINEST} is set OR we haven't exceed errors reporting limit.
+     */
+    public boolean shouldErrorBeReported() throws SAXException {
+        if (logger.isLoggable(Level.FINEST))
+            return true;
+
+        if (errorsCounter >= 0) {
+            --errorsCounter;
+            if (errorsCounter == 0) // it's possible to miss this because of concurrency. If required add synchronization here
+                handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, Messages.ERRORS_LIMIT_EXCEEDED.format(),
+                        getLocator().getLocation(), null), true);
+        }
+        return errorsCounter >= 0;
+    }
 }