HHH-5026 Ability to customize Scanner strategies

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@19060 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2010-03-22 14:40:05 +00:00
parent e94b5fc95f
commit 00b0459ce5
9 changed files with 204 additions and 29 deletions

View File

@ -67,7 +67,7 @@
<para>If you use Maven, add the following dependencies</para> <para>If you use Maven, add the following dependencies</para>
<programlisting role="XML" language="XML">&lt;project ...&gt; <programlisting language="XML" role="XML">&lt;project ...&gt;
... ...
&lt;dependencies&gt; &lt;dependencies&gt;
&lt;dependency&gt; &lt;dependency&gt;
@ -100,7 +100,7 @@
configuration, so by default, your persistence.xml will be quite configuration, so by default, your persistence.xml will be quite
minimalist:</para> minimalist:</para>
<programlisting role="XML" language="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence" <programlisting language="XML" role="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"&gt; version="2.0"&gt;
@ -116,7 +116,7 @@
<para>Here's a more complete example of a <para>Here's a more complete example of a
<filename><literal>persistence.xml</literal></filename> file</para> <filename><literal>persistence.xml</literal></filename> file</para>
<programlisting role="XML" language="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence" <programlisting language="XML" role="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"&gt; version="2.0"&gt;
@ -211,7 +211,7 @@
environment, the persistence.xml file is not under the same root environment, the persistence.xml file is not under the same root
directory or jar than your domain model).</para> directory or jar than your domain model).</para>
<programlisting role="XML" language="XML"> &lt;jar-file&gt;file:/home/turin/work/local/lab8/build/classes&lt;/jar-file&gt;</programlisting> <programlisting language="XML" role="XML"> &lt;jar-file&gt;file:/home/turin/work/local/lab8/build/classes&lt;/jar-file&gt;</programlisting>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -323,14 +323,14 @@
<literal>&lt;validation-mode&gt;</literal>. To use it, add a <literal>&lt;validation-mode&gt;</literal>. To use it, add a
regular property</para> regular property</para>
<programlisting role="XML" language="XML">&lt;property name="javax.persistence.validation.mode"&gt; <programlisting language="XML" role="XML">&lt;property name="javax.persistence.validation.mode"&gt;
ddl ddl
&lt;/property&gt;</programlisting> &lt;/property&gt;</programlisting>
<para>With this approach, you can mix ddl and callback <para>With this approach, you can mix ddl and callback
modes:</para> modes:</para>
<programlisting role="XML" language="XML">&lt;property name="javax.persistence.validation.mode"&gt; <programlisting language="XML" role="XML">&lt;property name="javax.persistence.validation.mode"&gt;
ddl, callback ddl, callback
&lt;/property&gt;</programlisting> &lt;/property&gt;</programlisting>
</listitem> </listitem>
@ -376,12 +376,35 @@
</listitem> </listitem>
<listitem> <listitem>
<para>javax.persistence.validation.group.pre-persist defines <para><literal>javax.persistence.validation.group.pre-persist</literal>
the group or list of groups to validate before persisting an defines the group or list of groups to validate before
entity. This is a comma separated fully qualified class name persisting an entity. This is a comma separated fully
string (eg <code>com.acme.groups.Common</code> or qualified class name string (eg
<code>com.acme.groups.Common</code> or
<code>com.acme.groups.Common, <code>com.acme.groups.Common,
javax.validation.groups.Default</code>)</para> javax.validation.groups.Default</code>). Defaults to the Bean
Validation default group.</para>
</listitem>
<listitem>
<para><literal>javax.persistence.validation.group.pre-update</literal>
defines the group or list of groups to validate before
updating an entity. This is a comma separated fully qualified
class name string (eg <code>com.acme.groups.Common</code> or
<code>com.acme.groups.Common,
javax.validation.groups.Default</code>). Defaults to the Bean
Validation default group.</para>
</listitem>
<listitem>
<para><literal>javax.persistence.validation.group.pre-remove</literal>
defines the group or list of groups to validate before
persisting an entity. This is a comma separated fully
qualified class name string (eg
<code>com.acme.groups.Common</code> or
<code>com.acme.groups.Common,
javax.validation.groups.Default</code>). Defaults to no
group.</para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
@ -427,7 +450,7 @@
the version embedded in the hibernate-entitymanager.jar. It won't fetch the version embedded in the hibernate-entitymanager.jar. It won't fetch
the resource from the internet.</para> the resource from the internet.</para>
<programlisting role="XML" language="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence" <programlisting language="XML" role="XML">&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"&gt;</programlisting> version="2.0"&gt;</programlisting>
@ -441,7 +464,7 @@
<classname>EntityManager</classname>. The bootstrap class is <classname>EntityManager</classname>. The bootstrap class is
<classname>javax.persistence.Persistence</classname>, e.g.</para> <classname>javax.persistence.Persistence</classname>, e.g.</para>
<programlisting role="JAVA" language="JAVA">EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1"); <programlisting language="JAVA" role="JAVA">EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1");
//or //or
@ -636,10 +659,26 @@ EntityManagerFactory programmaticEmf =
<entry>If true, the persistence context will be discarded (think <entry>If true, the persistence context will be discarded (think
clear() when the method is called. Otherwise the persistence clear() when the method is called. Otherwise the persistence
context will stay alive till the transaction completion: all context will stay alive till the transaction completion: all
objects will remain managed, and any change will be sy,chronized objects will remain managed, and any change will be synchronized
with the database (default to false, ie wait the transaction with the database (default to false, ie wait the transaction
completion)</entry> completion)</entry>
</row> </row>
<row>
<entry>hibernate.ejb.resource_scanner</entry>
<entry><para>By default, Hibernate EntityManager scans itself
the list of resources for annotated classes and persistence
deployment descriptors (like orm.xml and hbm.xml
files).</para><para>You can customize this scanning strategy by
implementing
<classname>org.hibernate.ejb.packaging.Scanner</classname>. This
property is used by container implementors to improve
integration with Hibernate.</para><para>Accepts an instance of
<classname>Scanner</classname> or the file name of a no-arg
constructor class implementing
<classname>Scanner</classname>.</para></entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
@ -663,7 +702,7 @@ EntityManagerFactory programmaticEmf =
<para>Here is a typical configuration in a Java SE environment</para> <para>Here is a typical configuration in a Java SE environment</para>
<programlisting role="XML" language="XML">&lt;persistence&gt; <programlisting language="XML" role="XML">&lt;persistence&gt;
&lt;persistence-unit name="manager1" transaction-type="RESOURCE_LOCAL"&gt; &lt;persistence-unit name="manager1" transaction-type="RESOURCE_LOCAL"&gt;
&lt;class&gt;org.hibernate.ejb.test.Cat&lt;/class&gt; &lt;class&gt;org.hibernate.ejb.test.Cat&lt;/class&gt;
&lt;class&gt;org.hibernate.ejb.test.Distributor&lt;/class&gt; &lt;class&gt;org.hibernate.ejb.test.Distributor&lt;/class&gt;
@ -695,7 +734,7 @@ EntityManagerFactory programmaticEmf =
<para>TODO: me more descriptive on some APIs like setDatasource()</para> <para>TODO: me more descriptive on some APIs like setDatasource()</para>
<programlisting role="JAVA" language="JAVA">Ejb3Configuration cfg = new Ejb3Configuration(); <programlisting language="JAVA" role="JAVA">Ejb3Configuration cfg = new Ejb3Configuration();
EntityManagerFactory emf = EntityManagerFactory emf =
cfg.addProperties( properties ) //add some properties cfg.addProperties( properties ) //add some properties
.setInterceptor( myInterceptorImpl ) // set an interceptor .setInterceptor( myInterceptorImpl ) // set an interceptor
@ -857,7 +896,7 @@ EntityManagerFactory emf =
<classname>Persistence</classname> class is bootstrap class to create an <classname>Persistence</classname> class is bootstrap class to create an
entity manager factory.</para> entity manager factory.</para>
<programlisting role="JAVA" language="JAVA">// Use persistence.xml configuration <programlisting language="JAVA" role="JAVA">// Use persistence.xml configuration
EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1") EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1")
EntityManager em = emf.createEntityManager(); // Retrieve an application managed entity manager EntityManager em = emf.createEntityManager(); // Retrieve an application managed entity manager
// Work with the EM // Work with the EM
@ -892,4 +931,4 @@ emf.close(); //close at application end</programlisting>
be no performance cost. For more information on Hibernate Validator, be no performance cost. For more information on Hibernate Validator,
please refer to the Hibernate Annotations reference guide.</para> please refer to the Hibernate Annotations reference guide.</para>
</section> </section>
</chapter> </chapter>

View File

@ -270,10 +270,17 @@ public class AvailableSettings {
public static final String CONFIGURATION_JNDI_NAME = "hibernate.ejb.configuration_jndi_name"; public static final String CONFIGURATION_JNDI_NAME = "hibernate.ejb.configuration_jndi_name";
/** /**
* Used to detrmine flush mode. * Used to determine flush mode.
*/ */
public static final String FLUSH_MODE = "org.hibernate.flushMode"; public static final String FLUSH_MODE = "org.hibernate.flushMode";
/**
* Pass an implementation of {@link org.hibernate.ejb.packaging.Scanner}:
* - preferably an actual instance
* - or a class name with a no-arg constructor
*/
public static final String SCANNER = "hibernate.ejb.resource_scanner";
/** /**
* List of classes names * List of classes names
* Internal use only * Internal use only

View File

@ -324,7 +324,7 @@ public class Ejb3Configuration implements Serializable, Referenceable {
Scanner scanner = null; Scanner scanner = null;
URL jarURL = null; URL jarURL = null;
if ( metadata.getName() == null ) { if ( metadata.getName() == null ) {
scanner = buildScanner(); scanner = buildScanner( metadata.getProps() );
jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" ); jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
metadata.setName( scanner.getUnqualifiedJarName(jarURL) ); metadata.setName( scanner.getUnqualifiedJarName(jarURL) );
} }
@ -333,7 +333,7 @@ public class Ejb3Configuration implements Serializable, Referenceable {
} }
else if ( persistenceUnitName == null || metadata.getName().equals( persistenceUnitName ) ) { else if ( persistenceUnitName == null || metadata.getName().equals( persistenceUnitName ) ) {
if (scanner == null) { if (scanner == null) {
scanner = buildScanner(); scanner = buildScanner( metadata.getProps() );
jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" ); jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
} }
//scan main JAR //scan main JAR
@ -373,8 +373,40 @@ public class Ejb3Configuration implements Serializable, Referenceable {
} }
} }
private NativeScanner buildScanner() { private Scanner buildScanner(Properties properties) {
return new NativeScanner(); final Object scanner = properties.getProperty( AvailableSettings.SCANNER );
if (scanner != null) {
Class<?> scannerClass;
if ( scanner instanceof String ) {
try {
scannerClass = ReflectHelper.classForName( (String) scanner, this.getClass() );
}
catch ( ClassNotFoundException e ) {
throw new PersistenceException( "Cannot find scanner class. " + AvailableSettings.SCANNER + "=" + scanner, e );
}
}
else if (scanner instanceof Class) {
scannerClass = (Class<? extends Scanner>) scanner;
}
else if (scanner instanceof Scanner) {
return (Scanner) scanner;
}
else {
throw new PersistenceException( "Scanner class configuration error: unknown type on the property. " + AvailableSettings.SCANNER );
}
try {
return (Scanner) scannerClass.newInstance();
}
catch ( InstantiationException e ) {
throw new PersistenceException( "Unable to load Scanner class: " + scannerClass, e );
}
catch ( IllegalAccessException e ) {
throw new PersistenceException( "Unable to load Scanner class: " + scannerClass, e );
}
}
else {
return new NativeScanner();
}
} }
private static class ScanningContext { private static class ScanningContext {
@ -531,7 +563,9 @@ public class Ejb3Configuration implements Serializable, Referenceable {
boolean searchForORMFiles = ! xmlFiles.contains( META_INF_ORM_XML ); boolean searchForORMFiles = ! xmlFiles.contains( META_INF_ORM_XML );
ScanningContext context = new ScanningContext(); ScanningContext context = new ScanningContext();
context.scanner( buildScanner() ) final Properties copyOfProperties = (Properties) info.getProperties().clone();
ConfigurationHelper.overrideProperties( copyOfProperties, integration );
context.scanner( buildScanner( copyOfProperties ) )
.searchOrm( searchForORMFiles ) .searchOrm( searchForORMFiles )
.explicitMappingFiles( null ); //URLs provided by the container already .explicitMappingFiles( null ); //URLs provided by the container already

View File

@ -225,7 +225,7 @@ public class NativeScanner implements Scanner {
return files; return files;
} }
public Set<NamedInputStream> getFilesInClasspath(URL jartoScan, Set<String> filePatterns) { public Set<NamedInputStream> getFilesInClasspath(Set<String> filePatterns) {
throw new AssertionFailure( "Not implemented" ); throw new AssertionFailure( "Not implemented" );
} }

View File

@ -50,11 +50,13 @@ public interface Scanner {
/** /**
* return all files in the classpath (ie PU visibility) matching one of these file names * Return all files in the classpath (ie PU visibility) matching one of these file names
* if filePatterns is empty, return all files * if filePatterns is empty, return all files
* the use case is really exact file name. * the use case is really exact file name.
*
* NOT USED by HEM at the moment. We use exact file search via getResourceAsStream for now.
*/ */
Set<NamedInputStream> getFilesInClasspath(URL jartoScan, Set<String> filePatterns); Set<NamedInputStream> getFilesInClasspath(Set<String> filePatterns);
/** /**
* return the unqualified JAR name ie customer-model.jar or store.war * return the unqualified JAR name ie customer-model.jar or store.war

View File

@ -11,6 +11,9 @@
<class>org.hibernate.ejb.test.Item</class> <class>org.hibernate.ejb.test.Item</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes> <exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> <properties>
<!-- custom scanner test -->
<property name="hibernate.ejb.resource_scanner" value="org.hibernate.ejb.test.packaging.CustomScanner"/>
<property name="hibernate.dialect" value="${db.dialect}"/> <property name="hibernate.dialect" value="${db.dialect}"/>
<property name="hibernate.connection.driver_class" value="${jdbc.driver}"/> <property name="hibernate.connection.driver_class" value="${jdbc.driver}"/>
<property name="hibernate.connection.username" value="${jdbc.user}"/> <property name="hibernate.connection.username" value="${jdbc.user}"/>

View File

@ -0,0 +1,50 @@
package org.hibernate.ejb.test.packaging;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Set;
import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner;
import org.hibernate.ejb.packaging.Scanner;
/**
* @author Emmanuel Bernard
*/
public class CustomScanner implements Scanner {
public static boolean isUsed = false;
private Scanner scanner = new NativeScanner();
public static boolean isUsed() {
return isUsed;
}
public static void resetUsed() {
isUsed = false;
}
public Set<Package> getPackagesInJar(URL jartoScan, Set<Class<? extends Annotation>> annotationsToLookFor) {
isUsed = true;
return scanner.getPackagesInJar( jartoScan, annotationsToLookFor );
}
public Set<Class<?>> getClassesInJar(URL jartoScan, Set<Class<? extends Annotation>> annotationsToLookFor) {
isUsed = true;
return scanner.getClassesInJar( jartoScan, annotationsToLookFor );
}
public Set<NamedInputStream> getFilesInJar(URL jartoScan, Set<String> filePatterns) {
isUsed = true;
return scanner.getFilesInJar( jartoScan, filePatterns );
}
public Set<NamedInputStream> getFilesInClasspath(Set<String> filePatterns) {
isUsed = true;
return scanner.getFilesInClasspath( filePatterns );
}
public String getUnqualifiedJarName(URL jarUrl) {
isUsed = true;
return scanner.getUnqualifiedJarName( jarUrl );
}
}

View File

@ -0,0 +1,20 @@
package org.hibernate.ejb.test.packaging;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Emmanuel Bernard
*/
@Entity
public class Pasta {
@Id @GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id;}
private Integer id;
public String getType() { return type; }
public void setType(String type) { this.type = type;}
private String type;
}

View File

@ -1,4 +1,4 @@
// $Id:$ // $Id$
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
@ -26,11 +26,14 @@ package org.hibernate.ejb.test.packaging;
import java.io.File; import java.io.File;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.MappedSuperclass; import javax.persistence.MappedSuperclass;
import javax.persistence.Persistence;
import org.hibernate.ejb.packaging.NamedInputStream; import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner; import org.hibernate.ejb.packaging.NativeScanner;
@ -42,7 +45,7 @@ import org.hibernate.ejb.test.pack.defaultpar.ApplicationServer;
* @author Emmanuel Bernard * @author Emmanuel Bernard
* @author Hardy Ferentschik * @author Hardy Ferentschik
*/ */
public class NativeScannerTest extends PackagingTestCase { public class ScannerTest extends PackagingTestCase {
public void testNativeScanner() throws Exception { public void testNativeScanner() throws Exception {
File defaultPar = buildDefaultPar(); File defaultPar = buildDefaultPar();
addPackageToClasspath( defaultPar ); addPackageToClasspath( defaultPar );
@ -71,4 +74,21 @@ public class NativeScannerTest extends PackagingTestCase {
file.getStream().close(); file.getStream().close();
} }
} }
public void testCustomScanner() throws Exception {
File defaultPar = buildDefaultPar();
File explicitPar = buildExplicitPar();
addPackageToClasspath( defaultPar, explicitPar );
EntityManagerFactory emf;
CustomScanner.resetUsed();
emf = Persistence.createEntityManagerFactory( "defaultpar", new HashMap() );
assertTrue( ! CustomScanner.isUsed() );
emf.close();
CustomScanner.resetUsed();
emf = Persistence.createEntityManagerFactory( "manager1", new HashMap() );
assertTrue( CustomScanner.isUsed() );
emf.close();
}
} }