All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jasperreports.phantomjs.PhantomJSProcess Maven / Gradle / Ivy

There is a newer version: 6.21.2
Show newest version
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see .
 */
package net.sf.jasperreports.phantomjs;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jasperreports.engine.JRPropertiesUtil.PropertySuffix;
import net.sf.jasperreports.engine.JRRuntimeException;

/**
 * @author Lucian Chirita ([email protected])
 */
public class PhantomJSProcess
{
	private static final Log log = LogFactory.getLog(PhantomJSProcess.class);
	
	public static final String EXCEPTION_MESSAGE_KEY_FAILED_START = "phantomjs.failed.start";
	
	public static final String PHANTOMJS_CONFIRMATION_MESSAGE = "PROCESS_STARTED";
	
	private static final AtomicLong ID_COUNTER = new AtomicLong();

	private String id;
	private ProcessDirector director;
	private URI listenURI;
	
	private Process process;
	private ProcessConnection processConnection;
	private volatile boolean ended;
	private AtomicReference exitCode = new AtomicReference();

	public PhantomJSProcess(ProcessDirector director, int listenPort)
	{
		this.id = "phantomjs#" + ID_COUNTER.incrementAndGet();
		this.director = director;
		this.listenURI = listenURI(director.getListenAddress(), listenPort);
	}
	
	private URI listenURI(Inet4Address listenAddress, int listenPort)
	{
		try
		{
			return new URI("http", null, listenAddress.getHostAddress(), listenPort, null, null, null);
		}
		catch (URISyntaxException e)
		{
			throw new JRRuntimeException(e);
		}
	}
	
	public String getId()
	{
		return id;
	}
	
	public URI getListenURI()
	{
		return listenURI;
	}
	
	public void startPhantomJS()
	{
		String mainScriptTempName = director.getScriptManager().getScriptFilename(PhantomJS.MAIN_SCRIPT_RESOURCE);
		String listenAddress = listenURI.getHost() + ":" + listenURI.getPort();
		int idleTimeout = director.getProcessIdleTimeout();
		
		List command = new ArrayList();
		command.add(director.getPhantomjsExecutablePath());
		String options = "";
		if(director.getOptions() != null)
		{
			for(PropertySuffix suffix : director.getOptions())
			{
				String option = suffix.getValue();
				if(option != null && !option.trim().isEmpty())
				{	
					command.add(option.trim());
					options += option.trim() + " ";
				}
			}
		}
		
		command.add(mainScriptTempName);
		command.add("-listenAddress");
		command.add(listenAddress);
		command.add("-confirmMessage");
		command.add(PHANTOMJS_CONFIRMATION_MESSAGE);
		command.add("-idleTimeout");
		command.add(Integer.toString(idleTimeout));

		log.info("PhantomJS process " + id + " starting on port " + listenURI.getPort());
		if (log.isDebugEnabled())
		{
			log.debug(id + " starting phantomjs process with command: "
					+ director.getPhantomjsExecutablePath() + options + " \"" + mainScriptTempName + "\""
					+ " -listenAddress \"" + listenAddress + "\""
					+ " -confirmMessage \"" + PHANTOMJS_CONFIRMATION_MESSAGE + "\""
					+ " -idleTimeout " + idleTimeout + "");
		}

		ProcessBuilder pb = new ProcessBuilder(command);
		pb.redirectErrorStream(false);
		pb.directory(director.getScriptManager().getTempFolder());

		try
		{
			process = pb.start();
			
			ProcessOutputReader outputReader = new ProcessOutputReader(this);
			outputReader.start();
			boolean started = outputReader.waitConfirmation(director.getProcessStartTimeout());
			if (!started)
			{
				log.error("PhantomJS process " + id + " failed to start");//TODO lucianc write error output
				process.destroy();
				
				throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_FAILED_START, (Object[]) null);
			}
			
			processConnection = new ProcessConnection(director, this);
		}
		catch (IOException e)
		{
			throw new JRRuntimeException(e);
		}
	}
	
	protected Process getProcess()
	{
		return process;
	}
	
	public ProcessConnection getProcessConnection()
	{
		return processConnection;
	}
	
	protected void signalEnd()
	{
		if (log.isDebugEnabled())
		{
			log.debug(id + " signal end");
		}
		
		ended = true;
		
		if (exitCode.get() == null)
		{
			//let's try to determine the exit code
			Integer exitValue;
			try
			{
				exitValue = process.exitValue();
			}
			catch (IllegalThreadStateException e)
			{
				//called too soon, we'll have to wait
				if (log.isDebugEnabled())
				{
					log.debug(id + " waiting for exit value");
				}
				
				try
				{
					exitValue = process.waitFor();
				}
				catch (InterruptedException ie)
				{
					if (log.isDebugEnabled())
					{
						log.debug(id + " wait interrupted", e);
					}
					
					exitValue = null;
				}
			}
			
			if (exitValue != null && exitCode.compareAndSet(null, exitValue))
			{
				log.info("PhantomJS process " + id + " done, exit value " + exitValue);
			}
		}
	}
	
	public boolean hasEnded()
	{
		return ended;
	}
	
	public void dispose()
	{
		if (processConnection != null)
		{
			processConnection.dispose();
		}
		
		if (process != null && exitCode.get() == null)
		{
			log.info("PhantomJS process " + id + " to be destroyed");
			
			//TODO lucianc destroyForcibly in Java 8
			process.destroy();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy