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>
<programlisting role="XML" language="XML">&lt;project ...&gt;
<programlisting language="XML" role="XML">&lt;project ...&gt;
...
&lt;dependencies&gt;
&lt;dependency&gt;
@ -100,7 +100,7 @@
configuration, so by default, your persistence.xml will be quite
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"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"&gt;
@ -116,7 +116,7 @@
<para>Here's a more complete example of a
<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"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"&gt;
@ -211,7 +211,7 @@
environment, the persistence.xml file is not under the same root
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>
</varlistentry>
@ -323,14 +323,14 @@
<literal>&lt;validation-mode&gt;</literal>. To use it, add a
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
&lt;/property&gt;</programlisting>
<para>With this approach, you can mix ddl and callback
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
&lt;/property&gt;</programlisting>
</listitem>
@ -376,12 +376,35 @@
</listitem>
<listitem>
<para>javax.persistence.validation.group.pre-persist 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
<para><literal>javax.persistence.validation.group.pre-persist</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>)</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>
</itemizedlist>
@ -427,7 +450,7 @@
the version embedded in the hibernate-entitymanager.jar. It won't fetch
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"
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>
@ -441,7 +464,7 @@
<classname>EntityManager</classname>. The bootstrap class is
<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
@ -636,10 +659,26 @@ EntityManagerFactory programmaticEmf =
<entry>If true, the persistence context will be discarded (think
clear() when the method is called. Otherwise the persistence
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
completion)</entry>
</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>
</tgroup>
</table>
@ -663,7 +702,7 @@ EntityManagerFactory programmaticEmf =
<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;class&gt;org.hibernate.ejb.test.Cat&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>
<programlisting role="JAVA" language="JAVA">Ejb3Configuration cfg = new Ejb3Configuration();
<programlisting language="JAVA" role="JAVA">Ejb3Configuration cfg = new Ejb3Configuration();
EntityManagerFactory emf =
cfg.addProperties( properties ) //add some properties
.setInterceptor( myInterceptorImpl ) // set an interceptor
@ -857,7 +896,7 @@ EntityManagerFactory emf =
<classname>Persistence</classname> class is bootstrap class to create an
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")
EntityManager em = emf.createEntityManager(); // Retrieve an application managed entity manager
// Work with the EM

View File

@ -270,10 +270,17 @@ public class AvailableSettings {
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";
/**
* 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
* Internal use only

View File

@ -324,7 +324,7 @@ public class Ejb3Configuration implements Serializable, Referenceable {
Scanner scanner = null;
URL jarURL = null;
if ( metadata.getName() == null ) {
scanner = buildScanner();
scanner = buildScanner( metadata.getProps() );
jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
metadata.setName( scanner.getUnqualifiedJarName(jarURL) );
}
@ -333,7 +333,7 @@ public class Ejb3Configuration implements Serializable, Referenceable {
}
else if ( persistenceUnitName == null || metadata.getName().equals( persistenceUnitName ) ) {
if (scanner == null) {
scanner = buildScanner();
scanner = buildScanner( metadata.getProps() );
jarURL = JarVisitorFactory.getJarURLFromURLEntry( url, "/META-INF/persistence.xml" );
}
//scan main JAR
@ -373,9 +373,41 @@ public class Ejb3Configuration implements Serializable, Referenceable {
}
}
private NativeScanner buildScanner() {
private Scanner buildScanner(Properties properties) {
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 {
//boolean excludeUnlistedClasses;
@ -531,7 +563,9 @@ public class Ejb3Configuration implements Serializable, Referenceable {
boolean searchForORMFiles = ! xmlFiles.contains( META_INF_ORM_XML );
ScanningContext context = new ScanningContext();
context.scanner( buildScanner() )
final Properties copyOfProperties = (Properties) info.getProperties().clone();
ConfigurationHelper.overrideProperties( copyOfProperties, integration );
context.scanner( buildScanner( copyOfProperties ) )
.searchOrm( searchForORMFiles )
.explicitMappingFiles( null ); //URLs provided by the container already

View File

@ -225,7 +225,7 @@ public class NativeScanner implements Scanner {
return files;
}
public Set<NamedInputStream> getFilesInClasspath(URL jartoScan, Set<String> filePatterns) {
public Set<NamedInputStream> getFilesInClasspath(Set<String> filePatterns) {
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
* 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

View File

@ -11,6 +11,9 @@
<class>org.hibernate.ejb.test.Item</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<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.connection.driver_class" value="${jdbc.driver}"/>
<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
*
@ -26,11 +26,14 @@ package org.hibernate.ejb.test.packaging;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.MappedSuperclass;
import javax.persistence.Persistence;
import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner;
@ -42,7 +45,7 @@ import org.hibernate.ejb.test.pack.defaultpar.ApplicationServer;
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public class NativeScannerTest extends PackagingTestCase {
public class ScannerTest extends PackagingTestCase {
public void testNativeScanner() throws Exception {
File defaultPar = buildDefaultPar();
addPackageToClasspath( defaultPar );
@ -71,4 +74,21 @@ public class NativeScannerTest extends PackagingTestCase {
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();
}
}