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 }