diff --git a/tooling/metamodel-generator/pom.xml b/tooling/metamodel-generator/pom.xml index 0306c9059c..f9eee5e41a 100644 --- a/tooling/metamodel-generator/pom.xml +++ b/tooling/metamodel-generator/pom.xml @@ -1,6 +1,4 @@ - + 4.0.0 diff --git a/tooling/metamodel-generator/src/main/docbook/en-US/images/eclipse-annotation-processor-config.png b/tooling/metamodel-generator/src/main/docbook/en-US/images/eclipse-annotation-processor-config.png index ba53ad8a38..2662d067f5 100644 Binary files a/tooling/metamodel-generator/src/main/docbook/en-US/images/eclipse-annotation-processor-config.png and b/tooling/metamodel-generator/src/main/docbook/en-US/images/eclipse-annotation-processor-config.png differ diff --git a/tooling/metamodel-generator/src/main/docbook/en-US/images/idea-annotation-processor-config.png b/tooling/metamodel-generator/src/main/docbook/en-US/images/idea-annotation-processor-config.png index 604d20d162..e619e50d87 100644 Binary files a/tooling/metamodel-generator/src/main/docbook/en-US/images/idea-annotation-processor-config.png and b/tooling/metamodel-generator/src/main/docbook/en-US/images/idea-annotation-processor-config.png differ diff --git a/tooling/metamodel-generator/src/main/docbook/en-US/master.xml b/tooling/metamodel-generator/src/main/docbook/en-US/master.xml index 3d5708a196..3d5940fa37 100644 --- a/tooling/metamodel-generator/src/main/docbook/en-US/master.xml +++ b/tooling/metamodel-generator/src/main/docbook/en-US/master.xml @@ -92,7 +92,7 @@ public class Order_ { Example of typesafe query using the metamodel class - <classname>Order_</classname> + Order_ CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Order> cq = cb.createQuery(Order.class); @@ -109,7 +109,7 @@ cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ).distinct(true); url="http://jcp.org/en/jsr/detail?id=317">JPA 2 specification and its definition is included for completeness in the following paragraphs . Feel free to skip ahead to - if you are not interested into the gory details. + if you are not interested into the gory details. The annotation processor produces for every managed class in the persistence unit a metamodel class based on these rules: @@ -188,8 +188,8 @@ cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ).distinct(true); Usage - The jar file for the annotation processor can be found in the - JBoss Maven repository + The jar file for the annotation processor can be found in the JBoss Maven repository under: @@ -233,7 +233,7 @@ cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ).distinct(true); url="http://ant.apache.org/manual/CoreTasks/javac.html">Ant Javac Task can be configured to just run annotation processing. - Ant Javac Task configuration + Ant Javac Task configuration <javac srcdir="${src.dir}" destdir="${target.dir}" @@ -407,7 +407,12 @@ cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ).distinct(true);
NetBeans - TODO + Netbeans support for annotation processors is at the time of + this wrinting still in the making. Refer to NetBeans issues 111065, + 111293 + and 111294.
@@ -455,6 +460,22 @@ cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ).distinct(true); mapping file names. Even when this option is specified /META-INF/orm.xml is implicit. + + + lazyXmlParsing + + Possible values are true or + false. If set to true + the annotation processor tries to determine whether any of the + xml files has changed between invocations and if unchanged + skips the xml parsing. This feature is experimental and + contains the risk of wron results in some cases of mixed mode + configurations. To determine wether a file has been modified a + temporary file + Hibernate-Static-Metamodel-Generator.tmp + is used. This file gets created in the + java.io.tmpdir directory. +
diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java index 32a0552d3c..88fb963798 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java @@ -56,6 +56,7 @@ public class Context { private final ProcessingEnvironment pe; private final boolean logDebug; + private final boolean lazyXmlParsing; private final String persistenceXmlLocation; private final List ormXmlFiles; @@ -90,6 +91,7 @@ public class Context { ormXmlFiles = Collections.emptyList(); } + lazyXmlParsing = Boolean.parseBoolean( pe.getOptions().get( JPAMetaModelEntityProcessor.LAZY_XML_PARSING ) ); logDebug = Boolean.parseBoolean( pe.getOptions().get( JPAMetaModelEntityProcessor.DEBUG_OPTION ) ); } @@ -125,8 +127,8 @@ public class Context { return metaEntities.values(); } - public void addMetaEntity(String fcqn, MetaEntity metaEntity) { - metaEntities.put( fcqn, metaEntity ); + public void addMetaEntity(String fqcn, MetaEntity metaEntity) { + metaEntities.put( fqcn, metaEntity ); } public boolean containsMetaEmbeddable(String fqcn) { @@ -181,12 +183,17 @@ public class Context { this.persistenceUnitDefaultAccessType = persistenceUnitDefaultAccessType; } + public boolean doLazyXmlParsing() { + return lazyXmlParsing; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append( "Context" ); sb.append( "{accessTypeInformation=" ).append( accessTypeInformation ); sb.append( ", logDebug=" ).append( logDebug ); + sb.append( ", lazyXmlParsing=" ).append( lazyXmlParsing ); sb.append( ", isPersistenceUnitCompletelyXmlConfigured=" ).append( isPersistenceUnitCompletelyXmlConfigured ); sb.append( ", ormXmlFiles=" ).append( ormXmlFiles ); sb.append( ", persistenceXmlLocation='" ).append( persistenceXmlLocation ).append( '\'' ); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java index 2316c72df7..fbf3689516 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java @@ -64,16 +64,22 @@ import static javax.lang.model.SourceVersion.RELEASE_6; @SupportedOptions({ JPAMetaModelEntityProcessor.DEBUG_OPTION, JPAMetaModelEntityProcessor.PERSISTENCE_XML_OPTION, - JPAMetaModelEntityProcessor.ORM_XML_OPTION + JPAMetaModelEntityProcessor.ORM_XML_OPTION, + JPAMetaModelEntityProcessor.FULLY_ANNOTATION_CONFIGURED_OPTION, + JPAMetaModelEntityProcessor.LAZY_XML_PARSING }) public class JPAMetaModelEntityProcessor extends AbstractProcessor { public static final String DEBUG_OPTION = "debug"; public static final String PERSISTENCE_XML_OPTION = "persistenceXml"; public static final String ORM_XML_OPTION = "ormXmlList"; + public static final String FULLY_ANNOTATION_CONFIGURED_OPTION = "fullyAnnotationConfigured"; + public static final String LAZY_XML_PARSING = "lazyXmlParsing"; + private static final Boolean ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS = Boolean.FALSE; private boolean xmlProcessed = false; private Context context; + private boolean fullyAnnotationConfigured = false; public void init(ProcessingEnvironment env) { super.init( env ); @@ -81,12 +87,13 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor { context.logMessage( Diagnostic.Kind.NOTE, "Hibernate JPA 2 Static-Metamodel Generator " + Version.getVersionString() ); + + String tmp = env.getOptions().get( JPAMetaModelEntityProcessor.FULLY_ANNOTATION_CONFIGURED_OPTION ); + fullyAnnotationConfigured = Boolean.parseBoolean( tmp ); } @Override - public boolean process(final Set annotations, - final RoundEnvironment roundEnvironment) { - + public boolean process(final Set annotations, final RoundEnvironment roundEnvironment) { if ( roundEnvironment.processingOver() ) { context.logMessage( Diagnostic.Kind.OTHER, "Last processing round." ); createMetaModelClasses(); @@ -94,7 +101,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor { return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS; } - if ( !xmlProcessed ) { + if ( !fullyAnnotationConfigured && !xmlProcessed ) { XmlParser parser = new XmlParser( context ); parser.parseXml(); xmlProcessed = true; @@ -156,7 +163,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor { for ( Element subElement : ElementFilter.fieldsIn( entity.getTypeElement().getEnclosedElements() ) ) { TypeMirror mirror = subElement.asType(); if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) { - continue; + continue; } boolean contains = mirror.accept( visitor, subElement ); if ( contains ) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/FileTimeStampChecker.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/FileTimeStampChecker.java new file mode 100644 index 0000000000..25acad774a --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/FileTimeStampChecker.java @@ -0,0 +1,73 @@ +// $Id:$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.hibernate.jpamodelgen.util; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Hardy Ferentschik + */ +public class FileTimeStampChecker implements Serializable { + + + Map lastModifiedCache; + + public FileTimeStampChecker() { + lastModifiedCache = new HashMap(); + } + + public void add(String fileName, Long lastModified) { + lastModifiedCache.put( fileName, lastModified ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + FileTimeStampChecker that = ( FileTimeStampChecker ) o; + + if ( !lastModifiedCache.equals( that.lastModifiedCache ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return lastModifiedCache.hashCode(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append( "FileTimeStampChecker" ); + sb.append( "{lastModifiedCache=" ).append( lastModifiedCache ); + sb.append( '}' ); + return sb.toString(); + } +} + + diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEmbeddable.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEmbeddable.java index 52f66f0661..380d618df5 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEmbeddable.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEmbeddable.java @@ -1,4 +1,4 @@ -// $Id: XmlMetaEntity.java 18753 2010-02-09 21:29:34Z hardy.ferentschik $ +// $Id$ /* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEntity.java index deacc2350f..7340e4e0ca 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlMetaEntity.java @@ -360,6 +360,9 @@ public class XmlMetaEntity implements MetaEntity { } private void parseEmbeddableAttributes(EmbeddableAttributes attributes) { + if ( attributes == null ) { + return; + } for ( Basic basic : attributes.getBasic() ) { parseBasic( basic ); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlParser.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlParser.java index 8f73b9529c..d17d5bff2c 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlParser.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/xml/XmlParser.java @@ -17,8 +17,15 @@ */ package org.hibernate.jpamodelgen.xml; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -40,6 +47,7 @@ import org.xml.sax.SAXException; import org.hibernate.jpamodelgen.AccessTypeInformation; import org.hibernate.jpamodelgen.Context; import org.hibernate.jpamodelgen.util.Constants; +import org.hibernate.jpamodelgen.util.FileTimeStampChecker; import org.hibernate.jpamodelgen.util.StringUtil; import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.xml.jaxb.Entity; @@ -56,6 +64,7 @@ public class XmlParser { private static final String ORM_XML = "/META-INF/orm.xml"; private static final String PERSISTENCE_XML_XSD = "persistence_2_0.xsd"; private static final String ORM_XSD = "orm_2_0.xsd"; + private static final String SERIALIZATION_FILE_NAME = "Hibernate-Static-Metamodel-Generator.tmp"; private Context context; private List entityMappings; @@ -66,7 +75,13 @@ public class XmlParser { } public void parseXml() { - collectAllEntityMappings(); + Collection mappingFileNames = determineMappingFileNames(); + + if ( context.doLazyXmlParsing() && mappingFilesUnchanged( mappingFileNames ) ) { + return; + } + + loadEntityMappings( mappingFileNames ); determineDefaultAccessTypeAndMetaCompleteness(); determineXmlAccessTypes(); if ( !context.isPersistenceUnitCompletelyXmlConfigured() ) { @@ -83,35 +98,107 @@ public class XmlParser { } } - private void collectAllEntityMappings() { + private Collection determineMappingFileNames() { + Collection mappingFileNames = new ArrayList(); + Persistence persistence = parseXml( context.getPersistenceXmlLocation(), Persistence.class, PERSISTENCE_XML_XSD ); if ( persistence != null ) { List persistenceUnits = persistence.getPersistenceUnit(); for ( Persistence.PersistenceUnit unit : persistenceUnits ) { - List mappingFiles = unit.getMappingFile(); - for ( String mappingFile : mappingFiles ) { - loadEntityMappings( mappingFile ); - } + mappingFileNames.addAll( unit.getMappingFile() ); } } // /META-INF/orm.xml is implicit - loadEntityMappings( ORM_XML ); + mappingFileNames.add( ORM_XML ); // not really part of the official spec, but the processor allows to specify mapping files directly as // command line options - for ( String optionalOrmFiles : context.getOrmXmlFiles() ) { - loadEntityMappings( optionalOrmFiles ); + mappingFileNames.addAll( context.getOrmXmlFiles() ); + return mappingFileNames; + } + + private void loadEntityMappings(Collection mappingFileNames) { + for ( String mappingFile : mappingFileNames ) { + EntityMappings mapping = parseXml( mappingFile, EntityMappings.class, ORM_XSD ); + if ( mapping != null ) { + entityMappings.add( mapping ); + } } } - private void loadEntityMappings(String resource) { - EntityMappings mapping = parseXml( resource, EntityMappings.class, ORM_XSD ); - if ( mapping != null ) { - entityMappings.add( mapping ); + private boolean mappingFilesUnchanged(Collection mappingFileNames) { + boolean mappingFilesUnchanged = false; + FileTimeStampChecker fileStampCheck = new FileTimeStampChecker(); + for ( String mappingFile : mappingFileNames ) { + try { + URL url = this.getClass().getResource( mappingFile ); + if ( url == null ) { + continue; + } + File file = new File( url.toURI() ); + context.logMessage( Diagnostic.Kind.OTHER, "Check file " + mappingFile ); + if ( file.exists() ) { + fileStampCheck.add( mappingFile, file.lastModified() ); + } + } + catch ( URISyntaxException e ) { + // in doubt return false + return false; + } } + + FileTimeStampChecker serializedTimeStampCheck = loadTimeStampCache(); + if ( serializedTimeStampCheck.equals( fileStampCheck ) ) { + context.logMessage( Diagnostic.Kind.OTHER, "XML parsing will be skipped due to unchanged xml files" ); + mappingFilesUnchanged = true; + } + else { + saveTimeStampCache( fileStampCheck ); + } + + return mappingFilesUnchanged; + } + + private void saveTimeStampCache(FileTimeStampChecker fileStampCheck) { + try { + File file = getSerializationTmpFile(); + ObjectOutput out = new ObjectOutputStream( new FileOutputStream( file ) ); + out.writeObject( fileStampCheck ); + out.close(); + context.logMessage( + Diagnostic.Kind.OTHER, "Serialized " + fileStampCheck + " into " + file.getAbsolutePath() + ); + } + catch ( IOException e ) { + // ignore - if the serialization failed we just have to keep parsing the xml + context.logMessage( Diagnostic.Kind.OTHER, "Error serializing " + fileStampCheck ); + } + } + + private File getSerializationTmpFile() { + File tmpDir = new File( System.getProperty( "java.io.tmpdir" ) ); + return new File( tmpDir, SERIALIZATION_FILE_NAME ); + } + + private FileTimeStampChecker loadTimeStampCache() { + FileTimeStampChecker serializedTimeStampCheck = new FileTimeStampChecker(); + File file = null; + try { + file = getSerializationTmpFile(); + if ( file.exists() ) { + ObjectInputStream in = new ObjectInputStream( new FileInputStream( file ) ); + serializedTimeStampCheck = ( FileTimeStampChecker ) in.readObject(); + in.close(); + } + } + catch ( Exception e ) { + // ignore - if the de-serialization failed we just have to keep parsing the xml + context.logMessage( Diagnostic.Kind.OTHER, "Error de-serializing " + file ); + } + return serializedTimeStampCheck; } private void parseEntities(Collection entities, String defaultPackageName) { @@ -254,7 +341,6 @@ public class XmlParser { private InputStream getInputStreamForResource(String resource) { String pkg = getPackage( resource ); String name = getRelativeName( resource ); - context.logMessage( Diagnostic.Kind.OTHER, "Reading resource " + resource ); InputStream ormStream; try { FileObject fileObject = context.getProcessingEnvironment() diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/arraytype/TemperatureSamples.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/arraytype/TemperatureSamples.java index 2b136fe93a..ad02d9fe68 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/arraytype/TemperatureSamples.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/arraytype/TemperatureSamples.java @@ -1,4 +1,4 @@ -// $Id: Image.java 17903 2009-11-04 13:22:37Z hardy.ferentschik $ +// $Id$ /* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/mixedmode/MixedConfigurationTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/mixedmode/MixedConfigurationTest.java index 78c7733bc0..afa240d8a6 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/mixedmode/MixedConfigurationTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/mixedmode/MixedConfigurationTest.java @@ -1,4 +1,4 @@ -// $Id: MixedModeMappingTest.java 18683 2010-02-02 21:51:40Z hardy.ferentschik $ +// $Id$ /* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/rawtypes/RawTypesTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/rawtypes/RawTypesTest.java index 436d7bc6f3..2c3975635a 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/rawtypes/RawTypesTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/rawtypes/RawTypesTest.java @@ -1,4 +1,4 @@ -// $Id: RawTypesTest.java 18664 2010-01-28 16:56:51Z hardy.ferentschik $ +// $Id$ /* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/CompilationTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/CompilationTest.java index 5d4d51c48c..a435509c83 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/CompilationTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/util/CompilationTest.java @@ -132,7 +132,6 @@ public abstract class CompilationTest { builder.append( entry.getValue() ); options.add( builder.toString() ); } - options.add( "-Adebug=true" ); return options; }