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;
+ }
}