/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.core.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import edu.stanford.ejalbert.BrowserLauncher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.settings.ExternalToolSettings;
import org.pentaho.reporting.designer.core.settings.SettingsUtil;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.libraries.base.util.StringUtils;
/**
* User: Martin Date: 01.03.2006 Time: 17:55:15
*/
public class ExternalToolLauncher
{
private static final Log logger = LogFactory.getLog(ExternalToolLauncher.class);
private static final long timeout = 10000;
private ExternalToolLauncher()
{
}
public static void openURL(final String url) throws IOException
{
//noinspection ConstantConditions
if (url == null)
{
throw new IllegalArgumentException("url must not be null");
}
final ExternalToolSettings instance = ExternalToolSettings.getInstance();
if (instance.isUseDefaultBrowser())
{
try
{
new BrowserLauncher().openURLinBrowser(url);
}
catch (Exception e)
{
UncaughtExceptionsModel.getInstance().addException(e);
}
}
else
{
if(execute(instance.getCustomBrowserExecutable(),
instance.getCustomBrowserParameters(), url) == false)
{
throw new IOException(UtilMessages.getInstance().getString("ExternalToolLauncher.errorMessage",instance.getCustomBrowserExecutable()));
}
}
}
public static void openPDF(final File file) throws IOException
{
//noinspection ConstantConditions
if (file == null)
{
throw new IllegalArgumentException("file must not be null");
}
final ExternalToolSettings toolSettings = ExternalToolSettings.getInstance();
if (toolSettings.isUseDefaultPDFViewer())
{
openDefaultViewer(file);
}
else
{
if(execute
(toolSettings.getCustomPDFViewerExecutable(),
toolSettings.getCustomPDFViewerParameters(),
file.getCanonicalPath()) == false)
{
throw new IOException(UtilMessages.getInstance().getString("ExternalToolLauncher.errorMessage", toolSettings.getCustomPDFViewerExecutable()));
}
}
}
public static void openXLS(final File file) throws IOException
{
//noinspection ConstantConditions
if (file == null)
{
throw new IllegalArgumentException("file must not be null");
}
final ExternalToolSettings toolSettings = ExternalToolSettings.getInstance();
if (toolSettings.isUseDefaultXLSViewer())
{
try
{
openDefaultViewer(file);
}
catch (Exception e)
{
UncaughtExceptionsModel.getInstance().addException(e);
}
}
else
{
if(execute(
toolSettings.getCustomXLSViewerExecutable(),
toolSettings.getCustomXLSViewerParameters(),
file.getCanonicalPath()) == false)
{
throw new IOException(UtilMessages.getInstance().getString("ExternalToolLauncher.errorMessage",toolSettings.getCustomXLSViewerExecutable()));
}
}
}
public static void openRTF(final File file) throws IOException
{
//noinspection ConstantConditions
if (file == null)
{
throw new IllegalArgumentException("file must not be null");
}
final ExternalToolSettings toolSettings = ExternalToolSettings.getInstance();
if (toolSettings.isUseDefaultRTFViewer())
{
openDefaultViewer(file);
}
else
{
if(execute(
toolSettings.getCustomRTFViewerExecutable(),
toolSettings.getCustomRTFViewerParameters(),
file.getCanonicalPath()) == false)
{
throw new IOException(UtilMessages.getInstance().getString("ExternalToolLauncher.errorMessage",toolSettings.getCustomRTFViewerExecutable()));
}
}
}
public static void openCSV(final File file) throws IOException
{
//noinspection ConstantConditions
if (file == null)
{
throw new IllegalArgumentException("file must not be null");
}
final ExternalToolSettings toolSettings = ExternalToolSettings.getInstance();
if (toolSettings.isUseDefaultCSVViewer())
{
openDefaultViewer(file);
}
else
{
if(execute(toolSettings.getCustomCSVViewerExecutable(),
toolSettings.getCustomCSVViewerParameters(),
file.getCanonicalPath()) == false)
{
throw new IOException(UtilMessages.getInstance().getString("ExternalToolLauncher.errorMessage",toolSettings.getCustomCSVViewerExecutable()));
}
}
}
public static boolean execute(final String executable, final String parameters, final String file)
throws IOException
{
// todo: Use a stream tokenizer (well, a custom one, as the builtin one messes up escaped quotes)
// so that we can handle quoting gracefully ..
boolean exitValue = false;
final ArrayList<String> command = new ArrayList<String>();
command.add(executable);
for (StringTokenizer tokenizer = new StringTokenizer(parameters); tokenizer.hasMoreTokens();)
{
String s = tokenizer.nextToken();
s = s.replace("{0}", file);
command.add(s);
}
final String osname = safeSystemGetProperty("os.name", "<protected by system security>"); // NON-NLS
if (StringUtils.startsWithIgnoreCase(osname, "Mac OS X")) // NON-NLS
{
logger.debug("Assuming Mac-OS X."); // NON-NLS
if (executable.endsWith(".app") || executable.endsWith(".app/")) // NON-NLS
{
command.add(0, "-a"); // NON-NLS
command.add(0, "/usr/bin/open"); // NON-NLS
}
}
final ProcessBuilder processBuilder = new ProcessBuilder(command.toArray(new String[command.size()]));
Process process = null;
ProcessWrapper processWrapper = null;
try{
process = processBuilder.start();
processWrapper = new ProcessWrapper(process);
processWrapper.start();
processWrapper.join(timeout);
if (processWrapper.getfExitCode() != null) {
exitValue = processWrapper.getfExitCode() == 0;
} else {
// Set our exit code to 1
exitValue = false;
process.destroy();
}
//final Process p = process.start();
//p.waitFor();
//exitValue = p.exitValue() == 0;
// 0 == normal; 2 == permissions issue, 3 == no rules found in mimeType
logger.debug("ProcessWrapper exitCode = " + processWrapper.getfExitCode());
}
catch(InterruptedException ie){
processWrapper.interrupt();
Thread.currentThread().interrupt();
process.destroy();
exitValue = false;
}
catch (Exception e)
{
logger.error("Error in execute shell command "+command +" error: "+e.getMessage());
// process fails so redirect to openURL command to locate viewer
exitValue = false;
}
return exitValue;
}
protected static String safeSystemGetProperty(final String name,
final String defaultValue)
{
try
{
return System.getProperty(name, defaultValue);
}
catch (final SecurityException se)
{
// ignore exception
return defaultValue;
}
}
public static void openDefaultViewer(final File file) throws IOException
{
final String osname = safeSystemGetProperty("os.name", "<protected by system security>"); // NON-NLS
final String jrepath = safeSystemGetProperty("java.home", "."); // NON-NLS
final String fs = safeSystemGetProperty("file.separator", File.separator); // NON-NLS
logger.debug("Running on operating system: " + osname); // NON-NLS
if (StringUtils.startsWithIgnoreCase(osname, "windows")) // NON-NLS
{
logger.debug("Detected Windows."); // NON-NLS
if (execute("rundll32.exe", "SHELL32.DLL,ShellExec_RunDLL {0}", file.getAbsolutePath()) == false) // NON-NLS
{
openURL(file.toURI().toURL().toString());
}
}
else if (StringUtils.startsWithIgnoreCase(osname, "Mac OS X")) // NON-NLS
{
logger.debug("Assuming Mac-OS X."); // NON-NLS
if (execute("/usr/bin/open", "{0}", file.getCanonicalPath()) == false) // NON-NLS
{
openURL(file.toURI().toURL().toString());
}
}
else if (StringUtils.startsWithIgnoreCase(osname, "Linux") || // NON-NLS
StringUtils.startsWithIgnoreCase(osname, "Solaris") || // NON-NLS
StringUtils.startsWithIgnoreCase(osname, "HP-UX") || // NON-NLS
StringUtils.startsWithIgnoreCase(osname, "AIX") || // NON-NLS
StringUtils.startsWithIgnoreCase(osname, "SunOS")) // NON-NLS
{
logger.debug("Assuming unix."); // NON-NLS
final File mailcapExe = new File("/usr/bin/run-mailcap"); // NON-NLS
if (mailcapExe.exists())
{
logger.debug("found /usr/bin/run=mailcap");
if (execute("/usr/bin/run-mailcap", "{0}", file.getCanonicalPath()) == false) // NON-NLS
{
openURL(file.toURI().toURL().toString());
}
}
else
{
// use our private version instead ...
final File installDir = SettingsUtil.computeInstallationDirectory();
if (installDir == null)
{
logger.debug("Cannot determine installation directory; using browser as generic launcher."); // NON-NLS
openURL(file.toURI().toURL().toString());
return;
}
final File privateMailCapExe = new File(installDir, "resources/run-mailcap"); // NON-NLS
if (privateMailCapExe.exists())
{
if (execute(privateMailCapExe.getPath(), "{0}", file.getCanonicalPath()) == false)
{
openURL(file.toURI().toURL().toString());
}
}
else
{
logger.debug("private copy of run-mailcap not found; using browser as generic launcher."); // NON-NLS
openURL(file.toURI().toURL().toString());
return;
}
}
}
else
{
logger.debug("Not a known OS-Type; using browser as generic launcher."); // NON-NLS
openURL(file.toURI().toURL().toString());
}
}
static class ProcessWrapper extends Thread {
private final Process fProcess;
private Integer fExitCode;
public Integer getfExitCode() {
return fExitCode;
}
public ProcessWrapper(Process fProcess) {
this.fProcess = fProcess;
}
public void run() {
try {
fExitCode = fProcess.waitFor();
} catch (InterruptedException e) {
fExitCode = 0;
} catch(Exception ex){
fExitCode = -1;
}
}
}
}