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