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    import javax.net.ssl.SSLServerSocket;
032    
033    import org.apache.activemq.command.ActiveMQDestination;
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    
037    public final class IntrospectionSupport {
038    
039        private static final Logger LOG = LoggerFactory.getLogger(IntrospectionSupport.class);
040    
041        private IntrospectionSupport() {
042        }
043    
044        public static boolean getProperties(Object target, Map props, String optionPrefix) {
045    
046            boolean rc = false;
047            if (target == null) {
048                throw new IllegalArgumentException("target was null.");
049            }
050            if (props == null) {
051                throw new IllegalArgumentException("props was null.");
052            }
053    
054            if (optionPrefix == null) {
055                optionPrefix = "";
056            }
057    
058            Class clazz = target.getClass();
059            Method[] methods = clazz.getMethods();
060            for (int i = 0; i < methods.length; i++) {
061                Method method = methods[i];
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    
094            return rc;
095        }
096    
097        public static boolean setProperties(Object target, Map<String, ?> props, String optionPrefix) {
098            boolean rc = false;
099            if (target == null) {
100                throw new IllegalArgumentException("target was null.");
101            }
102            if (props == null) {
103                throw new IllegalArgumentException("props was null.");
104            }
105    
106            for (Iterator<String> iter = props.keySet().iterator(); iter.hasNext();) {
107                String name = iter.next();
108                if (name.startsWith(optionPrefix)) {
109                    Object value = props.get(name);
110                    name = name.substring(optionPrefix.length());
111                    if (setProperty(target, name, value)) {
112                        iter.remove();
113                        rc = true;
114                    }
115                }
116            }
117            return rc;
118        }
119    
120        public static Map<String, Object> extractProperties(Map props, String optionPrefix) {
121            if (props == null) {
122                throw new IllegalArgumentException("props was null.");
123            }
124    
125            HashMap<String, Object> rc = new HashMap<String, Object>(props.size());
126    
127            for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
128                String name = (String)iter.next();
129                if (name.startsWith(optionPrefix)) {
130                    Object value = props.get(name);
131                    name = name.substring(optionPrefix.length());
132                    rc.put(name, value);
133                    iter.remove();
134                }
135            }
136    
137            return rc;
138        }
139    
140        public static boolean setProperties(Object target, Map props) {
141            boolean rc = false;
142    
143            if (target == null) {
144                throw new IllegalArgumentException("target was null.");
145            }
146            if (props == null) {
147                throw new IllegalArgumentException("props was null.");
148            }
149    
150            for (Iterator iter = props.entrySet().iterator(); iter.hasNext();) {
151                Map.Entry entry = (Entry)iter.next();
152                if (setProperty(target, (String)entry.getKey(), entry.getValue())) {
153                    iter.remove();
154                    rc = true;
155                }
156            }
157    
158            return rc;
159        }
160    
161        public static boolean setProperty(Object target, String name, Object value) {
162            try {
163                Class clazz = target.getClass();
164                if (target instanceof SSLServerSocket) {
165                    // overcome illegal access issues with internal implementation class
166                    clazz = SSLServerSocket.class;
167                }
168                Method setter = findSetterMethod(clazz, name);
169                if (setter == null) {
170                    return false;
171                }
172    
173                // If the type is null or it matches the needed type, just use the
174                // value directly
175                if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
176                    setter.invoke(target, value);
177                } else {
178                    // We need to convert it
179                    setter.invoke(target, convert(value, setter.getParameterTypes()[0]));
180                }
181                return true;
182            } catch (Throwable ignore) {
183                return false;
184            }
185        }
186    
187        private static Object convert(Object value, Class to) {
188            if (value == null) {
189                // lets avoid NullPointerException when converting to boolean for null values
190                if (boolean.class.isAssignableFrom(to)) {
191                    return Boolean.FALSE;
192                }
193                return null;
194            }
195    
196            // eager same instance type test to avoid the overhead of invoking the type converter
197            // if already same type
198            if (to.isAssignableFrom(value.getClass())) {
199                return to.cast(value);
200            }
201    
202            // special for String[] as we do not want to use a PropertyEditor for that
203            if (to.isAssignableFrom(String[].class)) {
204                return StringArrayConverter.convertToStringArray(value);
205            }
206    
207            // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
208            if (value.getClass().equals(String.class) && to.equals(List.class)) {
209                Object answer = StringToListOfActiveMQDestinationConverter.convertToActiveMQDestination(value);
210                if (answer != null) {
211                    return answer;
212                }
213            }
214    
215            TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), to);
216            if (converter != null) {
217                return converter.convert(value);
218            } else {
219                throw new IllegalArgumentException("Cannot convert from " + value.getClass()
220                        + " to " + to + " with value " + value);
221            }
222        }
223    
224        public static String convertToString(Object value, Class to) {
225            if (value == null) {
226                return null;
227            }
228    
229            // already a String
230            if (value instanceof String) {
231                return (String) value;
232            }
233    
234            // special for String[] as we do not want to use a PropertyEditor for that
235            if (String[].class.isInstance(value)) {
236                String[] array = (String[]) value;
237                return StringArrayConverter.convertToString(array);
238            }
239    
240            // special for String to List<ActiveMQDestination> as we do not want to use a PropertyEditor for that
241            if (List.class.isInstance(value)) {
242                // if the list is a ActiveMQDestination, then return a comma list
243                String answer = StringToListOfActiveMQDestinationConverter.convertFromActiveMQDestination(value);
244                if (answer != null) {
245                    return answer;
246                }
247            }
248    
249            TypeConversionSupport.Converter converter = TypeConversionSupport.lookupConverter(value.getClass(), String.class);
250            if (converter != null) {
251                return (String) converter.convert(value);
252            } else {
253                throw new IllegalArgumentException("Cannot convert from " + value.getClass()
254                        + " to " + to + " with value " + value);
255            }
256        }
257    
258        private static Method findSetterMethod(Class clazz, String name) {
259            // Build the method name.
260            name = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
261            Method[] methods = clazz.getMethods();
262            for (int i = 0; i < methods.length; i++) {
263                Method method = methods[i];
264                Class params[] = method.getParameterTypes();
265                if (method.getName().equals(name) && params.length == 1 ) {
266                    return method;
267                }
268            }
269            return null;
270        }
271    
272        public static String toString(Object target) {
273            return toString(target, Object.class, null);
274        }
275        
276        public static String toString(Object target, Class stopClass) {
277            return toString(target, stopClass, null);
278        }
279    
280        public static String toString(Object target, Class stopClass, Map<String, Object> overrideFields) {
281            LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
282            addFields(target, target.getClass(), stopClass, map);
283            if (overrideFields != null) {
284                    for(String key : overrideFields.keySet()) {
285                        Object value = overrideFields.get(key);
286                        map.put(key, value);
287                    }
288    
289            }
290            StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
291            buffer.append(" {");
292            Set entrySet = map.entrySet();
293            boolean first = true;
294            for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
295                Map.Entry entry = (Map.Entry)iter.next();
296                Object value = entry.getValue();
297                Object key = entry.getKey();
298                if (first) {
299                    first = false;
300                } else {
301                    buffer.append(", ");
302                }
303                buffer.append(key);
304                buffer.append(" = ");
305                
306                appendToString(buffer, key, value);
307            }
308            buffer.append("}");
309            return buffer.toString();
310        }
311    
312        protected static void appendToString(StringBuffer buffer, Object key, Object value) {
313            if (value instanceof ActiveMQDestination) {
314                ActiveMQDestination destination = (ActiveMQDestination)value;
315                buffer.append(destination.getQualifiedName());
316            } else if (key.toString().toLowerCase(Locale.ENGLISH).contains("password")){
317                buffer.append("*****");           
318            } else {
319                buffer.append(value);
320            }
321        }
322    
323        public static String simpleName(Class clazz) {
324            String name = clazz.getName();
325            int p = name.lastIndexOf(".");
326            if (p >= 0) {
327                name = name.substring(p + 1);
328            }
329            return name;
330        }
331    
332        private static void addFields(Object target, Class startClass, Class<Object> stopClass, LinkedHashMap<String, Object> map) {
333    
334            if (startClass != stopClass) {
335                addFields(target, startClass.getSuperclass(), stopClass, map);
336            }
337    
338            Field[] fields = startClass.getDeclaredFields();
339            for (int i = 0; i < fields.length; i++) {
340                Field field = fields[i];
341                if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())
342                    || Modifier.isPrivate(field.getModifiers())) {
343                    continue;
344                }
345    
346                try {
347                    field.setAccessible(true);
348                    Object o = field.get(target);
349                    if (o != null && o.getClass().isArray()) {
350                        try {
351                            o = Arrays.asList((Object[])o);
352                        } catch (Throwable e) {
353                        }
354                    }
355                    map.put(field.getName(), o);
356                } catch (Throwable e) {
357                    LOG.debug("Error getting field " + field + " on class " + startClass + ". This exception is ignored.", e);
358                }
359            }
360    
361        }
362    
363    }