You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by da...@apache.org on 2012/09/18 09:50:35 UTC

svn commit: r1387025 - in /activemq/trunk/activemq-core/src: main/java/org/apache/activemq/util/ test/java/org/apache/activemq/util/

Author: davsclaus
Date: Tue Sep 18 07:50:35 2012
New Revision: 1387025

URL: http://svn.apache.org/viewvc?rev=1387025&view=rev
Log:
AMQ-4011: IntrospectionSupport does not use java bean propery editors which are not thread safe, cause leaks, and are jvm global causing issues for other apps.

Added:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java   (with props)
Removed:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/ListEditor.java
Modified:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/BooleanEditor.java
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/IntrospectionSupport.java
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryIntPropertyEditor.java
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/TypeConversionSupport.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/ReflectionSupportTest.java
    activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/SimplePojo.java

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/BooleanEditor.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/BooleanEditor.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/BooleanEditor.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/BooleanEditor.java Tue Sep 18 07:50:35 2012
@@ -18,7 +18,12 @@ package org.apache.activemq.util;
 
 import java.beans.PropertyEditorSupport;
 
-@Deprecated
+/**
+ * Used by xbean to set booleans.
+ * <p/>
+ * <b>Important: </b> Do not use this for other purposes than xbean, as property editors
+ * are not thread safe, and they are slow to use.
+ */
 public class BooleanEditor extends PropertyEditorSupport {
 
     public String getJavaInitializationString() {

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/IntrospectionSupport.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/IntrospectionSupport.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/IntrospectionSupport.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/IntrospectionSupport.java Tue Sep 18 07:50:35 2012
@@ -16,12 +16,9 @@
  */
 package org.apache.activemq.util;
 
-import java.beans.PropertyEditor;
-import java.beans.PropertyEditorManager;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -34,34 +31,13 @@ import java.util.Set;
 import javax.net.ssl.SSLServerSocket;
 
 import org.apache.activemq.command.ActiveMQDestination;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public final class IntrospectionSupport {
-    
-    static {
-        // Add Spring and ActiveMQ specific property editors
-        String[] additionalPath = new String[] {
-                "org.springframework.beans.propertyeditors",
-                "org.apache.activemq.util" };
-        synchronized (PropertyEditorManager.class) {
-            List<String> list = new ArrayList<String>();
-            list.addAll(Arrays.asList(PropertyEditorManager.getEditorSearchPath()));
 
-            if (!list.contains(additionalPath[0])) {
-                list.add(additionalPath[0]);
-            }
-            if (!list.contains(additionalPath[1])) {
-                list.add(additionalPath[1]);
-            }
+    private static final Logger LOG = LoggerFactory.getLogger(IntrospectionSupport.class);
 
-            String[] newSearchPath = list.toArray(new String[list.size()]);
-            try {
-                PropertyEditorManager.setEditorSearchPath(newSearchPath);
-            } catch(java.security.AccessControlException ignore) {
-                // we might be in an applet...
-            }
-        }
-    }
-    
     private IntrospectionSupport() {
     }
 
@@ -86,7 +62,7 @@ public final class IntrospectionSupport 
             String name = method.getName();
             Class type = method.getReturnType();
             Class params[] = method.getParameterTypes();
-            if ((name.startsWith("is") || name.startsWith("get")) && params.length == 0 && type != null && isSettableType(type)) {
+            if ((name.startsWith("is") || name.startsWith("get")) && params.length == 0 && type != null) {
 
                 try {
 
@@ -208,33 +184,75 @@ public final class IntrospectionSupport 
         }
     }
 
-    private static Object convert(Object value, Class type) {
+    private static Object convert(Object value, Class to) {
+        if (value == null) {
+            // lets avoid NullPointerException when converting to boolean for null values
+            if (boolean.class.isAssignableFrom(to)) {
+                return Boolean.FALSE;
+            }
+            return null;
+        }
+
+        // eager same instance type test to avoid the overhead of invoking the type converter
+        // if already same type
+        if (to.isAssignableFrom(value.getClass())) {
+            return to.cast(value);
+        }
+
         // special for String[] as we do not want to use a PropertyEditor for that
-        if (type.isAssignableFrom(String[].class)) {
+        if (to.isAssignableFrom(String[].class)) {
             return StringArrayConverter.convertToStringArray(value);
         }
 
-        PropertyEditor editor = PropertyEditorManager.findEditor(type);
-        if (editor != null) {
-            editor.setAsText(value.toString());
-            return editor.getValue();
+        // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
+        if (value.getClass().equals(String.class) && to.equals(List.class)) {
+            Object answer = StringToListOfActiveMQDestinationConverter.convertToActiveMQDestination(value);
+            if (answer != null) {
+                return answer;
+            }
+        }
+
+        TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), to);
+        if (converter != null) {
+            return converter.convert(value);
+        } else {
+            throw new IllegalArgumentException("Cannot convert from " + value.getClass()
+                    + " to " + to + " with value " + value);
         }
-        return null;
     }
 
-    public static String convertToString(Object value, Class type) {
+    public static String convertToString(Object value, Class to) {
+        if (value == null) {
+            return null;
+        }
+
+        // already a String
+        if (value instanceof String) {
+            return (String) value;
+        }
+
         // special for String[] as we do not want to use a PropertyEditor for that
-        if (value != null && value.getClass().isAssignableFrom(String[].class)) {
+        if (String[].class.isInstance(value)) {
             String[] array = (String[]) value;
             return StringArrayConverter.convertToString(array);
         }
 
-        PropertyEditor editor = PropertyEditorManager.findEditor(type);
-        if (editor != null) {
-            editor.setValue(value);
-            return editor.getAsText();
+        // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
+        if (List.class.isInstance(value)) {
+            // if the list is a ActiveMQDestination, then return a comma list
+            String answer = StringToListOfActiveMQDestinationConverter.convertFromActiveMQDestination(value);
+            if (answer != null) {
+                return answer;
+            }
+        }
+
+        TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), String.class);
+        if (converter != null) {
+            return (String) converter.convert(value);
+        } else {
+            throw new IllegalArgumentException("Cannot convert from " + value.getClass()
+                    + " to " + to + " with value " + value);
         }
-        return null;
     }
 
     private static Method findSetterMethod(Class clazz, String name) {
@@ -251,19 +269,6 @@ public final class IntrospectionSupport 
         return null;
     }
 
-    private static boolean isSettableType(Class clazz) {
-        // special for String[]
-        if (clazz.isAssignableFrom(String[].class)) {
-            return true;
-        }
-
-        if (PropertyEditorManager.findEditor(clazz) != null) {
-            return true;
-        }
-        	
-        return false;
-    }
-
     public static String toString(Object target) {
         return toString(target, Object.class, null);
     }
@@ -349,7 +354,7 @@ public final class IntrospectionSupport 
                 }
                 map.put(field.getName(), o);
             } catch (Throwable e) {
-                e.printStackTrace();
+                LOG.debug("Error getting field " + field + " on class " + startClass + ". This exception is ignored.", e);
             }
         }
 

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryIntPropertyEditor.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryIntPropertyEditor.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryIntPropertyEditor.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryIntPropertyEditor.java Tue Sep 18 07:50:35 2012
@@ -21,10 +21,14 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
+ * Used by xbean to set integers.
+ * <p/>
+ * <b>Important: </b> Do not use this for other purposes than xbean, as property editors
+ * are not thread safe, and they are slow to use.
+ * <p/>
  * Converts string values like "20 Mb", "1024kb", and "1g" to int values in
  * bytes.
  */
-@Deprecated
 public class MemoryIntPropertyEditor extends PropertyEditorSupport {
     public void setAsText(String text) throws IllegalArgumentException {
 

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/MemoryPropertyEditor.java Tue Sep 18 07:50:35 2012
@@ -21,10 +21,14 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
+ * Used by xbean to set longs.
+ * <p/>
+ * <b>Important: </b> Do not use this for other purposes than xbean, as property editors
+ * are not thread safe, and they are slow to use.
+ * <p/>
  * Converts string values like "20 Mb", "1024kb", and "1g" to long values in
  * bytes.
  */
-@Deprecated
 public class MemoryPropertyEditor extends PropertyEditorSupport {
     public void setAsText(String text) throws IllegalArgumentException {
 

Added: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java?rev=1387025&view=auto
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java (added)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java Tue Sep 18 07:50:35 2012
@@ -0,0 +1,84 @@
+/**
+ * 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.activemq.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.activemq.command.ActiveMQDestination;
+import org.springframework.util.StringUtils;
+
+/**
+ * Special converter for String -> List<ActiveMQDestination> to be used instead of a
+ * {@link java.beans.PropertyEditor} which otherwise causes
+ * memory leaks as the JDK {@link java.beans.PropertyEditorManager}
+ * is a static class and has strong references to classes, causing
+ * problems in hot-deployment environments.
+ */
+public class StringToListOfActiveMQDestinationConverter {
+
+    public static List<ActiveMQDestination> convertToActiveMQDestination(Object value) {
+        if (value == null) {
+            return null;
+        }
+
+        // text must be enclosed with []
+
+        String text = value.toString();
+        if (text.startsWith("[") && text.endsWith("]")) {
+            text = text.substring(1, text.length() - 1);
+            String[] array = StringUtils.delimitedListToStringArray(text, ",", null);
+
+            List<ActiveMQDestination> list = new ArrayList<ActiveMQDestination>();
+            for (String item : array) {
+                list.add(ActiveMQDestination.createDestination(item.trim(), ActiveMQDestination.QUEUE_TYPE));
+            }
+            return list;
+        } else {
+            return null;
+        }
+    }
+
+    public static String convertFromActiveMQDestination(Object value) {
+        if (value == null) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder("[");
+        if (value instanceof List) {
+            List list = (List) value;
+            for (int i = 0; i < list.size(); i++) {
+                Object e = list.get(i);
+                if (e instanceof ActiveMQDestination) {
+                    ActiveMQDestination destination = (ActiveMQDestination) e;
+                    sb.append(destination);
+                    if (i < list.size() - 1) {
+                        sb.append(", ");
+                    }
+                }
+            }
+        }
+        sb.append("]");
+
+        if (sb.length() > 2) {
+            return sb.toString();
+        } else {
+            return null;
+        }
+    }
+
+}

Propchange: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/StringToListOfActiveMQDestinationConverter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/TypeConversionSupport.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/TypeConversionSupport.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/TypeConversionSupport.java (original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/TypeConversionSupport.java Tue Sep 18 07:50:35 2012
@@ -16,12 +16,17 @@
  */
 package org.apache.activemq.util;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.activemq.command.ActiveMQDestination;
 
+/**
+ * Type conversion support for ActiveMQ.
+ */
 public final class TypeConversionSupport {
 
     private static class ConversionKey {
@@ -45,7 +50,7 @@ public final class TypeConversionSupport
         }
     }
 
-    interface Converter {
+    public interface Converter {
         Object convert(Object value);
     }
 
@@ -138,16 +143,25 @@ public final class TypeConversionSupport
                 return ActiveMQDestination.createDestination((String)value, ActiveMQDestination.QUEUE_TYPE);
             }
         });
+        CONVERSION_MAP.put(new ConversionKey(String.class, URI.class), new Converter() {
+            public Object convert(Object value) {
+                String text = value.toString();
+                try {
+                    return new URI(text);
+                } catch (URISyntaxException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
     }
 
     private TypeConversionSupport() {
     }
 
-    public static Object convert(Object value, Class type) {
-
+    public static Object convert(Object value, Class to) {
         if (value == null) {
             // lets avoid NullPointerException when converting to boolean for null values
-            if (boolean.class.isAssignableFrom(type)) {
+            if (boolean.class.isAssignableFrom(to)) {
                 return Boolean.FALSE;
             }
             return null;
@@ -155,12 +169,12 @@ public final class TypeConversionSupport
 
         // eager same instance type test to avoid the overhead of invoking the type converter
         // if already same type
-        if (type.isInstance(value)) {
-            return type.cast(value);
+        if (to.isInstance(value)) {
+            return to.cast(value);
         }
 
         // lookup converter
-        Converter c = CONVERSION_MAP.get(new ConversionKey(value.getClass(), type));
+        Converter c = lookupConverter(value.getClass(), to);
         if (c != null) {
             return c.convert(value);
         } else {
@@ -168,4 +182,42 @@ public final class TypeConversionSupport
         }
     }
 
+    public static Converter lookupConverter(Class from, Class to) {
+        // use wrapped type for primitives
+        if (from.isPrimitive()) {
+            from = convertPrimitiveTypeToWrapperType(from);
+        }
+        if (to.isPrimitive()) {
+            to = convertPrimitiveTypeToWrapperType(to);
+        }
+
+        return CONVERSION_MAP.get(new ConversionKey(from, to));
+    }
+
+    /**
+     * Converts primitive types such as int to its wrapper type like
+     * {@link Integer}
+     */
+    private static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
+        Class<?> rc = type;
+        if (type.isPrimitive()) {
+            if (type == int.class) {
+                rc = Integer.class;
+            } else if (type == long.class) {
+                rc = Long.class;
+            } else if (type == double.class) {
+                rc = Double.class;
+            } else if (type == float.class) {
+                rc = Float.class;
+            } else if (type == short.class) {
+                rc = Short.class;
+            } else if (type == byte.class) {
+                rc = Byte.class;
+            } else if (type == boolean.class) {
+                rc = Boolean.class;
+            }
+        }
+        return rc;
+    }
+
 }

Modified: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/ReflectionSupportTest.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/ReflectionSupportTest.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/ReflectionSupportTest.java (original)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/ReflectionSupportTest.java Tue Sep 18 07:50:35 2012
@@ -52,6 +52,7 @@ public class ReflectionSupportTest exten
         map.put("favorites", favoritesString);
         map.put("nonFavorites", nonFavoritesString);
         map.put("others", null);
+        map.put("systems", "windows,mac");
         
         IntrospectionSupport.setProperties(pojo, map);
         
@@ -62,6 +63,8 @@ public class ReflectionSupportTest exten
         assertEquals(favorites, pojo.getFavorites());
         assertEquals(nonFavorites, pojo.getNonFavorites());
         assertNull(pojo.getOthers());
+        assertEquals("windows", pojo.getSystems()[0]);
+        assertEquals("mac", pojo.getSystems()[1]);
     }
     
     public void testGetProperties() {
@@ -72,6 +75,7 @@ public class ReflectionSupportTest exten
     	pojo.setFavorites(favorites);
     	pojo.setNonFavorites(nonFavorites);
     	pojo.setOthers(null);
+        pojo.setSystems(new String[]{"windows", "mac"});
     	
     	Properties props = new Properties();
     	
@@ -79,10 +83,11 @@ public class ReflectionSupportTest exten
     	
     	assertEquals("Dejan", props.get("name"));
     	assertEquals("31", props.get("age"));
-    	assertEquals("True", props.get("enabled"));
+    	assertEquals("true", props.get("enabled"));
     	assertEquals(favoritesString, props.get("favorites"));
     	assertEquals(nonFavoritesString, props.get("nonFavorites"));
     	assertNull(props.get("others"));
+        assertEquals("windows,mac", props.get("systems"));
     }
     
     public void testSetBoolean() {

Modified: activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/SimplePojo.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/SimplePojo.java?rev=1387025&r1=1387024&r2=1387025&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/SimplePojo.java (original)
+++ activemq/trunk/activemq-core/src/test/java/org/apache/activemq/util/SimplePojo.java Tue Sep 18 07:50:35 2012
@@ -31,6 +31,7 @@ public class SimplePojo {
     List<ActiveMQDestination> favorites = new ArrayList<ActiveMQDestination>();
     List<ActiveMQDestination> nonFavorites = new ArrayList<ActiveMQDestination>();
     List<ActiveMQDestination> others = new ArrayList<ActiveMQDestination>();
+    String[] systems;
     
     public int getAge() {
         return age;
@@ -74,5 +75,10 @@ public class SimplePojo {
 	public void setOthers(List<ActiveMQDestination> others) {
 		this.others = others;
 	}
-    
+    public String[] getSystems() {
+        return systems;
+    }
+    public void setSystems(String[] systems) {
+        this.systems = systems;
+    }
 }