You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2012/02/15 20:04:10 UTC

svn commit: r1244644 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java test/java/org/apache/camel/issues/MyCamelBean.java test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java

Author: davsclaus
Date: Wed Feb 15 19:04:10 2012
New Revision: 1244644

URL: http://svn.apache.org/viewvc?rev=1244644&view=rev
Log:
CAMEL-5002: Improved type converter lookup to avoid synchronization.

Added:
    camel/trunk/camel-core/src/test/java/org/apache/camel/issues/MyCamelBean.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java?rev=1244644&r1=1244643&r2=1244644&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java Wed Feb 15 19:04:10 2012
@@ -52,7 +52,7 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class BaseTypeConverterRegistry extends ServiceSupport implements TypeConverter, TypeConverterRegistry {
     protected final transient Logger log = LoggerFactory.getLogger(getClass());
-    protected final Map<TypeMapping, TypeConverter> typeMappings = new ConcurrentHashMap<TypeMapping, TypeConverter>();
+    protected final ConcurrentHashMap<TypeMapping, TypeConverter> typeMappings = new ConcurrentHashMap<TypeMapping, TypeConverter>();
     protected final Map<TypeMapping, TypeMapping> misses = new ConcurrentHashMap<TypeMapping, TypeMapping>();
     protected final List<TypeConverterLoader> typeConverterLoaders = new ArrayList<TypeConverterLoader>();
     protected final List<FallbackTypeConverter> fallbackConverters = new ArrayList<FallbackTypeConverter>();
@@ -237,18 +237,16 @@ public abstract class BaseTypeConverterR
     public void addTypeConverter(Class<?> toType, Class<?> fromType, TypeConverter typeConverter) {
         log.trace("Adding type converter: {}", typeConverter);
         TypeMapping key = new TypeMapping(toType, fromType);
-        synchronized (typeMappings) {
-            TypeConverter converter = typeMappings.get(key);
-            // only override it if its different
-            // as race conditions can lead to many threads trying to promote the same fallback converter
-            if (typeConverter != converter) {
-                if (converter != null) {
-                    log.warn("Overriding type converter from: " + converter + " to: " + typeConverter);
-                }
-                typeMappings.put(key, typeConverter);
-                // remove any previous misses, as we added the new type converter
-                misses.remove(key);
+        TypeConverter converter = typeMappings.get(key);
+        // only override it if its different
+        // as race conditions can lead to many threads trying to promote the same fallback converter
+        if (typeConverter != converter) {
+            if (converter != null) {
+                log.warn("Overriding type converter from: " + converter + " to: " + typeConverter);
             }
+            typeMappings.put(key, typeConverter);
+            // remove any previous misses, as we added the new type converter
+            misses.remove(key);
         }
     }
 
@@ -281,22 +279,18 @@ public abstract class BaseTypeConverterR
 
     public Set<Class<?>> getFromClassMappings() {
         Set<Class<?>> answer = new HashSet<Class<?>>();
-        synchronized (typeMappings) {
-            for (TypeMapping mapping : typeMappings.keySet()) {
-                answer.add(mapping.getFromType());
-            }
+        for (TypeMapping mapping : typeMappings.keySet()) {
+            answer.add(mapping.getFromType());
         }
         return answer;
     }
 
     public Map<Class<?>, TypeConverter> getToClassMappings(Class<?> fromClass) {
         Map<Class<?>, TypeConverter> answer = new HashMap<Class<?>, TypeConverter>();
-        synchronized (typeMappings) {
-            for (Map.Entry<TypeMapping, TypeConverter> entry : typeMappings.entrySet()) {
-                TypeMapping mapping = entry.getKey();
-                if (mapping.isApplicable(fromClass)) {
-                    answer.put(mapping.getToType(), entry.getValue());
-                }
+        for (Map.Entry<TypeMapping, TypeConverter> entry : typeMappings.entrySet()) {
+            TypeMapping mapping = entry.getKey();
+            if (mapping.isApplicable(fromClass)) {
+                answer.put(mapping.getToType(), entry.getValue());
             }
         }
         return answer;
@@ -312,14 +306,12 @@ public abstract class BaseTypeConverterR
             fromType = value.getClass();
         }
         TypeMapping key = new TypeMapping(toType, fromType);
-        TypeConverter converter;
-        synchronized (typeMappings) {
-            converter = typeMappings.get(key);
-            if (converter == null) {
-                converter = lookup(toType, fromType);
-                if (converter != null) {
-                    typeMappings.put(key, converter);
-                }
+        TypeConverter converter = typeMappings.get(key);
+        if (converter == null) {
+            // converter not found, try to lookup then
+            converter = lookup(toType, fromType);
+            if (converter != null) {
+                typeMappings.putIfAbsent(key, converter);
             }
         }
         return converter;

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/issues/MyCamelBean.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/issues/MyCamelBean.java?rev=1244644&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/issues/MyCamelBean.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/issues/MyCamelBean.java Wed Feb 15 19:04:10 2012
@@ -0,0 +1,42 @@
+/**
+ * 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.camel.issues;
+
+/**
+ *
+ */
+public class MyCamelBean {
+    
+    private int id;
+    private String name;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java?rev=1244644&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TypeConverterConcurrencyIssueTest.java Wed Feb 15 19:04:10 2012
@@ -0,0 +1,67 @@
+/**
+ * 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.camel.issues;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.impl.converter.StaticMethodTypeConverter;
+import org.apache.camel.util.StopWatch;
+
+/**
+ * Testing for CAMEL-5002
+ */
+public class TypeConverterConcurrencyIssueTest extends ContextTestSupport {
+
+    private int size = 100 * 1000;
+    
+    public void testTypeConverter() throws Exception {
+        // add as type converter
+        Method method = TypeConverterConcurrencyIssueTest.class.getMethod("toMyCamelBean", String.class);
+        assertNotNull(method);
+        context.getTypeConverterRegistry().addTypeConverter(MyCamelBean.class, String.class, new StaticMethodTypeConverter(method));
+
+        ExecutorService pool = context.getExecutorServiceManager().newThreadPool(this, "test", 50, 50);
+        final CountDownLatch latch = new CountDownLatch(size);
+
+        StopWatch watch = new StopWatch();
+        for (int i = 0; i < size; i++) {
+            pool.submit(new Runnable() {
+                @Override
+                public void run() {
+                    MyCamelBean bean = context.getTypeConverter().convertTo(MyCamelBean.class, "1;MyCamel");
+                    latch.countDown();
+                }
+            });
+        }
+        
+        assertTrue(latch.await(1, TimeUnit.MINUTES));
+        log.info("Took " + watch.stop() + " millis to convert " + size + " objects");
+    }
+    
+    public static MyCamelBean toMyCamelBean(String body) {
+        MyCamelBean bean = new MyCamelBean();
+        String[] data = body.split(";");
+        bean.setId(Integer.parseInt(data[0]));
+        bean.setName(data[1]);
+        return bean;
+    }
+
+}