Source code: javax/xml/bind/JAXBContext.java
1 /*
2 * Copyright 2003, 2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15
16 */
17 package javax.xml.bind;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.net.URL;
25 import java.util.Properties;
26 import java.util.StringTokenizer;
27
28
29 /** <p>The <code>JAXBContext</code> provides the JAXB users anchor to
30 * the implmentation and hos generated classes. A JAXBContext is used to
31 * obtain instances of {@link javax.xml.bind.Marshaller},
32 * {@link javax.xml.bind.Unmarshaller}, and
33 * {@link javax.xml.bind.Validator}. To obtain a JAXBContext, the
34 * application invokes
35 * <pre>
36 * JAXBContext context = JAXBContext.newInstance("com.mycompany:com.mycompany.xml");
37 * </pre>
38 * The list of colon separated package names matches the list in the
39 * schemas used to generate classes. In other words: If you have a
40 * schema using package name "com.mycompany.xml", then this package
41 * name has to be part of the list.</p>
42 * <p>The <code>JAXBContext</code> class will scan the given list of packages
43 * for a file called <samp>jaxb.properties</samp>. This file contains the
44 * name of an instantiation class in the property
45 * {@link #JAXB_CONTEXT_FACTORY}. (See {@link #newInstance(String)} for
46 * details on how the class name is obtained.) Once such a file is found, the
47 * given class is loaded via {@link ClassLoader#loadClass(java.lang.String)}.
48 * The <code>JAXBContext</code> class demands, that the created object
49 * has a method
50 * <pre>
51 * public static JAXBContext createContext(String pPath, ClassLoader pClassLoader)
52 * throws JAXBException;
53 * </pre>
54 * This method is invoked with the same path and {@link ClassLoader} than
55 * above. See {@link #newInstance(String,ClassLoader)}} for details on the choice
56 * of the {@link ClassLoader}.</p>
57 * <p>The created context will scan the same package path for implementation
58 * specific configuration details (in the case of the <code>JaxMe</code>
59 * application a file called <samp>Configuration.xml</samp> in any of the
60 * packages) and do whatever else is required to initialize the runtime.
61 * In particular it will invoke
62 * {@link DatatypeConverter#setDatatypeConverter(DatatypeConverterInterface)}.</p>
63 *
64 * @author JSR-31
65 * @since JAXB1.0
66 * @see Marshaller
67 * @see Unmarshaller
68 * @see Validator
69 */
70 public abstract class JAXBContext {
71 private static final Class[] CONTEXT_CLASSES = new Class[]{String.class, ClassLoader.class};
72
73 /** <p>This is the name of the property used to determine the name
74 * of the initialization class: "javax.xml.bind.context.factory".
75 * The name is used by {@link #newInstance(String)} and
76 * {@link #newInstance(String,ClassLoader)}. It contains a class
77 * name. The class must contain a static method
78 * <pre>
79 * public static JAXBContext createContext(String, ClassLoader) throws JAXBException;
80 * </pre>
81 * which is invoked to create the actual instance of JAXBContext.</p>
82 */
83 public static final java.lang.String JAXB_CONTEXT_FACTORY = "javax.xml.bind.context.factory";
84
85 /** <p>Creates a new instance of <code>JAXBContext</code> by applying
86 * the following algorithm:
87 * <ol>
88 * <li>The first step is to determine the name of an initialization class.
89 * For any of the package names in the list given by
90 * <code>pPath</code> the <code>JAXBContext</code> class will try to find a file
91 * called <samp>jaxb.properties</samp>. This file's got to be in
92 * standard property file format. The <code>JAXBContext</code> class
93 * will load the property file.</li>
94 * <li>A property called "javax.xml.bind.context.factory" (see
95 * {@link #JAXB_CONTEXT_FACTORY}) is evaluated. It must contain the
96 * name of an initialization class. The initialization class is
97 * loaded via
98 * <code>Thread.currentThread().getContextClassLoader().loadClass(String)</code>.</li>
99 * <li>The initialization class must contain a method
100 * <pre>
101 * public static JAXBContext createContext(String, ClassLoader) throws JAXBException;
102 * </pre>
103 * which is invoked with the <code>pPath</code> argument and the
104 * {@link ClassLoader} of the <code>JAXBContext</class> class as
105 * parameters. The result of this method is also used as the
106 * result of the <code>newInstance(String)</code> method.</li>
107 * </ol>
108 * @param pPath A colon separated path of package names where to look for
109 * <samp>jaxb.properties</samp> files. The package names must match the
110 * generated classes which you are going to use in your application.
111 * @return An initialized instance of <code>JAXBContext</code>.
112 * @throws JAXBException An error occurred while creating the JAXBContext instance.
113 */
114 public static JAXBContext newInstance(java.lang.String pPath) throws JAXBException {
115 ClassLoader cl = Thread.currentThread().getContextClassLoader();
116 if (cl == null) {
117 cl = JAXBContext.class.getClassLoader();
118 }
119 return newInstance(pPath, cl);
120 }
121
122 /** <p>Creates a new instance of <code>JAXBContext</code> by applying
123 * the following algorithm:
124 * <ol>
125 * <li>The first step is to determine the name of an initialization class.
126 * For any of the package names in the list given by
127 * <code>pPath</code> the <code>JAXBContext</code> class will try to find a file
128 * called <samp>jaxb.properties</samp>. This file's got to be in
129 * standard property file format. The <code>JAXBContext</code> class
130 * will load the property file.</li>
131 * <li>A property called "javax.xml.bind.context.factory" (see
132 * {@link #JAXB_CONTEXT_FACTORY}) is evaluated. It must contain the
133 * name of an initialization class. The initialization class is
134 * loaded via
135 * <code>pClassLoader.loadClass(String)</code>.</li>
136 * <li>The initialization class must contain a method
137 * <pre>
138 * public static JAXBContext createContext(String, ClassLoader) throws JAXBException;
139 * </pre>
140 * which is invoked with the parameters <code>pPath</code> and
141 * <code>pClassLoader</code>. The result of this method is also
142 * used as the result of the <code>newInstance(String)</code>
143 * method.</li>
144 * </ol>
145 * @param pPath A colon separated path of package names where to look for
146 * <samp>jaxb.properties</samp> files. The package names must match the
147 * generated classes which you are going to use in your application.
148 * @return An initialized instance of <code>JAXBContext</code>.
149 * @throws JAXBException An error occurred while creating the JAXBContext instance.
150 */
151 public static JAXBContext newInstance(String pPath, ClassLoader pClassLoader) throws JAXBException {
152 if (pPath == null) {
153 throw new JAXBException("The context path must not be null.");
154 }
155 if (pClassLoader == null) {
156 throw new JAXBException("The classloader must not be null.");
157 }
158 for (StringTokenizer st = new StringTokenizer(pPath, ":"); st.hasMoreTokens(); ) {
159 String packageName = st.nextToken();
160 String resourceName = packageName.replace('.', '/') + "/jaxb.properties";
161 URL resource = pClassLoader.getResource(resourceName);
162 if (resource == null) {
163 continue;
164 }
165 Properties props = new Properties();
166 InputStream istream = null;
167 try {
168 istream = resource.openStream();
169 props.load(istream);
170 istream.close();
171 istream = null;
172 } catch (IOException e) {
173 throw new JAXBException("Failed to load property file " + resource, e);
174 } finally {
175 if (istream != null) { try { istream.close(); } catch (Throwable ignore) {} }
176 }
177 String className = props.getProperty(JAXB_CONTEXT_FACTORY);
178 if (className == null) {
179 throw new JAXBException("The property " + JAXB_CONTEXT_FACTORY + " is not set in " + resource);
180 }
181 Class c;
182 try {
183 c = pClassLoader.loadClass(className);
184 } catch (ClassNotFoundException e) {
185 throw new JAXBException("The class " + className + ", referenced by property " +
186 JAXB_CONTEXT_FACTORY + " in " + resource + ", could not be loaded.");
187 }
188 Method m;
189 try {
190 m = c.getMethod("createContext", CONTEXT_CLASSES);
191 } catch (NoSuchMethodException e) {
192 throw new JAXBException("The class " + c + " does not have a method 'public static createContext(String, ClassLoader) throws JAXBException'");
193 }
194 int modifiers = m.getModifiers();
195 if (m == null || !Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
196 throw new JAXBException("The class " + c + " does not have a method 'public static createContext(String, ClassLoader) throws JAXBException'");
197 }
198 Object o;
199 try {
200 o = m.invoke(null, new Object[]{pPath, pClassLoader});
201 } catch (IllegalAccessException e) {
202 throw new JAXBException("Illegal access to method " + c.getName() +
203 ".createContext(String,ClassLoader).", e);
204 } catch (InvocationTargetException e) {
205 Throwable t = e.getTargetException();
206 throw new JAXBException(t.getClass().getName() + " in method " + c.getName() +
207 ".createContext(String,ClassLoader): " + t.getMessage(), t);
208 }
209 if (o == null) {
210 throw new JAXBException("The method " + c.getName() +
211 ".createContext(String,ClassLoader) returned null.");
212 }
213 try {
214 return (JAXBContext) o;
215 } catch (ClassCastException e) {
216 throw new JAXBException("The object created by " + c.getName() +
217 ".createContext(String,ClassLoader) cannot be casted to " +
218 JAXBContext.class.getName() + ".", e);
219 }
220 }
221 throw new JAXBException("Failed to resolve resource jaxb.properties via path " + pPath +
222 " and ClassLoader " + pClassLoader);
223 }
224
225 /** <p>Creates a new instance of {@link Marshaller}. The
226 * {@link Marshaller} can be used
227 * to convert JAXB objects into XML data.</p>
228 * <p><em>Note</em>: Marshallers are reusable, but not reentrant (thread safe).</p>
229 */
230 public abstract Marshaller createMarshaller() throws JAXBException;
231
232 /** <p>Creates a new instance of {@link Unmarshaller}. The
233 * {@link Unmarshaller} can be used
234 * to convert XML data into JAXB objects.</p>
235 * <p><em>Note</em>: Unmarshallers are reusable, but not reentrant (thread safe).</p>
236 */
237 public abstract Unmarshaller createUnmarshaller() throws JAXBException;
238
239 /** <p>Creates a new instance of {@link Validator}. The
240 * {@link Validator} can be used to validate JAXB objects.</p>
241 */
242 public abstract Validator createValidator() throws JAXBException;
243 }