001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.runtime;
038:
039: import java.util.HashMap;
040:
041: import javax.xml.bind.ValidationEvent;
042: import javax.xml.bind.ValidationEventHandler;
043: import javax.xml.bind.ValidationEventLocator;
044: import javax.xml.bind.annotation.adapters.XmlAdapter;
045: import javax.xml.bind.helpers.ValidationEventImpl;
046:
047: import com.sun.xml.bind.v2.ClassFactory;
048: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
049:
050: import org.xml.sax.ErrorHandler;
051: import org.xml.sax.SAXException;
052: import org.xml.sax.SAXParseException;
053:
054: /**
055: * Object that coordinates the marshalling/unmarshalling.
056: *
057: * <p>
058: * This class takes care of the logic that allows code to obtain
059: * {@link UnmarshallingContext} and {@link XMLSerializer} instances
060: * during the unmarshalling/marshalling.
061: *
062: * <p>
063: * This is done by using a {@link ThreadLocal}. Therefore one unmarshalling/marshalling
064: * episode has to be done from the beginning till end by the same thread.
065: * (Note that the same {@link Coordinator} can be then used by a different thread
066: * for an entirely different episode.)
067: *
068: * This class also maintains the user-configured instances of {@link XmlAdapter}s.
069: *
070: * <p>
071: * This class implements {@link ErrorHandler} and propages erros to this object
072: * as the {@link ValidationEventHandler}, which will be implemented in a derived class.
073: *
074: * @author Kohsuke Kawaguchi
075: */
076: public abstract class Coordinator implements ErrorHandler,
077: ValidationEventHandler {
078:
079: private final HashMap<Class<? extends XmlAdapter>, XmlAdapter> adapters = new HashMap<Class<? extends XmlAdapter>, XmlAdapter>();
080:
081: public final XmlAdapter putAdapter(Class<? extends XmlAdapter> c,
082: XmlAdapter a) {
083: if (a == null)
084: return adapters.remove(c);
085: else
086: return adapters.put(c, a);
087: }
088:
089: /**
090: * Gets the instance of the adapter.
091: *
092: * @return
093: * always non-null.
094: */
095: public final <T extends XmlAdapter> T getAdapter(Class<T> key) {
096: T v = key.cast(adapters.get(key));
097: if (v == null) {
098: v = ClassFactory.create(key);
099: putAdapter(key, v);
100: }
101: return v;
102: }
103:
104: public <T extends XmlAdapter> boolean containsAdapter(Class<T> type) {
105: return adapters.containsKey(type);
106: }
107:
108: /**
109: * The {@link Coordinator} in charge before this {@link Coordinator}.
110: */
111: private Coordinator old;
112:
113: /**
114: * A 'pointer' to a {@link Coordinator} that keeps track of the currently active {@link Coordinator}.
115: * Having this improves the runtime performance.
116: */
117: private Coordinator[] table;
118:
119: /**
120: * When we set {@link #table} to null, record who did it.
121: * This is for trouble-shooting a possible concurrency issue reported at:
122: * http://forums.java.net/jive/thread.jspa?threadID=15132
123: */
124: public Exception guyWhoSetTheTableToNull;
125:
126: /**
127: * Associates this {@link Coordinator} with the current thread.
128: * Should be called at the very beginning of the episode.
129: */
130: protected final void setThreadAffinity() {
131: table = activeTable.get();
132: assert table != null;
133: }
134:
135: /**
136: * Dis-associate this {@link Coordinator} with the current thread.
137: * Sohuld be called at the end of the episode to avoid memory leak.
138: */
139: protected final void resetThreadAffinity() {
140: if (debugTableNPE)
141: guyWhoSetTheTableToNull = new Exception(); // remember that we set it to null
142: table = null;
143: }
144:
145: /**
146: * Called whenever an execution flow enters the realm of this {@link Coordinator}.
147: */
148: protected final void pushCoordinator() {
149: old = table[0];
150: table[0] = this ;
151: }
152:
153: /**
154: * Called whenever an execution flow exits the realm of this {@link Coordinator}.
155: */
156: protected final void popCoordinator() {
157: assert table[0] == this ;
158: table[0] = old;
159: old = null; // avoid memory leak
160: }
161:
162: public static Coordinator _getInstance() {
163: return activeTable.get()[0];
164: }
165:
166: // this much is necessary to avoid calling get and set twice when we push.
167: private static final ThreadLocal<Coordinator[]> activeTable = new ThreadLocal<Coordinator[]>() {
168: public Coordinator[] initialValue() {
169: return new Coordinator[1];
170: }
171: };
172:
173: //
174: //
175: // ErrorHandler implementation
176: //
177: //
178: /**
179: * Gets the current location. Used for reporting the error source location.
180: */
181: protected abstract ValidationEventLocator getLocation();
182:
183: public final void error(SAXParseException exception)
184: throws SAXException {
185: propagateEvent(ValidationEvent.ERROR, exception);
186: }
187:
188: public final void warning(SAXParseException exception)
189: throws SAXException {
190: propagateEvent(ValidationEvent.WARNING, exception);
191: }
192:
193: public final void fatalError(SAXParseException exception)
194: throws SAXException {
195: propagateEvent(ValidationEvent.FATAL_ERROR, exception);
196: }
197:
198: private void propagateEvent(int severity,
199: SAXParseException saxException) throws SAXException {
200:
201: ValidationEventImpl ve = new ValidationEventImpl(severity,
202: saxException.getMessage(), getLocation());
203:
204: Exception e = saxException.getException();
205: if (e != null) {
206: ve.setLinkedException(e);
207: } else {
208: ve.setLinkedException(saxException);
209: }
210:
211: // call the client's event handler. If it returns false, then bail-out
212: // and terminate the unmarshal operation.
213: boolean result = handleEvent(ve);
214: if (!result) {
215: // bail-out of the parse with a SAX exception, but convert it into
216: // an UnmarshalException back in in the AbstractUnmarshaller
217: throw saxException;
218: }
219: }
220:
221: public static boolean debugTableNPE;
222:
223: static {
224: try {
225: debugTableNPE = Boolean.getBoolean(Coordinator.class
226: .getName()
227: + ".debugTableNPE");
228: } catch (SecurityException t) {
229: }
230: }
231: }
|