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 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 (Exception e) { 182 LOG.error(String.format("Could not set property %s on %s", name, target), e); 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 public 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 (Method method : methods) { 263 Class<?> params[] = method.getParameterTypes(); 264 if (method.getName().equals(name) && params.length == 1 ) { 265 return method; 266 } 267 } 268 return null; 269 } 270 271 public static Method findGetterMethod(Class clazz, String name) { 272 // Build the method name. 273 name = "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1); 274 Method[] methods = clazz.getMethods(); 275 for (Method method : methods) { 276 Class<?> params[] = method.getParameterTypes(); 277 if (method.getName().equals(name) && params.length == 0 ) { 278 return method; 279 } 280 } 281 return null; 282 } 283 284 public static String toString(Object target) { 285 return toString(target, Object.class, null); 286 } 287 288 public static String toString(Object target, Class stopClass) { 289 return toString(target, stopClass, null); 290 } 291 292 public static String toString(Object target, Class stopClass, Map<String, Object> overrideFields) { 293 LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(); 294 addFields(target, target.getClass(), stopClass, map); 295 if (overrideFields != null) { 296 for(String key : overrideFields.keySet()) { 297 Object value = overrideFields.get(key); 298 map.put(key, value); 299 } 300 301 } 302 StringBuffer buffer = new StringBuffer(simpleName(target.getClass())); 303 buffer.append(" {"); 304 Set<Entry<String, Object>> entrySet = map.entrySet(); 305 boolean first = true; 306 for (Map.Entry<String,Object> entry : entrySet) { 307 Object value = entry.getValue(); 308 Object key = entry.getKey(); 309 if (first) { 310 first = false; 311 } else { 312 buffer.append(", "); 313 } 314 buffer.append(key); 315 buffer.append(" = "); 316 317 appendToString(buffer, key, value); 318 } 319 buffer.append("}"); 320 return buffer.toString(); 321 } 322 323 protected static void appendToString(StringBuffer buffer, Object key, Object value) { 324 if (value instanceof ActiveMQDestination) { 325 ActiveMQDestination destination = (ActiveMQDestination)value; 326 buffer.append(destination.getQualifiedName()); 327 } else if (key.toString().toLowerCase(Locale.ENGLISH).contains("password")){ 328 buffer.append("*****"); 329 } else { 330 buffer.append(value); 331 } 332 } 333 334 public static String simpleName(Class clazz) { 335 String name = clazz.getName(); 336 int p = name.lastIndexOf("."); 337 if (p >= 0) { 338 name = name.substring(p + 1); 339 } 340 return name; 341 } 342 343 private static void addFields(Object target, Class startClass, Class<Object> stopClass, LinkedHashMap<String, Object> map) { 344 345 if (startClass != stopClass) { 346 addFields(target, startClass.getSuperclass(), stopClass, map); 347 } 348 349 Field[] fields = startClass.getDeclaredFields(); 350 for (Field field : fields) { 351 if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) 352 || Modifier.isPrivate(field.getModifiers())) { 353 continue; 354 } 355 356 try { 357 field.setAccessible(true); 358 Object o = field.get(target); 359 if (o != null && o.getClass().isArray()) { 360 try { 361 o = Arrays.asList((Object[])o); 362 } catch (Exception e) { 363 } 364 } 365 map.put(field.getName(), o); 366 } catch (Exception e) { 367 LOG.debug("Error getting field " + field + " on class " + startClass + ". This exception is ignored.", e); 368 } 369 } 370 } 371}