001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     <a href="mailto:grenard@nuxeo.com">Guillaume</a>
018 */
019package org.nuxeo.ecm.platform.sessioninspector.util;
020
021import java.lang.reflect.Array;
022import java.lang.reflect.Field;
023import java.lang.reflect.Modifier;
024import java.util.Collection;
025import java.util.HashMap;
026import java.util.IdentityHashMap;
027import java.util.Map;
028
029import org.nuxeo.ecm.platform.sessioninspector.jsf.model.ObjectStatistics;
030import org.nuxeo.runtime.javaagent.AgentLoader;
031
032/**
033 * Build the graph of reference of a given object.
034 *
035 * @since 5.9.2
036 */
037public class ObjectVisitor {
038
039    protected final Map<Object, Object> visited = new IdentityHashMap<Object, Object>();
040
041    public Collection<ObjectStatistics> getObjectStatisticsList() {
042        Map<String, ObjectStatistics> map = new HashMap<String, ObjectStatistics>();
043        for (Object o : visited.keySet()) {
044            final String type = o.getClass().getCanonicalName();
045            ObjectStatistics os = map.get(type);
046            if (os == null) {
047                os = new ObjectStatistics(type, 1, AgentLoader.INSTANCE.getSizer().sizeOf(o));
048                map.put(type, os);
049            } else {
050                os.setNbInstance(os.getNbInstance() + 1);
051                os.setCumulatedSize(os.getCumulatedSize() + AgentLoader.INSTANCE.getSizer().sizeOf(o));
052            }
053        }
054        return map.values();
055    }
056
057    public Map<Object, Object> getVisited() {
058        return visited;
059    }
060
061    public ObjectVisitor() {
062        super();
063    }
064
065    public void visit(Object each) {
066        if (each == null) {
067            return;
068        }
069        if (visited.containsKey(each)) {
070            return;
071        }
072        visited.put(each, each);
073        Class<?> eachType = each.getClass();
074        if (eachType.isArray()) {
075            if (eachType.getComponentType().isPrimitive()) {
076                return;
077            }
078            for (int i = 0; i < Array.getLength(each); i++) {
079                visit(Array.get(each, i));
080            }
081        } else {
082            visit(each, eachType);
083        }
084    }
085
086    protected void visit(Object each, Class<?> eachType) {
087        if (eachType.equals(Object.class)) {
088            return;
089        }
090        for (Field eachField : eachType.getDeclaredFields()) {
091            visit(each, eachField);
092        }
093        visit(each, eachType.getSuperclass());
094    }
095
096    protected void visit(Object each, Field eachField) {
097        if ((eachField.getModifiers() & Modifier.STATIC) != 0) {
098            return;
099        }
100        if (eachField.getType().isPrimitive()) {
101            return;
102        }
103        boolean oldAccessible = eachField.isAccessible();
104        eachField.setAccessible(true);
105        try {
106            visit(eachField.get(each));
107        } catch (Exception e) {
108            throw new RuntimeException("Exception trying to access field " + eachField, e);
109        } finally {
110            eachField.setAccessible(oldAccessible);
111        }
112    }
113}