You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuscany.apache.org by rf...@apache.org on 2008/05/19 23:49:56 UTC

svn commit: r657999 - in /incubator/tuscany/java/sca/modules/databinding-jaxb/src: main/java/org/apache/tuscany/sca/databinding/jaxb/ test/java/org/apache/tuscany/databinding/jaxb/

Author: rfeng
Date: Mon May 19 14:49:56 2008
New Revision: 657999

URL: http://svn.apache.org/viewvc?rev=657999&view=rev
Log:
Add cache capability for JAXBContext which significantly improves the performance of the creation of JAXBContext

Added:
    incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java   (with props)
    incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java   (with props)
Modified:
    incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextHelper.java

Added: incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java
URL: http://svn.apache.org/viewvc/incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java?rev=657999&view=auto
==============================================================================
--- incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java (added)
+++ incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java Mon May 19 14:49:56 2008
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * 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.    
+ */
+
+package org.apache.tuscany.sca.databinding.jaxb;
+
+import java.awt.Image;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.WeakHashMap;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class JAXBContextCache {
+    private static HashMap<String, Class<?>> loadClassMap = new HashMap<String, Class<?>>();
+
+    static {
+        loadClassMap.put("byte", byte.class);
+        loadClassMap.put("int", int.class);
+        loadClassMap.put("short", short.class);
+        loadClassMap.put("long", long.class);
+        loadClassMap.put("float", float.class);
+        loadClassMap.put("double", double.class);
+        loadClassMap.put("boolean", boolean.class);
+        loadClassMap.put("char", char.class);
+        loadClassMap.put("void", void.class);
+    }
+
+    protected static Class<?>[] COMMON_CLASSES =
+        new Class[] {boolean.class, byte.class, char.class, short.class, int.class, long.class, float.class,
+                     double.class, Boolean.class, Byte.class, Character.class, Short.class, Integer.class, Long.class,
+                     Float.class, Double.class, String.class, boolean[].class, byte[].class, char[].class,
+                     short[].class, int[].class, long[].class, float[].class, double[].class, String[].class,
+                     BigInteger.class, BigDecimal.class, Calendar.class, Date.class, QName.class, URI.class,
+                     XMLGregorianCalendar.class, Duration.class,
+                     // java.lang.Object.class, 
+                     Image.class,
+                     // java2XSD.put(javax.activation.DataHandler.class, "base64Binary");
+                     Source.class, UUID.class
+
+        };
+
+    protected static final Set<Class<?>> COMMON_CLASSES_SET = new HashSet<Class<?>>(Arrays.asList(COMMON_CLASSES));
+
+    protected LRUCache<Object, JAXBContext> cache;
+    protected LRUCache<JAXBContext, Unmarshaller> upool;
+    protected LRUCache<JAXBContext, Marshaller> mpool;
+    protected JAXBContext commonContext;
+
+    public JAXBContextCache() {
+        this(64, 64, 64);
+    }
+
+    public JAXBContextCache(int contextSize, int marshallerSize, int unmarshallerSize) {
+        cache = new LRUCache<Object, JAXBContext>(new WeakHashMap<Object, JAXBContext>(), contextSize);
+        upool = new LRUCache<JAXBContext, Unmarshaller>(new WeakHashMap<JAXBContext, Unmarshaller>(), unmarshallerSize);
+        mpool = new LRUCache<JAXBContext, Marshaller>(new WeakHashMap<JAXBContext, Marshaller>(), marshallerSize);
+        commonContext = getCommonJAXBContext();
+    }
+
+    public static JAXBContext getCommonJAXBContext() {
+        try {
+            return JAXBContext.newInstance(COMMON_CLASSES);
+        } catch (JAXBException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * @param name of primitive type
+     * @return primitive Class or null
+     */
+    public static Class<?> getPrimitiveClass(String text) {
+        return loadClassMap.get(text);
+    }
+
+    /**
+     * Return the class for this name
+     *
+     * @return Class
+     */
+    private static Class<?> forName(final String className, final boolean initialize, final ClassLoader classloader)
+        throws ClassNotFoundException {
+        // NOTE: This method must remain private because it uses AccessController
+        Class<?> cl = null;
+        try {
+            cl = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
+                public Class<?> run() throws ClassNotFoundException {
+                    // Class.forName does not support primitives
+                    Class<?> cls = getPrimitiveClass(className);
+                    if (cls == null) {
+                        cls = Class.forName(className, initialize, classloader);
+                    }
+                    return cls;
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            throw (ClassNotFoundException)e.getException();
+        }
+
+        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
+        }
+
+        try {
+            Class<?> cls = forName(p + ".package-info", 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
+        }
+
+        return false;
+    }
+
+    public Marshaller getMarshaller(JAXBContext context) throws JAXBException {
+        synchronized (mpool) {
+            Marshaller marshaller = mpool.get(context);
+            if (marshaller == null) {
+                marshaller = context.createMarshaller();
+                mpool.put(context, marshaller);
+            }
+            return marshaller;
+        }
+    }
+
+    public Unmarshaller getUnmarshaller(JAXBContext context) throws JAXBException {
+        synchronized (upool) {
+            Unmarshaller unmarshaller = upool.get(context);
+            if (unmarshaller == null) {
+                unmarshaller = context.createUnmarshaller();
+                upool.put(context, unmarshaller);
+            }
+            return unmarshaller;
+        }
+    }
+
+    public JAXBContext getJAXBContext(Class<?> cls) throws JAXBException {
+        if (COMMON_CLASSES_SET.contains(cls)) {
+            return commonContext;
+        }
+        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 = JAXBContext.newInstance(pkg.getName(), cls.getClassLoader());
+                cache.put(pkg, context);
+            } else {
+                context = JAXBContext.newInstance(cls);
+                cache.put(cls, context);
+            }
+            return context;
+
+        }
+    }
+
+    public void clear() {
+        synchronized (cache) {
+            cache.clear();
+        }
+        synchronized (upool) {
+            upool.clear();
+        }
+        synchronized (upool) {
+            upool.clear();
+        }
+    }
+
+    public static class LRUCache<K, V> {
+        private Map<K, V> cache;
+        private List<K> keyQueue;
+        private int queueSizeThreshold;
+
+        public LRUCache(int queueSizeThreshold) {
+            super();
+            this.cache = new HashMap<K, V>();
+            this.keyQueue = new ArrayList<K>();
+            this.queueSizeThreshold = queueSizeThreshold;
+        }
+
+        public LRUCache(Map<K, V> cache, int queueSizeThreshold) {
+            super();
+            this.cache = cache;
+            this.keyQueue = new ArrayList<K>(cache.keySet());
+            this.queueSizeThreshold = queueSizeThreshold;
+        }
+
+        public V get(K key) {
+            V value = cache.get(key);
+            if (value != null) {
+                // Move the most recently used key to the front of the queue
+                keyQueue.remove(key);
+                keyQueue.add(0, key);
+            }
+            return value;
+        }
+
+        public void put(K key, V value) {
+            if (cache.containsKey(key)) {
+                // Adjust the key usage
+                keyQueue.remove(key);
+                keyQueue.add(0, key);
+            } else {
+                if (keyQueue.size() >= queueSizeThreshold) {
+                    // Remove the least recently used key
+                    K last = keyQueue.remove(keyQueue.size() - 1);
+                    keyQueue.add(0, key);
+                    cache.remove(last);
+                } else {
+                    keyQueue.add(0, key);
+                }
+            }
+            cache.put(key, value);
+        }
+
+        public V remove(K key) {
+            V data = cache.remove(key);
+            if (data != null) {
+                keyQueue.remove(key);
+            }
+            return data;
+        }
+
+        public void clear() {
+            cache.clear();
+            keyQueue.clear();
+        }
+
+        public Map<K, V> getCache() {
+            return Collections.unmodifiableMap(cache);
+        }
+
+    }
+
+}

Propchange: incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextCache.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextHelper.java
URL: http://svn.apache.org/viewvc/incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextHelper.java?rev=657999&r1=657998&r2=657999&view=diff
==============================================================================
--- incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextHelper.java (original)
+++ incubator/tuscany/java/sca/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBContextHelper.java Mon May 19 14:49:56 2008
@@ -44,11 +44,14 @@
  *
  * @version $Rev$ $Date$
  */
+// FIXME: [rfeng] We probably should turn this into a pluggable system service
 public class JAXBContextHelper {
     // TODO: Do we need to set them for source and target?
     public static final String JAXB_CLASSES = "jaxb.classes";
 
     public static final String JAXB_CONTEXT_PATH = "jaxb.contextPath";
+    
+    private static final JAXBContextCache cache = new JAXBContextCache();
 
     private JAXBContextHelper() {
     }
@@ -65,7 +68,7 @@
         JAXBContext context = null;
         Class<?> cls = getJavaType(dataType);
 
-        context = JAXBContext.newInstance(cls);
+        context = cache.getJAXBContext(cls);
 
         if (context == null) {
             throw new TransformationException("JAXB context is not set for the transformation.");

Added: incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java
URL: http://svn.apache.org/viewvc/incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java?rev=657999&view=auto
==============================================================================
--- incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java (added)
+++ incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java Mon May 19 14:49:56 2008
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * 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.    
+ */
+
+package org.apache.tuscany.databinding.jaxb;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import junit.framework.Assert;
+
+import org.apache.tuscany.sca.databinding.jaxb.JAXBContextCache;
+import org.apache.tuscany.sca.databinding.jaxb.JAXBContextCache.LRUCache;
+import org.junit.Test;
+
+import com.example.ipo.jaxb.Address;
+import com.example.ipo.jaxb.PurchaseOrderType;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class JAXBContextCacheTestCase {
+    @Test
+    public void testCache() throws JAXBException {
+        JAXBContextCache cache = new JAXBContextCache();
+        JAXBContext context1 = cache.getJAXBContext(String.class);
+        JAXBContext context2 = cache.getJAXBContext(int.class);
+        JAXBContext context3 = cache.getJAXBContext(String[].class);
+        JAXBContext context4 = cache.getJAXBContext(Source.class);
+        Assert.assertSame(context1, context2);
+        Assert.assertSame(context2, context3);
+        Assert.assertSame(context3, context4);
+
+        QName name = new QName("http://example.com/ns1", "e1");
+        JAXBElement<String> element = new JAXBElement<String>(name, String.class, "123");
+        StringWriter sw = new StringWriter();
+        context4.createMarshaller().marshal(element, sw);
+        StreamSource source = new StreamSource(new StringReader(sw.toString()), null);
+        context4.createUnmarshaller().unmarshal(source, String.class);
+
+        JAXBContext context5 = cache.getJAXBContext(Address.class);
+        JAXBContext context6 = cache.getJAXBContext(PurchaseOrderType.class);
+        Assert.assertSame(context5, context6);
+    }
+
+    @Test
+    public void testLRUCache() {
+        JAXBContextCache.LRUCache<String, String> cache = new LRUCache<String, String>(3);
+        cache.put("1", "A");
+        Assert.assertEquals(1, cache.getCache().size());
+        cache.put("2", "B");
+        Assert.assertEquals(2, cache.getCache().size());
+        cache.put("3", "C");
+        Assert.assertEquals(3, cache.getCache().size());
+        cache.put("4", "D");
+        Assert.assertEquals(3, cache.getCache().size());
+        String data = cache.get("1");
+        Assert.assertNull(data);
+        data = cache.get("2");
+        Assert.assertEquals("B", data);
+        cache.put("5", "E");
+        data = cache.get("2");
+        Assert.assertEquals("B", data);
+    }
+
+    @Test
+    public void testPerf() throws JAXBException {
+        JAXBContextCache cache = new JAXBContextCache();
+        
+        // Test JAXBContext for simple java classes
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < 100; i++) {
+            JAXBContext context = JAXBContext.newInstance(String.class);
+        }
+        long end = System.currentTimeMillis();
+        long d1 = end - start;
+        start = System.currentTimeMillis();
+        for (int i = 0; i < 100; i++) {
+            JAXBContext context = cache.getJAXBContext(String.class);
+        }
+        end = System.currentTimeMillis();
+        long d2 = end - start;
+        System.out.println(d1 + "ms vs. " + d2 + "ms");
+        
+        // Test JAXBContext for generated JAXB classes
+        start = System.currentTimeMillis();
+        for (int i = 0; i < 20; i++) {
+            JAXBContext context = JAXBContext.newInstance(PurchaseOrderType.class);
+        }
+        end = System.currentTimeMillis();
+        d1 = end - start;
+        start = System.currentTimeMillis();
+        for (int i = 0; i < 20; i++) {
+            JAXBContext context = cache.getJAXBContext(PurchaseOrderType.class);
+        }
+        end = System.currentTimeMillis();
+        d2 = end - start;
+        System.out.println(d1 + "ms vs. " + d2 + "ms");
+
+    }
+}

Propchange: incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/tuscany/java/sca/modules/databinding-jaxb/src/test/java/org/apache/tuscany/databinding/jaxb/JAXBContextCacheTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date