From baf8012570331e3016f64021d294e82da30167ac Mon Sep 17 00:00:00 2001 From: rfeng Date: Tue, 23 Jun 2009 19:41:03 +0000 Subject: Use the JAXB package (ObjectFactory and indexed classes) to build JAXBContext git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@787802 13f79535-47bb-0310-9956-ffa450edef68 --- .../sca/databinding/jaxb/JAXBContextCache.java | 265 ++++++++++++++------- 1 file changed, 177 insertions(+), 88 deletions(-) diff --git a/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java b/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java index d616b9328e..cf330d67c0 100644 --- a/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java +++ b/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java @@ -6,19 +6,23 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations - * under the License. + * under the License. */ package org.apache.tuscany.sca.databinding.jaxb; import java.awt.Image; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.ref.SoftReference; import java.net.URI; import java.security.AccessController; @@ -27,6 +31,7 @@ import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -92,7 +97,7 @@ public class JAXBContextCache { protected LRUCache cache; protected Pool mpool; protected Pool upool; - + // protected JAXBContext commonContext; protected JAXBContext defaultContext; @@ -106,7 +111,7 @@ public class JAXBContextCache { upool = new Pool(); defaultContext = getDefaultJAXBContext(); } - + private static JAXBContext newJAXBContext(final Class... classesToBeBound) throws JAXBException { try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { @@ -130,8 +135,8 @@ public class JAXBContextCache { } catch (PrivilegedActionException e) { throw (JAXBException)e.getException(); } - } - + } + public static JAXBContext getDefaultJAXBContext() { try { return newJAXBContext(); @@ -175,53 +180,16 @@ public class JAXBContextCache { return cl; } - /** - * @param p Package - * @param cl - * @return true if each package has a ObjectFactory class or package-info - */ - public static boolean checkPackage(String p, ClassLoader cl) { - - // Each package must have an ObjectFactory - try { - Class cls = forName(p + ".ObjectFactory", false, cl); - if (cls != null) { - return true; - } - //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that - //does not extend Exception. So we will absorb any Throwable exception here. - } catch (Throwable e) { - // Ignore - } - - // [rfeng] If no ObjectFactory or jaxb.index is present, JAXBContext.newInstance(contextPath, classloader) - // will fail - /* - try { - Class cls = forName(p + ".package-info", false, cl); - if (cls != null) { - return cls.isAnnotationPresent(XmlSchema.class); - } - //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that - //does not extend Exception. So we will absorb any Throwable exception here. - } catch (Throwable e) { - // Ignore - } - */ - - return false; - } - public Marshaller getMarshaller(JAXBContext context) throws JAXBException { Marshaller marshaller = mpool.get(context); if (marshaller == null) { marshaller = context.createMarshaller(); } - marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); return marshaller; } - public void releaseJAXBMarshaller(JAXBContext context, Marshaller marshaller) { + public void releaseJAXBMarshaller(JAXBContext context, Marshaller marshaller) { if (marshaller != null) { marshaller.setAttachmentMarshaller(null); mpool.put(context, marshaller); @@ -229,7 +197,7 @@ public class JAXBContextCache { // doing the next get. } } - + public Unmarshaller getUnmarshaller(JAXBContext context) throws JAXBException { Unmarshaller unmarshaller = upool.get(context); if (unmarshaller == null) { @@ -238,13 +206,13 @@ public class JAXBContextCache { return unmarshaller; } - public void releaseJAXBUnmarshaller(JAXBContext context, Unmarshaller unmarshaller) { + public void releaseJAXBUnmarshaller(JAXBContext context, Unmarshaller unmarshaller) { if (unmarshaller != null) { unmarshaller.setAttachmentUnmarshaller(null); upool.put(context, unmarshaller); } } - + public LRUCache getCache() { return cache; } @@ -253,29 +221,7 @@ public class JAXBContextCache { if (BUILTIN_CLASSES_SET.contains(cls)) { return defaultContext; } - synchronized (cache) { - JAXBContext context = cache.get(cls); - if (context != null) { - return context; - } - Package pkg = cls.getPackage(); - if (pkg != null) { - context = cache.get(pkg); - if (context != null) { - return context; - } - } - - if (pkg != null && checkPackage(pkg.getName(), cls.getClassLoader())) { - context = newJAXBContext(pkg.getName(), cls.getClassLoader()); - cache.put(pkg, context); - } else { - context = newJAXBContext(cls); - cache.put(cls, context); - } - return context; - - } + return getJAXBContext(new Class[] {cls}); } public JAXBContext getJAXBContext(Class[] classes) throws JAXBException { @@ -284,10 +230,10 @@ public class JAXBContextCache { } public JAXBContext getJAXBContext(Set> classes) throws JAXBException { - // Remove the JAXB built-in types to maximize the cache hit + // Remove the JAXB built-in types to maximize the cache hit Set> classSet = new HashSet>(classes); classSet.removeAll(BUILTIN_CLASSES_SET); - + // FIXME: [rfeng] Remove java classes that are mapped to the same XSD type to avoid // conflicts if (classSet.contains(Date[].class)) { @@ -301,16 +247,14 @@ public class JAXBContextCache { if (classSet.contains(Source[].class)) { classSet.remove(Image[].class); classSet.remove(DataHandler[].class); - } - + } + + classSet = getJAXBClasses(classSet); + if(classSet.isEmpty()) { return defaultContext; } - - // For single class - if (classSet.size() == 1) { - return getJAXBContext(classSet.iterator().next()); - } + synchronized (cache) { JAXBContext context = cache.get(classSet); if (context != null) { @@ -342,7 +286,7 @@ public class JAXBContextCache { // without other dependencies so we might be better off copying it and avoiding a new // Axis2 dependency here. // - + /** * Pool a list of items for a specific key * @@ -350,14 +294,14 @@ public class JAXBContextCache { * @param Pooled object */ private static class Pool { - private SoftReference>> softMap = + private SoftReference>> softMap = new SoftReference>>( new ConcurrentHashMap>()); // The maps are freed up when a LOAD FACTOR is hit private static final int MAX_LIST_FACTOR = 50; private static final int MAX_LOAD_FACTOR = 32; // Maximum number of JAXBContext to store - + /** * @param key * @return removed item from pool or null. @@ -368,7 +312,7 @@ public class JAXBContextCache { if (values.size()>0) { V v = values.remove(values.size()-1); return v; - + } } return null; @@ -410,7 +354,7 @@ public class JAXBContextCache { if (values == null) { if (map == null) { map = new ConcurrentHashMap>(); - softMap = + softMap = new SoftReference>>(map); } values = new ArrayList(); @@ -420,12 +364,12 @@ public class JAXBContextCache { return values; } } - + /** * AdjustSize * When the number of keys exceeds the maximum load, half * of the entries are deleted. - * + * * The assumption is that the JAXBContexts, UnMarshallers, Marshallers, etc. require * a large footprint. */ @@ -445,4 +389,149 @@ public class JAXBContextCache { } } } + + /** + * Find the JAXB classes (looking into packages) to be bound + * @param classes A collection of classes + * @return A set of classes that include the ObjectFactory and indexed JAXB classes + * @throws JAXBException + */ + private static Set> getJAXBClasses(Collection> classes) throws JAXBException { + Set> classSet = new HashSet>(); + // Index the packages + Map pkgs = getPackages(classes); + Set nonJAXBPackages = new HashSet(); + for (Map.Entry p : pkgs.entrySet()) { + Package pkg = p.getKey(); + if (pkg == null) { + continue; + } + Set> set = getJAXBClasses(pkg.getName(), p.getValue()); + if (set.isEmpty()) { + // No JAXB package + nonJAXBPackages.add(pkg); + } else { + // Add JAXB ObjectFactory and indexed classes + classSet.addAll(set); + } + } + // Adding classes that are not part of JAXB packages + for (Class cls : classes) { + Package pkg = getPackage(cls); + if (pkg == null || nonJAXBPackages.contains(pkg)) { + classSet.add(cls); + } + } + return classSet; + } + + /** + * Get the package for a class, taking array into account + * @param cls + * @return + */ + private static Package getPackage(Class cls) { + Class type = cls; + while (type.isArray()) { + type = type.getComponentType(); + } + return type.getPackage(); + } + + /** + * Get a map of packages + * @param classes + * @return + */ + private static Map getPackages(Collection> classes) { + Map pkgs = new HashMap(); + for (Class cls : classes) { + Package pkg = getPackage(cls); + if (pkg != null) { + pkgs.put(pkg, cls.getClassLoader()); + } + } + return pkgs; + } + + /** + * Find ObjectFactory and indexed JAXB classes for the package + * @param pkg + * @param classLoader + * @return + * @throws JAXBException + */ + private static Set> getJAXBClasses(String pkg, ClassLoader classLoader) throws JAXBException { + Set> classes = new HashSet>(); + List> indexedClasses; + + // look for ObjectFactory and load it + final Class o; + try { + o = forName(pkg + ".ObjectFactory", false, classLoader); + classes.add(o); + } catch (ClassNotFoundException e) { + // not necessarily an error + } + + // look for jaxb.index and load the list of classes + try { + indexedClasses = loadIndexedClasses(pkg, classLoader); + } catch (IOException e) { + throw new JAXBException(e); + } + if (indexedClasses != null) { + classes.addAll(indexedClasses); + } + + return classes; + } + + /** + * Look for jaxb.index file in the specified package and load it's contents + * + * @param pkg package name to search in + * @param classLoader ClassLoader to search in + * @return a List of Class objects to load, null if there weren't any + * @throws IOException if there is an error reading the index file + * @throws JAXBException if there are any errors in the index file + */ + private static List> loadIndexedClasses(String pkg, ClassLoader classLoader) throws IOException, + JAXBException { + if (classLoader == null) { + return null; + } + final String resource = pkg.replace('.', '/') + "/jaxb.index"; + final InputStream resourceAsStream = classLoader.getResourceAsStream(resource); + + if (resourceAsStream == null) { + return null; + } + + BufferedReader in = new BufferedReader(new InputStreamReader(resourceAsStream, "UTF-8")); + try { + List> classes = new ArrayList>(); + String className = in.readLine(); + while (className != null) { + className = className.trim(); + if (className.startsWith("#") || (className.length() == 0)) { + className = in.readLine(); + continue; + } + + try { + classes.add(forName(pkg + '.' + className, false, classLoader)); + } catch (ClassNotFoundException e) { + throw new JAXBException(e); + } + + className = in.readLine(); + } + return classes; + } finally { + in.close(); + } + } + } + -- cgit v1.2.3