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