001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.message.databinding;
020:
021: import org.apache.axis2.java.security.AccessController;
022: import org.apache.axis2.jaxws.ExceptionFactory;
023: import org.apache.axis2.jaxws.i18n.Messages;
024: import org.apache.axis2.jaxws.message.factory.ClassFinderFactory;
025: import org.apache.axis2.jaxws.registry.FactoryRegistry;
026: import org.apache.axis2.jaxws.utility.ClassUtils;
027: import org.apache.axis2.jaxws.utility.JavaUtils;
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import javax.xml.bind.JAXBContext;
032: import javax.xml.bind.JAXBException;
033: import javax.xml.bind.JAXBIntrospector;
034: import javax.xml.bind.Marshaller;
035: import javax.xml.bind.Unmarshaller;
036: import javax.xml.ws.Holder;
037: import java.io.File;
038: import java.io.IOException;
039: import java.io.UnsupportedEncodingException;
040: import java.net.URL;
041: import java.net.URLDecoder;
042: import java.security.PrivilegedActionException;
043: import java.security.PrivilegedExceptionAction;
044: import java.util.ArrayList;
045: import java.util.Enumeration;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049: import java.util.TreeSet;
050: import java.util.concurrent.ConcurrentHashMap;
051:
052: /**
053: * JAXB Utilites to pool JAXBContext and related objects. Currently the JAXBContext is pooled by
054: * Class name. We may change this to create and pool by package name.
055: */
056: public class JAXBUtils {
057:
058: private static final Log log = LogFactory.getLog(JAXBUtils.class);
059:
060: // Create a concurrent map to get the JAXBObject: keys are ClassLoader and Set<String>.
061: private static Map<ClassLoader, Map<String, JAXBContextValue>> jaxbMap = new ConcurrentHashMap<ClassLoader, Map<String, JAXBContextValue>>();
062:
063: private static Map<JAXBContext, Unmarshaller> umap = new ConcurrentHashMap<JAXBContext, Unmarshaller>();
064:
065: private static Map<JAXBContext, Marshaller> mmap = new ConcurrentHashMap<JAXBContext, Marshaller>();
066:
067: private static Map<JAXBContext, JAXBIntrospector> imap = new ConcurrentHashMap<JAXBContext, JAXBIntrospector>();
068:
069: // From Lizet Ernand:
070: // If you really care about the performance,
071: // and/or your application is going to read a lot of small documents,
072: // then creating Unmarshaller could be relatively an expensive operation.
073: // In that case, consider pooling Unmarshaller objects.
074: // Different threads may reuse one Unmarshaller instance,
075: // as long as you don't use one instance from two threads at the same time.
076:
077: private static boolean ENABLE_ADV_POOLING = false;
078:
079: // The maps are freed up when a LOAD FACTOR is hit
080: private static int MAX_LOAD_FACTOR = 32;
081:
082: // Construction Type
083: public enum CONSTRUCTION_TYPE {
084: BY_CLASS_ARRAY, BY_CONTEXT_PATH, UNKNOWN
085: }
086:
087: ;
088:
089: /**
090: * Get a JAXBContext for the class
091: *
092: * @param contextPackage Set<Package>
093: * @return JAXBContext
094: * @throws JAXBException
095: * @deprecated
096: */
097: public static JAXBContext getJAXBContext(
098: TreeSet<String> contextPackages) throws JAXBException {
099: return getJAXBContext(contextPackages,
100: new Holder<CONSTRUCTION_TYPE>(), contextPackages
101: .toString());
102: }
103:
104: /**
105: * Get a JAXBContext for the class
106: *
107: * @param contextPackage Set<Package>
108: * @param contructionType (output value that indicates how the context was constructed)
109: * @return JAXBContext
110: * @throws JAXBException
111: */
112: public static JAXBContext getJAXBContext(
113: TreeSet<String> contextPackages,
114: Holder<CONSTRUCTION_TYPE> constructionType, String key)
115: throws JAXBException {
116: // JAXBContexts for the same class can be reused and are supposed to be thread-safe
117: if (log.isDebugEnabled()) {
118: log
119: .debug("Following packages are in this batch of getJAXBContext() :");
120: for (String pkg : contextPackages) {
121: log.debug(pkg);
122: }
123: }
124: // The JAXBContexts are keyed by ClassLoader and the set of Strings
125: ClassLoader cl = getContextClassLoader();
126:
127: // Get the innerMap
128: Map<String, JAXBContextValue> innerMap = jaxbMap.get(cl);
129: if (innerMap == null) {
130: adjustPoolSize(jaxbMap);
131: innerMap = new ConcurrentHashMap<String, JAXBContextValue>();
132: jaxbMap.put(cl, innerMap);
133: }
134:
135: if (contextPackages == null) {
136: contextPackages = new TreeSet<String>();
137: }
138:
139: JAXBContextValue contextValue = innerMap.get(key);
140: if (contextValue == null) {
141: adjustPoolSize(innerMap);
142:
143: // A pooled context was not found, so create one and put it in the map.
144: synchronized (contextPackages) {
145: // synchronized on contextPackages because this method may prune the contextPackages
146: contextValue = createJAXBContextValue(contextPackages,
147: cl);
148: }
149: // Put the new context in the map keyed by both the original and current list of packages
150: innerMap.put(key, contextValue);
151: innerMap.put(contextPackages.toString(), contextValue);
152: if (log.isDebugEnabled()) {
153: log.debug("JAXBContext [created] for "
154: + contextPackages.toString());
155: }
156:
157: } else {
158: if (log.isDebugEnabled()) {
159: log.debug("JAXBContext [from pool] for "
160: + contextPackages.toString());
161: }
162: }
163: constructionType.value = contextValue.constructionType;
164: return contextValue.jaxbContext;
165: }
166:
167: /**
168: * Create a JAXBContext using the contextPackages
169: *
170: * @param contextPackages Set<String>
171: * @param cl ClassLoader
172: * @return JAXBContextValue (JAXBContext + constructionType)
173: * @throws JAXBException
174: */
175: private static JAXBContextValue createJAXBContextValue(
176: TreeSet<String> contextPackages, ClassLoader cl)
177: throws JAXBException {
178:
179: JAXBContextValue contextValue = null;
180: if (log.isDebugEnabled()) {
181: log
182: .debug("Following packages are in this batch of getJAXBContext() :");
183: for (String pkg : contextPackages) {
184: log.debug(pkg);
185: }
186: }
187: // The contextPackages is a set of package names that are constructed using PackageSetBuilder.
188: // PackageSetBuilder gets the packages names from various sources.
189: // a) It walks the various annotations on the WebService collecting package names.
190: // b) It walks the wsdl/schemas and builds package names for each target namespace.
191: //
192: // The combination of these two sources should produce all of the package names.
193: // -------------
194: // Note that (b) is necessary for the following case:
195: // An operation has a parameter named BASE.
196: // Object DERIVED is an extension of BASE and is defined in a different package/schema.
197: // In this case, there will not be any annotations on the WebService that reference DERIVED.
198: // The only way to find the package for DERIVED is to walk the schemas.
199: // -------------
200:
201: Iterator<String> it = contextPackages.iterator();
202: while (it.hasNext()) {
203: String p = it.next();
204: // Don't consider java and javax packages
205: // REVIEW: We might have to refine this
206: if (p.startsWith("java.") || p.startsWith("javax.")) {
207: it.remove();
208: }
209: }
210:
211: // There are two ways to construct the context.
212: // 1) USE A CONTEXTPATH, which is a string containing
213: // all of the packages separated by colons.
214: // 2) USE A CLASS[], which is an array of all of the classes
215: // involved in the marshal/unmarshal.
216: //
217: // There are pros/cons with both approaches.
218: // USE A CONTEXTPATH:
219: // Pros: preferred way of doing this.
220: // performant
221: // most dynamic
222: // Cons: Each package in context path must have an ObjectFactory
223: //
224: //
225: // USE CLASS[]:
226: // Pros: Doesn't require ObjectFactory in each package
227: // Cons: Hard to set up, must account for JAX-WS classes, etc.
228: // Does not work if arrays of classes are needed
229: // slower
230: //
231: // The following code attempts to build a context path. It then
232: // choose one of the two constructions above (prefer USE A CONTEXT_PATH)
233: //
234:
235: // The packages are examined to see if they have ObjectFactory/package-info classes.
236: // Invalid packages are removed from the list
237: it = contextPackages.iterator();
238: boolean contextConstruction = true;
239: boolean isJAXBFound = false;
240: while (it.hasNext()) {
241: String p = it.next();
242: // See if this package has an ObjectFactory or package-info
243: if (checkPackage(p, cl)) {
244: // Flow to here indicates package can be used for CONTEXT construction
245: isJAXBFound = true;
246: if (log.isDebugEnabled()) {
247: log
248: .debug("Package "
249: + p
250: + " contains an ObjectFactory or package-info class.");
251: }
252: } else {
253: // Flow to here indicates that the package is not valid for context construction.
254: // Perhaps the package is invalid.
255: if (log.isDebugEnabled()) {
256: log
257: .debug("Package "
258: + p
259: + " does not contain an ObjectFactory or package-info class. Searching for JAXB classes");
260: }
261: List<Class> classes = null;
262: classes = getAllClassesFromPackage(p, cl);
263: if (classes == null || classes.size() == 0) {
264: if (log.isDebugEnabled()) {
265: log
266: .debug("Package "
267: + p
268: + " does not have any JAXB classes. It is removed from the JAXB context path.");
269: }
270: it.remove();
271: } else {
272: // Classes are found in the package. We cannot use the CONTEXT construction
273: contextConstruction = false;
274: if (log.isDebugEnabled()) {
275: log
276: .debug("Package "
277: + p
278: + " does not contain ObjectFactory, but it does contain other JAXB classes.");
279: }
280: }
281: }
282: }
283:
284: if (!isJAXBFound) {
285: if (log.isDebugEnabled()) {
286: log
287: .debug("Both ObjectFactory & package-info not found in package hierachy");
288: }
289: }
290:
291: // The code above may have removed some packages from the list.
292: // Retry our lookup with the updated list
293: Map<String, JAXBContextValue> innerMap = jaxbMap.get(cl);
294: if (innerMap != null) {
295: contextValue = innerMap.get(contextPackages.toString());
296: if (contextValue != null) {
297: if (log.isDebugEnabled()) {
298: log
299: .debug("Successfully found JAXBContext with updated context list:"
300: + contextValue.jaxbContext
301: .toString());
302: }
303: return contextValue;
304: }
305: }
306:
307: // CONTEXT construction
308: if (contextConstruction) {
309: JAXBContext context = createJAXBContextUsingContextPath(
310: contextPackages, cl);
311: if (context != null) {
312: contextValue = new JAXBContextValue(context,
313: CONSTRUCTION_TYPE.BY_CONTEXT_PATH);
314: }
315: }
316:
317: // CLASS construction
318: if (contextValue == null) {
319: it = contextPackages.iterator();
320: List<Class> fullList = new ArrayList<Class>();
321: while (it.hasNext()) {
322: String pkg = it.next();
323: fullList.addAll(getAllClassesFromPackage(pkg, cl));
324: }
325: //Lets add all common array classes
326: addCommonArrayClasses(fullList);
327: Class[] classArray = fullList.toArray(new Class[0]);
328: JAXBContext context = JAXBContext_newInstance(classArray);
329: if (context != null) {
330: contextValue = new JAXBContextValue(context,
331: CONSTRUCTION_TYPE.BY_CLASS_ARRAY);
332: }
333: }
334: if (log.isDebugEnabled()) {
335: log.debug("Successfully created JAXBContext "
336: + contextValue.jaxbContext.toString());
337: }
338: return contextValue;
339: }
340:
341: /**
342: * Get the unmarshaller. You must call releaseUnmarshaller to put it back into the pool
343: *
344: * @param context JAXBContext
345: * @return Unmarshaller
346: * @throws JAXBException
347: */
348: public static Unmarshaller getJAXBUnmarshaller(JAXBContext context)
349: throws JAXBException {
350: if (!ENABLE_ADV_POOLING) {
351: if (log.isDebugEnabled()) {
352: log.debug("Unmarshaller created [no pooling]");
353: }
354: return context.createUnmarshaller();
355: }
356: Unmarshaller u = umap.remove(context);
357: if (u == null) {
358: if (log.isDebugEnabled()) {
359: log.debug("Unmarshaller created [not in pool]");
360: }
361: u = context.createUnmarshaller();
362: } else {
363: if (log.isDebugEnabled()) {
364: log.debug("Unmarshaller obtained [from pool]");
365: }
366: }
367: return u;
368: }
369:
370: /**
371: * Release Unmarshaller Do not call this method if an exception occurred while using the
372: * Unmarshaller. We object my be in an invalid state.
373: *
374: * @param context JAXBContext
375: * @param unmarshaller Unmarshaller
376: */
377: public static void releaseJAXBUnmarshaller(JAXBContext context,
378: Unmarshaller unmarshaller) {
379: if (log.isDebugEnabled()) {
380: log.debug("Unmarshaller placed back into pool");
381: }
382: if (ENABLE_ADV_POOLING) {
383: adjustPoolSize(umap);
384: umap.put(context, unmarshaller);
385: }
386: }
387:
388: /**
389: * Get JAXBMarshaller
390: *
391: * @param context JAXBContext
392: * @return Marshaller
393: * @throws JAXBException
394: */
395: public static Marshaller getJAXBMarshaller(JAXBContext context)
396: throws JAXBException {
397: Marshaller m = null;
398: if (!ENABLE_ADV_POOLING) {
399: if (log.isDebugEnabled()) {
400: log.debug("Marshaller created [no pooling]");
401: }
402: m = context.createMarshaller();
403: } else {
404: m = mmap.remove(context);
405: if (m == null) {
406: if (log.isDebugEnabled()) {
407: log.debug("Marshaller created [not in pool]");
408: }
409: m = context.createMarshaller();
410: } else {
411: if (log.isDebugEnabled()) {
412: log.debug("Marshaller obtained [from pool]");
413: }
414: }
415: }
416: m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); // No PIs
417: return m;
418: }
419:
420: /**
421: * releaseJAXBMarshalller Do not call this method if an exception occurred while using the
422: * Marshaller. We object my be in an invalid state.
423: *
424: * @param context JAXBContext
425: * @param marshaller Marshaller
426: */
427: public static void releaseJAXBMarshaller(JAXBContext context,
428: Marshaller marshaller) {
429: if (log.isDebugEnabled()) {
430: log.debug("Marshaller placed back into pool");
431: }
432: if (ENABLE_ADV_POOLING) {
433: adjustPoolSize(mmap);
434: mmap.put(context, marshaller);
435: }
436: }
437:
438: /**
439: * get JAXB Introspector
440: *
441: * @param context JAXBContext
442: * @return JAXBIntrospector
443: * @throws JAXBException
444: */
445: public static JAXBIntrospector getJAXBIntrospector(
446: JAXBContext context) throws JAXBException {
447: JAXBIntrospector i = null;
448: if (!ENABLE_ADV_POOLING) {
449: if (log.isDebugEnabled()) {
450: log.debug("JAXBIntrospector created [no pooling]");
451: }
452: i = context.createJAXBIntrospector();
453: } else {
454: i = imap.remove(context);
455: if (i == null) {
456: if (log.isDebugEnabled()) {
457: log.debug("JAXBIntrospector created [not in pool]");
458: }
459: i = context.createJAXBIntrospector();
460: } else {
461: if (log.isDebugEnabled()) {
462: log.debug("JAXBIntrospector obtained [from pool]");
463: }
464: }
465: }
466: return i;
467: }
468:
469: /**
470: * Release JAXBIntrospector Do not call this method if an exception occurred while using the
471: * JAXBIntrospector. We object my be in an invalid state.
472: *
473: * @param context JAXBContext
474: * @param introspector JAXBIntrospector
475: */
476: public static void releaseJAXBIntrospector(JAXBContext context,
477: JAXBIntrospector introspector) {
478: if (log.isDebugEnabled()) {
479: log.debug("JAXBIntrospector placed back into pool");
480: }
481: if (ENABLE_ADV_POOLING) {
482: adjustPoolSize(imap);
483: imap.put(context, introspector);
484: }
485: }
486:
487: /**
488: * @param p Package
489: * @param cl
490: * @return true if each package has a ObjectFactory class or package-info
491: */
492: private static boolean checkPackage(String p, ClassLoader cl) {
493:
494: // Each package must have an ObjectFactory
495: if (log.isDebugEnabled()) {
496: log.debug("checking package :" + p);
497:
498: }
499: try {
500: Class cls = forName(p + ".ObjectFactory", false, cl);
501: if (cls != null) {
502: return true;
503: }
504: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
505: //does not extend Exception. So we will absorb any Throwable exception here.
506: } catch (Throwable e) {
507: if (log.isDebugEnabled()) {
508: log.debug("ObjectFactory Class Not Found " + e);
509: log.debug("...caused by " + e.getCause() + " "
510: + JavaUtils.stackToString(e));
511: }
512: }
513:
514: try {
515: Class cls = forName(p + ".package-info", false, cl);
516: if (cls != null) {
517: return true;
518: }
519: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
520: //does not extend Exception. So we will absorb any Throwable exception here.
521: } catch (Throwable e) {
522: if (log.isDebugEnabled()) {
523: log.debug("package-info Class Not Found " + e);
524: log.debug("...caused by " + e.getCause() + " "
525: + JavaUtils.stackToString(e));
526: }
527: }
528:
529: return false;
530: }
531:
532: /**
533: * Create a JAXBContext using the contextpath approach
534: *
535: * @param packages
536: * @param cl ClassLoader
537: * @return JAXBContext or null if unsuccessful
538: */
539: private static JAXBContext createJAXBContextUsingContextPath(
540: TreeSet<String> packages, ClassLoader cl) {
541: JAXBContext context = null;
542: String contextpath = "";
543:
544: // Iterate through the classes and build the contextpath
545: Iterator<String> it = packages.iterator();
546: while (it.hasNext()) {
547: String p = it.next();
548: if (contextpath.length() != 0) {
549: contextpath += ":";
550: }
551: contextpath += p;
552:
553: }
554: try {
555: if (log.isDebugEnabled()) {
556: log
557: .debug("Attempting to create JAXBContext with contextPath="
558: + contextpath);
559: }
560: context = JAXBContext_newInstance(contextpath, cl);
561: if (log.isDebugEnabled()) {
562: log.debug(" Successfully created JAXBContext:"
563: + context);
564: }
565: } catch (Throwable e) {
566: if (log.isDebugEnabled()) {
567: log
568: .debug(" Unsuccessful: We will now use an alterative JAXBConstruct construction");
569: log.debug(" Reason " + e.toString());
570: }
571: }
572: return context;
573: }
574:
575: /**
576: * This method will return all the Class names needed to construct a JAXBContext
577: *
578: * @param pkg Package
579: * @param ClassLoader cl
580: * @return
581: * @throws ClassNotFoundException if error occurs getting package
582: */
583: private static List<Class> getAllClassesFromPackage(String pkg,
584: ClassLoader cl) {
585: if (pkg == null) {
586: return new ArrayList<Class>();
587: }
588:
589: /*
590: * This method is a best effort method. We should always return an object.
591: */
592:
593: ArrayList<Class> classes = new ArrayList<Class>();
594:
595: try {
596: // This will load classes from directory
597: classes.addAll(getClassesFromDirectory(pkg, cl));
598: } catch (ClassNotFoundException e) {
599: if (log.isDebugEnabled()) {
600: log
601: .debug("getClassesFromDirectory failed to get Classes");
602: }
603: }
604: try {
605: //If Clases not found in directory then look for jar that has these classes
606: if (classes.size() <= 0) {
607: //This will load classes from jar file.
608: ClassFinderFactory cff = (ClassFinderFactory) FactoryRegistry
609: .getFactory(ClassFinderFactory.class);
610: ClassFinder cf = cff.getClassFinder();
611: classes.addAll(cf.getClassesFromJarFile(pkg, cl));
612: }
613: } catch (ClassNotFoundException e) {
614: if (log.isDebugEnabled()) {
615: log
616: .debug("getClassesFromJarFile failed to get Classes");
617: }
618: }
619:
620: return classes;
621: }
622:
623: private static ArrayList<Class> getClassesFromDirectory(String pkg,
624: ClassLoader cl) throws ClassNotFoundException {
625: // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
626: String pckgname = pkg;
627: ArrayList<File> directories = new ArrayList<File>();
628: try {
629: String path = pckgname.replace('.', '/');
630: // Ask for all resources for the path
631: Enumeration<URL> resources = cl.getResources(path);
632: while (resources.hasMoreElements()) {
633: directories.add(new File(URLDecoder.decode(resources
634: .nextElement().getPath(), "UTF-8")));
635: }
636: } catch (UnsupportedEncodingException e) {
637: if (log.isDebugEnabled()) {
638: log
639: .debug(pckgname
640: + " does not appear to be a valid package (Unsupported encoding)");
641: }
642: throw new ClassNotFoundException(Messages.getMessage(
643: "ClassUtilsErr2", pckgname));
644: } catch (IOException e) {
645: if (log.isDebugEnabled()) {
646: log
647: .debug("IOException was thrown when trying to get all resources for "
648: + pckgname);
649: }
650: throw new ClassNotFoundException(Messages.getMessage(
651: "ClassUtilsErr3", pckgname));
652: }
653:
654: ArrayList<Class> classes = new ArrayList<Class>();
655: // For every directory identified capture all the .class files
656: for (File directory : directories) {
657: if (log.isDebugEnabled()) {
658: log.debug(" Adding JAXB classes from directory: "
659: + directory.getName());
660: }
661: if (directory.exists()) {
662: // Get the list of the files contained in the package
663: String[] files = directory.list();
664: for (String file : files) {
665: // we are only interested in .class files
666: if (file.endsWith(".class")) {
667: // removes the .class extension
668: // TODO Java2 Sec
669: String className = pckgname + '.'
670: + file.substring(0, file.length() - 6);
671: try {
672: Class clazz = forName(className, false,
673: getContextClassLoader());
674: // Don't add any interfaces or JAXWS specific classes.
675: // Only classes that represent data and can be marshalled
676: // by JAXB should be added.
677: if (!clazz.isInterface()
678: && (clazz.isEnum() || ClassUtils
679: .getDefaultPublicConstructor(clazz) != null)
680: && !ClassUtils.isJAXWSClass(clazz)
681: && !java.lang.Exception.class
682: .isAssignableFrom(clazz)) {
683:
684: // Ensure that all the referenced classes are loadable too
685: clazz.getDeclaredMethods();
686: clazz.getDeclaredFields();
687:
688: if (log.isDebugEnabled()) {
689: log.debug("Adding class: " + file);
690: }
691: classes.add(clazz);
692:
693: // REVIEW:
694: // Support of RPC list (and possibly other scenarios) requires that the array classes should also be present.
695: // This is a hack until we can determine how to get this information.
696:
697: // The arrayName and loadable name are different. Get the loadable
698: // name, load the array class, and add it to our list
699: //className += "[]";
700: //String loadableName = ClassUtils.getLoadableClassName(className);
701:
702: //Class aClazz = Class.forName(loadableName, false, Thread.currentThread().getContextClassLoader());
703: }
704: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
705: //does not extend Exception
706: } catch (Throwable e) {
707: if (log.isDebugEnabled()) {
708: log
709: .debug("Tried to load class "
710: + className
711: + " while constructing a JAXBContext. This class will be skipped. Processing Continues.");
712: log
713: .debug(" The reason that class could not be loaded:"
714: + e.toString());
715: log.debug(JavaUtils.stackToString(e));
716: }
717: }
718:
719: }
720: }
721: }
722: }
723:
724: return classes;
725: }
726:
727: private static String[] commonArrayClasses = new String[] {
728: // primitives
729: "boolean[]", "byte[]", "char[][]", "double[]", "float[]",
730: "int[]", "long[]", "short[]",
731: "java.lang.String[]",
732: // Others
733: "java.lang.Object[]", "java.awt.Image[]",
734: "java.math.BigDecimal[]", "java.math.BigInteger[]",
735: "java.util.Calendar[]", "javax.xml.namespace.QName[]" };
736:
737: private static void addCommonArrayClasses(List<Class> list) {
738: // Add common primitives arrays (necessary for RPC list type support)
739: ClassLoader cl = getContextClassLoader();
740:
741: for (int i = 0; i < commonArrayClasses.length; i++) {
742: String className = commonArrayClasses[i];
743: try {
744: // Load and add the class
745: Class cls = forName(ClassUtils
746: .getLoadableClassName(className), false, cl);
747: list.add(cls);
748: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
749: //does not extend Exception
750: } catch (Throwable e) {
751: if (log.isDebugEnabled()) {
752: log
753: .debug("Tried to load class "
754: + className
755: + " while constructing a JAXBContext. This class will be skipped. Processing Continues.");
756: log
757: .debug(" The reason that class could not be loaded:"
758: + e.toString());
759: log.debug(JavaUtils.stackToString(e));
760: }
761: }
762: }
763: }
764:
765: /** @return ClassLoader */
766: private static ClassLoader getContextClassLoader() {
767: // NOTE: This method must remain private because it uses AccessController
768: ClassLoader cl = null;
769: try {
770: cl = (ClassLoader) AccessController
771: .doPrivileged(new PrivilegedExceptionAction() {
772: public Object run()
773: throws ClassNotFoundException {
774: return Thread.currentThread()
775: .getContextClassLoader();
776: }
777: });
778: } catch (PrivilegedActionException e) {
779: if (log.isDebugEnabled()) {
780: log.debug("Exception thrown from AccessController: "
781: + e);
782: }
783: throw ExceptionFactory.makeWebServiceException(e
784: .getException());
785: }
786:
787: return cl;
788: }
789:
790: /**
791: * Return the class for this name
792: *
793: * @return Class
794: */
795: private static Class forName(final String className,
796: final boolean initialize, final ClassLoader classloader)
797: throws ClassNotFoundException {
798: // NOTE: This method must remain private because it uses AccessController
799: Class cl = null;
800: try {
801: cl = (Class) AccessController
802: .doPrivileged(new PrivilegedExceptionAction() {
803: public Object run()
804: throws ClassNotFoundException {
805: // Class.forName does not support primitives
806: Class cls = ClassUtils
807: .getPrimitiveClass(className);
808: if (cls == null) {
809: cls = Class.forName(className,
810: initialize, classloader);
811: }
812: return cls;
813: }
814: });
815: } catch (PrivilegedActionException e) {
816: if (log.isDebugEnabled()) {
817: log.debug("Exception thrown from AccessController: "
818: + e);
819: }
820: throw (ClassNotFoundException) e.getException();
821: }
822:
823: return cl;
824: }
825:
826: /**
827: * Create JAXBContext from context String and ClassLoader
828: *
829: * @param context
830: * @param classloader
831: * @return
832: * @throws Exception
833: */
834: private static JAXBContext JAXBContext_newInstance(
835: final String context, final ClassLoader classloader)
836: throws Exception {
837: // NOTE: This method must remain private because it uses AccessController
838: JAXBContext jaxbContext = null;
839: try {
840: if (log.isDebugEnabled()) {
841: if (context == null || context.length() == 0) {
842: log
843: .debug("JAXBContext is constructed without a context String.");
844: } else {
845: log
846: .debug("JAXBContext is constructed with a context of:"
847: + context);
848: }
849: }
850: jaxbContext = (JAXBContext) AccessController
851: .doPrivileged(new PrivilegedExceptionAction() {
852: public Object run() throws JAXBException {
853: return JAXBContext.newInstance(context,
854: classloader);
855: }
856: });
857: } catch (PrivilegedActionException e) {
858: if (log.isDebugEnabled()) {
859: log.debug("Exception thrown from AccessController: "
860: + e);
861: }
862: throw e.getException();
863: }
864:
865: return jaxbContext;
866: }
867:
868: /**
869: * Create JAXBContext from Class[]
870: *
871: * @param classArray
872: * @return
873: * @throws Exception
874: */
875: private static JAXBContext JAXBContext_newInstance(
876: final Class[] classArray) throws JAXBException {
877: // NOTE: This method must remain private because it uses AccessController
878: JAXBContext jaxbContext = null;
879: try {
880: if (log.isDebugEnabled()) {
881: if (classArray == null || classArray.length == 0) {
882: log
883: .debug("JAXBContext is constructed with 0 input classes.");
884: } else {
885: log.debug("JAXBContext is constructed with "
886: + classArray.length + " input classes.");
887: }
888: }
889: jaxbContext = (JAXBContext) AccessController
890: .doPrivileged(new PrivilegedExceptionAction() {
891: public Object run() throws JAXBException {
892: return JAXBContext.newInstance(classArray);
893: }
894: });
895: } catch (PrivilegedActionException e) {
896: if (log.isDebugEnabled()) {
897: log.debug("Exception thrown from AccessController: "
898: + e);
899: log.debug(" Exception is " + e.getException());
900: }
901: if (e.getException() instanceof JAXBException) {
902: throw (JAXBException) e.getException();
903: } else if (e.getException() instanceof RuntimeException) {
904: ExceptionFactory.makeWebServiceException(e
905: .getException());
906: }
907: }
908:
909: return jaxbContext;
910: }
911:
912: /**
913: * Adjust the number of objects in the hash map if the limit is exceeded
914: *
915: * @param map
916: */
917: private static synchronized void adjustPoolSize(Map map) {
918: if (map.size() > MAX_LOAD_FACTOR) {
919: // Remove every other Entry in the map.
920: Iterator it = map.entrySet().iterator();
921: boolean removeIt = false;
922: while (it.hasNext()) {
923: it.next();
924: if (removeIt) {
925: it.remove();
926: }
927: removeIt = !removeIt;
928: }
929: }
930: }
931:
932: /** Holds the JAXBContext and the manner by which it was constructed */
933: static class JAXBContextValue {
934:
935: public JAXBContext jaxbContext;
936: public CONSTRUCTION_TYPE constructionType;
937:
938: public JAXBContextValue(JAXBContext jaxbContext,
939: CONSTRUCTION_TYPE constructionType) {
940: this.jaxbContext = jaxbContext;
941: this.constructionType = constructionType;
942: }
943: }
944:
945: }
|