From b18f9230155b51710e1c940a06e471da904da7af Mon Sep 17 00:00:00 2001 From: Jhonny Mertz Date: Sun, 15 Mar 2020 11:59:12 -0300 Subject: [PATCH 1/2] improving docs and configurations --- CONTRIBUTING.md | 10 ++++++++++ README.md | 2 ++ pom.xml | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8eb07ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +Requirements +------------ +**[wkhtmltopdf](http://wkhtmltopdf.org/) must be installed and working on your system.** + +Running tests +------------ + +This repo has two kinds of tests: integration and unit test. + +Use `mvn test -B` to run only the unit tests. diff --git a/README.md b/README.md index ad15176..f30b164 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ Known issues **Output of wkhtmltopdf is being added to resulting pdf** ([Issue #19](https://github.com/jhonnymertz/java-wkhtmltopdf-wrapper/issues/19)) - Starting from 1.1.10-RELEASE version, there is a method `saveAsDirect(String path)`, which executes wkhtmltopdf passing the `path` as output for wkhtmltopdf, instead of the standard input `-`. This saves the results directly to the specified file `path`. +**Because this library relies on `wkhtmltopdf`, it does not support concurrent PDF generations.** + License ------------ This project is available under MIT Licence. diff --git a/pom.xml b/pom.xml index bd8f317..932d7c0 100644 --- a/pom.xml +++ b/pom.xml @@ -39,8 +39,8 @@ - 1.7 - 1.7 + 1.8 + 1.8 UTF-8 From 2330c91287047279a081e03db8a75a8e32be7076 Mon Sep 17 00:00:00 2001 From: Jhonny Mertz Date: Sun, 15 Mar 2020 12:43:57 -0300 Subject: [PATCH 2/2] fixing #52 --- README.md | 19 +++++--- .../jhonnymertz/wkhtmltopdf/wrapper/Pdf.java | 45 ++++++++++++++----- .../configurations/FilenameFilterConfig.java | 14 ++++++ .../wrapper/configurations/WrapperConfig.java | 38 ++++++++-------- .../wrapper/configurations/XvfbConfig.java | 2 +- .../WkhtmltopdfConfigurationException.java | 2 +- .../wkhtmltopdf/wrapper/page/Page.java | 8 ++-- .../wkhtmltopdf/wrapper/params/Params.java | 7 +-- .../integration/PdfIntegrationTests.java | 8 ++-- .../wkhtmltopdf/wrapper/unit/PdfTests.java | 31 ++++++++++++- src/test/resources/simplelogger.properties | 7 --- 11 files changed, 124 insertions(+), 57 deletions(-) create mode 100644 src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/FilenameFilterConfig.java diff --git a/README.md b/README.md index f30b164..7886489 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ In your `pom.xml`: com.github.jhonnymertz java-wkhtmltopdf-wrapper - 1.1.11-RELEASE + 1.1.12-RELEASE ``` Usage and Examples ------------ -``` +```java Pdf pdf = new Pdf(); pdf.addPageFromString("

Müller

"); @@ -55,7 +55,7 @@ pdf.saveAs("output.pdf"); ### Xvfb Support -``` +```java XvfbConfig xc = new XvfbConfig(); xc.addParams(new Param("--auto-servernum"), new Param("--server-num=1")); @@ -71,9 +71,9 @@ pdf.saveAs("output.pdf"); ### wkhtmltopdf exit codes wkhtmltopdf may return non-zero exit codes to denote warnings, you can now set the Pdf object to allow this: -``` -Pdf pdf = new Pdf(wc); +```java +Pdf pdf = new Pdf(); pdf.addPageFromUrl("http://www.google.com"); pdf.setAllowMissingAssets(); @@ -83,6 +83,15 @@ pdf.setSuccessValues(Arrays.asList(0, 1)); pdf.saveAs("output.pdf"); ``` +### Cleaning up temporary files + +After the PDF generation, the library automatically cleans up the temporary files created. However, there may be situations in which the `Pdf` object is created but no PDF is generated. To avoid increasing the temp folder size and having problems, you can force the deletion of all temporary files created by the library by: + +```java +Pdf pdf = new Pdf(); +pdf.cleanAllTempFiles(); +``` + This is not an official Wkhtmltopdf product ------------ This library is not an official Wkhtmltopdf product. Support is available on a best-effort basis via github issue tracking. Pull requests are welcomed. diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/Pdf.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/Pdf.java index a025c66..9af2b34 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/Pdf.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/Pdf.java @@ -1,5 +1,6 @@ package com.github.jhonnymertz.wkhtmltopdf.wrapper; +import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.FilenameFilterConfig; import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.WrapperConfig; import com.github.jhonnymertz.wkhtmltopdf.wrapper.exceptions.PDFExportException; import com.github.jhonnymertz.wkhtmltopdf.wrapper.page.Page; @@ -22,11 +23,7 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * Represents a Pdf file @@ -127,9 +124,9 @@ public void setTimeout(int timeout) { /** * wkhtmltopdf often returns 1 to indicate some assets can't be found, - * this can occur for protocol less links or in other cases. Sometimes you - * may want to reject these with an exception which is the default, but in other - * cases the PDF is fine for your needs. Call this method allow return values of 1. + * this can occur for protocol less links or in other cases. Sometimes you + * may want to reject these with an exception which is the default, but in other + * cases the PDF is fine for your needs. Call this method allow return values of 1. */ public void setAllowMissingAssets() { if (!successValues.contains(1)) { @@ -145,7 +142,7 @@ public boolean getAllowMissingAssets() { * In standard process returns 0 means "ok" and any other value is an error. However, wkhtmltopdf * uses the return value to also return warning information which you may decide to ignore (@see setAllowMissingAssets) * - * @param successValues The full list of process return values you will accept as a 'success'. + * @param successValues The full list of process return values you will accept as a 'success'. */ public void setSuccessValues(List successValues) { this.successValues = successValues; @@ -154,6 +151,7 @@ public void setSuccessValues(List successValues) { /** * Sets the temporary folder to store files during the process * Default is provided by #File.createTempFile() + * * @param tempDirectory */ public void setTempDirectory(File tempDirectory) { @@ -162,6 +160,7 @@ public void setTempDirectory(File tempDirectory) { /** * Executes the wkhtmltopdf into standard out and captures the results. + * * @param path The path to the file where the PDF will be saved. * @return * @throws IOException @@ -176,12 +175,13 @@ public File saveAs(String path) throws IOException, InterruptedException { /** * Executes the wkhtmltopdf saving the results directly to the specified file path. + * * @param path The path to the file where the PDF will be saved. * @return * @throws IOException * @throws InterruptedException */ - public File saveAsDirect(String path)throws IOException, InterruptedException { + public File saveAsDirect(String path) throws IOException, InterruptedException { File file = new File(path); outputFilename = file.getAbsolutePath(); getPDF(); @@ -202,7 +202,7 @@ public byte[] getPDF() throws IOException, InterruptedException, PDFExportExcept process.waitFor(); - if (!successValues.contains( process.exitValue() )) { + if (!successValues.contains(process.exitValue())) { byte[] errorStream = getFuture(outputStreamToByteArray); logger.error("Error while generating pdf: {}", new String(errorStream)); throw new PDFExportException(command, process.exitValue(), errorStream, getFuture(inputStreamToByteArray)); @@ -239,6 +239,8 @@ protected String[] getCommandAsArray() throws IOException { if (page.getType().equals(PageType.htmlAsString)) { //htmlAsString pages are first store into a temp file, then the location is passed as parameter to // wkhtmltopdf, this is a workaround to avoid huge commands + if (page.getFilePath() != null) + Files.deleteIfExists(Paths.get(page.getFilePath())); File temp = File.createTempFile("java-wkhtmltopdf-wrapper" + UUID.randomUUID().toString(), ".html", tempDirectory); FileUtils.writeStringToFile(temp, page.getSource(), "UTF-8"); page.setFilePath(temp.getAbsolutePath()); @@ -247,7 +249,7 @@ protected String[] getCommandAsArray() throws IOException { commandLine.add(page.getSource()); } } - commandLine.add( (null != outputFilename) ? outputFilename : STDINOUT); + commandLine.add((null != outputFilename) ? outputFilename : STDINOUT); logger.debug("Command generated: {}", commandLine.toString()); return commandLine.toArray(new String[commandLine.size()]); } @@ -268,6 +270,9 @@ private byte[] getFuture(Future future) { } } + /** + * CLeans up temporary files used to generate the wkhtmltopdf command + */ private void cleanTempFiles() { logger.debug("Cleaning up temporary files..."); for (Page page : pages) { @@ -282,8 +287,24 @@ private void cleanTempFiles() { } } + /** + * Cleans up all the files generated by the library. + */ + public void cleanAllTempFiles() { + logger.debug("Cleaning up temporary files..."); + final File folder = new File(System.getProperty("java.io.tmpdir")); + final File[] files = folder.listFiles(new FilenameFilterConfig()); + for (final File file : files) { + if (!file.delete()) { + logger.warn("Couldn't delete temp file " + file.getAbsolutePath()); + } + } + logger.debug("{} temporary files removed.", files.length); + } + /** * Gets the final wkhtmltopdf command as string + * * @return the generated command from params * @throws IOException */ diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/FilenameFilterConfig.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/FilenameFilterConfig.java new file mode 100644 index 0000000..13985b1 --- /dev/null +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/FilenameFilterConfig.java @@ -0,0 +1,14 @@ +package com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations; + +import java.io.File; +import java.io.FilenameFilter; + +/** + * Implements a filter to get files generated by the library + */ +public class FilenameFilterConfig implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return name.matches("java-wkhtmltopdf-wrapper.*"); + } +} diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/WrapperConfig.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/WrapperConfig.java index ef1d7bd..88561da 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/WrapperConfig.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/WrapperConfig.java @@ -46,25 +46,6 @@ public WrapperConfig(String wkhtmltopdfCommand) { setWkhtmltopdfCommand(wkhtmltopdfCommand); } - /** - * Gets the wkhtmltopdf command to be used while calling wkhtmltopdf - * It's default is 'wkhtmltopdf' - * - * @return the wkhtmltopdf command - */ - public String getWkhtmltopdfCommand() { - return wkhtmltopdfCommand; - } - - /** - * Sets the configuration based on a provided wkhtmltopdf command to be used - * - * @param wkhtmltopdfCommand the wkhtmltopdf command - */ - public void setWkhtmltopdfCommand(String wkhtmltopdfCommand) { - this.wkhtmltopdfCommand = wkhtmltopdfCommand; - } - /** * Attempts to find the `wkhtmltopdf` executable in the system path. * @@ -93,6 +74,25 @@ public static String findExecutable() { } } + /** + * Gets the wkhtmltopdf command to be used while calling wkhtmltopdf + * It's default is 'wkhtmltopdf' + * + * @return the wkhtmltopdf command + */ + public String getWkhtmltopdfCommand() { + return wkhtmltopdfCommand; + } + + /** + * Sets the configuration based on a provided wkhtmltopdf command to be used + * + * @param wkhtmltopdfCommand the wkhtmltopdf command + */ + public void setWkhtmltopdfCommand(String wkhtmltopdfCommand) { + this.wkhtmltopdfCommand = wkhtmltopdfCommand; + } + /** * Verify whether the Xvfb support is enabled and configured * diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/XvfbConfig.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/XvfbConfig.java index c5eb689..cae9b7e 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/XvfbConfig.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/configurations/XvfbConfig.java @@ -11,8 +11,8 @@ */ public class XvfbConfig { - private String command; private final Params params = new Params(); + private String command; public XvfbConfig() { this("xvfb-run"); diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/exceptions/WkhtmltopdfConfigurationException.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/exceptions/WkhtmltopdfConfigurationException.java index e6275bc..456efeb 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/exceptions/WkhtmltopdfConfigurationException.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/exceptions/WkhtmltopdfConfigurationException.java @@ -4,7 +4,7 @@ /** * Exception to describe and track wrapper configuration errors */ -public class WkhtmltopdfConfigurationException extends RuntimeException { +public class WkhtmltopdfConfigurationException extends RuntimeException { public WkhtmltopdfConfigurationException(String s) { super(s); diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/page/Page.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/page/Page.java index 905f065..3a36f0a 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/page/Page.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/page/Page.java @@ -27,11 +27,11 @@ public void setType(PageType type) { this.type = type; } - public void setFilePath(String filePath) { - this.filePath = filePath; - } - public String getFilePath() { return filePath; } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } } diff --git a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/params/Params.java b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/params/Params.java index 310b720..0fd1bb1 100644 --- a/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/params/Params.java +++ b/src/main/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/params/Params.java @@ -1,10 +1,11 @@ package com.github.jhonnymertz.wkhtmltopdf.wrapper.params; +import org.apache.commons.lang3.StringUtils; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import org.apache.commons.lang3.StringUtils; public class Params { @@ -16,7 +17,7 @@ public Params() { public void add(Param param, Param... params) { this.params.add(param); - this.params.addAll( Arrays.asList( params ) ); + this.params.addAll(Arrays.asList(params)); } public List getParamsAsStringList() { @@ -37,7 +38,7 @@ public List getParamsAsStringList() { @Override public String toString() { - return StringUtils.join(params, ""); + return StringUtils.join(params, ""); } } diff --git a/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/integration/PdfIntegrationTests.java b/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/integration/PdfIntegrationTests.java index 37d35a8..35b41a9 100644 --- a/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/integration/PdfIntegrationTests.java +++ b/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/integration/PdfIntegrationTests.java @@ -22,8 +22,8 @@ public void findExecutable() throws Exception { WrapperConfig wc = new WrapperConfig(); //see if executable is installed try { - wc.findExecutable(); - }catch(RuntimeException ex){ + WrapperConfig.findExecutable(); + } catch (RuntimeException ex) { Assert.fail(ex.getMessage()); } } @@ -96,12 +96,12 @@ private String getPdfTextFromBytes(byte[] pdfBytes) throws IOException { } @Test - public void CleanUpTempFilesTest(){ + public void CleanUpTempFilesTest() { Pdf pdf = new Pdf(); pdf.addPageFromString("title

TEST

"); try { pdf.getPDF(); - } catch(Exception ex){ + } catch (Exception ex) { Assert.fail(ex.getMessage()); } } diff --git a/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/unit/PdfTests.java b/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/unit/PdfTests.java index 9e7dad6..8412779 100644 --- a/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/unit/PdfTests.java +++ b/src/test/java/com/github/jhonnymertz/wkhtmltopdf/wrapper/unit/PdfTests.java @@ -1,9 +1,11 @@ package com.github.jhonnymertz.wkhtmltopdf.wrapper.unit; import com.github.jhonnymertz.wkhtmltopdf.wrapper.Pdf; +import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.FilenameFilterConfig; import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.WrapperConfig; import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.XvfbConfig; import com.github.jhonnymertz.wkhtmltopdf.wrapper.params.Param; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -25,6 +27,11 @@ public void setUp() { pdf = new Pdf(wc); } + @After + public void cleanUp() { + pdf.cleanAllTempFiles(); + } + @Test public void testParams() throws Exception { pdf.addParam(new Param("--enable-javascript"), new Param("--html-header", "file:///example.html")); @@ -33,7 +40,29 @@ public void testParams() throws Exception { } @Test - public void testTempDirectory() throws IOException { + public void testUniqueTempFileGenerationDirectory() throws IOException { + pdf.addPageFromString("

Müller

"); + pdf.getCommand(); + pdf.getCommand(); + final File dir = new File(System.getProperty("java.io.tmpdir")); + File[] files = dir.listFiles(new FilenameFilterConfig()); + Assert.assertEquals(1, files.length); + } + + @Test + public void testTempDirectoryCleanup() throws IOException { + pdf.addPageFromString("

Müller

"); + pdf.getCommand(); + final File dir = new File(System.getProperty("java.io.tmpdir")); + File[] files = dir.listFiles(new FilenameFilterConfig()); + Assert.assertEquals(1, files.length); + pdf.cleanAllTempFiles(); + files = dir.listFiles(new FilenameFilterConfig()); + Assert.assertEquals(0, files.length); + } + + @Test + public void testCustomTempDirectory() throws IOException { File f = File.createTempFile("java-wrapper-wkhtmltopdf-test", ".html"); pdf.setTempDirectory(new File(f.getParent())); pdf.addPageFromString("

Müller

"); diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties index fce5434..7933d2f 100644 --- a/src/test/resources/simplelogger.properties +++ b/src/test/resources/simplelogger.properties @@ -1,34 +1,27 @@ # SLF4J's SimpleLogger configuration file # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. - # Default logging detail level for all instances of SimpleLogger. # Must be one of ("trace", "debug", "info", "warn", or "error"). # If not specified, defaults to "info". org.slf4j.simpleLogger.defaultLogLevel=debug - # Logging detail level for a SimpleLogger instance named "xxxxx". # Must be one of ("trace", "debug", "info", "warn", or "error"). # If not specified, the default logging detail level is used. #org.slf4j.simpleLogger.log.xxxxx= - # Set to true if you want the current date and time to be included in output messages. # Default is false, and will output the number of milliseconds elapsed since startup. org.slf4j.simpleLogger.showDateTime=true - # The date and time format to be used in the output messages. # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. # If the format is not specified or is invalid, the default format is used. # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z - # Set to true if you want to output the current thread name. # Defaults to true. org.slf4j.simpleLogger.showThreadName=true - # Set to true if you want the Logger instance name to be included in output messages. # Defaults to true. #org.slf4j.simpleLogger.showLogName=true - # Set to true if you want the last component of the name to be included in output messages. # Defaults to false. #org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file