001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2;
027:
028: import java.io.BufferedReader;
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.InputStreamReader;
032: import java.util.Collection;
033: import java.util.Collections;
034: import java.util.HashMap;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.StringTokenizer;
038:
039: import javax.xml.bind.JAXBContext;
040: import javax.xml.bind.JAXBException;
041:
042: import com.sun.istack.internal.FinalArrayList;
043: import com.sun.xml.internal.bind.api.JAXBRIContext;
044: import com.sun.xml.internal.bind.api.TypeReference;
045: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
046:
047: /**
048: * This class is responsible for producing RI JAXBContext objects. In
049: * the RI, this is the class that the javax.xml.bind.context.factory
050: * property will point to.
051: *
052: * <p>
053: * Used to create JAXBContext objects for v1.0.1 and forward
054: *
055: * @since 2.0
056: * @author Kohsuke Kawaguchi
057: */
058: public class ContextFactory {
059: /**
060: * The API will invoke this method via reflection
061: */
062: public static JAXBContext createContext(Class[] classes,
063: Map<String, Object> properties) throws JAXBException {
064: // fool-proof check, and copy the map to make it easier to find unrecognized properties.
065: if (properties == null)
066: properties = Collections.emptyMap();
067: else
068: properties = new HashMap<String, Object>(properties);
069:
070: String defaultNsUri = getPropertyValue(properties,
071: JAXBRIContext.DEFAULT_NAMESPACE_REMAP, String.class);
072:
073: Boolean c14nSupport = getPropertyValue(properties,
074: JAXBRIContext.CANONICALIZATION_SUPPORT, Boolean.class);
075: if (c14nSupport == null)
076: c14nSupport = false;
077:
078: if (!properties.isEmpty()) {
079: throw new JAXBException(Messages.UNSUPPORTED_PROPERTY
080: .format(properties.keySet().iterator().next()));
081: }
082:
083: return createContext(classes, Collections
084: .<TypeReference> emptyList(), defaultNsUri, c14nSupport);
085: }
086:
087: /**
088: * If a key is present in the map, remove the value and return it.
089: */
090: private static <T> T getPropertyValue(
091: Map<String, Object> properties, String keyName,
092: Class<T> type) throws JAXBException {
093: Object o = properties.get(keyName);
094: if (o == null)
095: return null;
096:
097: properties.remove(keyName);
098: if (!type.isInstance(o))
099: throw new JAXBException(Messages.INVALID_PROPERTY_VALUE
100: .format(keyName, o));
101: else
102: return type.cast(o);
103: }
104:
105: /**
106: * Used from the JAXB RI runtime API, invoked via reflection.
107: */
108: public static JAXBContext createContext(Class[] classes,
109: Collection<TypeReference> typeRefs, String defaultNsUri,
110: boolean c14nSupport) throws JAXBException {
111: return new JAXBContextImpl(classes, typeRefs, defaultNsUri,
112: c14nSupport);
113: }
114:
115: /**
116: * The API will invoke this method via reflection.
117: */
118: public static JAXBContext createContext(String contextPath,
119: ClassLoader classLoader, Map<String, Object> properties)
120: throws JAXBException {
121: FinalArrayList<Class> classes = new FinalArrayList<Class>();
122: StringTokenizer tokens = new StringTokenizer(contextPath, ":");
123: List<Class> indexedClasses;
124:
125: // at least on of these must be true per package
126: boolean foundObjectFactory;
127: boolean foundJaxbIndex;
128:
129: while (tokens.hasMoreTokens()) {
130: foundObjectFactory = foundJaxbIndex = false;
131: String pkg = tokens.nextToken();
132:
133: // look for ObjectFactory and load it
134: final Class<?> o;
135: try {
136: o = classLoader.loadClass(pkg + ".ObjectFactory");
137: classes.add(o);
138: foundObjectFactory = true;
139: } catch (ClassNotFoundException e) {
140: // not necessarily an error
141: }
142:
143: // look for jaxb.index and load the list of classes
144: try {
145: indexedClasses = loadIndexedClasses(pkg, classLoader);
146: } catch (IOException e) {
147: //TODO: think about this more
148: throw new JAXBException(e);
149: }
150: if (indexedClasses != null) {
151: classes.addAll(indexedClasses);
152: foundJaxbIndex = true;
153: }
154:
155: if (!(foundObjectFactory || foundJaxbIndex)) {
156: throw new JAXBException(Messages.BROKEN_CONTEXTPATH
157: .format(pkg));
158: }
159: }
160:
161: return createContext(
162: classes.toArray(new Class[classes.size()]), properties);
163: }
164:
165: /**
166: * Look for jaxb.index file in the specified package and load it's contents
167: *
168: * @param pkg package name to search in
169: * @param classLoader ClassLoader to search in
170: * @return a List of Class objects to load, null if there weren't any
171: * @throws IOException if there is an error reading the index file
172: * @throws JAXBException if there are any errors in the index file
173: */
174: private static List<Class> loadIndexedClasses(String pkg,
175: ClassLoader classLoader) throws IOException, JAXBException {
176: final String resource = pkg.replace('.', '/') + "/jaxb.index";
177: final InputStream resourceAsStream = classLoader
178: .getResourceAsStream(resource);
179:
180: if (resourceAsStream == null) {
181: return null;
182: }
183:
184: BufferedReader in = new BufferedReader(new InputStreamReader(
185: resourceAsStream, "UTF-8"));
186: try {
187: FinalArrayList<Class> classes = new FinalArrayList<Class>();
188: String className = in.readLine();
189: while (className != null) {
190: className = className.trim();
191: if (className.startsWith("#")
192: || (className.length() == 0)) {
193: className = in.readLine();
194: continue;
195: }
196:
197: if (className.endsWith(".class")) {
198: throw new JAXBException(Messages.ILLEGAL_ENTRY
199: .format(className));
200: }
201:
202: try {
203: classes.add(classLoader.loadClass(pkg + '.'
204: + className));
205: } catch (ClassNotFoundException e) {
206: throw new JAXBException(
207: Messages.ERROR_LOADING_CLASS.format(
208: className, resource), e);
209: }
210:
211: className = in.readLine();
212: }
213: return classes;
214: } finally {
215: in.close();
216: }
217: }
218:
219: public static final String USE_JAXB_PROPERTIES = "_useJAXBProperties";
220: }
|