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