Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » cfg » [javadoc | source]
    1   // $Id: AnnotationConfiguration.java 14990 2008-07-29 18:14:14Z hardy.ferentschik $
    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.InvocationTargetException;
    9   import java.lang.reflect.Method;
   10   import java.net.URL;
   11   import java.util.ArrayList;
   12   import java.util.Collection;
   13   import java.util.HashMap;
   14   import java.util.HashSet;
   15   import java.util.Iterator;
   16   import java.util.List;
   17   import java.util.Map;
   18   import java.util.Properties;
   19   import java.util.ResourceBundle;
   20   import java.util.Set;
   21   import java.util.StringTokenizer;
   22   
   23   import javax.persistence.Entity;
   24   import javax.persistence.MappedSuperclass;
   25   
   26   import org.dom4j.Attribute;
   27   import org.dom4j.Document;
   28   import org.dom4j.DocumentException;
   29   import org.dom4j.Element;
   30   import org.dom4j.io.SAXReader;
   31   import org.hibernate.AnnotationException;
   32   import org.hibernate.HibernateException;
   33   import org.hibernate.Interceptor;
   34   import org.hibernate.MappingException;
   35   import org.hibernate.SessionFactory;
   36   import org.hibernate.annotations.AnyMetaDef;
   37   import org.hibernate.annotations.common.reflection.ReflectionManager;
   38   import org.hibernate.annotations.common.reflection.XClass;
   39   import org.hibernate.cfg.annotations.Version;
   40   import org.hibernate.cfg.annotations.reflection.EJB3ReflectionManager;
   41   import org.hibernate.event.EventListeners;
   42   import org.hibernate.event.PreInsertEventListener;
   43   import org.hibernate.event.PreUpdateEventListener;
   44   import org.hibernate.mapping.Column;
   45   import org.hibernate.mapping.Join;
   46   import org.hibernate.mapping.PersistentClass;
   47   import org.hibernate.mapping.Table;
   48   import org.hibernate.mapping.UniqueKey;
   49   import org.hibernate.util.JoinedIterator;
   50   import org.hibernate.util.ReflectHelper;
   51   import org.hibernate.util.StringHelper;
   52   import org.slf4j.Logger;
   53   import org.slf4j.LoggerFactory;
   54   import org.xml.sax.InputSource;
   55   import org.xml.sax.SAXException;
   56   
   57   /**
   58    * Similar to the {@link Configuration} object but handles EJB3 and Hibernate
   59    * specific annotations as a metadata facility.
   60    *
   61    * @author Emmanuel Bernard
   62    * @author Hardy Ferentschik
   63    */
   64   public class AnnotationConfiguration extends Configuration {
   65   	private Logger log = LoggerFactory.getLogger( AnnotationConfiguration.class );
   66   	
   67   	/**
   68   	 * Class name of the class needed to enable Search.
   69   	 */
   70   	private static final String SEARCH_STARTUP_CLASS = "org.hibernate.search.event.EventListenerRegister";
   71   	
   72   	/**
   73   	 * Method to call to enable Search.
   74   	 */	
   75   	private static final String SEARCH_STARTUP_METHOD = "enableHibernateSearch";
   76   
   77   	static {
   78   		Version.touch(); //touch version
   79   	}
   80   
   81   	public static final String ARTEFACT = "hibernate.mapping.precedence";
   82   	public static final String DEFAULT_PRECEDENCE = "hbm, class";
   83   
   84   	private Map namedGenerators;
   85   	private Map<String, Map<String, Join>> joins;
   86   	private Map<String, AnnotatedClassType> classTypes;
   87   	private Set<String> defaultNamedQueryNames;
   88   	private Set<String> defaultNamedNativeQueryNames;
   89   	private Set<String> defaultSqlResulSetMappingNames;
   90   	private Set<String> defaultNamedGenerators;
   91   	private Map<String, Properties> generatorTables;
   92   	private Map<Table, List<String[]>> tableUniqueConstraints;
   93   	private Map<String, String> mappedByResolver;
   94   	private Map<String, String> propertyRefResolver;
   95   	private Map<String, AnyMetaDef> anyMetaDefs;
   96   	private List<XClass> annotatedClasses;
   97   	private Map<String, XClass> annotatedClassEntities;
   98   	private Map<String, Document> hbmEntities;
   99   	private List<CacheHolder> caches;
  100   	private List<Document> hbmDocuments; //user ordering matters, hence the list
  101   	private String precedence = null;
  102   	private boolean inSecondPass = false;
  103   	private transient ReflectionManager reflectionManager;
  104   	private boolean isDefaultProcessed = false;
  105   	private boolean isValidatorNotPresentLogged;
  106   
  107   	public AnnotationConfiguration() {
  108   		super();
  109   	}
  110   
  111   	public AnnotationConfiguration(SettingsFactory sf) {
  112   		super( sf );
  113   	}
  114   
  115   	protected List<XClass> orderAndFillHierarchy(List<XClass> original) {
  116   		//TODO remove embeddable
  117   		List<XClass> copy = new ArrayList<XClass>( original );
  118   		//for each class, copy all the relevant hierarchy
  119   		for (XClass clazz : original) {
  120   			XClass superClass = clazz.getSuperclass();
  121   			while ( superClass != null && !reflectionManager.equals( superClass, Object.class ) && !copy.contains( superClass ) ) {
  122   				if ( superClass.isAnnotationPresent( Entity.class )
  123   						|| superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
  124   					copy.add( superClass );
  125   				}
  126   				superClass = superClass.getSuperclass();
  127   			}
  128   		}
  129   		List<XClass> workingCopy = new ArrayList<XClass>( copy );
  130   		List<XClass> newList = new ArrayList<XClass>( copy.size() );
  131   		while ( workingCopy.size() > 0 ) {
  132   			XClass clazz = workingCopy.get( 0 );
  133   			orderHierarchy( workingCopy, newList, copy, clazz );
  134   		}
  135   		return newList;
  136   	}
  137   
  138   	private void orderHierarchy(List<XClass> copy, List<XClass> newList, List<XClass> original, XClass clazz) {
  139   		if ( clazz == null || reflectionManager.equals( clazz, Object.class ) ) return;
  140   		//process superclass first
  141   		orderHierarchy( copy, newList, original, clazz.getSuperclass() );
  142   		if ( original.contains( clazz ) ) {
  143   			if ( !newList.contains( clazz ) ) {
  144   				newList.add( clazz );
  145   			}
  146   			copy.remove( clazz );
  147   		}
  148   	}
  149   
  150   	/**
  151   	 * Read a mapping from the class annotation metadata (JSR 175).
  152   	 *
  153   	 * @param persistentClass the mapped class
  154   	 * @return the configuration object
  155   	 */
  156   	public AnnotationConfiguration addAnnotatedClass(Class persistentClass) throws MappingException {
  157   		XClass persistentXClass = reflectionManager.toXClass( persistentClass );
  158   		try {
  159   			annotatedClasses.add( persistentXClass );
  160   			return this;
  161   		}
  162   		catch (MappingException me) {
  163   			log.error( "Could not compile the mapping annotations", me );
  164   			throw me;
  165   		}
  166   	}
  167   
  168   	/**
  169   	 * Read package level metadata
  170   	 *
  171   	 * @param packageName java package name
  172   	 * @return the configuration object
  173   	 */
  174   	public AnnotationConfiguration addPackage(String packageName) throws MappingException {
  175   		log.info( "Mapping package {}", packageName );
  176   		try {
  177   			AnnotationBinder.bindPackage( packageName, createExtendedMappings() );
  178   			return this;
  179   		}
  180   		catch (MappingException me) {
  181   			log.error( "Could not compile the mapping annotations", me );
  182   			throw me;
  183   		}
  184   	}
  185   
  186   	public ExtendedMappings createExtendedMappings() {
  187   		return new ExtendedMappings(
  188   				classes,
  189   				collections,
  190   				tables,
  191   				namedQueries,
  192   				namedSqlQueries,
  193   				sqlResultSetMappings,
  194   				defaultNamedQueryNames,
  195   				defaultNamedNativeQueryNames,
  196   				defaultSqlResulSetMappingNames,
  197   				defaultNamedGenerators,
  198   				imports,
  199   				secondPasses,
  200   				propertyReferences,
  201   				namingStrategy,
  202   				typeDefs,
  203   				filterDefinitions,
  204   				namedGenerators,
  205   				joins,
  206   				classTypes,
  207   				extendsQueue,
  208   				tableNameBinding, columnNameBindingPerTable, auxiliaryDatabaseObjects,
  209   				generatorTables,
  210   				tableUniqueConstraints,
  211   				mappedByResolver,
  212   				propertyRefResolver,
  213   				anyMetaDefs,
  214   				reflectionManager
  215   		);
  216   	}
  217   
  218   	@Override
  219   	public void setCacheConcurrencyStrategy(
  220   			String clazz, String concurrencyStrategy, String region, boolean cacheLazyProperty
  221   	) throws MappingException {
  222   		caches.add( new CacheHolder( clazz, concurrencyStrategy, region, true, cacheLazyProperty ) );
  223   	}
  224   
  225   	@Override
  226   	public void setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy, String region)
  227   			throws MappingException {
  228   		caches.add( new CacheHolder( collectionRole, concurrencyStrategy, region, false, false ) );
  229   	}
  230   
  231   	@Override
  232   	protected void reset() {
  233   		super.reset();
  234   		namedGenerators = new HashMap();
  235   		joins = new HashMap<String, Map<String, Join>>();
  236   		classTypes = new HashMap<String, AnnotatedClassType>();
  237   		generatorTables = new HashMap<String, Properties>();
  238   		defaultNamedQueryNames = new HashSet<String>();
  239   		defaultNamedNativeQueryNames = new HashSet<String>();
  240   		defaultSqlResulSetMappingNames = new HashSet<String>();
  241   		defaultNamedGenerators = new HashSet<String>();
  242   		tableUniqueConstraints = new HashMap<Table, List<String[]>>();
  243   		mappedByResolver = new HashMap<String, String>();
  244   		propertyRefResolver = new HashMap<String, String>();
  245   		annotatedClasses = new ArrayList<XClass>();
  246   		caches = new ArrayList<CacheHolder>();
  247   		hbmEntities = new HashMap<String, Document>();
  248   		annotatedClassEntities = new HashMap<String, XClass>();
  249   		hbmDocuments = new ArrayList<Document>();
  250   		namingStrategy = EJB3NamingStrategy.INSTANCE;
  251   		setEntityResolver( new EJB3DTDEntityResolver() );
  252   		anyMetaDefs = new HashMap<String, AnyMetaDef>();
  253   		reflectionManager = new EJB3ReflectionManager();
  254   	}
  255   
  256   	@Override
  257   	protected void secondPassCompile() throws MappingException {
  258   		log.debug( "Execute first pass mapping processing" );
  259   		//build annotatedClassEntities
  260   		{
  261   			List<XClass> tempAnnotatedClasses = new ArrayList<XClass>( annotatedClasses.size() );
  262   			for (XClass clazz : annotatedClasses) {
  263   				if ( clazz.isAnnotationPresent( Entity.class ) ) {
  264   					annotatedClassEntities.put( clazz.getName(), clazz );
  265   					tempAnnotatedClasses.add( clazz );
  266   				}
  267   				else if ( clazz.isAnnotationPresent( MappedSuperclass.class ) ) {
  268   					tempAnnotatedClasses.add( clazz );
  269   				}
  270   				//only keep MappedSuperclasses and Entity in this list
  271   			}
  272   			annotatedClasses = tempAnnotatedClasses;
  273   		}
  274   
  275   		//process default values first
  276   		if ( !isDefaultProcessed ) {
  277   			AnnotationBinder.bindDefaults( createExtendedMappings() );
  278   			isDefaultProcessed = true;
  279   		}
  280   
  281   		//process entities
  282   		if ( precedence == null ) precedence = getProperties().getProperty( ARTEFACT );
  283   		if ( precedence == null ) precedence = DEFAULT_PRECEDENCE;
  284   		StringTokenizer precedences = new StringTokenizer( precedence, ",; ", false );
  285   		if ( !precedences.hasMoreElements() ) {
  286   			throw new MappingException( ARTEFACT + " cannot be empty: " + precedence );
  287   		}
  288   		while ( precedences.hasMoreElements() ) {
  289   			String artifact = (String) precedences.nextElement();
  290   			removeConflictedArtifact( artifact );
  291   			processArtifactsOfType( artifact );
  292   		}
  293   
  294   		int cacheNbr = caches.size();
  295   		for (int index = 0; index < cacheNbr; index++) {
  296   			CacheHolder cacheHolder = caches.get( index );
  297   			if ( cacheHolder.isClass ) {
  298   				super.setCacheConcurrencyStrategy(
  299   						cacheHolder.role, cacheHolder.usage, cacheHolder.region, cacheHolder.cacheLazy
  300   				);
  301   			}
  302   			else {
  303   				super.setCollectionCacheConcurrencyStrategy( cacheHolder.role, cacheHolder.usage, cacheHolder.region );
  304   			}
  305   		}
  306   		caches.clear();
  307   		try {
  308   			inSecondPass = true;
  309   			processFkSecondPassInOrder();
  310   			Iterator iter = secondPasses.iterator();
  311   			while ( iter.hasNext() ) {
  312   				SecondPass sp = (SecondPass) iter.next();
  313   				//do the second pass of fk before the others and remove them
  314   				if ( sp instanceof CreateKeySecondPass ) {
  315   					sp.doSecondPass( classes );
  316   					iter.remove();
  317   				}
  318   			}
  319   
  320   			iter = secondPasses.iterator();
  321   			while ( iter.hasNext() ) {
  322   				SecondPass sp = (SecondPass) iter.next();
  323   				//do the SecondaryTable second pass before any association becasue associations can be built on joins
  324   				if ( sp instanceof SecondaryTableSecondPass ) {
  325   					sp.doSecondPass( classes );
  326   					iter.remove();
  327   				}
  328   			}
  329   			super.secondPassCompile();
  330   			inSecondPass = false;
  331   		}
  332   		catch (RecoverableException e) {
  333   			//the exception was not recoverable after all
  334   			throw (RuntimeException) e.getCause();
  335   		}
  336   		Iterator tables = tableUniqueConstraints.entrySet().iterator();
  337   		Table table;
  338   		Map.Entry entry;
  339   		String keyName;
  340   		int uniqueIndexPerTable;
  341   		while ( tables.hasNext() ) {
  342   			entry = (Map.Entry) tables.next();
  343   			table = (Table) entry.getKey();
  344   			List<String[]> uniqueConstraints = (List<String[]>) entry.getValue();
  345   			uniqueIndexPerTable = 0;
  346   			for (String[] columnNames : uniqueConstraints) {
  347   				keyName = "key" + uniqueIndexPerTable++;
  348   				buildUniqueKeyFromColumnNames( columnNames, table, keyName );
  349   			}
  350   		}
  351   		boolean applyOnDdl = getProperties().getProperty(
  352   				"hibernate.validator.apply_to_ddl", //org.hibernate.validator.Environment.APPLY_TO_DDL
  353   				"true" )
  354   				.equalsIgnoreCase( "true" );
  355   
  356   		//TODO search for the method only once and cache it?
  357   		Constructor validatorCtr = null;
  358   		Method applyMethod = null;
  359   		try {
  360   			Class classValidator = ReflectHelper.classForName( "org.hibernate.validator.ClassValidator", this.getClass() );
  361   			Class messageInterpolator = ReflectHelper.classForName( "org.hibernate.validator.MessageInterpolator", this.getClass() );
  362   			validatorCtr = classValidator.getDeclaredConstructor(
  363   					Class.class, ResourceBundle.class, messageInterpolator, Map.class, ReflectionManager.class
  364   			);
  365   			applyMethod = classValidator.getMethod( "apply", PersistentClass.class );
  366   		}
  367   		catch (ClassNotFoundException e) {
  368   			if ( !isValidatorNotPresentLogged ) {
  369   				log.info( "Hibernate Validator not found: ignoring" );
  370   			}
  371   			isValidatorNotPresentLogged = true;
  372   		}
  373   		catch (NoSuchMethodException e) {
  374   			throw new AnnotationException( e );
  375   		}
  376   		if ( applyMethod != null && applyOnDdl ) {
  377   			for (PersistentClass persistentClazz : (Collection<PersistentClass>) classes.values()) {
  378   				//integrate the validate framework
  379   				String className = persistentClazz.getClassName();
  380   				if ( StringHelper.isNotEmpty( className ) ) {
  381   					try {
  382   						Object validator = validatorCtr.newInstance(
  383   								ReflectHelper.classForName( className ), null, null, null, reflectionManager
  384   						);
  385   						applyMethod.invoke( validator, persistentClazz );
  386   					}
  387   					catch (Exception e) {
  388   						log.warn( "Unable to apply constraints on DDL for " + className, e );
  389   					}
  390   				}
  391   			}
  392   		}
  393   	}
  394   
  395   	/**
  396   	 * Processes FKSecondPass instances trying to resolve any
  397   	 * graph circularity (ie PK made of a many to one linking to
  398   	 * an entity having a PK made of a ManyToOne ...).
  399   	 */
  400   	private void processFkSecondPassInOrder() {
  401   		log.debug( "processing fk mappings (*ToOne and JoinedSubclass)" );
  402   		List<FkSecondPass> fkSecondPasses = getFKSecondPassesOnly();
  403   		
  404   		if (fkSecondPasses.size() == 0) {
  405   			return; // nothing to do here
  406   		}
  407   		
  408   		// split FkSecondPass instances into primary key and non primary key FKs.
  409   		// While doing so build a map of class names to FkSecondPass instances depending on this class.
  410   		Map<String, Set<FkSecondPass>> isADependencyOf = new HashMap<String, Set<FkSecondPass>>();
  411   		List endOfQueueFkSecondPasses = new ArrayList( fkSecondPasses.size() );
  412   		for (FkSecondPass sp : fkSecondPasses) {
  413   			if ( sp.isInPrimaryKey() ) {
  414   				String referenceEntityName = sp.getReferencedEntityName();
  415   				PersistentClass classMapping = getClassMapping( referenceEntityName );
  416   				String dependentTable = classMapping.getTable().getQuotedName();
  417   				if ( !isADependencyOf.containsKey( dependentTable ) ) {
  418   					isADependencyOf.put( dependentTable, new HashSet<FkSecondPass>() );
  419   				}
  420   				isADependencyOf.get( dependentTable ).add( sp );
  421   			}
  422   			else {
  423   				endOfQueueFkSecondPasses.add( sp );
  424   			}
  425   		}
  426   		
  427   		// using the isADependencyOf map we order the FkSecondPass recursively instances into the right order for processing
  428   		List<FkSecondPass> orderedFkSecondPasses = new ArrayList( fkSecondPasses.size() );
  429   		for (String tableName : isADependencyOf.keySet()) {
  430   			buildRecursiveOrderedFkSecondPasses(orderedFkSecondPasses, isADependencyOf, tableName, tableName);
  431   		}
  432   		
  433   		// process the ordered FkSecondPasses
  434   		for ( FkSecondPass sp : orderedFkSecondPasses ) {
  435   			sp.doSecondPass( classes );
  436   		}
  437   
  438   		processEndOfQueue(endOfQueueFkSecondPasses);
  439   	}
  440   
  441   	private void processEndOfQueue(List endOfQueueFkSecondPasses) {
  442   		/*
  443   		 * If a second pass raises a recoverableException, queue it for next round
  444   		 * stop of no pass has to be processed or if the number of pass to processes
  445   		 * does not diminish between two rounds.
  446   		 * If some failing pass remain, raise the original exception
  447   		 */
  448   		boolean stopProcess = false;
  449   		RuntimeException originalException = null;
  450   		while ( ! stopProcess ) {
  451   			List failingSecondPasses = new ArrayList();
  452   			Iterator it = endOfQueueFkSecondPasses.listIterator();
  453   			while ( it.hasNext() ) {
  454   				final SecondPass pass = (SecondPass) it.next();
  455   				try {
  456   					pass.doSecondPass( classes );
  457   				}
  458   				catch (RecoverableException e) {
  459   					failingSecondPasses.add( pass );
  460   					if (originalException == null) originalException = (RuntimeException) e.getCause();
  461   				}
  462   			}
  463   			stopProcess = failingSecondPasses.size() == 0 || failingSecondPasses.size() == endOfQueueFkSecondPasses.size();
  464   			endOfQueueFkSecondPasses = failingSecondPasses;
  465   		}
  466   		if (endOfQueueFkSecondPasses.size() > 0) {
  467   			throw originalException;
  468   		}
  469   	}
  470   
  471   	/**
  472   	 * @return Returns a list of all <code>secondPasses</code> instances which are a instance of
  473   	 * <code>FkSecondPass</code>.
  474   	 */
  475   	private List<FkSecondPass> getFKSecondPassesOnly() {
  476   		Iterator iter = secondPasses.iterator();
  477   		List<FkSecondPass> fkSecondPasses = new ArrayList<FkSecondPass>(secondPasses.size());
  478   		while ( iter.hasNext() ) {
  479   			SecondPass sp = (SecondPass) iter.next();
  480   			//do the second pass of fk before the others and remove them
  481   			if ( sp instanceof FkSecondPass ) {
  482   				fkSecondPasses.add( (FkSecondPass) sp );
  483   				iter.remove();
  484   			}
  485   		}
  486   		return fkSecondPasses;
  487   	}
  488   
  489   	/**
  490   	 * Recursively builds a list of FkSecondPass instances ready to be processed in this order.
  491   	 * Checking all dependencies recursively seems quite expensive, but the original code just relied 
  492   	 * on some sort of table name sorting which failed in certain circumstances.
  493   	 * 
  494   	 * @param orderedFkSecondPasses The list containing the <code>FkSecondPass<code> instances ready 
  495   	 * for processing.
  496   	 * @param isADependencyOf Our lookup data structure to determine dependencies between tables
  497   	 * @param startTable Table name to start recursive algorithm.
  498   	 * @param currentTable The current table name used to check for 'new' dependencies.
  499   	 * 
  500   	 * @see ANN-722 ANN-730
  501   	 */
  502   	private void buildRecursiveOrderedFkSecondPasses(
  503   			List orderedFkSecondPasses,
  504   			Map<String, Set<FkSecondPass>> isADependencyOf, String startTable, String currentTable) {
  505   
  506   		Set<FkSecondPass> dependencies = isADependencyOf.get(currentTable);
  507   		
  508   		// bottom out
  509   		if (dependencies == null || dependencies.size() == 0) {
  510   			return;
  511   		}
  512   		
  513   		for (FkSecondPass sp : dependencies) {
  514   			String dependentTable = sp.getValue().getTable().getQuotedName();
  515   			if (dependentTable.compareTo(startTable) == 0) {
  516   				StringBuilder sb = new StringBuilder(
  517   						"Foreign key circularity dependency involving the following tables: ");
  518   				throw new AnnotationException(sb.toString());
  519   			}
  520   			buildRecursiveOrderedFkSecondPasses(orderedFkSecondPasses, isADependencyOf, startTable, dependentTable);
  521   			if (!orderedFkSecondPasses.contains(sp)) {
  522   				orderedFkSecondPasses.add(0, sp);
  523   			}
  524   		}		
  525   	}
  526   
  527   	private void processArtifactsOfType(String artifact) {
  528   		if ( "hbm".equalsIgnoreCase( artifact ) ) {
  529   			log.debug( "Process hbm files" );
  530   			for (Document document : hbmDocuments) {
  531   				super.add( document );
  532   			}
  533   			hbmDocuments.clear();
  534   			hbmEntities.clear();
  535   		}
  536   		else if ( "class".equalsIgnoreCase( artifact ) ) {
  537   			log.debug( "Process annotated classes" );
  538   			//bind classes in the correct order calculating some inheritance state
  539   			List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
  540   			Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
  541   					orderedClasses, reflectionManager
  542   			);
  543   			ExtendedMappings mappings = createExtendedMappings();
  544   			for (XClass clazz : orderedClasses) {
  545   				//todo use the same extended mapping
  546   				AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );
  547   			}
  548   			annotatedClasses.clear();
  549   			annotatedClassEntities.clear();
  550   		}
  551   		else {
  552   			log.warn( "Unknown artifact: {}",  artifact );
  553   		}
  554   	}
  555   
  556   	private void removeConflictedArtifact(String artifact) {
  557   		if ( "hbm".equalsIgnoreCase( artifact ) ) {
  558   			for (String entity : hbmEntities.keySet()) {
  559   				if ( annotatedClassEntities.containsKey( entity ) ) {
  560   					annotatedClasses.remove( annotatedClassEntities.get( entity ) );
  561   					annotatedClassEntities.remove( entity );
  562   				}
  563   			}
  564   		}
  565   		else if ( "class".equalsIgnoreCase( artifact ) ) {
  566   			for (String entity : annotatedClassEntities.keySet()) {
  567   				if ( hbmEntities.containsKey( entity ) ) {
  568   					hbmDocuments.remove( hbmEntities.get( entity ) );
  569   					hbmEntities.remove( entity );
  570   				}
  571   			}
  572   		}
  573   	}
  574   
  575   	private void buildUniqueKeyFromColumnNames(String[] columnNames, Table table, String keyName) {
  576   		UniqueKey uc;
  577   		int size = columnNames.length;
  578   		Column[] columns = new Column[size];
  579   		Set<Column> unbound = new HashSet<Column>();
  580   		Set<Column> unboundNoLogical = new HashSet<Column>();
  581   		ExtendedMappings mappings = createExtendedMappings();
  582   		for (int index = 0; index < size; index++) {
  583   			String columnName;
  584   			try {
  585   				columnName = mappings.getPhysicalColumnName( columnNames[index], table );
  586   				columns[index] = new Column( columnName );
  587   				unbound.add( columns[index] );
  588   				//column equals and hashcode is based on column name
  589   			}
  590   			catch (MappingException e) {
  591   				unboundNoLogical.add( new Column( columnNames[index] ) );
  592   			}
  593   		}
  594   		for (Column column : columns) {
  595   			if ( table.containsColumn( column ) ) {
  596   				uc = table.getOrCreateUniqueKey( keyName );
  597   				uc.addColumn( table.getColumn( column ) );
  598   				unbound.remove( column );
  599   			}
  600   		}
  601   		if ( unbound.size() > 0 || unboundNoLogical.size() > 0 ) {
  602   			StringBuilder sb = new StringBuilder( "Unable to create unique key constraint (" );
  603   			for (String columnName : columnNames) {
  604   				sb.append( columnName ).append( ", " );
  605   			}
  606   			sb.setLength( sb.length() - 2 );
  607   			sb.append( ") on table " ).append( table.getName() ).append( ": " );
  608   			for (Column column : unbound) {
  609   				sb.append( column.getName() ).append( ", " );
  610   			}
  611   			for (Column column : unboundNoLogical) {
  612   				sb.append( column.getName() ).append( ", " );
  613   			}
  614   			sb.setLength( sb.length() - 2 );
  615   			sb.append( " not found" );
  616   			throw new AnnotationException( sb.toString() );
  617   		}
  618   	}
  619   
  620   	@Override
  621   	protected void parseMappingElement(Element subelement, String name) {
  622   		Attribute rsrc = subelement.attribute( "resource" );
  623   		Attribute file = subelement.attribute( "file" );
  624   		Attribute jar = subelement.attribute( "jar" );
  625   		Attribute pckg = subelement.attribute( "package" );
  626   		Attribute clazz = subelement.attribute( "class" );
  627   		if ( rsrc != null ) {
  628   			log.debug( "{} <- {}", name, rsrc );
  629   			addResource( rsrc.getValue() );
  630   		}
  631   		else if ( jar != null ) {
  632   			log.debug( "{} <- {}", name, jar );
  633   			addJar( new File( jar.getValue() ) );
  634   		}
  635   		else if ( file != null ) {
  636   			log.debug(  "{} <- {}", name, file );
  637   			addFile( file.getValue() );
  638   		}
  639   		else if ( pckg != null ) {
  640   			log.debug(  "{} <- {}", name, pckg );
  641   			addPackage( pckg.getValue() );
  642   		}
  643   		else if ( clazz != null ) {
  644   			log.debug(  "{} <- {}", name, clazz );
  645   			Class loadedClass;
  646   			try {
  647   				loadedClass = ReflectHelper.classForName( clazz.getValue() );
  648   			}
  649   			catch (ClassNotFoundException cnf) {
  650   				throw new MappingException(
  651   						"Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
  652   						cnf
  653   				);
  654   			}
  655   			catch (NoClassDefFoundError ncdf) {
  656   				throw new MappingException(
  657   						"Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
  658   						ncdf
  659   				);
  660   			}
  661   
  662   			addAnnotatedClass( loadedClass );
  663   		}
  664   		else {
  665   			throw new MappingException( "<mapping> element in configuration specifies no attributes" );
  666   		}
  667   	}
  668   
  669   	@Override
  670   	protected void add(org.dom4j.Document doc) throws MappingException {
  671   		boolean ejb3Xml = "entity-mappings".equals( doc.getRootElement().getName() );
  672   		if ( inSecondPass ) {
  673   			//if in second pass bypass the queueing, getExtendedQueue reuse this method
  674   			if ( !ejb3Xml ) super.add( doc );
  675   		}
  676   		else {
  677   			if ( !ejb3Xml ) {
  678   				final Element hmNode = doc.getRootElement();
  679   				Attribute packNode = hmNode.attribute( "package" );
  680   				String defaultPackage = packNode != null
  681   						? packNode.getValue()
  682   						: "";
  683   				Set<String> entityNames = new HashSet<String>();
  684   				findClassNames( defaultPackage, hmNode, entityNames );
  685   				for (String entity : entityNames) {
  686   					hbmEntities.put( entity, doc );
  687   				}
  688   				hbmDocuments.add( doc );
  689   			}
  690   			else {
  691   				List<String> classnames = ( (EJB3ReflectionManager) reflectionManager ).getXMLContext().addDocument( doc );
  692   				for (String classname : classnames) {
  693   					try {
  694   						annotatedClasses.add( reflectionManager.classForName( classname, this.getClass() ) );
  695   					}
  696   					catch (ClassNotFoundException e) {
  697   						throw new AnnotationException( "Unable to load class defined in XML: " + classname, e );
  698   					}
  699   				}
  700   			}
  701   		}
  702   	}
  703   
  704   	private static void findClassNames(
  705   			String defaultPackage, final Element startNode,
  706   			final java.util.Set names
  707   	) {
  708   		// if we have some extends we need to check if those classes possibly could be inside the
  709   		// same hbm.xml file...
  710   		Iterator[] classes = new Iterator[4];
  711   		classes[0] = startNode.elementIterator( "class" );
  712   		classes[1] = startNode.elementIterator( "subclass" );
  713   		classes[2] = startNode.elementIterator( "joined-subclass" );
  714   		classes[3] = startNode.elementIterator( "union-subclass" );
  715   
  716   		Iterator classIterator = new JoinedIterator( classes );
  717   		while ( classIterator.hasNext() ) {
  718   			Element element = (Element) classIterator.next();
  719   			String entityName = element.attributeValue( "entity-name" );
  720   			if ( entityName == null ) entityName = getClassName( element.attribute( "name" ), defaultPackage );
  721   			names.add( entityName );
  722   			findClassNames( defaultPackage, element, names );
  723   		}
  724   	}
  725   
  726   	private static String getClassName(Attribute name, String defaultPackage) {
  727   		if ( name == null ) return null;
  728   		String unqualifiedName = name.getValue();
  729   		if ( unqualifiedName == null ) return null;
  730   		if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
  731   			return defaultPackage + '.' + unqualifiedName;
  732   		}
  733   		return unqualifiedName;
  734   	}
  735   
  736   	public void setPrecedence(String precedence) {
  737   		this.precedence = precedence;
  738   	}
  739   
  740   	private static class CacheHolder {
  741   		public CacheHolder(String role, String usage, String region, boolean isClass, boolean cacheLazy) {
  742   			this.role = role;
  743   			this.usage = usage;
  744   			this.region = region;
  745   			this.isClass = isClass;
  746   			this.cacheLazy = cacheLazy;
  747   		}
  748   
  749   		public String role;
  750   		public String usage;
  751   		public String region;
  752   		public boolean isClass;
  753   		public boolean cacheLazy;
  754   	}
  755   
  756   	@Override
  757   	public AnnotationConfiguration addInputStream(InputStream xmlInputStream) throws MappingException {
  758   		try {
  759   			List errors = new ArrayList();
  760   			SAXReader saxReader = xmlHelper.createSAXReader( "XML InputStream", errors, getEntityResolver() );
  761   			try {
  762   				saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
  763   				//saxReader.setFeature( "http://apache.org/xml/features/validation/dynamic", true );
  764   				//set the default schema locators
  765   				saxReader.setProperty(
  766   						"http://apache.org/xml/properties/schema/external-schemaLocation",
  767   						"http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
  768   				);
  769   			}
  770   			catch (SAXException e) {
  771   				saxReader.setValidation( false );
  772   			}
  773   			org.dom4j.Document doc = saxReader
  774   					.read( new InputSource( xmlInputStream ) );
  775   
  776   			if ( errors.size() != 0 ) {
  777   				throw new MappingException( "invalid mapping", (Throwable) errors.get( 0 ) );
  778   			}
  779   			add( doc );
  780   			return this;
  781   		}
  782   		catch (DocumentException e) {
  783   			throw new MappingException( "Could not parse mapping document in input stream", e );
  784   		}
  785   		finally {
  786   			try {
  787   				xmlInputStream.close();
  788   			}
  789   			catch (IOException ioe) {
  790   				log.warn( "Could not close input stream", ioe );
  791   			}
  792   		}
  793   	}
  794   
  795   	public SessionFactory buildSessionFactory() throws HibernateException {
  796   		//add validator events if the jar is available
  797   		boolean enableValidatorListeners = !"false".equalsIgnoreCase( getProperty( "hibernate.validator.autoregister_listeners" ) );
  798   		Class validateEventListenerClass = null;
  799   		try {
  800   			validateEventListenerClass = ReflectHelper.classForName(
  801   					"org.hibernate.validator.event.ValidateEventListener",
  802   					AnnotationConfiguration.class );
  803   		}
  804   		catch (ClassNotFoundException e) {
  805   			//validator is not present
  806   			log.debug( "Validator not present in classpath, ignoring event listener registration" );
  807   		}
  808   		if ( enableValidatorListeners && validateEventListenerClass != null ) {
  809   			//TODO so much duplication
  810   			Object validateEventListener;
  811   			try {
  812   				validateEventListener = validateEventListenerClass.newInstance();
  813   			}
  814   			catch (Exception e) {
  815   				throw new AnnotationException( "Unable to load Validator event listener", e );
  816   			}
  817   			{
  818   				boolean present = false;
  819   				PreInsertEventListener[] listeners = getEventListeners().getPreInsertEventListeners();
  820   				if ( listeners != null ) {
  821   					for (Object eventListener : listeners) {
  822   						//not isAssignableFrom since the user could subclass
  823   						present = present || validateEventListenerClass == eventListener.getClass();
  824   					}
  825   					if ( !present ) {
  826   						int length = listeners.length + 1;
  827   						PreInsertEventListener[] newListeners = new PreInsertEventListener[length];
  828   						System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
  829   						newListeners[length - 1] = (PreInsertEventListener) validateEventListener;
  830   						getEventListeners().setPreInsertEventListeners( newListeners );
  831   					}
  832   				}
  833   				else {
  834   					getEventListeners().setPreInsertEventListeners(
  835   							new PreInsertEventListener[] { (PreInsertEventListener) validateEventListener }
  836   					);
  837   				}
  838   			}
  839   
  840   			//update event listener
  841   			{
  842   				boolean present = false;
  843   				PreUpdateEventListener[] listeners = getEventListeners().getPreUpdateEventListeners();
  844   				if ( listeners != null ) {
  845   					for (Object eventListener : listeners) {
  846   						//not isAssignableFrom since the user could subclass
  847   						present = present || validateEventListenerClass == eventListener.getClass();
  848   					}
  849   					if ( !present ) {
  850   						int length = listeners.length + 1;
  851   						PreUpdateEventListener[] newListeners = new PreUpdateEventListener[length];
  852   						System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
  853   						newListeners[length - 1] = (PreUpdateEventListener) validateEventListener;
  854   						getEventListeners().setPreUpdateEventListeners( newListeners );
  855   					}
  856   				}
  857   				else {
  858   					getEventListeners().setPreUpdateEventListeners(
  859   							new PreUpdateEventListener[] { (PreUpdateEventListener) validateEventListener }
  860   					);
  861   				}
  862   			}
  863   		}
  864   		
  865   		enableHibernateSearch(); 
  866   		
  867   		return super.buildSessionFactory();
  868   	}
  869   
  870   	/**
  871   	 * Tries to automatically register Hibernate Search event listeners by locating the 
  872   	 * appropriate bootstrap class and calling the <code>enableHibernateSearch</code> method.
  873   	 */
  874   	private void enableHibernateSearch() {
  875   		// load the bootstrap class
  876   		Class searchStartupClass;
  877   		try {
  878   			searchStartupClass = ReflectHelper.classForName(SEARCH_STARTUP_CLASS, AnnotationConfiguration.class);	
  879   		} catch ( ClassNotFoundException e ) {
  880   			// TODO remove this together with SearchConfiguration after 3.1.0 release of Search
  881   			// try loading deprecated HibernateSearchEventListenerRegister
  882   			try {
  883   				searchStartupClass = ReflectHelper.classForName("org.hibernate.cfg.search.HibernateSearchEventListenerRegister", AnnotationConfiguration.class);
  884   			} catch ( ClassNotFoundException cnfe ) {
  885   				log.debug("Search not present in classpath, ignoring event listener registration.");
  886   				return;
  887   			}
  888   		}
  889   		
  890   		// call the method for registering the listeners
  891   		try {
  892   			Object searchStartupInstance = searchStartupClass.newInstance();
  893   			Method enableSearchMethod = searchStartupClass.getDeclaredMethod(SEARCH_STARTUP_METHOD,
  894   					EventListeners.class, Properties.class);
  895   			enableSearchMethod.invoke(searchStartupInstance, getEventListeners(), getProperties());
  896   		} catch ( InstantiationException e ) {
  897   			log.debug("Unable to instantiate {}, ignoring event listener registration.", SEARCH_STARTUP_CLASS);
  898   		} catch ( IllegalAccessException e ) {
  899   			log.debug("Unable to instantiate {}, ignoring event listener registration.", SEARCH_STARTUP_CLASS);
  900   		} catch ( NoSuchMethodException e ) {
  901   			log.debug("Method enableHibernateSearch() not found in {}.", SEARCH_STARTUP_CLASS);
  902   		} catch ( InvocationTargetException e ) {
  903   			log.debug("Unable to execute {}, ignoring event listener registration.", SEARCH_STARTUP_METHOD);
  904   		}
  905   	}
  906   
  907   	@Override
  908   	public AnnotationConfiguration addFile(String xmlFile) throws MappingException {
  909   		super.addFile( xmlFile );
  910   		return this;
  911   	}
  912   
  913   	@Override
  914   	public AnnotationConfiguration addFile(File xmlFile) throws MappingException {
  915   		super.addFile( xmlFile );
  916   		return this;
  917   	}
  918   
  919   	@Override
  920   	public AnnotationConfiguration addCacheableFile(File xmlFile) throws MappingException {
  921   		super.addCacheableFile( xmlFile );
  922   		return this;
  923   	}
  924   
  925   	@Override
  926   	public AnnotationConfiguration addCacheableFile(String xmlFile) throws MappingException {
  927   		super.addCacheableFile( xmlFile );
  928   		return this;
  929   	}
  930   
  931   	@Override
  932   	public AnnotationConfiguration addXML(String xml) throws MappingException {
  933   		super.addXML( xml );
  934   		return this;
  935   	}
  936   
  937   	@Override
  938   	public AnnotationConfiguration addURL(URL url) throws MappingException {
  939   		super.addURL( url );
  940   		return this;
  941   	}
  942   
  943   	@Override
  944   	public AnnotationConfiguration addResource(String resourceName, ClassLoader classLoader) throws MappingException {
  945   		super.addResource( resourceName, classLoader );
  946   		return this;
  947   	}
  948   
  949   	@Override
  950   	public AnnotationConfiguration addDocument(org.w3c.dom.Document doc) throws MappingException {
  951   		super.addDocument( doc );
  952   		return this;
  953   	}
  954   
  955   	@Override
  956   	public AnnotationConfiguration addResource(String resourceName) throws MappingException {
  957   		super.addResource( resourceName );
  958   		return this;
  959   	}
  960   
  961   	@Override
  962   	public AnnotationConfiguration addClass(Class persistentClass) throws MappingException {
  963   		super.addClass( persistentClass );
  964   		return this;
  965   	}
  966   
  967   	@Override
  968   	public AnnotationConfiguration addJar(File jar) throws MappingException {
  969   		super.addJar( jar );
  970   		return this;
  971   	}
  972   
  973   	@Override
  974   	public AnnotationConfiguration addDirectory(File dir) throws MappingException {
  975   		super.addDirectory( dir );
  976   		return this;
  977   	}
  978   
  979   	@Override
  980   	public AnnotationConfiguration setInterceptor(Interceptor interceptor) {
  981   		super.setInterceptor( interceptor );
  982   		return this;
  983   	}
  984   
  985   	@Override
  986   	public AnnotationConfiguration setProperties(Properties properties) {
  987   		super.setProperties( properties );
  988   		return this;
  989   	}
  990   
  991   	@Override
  992   	public AnnotationConfiguration addProperties(Properties extraProperties) {
  993   		super.addProperties( extraProperties );
  994   		return this;
  995   	}
  996   
  997   	@Override
  998   	public AnnotationConfiguration mergeProperties(Properties properties) {
  999   		super.mergeProperties( properties );
 1000   		return this;
 1001   	}
 1002   
 1003   	@Override
 1004   	public AnnotationConfiguration setProperty(String propertyName, String value) {
 1005   		super.setProperty( propertyName, value );
 1006   		return this;
 1007   	}
 1008   
 1009   	@Override
 1010   	public AnnotationConfiguration configure() throws HibernateException {
 1011   		super.configure();
 1012   		return this;
 1013   	}
 1014   
 1015   	@Override
 1016   	public AnnotationConfiguration configure(String resource) throws HibernateException {
 1017   		super.configure( resource );
 1018   		return this;
 1019   	}
 1020   
 1021   	@Override
 1022   	public AnnotationConfiguration configure(URL url) throws HibernateException {
 1023   		super.configure( url );
 1024   		return this;
 1025   	}
 1026   
 1027   	@Override
 1028   	public AnnotationConfiguration configure(File configFile) throws HibernateException {
 1029   		super.configure( configFile );
 1030   		return this;
 1031   	}
 1032   
 1033   	@Override
 1034   	protected AnnotationConfiguration doConfigure(InputStream stream, String resourceName) throws HibernateException {
 1035   		super.doConfigure( stream, resourceName );
 1036   		return this;
 1037   	}
 1038   
 1039   	@Override
 1040   	public AnnotationConfiguration configure(org.w3c.dom.Document document) throws HibernateException {
 1041   		super.configure( document );
 1042   		return this;
 1043   	}
 1044   
 1045   	@Override
 1046   	protected AnnotationConfiguration doConfigure(Document doc) throws HibernateException {
 1047   		super.doConfigure( doc );
 1048   		return this;
 1049   	}
 1050   
 1051   	@Override
 1052   	public AnnotationConfiguration setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy) throws MappingException {
 1053   		super.setCacheConcurrencyStrategy( clazz, concurrencyStrategy );
 1054   		return this;
 1055   	}
 1056   
 1057   	@Override
 1058   	public AnnotationConfiguration setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy) throws MappingException {
 1059   		super.setCollectionCacheConcurrencyStrategy( collectionRole, concurrencyStrategy );
 1060   		return this;
 1061   	}
 1062   
 1063   	@Override
 1064   	public AnnotationConfiguration setNamingStrategy(NamingStrategy namingStrategy) {
 1065   		super.setNamingStrategy( namingStrategy );
 1066   		return this;
 1067   	}
 1068   
 1069   	//not a public API
 1070   	public ReflectionManager getReflectionManager() {
 1071   		return reflectionManager;
 1072   	}
 1073   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » cfg » [javadoc | source]