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;
038:
039: import java.io.BufferedReader;
040: import java.io.IOException;
041: import java.io.InputStream;
042: import java.io.InputStreamReader;
043: import java.util.Collection;
044: import java.util.Collections;
045: import java.util.HashMap;
046: import java.util.List;
047: import java.util.Map;
048: import java.util.StringTokenizer;
049: import java.util.logging.Level;
050:
051: import javax.xml.bind.JAXBContext;
052: import javax.xml.bind.JAXBException;
053:
054: import com.sun.istack.FinalArrayList;
055: import com.sun.xml.bind.Util;
056: import com.sun.xml.bind.api.JAXBRIContext;
057: import com.sun.xml.bind.api.TypeReference;
058: import com.sun.xml.bind.v2.model.annotation.RuntimeAnnotationReader;
059: import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
060: import com.sun.xml.bind.v2.util.TypeCast;
061:
062: /**
063: * This class is responsible for producing RI JAXBContext objects. In
064: * the RI, this is the class that the javax.xml.bind.context.factory
065: * property will point to.
066: *
067: * <p>
068: * Used to create JAXBContext objects for v1.0.1 and forward
069: *
070: * @since 2.0
071: * @author Kohsuke Kawaguchi
072: */
073: public class ContextFactory {
074: /**
075: * The API will invoke this method via reflection
076: */
077: public static JAXBContext createContext(Class[] classes,
078: Map<String, Object> properties) throws JAXBException {
079: // fool-proof check, and copy the map to make it easier to find unrecognized properties.
080: if (properties == null)
081: properties = Collections.emptyMap();
082: else
083: properties = new HashMap<String, Object>(properties);
084:
085: String defaultNsUri = getPropertyValue(properties,
086: JAXBRIContext.DEFAULT_NAMESPACE_REMAP, String.class);
087:
088: Boolean c14nSupport = getPropertyValue(properties,
089: JAXBRIContext.CANONICALIZATION_SUPPORT, Boolean.class);
090: if (c14nSupport == null)
091: c14nSupport = false;
092:
093: Boolean allNillable = getPropertyValue(properties,
094: JAXBRIContext.TREAT_EVERYTHING_NILLABLE, Boolean.class);
095: if (allNillable == null)
096: allNillable = false;
097:
098: Boolean xmlAccessorFactorySupport = getPropertyValue(
099: properties, JAXBRIContext.XMLACCESSORFACTORY_SUPPORT,
100: Boolean.class);
101: if (xmlAccessorFactorySupport == null) {
102: xmlAccessorFactorySupport = false;
103: Util
104: .getClassLogger()
105: .log(
106: Level.FINE,
107: "Property "
108: + JAXBRIContext.XMLACCESSORFACTORY_SUPPORT
109: + "is not active. Using JAXB's implementation");
110: }
111:
112: RuntimeAnnotationReader ar = getPropertyValue(properties,
113: JAXBRIContext.ANNOTATION_READER,
114: RuntimeAnnotationReader.class);
115:
116: Map<Class, Class> subclassReplacements;
117: try {
118: subclassReplacements = TypeCast.checkedCast(
119: getPropertyValue(properties,
120: JAXBRIContext.SUBCLASS_REPLACEMENTS,
121: Map.class), Class.class, Class.class);
122: } catch (ClassCastException e) {
123: throw new JAXBException(Messages.INVALID_TYPE_IN_MAP
124: .format(), e);
125: }
126:
127: if (!properties.isEmpty()) {
128: throw new JAXBException(Messages.UNSUPPORTED_PROPERTY
129: .format(properties.keySet().iterator().next()));
130: }
131:
132: return createContext(classes, Collections
133: .<TypeReference> emptyList(), subclassReplacements,
134: defaultNsUri, c14nSupport, ar,
135: xmlAccessorFactorySupport, allNillable);
136: }
137:
138: /**
139: * If a key is present in the map, remove the value and return it.
140: */
141: private static <T> T getPropertyValue(
142: Map<String, Object> properties, String keyName,
143: Class<T> type) throws JAXBException {
144: Object o = properties.get(keyName);
145: if (o == null)
146: return null;
147:
148: properties.remove(keyName);
149: if (!type.isInstance(o))
150: throw new JAXBException(Messages.INVALID_PROPERTY_VALUE
151: .format(keyName, o));
152: else
153: return type.cast(o);
154: }
155:
156: public static JAXBRIContext createContext(Class[] classes,
157: Collection<TypeReference> typeRefs,
158: Map<Class, Class> subclassReplacements,
159: String defaultNsUri, boolean c14nSupport,
160: RuntimeAnnotationReader ar,
161: boolean xmlAccessorFactorySupport, boolean allNillable)
162: throws JAXBException {
163: return new JAXBContextImpl(classes, typeRefs,
164: subclassReplacements, defaultNsUri, c14nSupport, ar,
165: xmlAccessorFactorySupport, allNillable);
166: }
167:
168: /**
169: * The API will invoke this method via reflection.
170: */
171: public static JAXBContext createContext(String contextPath,
172: ClassLoader classLoader, Map<String, Object> properties)
173: throws JAXBException {
174: FinalArrayList<Class> classes = new FinalArrayList<Class>();
175: StringTokenizer tokens = new StringTokenizer(contextPath, ":");
176: List<Class> indexedClasses;
177:
178: // at least on of these must be true per package
179: boolean foundObjectFactory;
180: boolean foundJaxbIndex;
181:
182: while (tokens.hasMoreTokens()) {
183: foundObjectFactory = foundJaxbIndex = false;
184: String pkg = tokens.nextToken();
185:
186: // look for ObjectFactory and load it
187: final Class<?> o;
188: try {
189: o = classLoader.loadClass(pkg + ".ObjectFactory");
190: classes.add(o);
191: foundObjectFactory = true;
192: } catch (ClassNotFoundException e) {
193: // not necessarily an error
194: }
195:
196: // look for jaxb.index and load the list of classes
197: try {
198: indexedClasses = loadIndexedClasses(pkg, classLoader);
199: } catch (IOException e) {
200: //TODO: think about this more
201: throw new JAXBException(e);
202: }
203: if (indexedClasses != null) {
204: classes.addAll(indexedClasses);
205: foundJaxbIndex = true;
206: }
207:
208: if (!(foundObjectFactory || foundJaxbIndex)) {
209: throw new JAXBException(Messages.BROKEN_CONTEXTPATH
210: .format(pkg));
211: }
212: }
213:
214: return createContext(
215: classes.toArray(new Class[classes.size()]), properties);
216: }
217:
218: /**
219: * Look for jaxb.index file in the specified package and load it's contents
220: *
221: * @param pkg package name to search in
222: * @param classLoader ClassLoader to search in
223: * @return a List of Class objects to load, null if there weren't any
224: * @throws IOException if there is an error reading the index file
225: * @throws JAXBException if there are any errors in the index file
226: */
227: private static List<Class> loadIndexedClasses(String pkg,
228: ClassLoader classLoader) throws IOException, JAXBException {
229: final String resource = pkg.replace('.', '/') + "/jaxb.index";
230: final InputStream resourceAsStream = classLoader
231: .getResourceAsStream(resource);
232:
233: if (resourceAsStream == null) {
234: return null;
235: }
236:
237: BufferedReader in = new BufferedReader(new InputStreamReader(
238: resourceAsStream, "UTF-8"));
239: try {
240: FinalArrayList<Class> classes = new FinalArrayList<Class>();
241: String className = in.readLine();
242: while (className != null) {
243: className = className.trim();
244: if (className.startsWith("#")
245: || (className.length() == 0)) {
246: className = in.readLine();
247: continue;
248: }
249:
250: if (className.endsWith(".class")) {
251: throw new JAXBException(Messages.ILLEGAL_ENTRY
252: .format(className));
253: }
254:
255: try {
256: classes.add(classLoader.loadClass(pkg + '.'
257: + className));
258: } catch (ClassNotFoundException e) {
259: throw new JAXBException(
260: Messages.ERROR_LOADING_CLASS.format(
261: className, resource), e);
262: }
263:
264: className = in.readLine();
265: }
266: return classes;
267: } finally {
268: in.close();
269: }
270: }
271:
272: public static final String USE_JAXB_PROPERTIES = "_useJAXBProperties";
273: }
|