/
AnnotationMetadataSourceProcessorImpl.java
349 lines (286 loc) · 12.5 KB
/
AnnotationMetadataSourceProcessorImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.boot.model.source.internal.annotations;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.AttributeConverterInfo;
import org.hibernate.boot.internal.MetadataBuildingContextRootImpl;
import org.hibernate.boot.jaxb.spi.Binding;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware;
import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware.JpaOrmXmlPersistenceUnitDefaults;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector;
import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.jboss.jandex.IndexView;
import org.jboss.logging.Logger;
import org.dom4j.Document;
/**
* @author Steve Ebersole
*/
public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProcessor {
private static final Logger log = Logger.getLogger( AnnotationMetadataSourceProcessorImpl.class );
private final MetadataBuildingContextRootImpl rootMetadataBuildingContext;
@SuppressWarnings("FieldCanBeLocal")
private final IndexView jandexView;
private final ReflectionManager reflectionManager;
private final LinkedHashSet<String> annotatedPackages = new LinkedHashSet<String>();
private final List<XClass> xClasses = new ArrayList<XClass>();
private final ClassLoaderService classLoaderService;
public AnnotationMetadataSourceProcessorImpl(
ManagedResources managedResources,
final MetadataBuildingContextRootImpl rootMetadataBuildingContext,
IndexView jandexView) {
this.rootMetadataBuildingContext = rootMetadataBuildingContext;
this.jandexView = jandexView;
this.reflectionManager = rootMetadataBuildingContext.getBootstrapContext().getReflectionManager();
if ( CollectionHelper.isNotEmpty( managedResources.getAnnotatedPackageNames() ) ) {
annotatedPackages.addAll( managedResources.getAnnotatedPackageNames() );
}
final AttributeConverterManager attributeConverterManager = new AttributeConverterManager( rootMetadataBuildingContext );
this.classLoaderService = rootMetadataBuildingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class );
if ( rootMetadataBuildingContext.getBuildingOptions().isXmlMappingEnabled() ) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ewww. This is temporary until we migrate to Jandex + StAX for annotation binding
final JPAMetadataProvider jpaMetadataProvider = (JPAMetadataProvider) ( (MetadataProviderInjector) reflectionManager )
.getMetadataProvider();
for ( Binding xmlBinding : managedResources.getXmlMappingBindings() ) {
// if ( !MappingBinder.DelayedOrmXmlData.class.isInstance( xmlBinding.getRoot() ) ) {
// continue;
// }
//
// // convert the StAX representation in delayedOrmXmlData to DOM because that's what commons-annotations needs
// final MappingBinder.DelayedOrmXmlData delayedOrmXmlData = (MappingBinder.DelayedOrmXmlData) xmlBinding.getRoot();
// org.dom4j.Document dom4jDocument = toDom4jDocument( delayedOrmXmlData );
if ( !org.dom4j.Document.class.isInstance( xmlBinding.getRoot() ) ) {
continue;
}
org.dom4j.Document dom4jDocument = (Document) xmlBinding.getRoot();
final List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( dom4jDocument );
for ( String className : classNames ) {
xClasses.add( toXClass( className, reflectionManager, classLoaderService ) );
}
}
jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
for ( String className : managedResources.getAnnotatedClassNames() ) {
final Class annotatedClass = classLoaderService.classForName( className );
categorizeAnnotatedClass( annotatedClass, attributeConverterManager, classLoaderService );
}
for ( Class annotatedClass : managedResources.getAnnotatedClassReferences() ) {
categorizeAnnotatedClass( annotatedClass, attributeConverterManager, classLoaderService );
}
}
private void categorizeAnnotatedClass(Class annotatedClass, AttributeConverterManager attributeConverterManager, ClassLoaderService cls) {
final XClass xClass = reflectionManager.toXClass( annotatedClass );
// categorize it, based on assumption it does not fall into multiple categories
if ( xClass.isAnnotationPresent( Converter.class ) ) {
//noinspection unchecked
attributeConverterManager.addAttributeConverter( annotatedClass );
}
else if ( xClass.isAnnotationPresent( Entity.class )
|| xClass.isAnnotationPresent( MappedSuperclass.class ) ) {
xClasses.add( xClass );
}
else if ( xClass.isAnnotationPresent( Embeddable.class ) ) {
xClasses.add( xClass );
}
else {
log.debugf( "Encountered a non-categorized annotated class [%s]; ignoring", annotatedClass.getName() );
}
}
@SuppressWarnings("deprecation")
private XClass toXClass(String className, ReflectionManager reflectionManager, ClassLoaderService cls) {
return reflectionManager.toXClass( cls.classForName( className ) );
}
// private Document toDom4jDocument(MappingBinder.DelayedOrmXmlData delayedOrmXmlData) {
// // todo : do we need to build a DocumentFactory instance for use here?
// // historically we did that to set TCCL since, iirc, dom4j uses TCCL
// org.dom4j.io.STAXEventReader staxToDom4jReader = new STAXEventReader();
// try {
// return staxToDom4jReader.readDocument( delayedOrmXmlData.getStaxEventReader() );
// }
// catch (XMLStreamException e) {
// throw new MappingException(
// "An error occurred transforming orm.xml document from StAX to dom4j representation ",
// e,
// delayedOrmXmlData.getOrigin()
// );
// }
// }
@Override
public void prepare() {
// use any persistence-unit-defaults defined in orm.xml
( (JpaOrmXmlPersistenceUnitDefaultAware) rootMetadataBuildingContext.getBuildingOptions() ).apply(
new JpaOrmXmlPersistenceUnitDefaults() {
final Map persistenceUnitDefaults = reflectionManager.getDefaults();
@Override
public String getDefaultSchemaName() {
return StringHelper.nullIfEmpty( (String) persistenceUnitDefaults.get( "schema" ) );
}
@Override
public String getDefaultCatalogName() {
return StringHelper.nullIfEmpty( (String) persistenceUnitDefaults.get( "catalog" ) );
}
@Override
public boolean shouldImplicitlyQuoteIdentifiers() {
final Object isDelimited = persistenceUnitDefaults.get( "delimited-identifier" );
return isDelimited != null && isDelimited == Boolean.TRUE;
}
}
);
rootMetadataBuildingContext.getMetadataCollector().getDatabase().adjustDefaultNamespace(
rootMetadataBuildingContext.getBuildingOptions().getMappingDefaults().getImplicitCatalogName(),
rootMetadataBuildingContext.getBuildingOptions().getMappingDefaults().getImplicitSchemaName()
);
AnnotationBinder.bindDefaults( rootMetadataBuildingContext );
for ( String annotatedPackage : annotatedPackages ) {
AnnotationBinder.bindPackage( classLoaderService, annotatedPackage, rootMetadataBuildingContext );
}
}
@Override
public void processTypeDefinitions() {
}
@Override
public void processQueryRenames() {
}
@Override
public void processNamedQueries() {
}
@Override
public void processAuxiliaryDatabaseObjectDefinitions() {
}
@Override
public void processIdentifierGenerators() {
}
@Override
public void processFilterDefinitions() {
}
@Override
public void processFetchProfiles() {
}
@Override
public void prepareForEntityHierarchyProcessing() {
}
@Override
public void processEntityHierarchies(Set<String> processedEntityNames) {
final List<XClass> orderedClasses = orderAndFillHierarchy( xClasses );
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
orderedClasses,
rootMetadataBuildingContext
);
for ( XClass clazz : orderedClasses ) {
if ( processedEntityNames.contains( clazz.getName() ) ) {
log.debugf( "Skipping annotated class processing of entity [%s], as it has already been processed", clazz );
continue;
}
AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, rootMetadataBuildingContext );
AnnotationBinder.bindFetchProfilesForClass( clazz, rootMetadataBuildingContext );
processedEntityNames.add( clazz.getName() );
}
}
private List<XClass> orderAndFillHierarchy(List<XClass> original) {
List<XClass> copy = new ArrayList<>( original.size() );
insertMappedSuperclasses( original, copy );
// order the hierarchy
List<XClass> workingCopy = new ArrayList<XClass>( copy );
List<XClass> newList = new ArrayList<XClass>( copy.size() );
while ( workingCopy.size() > 0 ) {
XClass clazz = workingCopy.get( 0 );
orderHierarchy( workingCopy, newList, copy, clazz );
}
return newList;
}
private void insertMappedSuperclasses(List<XClass> original, List<XClass> copy) {
final boolean debug = log.isDebugEnabled();
for ( XClass clazz : original ) {
if ( clazz.isAnnotationPresent( javax.persistence.MappedSuperclass.class ) ) {
if ( debug ) {
log.debugf(
"Skipping explicit MappedSuperclass %s, the class will be discovered analyzing the implementing class",
clazz
);
}
}
else {
copy.add( clazz );
XClass superClass = clazz.getSuperclass();
while ( superClass != null
&& !reflectionManager.equals( superClass, Object.class )
&& !copy.contains( superClass ) ) {
if ( superClass.isAnnotationPresent( Entity.class )
|| superClass.isAnnotationPresent( javax.persistence.MappedSuperclass.class ) ) {
copy.add( superClass );
}
superClass = superClass.getSuperclass();
}
}
}
}
private void orderHierarchy(List<XClass> copy, List<XClass> newList, List<XClass> original, XClass clazz) {
if ( clazz == null || reflectionManager.equals( clazz, Object.class ) ) {
return;
}
//process superclass first
orderHierarchy( copy, newList, original, clazz.getSuperclass() );
if ( original.contains( clazz ) ) {
if ( !newList.contains( clazz ) ) {
newList.add( clazz );
}
copy.remove( clazz );
}
}
@Override
public void postProcessEntityHierarchies() {
for ( String annotatedPackage : annotatedPackages ) {
AnnotationBinder.bindFetchProfilesForPackage( classLoaderService, annotatedPackage, rootMetadataBuildingContext );
}
}
@Override
public void processResultSetMappings() {
}
@Override
public void finishUp() {
}
private static class AttributeConverterManager implements AttributeConverterDefinitionCollector {
private final MetadataBuildingContextRootImpl rootMetadataBuildingContext;
public AttributeConverterManager(MetadataBuildingContextRootImpl rootMetadataBuildingContext) {
this.rootMetadataBuildingContext = rootMetadataBuildingContext;
}
@Override
public void addAttributeConverter(AttributeConverterInfo info) {
rootMetadataBuildingContext.getMetadataCollector().addAttributeConverter(
info.toConverterDescriptor( rootMetadataBuildingContext )
);
}
@Override
public void addAttributeConverter(ConverterDescriptor descriptor) {
rootMetadataBuildingContext.getMetadataCollector().addAttributeConverter( descriptor );
}
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
rootMetadataBuildingContext.getMetadataCollector().addAttributeConverter( converterClass );
}
}
}