Author: tchemit Date: 2010-04-03 11:38:02 +0200 (Sat, 03 Apr 2010) New Revision: 1733 Log: finalize fix bug #438 + improve design and integration tests Added: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/I18nParserConfiguration.java Modified: trunk/maven-i18n-plugin/src/it/parsers/newProject/invoker.properties trunk/maven-i18n-plugin/src/it/parsers/newProject/pom.xml trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/invoker.properties trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/pom.xml trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nMojo.java trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/GenerateMojo.java trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractFileParser.java trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParserMojo.java trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/FileParser.java trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java Modified: trunk/maven-i18n-plugin/src/it/parsers/newProject/invoker.properties =================================================================== --- trunk/maven-i18n-plugin/src/it/parsers/newProject/invoker.properties 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/it/parsers/newProject/invoker.properties 2010-04-03 09:38:02 UTC (rev 1733) @@ -1,6 +1,6 @@ # A comma or space separated list of goals/phases to execute, may # specify an empty list to execute the default goal of the IT project -invoker.goals=clean process-resources +invoker.goals=clean compile # Optionally, a list of goals to run during further invocations of Maven #invoker.goals.2=${project.groupId}:${project.artifactId}:${project.version}:run Modified: trunk/maven-i18n-plugin/src/it/parsers/newProject/pom.xml =================================================================== --- trunk/maven-i18n-plugin/src/it/parsers/newProject/pom.xml 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/it/parsers/newProject/pom.xml 2010-04-03 09:38:02 UTC (rev 1733) @@ -15,6 +15,7 @@ <version>@pom.version@</version> </parent> + <groupId>org.nuiton.i18n</groupId> <artifactId>parsers-newProject</artifactId> <!-- ************************************************************* --> @@ -29,15 +30,17 @@ <packaging>jar</packaging> - <properties> - <i18nVersion>@pom.version@</i18nVersion> + <dependencies> - <!-- default encoding --> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>nuiton-i18n</artifactId> + <version>${project.version}</version> + </dependency> - </properties> - + </dependencies> + <build> <plugins> @@ -45,27 +48,27 @@ <plugin> <groupId>org.nuiton.i18n</groupId> <artifactId>maven-i18n-plugin</artifactId> - <version>${i18nVersion}</version> - - <!--configuration> - <treateDefaultEntry>false</treateDefaultEntry> - <entries> - <entry> - <specificGoal>parserValidation</specificGoal> - <basedir>${maven.src.dir}/main/java/</basedir> - <includes> - <param>**/**-validation.xml</param> - </includes> - </entry> - </entries> - </configuration--> + <version>@pom.version@</version> + <configuration> + <verbose>true</verbose> + </configuration> <executions> <execution> + <id>first</id> <goals> <goal>parserJava</goal> <goal>parserValidation</goal> </goals> </execution> + <execution> + <!-- this execution is to test thread with no files --> + <id>second</id> + <goals> + <goal>parserJava</goal> + <goal>parserValidation</goal> + </goals> + <phase>compile</phase> + </execution> </executions> </plugin> </plugins> Modified: trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/invoker.properties =================================================================== --- trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/invoker.properties 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/invoker.properties 2010-04-03 09:38:02 UTC (rev 1733) @@ -1,6 +1,6 @@ # A comma or space separated list of goals/phases to execute, may # specify an empty list to execute the default goal of the IT project -invoker.goals=clean process-resources +invoker.goals=clean compile # Optionally, a list of goals to run during further invocations of Maven #invoker.goals.2=${project.groupId}:${project.artifactId}:${project.version}:run Modified: trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/pom.xml =================================================================== --- trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/pom.xml 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/it/parsers/withNewKeys/pom.xml 2010-04-03 09:38:02 UTC (rev 1733) @@ -15,6 +15,7 @@ <version>@pom.version@</version> </parent> + <groupId>org.nuiton.i18n</groupId> <artifactId>parsers-withNewKeys</artifactId> <!-- ************************************************************* --> @@ -29,15 +30,16 @@ <packaging>jar</packaging> - <properties> + <dependencies> - <i18nVersion>@pom.version@</i18nVersion> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>nuiton-i18n</artifactId> + <version>${project.version}</version> + </dependency> - <!-- default encoding --> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </dependencies> - </properties> - <build> <plugins> @@ -45,30 +47,30 @@ <plugin> <groupId>org.nuiton.i18n</groupId> <artifactId>maven-i18n-plugin</artifactId> - <version>${i18nVersion}</version> + <version>@pom.version@</version> <configuration> <keepGetters>true</keepGetters> + <verbose>true</verbose> </configuration> - <!--configuration> - <treateDefaultEntry>false</treateDefaultEntry> - <entries> - <entry> - <specificGoal>parserValidation</specificGoal> - <basedir>${maven.src.dir}/main/java/</basedir> - <includes> - <param>**/**-validation.xml</param> - </includes> - </entry> - </entries> - </configuration--> <executions> <execution> + <id>first</id> <goals> <goal>parserJava</goal> <goal>parserValidation</goal> <goal>gen</goal> </goals> </execution> + <execution> + <!-- this execution is to test thread with no files --> + <id>second</id> + <goals> + <goal>parserJava</goal> + <goal>parserValidation</goal> + <goal>gen</goal> + </goals> + <phase>compile</phase> + </execution> </executions> </plugin> </plugins> Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nMojo.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nMojo.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nMojo.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -139,6 +139,7 @@ return !acceptPackaging(Packaging.pom); } + @Override public void init() throws Exception { if (verbose) { @@ -251,10 +252,12 @@ } + @Override public File getBackupFile(File file) { return new File(file.getAbsolutePath() + "~"); } + @Override protected void backupFile(File f) throws IOException { File dst = getBackupFile(f); copyFile(f, dst); @@ -268,19 +271,31 @@ this.encoding = encoding; } + @Override public MavenProject getProject() { return project; } + @Override public void setProject(MavenProject project) { this.project = project; } + @Override public boolean isVerbose() { return verbose; } + @Override public void setVerbose(boolean verbose) { this.verbose = verbose; } + + public boolean isSilent() { + return silent; + } + + public boolean isStrictMode() { + return strictMode; + } } Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/GenerateMojo.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/GenerateMojo.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/GenerateMojo.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -73,7 +73,6 @@ */ protected boolean keepGetters; - @Override protected boolean checkSkip() { if (!needGeneration()) { Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractFileParser.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractFileParser.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractFileParser.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -63,6 +63,11 @@ return log; } + /** + * Method to invoke when a i18n key was detected . + * + * @param key the i18n key to register + */ protected void registerKey(String key) { Object value = oldParser.get(key); if (value == null) { Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParserMojo.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParserMojo.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParserMojo.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -34,7 +34,7 @@ * * @author tony */ -public abstract class AbstractI18nParserMojo extends AbstractI18nMojo { +public abstract class AbstractI18nParserMojo extends AbstractI18nMojo implements I18nParserConfiguration { /** @return the outGetter to use for the instance (java.getter,...) */ protected abstract String getOutGetter(); @@ -64,8 +64,16 @@ /** @return the default src directory to use in directory scanner */ protected abstract File getDefaultBasedir(); + /** + * @return a new file parser to be used in the parser consumer thread + * @since 1.2 + */ public abstract FileParser newFileParser(); + /** + * @param entry the incoming source entry to attach to the file updater + * @return a new file updater to detects files to treate + */ public abstract FileUpdater newFileUpdater(SourceEntry entry); /** @@ -146,6 +154,7 @@ ParserThread thread; + @Override public boolean isStrictMode() { return strictMode; } @@ -187,7 +196,7 @@ } } - thread = new ParserThread(result); + thread = new ParserThread(this); } @Override @@ -245,13 +254,15 @@ continue; } + // launch parser for found files + String[] files = entry.getFiles(); + if (!silent) { getLog().info("start entry " + entry.toString()); - getLog().info("incoming file(s) : " + entry.getFoudFiles()); + getLog().info(files.length + " file(s) to treate (amoung " + + entry.getFoudFiles() + " files)"); } - // launch parser for found files - String[] files = entry.getFiles(); for (int i = 0, max = files.length; i < max; i++) { String file1 = files[i]; String fileName = entry.getBasedir().getAbsolutePath() + @@ -301,6 +312,16 @@ thread.clear(); } + @Override + public boolean isShowTouchedFiles() { + return showTouchedFiles; + } + + @Override + public SortedProperties getResult() { + return result; + } + /** * Add the default entry to entries given in configuration. * <p/> Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/FileParser.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/FileParser.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/FileParser.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -1,6 +1,5 @@ package org.nuiton.i18n.plugin.parser; -import org.apache.maven.plugin.logging.Log; import org.nuiton.io.SortedProperties; import java.io.File; @@ -16,24 +15,15 @@ public interface FileParser { /** - * maven logger to use. - * - * @return the logger - */ - Log getLog(); - - /** @return {@code true} if should print on logger when file is touched */ - boolean isShowTouchedFiles(); - - /** * @return {@code true} if file was touched (says contains at least one i18n * key) */ boolean isTouched(); /** - * TODO As we do not used anylonger old language, we should directly TODO - * use only a set of keys, no need to keep i18n value... + * TODO As we do not used anylonger old language, we should directly + * <p/> + * TODO use only a set of keys, no need to keep i18n value... * * @return the results of i18n keys found for the given file */ Added: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/I18nParserConfiguration.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/I18nParserConfiguration.java (rev 0) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/I18nParserConfiguration.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -0,0 +1,23 @@ +package org.nuiton.i18n.plugin.parser; + +import org.apache.maven.plugin.logging.Log; +import org.nuiton.io.SortedProperties; + +/** + * Shared configuration for a parser mojo. + * + * @author tchemit <chemit@codelutin.com> + * @since 1.2 + */ +public interface I18nParserConfiguration { + + boolean isVerbose(); + + boolean isSilent(); + + boolean isShowTouchedFiles(); + + Log getLog(); + + SortedProperties getResult(); +} Property changes on: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/I18nParserConfiguration.java ___________________________________________________________________ Added: svn:keywords + "Author Date Id Revision HeadURL Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java 2010-04-02 20:02:13 UTC (rev 1732) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java 2010-04-03 09:38:02 UTC (rev 1733) @@ -1,7 +1,6 @@ package org.nuiton.i18n.plugin.parser; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.maven.plugin.logging.Log; import org.nuiton.io.SortedProperties; import org.nuiton.plugin.PluginHelper; @@ -17,135 +16,15 @@ * @author tchemit <chemit@codelutin.com> * @since 1.2 */ -public class ParserThread extends Thread { +public class ParserThread extends Thread implements I18nParserConfiguration { - /** Logger */ - private static final Log log = LogFactory.getLog(ParserThread.class); - /** - * This is a task to parse a {@link #file}. - * <p/> - * The task will be executed in the executor service created in the thread. + * the incoming configuration (from mojo which contains shared result and + * logger) */ - class ParserTask implements Runnable { + protected final I18nParserConfiguration configuration; - /** the file parser */ - protected final FileParser parser; - - /** the file to parse */ - protected final File file; - - /** starting time */ - protected long startingTime; - - /** ending time */ - protected long endingTime; - - ParserTask(FileParser parser, File file) { - this.parser = parser; - this.file = file; - } - - @Override - public void run() { - startingTime = System.nanoTime(); - if (log.isDebugEnabled()) { - log.debug("starting action for " + file); - } - try { - - parser.parseFile(file); - - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error("could not parse file " + file, e); - } - } finally { - - if (log.isDebugEnabled()) { - log.debug("ending action for " + file); - } - endingTime = System.nanoTime(); - } - } - - @Override - public String toString() { - return super.toString() + " - " + file; - } - - protected File getFile() { - return file; - } - - protected long getDelay() { - return endingTime - startingTime; - } - - protected void destroy() { - parser.destroy(); - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - destroy(); - } - - /** - * Register the result of the parsing of the {@link #file} after {@link - * #run()} method was invoked. - * <p/> - * This method should be invoked by the executor as an ending hook. - * - * @param treatedFiles list of files already treated - * @param touchedFiles list of files already touched - * @param result shared result. - */ - protected synchronized void registerResult(List<File> treatedFiles, - List<File> touchedFiles, - SortedProperties result) { - - try { - treatedFiles.add(file); - if (log.isDebugEnabled()) { - String delay = PluginHelper.convertTime(getDelay()); - log.debug("[" + treatedFiles.size() + "] " + file + - " in " + delay); - } - - if (parser.isTouched()) { - - // mark file as touched - touchedFiles.add(file); - - if (parser.isShowTouchedFiles()) { - parser.getLog().info("touch " + file); - } - if (log.isInfoEnabled()) { - String delay = PluginHelper.convertTime(getDelay()); - log.info("[" + treatedFiles.size() + "] touchs " + - file + " in " + delay); - } - - // merge file result with - // merge result - result.putAll(parser.getResult()); - } - } finally { - - // destroy runner - destroy(); - - // release lock2 - - synchronized (lock2) { - lock2.notifyAll(); - } - } - } - } - + /** queue of tasks to consume */ protected final BlockingQueue<ParserTask> tasks; /** list of files consumed */ @@ -154,8 +33,8 @@ /** list of files touched (says having at least one i18n key) */ protected final List<File> touchedFiles; - /** the shared result where to merge atomic results */ - protected final SortedProperties result; + /** number of files registred to consume */ + protected int nbFiles; /** * object used to lock method {@link #terminatesAndWaits()} until the thread @@ -169,17 +48,39 @@ */ private final Object lock2 = new Object(); - /** number of files registred to consume */ - protected int nbFiles; - - public ParserThread(SortedProperties result) { + public ParserThread(I18nParserConfiguration configuration) { super(ParserThread.class.getSimpleName()); - this.result = result; + this.configuration = configuration; tasks = new LinkedBlockingQueue<ParserTask>(); touchedFiles = new ArrayList<File>(); treatedFiles = new ArrayList<File>(); } + @Override + public boolean isVerbose() { + return getConfiguration().isVerbose(); + } + + @Override + public boolean isSilent() { + return getConfiguration().isSilent(); + } + + @Override + public boolean isShowTouchedFiles() { + return getConfiguration().isShowTouchedFiles(); + } + + @Override + public Log getLog() { + return getConfiguration().getLog(); + } + + @Override + public SortedProperties getResult() { + return getConfiguration().getResult(); + } + public List<File> getTreatedFiles() { return treatedFiles; } @@ -199,8 +100,8 @@ for (File f : files) { nbFiles++; - if (log.isInfoEnabled()) { - log.info("[" + nbFiles + "] " + f); + if (isVerbose()) { + getLog().info("[" + nbFiles + "] " + f); } tasks.offer(new ParserTask(parser, f)); } @@ -215,49 +116,55 @@ * <b>Note:</b> The method does not return until all files are not * consumed. */ - public void terminatesAndWaits() { + public synchronized void terminatesAndWaits() { - if (log.isInfoEnabled()) { - log.info("add terminate runner"); + if (isVerbose()) { + getLog().info("add terminate runner"); } tasks.offer(new ParserTask(null, null)); - // block until end of service - - if (log.isInfoEnabled()) { - log.info("block until ends..."); + // we waits a little time to make sure, the thread receive the + // sentinel, we can not use a lock here + try { + Thread.sleep(100); + } catch (InterruptedException e) { + if (getLog().isErrorEnabled()) { + getLog().error(e); + } } - //TODO-TC20100402 Should test (and use another internal state) to be - //TODO-TC20100402 sure We have to perform a lock, sometimes it is not - //TODO-TC20100402 necessary if thread is dead before coming at this point + if (getNbFilesToTreate() > 0) { - synchronized (lock) { - try { - lock.wait(); - } catch (InterruptedException e) { - if (log.isErrorEnabled()) { - log.error(e); + // there is still something to treate, + // MUST block until the thread is fully terminated + + if (isVerbose()) { + getLog().info("block until ends..."); + } + + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + if (getLog().isErrorEnabled()) { + getLog().error(e); + } } } } - if (nbFiles != treatedFiles.size()) { + + if (getNbFilesToTreate() > 0) { + // this case should never happens : once we are here, all files + // MUST have been consumed throw new IllegalStateException( "should have " + nbFiles + " files treated, but found " + treatedFiles.size()); } - if (log.isDebugEnabled()) { - log.debug("thread is terminated."); + if (getLog().isDebugEnabled()) { + getLog().debug("thread is terminated."); } } - /** clean internal state after usage of the thread. */ - public void clear() { - treatedFiles.clear(); - touchedFiles.clear(); - nbFiles = 0; - } - @Override public void run() { @@ -273,8 +180,8 @@ try { runner = tasks.take(); } catch (InterruptedException e) { - if (log.isErrorEnabled()) { - log.error(e); + if (getLog().isErrorEnabled()) { + getLog().error(e); } canQuit = true; continue; @@ -282,31 +189,21 @@ if (runner.getFile() == null) { // ask to quit the tread - if (log.isDebugEnabled()) { - log.debug("no more files to treate, wait termination..."); + if (getLog().isDebugEnabled()) { + getLog().debug("Termination is asked..., will terminate to " + + "resting consume files"); } - //FIXME-TC20100402, sleep to make sure that the lock2 is always - //FIXME-TC20100402 block before the thread is done. - //FIXME-TC20100402 otherwise, it could ends in a dead-end... - //TODO Should really use another lock or internal state before - //TODO doing lock in method terminatesAndWaits - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - log.error(e); - } canQuit = true; continue; } - if (log.isDebugEnabled()) { - log.debug("consume " + runner); + if (getLog().isDebugEnabled()) { + getLog().debug("consume " + runner); } executor.execute(runner); - if (log.isDebugEnabled()) { - log.debug("after consume " + runner); + if (getLog().isDebugEnabled()) { + getLog().debug("after consume " + runner); } } @@ -315,14 +212,14 @@ if (nbFiles == 0) { // no file consumed - if (log.isInfoEnabled()) { - log.info("no file consumed."); + if (isVerbose()) { + getLog().info("No file consumed."); } } else { - if (log.isInfoEnabled()) { - log.info("will waits until all files (" + nbFiles + - ") are consumed (still " + getFilesToTreate() + - " file(s) to consume)"); + if (isVerbose()) { + getLog().info("Will waits until all files (" + nbFiles + + ") are consumed (still " + getNbFilesToTreate() + + " file(s) to consume)"); } } @@ -332,20 +229,20 @@ while (!canQuit) { - int treate = getFilesToTreate(); + int nbFilesToTreate = getNbFilesToTreate(); // can quit if and only if executore is terminated are there is no // more files to consume - // Note : normally, the second is not necessary... - canQuit = executor.isTerminated() || treate == 0; + // Note : normally, the second test is not necessary... + canQuit = executor.isTerminated() || nbFilesToTreate == 0; if (canQuit) { continue; } - if (log.isDebugEnabled()) { - log.debug("waiting end... (still " + treate + - " file(s) to consume)"); + if (getLog().isDebugEnabled()) { + getLog().debug("Waiting end... (still " + nbFilesToTreate + + " file(s) to consume)"); } // waits a task to be done @@ -353,14 +250,19 @@ try { lock2.wait(); } catch (InterruptedException e) { - log.error(e); + getLog().error(e); break; } } + + if (getLog().isDebugEnabled()) { + getLog().debug("A task was consumed, still " + getNbFilesToTreate() + + " file(s) to treate."); + } } - if (log.isInfoEnabled()) { - log.info("executor is terminated, will release thread lock."); + if (isVerbose()) { + getLog().info("Executor is terminated, will release thread lock."); } // service was shutdown and terminated, release thread main lock. @@ -371,7 +273,20 @@ } + /** clean internal state after usage of the thread. */ + public void clear() { + treatedFiles.clear(); + touchedFiles.clear(); + nbFiles = 0; + } + + /** + * Creates the {@link ExecutorService} which will consumed i8n tasks. + * + * @return the instanciated executor + */ protected ExecutorService createExecutor() { + // creates thread factory ThreadFactory factory = new ThreadFactory() { @@ -396,16 +311,146 @@ @Override protected void afterExecute(Runnable r, Throwable t) { - super.afterExecute(r, t); - ParserTask i18n = (ParserTask) r; - i18n.registerResult(treatedFiles, touchedFiles, result); + try { + super.afterExecute(r, t); + ParserTask i18n = (ParserTask) r; + i18n.registerResult(treatedFiles, touchedFiles, getResult()); + } finally { + + // task is done, notify thread + + synchronized (lock2) { + lock2.notifyAll(); + } + } } }; return executor; } - protected int getFilesToTreate() { + protected I18nParserConfiguration getConfiguration() { + return configuration; + } + + protected int getNbFilesToTreate() { return nbFiles - treatedFiles.size(); } + /** + * This is a task to parse a {@link #file}. + * <p/> + * The task will be executed in the executor service created in the thread. + */ + class ParserTask implements Runnable { + + /** the file parser */ + protected final FileParser parser; + + /** the file to parse */ + protected final File file; + + /** starting time */ + protected long startingTime; + + /** ending time */ + protected long endingTime; + + ParserTask(FileParser parser, File file) { + this.parser = parser; + this.file = file; + } + + @Override + public void run() { + startingTime = System.nanoTime(); + if (getLog().isDebugEnabled()) { + getLog().debug("starting action for " + file); + } + try { + + parser.parseFile(file); + + } catch (IOException e) { + if (getLog().isErrorEnabled()) { + getLog().error("could not parse file " + file, e); + } + } finally { + + if (getLog().isDebugEnabled()) { + getLog().debug("ending action for " + file); + } + endingTime = System.nanoTime(); + } + } + + @Override + public String toString() { + return super.toString() + " - " + file; + } + + protected File getFile() { + return file; + } + + protected long getDelay() { + return endingTime - startingTime; + } + + protected void destroy() { + parser.destroy(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + destroy(); + } + + /** + * Register the result of the parsing of the {@link #file} after {@link + * #run()} method was invoked. + * <p/> + * This method should be invoked by the executor as an ending hook. + * + * @param treatedFiles list of files already treated + * @param touchedFiles list of files already touched + * @param result shared result. + */ + protected synchronized void registerResult(List<File> treatedFiles, + List<File> touchedFiles, + SortedProperties result) { + + try { + treatedFiles.add(file); + if (getLog().isDebugEnabled()) { + String delay = PluginHelper.convertTime(getDelay()); + getLog().debug("[" + treatedFiles.size() + "] " + file + + " in " + delay); + } + + if (parser.isTouched()) { + + // mark file as touched + touchedFiles.add(file); + + if (isShowTouchedFiles()) { + getLog().info("touch " + file); + } + if (isVerbose()) { + String delay = PluginHelper.convertTime(getDelay()); + getLog().info("[" + treatedFiles.size() + "] touchs " + + file + " in " + delay); + } + + // merge file result with + // merge result + result.putAll(parser.getResult()); + } + } finally { + + // destroy runner + destroy(); + } + } + } }