/******************************************************************************** * * * (c) Copyright 2010 Verizon Communications USA and The Open University UK * * * * This software is freely distributed in accordance with * * the GNU Lesser General Public (LGPL) license, version 3 or later * * as published by the Free Software Foundation. * * For details see LGPL: http://www.fsf.org/licensing/licenses/lgpl.html * * and GPL: http://www.fsf.org/licensing/licenses/gpl-3.0.html * * * * This software is provided by the copyright holders and contributors "as is" * * and any express or implied warranties, including, but not limited to, the * * implied warranties of merchantability and fitness for a particular purpose * * are disclaimed. In no event shall the copyright owner or contributors be * * liable for any direct, indirect, incidental, special, exemplary, or * * consequential damages (including, but not limited to, procurement of * * substitute goods or services; loss of use, data, or profits; or business * * interruption) however caused and on any theory of liability, whether in * * contract, strict liability, or tort (including negligence or otherwise) * * arising in any way out of the use of this software, even if advised of the * * possibility of such damage. * * * ********************************************************************************/ package com.compendium.ui; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Properties; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.compendium.LanguageProperties; import com.compendium.ProjectCompendium; import com.compendium.core.datamodel.LinkedFile; import com.compendium.core.datamodel.LinkedFileDatabase; /** * This is an execute control class that invokes the default system * browser or other application as required. Can be used for UNIX or Windows platforms (for browser execution only). * * To execute this class to open a browser from outside env, type the class name * followed by the URL to display * * @author Mohammed Sajid Ali / Michelle Bachler */ public class ExecuteControl { static final Logger log = LoggerFactory.getLogger(ExecuteControl.class); /** The default system browser under windows.*/ //private static final String WIN_PATH = "rundll32"; /** The flag to display a url.*/ //private static final String WIN_FLAG = "url.dll,FileProtocolHandler"; /** The default browser under unix.*/ //private static final String UNIX_PATH = "netscape"; /** The flag to display a url.*/ //private static final String UNIX_FLAG = "-remote openURL"; /** * Launch a file or URL in the relevant application. * @author Sebastian Ehrich * @param path the path to the file or URL path to launch"). */ public static String launch( String path ) { try { if (path != null && !path.equals("")) { //$NON-NLS-1$ /* fetch file from database if needed */ if (LinkedFileDatabase.isDatabaseURI(path)) { LinkedFile lf; try { lf = new LinkedFileDatabase(new URI(path)); } catch (URISyntaxException e1) { log.error("Exception: (ExecuteControl.launch) Could not create URI for linked File."); //$NON-NLS-1$ e1.printStackTrace(); return (null); } File tempFile = lf.getFile(ProjectCompendium.temporaryDirectory); if (launchFile(tempFile.getAbsolutePath())) { updateDBWhenFinished(lf, tempFile); return path; } else return null; } else { if (launchFile(UIUtilities.modifyPath(path))) return path; else return null; } } } catch (IllegalThreadStateException e) { log.info("Exception: (ExecuteControl.launch) " //$NON-NLS-1$ + e.getMessage()); return null; } catch (IOException e) { log.info("Exception: (ExecuteControl.launch) " //$NON-NLS-1$ + e.getMessage()); return null; } catch (InterruptedException e) { log.info("Exception: (ExecuteControl.launch) " //$NON-NLS-1$ + e.getMessage()); return null; } catch (Exception e) { log.info("Exception: (ExecuteControl.launch) " //$NON-NLS-1$ + e.getMessage()); return null; } return null; } /** * Launch relevant application to view/edit the file given by "path". This * method blocks until the application has finished. * @param path path or URI to the file to open * @return true when an application was launched successfully, false otherwise. * @throws IllegalThreadStateException * @throws IOException * @throws InterruptedException */ public static boolean launchFile(String path) throws IllegalThreadStateException, IOException, InterruptedException { if (ProjectCompendium.isWindows) { return launchWindowsCommand(path); } else if (ProjectCompendium.isMac) { return launchMacCommand(path); } else if (ProjectCompendium.isLinux) { if (FormatProperties.useKFMClient) { return launchLinuxCommandKDE(path); } else { return launchLinuxCommand(path); } } return false; } /** * Pops up a non-modal dialog asking the user whether he has finished viewing * or editing the given temporary copy of a database file and updates the * database depending on user input. * <br /> * Note: It would be nice not to have to ask the user about when he has * finished editing. But an automated solution seems not feasible as there are * so many differences in how each individual application behaves, let alone on * different OSes. * @author Sebastian Ehrich * @param lf The Compendium object representing a file in the database. * @param tmpcopy The temporary copy of the database object that is currently * viewed/edited. This file will be deleted (except for when the user chooses * to update but update fails). */ private static void updateDBWhenFinished( LinkedFile lf, File tmpcopy ) { final JOptionPane jop = new JOptionPane(); final Object[] possibleValues = { LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.option1"), //$NON-NLS-1$ LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.option2") }; //$NON-NLS-1$ String message = LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.message1a") +"\n"+ //$NON-NLS-1$ //$NON-NLS-2$ LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.message1b")+":"; //$NON-NLS-1$ //$NON-NLS-2$ jop.setMessage(message); jop.setOptions(possibleValues); jop.setMessageType(JOptionPane.INFORMATION_MESSAGE); jop.addPropertyChangeListener(new DBUpdateListener(lf, tmpcopy, jop)); JDialog jd = jop.createDialog(ProjectCompendium.APP, LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.messageTitle") + lf.getName()); //$NON-NLS-1$ jd.setModal(false); jd.setVisible(true); } /** * Display a file or URL in the system application on a Windows Machine. * * @param path, the path to the file or URL path to launch"). */ private static boolean launchWindowsCommand(String path) throws IOException, InterruptedException, IllegalThreadStateException { if (ProjectCompendium.platform.indexOf("98") != -1) { //$NON-NLS-1$ Process p = Runtime.getRuntime().exec("start \"" + path + "\""); //$NON-NLS-1$ //$NON-NLS-2$ p.waitFor(); } else { Process p = Runtime.getRuntime().exec("cmd.exe /c start \"Compendium Reference node\" \"" + path + "\""); //$NON-NLS-1$ //$NON-NLS-2$ p.waitFor(); } return true; } /** * Display a file or URL in the system application on a Mac machine. * * @param path, the path to the file or URL path to launch"). */ private static boolean launchMacCommand(String path) throws IOException, InterruptedException, IllegalThreadStateException { Process p = Runtime.getRuntime().exec(new String[] {"open", path}); //$NON-NLS-1$ if (p.waitFor() != 0) { // DID WE STORE THE INFO FROM A PREVIOUS ATTEMPT? String refString = path.toLowerCase(); String key = ""; //$NON-NLS-1$ if (refString.startsWith("www.")) //$NON-NLS-1$ key = "www"; //$NON-NLS-1$ else if (refString.startsWith("http:")) //$NON-NLS-1$ key = "http"; //$NON-NLS-1$ else if (refString.startsWith("https:")) //$NON-NLS-1$ key = "https"; //$NON-NLS-1$ else if (refString.startsWith("file:")) //$NON-NLS-1$ key = "file"; //$NON-NLS-1$ else if ( refString.indexOf("\n") == -1 && refString.indexOf("\r") == -1 //$NON-NLS-1$ //$NON-NLS-2$ && refString.length() <= 100 && refString.indexOf("@") != -1) //$NON-NLS-1$ key = "email"; //$NON-NLS-1$ else { int index = refString.lastIndexOf("."); //$NON-NLS-1$ if (index != -1) { key = refString.substring(index+1); } } String application = ""; //$NON-NLS-1$ Properties apps = null; if (!key.equals("")) { //$NON-NLS-1$ apps = ProjectCompendium.APP.getLaunchApplications(); String value = apps.getProperty(key); if (value != null) application = value; } if (!application.equals("")) { //$NON-NLS-1$ p = Runtime.getRuntime().exec(new String[] {"open", "-a", application, path}); //$NON-NLS-1$ //$NON-NLS-2$ if (p.waitFor() != 0) log.info("FAILED to launch "+path); //$NON-NLS-1$ else return true; } // We get here when 'open' couldn't open the file, so we // need to ask the user to find the app for us UIFileChooser fileDialog = new UIFileChooser(); fileDialog.setDialogTitle(LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.selectApplication")); //$NON-NLS-1$ fileDialog.setApproveButtonText(LanguageProperties.getString(LanguageProperties.DIALOGS_BUNDLE, "ExecuteControl.loadButton")); //$NON-NLS-1$ fileDialog.setApproveButtonMnemonic(LanguageProperties.getString(LanguageProperties.DIALOGS_BUNDLE, "ExecuteControl.loadButtonMnemonic").charAt(0)); //$NON-NLS-1$ int retval = fileDialog.showOpenDialog(ProjectCompendium.APP); if (retval == JFileChooser.APPROVE_OPTION) { if (fileDialog.getSelectedFile() != null) { // Now we can try to open the file again File app = new File(fileDialog.getSelectedFile().getAbsolutePath()); p = Runtime.getRuntime().exec(new String[] {"open", "-a", app.getPath(), path}); //$NON-NLS-1$ //$NON-NLS-2$ if (p.waitFor() != 0) log.info("FAILED to launch "+path); //$NON-NLS-1$ else { if (!key.equals("")) { //$NON-NLS-1$ apps.put(key, app.getPath()); apps.store(new FileOutputStream("System"+ProjectCompendium.sFS+"resources"+ProjectCompendium.sFS+"LaunchApplications.properties"), "Launch Application Details"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } return true; } } } } else { return true; } return false; } /** * Display a file or URL in the system application on a Linux machine. * * @param path, the path to the file or URL path to launch"). */ private static boolean launchLinuxCommand(String path) throws IOException, InterruptedException, IllegalThreadStateException { String refString = path.toLowerCase(); String key = ""; //$NON-NLS-1$ if (refString.startsWith("www.")) //$NON-NLS-1$ key = "www"; //$NON-NLS-1$ else if (refString.startsWith("http:")) //$NON-NLS-1$ key = "http"; //$NON-NLS-1$ else if (refString.startsWith("https:")) //$NON-NLS-1$ key = "https"; //$NON-NLS-1$ else if (refString.startsWith("file:")) //$NON-NLS-1$ key = "file"; //$NON-NLS-1$ else if ( refString.indexOf("\n") == -1 && refString.indexOf("\r") == -1 //$NON-NLS-1$ //$NON-NLS-2$ && refString.length() <= 100 && refString.indexOf("@") != -1) //$NON-NLS-1$ key = "email"; //$NON-NLS-1$ else { int index = refString.lastIndexOf("."); //$NON-NLS-1$ if (index != -1) { key = refString.substring(index+1); } } String application = ""; //$NON-NLS-1$ Properties apps = null; if (!key.equals("")) { //$NON-NLS-1$ apps = ProjectCompendium.APP.getLaunchApplications(); String value = apps.getProperty(key); if (value != null) application = value; } if (!application.equals("")) { //$NON-NLS-1$ Process p = Runtime.getRuntime().exec(new String[] {application, path}); if (p.waitFor() != 0) log.info("FAILED to launch "+path); //$NON-NLS-1$ else return true; } // We get here when 'open' couldn't open the file, so we // need to ask the user to find the app for us JFileChooser fileDialog = new JFileChooser(); fileDialog.setDialogTitle(LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "ExecuteControl.selectApplication")); //$NON-NLS-1$ int retval = fileDialog.showOpenDialog(ProjectCompendium.APP); if (retval == JFileChooser.APPROVE_OPTION) { if ((fileDialog.getSelectedFile()) != null) { String fileName = fileDialog.getSelectedFile().getAbsolutePath(); File app = new File(fileName); Process p = Runtime.getRuntime().exec(new String[] {app.getPath(), path}); if (p.waitFor() != 0) log.info("FAILED to launch "+path); //$NON-NLS-1$ else { if (!key.equals("")) { //$NON-NLS-1$ apps.put(key, app.getPath()); apps.store(new FileOutputStream("System"+ProjectCompendium.sFS+"resources"+ProjectCompendium.sFS+"LaunchApplications.properties"), "Launch Application Details"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } return true; } } } return false; } /** * Display a file or URL in the system application on a KDE Linux machine. * Used when launching applications under KDE, i.e. kfmclient is handling all files extensions appropriately. * * @param path, the path to the file or URL path to launch"). */ private static boolean launchLinuxCommandKDE(String path) throws IOException, InterruptedException, IllegalThreadStateException { if (path.startsWith("www.")) { //$NON-NLS-1$ path= "http://"+path; //$NON-NLS-1$ } Process p = Runtime.getRuntime().exec(new String[] {"/usr/bin/kfmclient", "exec" , path}); //$NON-NLS-1$ //$NON-NLS-2$ int reply = p.waitFor(); //reply seems to always be 1? Not sure why, but it launches OK. if (reply != 0 && reply != 1){ log.info("FAILED to launch "+path); //$NON-NLS-1$ return false; } else { return true; } } }