001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.util;
018    
019    import java.lang.reflect.Field;
020    import java.lang.reflect.Method;
021    import java.lang.reflect.Modifier;
022    import java.util.Arrays;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.LinkedHashMap;
026    import java.util.List;
027    import java.util.Locale;
028    import java.util.Map;
029    import java.util.Map.Entry;
030    import java.util.Set;
031    
032    import javax.net.ssl.SSLServerSocket;
033    
034    import org.apache.activemq.command.ActiveMQDestination;
035    import org.slf4j.Logger;
036    import org.slf4j.LoggerFactory;
037    
038    public final class IntrospectionSupport {
039    
040        private static final Logger LOG = LoggerFactory.getLogger(IntrospectionSupport.class);
041    
042        private IntrospectionSupport() {
043        }
044    
045        public static boolean getProperties(Object target, Map props, String optionPrefix) {
046    
047            boolean rc = false;
048            if (target == null) {
049                throw new IllegalArgumentException("target was null.");
050            }
051            if (props == null) {
052                throw new IllegalArgumentException("props was null.");
053            }
054    
055            if (optionPrefix == null) {
056                optionPrefix = "";
057            }
058    
059            Class<?> clazz = target.getClass();
060            Method[] methods = clazz.getMethods();
061            for (Method method : methods) {
062                String name = method.getName();
063                Class<?> type = method.getReturnType();
064                Class<?> params[] = method.getParameterTypes();
065                if ((name.startsWith("is") || name.startsWith("get")) && params.length == 0 && type != null) {
066    
067                    try {
068    
069                        Object value = method.invoke(target);
070                        if (value == null) {
071                            continue;
072                        }
073    
074                        String strValue = convertToString(value, type);
075                        if (strValue == null) {
076                            continue;
077                        }
078                        if (name.startsWith("get")) {
079                            name = name.substring(3, 4).toLowerCase(Locale.ENGLISH)
080                                    + name.substring(4);
081                        } else {
082                            name = name.substring(2, 3).toLowerCase(Locale.ENGLISH)
083                                    + name.substring(3);
084                        }
085                        props.put(optionPrefix + name, strValue);
086                        rc = true;
087    
088                    } catch (Throwable ignore) {
089                    }
090                }
091            }
092    
093            return rc;
094        }
095    
096        public static boolean setProperties(Object target, Map<String, ?> props, String optionPrefix) {
097            boolean rc = false;
098            if (target == null) {
099                throw new IllegalArgumentException("target was null.");
100            }
101            if (props == null) {
102                throw new IllegalArgumentException("props was null.");
103            }
104    
105            for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) {
106                String name = iter.next();
107                if (name.startsWith(optionPrefix)) {
108                    Object value = props.get(name);
109                    name = name.substring(optionPrefix.length());
110                    if (setProperty(target, name, value)) {
111                        iter.remove();
112                        rc = true;
113                    }
114                }
115            }
116            return rc;
117        }
118    
119        public static Map<String, Object> extractProperties(Map props, String optionPrefix) {
120            if (props == null) {
121                throw new IllegalArgumentException("props was null.");
122            }
123    
124            HashMap<String, Object> rc = new HashMap<String, Object>(props.size());
125    
126            for (Iterator<?> iter = props.keySet().iterator(); iter.hasNext();) {
127                String name = (String)iter.next();
128                if (name.startsWith(optionPrefix)) {
129                    Object value = props.get(name);
130                    name = name.substring(optionPrefix.length());
131                    rc.put(name, value);
132                    iter.remove();
133                }
134            }
135    
136            return rc;
137        }
138    
139        public static boolean setProperties(Object target, Map props) {
140            boolean rc = false;
141    
142            if (target == null) {
143                throw new IllegalArgumentException("target was null.");
144            }
145            if (props == null) {
146                throw new IllegalArgumentException("props was null.");
147            }
148    
149            for (Iterator<?> iter = props.entrySet().iterator(); iter.hasNext();) {
150                Map.Entry<?,?> entry = (Entry<?,?>)iter.next();
151                if (setProperty(target, (String)entry.getKey(), entry.getValue())) {
152                    iter.remove();
153                    rc = true;
154                }
155            }
156    
157            return rc;
158        }
159    
160        public static boolean setProperty(Object target, String name, Object value) {
161            try {
162                Class<?> clazz = target.getClass();
163                if (target instanceof SSLServerSocket) {
164                    // overcome illegal access issues with internal implementation class
165                    clazz = SSLServerSocket.class;
166                }
167                Method setter = findSetterMethod(clazz, name);
168                if (setter == null) {
169                    return false;
170                }
171    
172                // If the type is null or it matches the needed type, just use the
173                // value directly
174                if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
175                    setter.invoke(target, value);
176                } else {
177                    // We need to convert it
178                    setter.invoke(target, convert(value, setter.getParameterTypes()[0]));
179                }
180                return true;
181            } catch (Throwable ignore) {
182                return false;
183            }
184        }
185    
186        private static Object convert(Object value, Class to) {
187            if (value == null) {
188                // lets avoid NullPointerException when converting to boolean for null values
189                if (boolean.class.isAssignableFrom(to)) {
190                    return Boolean.FALSE;
191                }
192                return null;
193            }
194    
195            // eager same instance type test to avoid the overhead of invoking the type converter
196            // if already same type
197            if (to.isAssignableFrom(value.getClass())) {
198                return to.cast(value);
199            }
200    
201            // special for String[] as we do not want to use a PropertyEditor for that
202            if (to.isAssignableFrom(String[].class)) {
203                return StringArrayConverter.convertToStringArray(value);
204            }
205    
206            // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
207            if (value.getClass().equals(String.class) && to.equals(List.class)) {
208                Object answer = StringToListOfActiveMQDestinationConverter.convertToActiveMQDestination(value);
209                if (answer != null) {
210                    return answer;
211                }
212            }
213    
214            TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), to);
215            if (converter != null) {
216                return converter.convert(value);
217            } else {
218                throw new IllegalArgumentException("Cannot convert from " + value.getClass()
219                        + " to " + to + " with value " + value);
220            }
221        }
222    
223        public static String convertToString(Object value, Class to) {
224            if (value == null) {
225                return null;
226            }
227    
228            // already a String
229            if (value instanceof String) {
230                return (String) value;
231            }
232    
233            // special for String[] as we do not want to use a PropertyEditor for that
234            if (String[].class.isInstance(value)) {
235                String[] array = (String[]) value;
236                return StringArrayConverter.convertToString(array);
237            }
238    
239            // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
240            if (List.class.isInstance(value)) {
241                // if the list is a ActiveMQDestination, then return a comma list
242                String answer = StringToListOfActiveMQDestinationConverter.convertFromActiveMQDestination(value);
243                if (answer != null) {
244                    return answer;
245                }
246            }
247    
248            TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), String.class);
249            if (converter != null) {
250                return (String) converter.convert(value);
251            } else {
252                throw new IllegalArgumentException("Cannot convert from " + value.getClass()
253                        + " to " + to + " with value " + value);
254            }
255        }
256    
257        private static Method findSetterMethod(Class clazz, String name) {
258            // Build the method name.
259            name = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
260            Method[] methods = clazz.getMethods();
261            for (Method method : methods) {
262                Class<?> params[] = method.getParameterTypes();
263                if (method.getName().equals(name) && params.length == 1 ) {
264                    return method;
265                }
266            }
267            return null;
268        }
269    
270        public static String toString(Object target) {
271            return toString(target, Object.class, null);
272        }
273    
274        public static String toString(Object target, Class stopClass) {
275            return toString(target, stopClass, null);
276        }
277    
278        public static String toString(Object target, Class stopClass, Map<String, Object> overrideFields) {
279            LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
280            addFields(target, target.getClass(), stopClass, map);
281            if (overrideFields != null) {
282                for(String key : overrideFields.keySet()) {
283                    Object value = overrideFields.get(key);
284                    map.put(key, value);
285                }
286    
287            }
288            StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
289            buffer.append(" {");
290            Set<Entry<String, Object>> entrySet = map.entrySet();
291            boolean first = true;
292            for (Map.Entry<String,Object> entry : entrySet) {
293                Object value = entry.getValue();
294                Object key = entry.getKey();
295                if (first) {
296                    first = false;
297                } else {
298                    buffer.append(", ");
299                }
300                buffer.append(key);
301                buffer.append(" = ");
302    
303                appendToString(buffer, key, value);
304            }
305            buffer.append("}");
306            return buffer.toString();
307        }
308    
309        protected static void appendToString(StringBuffer buffer, Object key, Object value) {
310            if (value instanceof ActiveMQDestination) {
311                ActiveMQDestination destination = (ActiveMQDestination)value;
312                buffer.append(destination.getQualifiedName());
313            } else if (key.toString().toLowerCase(Locale.ENGLISH).contains("password")){
314                buffer.append("*****");
315            } else {
316                buffer.append(value);
317            }
318        }
319    
320        public static String simpleName(Class clazz) {
321            String name = clazz.getName();
322            int p = name.lastIndexOf(".");
323            if (p >= 0) {
324                name = name.substring(p + 1);
325            }
326            return name;
327        }
328    
329        private static void addFields(Object target, Class startClass, Class<Object> stopClass, LinkedHashMap<String, Object> map) {
330    
331            if (startClass != stopClass) {
332                addFields(target, startClass.getSuperclass(), stopClass, map);
333            }
334    
335            Field[] fields = startClass.getDeclaredFields();
336            for (Field field : fields) {
337                if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())
338                    || Modifier.isPrivate(field.getModifiers())) {
339                    continue;
340                }
341    
342                try {
343                    field.setAccessible(true);
344                    Object o = field.get(target);
345                    if (o != null && o.getClass().isArray()) {
346                        try {
347                            o = Arrays.asList((Object[])o);
348                        } catch (Throwable e) {
349                        }
350                    }
351                    map.put(field.getName(), o);
352                } catch (Throwable e) {
353                    LOG.debug("Error getting field " + field + " on class " + startClass + ". This exception is ignored.", e);
354                }
355            }
356        }
357    }