package org.jboss.windup.reporting.rules.rendering; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.jboss.forge.furnace.Furnace; import org.jboss.windup.config.AbstractRuleProvider; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.Variables; import org.jboss.windup.config.loader.RuleLoaderContext; import org.jboss.windup.config.metadata.RuleMetadata; import org.jboss.windup.config.operation.GraphOperation; import org.jboss.windup.config.operation.Iteration; import org.jboss.windup.config.operation.IterationProgress; import org.jboss.windup.config.operation.iteration.AbstractIterationOperation; import org.jboss.windup.config.phase.ReportRenderingPhase; import org.jboss.windup.config.query.Query; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.graph.model.resource.ReportResourceFileModel; import org.jboss.windup.reporting.freemarker.FreeMarkerIterationOperation; import org.jboss.windup.reporting.model.ReportModel; import org.jboss.windup.reporting.model.TemplateType; import org.jboss.windup.reporting.service.ReportService; import org.jboss.windup.util.Logging; import org.jboss.windup.util.exception.WindupException; import org.jboss.windup.util.threading.WindupExecutors; import org.ocpsoft.rewrite.config.Configuration; import org.ocpsoft.rewrite.config.ConfigurationBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; /** * This renders the ApplicationReport, along with all of its subapplications via freemarker. * * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> * */ @RuleMetadata(phase = ReportRenderingPhase.class) public class RenderReportRuleProvider extends AbstractRuleProvider { private static final Logger LOG = Logging.get(RenderReportRuleProvider.class); @Inject private Furnace furnace; // @formatter:off @Override public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext) { return ConfigurationBuilder .begin() .addRule() .when(Query.fromType(ReportModel.class).withProperty(ReportModel.TEMPLATE_TYPE, TemplateType.FREEMARKER.toString())) .perform(new FreeMarkerThreadedRenderer(furnace)) .addRule() .when(Query.fromType(ReportResourceFileModel.class)) .perform(new AbstractIterationOperation<ReportResourceFileModel>() { @Override public void perform(GraphRewrite event, EvaluationContext context, ReportResourceFileModel payload) { ReportService reportService = new ReportService(event.getGraphContext()); Path outputDir = reportService.getReportDirectory(); File directory = outputDir.toFile(); File fullPath = new File(directory, FilenameUtils.separatorsToSystem("resources/" + payload.getPrettyPath())); try { FileUtils.forceMkdir(fullPath.getParentFile()); FileUtils.copyFile(payload.asFile(), fullPath); LOG.info("Copied raw file: " + payload.getFilePath() + " to: " + fullPath.getAbsolutePath()); } catch (IOException e) { LOG.warning("IOException creating file: " + fullPath.getAbsolutePath() + ", due to: " + e.getMessage()); } } }); } // @formatter:on private class FreeMarkerThreadedRenderer extends GraphOperation { private final Furnace furnace; public FreeMarkerThreadedRenderer(Furnace furnace) { this.furnace = furnace; } @Override public void perform(final GraphRewrite event, final EvaluationContext context) { Iterable<? extends WindupVertexFrame> reportModelsIterable = Variables.instance(event) .findVariable(Iteration.DEFAULT_VARIABLE_LIST_STRING); final Queue<WindupVertexFrame> reportModels = new ConcurrentLinkedDeque<>(); for (WindupVertexFrame frame : reportModelsIterable) reportModels.add(frame); final FreeMarkerIterationOperation freeMarkerIterationOperation = FreeMarkerIterationOperation.create(furnace); final IterationProgress iterationProgress = IterationProgress.monitoring("Rendering Reports", 100); int threadCount = WindupExecutors.getDefaultThreadCount(); ExecutorService executorService = WindupExecutors.newFixedThreadPool(WindupExecutors.getDefaultThreadCount()); // Set the frames as iteration progress uses this event.getRewriteContext().put(Iteration.DEFAULT_VARIABLE_LIST_STRING, reportModels); iterationProgress.perform(event, context); for (int i = 0; i < threadCount; i++) { executorService.submit(new Callable<Void>() { @Override public Void call() throws Exception { while (true) { final ReportModel reportModel; WindupVertexFrame reportModelObject = reportModels.remove(); if (reportModelObject == null) return null; reportModel = (ReportModel) reportModelObject; try { Thread.currentThread().setName(reportModel.getTemplatePath() + "_" + reportModel.getReportFilename()); iterationProgress.perform(event, context); freeMarkerIterationOperation.perform(event, context, reportModel); } catch (Throwable t) { LOG.log(Level.WARNING, "Failed to render freemarker report:\n " + reportModel + "\n " + t.getMessage(), t); } } } }); } executorService.shutdown(); try { executorService.awaitTermination(2, TimeUnit.DAYS); } catch (InterruptedException e) { throw new WindupException("Failed to render reports due to a timeout: " + e.getMessage(), e); } // reset them event.getRewriteContext().put(Iteration.DEFAULT_VARIABLE_LIST_STRING, null); } } }