package com.highcharts.export.server;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.net.SocketTimeoutException;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.util.ArrayList;
|
|
import java.util.Timer;
|
|
import java.util.concurrent.TimeoutException;
|
|
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
import com.highcharts.export.converter.SVGConverterException;
|
|
import com.highcharts.export.util.TempDir;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
public class Server {
|
|
private Process process;
|
|
private final int port;
|
|
private final String host;
|
|
private final int readTimeout;
|
|
private final int connectTimeout;
|
|
private final int maxTimeout;
|
|
private ServerState state = ServerState.IDLE;
|
|
|
|
protected static final Logger logger = Logger.getLogger("server");
|
|
|
|
public Server(String exec, String script, String host, int port, int connectTimeout, int readTimeout, int maxTimeout) {
|
|
|
|
// assign port and host to this instance
|
|
this.port = port;
|
|
this.host = host;
|
|
this.connectTimeout = connectTimeout;
|
|
this.readTimeout = readTimeout;
|
|
this.maxTimeout = maxTimeout;
|
|
|
|
try {
|
|
ArrayList<String> commands = new ArrayList<String>();
|
|
commands.add(exec);
|
|
commands.add(script);
|
|
commands.add("-host");
|
|
commands.add(host);
|
|
commands.add("-port");
|
|
commands.add("" + port);
|
|
|
|
logger.log(Level.FINE, commands.toString());
|
|
|
|
process = new ProcessBuilder(commands).start();
|
|
final BufferedReader bufferedReader = new BufferedReader(
|
|
new InputStreamReader(process.getInputStream()));
|
|
String readLine = bufferedReader.readLine();
|
|
if (readLine == null || !readLine.contains("ready")) {
|
|
logger.log(Level.WARNING, "Command starting Phantomjs failed");
|
|
process.destroy();
|
|
throw new RuntimeException("Error, PhantomJS couldnot start");
|
|
}
|
|
|
|
initialize();
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
|
@Override
|
|
public void run() {
|
|
if (process != null) {
|
|
logger.log(Level.WARNING, "Shutting down PhantomJS instance, kill process directly, {0}", this.toString());
|
|
try {
|
|
process.getErrorStream().close();
|
|
process.getInputStream().close();
|
|
process.getOutputStream().close();
|
|
} catch (IOException e) {
|
|
logger.log(Level.WARNING, "Error while shutting down process: {0}", e.getMessage());
|
|
}
|
|
process.destroy();
|
|
}
|
|
}
|
|
});
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public void initialize() {
|
|
logger.log(Level.FINE, "Phantom server started on port {0}", port);
|
|
}
|
|
|
|
public String request(String params) throws SocketTimeoutException, SVGConverterException, TimeoutException {
|
|
String response = "";
|
|
Timer _timer = new Timer();
|
|
try {
|
|
URL url = new URL("http://" + host + ":"
|
|
+ port + "/");
|
|
|
|
// TEST with running a local phantom instance
|
|
// url = new URL("http://" + host + ":7777/");
|
|
// logger.log(Level.INFO, "requesting url: " + url.toString());
|
|
// logger.log(Level.INFO, "parameters: " + params);
|
|
|
|
state = ServerState.BUSY;
|
|
|
|
_timer.schedule(new TimeOut(this), maxTimeout);
|
|
|
|
URLConnection connection = url.openConnection();
|
|
connection.setDoOutput(true);
|
|
connection.setConnectTimeout(connectTimeout);
|
|
connection.setReadTimeout(readTimeout);
|
|
|
|
OutputStream out = connection.getOutputStream();
|
|
out.write(params.getBytes("utf-8"));
|
|
out.close();
|
|
InputStream in = connection.getInputStream();
|
|
response = IOUtils.toString(in, "utf-8");
|
|
|
|
in.close();
|
|
_timer.cancel();
|
|
state = ServerState.IDLE;
|
|
} catch (SocketTimeoutException ste) {
|
|
_timer.cancel();
|
|
throw new SocketTimeoutException(ste.getMessage());
|
|
} catch (Exception e) {
|
|
if(state == ServerState.TIMEDOUT) {
|
|
throw new TimeoutException(e.getMessage());
|
|
}
|
|
_timer.cancel();
|
|
throw new SVGConverterException(e.getMessage());
|
|
}
|
|
return response;
|
|
}
|
|
|
|
public void cleanup() {
|
|
try {
|
|
/* It's not enough to only destroy the process, this helps*/
|
|
process.getErrorStream().close();
|
|
process.getInputStream().close();
|
|
process.getOutputStream().close();
|
|
} catch (IOException e) {
|
|
logger.log(Level.SEVERE, "Error while shutting down process: {0}", e.getMessage());
|
|
}
|
|
|
|
process.destroy();
|
|
process = null;
|
|
logger.log(Level.FINE, "Destroyed phantomJS process running on port {0}", port);
|
|
}
|
|
|
|
public int getPort() {
|
|
return port;
|
|
}
|
|
|
|
public String getHost() {
|
|
return host;
|
|
}
|
|
|
|
public ServerState getState() {
|
|
return state;
|
|
}
|
|
|
|
public void setState(ServerState state) {
|
|
this.state = state;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return this.getClass().getName() + "listening to port: " + port;
|
|
}
|
|
}
|