Author: tfischer Date: Mon Sep 9 20:25:24 2013 New Revision: 1521281 URL: http://svn.apache.org/r1521281 Log: TORQUE-292 implement runOnlyOnSchemaChange
Added: db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/source/stream/CombinedFileSourceTest.java Modified: db/torque/torque4/trunk/torque-generator/pom.xml db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfiguration.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfigurationReader.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitDescriptor.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/control/Controller.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/Source.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/jdbc/JdbcMetadataSource.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/CombinedFileSource.java db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/FileSource.java db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/control/PropertyToJavaGenerationTest.java Modified: db/torque/torque4/trunk/torque-generator/pom.xml URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/pom.xml?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/pom.xml (original) +++ db/torque/torque4/trunk/torque-generator/pom.xml Mon Sep 9 20:25:24 2013 @@ -124,6 +124,12 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.9.0</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>10.5.3.0_1</version> Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfiguration.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfiguration.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfiguration.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfiguration.java Mon Sep 9 20:25:24 2013 @@ -67,9 +67,7 @@ public class UnitConfiguration */ private final Map<String, File> outputDirectoryMap = new HashMap<String, File>(); - /** - * A directory where the Torque generator can store internal files - */ + /** A directory where the Torque generator can store internal files. */ private File workDirectory; /** The Loglevel for generation. */ @@ -92,6 +90,12 @@ public class UnitConfiguration private ClassLoader classLoader; /** + * Set to true if only the sources should be processed which have + * changed since last generation. + */ + private boolean runOnlyOnSchemaChange = false; + + /** * Returns the configuration of the outlets in this generation unit. * * @return the outlet configuration, not null. @@ -116,7 +120,7 @@ public class UnitConfiguration * @throws NullPointerException if outletConfiguration is null. */ public void setOutletConfiguration( - OutletConfiguration outletConfiguration) + final OutletConfiguration outletConfiguration) { if (outletConfiguration == null) { @@ -150,7 +154,7 @@ public class UnitConfiguration * * @throws NullPointerException if options is null. */ - public void setOptions(Options options) + public void setOptions(final Options options) { if (options == null) { @@ -172,7 +176,7 @@ public class UnitConfiguration * @throws IllegalStateException if the default output directory * was not yet set. */ - public File getOutputDirectory(String outputDirKey) + public File getOutputDirectory(final String outputDirKey) { File result = outputDirectoryMap.get(outputDirKey); if (result == null) @@ -215,7 +219,7 @@ public class UnitConfiguration * @throws NullPointerException if outputDirectoryMap is null. * @throws IllegalStateException if the target directory was not yet set. */ - public void setOutputDirectoryMap(Map<String, File> outputDirectoryMap) + public void setOutputDirectoryMap(final Map<String, File> outputDirectoryMap) { if (outputDirectoryMap == null) { @@ -256,7 +260,7 @@ public class UnitConfiguration * * @throws NullPointerException if workDirectory is null. */ - public void setWorkDirectory(File workDirectory) + public void setWorkDirectory(final File workDirectory) { if (workDirectory == null) { @@ -272,7 +276,7 @@ public class UnitConfiguration * * @throws NullPointerException if outputFiles is null. */ - public void setOutputList(List<Output> outputList) + public void setOutputList(final List<Output> outputList) { if (outputList == null) { @@ -324,7 +328,7 @@ public class UnitConfiguration * * @throws NullPointerException if loglevel is set to null. */ - public void setLoglevel(Loglevel loglevel) + public void setLoglevel(final Loglevel loglevel) { if (loglevel == null) { @@ -349,7 +353,7 @@ public class UnitConfiguration * @param addDebuggingInfoToOutput true if debug information should be * added, false otherwise. */ - public void setAddDebuggingInfoToOutput(boolean addDebuggingInfoToOutput) + public void setAddDebuggingInfoToOutput(final boolean addDebuggingInfoToOutput) { this.addDebuggingInfoToOutput = addDebuggingInfoToOutput; } @@ -381,7 +385,7 @@ public class UnitConfiguration * @throws NullPointerException if configurationHandlers is null. */ public void setConfigurationHandlers( - ConfigurationHandlers configurationHandlers) + final ConfigurationHandlers configurationHandlers) { if (configurationHandlers == null) { @@ -418,7 +422,7 @@ public class UnitConfiguration * to use the source provider defined in the control file. */ public void setOverrideSourceProvider( - SourceProvider overrideSourceProvider) + final SourceProvider overrideSourceProvider) { this.overrideSourceProvider = overrideSourceProvider; overrideSourceProviderInitialized = true; @@ -448,7 +452,7 @@ public class UnitConfiguration * * @throws NullPointerException if entityReferences is null. */ - public void setEntityReferences(EntityReferences entityReferences) + public void setEntityReferences(final EntityReferences entityReferences) { if (entityReferences == null) { @@ -477,7 +481,7 @@ public class UnitConfiguration * @param defaultOutputEncoding the default output encoding, * null for the default platform encoding. */ - public void setDefaultOutputEncoding(String defaultOutputEncoding) + public void setDefaultOutputEncoding(final String defaultOutputEncoding) { this.defaultOutputEncoding = defaultOutputEncoding; } @@ -502,12 +506,39 @@ public class UnitConfiguration * or null if the standard class loader * of the torque generator classes should be used. */ - public void setClassLoader(ClassLoader classLoader) + public void setClassLoader(final ClassLoader classLoader) { this.classLoader = classLoader; } /** + * Returns true if only the sources should be processed which have + * changed since last generation. + * + * @return false if the controller should be run irrespective of changes + * in the source files, true if the controller should be run for + * source files which have changed during last generation. + */ + public boolean isRunOnlyOnSchemaChange() + { + return runOnlyOnSchemaChange; + } + + /** + * Sets whether only the sources should be processed which have + * changed since last generation. + * + * @param runOnlyOnSchemaChange false if the controller should be run + * irrespective of changes in the source files, + * true if the controller should be run for source files + * which have changed during last generation. + */ + public void setRunOnlyOnSchemaChange(final boolean runOnlyOnSchemaChange) + { + this.runOnlyOnSchemaChange = runOnlyOnSchemaChange; + } + + /** * Checks whether the unit configuration is fully initialized. * * @return true if the unit configuration is fully initialized, Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfigurationReader.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfigurationReader.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfigurationReader.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitConfigurationReader.java Mon Sep 9 20:25:24 2013 @@ -66,8 +66,8 @@ class UnitConfigurationReader * for the given configuration type. */ public UnitConfiguration read( - UnitDescriptor unitDescriptor, - ConfigurationHandlers configurationHandlers) + final UnitDescriptor unitDescriptor, + final ConfigurationHandlers configurationHandlers) throws ConfigurationException { if (unitDescriptor == null) @@ -96,6 +96,8 @@ class UnitConfigurationReader unitConfiguration.setOverrideSourceProvider( unitDescriptor.getOverrideSourceProvider()); unitConfiguration.setClassLoader(unitDescriptor.getClassLoader()); + unitConfiguration.setRunOnlyOnSchemaChange( + unitDescriptor.isRunOnlyOnSchemaChange()); ConfigurationProvider configurationProvider = createConfigurationProvider(unitDescriptor); @@ -163,8 +165,8 @@ class UnitConfigurationReader * which option configuration should be merged. */ private void mergeInheritedOutputFiles( - UnitConfiguration unitConfiguration, - UnitConfiguration inheritedConfiguration) + final UnitConfiguration unitConfiguration, + final UnitConfiguration inheritedConfiguration) { List<Output> outputFiles = new ArrayList<Output>(); // inherited Files are generated first. @@ -204,8 +206,8 @@ class UnitConfigurationReader * which entity references should be merged. */ private void mergeInheritedEntityRefernces( - UnitConfiguration unitConfiguration, - UnitConfiguration inheritedConfiguration) + final UnitConfiguration unitConfiguration, + final UnitConfiguration inheritedConfiguration) { EntityReferences entityReferences = unitConfiguration.getEntityReferences(); @@ -243,8 +245,8 @@ class UnitConfigurationReader * which option configuration should be merged. */ private void mergeInheritedOptionConfiguration( - UnitConfiguration unitConfiguration, - UnitConfiguration inheritedConfiguration) + final UnitConfiguration unitConfiguration, + final UnitConfiguration inheritedConfiguration) { Options options = unitConfiguration.getOptions(); Options inheritedOptions = inheritedConfiguration.getOptions(); @@ -272,9 +274,9 @@ class UnitConfigurationReader * @throws ConfigurationException will not normally happen. */ private void mergeInheritedOutletConfiguration( - UnitDescriptor unitDescriptor, - UnitConfiguration unitConfiguration, - UnitConfiguration inheritedConfiguration) + final UnitDescriptor unitDescriptor, + final UnitConfiguration unitConfiguration, + final UnitConfiguration inheritedConfiguration) throws ConfigurationException { OutletConfiguration outletConfiguration @@ -314,10 +316,10 @@ class UnitConfigurationReader * the configuration. */ private void readControlConfiguration( - UnitConfiguration unitConfiguration, - UnitDescriptor unitDescriptor, - ConfigurationHandlers configurationHandlers, - ConfigurationProvider configurationProvider) + final UnitConfiguration unitConfiguration, + final UnitDescriptor unitDescriptor, + final ConfigurationHandlers configurationHandlers, + final ConfigurationProvider configurationProvider) throws ConfigurationException { if (log.isDebugEnabled()) @@ -388,7 +390,7 @@ class UnitConfigurationReader * or if a jar file cannot be accessed. */ private ConfigurationProvider createConfigurationProvider( - UnitDescriptor unitDescriptor) + final UnitDescriptor unitDescriptor) throws ConfigurationException { ConfigurationProvider configurationProvider; @@ -418,7 +420,7 @@ class UnitConfigurationReader return configurationProvider; } - private String getUnitDisplayName(UnitDescriptor unitDescriptor) + private String getUnitDisplayName(final UnitDescriptor unitDescriptor) { if (Packaging.CLASSPATH == unitDescriptor.getPackaging()) { Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitDescriptor.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitDescriptor.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitDescriptor.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/configuration/UnitDescriptor.java Mon Sep 9 20:25:24 2013 @@ -46,17 +46,17 @@ public class UnitDescriptor /** * The packaging of the generation unit. */ - private Packaging packaging; + private final Packaging packaging; /** * The paths the Torque generator must know about the surrounding project. */ - private ProjectPaths projectPaths; + private final ProjectPaths projectPaths; /** * How the Torque generator's configuration is organized internally. */ - private TorqueGeneratorPaths configurationPaths; + private final TorqueGeneratorPaths configurationPaths; /** * The parent of this generation unit, or null if it has no parent. @@ -92,6 +92,12 @@ public class UnitDescriptor private ClassLoader classLoader; /** + * Set to true if only the sources should be processed which have + * changed since last generation. + */ + private boolean runOnlyOnSchemaChange = false; + + /** * Constructor without inheritance, override options, overrideSourceFileset, * loglevel and addDebuggingInfoToOutput. * @@ -102,9 +108,9 @@ public class UnitDescriptor * of the configuration unit, not null. */ public UnitDescriptor( - Packaging packaging, - ProjectPaths projectPaths, - TorqueGeneratorPaths configurationPaths) + final Packaging packaging, + final ProjectPaths projectPaths, + final TorqueGeneratorPaths configurationPaths) { if (packaging == null) { @@ -176,7 +182,7 @@ public class UnitDescriptor * @param inheritsFrom the parents unit descriptor, * or null if no parent exists. */ - public void setInheritsFrom(UnitDescriptor inheritsFrom) + public void setInheritsFrom(final UnitDescriptor inheritsFrom) { this.inheritsFrom = inheritsFrom; } @@ -200,7 +206,7 @@ public class UnitDescriptor * @param overrideSourceProvider the overriding source provider, * or null if the control file definition is not overridden. */ - public void setOverrideSourceProvider(SourceProvider overrideSourceProvider) + public void setOverrideSourceProvider(final SourceProvider overrideSourceProvider) { this.overrideSourceProvider = overrideSourceProvider; } @@ -221,7 +227,7 @@ public class UnitDescriptor * @param overrideOptions the configuration of the overriding options, * or null. */ - public void setOverrideOptions(OptionsConfiguration overrideOptions) + public void setOverrideOptions(final OptionsConfiguration overrideOptions) { this.overrideOptions = overrideOptions; } @@ -243,7 +249,7 @@ public class UnitDescriptor * * @param loglevel the log level, or null. */ - public void setLoglevel(Loglevel loglevel) + public void setLoglevel(final Loglevel loglevel) { this.loglevel = loglevel; } @@ -265,7 +271,7 @@ public class UnitDescriptor * @param addDebuggingInfoToOutput true if debugging info should be added * to the output, false if not. */ - public void setAddDebuggingInfoToOutput(boolean addDebuggingInfoToOutput) + public void setAddDebuggingInfoToOutput(final boolean addDebuggingInfoToOutput) { this.addDebuggingInfoToOutput = addDebuggingInfoToOutput; } @@ -289,7 +295,7 @@ public class UnitDescriptor * @param defaultOutputEncoding the default output encoding, * null for the default platform encoding. */ - public void setDefaultOutputEncoding(String defaultOutputEncoding) + public void setDefaultOutputEncoding(final String defaultOutputEncoding) { this.defaultOutputEncoding = defaultOutputEncoding; } @@ -314,10 +320,36 @@ public class UnitDescriptor * or null if the standard class loader * of the torque generator classes should be used. */ - public void setClassLoader(ClassLoader classLoader) + public void setClassLoader(final ClassLoader classLoader) { this.classLoader = classLoader; } + /** + * Returns true if only the sources should be processed which have + * changed since last generation. + * + * @return false if the controller should be run irrespective of changes + * in the source files, true if the controller should be run for + * source files which have changed during last generation. + */ + public boolean isRunOnlyOnSchemaChange() + { + return runOnlyOnSchemaChange; + } + + /** + * Sets whether only the sources should be processed which have + * changed since last generation. + * + * @param runOnlyOnSchemaChange false if the controller should be run + * irrespective of changes in the source files, + * true if the controller should be run for source files + * which have changed during last generation. + */ + public void setRunOnlyOnSchemaChange(final boolean runOnlyOnSchemaChange) + { + this.runOnlyOnSchemaChange = runOnlyOnSchemaChange; + } } Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/control/Controller.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/control/Controller.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/control/Controller.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/control/Controller.java Mon Sep 9 20:25:24 2013 @@ -24,11 +24,16 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; +import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.PropertyConfigurator; @@ -66,6 +71,20 @@ public class Controller private static Log log = LogFactory.getLog(Controller.class); /** + * The subdirectory in the work directory where last source changes + * are stored. + */ + public static final String LAST_SOURCE_CHANGE_WORK_SUBDIR + = "last-source-changes"; + + /** The suffix for written checksum files. */ + public static final String CHECKSUM_SUFFIX = ".checksum"; + + /** The content of checksum files if no checksum can be computed. */ + public static final String NO_CHECKSUM_CONTENT + = "[checksum could not be determined]"; + + /** * All known ExistingTargetStrategies. * TODO: move to a better place. */ @@ -85,6 +104,18 @@ public class Controller } /** + * Caches the result whether the source was modified since last generation + * (relevant if the runOnlyOnSchemaCahne Flag is set). + * Caching is necessary because a source file can be read several times + * during one generation, and only the first check against the checksum file + * is meaningful. + * The key is the absolute path to the source file, the value the result + * of the sourceModified check. + */ + private final Map<String,Boolean> sourceModifiedCache + = new HashMap<String,Boolean>(); + + /** * Executes the controller action. * * @param unitDescriptors the units of generation to execute. @@ -101,6 +132,7 @@ public class Controller throws GeneratorException { initLogging(); + sourceModifiedCache.clear(); final Configuration configuration = readConfiguration(unitDescriptors); final List<UnitConfiguration> unitConfigurations @@ -225,11 +257,19 @@ public class Controller while (sourceProvider.hasNext()) { final Source source = sourceProvider.next(); - processSourceInOutput( - source, - output, - controllerState, - unitConfiguration); + if (!unitConfiguration.isRunOnlyOnSchemaChange() + || checkSourceModified(source, unitConfiguration)) + { + processSourceInOutput( + source, + output, + controllerState, + unitConfiguration); + } + if (unitConfiguration.isRunOnlyOnSchemaChange()) + { + writeLastModified(source, unitConfiguration); + } } controllerState.setSourceProvider(null); } @@ -573,4 +613,153 @@ public class Controller return Charset.defaultCharset().displayName(); } + /** + * Checks whether a source file was modified since the last generation. + * + * @param source the source file to check, not null. + * @param unitConfiguration the configuration of the unit of generation, + * not null. + * + * @return true if the source file was modified since last generation, + * if the time of the last generation cannot be determined, + * or if the source file does not exist, + * false otherwise. + */ + private boolean checkSourceModified( + final Source source, + final UnitConfiguration unitConfiguration) + { + File sourceFile = source.getSourceFile(); + if (sourceFile == null) + { + log.trace("checkSourceModified(): " + + "source file cannot be determined, return true"); + return true; + } + String absoluteSourcePath = sourceFile.getAbsoluteFile().toString(); + if (sourceModifiedCache.get(absoluteSourcePath) != null) + { + return sourceModifiedCache.get(absoluteSourcePath); + } + + Date sourceLastModified = source.getLastModified(); + if (sourceLastModified == null) + { + log.trace("checkSourceModified(): " + + "lastModified date of source cannot be determined, " + + "return true"); + sourceModifiedCache.put(absoluteSourcePath, true); + return true; + } + File lastChangesDir = new File( + unitConfiguration.getWorkDirectory(), + LAST_SOURCE_CHANGE_WORK_SUBDIR); + File lastChangesFile = new File( + lastChangesDir, + sourceFile.getName() + CHECKSUM_SUFFIX); + if (!lastChangesFile.exists()) + { + log.trace("checkSourceModified(): " + + "lastChanges file does not exist, return true"); + sourceModifiedCache.put(absoluteSourcePath, true); + return true; + } + if (lastChangesFile.lastModified() < sourceLastModified.getTime()) + { + log.trace("checkSourceModified(): " + + "lastChanges file was last changed before source (" + + new Date(lastChangesDir.lastModified()) + + " < " + + sourceLastModified + + "), return true"); + sourceModifiedCache.put(absoluteSourcePath, true); + return true; + } + byte[] lastChangesContent = null; + try + { + lastChangesContent = FileUtils.readFileToByteArray(lastChangesFile); + } + catch (IOException e) + { + log.warn("checkSourceModified(): " + + "could not access File " + + lastChangesFile.getAbsolutePath() + + ", return true"); + sourceModifiedCache.put(absoluteSourcePath, true); + return true; + } + if (!Arrays.equals(lastChangesContent, source.getContentChecksum())) + { + log.trace("checkSourceModified(): " + + " different checksum, return true"); + sourceModifiedCache.put(absoluteSourcePath, true); + return true; + } + log.trace("checkSourceModified() : returning false"); + sourceModifiedCache.put(absoluteSourcePath, false); + return false; + } + + /** + * Writes the last modification time of a source to the work directory. + * + * @param source the source which modification time should be written, + * not null. + * @param unitConfigurationthe configuration of the unit of generation, + * not null. + */ + private void writeLastModified( + final Source source, + final UnitConfiguration unitConfiguration) + { + File sourceFile = source.getSourceFile(); + if (sourceFile == null) + { + log.trace("writeLastModified(): " + + "source file cannot be determined, do nothing"); + return; + } + File lastChangesDir = new File( + unitConfiguration.getWorkDirectory(), + LAST_SOURCE_CHANGE_WORK_SUBDIR); + if (!lastChangesDir.exists()) + { + boolean dirCreationSuccessfull = lastChangesDir.mkdirs(); + if (!dirCreationSuccessfull) + { + log.warn("could not create directory(): " + + lastChangesDir.getAbsolutePath() + + ", do nothing"); + return; + } + } + File lastChangesFile = new File( + lastChangesDir, + sourceFile.getName() + CHECKSUM_SUFFIX); + byte[] contentChecksum = source.getContentChecksum(); + try + { + if (contentChecksum != null) + { + FileUtils.writeByteArrayToFile( + lastChangesFile, + source.getContentChecksum()); + } + else + { + FileUtils.writeStringToFile( + lastChangesFile, + NO_CHECKSUM_CONTENT, + "ISO-8859-1"); + } + } + catch (IOException e) + { + log.warn("could not write to file(): " + + lastChangesFile, + e); + return; + } + } } Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/Source.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/Source.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/Source.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/Source.java Mon Sep 9 20:25:24 2013 @@ -20,6 +20,7 @@ package org.apache.torque.generator.sour */ import java.io.File; +import java.util.Date; /** * An entity which serves as an input for the generation process. @@ -49,4 +50,23 @@ public interface Source * @return the source file, or null if the source is not read from a file. */ File getSourceFile(); + + /** + * Returns the date when the source was last modified. + * + * @return the last modification date, or null when unknown. + */ + Date getLastModified(); + + /** + * Returns the checksum of the content of the source. + * It is not defined which checksum is returned, the only + * requirement is that collisions should be extremely rare, + * i.e it can be assumed that if the checksum is the same, + * the content is also the same. + * + * @return the checksum of the content of the source, or null if + * it cannot be determined. + */ + byte[] getContentChecksum(); } Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/jdbc/JdbcMetadataSource.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/jdbc/JdbcMetadataSource.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/jdbc/JdbcMetadataSource.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/jdbc/JdbcMetadataSource.java Mon Sep 9 20:25:24 2013 @@ -28,6 +28,7 @@ import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -118,20 +119,19 @@ public class JdbcMetadataSource extends private static Log log = LogFactory.getLog(JdbcMetadataSource.class); /** The fully qualified class name of the database driver. */ - private String driver; + private final String driver; /** The connection url to the database, */ - private String url; + private final String url; /** The username to connect to the database. */ - private String username; + private final String username; /** The password to connect to the database. */ - private String password; + private final String password; /** Which database(mysql) or schema (oracle) should be read. */ - private String schema; - + private final String schema; /** * Constructor. @@ -143,11 +143,11 @@ public class JdbcMetadataSource extends * @param schema the schema to read. */ public JdbcMetadataSource( - String driver, - String url, - String username, - String password, - String schema) + final String driver, + final String url, + final String username, + final String password, + final String schema) { this.driver = driver; this.url = url; @@ -187,7 +187,7 @@ public class JdbcMetadataSource extends for (int i = 0; i < tableList.size(); i++) { // Add Table. - String tableName = (String) tableList.get(i); + String tableName = tableList.get(i); log.debug("Processing table: " + tableName); SourceElement table = new SourceElement("table"); @@ -320,7 +320,7 @@ public class JdbcMetadataSource extends * @return The list of all the tables in a database. * @throws SQLException */ - List<String> getTableNames(DatabaseMetaData dbMeta, String dbSchema) + List<String> getTableNames(final DatabaseMetaData dbMeta, final String dbSchema) throws SQLException { log.debug("Getting table list..."); @@ -360,9 +360,9 @@ public class JdbcMetadataSource extends * @throws SQLException if an sql error occurs during information retrieval. */ List<ColumnMetadata> getColumns( - DatabaseMetaData dbMeta, - String tableName, - String dbSchema) + final DatabaseMetaData dbMeta, + final String tableName, + final String dbSchema) throws SQLException { List<ColumnMetadata> columns = new ArrayList<ColumnMetadata>(); @@ -415,9 +415,9 @@ public class JdbcMetadataSource extends * @throws SQLException */ Set<String> getPrimaryKeys( - DatabaseMetaData dbMeta, - String tableName, - String schemaName) + final DatabaseMetaData dbMeta, + final String tableName, + final String schemaName) throws SQLException { Set<String> pk = new HashSet<String>(); @@ -450,9 +450,9 @@ public class JdbcMetadataSource extends * @throws SQLException */ Collection<ForeignKeyMetadata> getForeignKeys( - DatabaseMetaData dbMeta, - String tableName, - String schemaName) + final DatabaseMetaData dbMeta, + final String tableName, + final String schemaName) throws SQLException { Map<String, ForeignKeyMetadata> foreignKeys @@ -504,4 +504,27 @@ public class JdbcMetadataSource extends } return foreignKeys.values(); } + + /** + * Returns the last modification date of the source files. + * + * @return always null because no source file exist. + */ + public Date getLastModified() + { + return null; + } + + /** + * Returns the checksum of the content. + * + * @return always null. + */ + public byte[] getContentChecksum() + { + // Although we could determine a checksum for the content, + // doing so makes no sense because we cannot determine + // al last modified date. + return null; + } } Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/CombinedFileSource.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/CombinedFileSource.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/CombinedFileSource.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/CombinedFileSource.java Mon Sep 9 20:25:24 2013 @@ -22,6 +22,7 @@ package org.apache.torque.generator.sour import java.io.File; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import org.apache.commons.logging.Log; @@ -66,7 +67,7 @@ public class CombinedFileSource extends /** * The list of contained file sources, not null.. */ - private List<FileSource> fileSources; + private final List<FileSource> fileSources; /** * Constructor. @@ -76,7 +77,7 @@ public class CombinedFileSource extends * @throws NullPointerException if path or format is null. */ public CombinedFileSource( - Collection<FileSource> fileSources) + final Collection<FileSource> fileSources) { if (fileSources == null) { @@ -151,9 +152,73 @@ public class CombinedFileSource extends return null; } + /** + * Returns the earliest date when any of the source files was last modified. + * + * @return the last modification date, + * or null when unknown for at least one of the files. + */ + public Date getLastModified() + { + Date result = null; + for (FileSource fileSource : fileSources) + { + Date fileLastModified = fileSource.getLastModified(); + if (fileLastModified == null) + { + return null; + } + if (result == null || fileLastModified.before(result)) + { + result = fileLastModified; + } + } + return result; + } + + /** + * Returns the checksum of all files. + * All bytes are added so order of the files does not matter. + * + * @return a checksum for all files, or null if one of the checksums + * of the file sources is null. + */ + public byte[] getContentChecksum() + { + byte[] result = new byte[] {}; + + for (FileSource fileSource : fileSources) + { + byte[] fileChecksum = fileSource.getContentChecksum(); + if (fileChecksum == null) + { + return null; + } + byte[] lastResult = result; + result = new byte[Math.max(lastResult.length, fileChecksum.length)]; + for (int i = 0; i < result.length; ++i) + { + if (i < lastResult.length && i < fileChecksum.length) + { + result[i] = (byte) (lastResult[i] + fileChecksum[i]); + } + else if (i < lastResult.length) + { + result[i] = lastResult[i]; + } + else + { + result[i] = fileChecksum[i]; + } + } + } + return result; + } + @Override public String toString() { return getDescription(); } + } Modified: db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/FileSource.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/FileSource.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/FileSource.java (original) +++ db/torque/torque4/trunk/torque-generator/src/main/java/org/apache/torque/generator/source/stream/FileSource.java Mon Sep 9 20:25:24 2013 @@ -24,6 +24,10 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -58,6 +62,11 @@ public class FileSource extends SourceIm private final ControllerState controllerState; /** + * The md5sum of the file content. + */ + private byte[] contentMd5Sum; + + /** * Constructor. * * @param format the source format, not null. @@ -67,9 +76,9 @@ public class FileSource extends SourceIm * @throws NullPointerException if path or format is null. */ public FileSource( - StreamSourceFormat format, - File path, - ControllerState controllerState) + final StreamSourceFormat format, + final File path, + final ControllerState controllerState) { if (path == null) { @@ -127,11 +136,15 @@ public class FileSource extends SourceIm try { inputStream = new FileInputStream(path); + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + DigestInputStream digestInputStream + = new DigestInputStream(inputStream, messageDigest); log.debug("Reading file " + path.getAbsolutePath() + " of type " + format.getKey()); - result = format.parse(inputStream, controllerState); + result = format.parse(digestInputStream, controllerState); + contentMd5Sum = messageDigest.digest(); } catch (FileNotFoundException e) { @@ -139,6 +152,12 @@ public class FileSource extends SourceIm "File not found: " + path.getAbsolutePath(), e); } + catch (NoSuchAlgorithmException e) + { + throw new SourceException( + "MD5 message Digest not implemented", + e); + } finally { if (inputStream != null) @@ -185,6 +204,42 @@ public class FileSource extends SourceIm return path; } + /** + * Returns the date when the source was last modified. + * + * @return the last modification date, or null when unknown. + */ + public Date getLastModified() + { + long lastModified = path.lastModified(); + if (lastModified == 0) + { + return null; + } + return new Date(lastModified); + } + + /** + * Returns the checksum of the content. + * + * @return always null. + */ + public byte[] getContentChecksum() + { + if (contentMd5Sum == null) + { + try + { + getRootElement(); + } + catch (SourceException e) + { + // do nothing, contentMd5Sum remains null + } + } + return contentMd5Sum; + } + @Override public String toString() { Modified: db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/control/PropertyToJavaGenerationTest.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/control/PropertyToJavaGenerationTest.java?rev=1521281&r1=1521280&r2=1521281&view=diff ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/control/PropertyToJavaGenerationTest.java (original) +++ db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/control/PropertyToJavaGenerationTest.java Mon Sep 9 20:25:24 2013 @@ -21,6 +21,7 @@ package org.apache.torque.generator.cont import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; @@ -35,6 +36,14 @@ import org.apache.torque.generator.confi import org.apache.torque.generator.configuration.paths.Maven2DirectoryProjectPaths; import org.junit.Test; +/** + * A test case for a more complex generation. + * Checks that we can use different generator types in one generation, + * that debugging output works + * and that the runOnlyOnSchemaChange detection works. + * + * @version $Id$ + */ public class PropertyToJavaGenerationTest extends BaseTest { /** @@ -125,4 +134,169 @@ public class PropertyToJavaGenerationTes assertEquals(FileUtils.readFileToString(propertiesExpectedFile), FileUtils.readFileToString(propertiesResultFile)); } + + /** + * Tests that runOnlyOnSchemaChange set true and an unchanged source + * results in not generating a second time. + * + * @throws Exception if the test fails. + */ + @Test + public void testPropertyToJavaRunOnlyOnSchemChangeNoChange() + throws Exception + { + // prepare + File targetDir = new File("target/test/propertyToJava"); + File workDir = new File("target/work/propertyToJava"); + File sourceDir = new File("target/source/propertyToJava"); + FileUtils.deleteDirectory(targetDir); + FileUtils.deleteDirectory(workDir); + FileUtils.deleteDirectory(sourceDir); + FileUtils.copyDirectory( + new File("src/test/propertyToJava/src/main/torque-gen/src"), + sourceDir); + Controller controller = new Controller(); + List<UnitDescriptor> unitDescriptors = new ArrayList<UnitDescriptor>(); + CustomProjectPaths projectPaths = new CustomProjectPaths( + new Maven2DirectoryProjectPaths( + new File("src/test/propertyToJava"))); + projectPaths.setOutputDirectory(null, targetDir); + projectPaths.setWorkDir(workDir); + projectPaths.setSourceDir(sourceDir); + UnitDescriptor unitDescriptor = new UnitDescriptor( + UnitDescriptor.Packaging.DIRECTORY, + projectPaths, + new DefaultTorqueGeneratorPaths()); + unitDescriptor.setRunOnlyOnSchemaChange(true); + unitDescriptors.add(unitDescriptor); + + // run first time + controller.run(unitDescriptors); + File propertiesResultFile + = new File(targetDir, "Properties.properties"); + assertTrue(propertiesResultFile.exists()); + long firstLastModified = propertiesResultFile.lastModified(); + + // execute + controller.run(unitDescriptors); + + // verify + assertTrue(propertiesResultFile.exists()); + assertEquals(firstLastModified, propertiesResultFile.lastModified()); + } + + /** + * Tests that runOnlyOnSchemaChange set true and a different checksum + * results in generating a second time. + * + * @throws Exception if the test fails. + */ + @Test + public void testPropertyToJavaRunOnlyOnSchemChangeChecksumChange() + throws Exception + { + // prepare + File targetDir = new File("target/test/propertyToJava"); + File workDir = new File("target/work/propertyToJava"); + File sourceDir = new File("target/source/propertyToJava"); + FileUtils.deleteDirectory(targetDir); + FileUtils.deleteDirectory(workDir); + FileUtils.deleteDirectory(sourceDir); + FileUtils.copyDirectory( + new File("src/test/propertyToJava/src/main/torque-gen/src"), + sourceDir); + Controller controller = new Controller(); + List<UnitDescriptor> unitDescriptors = new ArrayList<UnitDescriptor>(); + CustomProjectPaths projectPaths = new CustomProjectPaths( + new Maven2DirectoryProjectPaths( + new File("src/test/propertyToJava"))); + projectPaths.setOutputDirectory(null, targetDir); + projectPaths.setWorkDir(workDir); + projectPaths.setSourceDir(sourceDir); + UnitDescriptor unitDescriptor = new UnitDescriptor( + UnitDescriptor.Packaging.DIRECTORY, + projectPaths, + new DefaultTorqueGeneratorPaths()); + unitDescriptor.setRunOnlyOnSchemaChange(true); + unitDescriptors.add(unitDescriptor); + + // run first time + controller.run(unitDescriptors); + File propertiesResultFile + = new File(targetDir, "Properties.properties"); + assertTrue(propertiesResultFile.exists()); + long firstLastModified = propertiesResultFile.lastModified(); + + // change checksum file + File checksumFile = new File( + workDir, + "last-source-changes/propertiesData.properties.checksum"); + long checksumFileLastModified = checksumFile.lastModified(); + FileUtils.writeStringToFile(checksumFile, "abc", "ISO-8859-1"); + assertTrue(checksumFile.setLastModified(checksumFileLastModified)); + + // execute + controller.run(unitDescriptors); + + // verify + assertTrue(propertiesResultFile.exists()); + assertFalse(firstLastModified == propertiesResultFile.lastModified()); + } + + /** + * Tests that runOnlyOnSchemaChange set true and a different modification + * date results in generating a second time. + * + * @throws Exception if the test fails. + */ + @Test + public void testPropertyToJavaRunOnlyOnSchemChangeModificationDateChange() + throws Exception + { + // prepare + File targetDir = new File("target/test/propertyToJava"); + File workDir = new File("target/work/propertyToJava"); + File sourceDir = new File("target/source/propertyToJava"); + FileUtils.deleteDirectory(targetDir); + FileUtils.deleteDirectory(workDir); + FileUtils.deleteDirectory(sourceDir); + FileUtils.copyDirectory( + new File("src/test/propertyToJava/src/main/torque-gen/src"), + sourceDir); + Controller controller = new Controller(); + List<UnitDescriptor> unitDescriptors = new ArrayList<UnitDescriptor>(); + CustomProjectPaths projectPaths = new CustomProjectPaths( + new Maven2DirectoryProjectPaths( + new File("src/test/propertyToJava"))); + projectPaths.setOutputDirectory(null, targetDir); + projectPaths.setWorkDir(workDir); + projectPaths.setSourceDir(sourceDir); + UnitDescriptor unitDescriptor = new UnitDescriptor( + UnitDescriptor.Packaging.DIRECTORY, + projectPaths, + new DefaultTorqueGeneratorPaths()); + unitDescriptor.setRunOnlyOnSchemaChange(true); + unitDescriptors.add(unitDescriptor); + + // run first time + controller.run(unitDescriptors); + File propertiesResultFile + = new File(targetDir, "Properties.properties"); + assertTrue(propertiesResultFile.exists()); + long firstLastModified = propertiesResultFile.lastModified(); + + // change modification date of source file + File sourceFile = new File( + sourceDir, + "propertiesData.properties"); + assertTrue(sourceFile.setLastModified( + System.currentTimeMillis() + 1000L)); + + // execute + controller.run(unitDescriptors); + + // verify + assertTrue(propertiesResultFile.exists()); + assertFalse(firstLastModified == propertiesResultFile.lastModified()); + } } Added: db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/source/stream/CombinedFileSourceTest.java URL: http://svn.apache.org/viewvc/db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/source/stream/CombinedFileSourceTest.java?rev=1521281&view=auto ============================================================================== --- db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/source/stream/CombinedFileSourceTest.java (added) +++ db/torque/torque4/trunk/torque-generator/src/test/java/org/apache/torque/generator/source/stream/CombinedFileSourceTest.java Mon Sep 9 20:25:24 2013 @@ -0,0 +1,109 @@ +package org.apache.torque.generator.source.stream; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests for CombinedFileSource. + * @version $Id: $ + */ +public class CombinedFileSourceTest +{ + @Mock + private FileSource fileSource1; + + @Mock + private FileSource fileSource2; + + @Mock + private FileSource fileSource3; + + /** System under test. */ + private CombinedFileSource combinedFileSource; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + List<FileSource> fileSources = new ArrayList<FileSource>(); + fileSources.add(fileSource1); + fileSources.add(fileSource2); + fileSources.add(fileSource3); + combinedFileSource = new CombinedFileSource(fileSources); + } + + @Test + public void testGetLastModifiedAllDatesSet() + { + when(fileSource1.getLastModified()).thenReturn(new Date(100000)); + when(fileSource2.getLastModified()).thenReturn(new Date(99999)); + when(fileSource3.getLastModified()).thenReturn(new Date(100001)); + assertEquals(new Date(99999), combinedFileSource.getLastModified()); + } + + @Test + public void testGetLastModifiedNoDateSet() + { + when(fileSource1.getLastModified()).thenReturn(null); + when(fileSource2.getLastModified()).thenReturn(null); + when(fileSource3.getLastModified()).thenReturn(null); + assertEquals(null, combinedFileSource.getLastModified()); + } + + @Test + public void testGetLastModifiedOneDateNotSet() + { + when(fileSource1.getLastModified()).thenReturn(new Date(100000)); + when(fileSource2.getLastModified()).thenReturn(null); + when(fileSource3.getLastModified()).thenReturn(new Date(100001)); + assertEquals(null, combinedFileSource.getLastModified()); + } + + @Test + public void testGetContentChecksum() + { + when(fileSource1.getContentChecksum()).thenReturn(new byte[] {1, 2, 100, -100, 100}); + when(fileSource2.getContentChecksum()).thenReturn(new byte[] {2, 4}); + when(fileSource3.getContentChecksum()).thenReturn(new byte[] {4, 8, 100, -100, -100}); + assertArrayEquals(new byte[] {7, 14, -56, 56, 0}, combinedFileSource.getContentChecksum()); + } + + @Test + public void testGetContentChecksumOneNull() + { + when(fileSource1.getContentChecksum()).thenReturn(new byte[] {1, 2, 100, -100}); + when(fileSource2.getContentChecksum()).thenReturn(null); + when(fileSource3.getContentChecksum()).thenReturn(new byte[] {4, 8, 100, -100}); + assertEquals(null, combinedFileSource.getContentChecksum()); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: torque-dev-unsubscr...@db.apache.org For additional commands, e-mail: torque-dev-h...@db.apache.org