diff --git a/build.gradle b/build.gradle index 8d74752084..a8143365f9 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,8 @@ allprojects { apply plugin: 'project-report' apply plugin: 'checkstyle' + jar.dependsOn(checkstyleMain, findbugsMain) + repositories { mavenLocal() maven {url "http://download.osgeo.org/webdav/geotools"} @@ -50,7 +52,7 @@ configurations { project.ext.junitVersion = "4.11" dependencies { - def springVersion = "3.1.0.RELEASE" + def springVersion = "4.0.2.RELEASE" def metricsVersion = "3.0.2" compile ( "org.springframework:spring-context:$springVersion", @@ -70,6 +72,11 @@ dependencies { "com.codahale.metrics:metrics-logback:$metricsVersion", 'org.springframework:spring-webmvc:4.0.2.RELEASE') + + // This jsr166 dependendency is here only so long as Java 6 is supported + // it is the forkjoin code and can be removed if java 7 is a required library + compile ('org.codehaus.jsr166-mirror:jsr166:1.7.0') + compile ('org.apache.xmlgraphics:batik-transcoder:1.7'){ exclude module: 'fop' } diff --git a/examples/config.yaml b/examples/config.yaml index daa4536433..c25037fa33 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -28,13 +28,13 @@ hosts: port: 80 security: - - !basicAuth - matcher: !dnsMatch - host: c2cpc61.camptocamp.com - port: 80 - username: xyz - password: yxz - preemptive: true +# - !basicAuth +# matcher: !dnsMatch +# host: c2cpc61.camptocamp.com +# port: 80 +# username: xyz +# password: yxz +# preemptive: true templates: A4 portrait: !template diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3c7abdf127..5838598129 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cf07ff679d..76e4ea407e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Mar 21 14:22:15 CET 2014 +#Mon Mar 24 09:09:03 CET 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip diff --git a/gradlew b/gradlew index 9a3acf2bf2..91a7e269e1 100755 --- a/gradlew +++ b/gradlew @@ -7,7 +7,7 @@ ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="-XX:MaxPermSize=128m -Xmx512M" +DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` diff --git a/gradlew.bat b/gradlew.bat index 6e4992a36e..8a0b282aa6 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -9,7 +9,7 @@ if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS=-XX:MaxPermSize=128m -Xmx512M +set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. diff --git a/output.pdf b/output.pdf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/mapfish/print/config/BasicAuthSecurity.java b/src/main/java/org/mapfish/print/config/BasicAuthSecurity.java deleted file mode 100644 index dfbd147462..0000000000 --- a/src/main/java/org/mapfish/print/config/BasicAuthSecurity.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 Camptocamp - * - * This file is part of MapFish Print - * - * MapFish Print is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MapFish Print 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 MapFish Print. If not, see . - */ - -package org.mapfish.print.config; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import org.apache.commons.httpclient.auth.AuthScope; - -import java.net.URI; - -/** - * Authenticate using basic auth. - * - * @author Jesse - */ -public class BasicAuthSecurity extends SecurityStrategy implements ConfigurationObject { - - private String username = null; - private String password = null; - private boolean preemptive = false; - - @Override - public final void configure(final URI uri, final HttpClient httpClient) { - if (this.username == null || this.password == null) { - throw new IllegalStateException("username and password configuration of BasicAuthSecurity is required"); - } - - if (this.preemptive) { - httpClient.getParams().setAuthenticationPreemptive(true); - } - httpClient.getState().setCredentials(new AuthScope(uri.getHost(), uri.getPort()), - new UsernamePasswordCredentials(this.username, this.password)); - } - - public final void setUsername(final String username) { - this.username = username; - } - - public final void setPassword(final String password) { - this.password = password; - } - - public final void setPreemptive(final boolean preemptive) { - this.preemptive = preemptive; - } -} diff --git a/src/main/java/org/mapfish/print/config/ConfigurationFactory.java b/src/main/java/org/mapfish/print/config/ConfigurationFactory.java index 5088b44ad9..0242f5db19 100644 --- a/src/main/java/org/mapfish/print/config/ConfigurationFactory.java +++ b/src/main/java/org/mapfish/print/config/ConfigurationFactory.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.Map; - import javax.annotation.PostConstruct; /** diff --git a/src/main/java/org/mapfish/print/config/InetHostMatcher.java b/src/main/java/org/mapfish/print/config/InetHostMatcher.java index 74fa59c704..b406733f48 100644 --- a/src/main/java/org/mapfish/print/config/InetHostMatcher.java +++ b/src/main/java/org/mapfish/print/config/InetHostMatcher.java @@ -20,7 +20,6 @@ package org.mapfish.print.config; import com.google.common.base.Optional; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/mapfish/print/config/SecurityStrategy.java b/src/main/java/org/mapfish/print/config/SecurityStrategy.java index e757f61a16..6e9a9b3f01 100644 --- a/src/main/java/org/mapfish/print/config/SecurityStrategy.java +++ b/src/main/java/org/mapfish/print/config/SecurityStrategy.java @@ -19,7 +19,7 @@ package org.mapfish.print.config; -import org.apache.commons.httpclient.HttpClient; +import org.apache.http.client.methods.HttpRequestBase; import java.io.IOException; import java.net.URI; @@ -33,10 +33,10 @@ public abstract class SecurityStrategy implements ConfigurationObject { /** * Configure security of the http client for the uri. * - * @param uri uri of request + * @param uri uri of request * @param httpClient http client which will make request. */ - public abstract void configure(URI uri, HttpClient httpClient); + public abstract void configure(URI uri, HttpRequestBase httpClient); /** * Return true if this strategy can be used for the provided URI. @@ -54,6 +54,7 @@ public final boolean matches(final URI uri) { /** * Set the matching strategy for determining if this strategy can be used to secure a give URL. + * * @param matcher the matcher. */ public final void setMatcher(final HostMatcher matcher) { diff --git a/src/main/java/org/mapfish/print/config/Template.java b/src/main/java/org/mapfish/print/config/Template.java index 0c870f397d..c4e9b50244 100644 --- a/src/main/java/org/mapfish/print/config/Template.java +++ b/src/main/java/org/mapfish/print/config/Template.java @@ -23,10 +23,13 @@ import org.json.JSONWriter; import org.mapfish.print.attribute.Attribute; import org.mapfish.print.processor.Processor; +import org.mapfish.print.processor.ProcessorDependencyGraph; +import org.mapfish.print.processor.ProcessorDependencyGraphFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.annotation.Nonnull; /** * Represents a report template configuration. @@ -43,6 +46,8 @@ public class Template implements ConfigurationObject { private String jdbcUrl; private String jdbcUser; private String jdbcPassword; + private volatile ProcessorDependencyGraph processorGraph; + private volatile ProcessorDependencyGraph iterProcessorGraph; /** * Print out the template information that the client needs for performing a request. @@ -77,10 +82,6 @@ public final void setJasperTemplate(final String jasperTemplate) { this.jasperTemplate = jasperTemplate; } - public final List getProcessors() { - return this.processors; - } - public final void setProcessors(final List processors) { this.processors = processors; } @@ -124,4 +125,40 @@ public final String getJdbcPassword() { public final void setJdbcPassword(final String jdbcPassword) { this.jdbcPassword = jdbcPassword; } + + /** + * Get the processor graph to use for executing all the processors for the template. + * + * @param factory a factory for creating graphs. + * + * @return the processor graph. + */ + public final ProcessorDependencyGraph getProcessorGraph(@Nonnull final ProcessorDependencyGraphFactory factory) { + if (this.processorGraph == null) { + synchronized (this) { + if (this.processorGraph == null) { + this.processorGraph = factory.build(this.processors); + } + } + } + return this.processorGraph; + } + + /** + * Get the processor graph to use for executing all the iter processors for the template. + * + * @param factory a factory for creating graphs. + * + * @return the processor graph. + */ + public final ProcessorDependencyGraph getIterProcessorGraph(@Nonnull final ProcessorDependencyGraphFactory factory) { + if (this.iterProcessorGraph == null) { + synchronized (this) { + if (this.iterProcessorGraph == null) { + this.iterProcessorGraph = factory.build(this.iterProcessors); + } + } + } + return this.iterProcessorGraph; + } } diff --git a/src/main/java/org/mapfish/print/metrics/HealthCheckRegistryContextListener.java b/src/main/java/org/mapfish/print/metrics/HealthCheckRegistryContextListener.java index 1622b053f2..ff99c7228d 100644 --- a/src/main/java/org/mapfish/print/metrics/HealthCheckRegistryContextListener.java +++ b/src/main/java/org/mapfish/print/metrics/HealthCheckRegistryContextListener.java @@ -19,10 +19,8 @@ package org.mapfish.print.metrics; -import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.health.HealthCheckRegistry; import com.codahale.metrics.servlets.HealthCheckServlet; -import com.codahale.metrics.servlets.MetricsServlet; import org.springframework.web.context.WebApplicationContext; import javax.servlet.ServletContext; diff --git a/src/main/java/org/mapfish/print/metrics/InstrumentedFilterContextListener.java b/src/main/java/org/mapfish/print/metrics/MapfishPrintInstrumentedFilterContextListener.java similarity index 87% rename from src/main/java/org/mapfish/print/metrics/InstrumentedFilterContextListener.java rename to src/main/java/org/mapfish/print/metrics/MapfishPrintInstrumentedFilterContextListener.java index 44ddd60029..8dbce4af22 100644 --- a/src/main/java/org/mapfish/print/metrics/InstrumentedFilterContextListener.java +++ b/src/main/java/org/mapfish/print/metrics/MapfishPrintInstrumentedFilterContextListener.java @@ -20,8 +20,7 @@ package org.mapfish.print.metrics; import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.health.HealthCheckRegistry; -import com.codahale.metrics.servlets.HealthCheckServlet; +import com.codahale.metrics.servlet.InstrumentedFilterContextListener; import org.springframework.web.context.WebApplicationContext; import javax.servlet.ServletContext; @@ -31,10 +30,10 @@ /** * Allows the AdminServlet to get access to the MetricRegistry so it can display the statistics via the admin servlet. - * + *

* Created by Jesse on 3/21/2014. */ -public class InstrumentedFilterContextListener extends com.codahale.metrics.servlet.InstrumentedFilterContextListener { +public class MapfishPrintInstrumentedFilterContextListener extends InstrumentedFilterContextListener { private ServletContext servletContext; @Override diff --git a/src/main/java/org/mapfish/print/metrics/MetricsRegistryContextListener.java b/src/main/java/org/mapfish/print/metrics/MetricsRegistryContextListener.java index 0e9353caaa..14b8e6b3f7 100644 --- a/src/main/java/org/mapfish/print/metrics/MetricsRegistryContextListener.java +++ b/src/main/java/org/mapfish/print/metrics/MetricsRegistryContextListener.java @@ -25,11 +25,6 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionEvent; -import javax.servlet.http.HttpSessionListener; -import javax.servlet.http.HttpSessionBindingEvent; import static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext; /** diff --git a/src/main/java/org/mapfish/print/output/JasperReportOutputFormat.java b/src/main/java/org/mapfish/print/output/JasperReportOutputFormat.java index 9f0aaf6429..9a2c27bfd0 100644 --- a/src/main/java/org/mapfish/print/output/JasperReportOutputFormat.java +++ b/src/main/java/org/mapfish/print/output/JasperReportOutputFormat.java @@ -25,15 +25,16 @@ import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; - import org.mapfish.print.Constants; import org.mapfish.print.attribute.Attribute; import org.mapfish.print.config.Configuration; import org.mapfish.print.config.Template; import org.mapfish.print.json.PJsonObject; import org.mapfish.print.processor.Processor; +import org.mapfish.print.processor.ProcessorDependencyGraphFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import java.io.File; import java.io.OutputStream; @@ -41,14 +42,10 @@ import java.sql.DriverManager; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ForkJoinPool; /** * An PDF output format that uses Jasper reports to generate the result. @@ -59,22 +56,11 @@ public class JasperReportOutputFormat implements OutputFormat { @SuppressWarnings("unused") private static final Logger LOGGER = LoggerFactory.getLogger(JasperReportOutputFormat.class); + @Autowired + private ForkJoinPool forkJoinPool; + @Autowired + private ProcessorDependencyGraphFactory processorGraphFactory; - private static final int WAIT_TIME = 200; - private static final int DEFAULT_THREAD_NUMBER = 4; - - /** - * The used number of threads. - */ - private int threadNumber = DEFAULT_THREAD_NUMBER; - - /** - * Set the used number of threads. - * @param threadNumber The number of thread. - */ - public final void setThreadNumber(final int threadNumber) { - this.threadNumber = threadNumber; - } @Override public final String getContentType() { @@ -108,13 +94,13 @@ public final void print(final PJsonObject spec, final Configuration config, fina } - runProcessors(template.getProcessors(), values); + this.forkJoinPool.invoke(template.getProcessorGraph(this.processorGraphFactory).createTask(values)); if (template.getIterValue() != null) { List> dataSource = new ArrayList>(); Iterable iter = values.getIterator(template.getIterValue()); for (Values iterValues : iter) { - runProcessors(template.getIterProcessors(), iterValues); + this.forkJoinPool.invoke(template.getIterProcessorGraph(this.processorGraphFactory).createTask(values)); dataSource.add(iterValues.getParameters()); } final JRDataSource jrDataSource = new JRMapCollectionDataSource(dataSource); @@ -147,62 +133,6 @@ public final void print(final PJsonObject spec, final Configuration config, fina } } - private void runProcessors(final List processors, final Values values) { - // build the processor dependencies - final Map provideBy = new HashMap(); - final Map> required = new ConcurrentHashMap>(); - final Queue ready = new ConcurrentLinkedQueue(); - for (Processor process : processors) { - List req = new CopyOnWriteArrayList(); - for (String value : process.getInputMapper().keySet()) { - if (provideBy.containsKey(value)) { - req.add(provideBy.get(value)); - } - } - if (req.isEmpty()) { - ready.add(process); - } else { - required.put(process, req); - } - for (String value : process.getOutputMapper().values()) { - provideBy.put(value, process); - } - } - - final Set threads = new HashSet(); - final Map runs = new HashMap(); - for (int i = 0; i < this.threadNumber; i++) { - ProcessRun processRun = new ProcessRun(required, ready, values); - Thread thread = new Thread(processRun); - threads.add(thread); - runs.put(thread, processRun); - thread.start(); - } - while (true) { - for (Thread thread : threads) { - if (!thread.isAlive()) { - threads.remove(thread); - ProcessRun processRun = runs.get(thread); - runs.remove(thread); - if (processRun.errored) { - for (ProcessRun pr : runs.values()) { - pr.stopped = true; - } - throw new RuntimeException("Error while running a Processor, seel log for details"); - } - break; - } - } - if (threads.isEmpty()) { - break; // finish - } - try { - Thread.sleep(WAIT_TIME); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } private void runProcess(final Processor process, final Values values) throws Exception { Map input = new HashMap(); @@ -224,6 +154,7 @@ private void runProcess(final Processor process, final Values values) throws Exc /** * The runnable that process the prossessor. + * * @author sbrunner */ private class ProcessRun implements Runnable { @@ -236,7 +167,7 @@ private class ProcessRun implements Runnable { private static final int STEEP_TIME = 1000; public ProcessRun(final Map> required, - final Queue ready, final Values values) { + final Queue ready, final Values values) { this.required = required; this.ready = ready; this.values = values; diff --git a/src/main/java/org/mapfish/print/output/Values.java b/src/main/java/org/mapfish/print/output/Values.java index 339db94edb..5d694d95de 100644 --- a/src/main/java/org/mapfish/print/output/Values.java +++ b/src/main/java/org/mapfish/print/output/Values.java @@ -19,8 +19,8 @@ package org.mapfish.print.output; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Values that go into a processor from previous processors in the processor processing graph. @@ -28,7 +28,7 @@ * */ public class Values { - private final Map values = new HashMap(); + private final Map values = new ConcurrentHashMap(); /** * Constructor. @@ -52,7 +52,7 @@ public Values() { * @param key id of the value for looking up. * @param value the value. */ - protected final void put(final String key, final Object value) { + public final void put(final String key, final Object value) { this.values.put(key, value); } diff --git a/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraph.java b/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraph.java new file mode 100644 index 0000000000..e5cc4c95b0 --- /dev/null +++ b/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraph.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Camptocamp + * + * This file is part of MapFish Print + * + * MapFish Print is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MapFish Print 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 MapFish Print. If not, see . + */ + +package org.mapfish.print.processor; + +import org.mapfish.print.output.Values; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveTask; +import javax.annotation.Nonnull; + +/** + * Represents a graph of the processors dependencies. The root nodes can execute in parallel but processors with + * dependencies must wait for their dependencies to complete before execution. + *

+ * Created by Jesse on 3/24/14. + */ +public final class ProcessorDependencyGraph { + private final List roots; + + ProcessorDependencyGraph() { + this.roots = new ArrayList(); + } + + /** + * Create a ForkJoinTask for running in a fork join pool. + * + * @param values the values to use for getting the required inputs of the processor and putting the output in. + * @return a task ready to be submitted to a fork join pool. + */ + public ProcessorGraphForkJoinTask createTask(@Nonnull final Values values) { + return new ProcessorGraphForkJoinTask(values); + } + + + void addRoot(final ProcessorGraphNode node) { + this.roots.add(node); + } + + /** + * A ForkJoinTask that will create ForkJoinTasks from each root and run each of them. + */ + public final class ProcessorGraphForkJoinTask extends RecursiveTask { + private final Values values; + + private ProcessorGraphForkJoinTask(@Nonnull final Values values) { + this.values = values; + } + + @Override + protected Values compute() { + final ProcessorDependencyGraph graph = ProcessorDependencyGraph.this; + + final List dependencyNodes = graph.roots; + if (!dependencyNodes.isEmpty()) { + List> tasks = new ArrayList>(dependencyNodes.size()); + + // fork all but 1 dependencies (the first will be ran in current thread) + for (int i = 1; i < dependencyNodes.size(); i++) { + final ForkJoinTask task = dependencyNodes.get(i).createTask(this.values); + tasks.add(task); + task.fork(); + } + + // compute one task in current thread so as not to waste threads + dependencyNodes.get(0).createTask(this.values).compute(); + + for (ForkJoinTask task : tasks) { + task.join(); + } + } + + return this.values; + } + } +} diff --git a/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraphFactory.java b/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraphFactory.java new file mode 100644 index 0000000000..639b6de12a --- /dev/null +++ b/src/main/java/org/mapfish/print/processor/ProcessorDependencyGraphFactory.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 Camptocamp + * + * This file is part of MapFish Print + * + * MapFish Print is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MapFish Print 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 MapFish Print. If not, see . + */ + +package org.mapfish.print.processor; + +import com.codahale.metrics.MetricRegistry; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class for constructing {@link org.mapfish.print.processor.ProcessorDependencyGraph} instances. + * + * Created by Jesse on 3/24/14. + */ +public final class ProcessorDependencyGraphFactory { + + @Autowired + private MetricRegistry metricRegistry; + + + /** + * Create a {@link ProcessorDependencyGraph}. + * + * @param processors the processors that will be part of the graph + * @return a {@link org.mapfish.print.processor.ProcessorDependencyGraph} constructed from the passed in processors + */ + public ProcessorDependencyGraph build(final List processors) { + ProcessorDependencyGraph graph = new ProcessorDependencyGraph(); + + final Map provideBy = new HashMap(); + final List nodes = new ArrayList(processors.size()); + + for (Processor processor : processors) { + final Map outputMapper = processor.getOutputMapper(); + + for (String value : outputMapper.values()) { + if (provideBy.containsKey(value)) { + throw new IllegalStateException("Multiple processors provide the same output value: '" + processor + "' and '" + + provideBy.get(value) + "' both provide: '" + value + "'"); + } + + final ProcessorGraphNode node = new ProcessorGraphNode(processor, this.metricRegistry); + nodes.add(node); + provideBy.put(value, node); + } + } + + for (ProcessorGraphNode node : nodes) { + if (node.getProcessor().getInputMapper().isEmpty()) { + graph.addRoot(node); + } else { + for (String requiredKey : node.getProcessor().getInputMapper().keySet()) { + final ProcessorGraphNode solution = provideBy.get(requiredKey); + solution.addDependency(node); + } + } + } + return graph; + } + +} diff --git a/src/main/java/org/mapfish/print/processor/ProcessorGraphNode.java b/src/main/java/org/mapfish/print/processor/ProcessorGraphNode.java new file mode 100644 index 0000000000..6acfd48357 --- /dev/null +++ b/src/main/java/org/mapfish/print/processor/ProcessorGraphNode.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Camptocamp + * + * This file is part of MapFish Print + * + * MapFish Print is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MapFish Print 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 MapFish Print. If not, see . + */ + +package org.mapfish.print.processor; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import org.mapfish.print.output.Values; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.RecursiveTask; +import javax.annotation.Nonnull; + +/** + * Represents one node in the Processor dependency graph ({@link ProcessorDependencyGraph}). + *

+ * Created by Jesse on 3/24/14. + */ +public class ProcessorGraphNode { + private static final Logger LOGGER = LoggerFactory.getLogger(ProcessorGraphNode.class); + private final Processor processor; + private final List dependencies = new ArrayList(); + private final MetricRegistry metricRegistry; + + /** + * Constructor. + * + * @param processor The processor associated with this node. + * @param metricRegistry registry for timing the execution time of the processor. + */ + public ProcessorGraphNode(@Nonnull final Processor processor, @Nonnull final MetricRegistry metricRegistry) { + this.processor = processor; + this.metricRegistry = metricRegistry; + } + + public final Processor getProcessor() { + return this.processor; + } + + /** + * Add a depedendcy to this node. + * + * @param node the dependency to add. + */ + public final void addDependency(final ProcessorGraphNode node) { + this.dependencies.add(node); + } + + /** + * Create a ForkJoinTask for running in a fork join pool. + * + * @param values the values to use for getting the required inputs of the processor and putting the output in. + * @return a task ready to be submitted to a fork join pool. + */ + public final ProcessorNodeForkJoinTask createTask(@Nonnull final Values values) { + return new ProcessorNodeForkJoinTask(values); + } + + /** + * A ForkJoinTask that will run the processor and all of its dependencies. + */ + public final class ProcessorNodeForkJoinTask extends RecursiveTask { + private final Values values; + + private ProcessorNodeForkJoinTask(final Values values) { + this.values = values; + } + + @Override + protected Values compute() { + final Processor process = ProcessorGraphNode.this.processor; + final MetricRegistry registry = ProcessorGraphNode.this.metricRegistry; + Timer.Context timerContext = registry.timer(ProcessorGraphNode.class.getName() + "_compute():" + + process.getClass()).time(); + try { + Map input = new HashMap(); + Map inputMap = process.getInputMapper(); + for (String value : inputMap.keySet()) { + input.put( + inputMap.get(value), + this.values.getObject(value, Object.class)); + } + + Map output; + try { + output = process.execute(input); + } catch (Exception e) { + throw new RuntimeException(e); + } + Map outputMap = process.getOutputMapper(); + for (String value : outputMap.keySet()) { + this.values.put( + outputMap.get(value), + output.get(value)); + } + + executeDependencyProcessors(); + + return this.values; + } finally { + final long processorTime = timerContext.stop(); + LOGGER.debug("Time taken to run processor: '" + process.getClass() + "' was " + processorTime + " ms"); + } + } + + private void executeDependencyProcessors() { + final List dependencyNodes = ProcessorGraphNode.this.dependencies; + if (!dependencyNodes.isEmpty()) { + List tasks = new ArrayList(dependencyNodes.size()); + + // fork all but 1 dependencies (the first will be ran in current thread) + for (int i = 1; i < dependencyNodes.size(); i++) { + final ProcessorNodeForkJoinTask task = dependencyNodes.get(i).createTask(this.values); + tasks.add(task); + task.fork(); + } + + // compute one task in current thread so as not to waste threads + dependencyNodes.get(0).createTask(this.values).compute(); + + for (ProcessorNodeForkJoinTask task : tasks) { + task.join(); + } + } + } + } +} diff --git a/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java b/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java index 79a49d2853..365b8bf017 100644 --- a/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java +++ b/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java @@ -20,7 +20,6 @@ package org.mapfish.print.processor.jasper; import net.sf.jasperreports.engine.data.JRTableModelDataSource; - import org.mapfish.print.attribute.LegendAttribute.LegendAttributeValue; import org.mapfish.print.json.PJsonArray; import org.mapfish.print.json.PJsonObject; @@ -33,7 +32,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.imageio.ImageIO; /** diff --git a/src/main/java/org/mapfish/print/processor/jasper/TableListProcessor.java b/src/main/java/org/mapfish/print/processor/jasper/TableListProcessor.java index 4802acf959..378e841b69 100644 --- a/src/main/java/org/mapfish/print/processor/jasper/TableListProcessor.java +++ b/src/main/java/org/mapfish/print/processor/jasper/TableListProcessor.java @@ -29,10 +29,8 @@ import ar.com.fdvs.dj.domain.constants.Font; import ar.com.fdvs.dj.domain.constants.HorizontalAlign; import ar.com.fdvs.dj.domain.constants.VerticalAlign; - import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; - import org.mapfish.print.attribute.TableListAttribute.TableListAttributeValue; import org.mapfish.print.json.PJsonArray; import org.mapfish.print.json.PJsonObject; diff --git a/src/main/java/org/mapfish/print/processor/jasper/TableProcessor.java b/src/main/java/org/mapfish/print/processor/jasper/TableProcessor.java index 118963e5e9..a9a4fa8abd 100644 --- a/src/main/java/org/mapfish/print/processor/jasper/TableProcessor.java +++ b/src/main/java/org/mapfish/print/processor/jasper/TableProcessor.java @@ -20,7 +20,6 @@ package org.mapfish.print.processor.jasper; import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; - import org.mapfish.print.attribute.TableAttribute.TableAttributeValue; import org.mapfish.print.json.PJsonArray; import org.mapfish.print.json.PJsonObject; diff --git a/src/main/java/org/mapfish/print/servlet/BaseMapServlet.java b/src/main/java/org/mapfish/print/servlet/BaseMapServlet.java index 2369874bb1..b8f41a7974 100644 --- a/src/main/java/org/mapfish/print/servlet/BaseMapServlet.java +++ b/src/main/java/org/mapfish/print/servlet/BaseMapServlet.java @@ -28,7 +28,6 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; - import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/src/main/java/org/mapfish/print/servlet/MapPrinterServlet.java b/src/main/java/org/mapfish/print/servlet/MapPrinterServlet.java index fb5686ece0..bfb06bc756 100644 --- a/src/main/java/org/mapfish/print/servlet/MapPrinterServlet.java +++ b/src/main/java/org/mapfish/print/servlet/MapPrinterServlet.java @@ -21,7 +21,6 @@ import com.google.common.base.Optional; import com.google.common.collect.Lists; - import org.json.JSONException; import org.json.JSONWriter; import org.mapfish.print.MapPrinter; @@ -54,7 +53,6 @@ import java.util.Enumeration; import java.util.List; import java.util.UUID; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/src/main/java/org/mapfish/print/servlet/OldAPIMapPrinterServlet.java b/src/main/java/org/mapfish/print/servlet/OldAPIMapPrinterServlet.java index cf02ecde62..b503ffaead 100644 --- a/src/main/java/org/mapfish/print/servlet/OldAPIMapPrinterServlet.java +++ b/src/main/java/org/mapfish/print/servlet/OldAPIMapPrinterServlet.java @@ -20,7 +20,6 @@ package org.mapfish.print.servlet; import com.google.common.io.ByteStreams; - import org.json.JSONException; import org.json.JSONWriter; import org.mapfish.print.Constants; @@ -52,7 +51,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/src/main/java/org/mapfish/print/servlet/ServletMapPrinterFactory.java b/src/main/java/org/mapfish/print/servlet/ServletMapPrinterFactory.java index c14eb4e356..10d4ffca17 100644 --- a/src/main/java/org/mapfish/print/servlet/ServletMapPrinterFactory.java +++ b/src/main/java/org/mapfish/print/servlet/ServletMapPrinterFactory.java @@ -30,7 +30,6 @@ import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; - import javax.annotation.Nullable; import javax.servlet.Servlet; import javax.servlet.ServletConfig; diff --git a/src/main/java/org/mapfish/print/servlet/job/FilePrintJob.java b/src/main/java/org/mapfish/print/servlet/job/FilePrintJob.java index 5159a436aa..8db01ad020 100644 --- a/src/main/java/org/mapfish/print/servlet/job/FilePrintJob.java +++ b/src/main/java/org/mapfish/print/servlet/job/FilePrintJob.java @@ -24,7 +24,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; - import javax.annotation.PostConstruct; /** diff --git a/src/main/java/org/mapfish/print/servlet/job/ThreadPoolJobManager.java b/src/main/java/org/mapfish/print/servlet/job/ThreadPoolJobManager.java index cf3652247f..c49f7c27f9 100644 --- a/src/main/java/org/mapfish/print/servlet/job/ThreadPoolJobManager.java +++ b/src/main/java/org/mapfish/print/servlet/job/ThreadPoolJobManager.java @@ -20,7 +20,6 @@ package org.mapfish.print.servlet.job; import com.google.common.base.Optional; - import org.json.JSONException; import org.mapfish.print.servlet.registry.Registry; import org.springframework.beans.factory.annotation.Autowired; @@ -36,7 +35,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; - import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; diff --git a/src/main/resources/mapfish-spring-application-context.xml b/src/main/resources/mapfish-spring-application-context.xml index 874d376d8e..70677b69da 100644 --- a/src/main/resources/mapfish-spring-application-context.xml +++ b/src/main/resources/mapfish-spring-application-context.xml @@ -24,16 +24,46 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -45,7 +75,6 @@ - @@ -53,27 +82,4 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index a0597fb49b..f3058ff356 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -33,7 +33,7 @@ - org.mapfish.print.metrics.InstrumentedFilterContextListener + org.mapfish.print.metrics.MapfishPrintInstrumentedFilterContextListener