├── .gitignore ├── README.md ├── pom.xml └── src ├── config └── checkstyle.xml ├── main ├── java │ └── com │ │ └── github │ │ └── jarlakxen │ │ └── embedphantomjs │ │ ├── ExecutionTimeout.java │ │ ├── PhantomJSReference.java │ │ ├── Version.java │ │ ├── exception │ │ └── UnexpectedProcessEndException.java │ │ └── executor │ │ ├── PhantomJSConsoleExecutor.java │ │ └── PhantomJSFileExecutor.java └── resources │ ├── etc │ └── license.txt │ └── phantomjs │ └── data.properties └── test └── java └── com └── github └── jarlakxen └── embedphantomjs └── PhantomJSExecutorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | 4 | *.class 5 | 6 | .settings/ 7 | target/ 8 | 9 | # Package Files # 10 | *.jar 11 | *.war 12 | *.ear 13 | 14 | #pom 15 | release.properties 16 | pom.xml.releaseBackup 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | embedphantomjs 2 | ============== 3 | 4 | Embedded PhantomJS for Java 5 | 6 | This project provides an easy interface for execute PhantomJS with Java. 7 | 8 | ## Why? 9 | 10 | - Easy way to execute JS inside Java 11 | - Easy way to make a Scraper with Java 12 | 13 | ### Maven 14 | 15 | Stable [OSS Sonatype](https://oss.sonatype.org/content/repositories/releases/com/github/jarlakxen/embedphantomjs/maven-metadata.xml) 16 | 17 | ```xml 18 | 19 | com.github.jarlakxen 20 | embedphantomjs 21 | 3.0 22 | 23 | ``` 24 | ### Changelog 25 | 26 | - 3.0 27 | - Move to Java 8 28 | - Remove Guava dependancy, switch to `CompletableFuture` 29 | 30 | - 2.9 31 | - https://github.com/Jarlakxen/embedphantomjs/pull/3 32 | - Update dependancies 33 | - Sopport for versions last versions of PhantomJS 34 | - Move PhantomJS download url to bitbucket. 35 | 36 | - 2.8 37 | - Better windows support ( Thanks to @Dmitry-Shweikus ) 38 | 39 | - 2.7 40 | - Unify Sync and Async interfaces for PhantomJSFileExecutor 41 | - Improve error handling 42 | 43 | - 2.6 44 | - Some API improvments in PhantomJSFileExecutor 45 | - Improve the core of the PhantomJSConsoleExecutor 46 | 47 | - 2.5 48 | - Some API improvments 49 | 50 | - 2.4 51 | - Provide some basic information of the PhantomJS process 52 | - Better exception handling 53 | 54 | - 2.3 55 | - Bug fixing 56 | 57 | - 2.2 58 | - Mejor API Refactor 59 | - Now supports console style executor 60 | 61 | - 2.1 62 | - Add asyncronic execution 63 | 64 | - 2.0 65 | - Sopport for versions 1.9.2, 1.9.1, 1.9.0, 1.8.2, 1.8.1, 1.8.0 66 | - Sopport binary versioned 67 | 68 | - 1.3 69 | - Full support for input stream script 70 | 71 | - 1.2 72 | - Bug fixing 73 | 74 | - 1.1 75 | - Bug fixing 76 | 77 | - 1.0 78 | - Auto-detect OS 79 | - Auto-detect architecture 80 | - Sopport for versions 1.7.0, 1.6.1, 1.6.0, 1.5.0, 1.4.1, 1.4.0, 1.3.0 81 | - Check native installation 82 | - Download from page 83 | 84 | 85 | 86 | ### Supported Versions 87 | 88 | Versions: 2.1.1, 1.9.8, 1.9.7, 1.9.6, 1.9.5, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9.0, 1.8.2, 1.8.1, 1.8.0, 1.7.0, 1.6.1, 1.6.0, 1.5.0, 1.4.1, 1.4.0, 1.3.0 89 | Support for Linux, Windows and MacOSX. 90 | 91 | ### Usage 92 | 93 | ####Example I: 94 | 95 | PhantomJSFileExecutor ex = new PhantomJSFileExecutor(PhantomJSReference.create().build(), new ExecutionTimeout(1, TimeUnit.SECONDS)); 96 | String output = executor.execute(new File("~/scrapper.js")).get(); 97 | System.out.println(output); // This prints "TEST1" 98 | 99 | 100 | ####Example II: 101 | 102 | PhantomJSFileExecutor ex = new new PhantomJSFileExecutor(PhantomJSReference.create().build(), new ExecutionTimeout(1, TimeUnit.SECONDS)); 103 | String output = executor.execute("console.log('TEST1');phantom.exit();").get() 104 | System.out.println(output); // This prints "TEST1" 105 | 106 | ####Example III: 107 | 108 | PhantomJSConsoleExecutor ex = new PhantomJSConsoleExecutor(PhantomJSReference.create().build()); 109 | ex.start(); 110 | ex.execute("var system = require('system');"); 111 | System.out.println(ex.execute("system.stdout.writeLine('TEST1');", "true")); // This prints "TEST1" 112 | System.out.println( ex.execute("system.stdout.writeLine('TEST2');", "true")); // This prints "TEST2" 113 | ex.destroy(); 114 | 115 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Jarlakxen/embedphantomjs/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 116 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.github.jarlakxen 4 | embedphantomjs 5 | 3.1-SNAPSHOT 6 | EmbedPhantomJS 7 | 8 | Embedded PhantomJS for Java 9 | https://github.com/Jarlakxen/embedphantomjs 10 | 11 | 12 | 13 | The Apache Software License, Version 2.0 14 | http://www.apache.org/licenses/LICENSE-2.0.txt 15 | repo 16 | 17 | 18 | 19 | 20 | scm:git:git@github.com:Jarlakxen/embedphantomjs.git 21 | scm:git:git@github.com:Jarlakxen/embedphantomjs.git 22 | git@github.com:Jarlakxen/embedphantomjs.git 23 | HEAD 24 | 25 | 26 | 27 | 28 | jarlakxen 29 | Facundo Viale 30 | fviale@despegar.com 31 | GMT+3 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | sonatype-nexus-snapshots 40 | Sonatype Nexus Snapshots 41 | https://oss.sonatype.org/content/repositories/snapshots/ 42 | 43 | 44 | sonatype-nexus-staging 45 | Nexus Release Repository 46 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 47 | 48 | 49 | 50 | 51 | 52 | org.apache.commons 53 | commons-lang3 54 | 3.4 55 | 56 | 57 | commons-io 58 | commons-io 59 | 2.5 60 | 61 | 62 | org.apache.commons 63 | commons-compress 64 | 1.12 65 | 66 | 67 | 68 | org.slf4j 69 | log4j-over-slf4j 70 | 1.7.21 71 | 72 | 73 | 74 | junit 75 | junit 76 | 4.12 77 | test 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-compiler-plugin 86 | 3.5.1 87 | 88 | 1.8 89 | 1.8 90 | UTF-8 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-release-plugin 97 | 2.5.3 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-resources-plugin 103 | 2.5 104 | 105 | UTF-8 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-source-plugin 112 | 3.0.1 113 | 114 | 115 | attach-sources 116 | 117 | jar-no-fork 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-javadoc-plugin 126 | 2.10.4 127 | 128 | 129 | attach-javadocs 130 | 131 | jar 132 | 133 | 134 | 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-surefire-plugin 140 | 2.11 141 | 142 | 143 | **/examples/** 144 | 145 | 146 | 147 | 148 | 149 | com.mycila.maven-license-plugin 150 | maven-license-plugin 151 | 1.9.0 152 | 153 |
${basedir}/src/main/resources/etc/license.txt
154 | false 155 | true 156 | true 157 | true 158 | 159 | src/**/*.java 160 | **/test/**/*.java 161 | 162 | 163 | 2013 164 | Facundo Viale 165 | Facundo Viale (Jarlakxen@github) 166 | 167 | UTF-8 168 |
169 |
170 | 171 | 172 | org.apache.maven.plugins 173 | maven-checkstyle-plugin 174 | 2.9.1 175 | 176 | config/checkstyle.xml 177 | 178 | ${basedir}/src/test/**/* 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 1.6 187 | 188 | ${gpg.passphrase} 189 | 190 | 191 | 192 | sign-artifacts 193 | verify 194 | 195 | sign 196 | 197 | 198 | 199 | 200 | 201 | 202 | org.sonatype.plugins 203 | nexus-staging-maven-plugin 204 | 1.6.7 205 | true 206 | 207 | ossrh 208 | https://oss.sonatype.org/ 209 | true 210 | 211 | 212 |
213 |
214 | 215 | 216 | 217 | release-sign-artifacts 218 | 219 | 220 | performRelease 221 | true 222 | 223 | 224 | 225 | 226 | include-sources 227 | 228 | 229 | 230 | / 231 | true 232 | src/main/java 233 | 234 | **/*.java 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 |
243 | -------------------------------------------------------------------------------- /src/config/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/ExecutionTimeout.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class ExecutionTimeout { 25 | 26 | private final long timeout; 27 | private final TimeUnit unit; 28 | 29 | public ExecutionTimeout(long timeout, TimeUnit unit) { 30 | this.timeout = timeout; 31 | this.unit = unit; 32 | } 33 | 34 | public long getTimeout() { 35 | return timeout; 36 | } 37 | 38 | public TimeUnit getUnit() { 39 | return unit; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/PhantomJSReference.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs; 21 | 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | import java.net.URL; 27 | import java.nio.charset.Charset; 28 | import java.util.Properties; 29 | import org.apache.commons.compress.archivers.ArchiveEntry; 30 | import org.apache.commons.compress.archivers.ArchiveInputStream; 31 | import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 32 | import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 33 | import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 34 | import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 35 | import org.apache.commons.io.FileUtils; 36 | import org.apache.commons.io.IOUtils; 37 | import org.apache.commons.lang3.StringUtils; 38 | import org.apache.commons.lang3.builder.ToStringBuilder; 39 | import org.apache.log4j.Logger; 40 | 41 | public class PhantomJSReference { 42 | 43 | private static final Logger LOGGER = Logger.getLogger(PhantomJSReference.class); 44 | 45 | public static final String PHANTOMJS_NATIVE_CMD = "/usr/bin/phantomjs"; 46 | 47 | public static final String PHANTOMJS_DATA_FILE = "phantomjs/data.properties"; 48 | 49 | public static final String PHANTOMJS_DOWNLOAD_BINARY_PATH = "/bin/phantomjs"; 50 | 51 | public static PhantomJSReferenceBuilder create() { 52 | return new PhantomJSReferenceBuilder(); 53 | } 54 | 55 | /*** 56 | * This inner builder is defined because we want immutable BusinessDomain 57 | * object. Ref. Effective Java Second Edition Pag. 14. 58 | * 59 | */ 60 | public static class PhantomJSReferenceBuilder { 61 | private Version version = Version.v_2_1_1; 62 | private String architecture = System.getProperty("os.arch").toLowerCase(); 63 | private String hostOs; 64 | 65 | private String downloadUrl = "https://bitbucket.org/ariya/phantomjs/downloads/"; 66 | private String targetInstallationFolder = System.getProperty("user.home") + "/.embedphantomjs"; 67 | 68 | private String commandLineOptions; 69 | 70 | public PhantomJSReferenceBuilder withVersion(final Version version) { 71 | this.version = version; 72 | return this; 73 | } 74 | 75 | public PhantomJSReferenceBuilder withArchitecture(final String architecture) { 76 | this.architecture = architecture; 77 | return this; 78 | } 79 | 80 | public PhantomJSReferenceBuilder withHostOS(final String hostOs) { 81 | this.hostOs = hostOs; 82 | return this; 83 | } 84 | 85 | public PhantomJSReferenceBuilder useDownloadUrl(final String downloadUrl) { 86 | this.downloadUrl = downloadUrl; 87 | return this; 88 | } 89 | /** 90 | * Adds command line options so they are sent to PhantomJs by the executor 91 | * @param commandLineOptions Array of command line options to send 92 | * @return Reference to the builder 93 | */ 94 | public PhantomJSReferenceBuilder addCommandLineOptions(final String... commandLineOptions){ 95 | this.commandLineOptions = StringUtils.join(commandLineOptions, " "); 96 | return this; 97 | } 98 | public PhantomJSReferenceBuilder useTargetInstallationFolder(final String targetInstallationFolder) { 99 | this.targetInstallationFolder = targetInstallationFolder; 100 | return this; 101 | } 102 | 103 | public PhantomJSReference build() { 104 | return new PhantomJSReference(this); 105 | } 106 | 107 | 108 | public PhantomJSReferenceBuilder() 109 | { 110 | final String os = System.getProperty("os.name").toLowerCase(); 111 | if (os.contains("win")) 112 | hostOs = "win"; 113 | else if (os.contains("mac")) 114 | hostOs = "macosx"; 115 | else 116 | hostOs = "linux"; 117 | } 118 | } 119 | 120 | private Version version; 121 | private String architecture; 122 | private String hostOs; 123 | private String downloadUrl; 124 | private String targetInstallationFolder; 125 | private String binaryPath; 126 | private String commandLineOptions; 127 | 128 | private PhantomJSReference(final PhantomJSReferenceBuilder builder) { 129 | this.version = builder.version; 130 | this.architecture = builder.architecture; 131 | this.hostOs = builder.hostOs; 132 | this.downloadUrl = builder.downloadUrl; 133 | this.targetInstallationFolder = builder.targetInstallationFolder; 134 | this.commandLineOptions = builder.commandLineOptions ==null?"":builder.commandLineOptions; 135 | } 136 | 137 | public String getBinaryPath() { 138 | if (binaryPath == null) { 139 | ensureBinary(); 140 | } 141 | return binaryPath; 142 | } 143 | 144 | public synchronized void ensureBinary() { 145 | 146 | if (binaryPath != null) { 147 | // The binary is already ensure 148 | return; 149 | } 150 | 151 | // Check if phantomjs is installed locally 152 | if (Version.NATIVE.equals(version)) { 153 | 154 | LOGGER.debug("Checking PhantomJS native installation"); 155 | if (this.checkPhantomJSBinaryAnyVersion(PHANTOMJS_NATIVE_CMD)) { 156 | LOGGER.debug("Native installation founded"); 157 | binaryPath = PHANTOMJS_NATIVE_CMD; 158 | return; 159 | } else { 160 | throw new RuntimeException("Invalid native installation!"); 161 | } 162 | } 163 | 164 | if (!getVersion().isDownloadSopported()) { 165 | throw new RuntimeException("Unsopported version for downloading!"); 166 | } 167 | 168 | final File binaryFile = new File(this.getTargetInstallationFolder() + "/" + this.getVersion().getDescription() + "/phantomjs"); 169 | final String binaryFilePath = binaryFile.getAbsolutePath(); 170 | 171 | // Check if phantomjs is already installed in target path 172 | LOGGER.debug("Checking PhantomJS installation in " + binaryFilePath); 173 | if (this.checkPhantomJSBinaryVersion(binaryFilePath, getVersion())) { 174 | LOGGER.debug("PhantomJS founded in " + binaryFilePath); 175 | binaryPath = binaryFilePath; 176 | } else { 177 | LOGGER.debug("PhantomJS not founded in " + binaryFilePath); 178 | 179 | try { 180 | downloadPhantomJS(binaryFile); 181 | } catch (IOException e) { 182 | throw new RuntimeException(e); 183 | } 184 | 185 | if (!this.checkPhantomJSBinaryVersion(binaryFilePath, getVersion())) { 186 | throw new RuntimeException("Invalid download"); 187 | } 188 | 189 | binaryPath = binaryFilePath; 190 | } 191 | } 192 | /** 193 | * Command line options that are going to be sent to PhantomJS 194 | * @return 195 | */ 196 | public String getCommandLineOptions() { 197 | return commandLineOptions; 198 | } 199 | 200 | private void downloadPhantomJS(final File binaryFile) throws IOException { 201 | final Properties properties = new Properties(); 202 | properties.load(this.getClass().getClassLoader().getResourceAsStream(PHANTOMJS_DATA_FILE)); 203 | 204 | String name = properties.getProperty(this.getVersion().getDescription() + "." + this.getHostOs() + ".name"); 205 | 206 | final String architecture = this.getArchitecture().indexOf("64") >= 0 ? "x86_64" : "i686"; 207 | 208 | LOGGER.debug("System Data: Arch [" + architecture + "] - OS [" + this.getHostOs() + "]"); 209 | 210 | if (this.getHostOs().equals("linux")) { 211 | name = String.format(name, architecture); 212 | } 213 | 214 | // Download PhantomJS 215 | final URL downloadPath = new URL(this.getDownloadUrl() + name); 216 | final File phantomJsCompressedFile = new File(System.getProperty("java.io.tmpdir") + "/" + name); 217 | 218 | LOGGER.info("Downloading " + downloadPath.getPath() + " ..."); 219 | 220 | FileUtils.copyURLToFile(downloadPath, phantomJsCompressedFile); 221 | 222 | ArchiveInputStream archiveInputStream = null; 223 | 224 | if (phantomJsCompressedFile.getName().endsWith(".zip")) { 225 | 226 | archiveInputStream = new ZipArchiveInputStream(new FileInputStream(phantomJsCompressedFile)); 227 | 228 | } else if (phantomJsCompressedFile.getName().endsWith(".bz2")) { 229 | 230 | archiveInputStream = new TarArchiveInputStream(new BZip2CompressorInputStream(new FileInputStream(phantomJsCompressedFile))); 231 | 232 | } else if (phantomJsCompressedFile.getName().endsWith(".gz")) { 233 | 234 | archiveInputStream = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(phantomJsCompressedFile))); 235 | 236 | } 237 | 238 | ArchiveEntry entry; 239 | while ((entry = archiveInputStream.getNextEntry()) != null) { 240 | if (entry.getName().endsWith(PHANTOMJS_DOWNLOAD_BINARY_PATH) || entry.getName().toLowerCase().endsWith("phantomjs.exe")) { 241 | 242 | // Create target folder 243 | new File(this.getTargetInstallationFolder() + "/" + this.getVersion().getDescription()).mkdirs(); 244 | 245 | FileUtils.forceMkdir(new File(binaryFile.getParent())); 246 | 247 | if (!binaryFile.exists()) { 248 | binaryFile.createNewFile(); 249 | } 250 | 251 | binaryFile.setExecutable(true); 252 | binaryFile.setReadable(true); 253 | 254 | // Untar the binary file 255 | final FileOutputStream outputBinary = new FileOutputStream(binaryFile); 256 | 257 | LOGGER.info("Un-compress download to " + downloadPath.getPath() + " ..."); 258 | IOUtils.copy(archiveInputStream, outputBinary); 259 | 260 | outputBinary.close(); 261 | } 262 | } 263 | 264 | archiveInputStream.close(); 265 | } 266 | 267 | private String checkPhantomJSBinary(final String path) { 268 | try { 269 | final Process process = Runtime.getRuntime().exec(path + " --version"); 270 | process.waitFor(); 271 | 272 | final String processOutput = IOUtils.toString(process.getInputStream(), Charset.defaultCharset()); 273 | 274 | return processOutput.substring(0, 5); 275 | 276 | } catch (Exception e) { 277 | LOGGER.warn(e); 278 | } 279 | 280 | return null; 281 | } 282 | 283 | private Boolean checkPhantomJSBinaryAnyVersion(final String path) { 284 | final String outputVersion = checkPhantomJSBinary(path); 285 | return Version.fromValue(outputVersion) != null; 286 | } 287 | 288 | private Boolean checkPhantomJSBinaryVersion(final String path, final Version version) { 289 | final String outputVersion = checkPhantomJSBinary(path); 290 | return version.getDescription().equals(outputVersion); 291 | } 292 | 293 | public Version getVersion() { 294 | return this.version; 295 | } 296 | 297 | public String getHostOs() { 298 | return this.hostOs; 299 | } 300 | 301 | public String getArchitecture() { 302 | return this.architecture; 303 | } 304 | 305 | public String getDownloadUrl() { 306 | return this.downloadUrl; 307 | } 308 | 309 | public String getTargetInstallationFolder() { 310 | return this.targetInstallationFolder; 311 | } 312 | 313 | @Override 314 | public String toString() { 315 | return ToStringBuilder.reflectionToString(this); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/Version.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs; 21 | 22 | public enum Version { 23 | v_2_1_1("2.1.1", true), v_1_9_8("1.9.8", true), v_1_9_7("1.9.7", true), 24 | v_1_9_6("1.9.6", true), v_1_9_5("1.9.5", true), v_1_9_4("1.9.4", true), v_1_9_3("1.9.3", true), 25 | v_1_9_2("1.9.2", true), v_1_9_1("1.9.1", true), v_1_9_0("1.9.0", true), v_1_8_2("1.8.2", true), 26 | v_1_8_1("1.8.1", true), v_1_8_0("1.8.0", true), v_1_7_0("1.7.0", true), v_1_6_1("1.6.1", true), 27 | v_1_6_0("1.6.0", true), v_1_5_0("1.5.0", true), v_1_4_1("1.4.1", true), v_1_4_0("1.4.0", false), 28 | v_1_3_0("1.3.0", false), NATIVE(null, false); 29 | 30 | public static Version fromValue(final String version) { 31 | 32 | for (Version value : Version.values()) { 33 | if (value.getDescription().equals(version)) { 34 | return value; 35 | } 36 | } 37 | 38 | return null; 39 | } 40 | 41 | private final String description; 42 | private final boolean downloadSopported; 43 | 44 | private Version(final String description, final boolean downloadSopported) { 45 | this.description = description; 46 | this.downloadSopported = downloadSopported; 47 | } 48 | 49 | public String getDescription() { 50 | return this.description; 51 | } 52 | 53 | public boolean isDownloadSopported() { 54 | return this.downloadSopported; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return this.getDescription(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/exception/UnexpectedProcessEndException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs.exception; 21 | 22 | public class UnexpectedProcessEndException extends RuntimeException { 23 | private static final long serialVersionUID = 6312464814429570001L; 24 | 25 | public UnexpectedProcessEndException() { 26 | 27 | } 28 | 29 | public UnexpectedProcessEndException(final Throwable throwable) { 30 | super(throwable); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/executor/PhantomJSConsoleExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs.executor; 21 | 22 | import static java.util.Arrays.*; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.InputStreamReader; 29 | import java.io.OutputStream; 30 | import java.lang.reflect.Field; 31 | import java.nio.charset.Charset; 32 | import java.util.List; 33 | import java.util.concurrent.CompletableFuture; 34 | import java.util.concurrent.ExecutorService; 35 | import java.util.concurrent.Executors; 36 | 37 | import org.apache.commons.io.IOUtils; 38 | import org.apache.commons.lang3.StringUtils; 39 | import org.apache.log4j.Logger; 40 | 41 | import com.github.jarlakxen.embedphantomjs.PhantomJSReference; 42 | import com.github.jarlakxen.embedphantomjs.exception.UnexpectedProcessEndException; 43 | 44 | public class PhantomJSConsoleExecutor { 45 | 46 | private static final Logger LOGGER = Logger.getLogger(PhantomJSConsoleExecutor.class); 47 | 48 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; 49 | private static final int EOF = -1; 50 | 51 | private static final char SYSTEM_NEWLINE[] = System.getProperty("line.separator").toCharArray(); 52 | 53 | private static final String DEFAULT_PHANTOMJS_CONSOLE_PREFIX = "phantomjs> "; 54 | private static final List DEFAULT_PHANTOMJS_CONSOLE_POSTFIXS = asList("{}", "undefined"); 55 | private static final String PHANTOMJS_PARSER_ERROR_PREFIX = "Parse error"; 56 | 57 | private final ExecutorService executorService = Executors.newSingleThreadExecutor(); 58 | private final PhantomJSReference phantomReference; 59 | private final File scriptFile; 60 | private final String scriptArgs[]; 61 | private final String consolePrefix; 62 | private final List consolePostfix; 63 | private Process process; 64 | 65 | public PhantomJSConsoleExecutor(final PhantomJSReference phantomReference) { 66 | this(phantomReference, null); 67 | } 68 | 69 | public PhantomJSConsoleExecutor(final PhantomJSReference phantomReference, final File scriptFile, final String... scriptArgs) { 70 | this(phantomReference, DEFAULT_PHANTOMJS_CONSOLE_PREFIX, DEFAULT_PHANTOMJS_CONSOLE_POSTFIXS, scriptFile, scriptArgs); 71 | } 72 | 73 | public PhantomJSConsoleExecutor(final PhantomJSReference phantomReference, final String consolePrefix, final List consolePostfix, final File scriptFile, final String... scriptArgs) { 74 | this.phantomReference = phantomReference; 75 | this.scriptFile = scriptFile; 76 | this.scriptArgs = scriptArgs; 77 | this.consolePrefix = consolePrefix; 78 | this.consolePostfix = consolePostfix; 79 | } 80 | 81 | public int getPid() { 82 | if (process.getClass().getName().equals("java.lang.UNIXProcess")) { 83 | /* get the PID on unix/linux systems */ 84 | try { 85 | Field f = process.getClass().getDeclaredField("pid"); 86 | f.setAccessible(true); 87 | return f.getInt(process); 88 | } catch (Throwable e) { 89 | } 90 | } 91 | 92 | return -1; 93 | } 94 | 95 | public boolean isAlive() { 96 | try { 97 | process.exitValue(); 98 | return false; 99 | } catch (IllegalThreadStateException ex) { 100 | return true; 101 | } 102 | } 103 | 104 | public void start() { 105 | try { 106 | String cmd = this.phantomReference.getBinaryPath(); 107 | 108 | if (scriptFile != null) { 109 | cmd = cmd + " " + scriptFile.getAbsolutePath(); 110 | } 111 | 112 | if (scriptArgs != null && scriptArgs.length > 0) { 113 | cmd = cmd + " " + StringUtils.join(scriptArgs, " "); 114 | } 115 | 116 | process = Runtime.getRuntime().exec(cmd); 117 | if (StringUtils.isNotBlank(consolePrefix)) { 118 | process.getInputStream().read(new byte[consolePrefix.length()]); 119 | } 120 | } catch (IOException e) { 121 | throw new RuntimeException(e); 122 | } 123 | } 124 | 125 | public int destroy() { 126 | try { 127 | process.destroy(); 128 | } catch (Exception e) { 129 | } 130 | 131 | if (isAlive()) { 132 | try { 133 | return process.waitFor(); 134 | } catch (InterruptedException e) { 135 | 136 | } 137 | } 138 | 139 | return process.exitValue(); 140 | } 141 | 142 | public CompletableFuture execute(final String scriptSource) { 143 | return this.execute(IOUtils.toInputStream(scriptSource, Charset.defaultCharset()), consolePostfix); 144 | } 145 | 146 | public CompletableFuture execute(final String scriptSource, String... endLines) { 147 | return this.execute(IOUtils.toInputStream(scriptSource, Charset.defaultCharset()), asList(endLines)); 148 | } 149 | 150 | public CompletableFuture execute(final InputStream scriptSourceInputStream, String... endLines) { 151 | return this.execute(scriptSourceInputStream, asList(endLines)); 152 | } 153 | 154 | public CompletableFuture execute(final InputStream scriptSourceInputStream, final List endLines) 155 | throws UnexpectedProcessEndException { 156 | return CompletableFuture.supplyAsync(() -> doExecute(scriptSourceInputStream, endLines), executorService); 157 | } 158 | 159 | private String doExecute(final InputStream scriptSourceInputStream, final List endLines) { 160 | 161 | if (!isAlive()) { 162 | throw new UnexpectedProcessEndException(); 163 | } 164 | 165 | try { 166 | final String input = copy(scriptSourceInputStream, process.getOutputStream()); 167 | // Append Enter to the input 168 | 169 | if (!endWithNewLine(input)) { 170 | for (char c : SYSTEM_NEWLINE) { 171 | process.getOutputStream().write(c); 172 | } 173 | } 174 | 175 | process.getOutputStream().flush(); 176 | 177 | final String output = readPhantomJSOutput(process.getInputStream(), endLines); 178 | 179 | LOGGER.debug("Program output: " + output); 180 | 181 | return output; 182 | } catch (IOException e) { 183 | throw new UnexpectedProcessEndException(e); 184 | } 185 | } 186 | 187 | private String readPhantomJSOutput(InputStream processInput, List endLines) throws IOException { 188 | final StringBuilder out = new StringBuilder(); 189 | final BufferedReader in = new BufferedReader(new InputStreamReader(processInput, "UTF-8")); 190 | 191 | while (true) { 192 | final String line = in.readLine(); 193 | 194 | LOGGER.trace("Incoming line from process: " + line); 195 | 196 | if (line.equals(PHANTOMJS_PARSER_ERROR_PREFIX)) { 197 | return line; 198 | } 199 | 200 | if (line == null || endLines.contains(line)) { 201 | if (StringUtils.isNotBlank(consolePrefix)) { 202 | in.skip(consolePrefix.length()); 203 | } 204 | break; 205 | } 206 | 207 | if (out.length() > 0) { 208 | out.append("\n"); 209 | } 210 | 211 | out.append(line); 212 | } 213 | 214 | return out.toString(); 215 | } 216 | 217 | private boolean endWithNewLine(final String input) { 218 | return input.endsWith(String.valueOf(SYSTEM_NEWLINE)); 219 | } 220 | 221 | private String copy(final InputStream input, final OutputStream output) throws IOException { 222 | final StringBuilder inputString = new StringBuilder(); 223 | final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 224 | int n = 0; 225 | while (EOF != (n = input.read(buffer))) { 226 | output.write(buffer, 0, n); 227 | inputString.append(new String(buffer, 0, n)); 228 | } 229 | return inputString.toString(); 230 | } 231 | 232 | public String getConsolePrefix() { 233 | return consolePrefix; 234 | } 235 | public List getConsolePostfix() { 236 | return consolePostfix; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/main/java/com/github/jarlakxen/embedphantomjs/executor/PhantomJSFileExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs.executor; 21 | 22 | import java.io.File; 23 | import java.nio.charset.Charset; 24 | import java.util.concurrent.CompletableFuture; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | import java.util.function.Function; 28 | 29 | import org.apache.commons.io.FileUtils; 30 | import org.apache.commons.io.IOUtils; 31 | import org.apache.commons.lang3.RandomStringUtils; 32 | import org.apache.commons.lang3.StringUtils; 33 | import org.apache.log4j.Logger; 34 | 35 | import com.github.jarlakxen.embedphantomjs.ExecutionTimeout; 36 | import com.github.jarlakxen.embedphantomjs.PhantomJSReference; 37 | 38 | public class PhantomJSFileExecutor { 39 | 40 | private static final Logger LOGGER = Logger.getLogger(PhantomJSFileExecutor.class); 41 | 42 | private final ExecutorService processExecutorService; 43 | 44 | private final PhantomJSReference phantomReference; 45 | 46 | private final ExecutionTimeout executionTimeout; 47 | 48 | public PhantomJSFileExecutor(PhantomJSReference phantomReference, ExecutionTimeout executionTimeout) { 49 | this(phantomReference, Executors.newCachedThreadPool(), executionTimeout); 50 | } 51 | 52 | public PhantomJSFileExecutor(final PhantomJSReference phantomReference, final ExecutorService executorService, 53 | final ExecutionTimeout executionTimeout) { 54 | this.phantomReference = phantomReference; 55 | this.executionTimeout = executionTimeout; 56 | this.processExecutorService = executorService; 57 | } 58 | 59 | public CompletableFuture execute(final String fileContent, final String... args) { 60 | try { 61 | final File tmp = File.createTempFile(RandomStringUtils.randomAlphabetic(10), ".js"); 62 | FileUtils.write(tmp, fileContent, Charset.defaultCharset()); 63 | final CompletableFuture result = execute(tmp, args); 64 | 65 | return result.handle((value, error) -> { 66 | if (tmp != null) { 67 | tmp.delete(); 68 | } 69 | 70 | if (error == null){ 71 | return CompletableFuture.completedFuture(value); 72 | } 73 | 74 | LOGGER.error("", error); 75 | final CompletableFuture errorFuture = new CompletableFuture<>(); 76 | errorFuture.completeExceptionally(error); 77 | return errorFuture; 78 | }).thenCompose(Function.identity()); 79 | } catch (Exception e) { 80 | throw new RuntimeException(e); 81 | } 82 | } 83 | 84 | /** 85 | * Invokes the instances of PhantomJS 86 | * 87 | * @param sourceFile 88 | * JavaScript source file that is executed by PhantomJA 89 | * @param args 90 | * Parameters that are read by sourceFile 91 | * @return The result that the process has output in console 92 | */ 93 | public CompletableFuture execute(final File sourceFile, final String... args) { 94 | final String cmd = this.phantomReference.getBinaryPath() + " " + this.phantomReference.getCommandLineOptions() 95 | + " " + sourceFile.getAbsolutePath() + " " + StringUtils.join(args, " "); 96 | try { 97 | final Process process = Runtime.getRuntime().exec(cmd); 98 | LOGGER.info("Command to execute: " + cmd); 99 | 100 | final CompletableFuture action = CompletableFuture.supplyAsync(() -> { 101 | try { 102 | LOGGER.info("Command to execute: " + cmd); 103 | final String output = IOUtils.toString(process.getInputStream(), Charset.defaultCharset()); 104 | process.waitFor(); 105 | LOGGER.debug("Command " + cmd + " output:" + output); 106 | return output; 107 | } catch (Exception e) { 108 | throw new RuntimeException(e); 109 | } 110 | }, processExecutorService); 111 | 112 | CompletableFuture.runAsync(() -> { 113 | try { 114 | action.get(executionTimeout.getTimeout(), executionTimeout.getUnit()); 115 | } catch (Exception ex) { 116 | action.completeExceptionally(ex); 117 | process.destroy(); 118 | } 119 | }); 120 | 121 | return action; 122 | } catch (Exception e) { 123 | throw new RuntimeException(e); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/resources/etc/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) ${lic.year} 2 | ${lic.username} 3 | 4 | with contributions from 5 | ${lic.developers} 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. -------------------------------------------------------------------------------- /src/main/resources/phantomjs/data.properties: -------------------------------------------------------------------------------- 1 | 2.1.1.linux.name = phantomjs-2.1.1-linux-%s.tar.bz2 2 | 2.1.1.win.name = phantomjs-2.1.1-windows.zip 3 | 2.1.1.macosx.name = phantomjs-2.1.1-macosx.zip 4 | 5 | 1.9.8.linux.name = phantomjs-1.9.8-linux-%s.tar.bz2 6 | 1.9.8.win.name = phantomjs-1.9.8-windows.zip 7 | 1.9.8.macosx.name = phantomjs-1.9.8-macosx.zip 8 | 9 | 1.9.7.linux.name = phantomjs-1.9.7-linux-%s.tar.bz2 10 | 1.9.7.win.name = phantomjs-1.9.7-windows.zip 11 | 1.9.7.macosx.name = phantomjs-1.9.7-macosx.zip 12 | 13 | 1.9.6.linux.name = phantomjs-1.9.6-linux-%s.tar.bz2 14 | 1.9.6.win.name = phantomjs-1.9.6-windows.zip 15 | 1.9.6.macosx.name = phantomjs-1.9.6-macosx.zip 16 | 17 | 1.9.5.linux.name = phantomjs-1.9.5-linux-%s.tar.bz2 18 | 1.9.5.win.name = phantomjs-1.9.5-windows.zip 19 | 1.9.5.macosx.name = phantomjs-1.9.5-macosx.zip 20 | 21 | 1.9.4.linux.name = phantomjs-1.9.4-linux-%s.tar.bz2 22 | 1.9.4.win.name = phantomjs-1.9.4-windows.zip 23 | 1.9.4.macosx.name = phantomjs-1.9.4-macosx.zip 24 | 25 | 1.9.3.linux.name = phantomjs-1.9.3-linux-%s.tar.bz2 26 | 1.9.3.win.name = phantomjs-1.9.3-windows.zip 27 | 1.9.3.macosx.name = phantomjs-1.9.3-macosx.zip 28 | 29 | 1.9.2.linux.name = phantomjs-1.9.2-linux-%s.tar.bz2 30 | 1.9.2.win.name = phantomjs-1.9.2-windows.zip 31 | 1.9.2.macosx.name = phantomjs-1.9.2-macosx.zip 32 | 33 | 1.9.1.linux.name = phantomjs-1.9.1-linux-%s.tar.bz2 34 | 1.9.1.win.name = phantomjs-1.9.1-windows.zip 35 | 1.9.1.macosx.name = phantomjs-1.9.1-macosx.zip 36 | 37 | 1.9.0.linux.name = phantomjs-1.9.0-linux-%s.tar.bz2 38 | 1.9.0.win.name = phantomjs-1.9.0-windows.zip 39 | 1.9.0.macosx.name = phantomjs-1.9.0-macosx.zip 40 | 41 | 1.8.2.linux.name = phantomjs-1.8.2-linux-%s.tar.bz2 42 | 1.8.2.win.name = phantomjs-1.8.2-windows.zip 43 | 1.8.2.macosx.name = phantomjs-1.8.2-macosx.zip 44 | 45 | 1.8.1.linux.name = phantomjs-1.8.1-linux-%s.tar.bz2 46 | 1.8.1.win.name = phantomjs-1.8.1-windows.zip 47 | 1.8.1.macosx.name = phantomjs-1.8.1-macosx.zip 48 | 49 | 1.8.0.linux.name = phantomjs-1.8.0-linux-%s.tar.bz2 50 | 1.8.0.win.name = phantomjs-1.8.0-windows.zip 51 | 1.8.0.macosx.name = phantomjs-1.8.0-macosx.zip 52 | 53 | 1.7.0.linux.name = phantomjs-1.7.0-linux-%s.tar.bz2 54 | 1.7.0.win.name = phantomjs-1.7.0-windows.zip 55 | 1.7.0.macosx.name = phantomjs-1.7.0-macosx.zip 56 | 57 | 1.6.1.linux.name = phantomjs-1.6.1-linux-%s-dynamic.tar.bz2 58 | 1.6.1.win.name = phantomjs-1.6.1-win32-static.zip 59 | 1.6.1.macosx.name = phantomjs-1.6.1-macosx-static.zip 60 | 61 | 1.6.0.linux.name = phantomjs-1.6.0-linux-%s-dynamic.tar.bz2 62 | 1.6.0.win.name = phantomjs-1.6.0-win32-static.zip 63 | 1.6.0.macosx.name = phantomjs-1.6.0-macosx-static.zip 64 | 65 | 1.5.0.linux.name = phantomjs-1.5.0-linux-%s-dynamic.tar.gz 66 | 1.5.0.win.name = phantomjs-1.5.0-win32-static.zip 67 | 1.5.0.macosx.name = phantomjs-1.5.0-macosx-static.zip 68 | 69 | 1.4.1.linux.name = phantomjs-1.4.1-linux-%s-dynamic.tar.gz 70 | 1.4.1.win.name = phantomjs-1.4.1-win32-static.zip 71 | 1.4.1.macosx.name = phantomjs-1.4.1-macosx-static.zip -------------------------------------------------------------------------------- /src/test/java/com/github/jarlakxen/embedphantomjs/PhantomJSExecutorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 3 | * Facundo Viale 4 | * 5 | * with contributions from 6 | * Facundo Viale (Jarlakxen@github) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package com.github.jarlakxen.embedphantomjs; 21 | 22 | import static org.junit.Assert.assertEquals; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.nio.charset.Charset; 27 | import java.util.concurrent.CompletableFuture; 28 | import java.util.concurrent.ExecutionException; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | import org.apache.commons.io.FileUtils; 32 | import org.junit.AfterClass; 33 | import org.junit.BeforeClass; 34 | import org.junit.Test; 35 | 36 | import com.github.jarlakxen.embedphantomjs.exception.UnexpectedProcessEndException; 37 | import com.github.jarlakxen.embedphantomjs.executor.PhantomJSConsoleExecutor; 38 | import com.github.jarlakxen.embedphantomjs.executor.PhantomJSFileExecutor; 39 | 40 | public class PhantomJSExecutorTest { 41 | 42 | private static final String DEFAULT_FILE_JS = "console.log('TEST1');phantom.exit();"; 43 | private static final String DEFAULT_CONSOLE_BOOTSTRAP = "var system = require('system');"; 44 | private static final String DEFAULT_CONSOLE_JS = "system.standardout.writeLine('TEST1')"; 45 | 46 | private static final File test1 = new File(System.getProperty("java.io.tmpdir") + "/embedphantomjs.test1.js"); 47 | 48 | private static final PhantomJSReference phantomJSRef = PhantomJSReference.create().build(); 49 | 50 | @BeforeClass 51 | public static void setUpClass() throws IOException { 52 | test1.createNewFile(); 53 | FileUtils.write(test1, DEFAULT_FILE_JS, Charset.defaultCharset()); 54 | phantomJSRef.ensureBinary(); 55 | } 56 | 57 | @AfterClass 58 | public static void tearDownClass() throws IOException { 59 | test1.delete(); 60 | } 61 | 62 | @Test 63 | public void test_FileExecutor_FromString() throws InterruptedException, ExecutionException { 64 | PhantomJSFileExecutor ex = new PhantomJSFileExecutor(phantomJSRef, new ExecutionTimeout(5, TimeUnit.SECONDS)); 65 | assertEquals("TEST1"+String.format("%n"), ex.execute(DEFAULT_FILE_JS).get()); 66 | } 67 | 68 | @Test 69 | public void test__FileExecutor_FromFile() throws InterruptedException, ExecutionException { 70 | PhantomJSFileExecutor ex = new PhantomJSFileExecutor(phantomJSRef, new ExecutionTimeout(5, TimeUnit.SECONDS)); 71 | assertEquals("TEST1"+String.format("%n"), ex.execute(test1).get()); 72 | } 73 | 74 | @Test 75 | public void test_FileExecutor_FromString_Timeout() throws InterruptedException, ExecutionException { 76 | PhantomJSFileExecutor ex = new PhantomJSFileExecutor(phantomJSRef, new ExecutionTimeout(100, TimeUnit.MILLISECONDS)); 77 | CompletableFuture result = ex.execute("while(true){};"); 78 | Thread.sleep(200); 79 | assertEquals(true,result.isDone()); 80 | assertEquals(true,result.isCompletedExceptionally()); 81 | } 82 | 83 | @Test 84 | public void test_executor_FromConsole() throws UnexpectedProcessEndException, InterruptedException, ExecutionException { 85 | PhantomJSConsoleExecutor ex = new PhantomJSConsoleExecutor(phantomJSRef); 86 | ex.start(); 87 | ex.execute(DEFAULT_CONSOLE_BOOTSTRAP).get(); 88 | assertEquals("TEST1", ex.execute(DEFAULT_CONSOLE_JS, "true").get()); 89 | assertEquals("TEST1", ex.execute(DEFAULT_CONSOLE_JS, "true").get()); 90 | ex.destroy(); 91 | } 92 | } 93 | --------------------------------------------------------------------------------