1 // $Id: AnnotationConfiguration.java 11251 2007-03-02 08:26:10Z epbernard $
2 package org.hibernate.cfg;
3
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.Method;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Comparator;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Properties;
18 import java.util.ResourceBundle;
19 import java.util.Set;
20 import java.util.SortedSet;
21 import java.util.StringTokenizer;
22 import java.util.TreeSet;
23 import javax.persistence.Entity;
24 import javax.persistence.MappedSuperclass;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.dom4j.Attribute;
29 import org.dom4j.Document;
30 import org.dom4j.DocumentException;
31 import org.dom4j.Element;
32 import org.dom4j.io.SAXReader;
33 import org.hibernate.AnnotationException;
34 import org.hibernate.AssertionFailure;
35 import org.hibernate.HibernateException;
36 import org.hibernate.MappingException;
37 import org.hibernate.SessionFactory;
38 import org.hibernate.annotations.common.reflection.ReflectionManager;
39 import org.hibernate.annotations.common.reflection.XClass;
40 import org.hibernate.cfg.annotations.Version;
41 import org.hibernate.cfg.annotations.reflection.EJB3ReflectionManager;
42 import org.hibernate.event.PreInsertEventListener;
43 import org.hibernate.event.PreUpdateEventListener;
44 import org.hibernate.event.PostInsertEventListener;
45 import org.hibernate.event.PostUpdateEventListener;
46 import org.hibernate.event.PostDeleteEventListener;
47 import org.hibernate.mapping.Column;
48 import org.hibernate.mapping.Join;
49 import org.hibernate.mapping.PersistentClass;
50 import org.hibernate.mapping.Table;
51 import org.hibernate.mapping.UniqueKey;
52 import org.hibernate.util.JoinedIterator;
53 import org.hibernate.util.ReflectHelper;
54 import org.hibernate.util.StringHelper;
55 import org.xml.sax.InputSource;
56 import org.xml.sax.SAXException;
57
58 /**
59 * Similar to the {@link Configuration} object but handles EJB3 and Hibernate
60 * specific annotations as a metadata facility
61 *
62 * @author Emmanuel Bernard
63 */
64 public class AnnotationConfiguration extends Configuration {
65 private static Log log = LogFactory.getLog( AnnotationConfiguration.class );
66
67 static {
68 Version.touch(); //touch version
69 }
70
71 public static final String ARTEFACT = "hibernate.mapping.precedence";
72 public static final String DEFAULT_PRECEDENCE = "hbm, class";
73
74 private Map namedGenerators;
75 private Map<String, Map<String, Join>> joins;
76 private Map<String, AnnotatedClassType> classTypes;
77 private Set<String> defaultNamedQueryNames;
78 private Set<String> defaultNamedNativeQueryNames;
79 private Set<String> defaultSqlResulSetMappingNames;
80 private Set<String> defaultNamedGenerators;
81 private Map<String, Properties> generatorTables;
82 private Map<Table, List<String[]>> tableUniqueConstraints;
83 private Map<String, String> mappedByResolver;
84 private Map<String, String> propertyRefResolver;
85 private List<XClass> annotatedClasses;
86 private Map<String, XClass> annotatedClassEntities;
87 private Map<String, Document> hbmEntities;
88 private List<CacheHolder> caches;
89 private List<Document> hbmDocuments; //user ordering matters, hence the list
90 private String precedence = null;
91 private boolean inSecondPass = false;
92 private transient ReflectionManager reflectionManager;
93 private boolean isDefaultProcessed = false;
94
95 public AnnotationConfiguration() {
96 super();
97 }
98
99 public AnnotationConfiguration(SettingsFactory sf) {
100 super( sf );
101 }
102
103 protected List<XClass> orderAndFillHierarchy(List<XClass> original) {
104 //TODO remove embeddable
105 List<XClass> copy = new ArrayList<XClass>( original );
106 //for each class, copy all the relevent hierarchy
107 for ( XClass clazz : original ) {
108 XClass superClass = clazz.getSuperclass();
109 while ( superClass != null && ! reflectionManager.equals( superClass, Object.class ) && ! copy.contains( superClass ) ) {
110 if ( superClass.isAnnotationPresent( Entity.class )
111 || superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
112 copy.add( superClass );
113 }
114 superClass = superClass.getSuperclass();
115 }
116 }
117 List<XClass> workingCopy = new ArrayList<XClass>( copy );
118 List<XClass> newList = new ArrayList<XClass>( copy.size() );
119 while ( workingCopy.size() > 0 ) {
120 XClass clazz = workingCopy.get( 0 );
121 orderHierarchy( workingCopy, newList, copy, clazz );
122 }
123 return newList;
124 }
125
126 private void orderHierarchy(List<XClass> copy, List<XClass> newList, List<XClass> original, XClass clazz) {
127 if ( clazz == null || reflectionManager.equals( clazz, Object.class ) ) return;
128 //process superclass first
129 orderHierarchy( copy, newList, original, clazz.getSuperclass() );
130 if ( original.contains( clazz ) ) {
131 if ( !newList.contains( clazz ) ) {
132 newList.add( clazz );
133 }
134 copy.remove( clazz );
135 }
136 }
137
138 /**
139 * Read a mapping from the class annotation metadata (JSR 175).
140 *
141 * @param persistentClass the mapped class
142 * @return the configuration object
143 */
144 public AnnotationConfiguration addAnnotatedClass(Class persistentClass) throws MappingException {
145 XClass persistentXClass = reflectionManager.toXClass( persistentClass );
146 try {
147 annotatedClasses.add( persistentXClass );
148 return this;
149 }
150 catch (MappingException me) {
151 log.error( "Could not compile the mapping annotations", me );
152 throw me;
153 }
154 }
155
156 /**
157 * Read package level metadata
158 *
159 * @param packageName java package name
160 * @return the configuration object
161 */
162 public AnnotationConfiguration addPackage(String packageName) throws MappingException {
163 log.info( "Mapping package " + packageName );
164 try {
165 AnnotationBinder.bindPackage( packageName, createExtendedMappings() );
166 return this;
167 }
168 catch (MappingException me) {
169 log.error( "Could not compile the mapping annotations", me );
170 throw me;
171 }
172 }
173
174 public ExtendedMappings createExtendedMappings() {
175 return new ExtendedMappings(
176 classes,
177 collections,
178 tables,
179 namedQueries,
180 namedSqlQueries,
181 sqlResultSetMappings,
182 defaultNamedQueryNames,
183 defaultNamedNativeQueryNames,
184 defaultSqlResulSetMappingNames,
185 defaultNamedGenerators,
186 imports,
187 secondPasses,
188 propertyReferences,
189 namingStrategy,
190 typeDefs,
191 filterDefinitions,
192 namedGenerators,
193 joins,
194 classTypes,
195 extendsQueue,
196 tableNameBinding, columnNameBindingPerTable, auxiliaryDatabaseObjects,
197 generatorTables,
198 tableUniqueConstraints,
199 mappedByResolver,
200 propertyRefResolver,
201 reflectionManager
202 );
203 }
204
205 @Override
206 public void setCacheConcurrencyStrategy(
207 String clazz, String concurrencyStrategy, String region, boolean cacheLazyProperty
208 ) throws MappingException {
209 caches.add( new CacheHolder( clazz, concurrencyStrategy, region, true, cacheLazyProperty ) );
210 }
211
212 @Override
213 public void setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy, String region)
214 throws MappingException {
215 caches.add( new CacheHolder( collectionRole, concurrencyStrategy, region, false, false ) );
216 }
217
218 @Override
219 protected void reset() {
220 super.reset();
221 namedGenerators = new HashMap();
222 joins = new HashMap<String, Map<String, Join>>();
223 classTypes = new HashMap<String, AnnotatedClassType>();
224 generatorTables = new HashMap<String, Properties>();
225 defaultNamedQueryNames = new HashSet<String>();
226 defaultNamedNativeQueryNames = new HashSet<String>();
227 defaultSqlResulSetMappingNames = new HashSet<String>();
228 defaultNamedGenerators = new HashSet<String>();
229 tableUniqueConstraints = new HashMap<Table, List<String[]>>();
230 mappedByResolver = new HashMap<String, String>();
231 propertyRefResolver = new HashMap<String, String>();
232 annotatedClasses = new ArrayList<XClass>();
233 caches = new ArrayList<CacheHolder>();
234 hbmEntities = new HashMap<String, Document>();
235 annotatedClassEntities = new HashMap<String, XClass>();
236 hbmDocuments = new ArrayList<Document>();
237 namingStrategy = EJB3NamingStrategy.INSTANCE;
238 setEntityResolver( new EJB3DTDEntityResolver() );
239 reflectionManager = new EJB3ReflectionManager();
240 }
241
242 @Override
243 protected void secondPassCompile() throws MappingException {
244 log.debug( "Execute first pass mapping processing" );
245 //build annotatedClassEntities
246 {
247 List<XClass> tempAnnotatedClasses = new ArrayList<XClass>( annotatedClasses.size() );
248 for ( XClass clazz : annotatedClasses ) {
249 if ( clazz.isAnnotationPresent( Entity.class ) ) {
250 annotatedClassEntities.put( clazz.getName(), clazz );
251 tempAnnotatedClasses.add( clazz );
252 }
253 else if ( clazz.isAnnotationPresent( MappedSuperclass.class ) ) {
254 tempAnnotatedClasses.add( clazz );
255 }
256 //only keep MappedSuperclasses and Entity in this list
257 }
258 annotatedClasses = tempAnnotatedClasses;
259 }
260
261 //process default values first
262 if ( ! isDefaultProcessed ) {
263 AnnotationBinder.bindDefaults( createExtendedMappings() );
264 isDefaultProcessed = true;
265 }
266
267 //process entities
268 if ( precedence == null ) precedence = getProperties().getProperty( ARTEFACT );
269 if ( precedence == null ) precedence = DEFAULT_PRECEDENCE;
270 StringTokenizer precedences = new StringTokenizer( precedence, ",; ", false );
271 if ( ! precedences.hasMoreElements() ) {
272 throw new MappingException( ARTEFACT + " cannot be empty: " + precedence );
273 }
274 while ( precedences.hasMoreElements() ) {
275 String artifact = (String) precedences.nextElement();
276 removeConflictedArtifact( artifact );
277 processArtifactsOfType( artifact );
278 }
279
280 int cacheNbr = caches.size();
281 for ( int index = 0; index < cacheNbr ; index++ ) {
282 CacheHolder cacheHolder = caches.get( index );
283 if ( cacheHolder.isClass ) {
284 super.setCacheConcurrencyStrategy(
285 cacheHolder.role, cacheHolder.usage, cacheHolder.region, cacheHolder.cacheLazy
286 );
287 }
288 else {
289 super.setCollectionCacheConcurrencyStrategy( cacheHolder.role, cacheHolder.usage, cacheHolder.region );
290 }
291 }
292 caches.clear();
293
294 inSecondPass = true;
295 processFkSecondPassInOrder();
296 Iterator iter = secondPasses.iterator();
297 while ( iter.hasNext() ) {
298 SecondPass sp = (SecondPass) iter.next();
299 //do the second pass of fk before the others and remove them
300 if ( sp instanceof CreateKeySecondPass ) {
301 sp.doSecondPass( classes );
302 iter.remove();
303 }
304 }
305
306 //process OneToManySecondPass in order: first
307 iter = secondPasses.iterator();
308 while ( iter.hasNext() ) {
309 SecondPass sp = (SecondPass) iter.next();
310
311 if ( sp instanceof CreateKeySecondPass ) {
312 sp.doSecondPass( classes );
313 iter.remove();
314 }
315 }
316 super.secondPassCompile();
317 inSecondPass = false;
318 Iterator tables = (Iterator<Map.Entry<Table, List<String[]>>>) tableUniqueConstraints.entrySet().iterator();
319 Table table;
320 Map.Entry entry;
321 String keyName;
322 int uniqueIndexPerTable;
323 while ( tables.hasNext() ) {
324 entry = (Map.Entry) tables.next();
325 table = (Table) entry.getKey();
326 List<String[]> uniqueConstraints = (List<String[]>) entry.getValue();
327 uniqueIndexPerTable = 0;
328 for ( String[] columnNames : uniqueConstraints ) {
329 keyName = "key" + uniqueIndexPerTable++;
330 buildUniqueKeyFromColumnNames( columnNames, table, keyName );
331 }
332 }
333 boolean applyOnDdl = getProperties().getProperty(
334 "hibernate.validator.apply_to_ddl", //org.hibernate.validator.Environment.APPLY_TO_DDL
335 "true" )
336 .equalsIgnoreCase( "true" );
337
338 Constructor validatorCtr = null;
339 Method applyMethod = null;
340 try {
341 Class classValidator = ReflectHelper.classForName("org.hibernate.validator.ClassValidator", this.getClass() );
342 Class messageInterpolator = ReflectHelper.classForName("org.hibernate.validator.MessageInterpolator", this.getClass() );
343 validatorCtr = classValidator.getDeclaredConstructor( new Class[] {
344 Class.class, ResourceBundle.class, messageInterpolator, Map.class, ReflectionManager.class
345 }
346 );
347 applyMethod = classValidator.getMethod( "apply", PersistentClass.class );
348 }
349 catch (ClassNotFoundException e) {
350 log.info( "Hibernate Validator not found: ignoring");
351 }
352 catch (NoSuchMethodException e) {
353 throw new AnnotationException(e);
354 }
355 if ( applyMethod != null && applyOnDdl) {
356 for ( PersistentClass persistentClazz : (Collection<PersistentClass>) classes.values() ) {
357 //integrate the validate framework
358 String className = persistentClazz.getClassName();
359 if ( StringHelper.isNotEmpty( className ) ) {
360 try {
361 Object validator = validatorCtr.newInstance(
362 ReflectHelper.classForName( className ), null, null, null, reflectionManager
363 );
364 applyMethod.invoke( validator, persistentClazz );
365 }
366 catch (Exception e) {
367 log.warn("Unable to apply constraints on DDL for " + className, e);
368 }
369 }
370 }
371 }
372 }
373
374 private void processFkSecondPassInOrder() {
375 log.debug( "processing manytoone fk mappings" );
376 Iterator iter = secondPasses.iterator();
377 /* We need to process FKSecond pass trying to resolve any
378 * graph circularity (ie PK made of a many to one linking to
379 * an entity having a PK made of a ManyToOne ...
380 */
381 SortedSet<FkSecondPass> fkSecondPasses = new TreeSet<FkSecondPass>(
382 new Comparator() {
383 //The comparator implementation has to respect the compare=0 => equals() = true for sets
384 public int compare(Object o1, Object o2) {
385 if (! (o1 instanceof FkSecondPass && o2 instanceof FkSecondPass) ) {
386 throw new AssertionFailure("comparint FkSecondPass with non FkSecondPass");
387 }
388 FkSecondPass f1 = (FkSecondPass) o1;
389 FkSecondPass f2 = (FkSecondPass) o2;
390 int compare = f1.getValue().getTable().getQuotedName().compareTo(
391 f2.getValue().getTable().getQuotedName()
392 );
393 if (compare == 0) {
394 //same table, we still need to differenciate true equality
395 if ( f1.hashCode() < f2.hashCode() ) {
396 compare = -1;
397 }
398 else if ( f1.hashCode() == f2.hashCode() ) {
399 compare = 0;
400 }
401 else {
402 compare = 1;
403 }
404 }
405 return compare;
406 }
407 }
408 );
409 while ( iter.hasNext() ) {
410 SecondPass sp = (SecondPass) iter.next();
411 //do the second pass of fk before the others and remove them
412 if ( sp instanceof FkSecondPass ) {
413 fkSecondPasses.add( (FkSecondPass) sp );
414 iter.remove();
415 }
416 }
417 if ( fkSecondPasses.size() > 0 ) {
418 Map<String, Set<String>> isADependencyOf = new HashMap<String, Set<String>>();
419 List orderedFkSecondPasses = new ArrayList( fkSecondPasses.size() );
420 List endOfQueueFkSecondPasses = new ArrayList( fkSecondPasses.size() );
421 List orderedTable = new ArrayList( fkSecondPasses.size() );
422 Iterator it = fkSecondPasses.iterator();
423 while ( it.hasNext() ) {
424 FkSecondPass sp = (FkSecondPass) it.next();
425 String referenceEntityName = sp.getValue().getReferencedEntityName();
426 PersistentClass classMapping = getClassMapping( referenceEntityName );
427 if ( sp.isInPrimaryKey() ) {
428 String dependentTable = classMapping.getTable().getQuotedName();
429 if ( ! isADependencyOf.containsKey( dependentTable ) ) {
430 isADependencyOf.put( dependentTable, new HashSet<String>() );
431 }
432 String table = sp.getValue().getTable().getQuotedName();
433 isADependencyOf.get( dependentTable ).add( table );
434 int beAfter = orderedTable.indexOf( dependentTable );
435 int beBefore = orderedFkSecondPasses.size();
436 Set<String> dependencies = isADependencyOf.get( table );
437 if ( dependencies != null ) {
438 for ( String tableDep : dependencies ) {
439 //for each declared dependency take the lowest index
440 int index = orderedTable.indexOf( tableDep );
441 //index = -1 when we have a self dependency
442 beBefore = index != -1 && index < beBefore ? index : beBefore;
443 }
444 }
445 int currentIndex = orderedTable.indexOf( table );
446 if ( beBefore < beAfter ||
447 ( currentIndex != -1 && ( currentIndex < beAfter || currentIndex > beBefore ) )
448 ) {
449 StringBuilder sb = new StringBuilder(
450 "Foreign key circularity dependency involving the following tables: "
451 );
452 //TODO deduplicate tables
453 sb.append( table );
454 if ( beAfter > -1 ) sb.append( ", " ).append( dependentTable );
455 if ( beBefore < orderedFkSecondPasses.size() ) {
456 sb.append( ", " ).append( orderedTable.get( beBefore ) );
457 }
458 throw new AnnotationException( sb.toString() );
459 }
460 currentIndex = currentIndex == -1 ? beBefore : currentIndex;
461 orderedTable.add( currentIndex, table );
462 orderedFkSecondPasses.add( currentIndex, sp );
463 }
464 else {
465 endOfQueueFkSecondPasses.add( sp );
466 }
467 }
468 it = orderedFkSecondPasses.listIterator();
469 while ( it.hasNext() ) {
470 ( (SecondPass) it.next() ).doSecondPass( classes );
471 }
472 it = endOfQueueFkSecondPasses.listIterator();
473 while ( it.hasNext() ) {
474 ( (SecondPass) it.next() ).doSecondPass( classes );
475 }
476 }
477 }
478
479 private void processArtifactsOfType(String artifact) {
480 if ( "hbm".equalsIgnoreCase( artifact ) ) {
481 log.debug( "Process hbm files" );
482 for ( Document document : hbmDocuments ) {
483 super.add( document );
484 }
485 hbmDocuments.clear();
486 hbmEntities.clear();
487 }
488 else if ( "class".equalsIgnoreCase( artifact ) ) {
489 log.debug( "Process annotated classes" );
490 //bind classes in the correct order calculating some inheritance state
491 List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
492 Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
493 orderedClasses, reflectionManager
494 );
495 ExtendedMappings mappings = createExtendedMappings();
496 for ( XClass clazz : orderedClasses ) {
497 //todo use the same extended mapping
498 AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );
499 }
500 annotatedClasses.clear();
501 annotatedClassEntities.clear();
502 }
503 else {
504 log.warn( "Unknown artifact: " + artifact );
505 }
506 }
507
508 private void removeConflictedArtifact(String artifact) {
509 if ( "hbm".equalsIgnoreCase( artifact ) ) {
510 for ( String entity : hbmEntities.keySet() ) {
511 if ( annotatedClassEntities.containsKey( entity ) ) {
512 annotatedClasses.remove( annotatedClassEntities.get( entity ) );
513 annotatedClassEntities.remove( entity );
514 }
515 }
516 }
517 else if ( "class".equalsIgnoreCase( artifact ) ) {
518 for ( String entity : annotatedClassEntities.keySet() ) {
519 if ( hbmEntities.containsKey( entity ) ) {
520 hbmDocuments.remove( hbmEntities.get( entity ) );
521 hbmEntities.remove( entity );
522 }
523 }
524 }
525 }
526
527 private void buildUniqueKeyFromColumnNames(String[] columnNames, Table table, String keyName) {
528 UniqueKey uc;
529 int size = columnNames.length;
530 Column[] columns = new Column[size];
531 Set<Column> unbound = new HashSet<Column>();
532 Set<Column> unboundNoLogical = new HashSet<Column>();
533 ExtendedMappings mappings = createExtendedMappings();
534 for ( int index = 0; index < size ; index++ ) {
535 String columnName;
536 try {
537 columnName = mappings.getPhysicalColumnName( columnNames[index], table );
538 columns[index] = new Column( columnName );
539 unbound.add( columns[index] );
540 //column equals and hashcode is based on column name
541 }
542 catch( MappingException e ) {
543 unboundNoLogical.add( new Column( columnNames[index] ) );
544 }
545 }
546 for ( Column column : columns ) {
547 if ( table.containsColumn( column ) ) {
548 uc = table.getOrCreateUniqueKey( keyName );
549 uc.addColumn( table.getColumn( column ) );
550 unbound.remove( column );
551 }
552 }
553 if ( unbound.size() > 0 || unboundNoLogical.size() > 0 ) {
554 StringBuilder sb = new StringBuilder( "Unable to create unique key constraint (" );
555 for ( String columnName : columnNames ) {
556 sb.append( columnName ).append( ", " );
557 }
558 sb.setLength( sb.length() - 2 );
559 sb.append( ") on table " ).append( table.getName() ).append( ": " );
560 for ( Column column : unbound ) {
561 sb.append( column.getName() ).append( ", " );
562 }
563 for ( Column column : unboundNoLogical ) {
564 sb.append( column.getName() ).append( ", " );
565 }
566 sb.setLength( sb.length() - 2 );
567 sb.append( " not found" );
568 throw new AnnotationException( sb.toString() );
569 }
570 }
571
572 @Override
573 protected void parseMappingElement(Element subelement, String name) {
574 Attribute rsrc = subelement.attribute( "resource" );
575 Attribute file = subelement.attribute( "file" );
576 Attribute jar = subelement.attribute( "jar" );
577 Attribute pckg = subelement.attribute( "package" );
578 Attribute clazz = subelement.attribute( "class" );
579 if ( rsrc != null ) {
580 log.debug( name + "<-" + rsrc );
581 addResource( rsrc.getValue() );
582 }
583 else if ( jar != null ) {
584 log.debug( name + "<-" + jar );
585 addJar( new File( jar.getValue() ) );
586 }
587 else if ( file != null ) {
588 log.debug( name + "<-" + file );
589 addFile( file.getValue() );
590 }
591 else if ( pckg != null ) {
592 log.debug( name + "<-" + pckg );
593 addPackage( pckg.getValue() );
594 }
595 else if ( clazz != null ) {
596 log.debug( name + "<-" + clazz );
597 Class loadedClass = null;
598 try {
599 loadedClass = ReflectHelper.classForName( clazz.getValue() );
600 }
601 catch (ClassNotFoundException cnf) {
602 throw new MappingException(
603 "Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
604 cnf
605 );
606 }
607 catch (NoClassDefFoundError ncdf) {
608 throw new MappingException(
609 "Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
610 ncdf
611 );
612 }
613
614 addAnnotatedClass( loadedClass );
615 }
616 else {
617 throw new MappingException( "<mapping> element in configuration specifies no attributes" );
618 }
619 }
620
621 @Override
622 protected void add(org.dom4j.Document doc) throws MappingException {
623 boolean ejb3Xml = "entity-mappings".equals( doc.getRootElement().getName() );
624 if ( inSecondPass ) {
625 //if in second pass bypass the queueing, getExtendedQueue reuse this method
626 if ( !ejb3Xml ) super.add( doc );
627 }
628 else {
629 if ( ! ejb3Xml ) {
630 final Element hmNode = doc.getRootElement();
631 Attribute packNode = hmNode.attribute( "package" );
632 String defaultPackage = packNode != null
633 ? packNode.getValue()
634 : "";
635 Set<String> entityNames = new HashSet<String>();
636 findClassNames( defaultPackage, hmNode, entityNames );
637 for ( String entity : entityNames ) {
638 hbmEntities.put( entity, doc );
639 }
640 hbmDocuments.add( doc );
641 }
642 else {
643 List<String> classnames = ( (EJB3ReflectionManager) reflectionManager ).getXMLContext().addDocument( doc );
644 for ( String classname : classnames ) {
645 try {
646 annotatedClasses.add( reflectionManager.classForName( classname, this.getClass() ) );
647 }
648 catch (ClassNotFoundException e) {
649 throw new AnnotationException( "Unable to load class defined in XML: " + classname, e );
650 }
651 }
652 }
653 }
654 }
655
656 private static void findClassNames(
657 String defaultPackage, final Element startNode,
658 final java.util.Set names
659 ) {
660 // if we have some extends we need to check if those classes possibly could be inside the
661 // same hbm.xml file...
662 Iterator[] classes = new Iterator[4];
663 classes[0] = startNode.elementIterator( "class" );
664 classes[1] = startNode.elementIterator( "subclass" );
665 classes[2] = startNode.elementIterator( "joined-subclass" );
666 classes[3] = startNode.elementIterator( "union-subclass" );
667
668 Iterator classIterator = new JoinedIterator( classes );
669 while ( classIterator.hasNext() ) {
670 Element element = (Element) classIterator.next();
671 String entityName = element.attributeValue( "entity-name" );
672 if ( entityName == null ) entityName = getClassName( element.attribute( "name" ), defaultPackage );
673 names.add( entityName );
674 findClassNames( defaultPackage, element, names );
675 }
676 }
677
678 private static String getClassName(Attribute name, String defaultPackage) {
679 if ( name == null ) return null;
680 String unqualifiedName = name.getValue();
681 if ( unqualifiedName == null ) return null;
682 if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
683 return defaultPackage + '.' + unqualifiedName;
684 }
685 return unqualifiedName;
686 }
687
688 public void setPrecedence(String precedence) {
689 this.precedence = precedence;
690 }
691
692 private static class CacheHolder {
693 public CacheHolder(String role, String usage, String region, boolean isClass, boolean cacheLazy) {
694 this.role = role;
695 this.usage = usage;
696 this.region = region;
697 this.isClass = isClass;
698 this.cacheLazy = cacheLazy;
699 }
700
701 public String role;
702 public String usage;
703 public String region;
704 public boolean isClass;
705 public boolean cacheLazy;
706 }
707
708 @Override
709 public Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
710 try {
711 List errors = new ArrayList();
712 SAXReader saxReader = xmlHelper.createSAXReader( "XML InputStream", errors, getEntityResolver() );
713 try {
714 saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
715 //saxReader.setFeature( "http://apache.org/xml/features/validation/dynamic", true );
716 //set the default schema locators
717 saxReader.setProperty(
718 "http://apache.org/xml/properties/schema/external-schemaLocation",
719 "http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
720 );
721 }
722 catch (SAXException e) {
723 saxReader.setValidation( false );
724 }
725 org.dom4j.Document doc = saxReader
726 .read( new InputSource( xmlInputStream ) );
727
728 if ( errors.size() != 0 ) {
729 throw new MappingException( "invalid mapping", (Throwable) errors.get( 0 ) );
730 }
731 add( doc );
732 return this;
733 }
734 catch (DocumentException e) {
735 throw new MappingException( "Could not parse mapping document in input stream", e );
736 }
737 finally {
738 try {
739 xmlInputStream.close();
740 }
741 catch (IOException ioe) {
742 log.warn( "Could not close input stream", ioe );
743 }
744 }
745 }
746
747 public SessionFactory buildSessionFactory() throws HibernateException {
748 //add validator events if the jar is available
749 boolean enableValidatorListeners = ! "false".equalsIgnoreCase( getProperty( "hibernate.validator.autoregister_listeners" ) );
750 Class validateEventListenerClass = null;
751 try {
752 validateEventListenerClass = ReflectHelper.classForName(
753 "org.hibernate.validator.event.ValidateEventListener",
754 AnnotationConfiguration.class );
755 }
756 catch (ClassNotFoundException e) {
757 //validator is not present
758 log.debug( "Validator not present in classpath, ignoring event listener registration" );
759 }
760 if (enableValidatorListeners && validateEventListenerClass != null) {
761 //TODO so much duplication
762 Object validateEventListener;
763 try {
764 validateEventListener = validateEventListenerClass.newInstance();
765 }
766 catch (Exception e) {
767 throw new AnnotationException("Unable to load Validator event listener", e );
768 }
769 {
770 boolean present = false;
771 PreInsertEventListener[] listeners = getEventListeners().getPreInsertEventListeners();
772 if (listeners != null) {
773 for ( Object eventListener : listeners ) {
774 //not isAssignableFrom since the user could subclass
775 present = present || validateEventListenerClass == eventListener.getClass();
776 }
777 if (!present) {
778 int length = listeners.length + 1;
779 PreInsertEventListener[] newListeners = new PreInsertEventListener[length];
780 for ( int i = 0 ; i < length - 1 ; i++ ) {
781 newListeners[i] = listeners[i];
782 }
783 newListeners[length-1] = (PreInsertEventListener) validateEventListener;
784 getEventListeners().setPreInsertEventListeners(newListeners);
785 }
786 }
787 else {
788 getEventListeners().setPreInsertEventListeners(
789 new PreInsertEventListener[] { (PreInsertEventListener) validateEventListener }
790 );
791 }
792 }
793
794 //update event listener
795 {
796 boolean present = false;
797 PreUpdateEventListener[] listeners = getEventListeners().getPreUpdateEventListeners();
798 if (listeners != null) {
799 for ( Object eventListener : listeners ) {
800 //not isAssignableFrom since the user could subclass
801 present = present || validateEventListenerClass == eventListener.getClass();
802 }
803 if (!present) {
804 int length = listeners.length + 1;
805 PreUpdateEventListener[] newListeners = new PreUpdateEventListener[length];
806 for ( int i = 0 ; i < length - 1 ; i++ ) {
807 newListeners[i] = listeners[i];
808 }
809 newListeners[length-1] = (PreUpdateEventListener) validateEventListener;
810 getEventListeners().setPreUpdateEventListeners(newListeners);
811 }
812 }
813 else {
814 getEventListeners().setPreUpdateEventListeners(
815 new PreUpdateEventListener[] { (PreUpdateEventListener) validateEventListener }
816 );
817 }
818 }
819 }
820
821 //add search events if the jar is available
822 boolean enableSearchListeners = ! "false".equalsIgnoreCase( getProperty( "hibernate.search.autoregister_listeners" ) );
823 Class searchEventListenerClass = null;
824 try {
825 searchEventListenerClass = ReflectHelper.classForName(
826 "org.hibernate.search.event.FullTextIndexEventListener",
827 AnnotationConfiguration.class );
828 }
829 catch (ClassNotFoundException e) {
830 //search is not present
831 log.debug( "Search not present in classpath, ignoring event listener registration" );
832 }
833 if (enableSearchListeners && searchEventListenerClass != null) {
834 //TODO so much duplication
835 Object searchEventListener;
836 try {
837 searchEventListener = searchEventListenerClass.newInstance();
838 }
839 catch (Exception e) {
840 throw new AnnotationException("Unable to load Search event listener", e );
841 }
842 {
843 boolean present = false;
844 PostInsertEventListener[] listeners = getEventListeners().getPostInsertEventListeners();
845 if (listeners != null) {
846 for ( Object eventListener : listeners ) {
847 //not isAssignableFrom since the user could subclass
848 present = present || searchEventListenerClass == eventListener.getClass();
849 }
850 if (!present) {
851 int length = listeners.length + 1;
852 PostInsertEventListener[] newListeners = new PostInsertEventListener[length];
853 for ( int i = 0 ; i < length - 1 ; i++ ) {
854 newListeners[i] = listeners[i];
855 }
856 newListeners[length-1] = (PostInsertEventListener) searchEventListener;
857 getEventListeners().setPostInsertEventListeners(newListeners);
858 }
859 }
860 else {
861 getEventListeners().setPostInsertEventListeners(
862 new PostInsertEventListener[] { (PostInsertEventListener) searchEventListener }
863 );
864 }
865 }
866 {
867 boolean present = false;
868 PostUpdateEventListener[] listeners = getEventListeners().getPostUpdateEventListeners();
869 if (listeners != null) {
870 for ( Object eventListener : listeners ) {
871 //not isAssignableFrom since the user could subclass
872 present = present || searchEventListenerClass == eventListener.getClass();
873 }
874 if (!present) {
875 int length = listeners.length + 1;
876 PostUpdateEventListener[] newListeners = new PostUpdateEventListener[length];
877 for ( int i = 0 ; i < length - 1 ; i++ ) {
878 newListeners[i] = listeners[i];
879 }
880 newListeners[length-1] = (PostUpdateEventListener) searchEventListener;
881 getEventListeners().setPostUpdateEventListeners(newListeners);
882 }
883 }
884 else {
885 getEventListeners().setPostUpdateEventListeners(
886 new PostUpdateEventListener[] { (PostUpdateEventListener) searchEventListener }
887 );
888 }
889 }
890 {
891 boolean present = false;
892 PostDeleteEventListener[] listeners = getEventListeners().getPostDeleteEventListeners();
893 if (listeners != null) {
894 for ( Object eventListener : listeners ) {
895 //not isAssignableFrom since the user could subclass
896 present = present || searchEventListenerClass == eventListener.getClass();
897 }
898 if (!present) {
899 int length = listeners.length + 1;
900 PostDeleteEventListener[] newListeners = new PostDeleteEventListener[length];
901 for ( int i = 0 ; i < length - 1 ; i++ ) {
902 newListeners[i] = listeners[i];
903 }
904 newListeners[length-1] = (PostDeleteEventListener) searchEventListener;
905 getEventListeners().setPostDeleteEventListeners(newListeners);
906 }
907 }
908 else {
909 getEventListeners().setPostDeleteEventListeners(
910 new PostDeleteEventListener[] { (PostDeleteEventListener) searchEventListener }
911 );
912 }
913 }
914 }
915 return super.buildSessionFactory();
916 }
917
918 //not a public API
919 public ReflectionManager getReflectionManager() {
920 return reflectionManager;
921 }
922 }