Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]
    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   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]