/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.build.batch;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.novelang.common.FileTools;
import org.novelang.configuration.ConfigurationTools;
import org.novelang.configuration.ContentConfiguration;
import org.novelang.configuration.FopFontStatus;
import org.novelang.configuration.FopTools;
import org.novelang.configuration.ProducerConfiguration;
import org.novelang.configuration.RenderingConfiguration;
import org.novelang.configuration.RenditionKinematic;
import org.novelang.logger.ConcreteLoggerFactory;
import org.novelang.logger.LoggerFactory;
import org.novelang.outfit.DefaultCharset;
import org.novelang.outfit.loader.ClasspathResourceLoader;
import org.novelang.outfit.loader.CompositeResourceLoader;
import org.novelang.outfit.loader.ResourceLoader;
import org.novelang.outfit.loader.UrlResourceLoader;
import org.novelang.produce.DocumentProducer;
import org.novelang.produce.DocumentRequest;
import org.novelang.produce.GenericRequest;
import org.novelang.produce.MalformedRequestException;
import org.novelang.produce.StreamDirector;
/**
* Starts a {@link org.novelang.batch.DocumentGenerator} inside Maven's JVM.
*
* Running the {@link org.novelang.produce.DocumentProducer} in the same JVM doesn't work because of
* <a href="http://forums.sun.com/thread.jspa?threadID=5134880">some Xalan bug</a>
* which wrecks XSL-based document rendering.
*
* TODO: remove duplicated logic. Involves finding a decent API.
*
* @goal produceNoFork
* @requiresDependencyResolution runtime
* @threadSafe
*
* @author Laurent Caillette
*/
@SuppressWarnings( { "UnusedDeclaration" } )
public class NoForkBatchProducerMojo extends AbstractProducerMojo {
/**
* Base directory for rendered documents.
*
* @parameter expression="${produce.outputDirectory}"
* @required
*/
private File outputDirectory = null ;
/**
* Working directory, which serves as reference for default directories like style or fonts.
*
* @parameter expression="${produce.workingDirectory}"
* @required
*/
private File workingDirectory = null ;
/**
* List of documents to render.
*
* @parameter
* @required
*/
private List< String > documentsToRender = null ;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
ConcreteLoggerFactory.setMojoLog( getLog() ) ;
LoggerFactory.configurationComplete() ;
try {
FileTools.createFreshDirectory( outputDirectory ) ;
} catch( IOException e ) {
throw new MojoExecutionException( "Could not create/delete output directory", e ) ;
}
final ProducerConfiguration producerConfiguration = createProducerConfiguration() ;
final DocumentProducer producer = new DocumentProducer( producerConfiguration ) ;
for( final String requestAsString : documentsToRender ) {
final DocumentRequest documentRequest ;
try {
documentRequest = ( DocumentRequest )
GenericRequest.parse( requestAsString ) ;
} catch( MalformedRequestException e ) {
throw new MojoExecutionException( "Bad documentRequest: '" + requestAsString + "'", e ) ;
}
final StreamDirector streamDirector =
StreamDirector.forDirectory( documentRequest, outputDirectory ) ;
try {
producer.produce(
documentRequest,
producer.createRenderable( documentRequest ),
streamDirector
) ;
} catch( Exception e ) {
throw new MojoExecutionException(
"Failed to produce document for '" + requestAsString + "'", e ) ;
}
}
}
private ProducerConfiguration createProducerConfiguration() throws MojoExecutionException {
final ResourceLoader resourceLoader ;
final File styleDirectory = new File( workingDirectory, ConfigurationTools.DEFAULT_STYLE_DIR ) ;
final ClasspathResourceLoader classpathResourceLoader =
new ClasspathResourceLoader( ConfigurationTools.BUNDLED_STYLE_DIR ) ;
if( styleDirectory.exists() ) {
final UrlResourceLoader urlResourceLoader;
try {
urlResourceLoader = new UrlResourceLoader( styleDirectory.toURI().toURL() );
} catch( MalformedURLException e ) {
throw new MojoExecutionException( "Couldn't parse '" + styleDirectory +"'", e ) ;
}
resourceLoader = new CompositeResourceLoader( urlResourceLoader, classpathResourceLoader ) ;
} else {
resourceLoader = classpathResourceLoader;
}
final FopFactory fopFactory;
try {
fopFactory = FopTools.createFopFactory( null, null ) ;
} catch( FOPException e ) {
throw new MojoExecutionException( "Problem when configuring FOP", e ) ;
}
final RenderingConfiguration renderingConfiguration = new RenderingConfiguration() {
@Override
public ResourceLoader getResourceLoader() {
return resourceLoader ;
}
@Override
public FopFactory getFopFactory() {
return fopFactory ;
}
@Override
public FopFontStatus getCurrentFopFontStatus() {
return null;
}
@Override
public Charset getDefaultCharset() {
return DefaultCharset.RENDERING ;
}
@Override
public RenditionKinematic getRenderingKinematic() {
return RenditionKinematic.BATCH ;
}
} ;
final ContentConfiguration contentConfiguration = new ContentConfiguration() {
@Override
public File getContentRoot() {
return workingDirectory ;
}
@Override
public Charset getSourceCharset() {
return DefaultCharset.SOURCE ;
}
} ;
final ExecutorService executorService = Executors.newSingleThreadExecutor(
ConfigurationTools.getExecutorThreadFactory() ) ;
final ProducerConfiguration producerConfiguration = new ProducerConfiguration() {
@Override
public RenderingConfiguration getRenderingConfiguration() {
return renderingConfiguration ;
}
@Override
public ContentConfiguration getContentConfiguration() {
return contentConfiguration ;
}
@Override
public ExecutorService getExecutorService() {
return executorService ;
}
} ;
return producerConfiguration;
}
}