001
014
015 package com.liferay.portal.server;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.util.IntegerWrapper;
020 import com.liferay.portal.kernel.util.ObjectValuePair;
021 import com.liferay.portal.kernel.util.StringBundler;
022 import com.liferay.portal.kernel.util.StringUtil;
023
024 import java.lang.reflect.Field;
025 import java.lang.reflect.Modifier;
026 import java.lang.reflect.Proxy;
027
028 import java.util.ArrayList;
029 import java.util.Collection;
030 import java.util.Collections;
031 import java.util.HashMap;
032 import java.util.HashSet;
033 import java.util.List;
034 import java.util.Map;
035 import java.util.Set;
036
037
040 public class DeepNamedValueScanner {
041
042 public DeepNamedValueScanner(String value) {
043 _value = StringUtil.toLowerCase(value);
044 }
045
046 public DeepNamedValueScanner(String value, boolean visit) {
047 this(value);
048
049 _visitArrays = visit;
050 _visitCollections = visit;
051 _visitLists = visit;
052 _visitSets = visit;
053 _visitMaps = visit;
054 _visitStaticFields = visit;
055 }
056
057 public long getElapsedTime() {
058 return _elapsedTime;
059 }
060
061 public String[] getExcludedClassNames() {
062 return _excludedClassNames;
063 }
064
065 public String[] getExcludedNames() {
066 return _excludedNames;
067 }
068
069 public String[] getIncludedClassNames() {
070 return _includedClassNames;
071 }
072
073 public Object getMatchedValue() {
074 return _matchedValue;
075 }
076
077 public int getMatchingCount() {
078 return _matchingCount;
079 }
080
081 public int getSkipFirstCount() {
082 return _skipFirstCount;
083 }
084
085 public boolean isScanning() {
086 return _scanning;
087 }
088
089 public boolean isTrackUsageCount() {
090 return _trackUsageCount;
091 }
092
093 public boolean isVisitArrays() {
094 return _visitArrays;
095 }
096
097 public boolean isVisitCollectionss() {
098 return _visitCollections;
099 }
100
101 public boolean isVisitLists() {
102 return _visitLists;
103 }
104
105 public boolean isVisitMaps() {
106 return _visitMaps;
107 }
108
109 public boolean isVisitSets() {
110 return _visitSets;
111 }
112
113 public boolean isVisitStaticFields() {
114 return _visitStaticFields;
115 }
116
117 public void printStatistics(int topCount) {
118 if (!_trackUsageCount) {
119 return;
120 }
121
122 System.out.println("-- names statistics --");
123
124 _printStatistics(_nameDatasets.values(), topCount);
125
126 System.out.println("-- types statistics --");
127
128 _printStatistics(_typeDatasets.values(), topCount);
129 }
130
131 public boolean scan(Object target) throws Exception {
132 _elapsedTime = System.currentTimeMillis();
133
134 _visitedIds = new HashSet<String>();
135
136 _scanning = true;
137
138 _scan(target);
139
140 _visitedIds = null;
141
142 _elapsedTime = System.currentTimeMillis() - _elapsedTime;
143
144 if (_log.isDebugEnabled()) {
145 if (!_scanning) {
146 StringBundler sb = new StringBundler(5);
147
148 sb.append("Deep named value scanner found ");
149 sb.append(_matchingCount);
150 sb.append(" matches in ");
151 sb.append(_elapsedTime);
152 sb.append(" ms");
153
154 _log.debug(sb.toString());
155 }
156 else {
157 _log.debug("Deep named value scanner did not finish scanning");
158 }
159 }
160
161 return !_scanning;
162 }
163
164 public void setExcludedClassNames(String... excludedClassNames) {
165 _excludedClassNames = excludedClassNames;
166
167 StringUtil.lowerCase(excludedClassNames);
168 }
169
170 public void setExcludedNames(String... excludedNames) {
171 _excludedNames = excludedNames;
172
173 StringUtil.lowerCase(excludedNames);
174 }
175
176 public void setIncludedClassNames(String... includedClassNames) {
177 _includedClassNames = includedClassNames;
178
179 StringUtil.lowerCase(includedClassNames);
180 }
181
182 public void setSkipFirstCount(int skipFirstCount) {
183 _skipFirstCount = skipFirstCount;
184 }
185
186 public void setTrackUsageCount(boolean trackUsageCount) {
187 _trackUsageCount = trackUsageCount;
188
189 if (trackUsageCount) {
190 _nameDatasets = new HashMap<String, Dataset>();
191 _typeDatasets = new HashMap<String, Dataset>();
192 }
193 }
194
195 public void setVisitArrays(boolean visitArrays) {
196 _visitArrays = visitArrays;
197 }
198
199 public void setVisitCollections(boolean visitCollections) {
200 _visitCollections = visitCollections;
201 }
202
203 public void setVisitLists(boolean visitLists) {
204 _visitLists = visitLists;
205 }
206
207 public void setVisitMaps(boolean visitMaps) {
208 _visitMaps = visitMaps;
209 }
210
211 public void setVisitSets(boolean visitSets) {
212 _visitSets = visitSets;
213 }
214
215 public void setVisitStaticFields(boolean visitStaticFields) {
216 _visitStaticFields = visitStaticFields;
217 }
218
219 private void _incrementUsageCount(
220 Map<String, Dataset> datasets, String name) {
221
222 Dataset dataset = datasets.get(name);
223
224 if (dataset == null) {
225 dataset = new Dataset();
226
227 dataset.setKey(name);
228 dataset.setValue(new IntegerWrapper());
229
230 datasets.put(name, dataset);
231 }
232
233 IntegerWrapper integerWrapper = dataset.getValue();
234
235 integerWrapper.increment();
236 }
237
238 private boolean _isAcceptClass(Class<?> targetClass) {
239 String targetClassName = targetClass.getName();
240
241 targetClassName = StringUtil.toLowerCase(targetClassName);
242
243 if (targetClassName.startsWith("java.")) {
244 return false;
245 }
246
247 if (targetClassName.startsWith("org.eclipse.osgi.")) {
248 return false;
249 }
250
251 if (targetClassName.startsWith("sun.misc.")) {
252 return false;
253 }
254
255 if (targetClassName.contains("log")) {
256 return false;
257 }
258
259 if (_excludedClassNames != null) {
260 for (String excludedClassName : _excludedClassNames) {
261 if (targetClassName.contains(excludedClassName)) {
262 return false;
263 }
264 }
265 }
266
267 if (_includedClassNames != null) {
268 boolean accept = false;
269
270 for (String includedClassName : _includedClassNames) {
271 if (targetClassName.contains(includedClassName)) {
272 accept = true;
273
274 break;
275 }
276 }
277
278 if (!accept) {
279 return false;
280 }
281 }
282
283 if (_trackUsageCount) {
284 _incrementUsageCount(_typeDatasets, targetClass.getName());
285 }
286
287 return true;
288 }
289
290 private boolean _isAcceptName(String name) {
291 if (name == null) {
292 return true;
293 }
294
295 name = StringUtil.toLowerCase(name);
296
297 if (_excludedNames != null) {
298 for (String excludedNames : _excludedNames) {
299 if (name.contains(excludedNames)) {
300 return false;
301 }
302 }
303 }
304
305 if (_trackUsageCount) {
306 _incrementUsageCount(_nameDatasets, name);
307 }
308
309 return true;
310 }
311
312 private void _matchField(Object target, Field field, String name)
313 throws IllegalAccessException {
314
315 if (name == null) {
316 return;
317 }
318
319 _matchingCount++;
320
321 name = StringUtil.toLowerCase(name);
322
323 if (name.contains(_value)) {
324 if (_skipFirstCount > 0) {
325 _skipFirstCount--;
326
327 return;
328 }
329
330 field.setAccessible(true);
331
332 _matchedValue = field.get(target);
333
334 _scanning = false;
335 }
336 }
337
338 private void _matchName(Object value, String name) {
339 if (name == null) {
340 return;
341 }
342
343 _matchingCount++;
344
345 name = StringUtil.toLowerCase(name);
346
347 if (name.contains(_value)) {
348 if (_skipFirstCount > 0) {
349 _skipFirstCount--;
350
351 return;
352 }
353
354 _matchedValue = value;
355
356 _scanning = false;
357 }
358 }
359
360 private void _printStatistics(Collection<Dataset> datasets, int topCount) {
361 List<Dataset> datasetsList = new ArrayList<Dataset>();
362
363 for (Dataset dataset : datasets) {
364 datasetsList.add(dataset);
365 }
366
367 Collections.sort(datasetsList);
368
369 for (Dataset dataset : datasetsList) {
370 System.out.println(dataset.getValue() + " " + dataset.getKey());
371
372 topCount--;
373
374 if (topCount == 0) {
375 break;
376 }
377 }
378 }
379
380 private Object _resolveJavaProxy(Object target)
381 throws IllegalAccessException, NoSuchFieldException {
382
383 Class<?> targetClass = target.getClass();
384 Class<?> targetSuperClass = targetClass.getSuperclass();
385
386 if ((targetSuperClass != null) &&
387 targetSuperClass.equals(Proxy.class)) {
388
389 Field field = targetSuperClass.getDeclaredField("h");
390
391 field.setAccessible(true);
392
393 target = field.get(target);
394 }
395
396 return target;
397 }
398
399 private void _scan(Object target) throws Exception {
400 if (target == null) {
401 return;
402 }
403
404 if (!_scanning) {
405 return;
406 }
407
408 target = _resolveJavaProxy(target);
409
410 String visitedId = null;
411
412 try {
413 visitedId = String.valueOf(System.identityHashCode(target));
414
415 if (_visitedIds.contains(visitedId)) {
416 return;
417 }
418 }
419 catch (Exception e) {
420 return;
421 }
422
423 _visitedIds.add(visitedId);
424
425 Class<?> targetClass = target.getClass();
426
427 if (targetClass.isArray()) {
428 if (!_visitArrays) {
429 return;
430 }
431
432 Class<?> componentTypeClass = targetClass.getComponentType();
433
434 if (componentTypeClass.isPrimitive() == false) {
435 Object[] array = (Object[])target;
436
437 for (Object element : array) {
438 _scan(element);
439 }
440 }
441 }
442 else if (_visitLists && (target instanceof List)) {
443 _scanCollection((List<Object>)target);
444 }
445 else if (_visitMaps && (target instanceof Map)) {
446 _scanMap((Map<Object, Object>)target);
447 }
448 else if (_visitSets && (target instanceof Set)) {
449 _scanCollection((Set<Object>)target);
450 }
451 else if (_visitCollections && (target instanceof Collection)) {
452 _scanCollection((Collection<Object>)target);
453 }
454 else {
455 _scanObject(target);
456 }
457 }
458
459 private void _scanCollection(Collection<Object> collection)
460 throws Exception {
461
462 for (Object element : collection) {
463 if (!_scanning) {
464 break;
465 }
466
467 _scan(element);
468 }
469 }
470
471 private void _scanMap(Map<Object, Object> map) throws Exception {
472 Set<Map.Entry<Object, Object>> entrySet = map.entrySet();
473
474 for (Map.Entry<Object, Object> entry : entrySet) {
475 if (!_scanning) {
476 break;
477 }
478
479 Object key = entry.getKey();
480 Object value = entry.getValue();
481
482 String name = null;
483
484 if (key != null) {
485 name = key.toString();
486 }
487
488 if (_isAcceptName(name)) {
489 _matchName(value, name);
490 _scan(value);
491 }
492 }
493 }
494
495 private void _scanObject(Object target) throws Exception {
496 Class<?> targetClass = target.getClass();
497
498 if (!_isAcceptClass(targetClass)) {
499 return;
500 }
501
502 while (targetClass != null) {
503 Field[] fields = targetClass.getDeclaredFields();
504
505 for (Field field : fields) {
506 if (!_scanning) {
507 break;
508 }
509
510 if (!_visitStaticFields) {
511 if ((field.getModifiers() & Modifier.STATIC) != 0) {
512 continue;
513 }
514 }
515
516 String fieldName = field.getName();
517
518 if (_isAcceptName(fieldName)) {
519 _matchField(target, field, fieldName);
520
521 field.setAccessible(true);
522
523 Object fieldValue = field.get(target);
524
525 if (fieldValue != null) {
526 _scan(fieldValue);
527 }
528 }
529 }
530
531 targetClass = targetClass.getSuperclass();
532 }
533 }
534
535 private static Log _log = LogFactoryUtil.getLog(
536 DeepNamedValueScanner.class);
537
538 private long _elapsedTime;
539 private String[] _excludedClassNames;
540 private String[] _excludedNames;
541 private String[] _includedClassNames;
542 private Object _matchedValue;
543 private int _matchingCount;
544 private Map<String, Dataset> _nameDatasets;
545 private boolean _scanning;
546 private int _skipFirstCount;
547 private boolean _trackUsageCount;
548 private Map<String, Dataset> _typeDatasets;
549 private final String _value;
550 private boolean _visitArrays;
551 private boolean _visitCollections;
552 private Set<String> _visitedIds;
553 private boolean _visitLists;
554 private boolean _visitMaps;
555 private boolean _visitSets;
556 private boolean _visitStaticFields;
557
558 private class Dataset
559 extends ObjectValuePair<String, IntegerWrapper>
560 implements Comparable<Dataset> {
561
562 @Override
563 public int compareTo(Dataset dataset) {
564 IntegerWrapper integerWrapper = dataset.getValue();
565
566 return integerWrapper.compareTo(getValue());
567 }
568
569 }
570
571 }