Project

General

Profile

Download (7.84 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.debug;
10

    
11
import java.beans.PropertyDescriptor;
12
import java.io.PrintStream;
13
import java.lang.reflect.InvocationTargetException;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21

    
22
import org.apache.commons.beanutils.PropertyUtils;
23
import org.apache.commons.lang.builder.HashCodeBuilder;
24
import org.apache.log4j.Logger;
25
import org.hibernate.Hibernate;
26
import org.hibernate.Session;
27
import org.hibernate.collection.internal.AbstractPersistentCollection;
28
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.CollectionProxy;
29
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.MapProxy;
30
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SortedMapProxy;
31

    
32
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
33
import eu.etaxonomy.cdm.model.common.CdmBase;
34
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
35

    
36
/**
37
 * @author a.kohlbecker
38
 * @since 08.11.2017
39
 *
40
 */
41
public class PersistentContextAnalyzer {
42

    
43
    /**
44
     *
45
     */
46
    private static final String COPY_ENTITY = "!";
47

    
48
    /**
49
     *
50
     */
51
    private static final String IN_PERSITENT_CONTEXT = "*";
52

    
53
    private final static Logger logger = Logger.getLogger(PersistentContextAnalyzer.class);
54

    
55
    private Session session;
56

    
57
    private CdmBase entity;
58

    
59
    private Map<EntityKey, CdmBase> entityyMap = new HashMap<>();
60

    
61
    private List<String> entityPathList = new ArrayList<>();
62

    
63
    private Map<EntityKey, List<String>> entityPathsMap = new HashMap<>();
64

    
65
    private Set<EntityKey> copyEntitiyKeys = new HashSet<>();
66

    
67
    private Set<Object> objectsSeen = new HashSet<>();
68

    
69

    
70
    public PersistentContextAnalyzer(CdmBase entity, Session session){
71
        this.session = session;
72
        this.entity = entity;
73
        update();
74
    }
75

    
76
    public PersistentContextAnalyzer(CdmBase entity){
77
        this(entity, null);
78
    }
79

    
80
    /**
81
     * - find copied entities in the graph
82
     */
83
    private void update() {
84

    
85
        String propertyPath = "";
86
        analyzeEntity(entity, propertyPath);
87
    }
88

    
89
    public void printEntityGraph(PrintStream printStream){
90
        printLegend(printStream);
91
        for(String path : entityPathList) {
92
            printStream.println(path);
93
        }
94
    }
95

    
96
    public void printCopyEntities(PrintStream printStream){
97
        printLegend(printStream);
98
        for(EntityKey key : copyEntitiyKeys){
99
            for(String path : entityPathsMap.get(key)) {
100
                printStream.println(path);
101
            }
102
        }
103
    }
104

    
105
    /**
106
     * @param printStream
107
     */
108
    protected void printLegend(PrintStream printStream) {
109
        printStream.println("PersistentContextAnalyzer legend: ");
110
        printStream.println("    - '*': entity mapped in persistent context");
111
        printStream.println("    - '!{objectHash}': detected copy entity, followed by object hash");
112
    }
113

    
114
    /**
115
     *
116
     */
117
    protected void analyzeEntity(CdmBase bean, String propertyPath) {
118

    
119
        EntityKey entityKey = new EntityKey(bean);
120

    
121
        propertyPath += "[" + entityKey;
122
        String flags = "";
123
        CdmBase mappedEntity = entityyMap.put(entityKey, bean);
124

    
125
        if(session != null && session.contains(bean)){
126
            flags += IN_PERSITENT_CONTEXT;
127
        }
128
        if(mappedEntity != null && mappedEntity != bean) {
129
            copyEntitiyKeys.add(entityKey);
130
            flags += COPY_ENTITY + bean.hashCode();
131
        }
132
        if(!flags.isEmpty()){
133
            propertyPath += "(" + flags + ")";
134
        }
135
        propertyPath += "]";
136

    
137
        logger.debug(propertyPath);
138

    
139
        entityPathList.add(propertyPath);
140
        if(!entityPathsMap.containsKey(entityKey)){
141
            entityPathsMap.put(entityKey, new ArrayList<>());
142
        }
143
        entityPathsMap.get(entityKey).add(propertyPath);
144

    
145
        if(!objectsSeen.add(bean)){
146
            // avoid cycles, do not recurse into properties of objects that have been analyzed already
147
            return;
148
        }
149

    
150
        Set<PropertyDescriptor> properties = AbstractBeanInitializer.getProperties(bean, null);
151
        for(PropertyDescriptor prop : properties){
152

    
153
            try {
154
                Object propertyValue = PropertyUtils.getProperty(bean, prop.getName());
155

    
156
                if(propertyValue == null){
157
                    continue;
158
                }
159

    
160
                String propertyPathSuffix = "." + prop.getName();
161

    
162
                if(Hibernate.isInitialized(propertyValue)) {
163

    
164
                    if(CdmBase.class.isAssignableFrom(prop.getPropertyType())){
165
                        analyzeEntity(HibernateProxyHelper.deproxy(propertyValue, CdmBase.class), propertyPath + propertyPathSuffix);
166
                        continue;
167
                    }
168

    
169
                    Collection<CdmBase> collection = null;
170
                    if(propertyValue instanceof AbstractPersistentCollection){
171
                        if (propertyValue  instanceof Collection) {
172
                            collection = (Collection<CdmBase>) propertyValue;
173
                        } else if (propertyValue instanceof Map) {
174
                            collection = ((Map<?,CdmBase>)propertyValue).values();
175
                        } else {
176
                            logger.error("unhandled subtype of AbstractPersistentCollection");
177
                        }
178
                    } else if (propertyValue instanceof CollectionProxy
179
                                || propertyValue instanceof MapProxy<?, ?>
180
                                || propertyValue instanceof SortedMapProxy<?, ?>){
181
                            //hibernate envers collections
182
                            collection = (Collection<CdmBase>)propertyValue;
183
                    }
184

    
185
                    if(collection != null){
186
                        for(CdmBase collectionItem : collection){
187
                            analyzeEntity(HibernateProxyHelper.deproxy(collectionItem, CdmBase.class), propertyPath + propertyPathSuffix);
188
                        }
189
                    } else {
190
                        // logger.error("Unhandled property type " + propertyValue.getClass().getName());
191
                    }
192
                }
193

    
194
            } catch (IllegalAccessException e) {
195
                String message = "Illegal access on property " + prop;
196
                logger.error(message);
197
                throw new RuntimeException(message, e);
198
            } catch (InvocationTargetException e) {
199
                String message = "Cannot invoke property " + prop + " not found";
200
                logger.error(message);
201
                throw new RuntimeException(message, e);
202
            } catch (NoSuchMethodException e) {
203
                String message = "Property " + prop.getName() + " not found for class " + bean.getClass();
204
                logger.error(message);
205
            }
206

    
207
        }
208
    }
209

    
210
    class EntityKey {
211

    
212
        Class type;
213
        int id;
214

    
215
        public EntityKey(CdmBase entity){
216
            type = entity.getClass();
217
            id = entity.getId();
218
        }
219

    
220
        /**
221
         * @return the type
222
         */
223
        public Class getType() {
224
            return type;
225
        }
226

    
227
        /**
228
         * @return the id
229
         */
230
        public int getId() {
231
            return id;
232
        }
233

    
234
        /**
235
         * {@inheritDoc}
236
         */
237
        @Override
238
        public int hashCode() {
239
            return new HashCodeBuilder(17, 31)
240
                    .append(type)
241
                    .append(id)
242
                    .toHashCode();
243
        }
244

    
245
        @Override
246
        public String toString() {
247
            return type.getSimpleName() + "#" + getId();
248
        }
249

    
250
    }
251

    
252

    
253
}
    (1-1/1)