001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
038     * @author Igor Spasic
039     */
040    public class DeepNamedValueScanner {
041    
042            public DeepNamedValueScanner(String value) {
043                    _value = value.toLowerCase();
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 = targetClassName.toLowerCase();
242    
243                    if (targetClassName.startsWith("java.")) {
244                            return false;
245                    }
246    
247                    if (targetClassName.startsWith("sun.misc.")) {
248                            return false;
249                    }
250    
251                    if (targetClassName.contains("log")) {
252                            return false;
253                    }
254    
255                    if (_excludedClassNames != null) {
256                            for (String excludedClassName : _excludedClassNames) {
257                                    if (targetClassName.contains(excludedClassName)) {
258                                            return false;
259                                    }
260                            }
261                    }
262    
263                    if (_includedClassNames != null) {
264                            boolean accept = false;
265    
266                            for (String includedClassName : _includedClassNames) {
267                                    if (targetClassName.contains(includedClassName)) {
268                                            accept = true;
269    
270                                            break;
271                                    }
272                            }
273    
274                            if (!accept) {
275                                    return false;
276                            }
277                    }
278    
279                    if (_trackUsageCount) {
280                            _incrementUsageCount(_typeDatasets, targetClass.getName());
281                    }
282    
283                    return true;
284            }
285    
286            private boolean _isAcceptName(String name) {
287                    if (name == null) {
288                            return true;
289                    }
290    
291                    name = name.toLowerCase();
292    
293                    if (_excludedNames != null) {
294                            for (String excludedNames : _excludedNames) {
295                                    if (name.contains(excludedNames)) {
296                                            return false;
297                                    }
298                            }
299                    }
300    
301                    if (_trackUsageCount) {
302                            _incrementUsageCount(_nameDatasets, name);
303                    }
304    
305                    return true;
306            }
307    
308            private void _matchField(Object target, Field field, String name)
309                    throws IllegalAccessException {
310    
311                    if (name == null) {
312                            return;
313                    }
314    
315                    _matchingCount++;
316    
317                    name = name.toLowerCase();
318    
319                    if (name.contains(_value)) {
320                            if (_skipFirstCount > 0) {
321                                    _skipFirstCount--;
322    
323                                    return;
324                            }
325    
326                            field.setAccessible(true);
327    
328                            _matchedValue = field.get(target);
329    
330                            _scanning = false;
331                    }
332            }
333    
334            private void _matchName(Object value, String name) {
335                    if (name == null) {
336                            return;
337                    }
338    
339                    _matchingCount++;
340    
341                    name = name.toLowerCase();
342    
343                    if (name.contains(_value)) {
344                            if (_skipFirstCount > 0) {
345                                    _skipFirstCount--;
346    
347                                    return;
348                            }
349    
350                            _matchedValue = value;
351    
352                            _scanning = false;
353                    }
354            }
355    
356            private void _printStatistics(Collection<Dataset> datasets, int topCount) {
357                    List<Dataset> datasetsList = new ArrayList<Dataset>();
358    
359                    for (Dataset dataset : datasets) {
360                            datasetsList.add(dataset);
361                    }
362    
363                    Collections.sort(datasetsList);
364    
365                    for (Dataset dataset : datasetsList) {
366                            System.out.println(dataset.getValue() + " " + dataset.getKey());
367    
368                            topCount--;
369    
370                            if (topCount == 0) {
371                                    break;
372                            }
373                    }
374            }
375    
376            private Object _resolveJavaProxy(Object target)
377                    throws IllegalAccessException, NoSuchFieldException {
378    
379                    Class<?> targetClass = target.getClass();
380                    Class<?> targetSuperClass = targetClass.getSuperclass();
381    
382                    if ((targetSuperClass != null) &&
383                            targetSuperClass.equals(Proxy.class)) {
384    
385                            Field field = targetSuperClass.getDeclaredField("h");
386    
387                            field.setAccessible(true);
388    
389                            target = field.get(target);
390                    }
391    
392                    return target;
393            }
394    
395            private void _scan(Object target) throws Exception {
396                    if (target == null) {
397                            return;
398                    }
399    
400                    if (!_scanning) {
401                            return;
402                    }
403    
404                    target = _resolveJavaProxy(target);
405    
406                    String visitedId = null;
407    
408                    try {
409                            visitedId = String.valueOf(System.identityHashCode(target));
410    
411                            if (_visitedIds.contains(visitedId)) {
412                                    return;
413                            }
414                    }
415                    catch (Exception e) {
416                            return;
417                    }
418    
419                    _visitedIds.add(visitedId);
420    
421                    Class<?> targetClass = target.getClass();
422    
423                    if (targetClass.isArray()) {
424                            if (!_visitArrays) {
425                                    return;
426                            }
427    
428                            Class<?> componentTypeClass = targetClass.getComponentType();
429    
430                            if (componentTypeClass.isPrimitive() == false) {
431                                    Object[] array = (Object[])target;
432    
433                                    for (Object element : array) {
434                                            _scan(element);
435                                    }
436                            }
437    
438                    }
439                    else if (_visitLists && (target instanceof List)) {
440                            _scanCollection((List<Object>)target);
441                    }
442                    else if (_visitMaps && (target instanceof Map)) {
443                            _scanMap((Map<Object, Object>)target);
444                    }
445                    else if (_visitSets && (target instanceof Set)) {
446                            _scanCollection((Set<Object>)target);
447                    }
448                    else if (_visitCollections && (target instanceof Collection)) {
449                            _scanCollection((Collection<Object>)target);
450                    }
451                    else {
452                            _scanObject(target);
453                    }
454            }
455    
456            private void _scanCollection(Collection<Object> collection)
457                    throws Exception {
458    
459                    for (Object element : collection) {
460                            if (!_scanning) {
461                                    break;
462                            }
463    
464                            _scan(element);
465                    }
466            }
467    
468            private void _scanMap(Map<Object, Object> map) throws Exception {
469                    Set<Map.Entry<Object, Object>> entrySet = map.entrySet();
470    
471                    for (Map.Entry<Object, Object> entry : entrySet) {
472                            if (!_scanning) {
473                                    break;
474                            }
475    
476                            Object key = entry.getKey();
477                            Object value = entry.getValue();
478    
479                            String name = null;
480    
481                            if (key != null) {
482                                    name = key.toString();
483                            }
484    
485                            if (_isAcceptName(name)) {
486                                    _matchName(value, name);
487                                    _scan(value);
488                            }
489                    }
490            }
491    
492            private void _scanObject(Object target) throws Exception {
493                    Class<?> targetClass = target.getClass();
494    
495                    if (!_isAcceptClass(targetClass)) {
496                            return;
497                    }
498    
499                    while (targetClass != null) {
500                            Field[] fields = targetClass.getDeclaredFields();
501    
502                            for (Field field : fields) {
503                                    if (!_scanning) {
504                                            break;
505                                    }
506    
507                                    if (!_visitStaticFields) {
508                                            if ((field.getModifiers() & Modifier.STATIC) != 0) {
509                                                    continue;
510                                            }
511                                    }
512    
513                                    String fieldName = field.getName();
514    
515                                    if (_isAcceptName(fieldName)) {
516                                            _matchField(target, field, fieldName);
517    
518                                            field.setAccessible(true);
519    
520                                            Object fieldValue = field.get(target);
521    
522                                            if (fieldValue != null) {
523                                                    _scan(fieldValue);
524                                            }
525                                    }
526                            }
527    
528                            targetClass = targetClass.getSuperclass();
529                    }
530            }
531    
532            private static Log _log = LogFactoryUtil.getLog(
533                    DeepNamedValueScanner.class);
534    
535            private long _elapsedTime;
536            private String[] _excludedClassNames;
537            private String[] _excludedNames;
538            private String[] _includedClassNames;
539            private Object _matchedValue;
540            private int _matchingCount;
541            private Map<String, Dataset> _nameDatasets;
542            private boolean _scanning;
543            private int _skipFirstCount;
544            private boolean _trackUsageCount;
545            private Map<String, Dataset> _typeDatasets;
546            private final String _value;
547            private boolean _visitArrays;
548            private boolean _visitCollections;
549            private Set<String> _visitedIds;
550            private boolean _visitLists;
551            private boolean _visitMaps;
552            private boolean _visitSets;
553            private boolean _visitStaticFields;
554    
555            private class Dataset
556                    extends ObjectValuePair<String, IntegerWrapper>
557                    implements Comparable<Dataset> {
558    
559                    @Override
560                    public int compareTo(Dataset dataset) {
561                            IntegerWrapper integerWrapper = dataset.getValue();
562    
563                            return integerWrapper.compareTo(getValue());
564                    }
565    
566            }
567    
568    }