| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.extbrowser; |
| |
| import java.awt.EventQueue; |
| import java.io.*; |
| import java.net.*; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import org.openide.awt.StatusDisplayer; |
| import org.openide.execution.NbProcessDescriptor; |
| import org.openide.util.Exceptions; |
| import org.openide.util.NbBundle; |
| |
| import org.openide.util.RequestProcessor; |
| |
| /** |
| * The UnixBrowserImpl is implementation of browser that displays content in |
| * external program (Netscape). It is usable on Unix platform only because it |
| * uses command line option specific to this environment. |
| * Additionally it uses some XWindow utilities to get information about |
| * browser windows. |
| * |
| * @author Radim Kubacki |
| */ |
| public class UnixBrowserImpl extends ExtBrowserImpl { |
| |
| /** Number of probes to get exit status of executed command. |
| * Status is checked after each second. |
| */ |
| protected static final int CMD_TIMEOUT = 6; |
| |
| private static RequestProcessor RP = new RequestProcessor(); |
| |
| /** Creates modified NbProcessDescriptor that can be used to start |
| * browser process when <CODE>-remote openURL()</CODE> options |
| * cannot be used. |
| * @return command or <CODE>null</CODE> |
| * @param p Original command. |
| */ |
| protected static NbProcessDescriptor createPatchedExecutable (NbProcessDescriptor p) { |
| NbProcessDescriptor newP = null; |
| |
| String [] args = org.openide.util.Utilities.parseParameters(p.getArguments()); |
| if (args.length > 1) { |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Old arguments: " + p.getArguments()); // NOI18N |
| } |
| StringBuffer newArgs = new StringBuffer (); |
| boolean found = false; |
| for (int i=0; i<args.length-1; i++) { |
| if (newArgs.length() > 0) { |
| newArgs.append(" "); // NOI18N |
| } |
| if (args[i].indexOf("-remote") >= 0 // NOI18N |
| && args[i+1].indexOf("openURL(") >=0) { // NOI18N |
| found = true; |
| newArgs.append("\"{URL}\""); // NOI18N |
| } |
| else { |
| newArgs.append("\""+args[i]+"\""); // NOI18N |
| } |
| } |
| if (found) { |
| newP = new NbProcessDescriptor (p.getProcessName(), newArgs.toString(), p.getInfo()); |
| } |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "ProcessName: " + p.getProcessName()); // NOI18N |
| ExtWebBrowser.getEM().log(Level.FINE, "New arguments: " + newArgs.toString()); // NOI18N |
| } |
| } |
| return newP; |
| } |
| |
| /** Creates new UnixBrowserImpl */ |
| public UnixBrowserImpl () { |
| this (null); |
| } |
| |
| /** Creates new UnixBrowserImpl |
| * @param extBrowserFactory Associated browser factory to get settings from. |
| */ |
| public UnixBrowserImpl (ExtWebBrowser extBrowserFactory) { |
| super(); |
| this.extBrowserFactory = extBrowserFactory; |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "UnixBrowserImpl created from factory: " + extBrowserFactory); // NOI18N |
| } |
| } |
| |
| /** |
| * Sets current URL.</P> |
| * |
| * <P>If browser is running and we know window ID we call |
| * <CODE>browser_command -id _winID_ -raise -remote 'openURL(_url)'</CODE> |
| * else we start it with |
| * <CODE>browser_command _url_</CODE></P> |
| * |
| * @param url URL to show in the browser. |
| */ |
| @Override |
| protected void loadURLInBrowserInternal(URL url) { |
| assert !EventQueue.isDispatchThread(); |
| |
| NbProcessDescriptor cmd = extBrowserFactory.getBrowserExecutable (); // NOI18N |
| Process p; |
| StatusDisplayer sd = StatusDisplayer.getDefault (); |
| try { |
| // internal protocols cannot be displayed in external viewer |
| url = URLUtil.createExternalURL(url, false); |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "External url: " + url); // NOI18N |
| } |
| |
| cmd = extBrowserFactory.getBrowserExecutable (); // NOI18N |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Executable: " + cmd); // NOI18N |
| } |
| sd.setStatusText (NbBundle.getMessage (UnixBrowserImpl.class, "MSG_Running_command", cmd.getProcessName ())); |
| p = cmd.exec (new ExtWebBrowser.UnixBrowserFormat (url.toString ())); |
| |
| RP.post (new Status (cmd, p, url), 1000); |
| |
| pcs.firePropertyChange (PROP_URL, getURL(), url); |
| } |
| catch (java.io.IOException ex) { |
| ExtWebBrowser.getEM().log(Level.INFO, null, ex); |
| // occurs when executable is not found or not executable |
| BrowserUtils.notifyMissingBrowser(cmd.getProcessName()); |
| } |
| catch (NumberFormatException ex) { |
| Logger.getLogger("global").log(Level.INFO, null, ex); |
| } |
| catch (java.lang.Exception ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| |
| /** Object that checks execution result |
| * of browser invocation request. |
| * <p>It can made another attempt to start the browser |
| * when error output contains information that communication |
| * through Xremote protocol failed. |
| */ |
| private class Status implements Runnable { |
| |
| /** Message printed when invocation fails even though the |
| * application runs, but there's no browser window (only mail client, e.g.). |
| */ |
| private static final String FAILURE_MSG_BADWINDOW = "BadWindow"; // NOI18N |
| |
| /** Message printed when invocation fails because the application does not run. */ |
| private static final String FAILURE_MSG = "No running window found."; // NOI18N |
| |
| /** Originally executed command. */ |
| private NbProcessDescriptor cmd; |
| |
| /** Handle to executed process. */ |
| private Process p; |
| |
| /** URL to be displayed. */ |
| private URL url; |
| |
| /** Retries counter. */ |
| private int retries = CMD_TIMEOUT; |
| |
| /** Creates Status object to check execution result |
| * of browser invocation request. |
| * @param cmd Originally executed command. |
| * @param p Process that is checked. |
| * @param url Displayed URL that can be used when another attempt |
| * to start the browser is made or <CODE>null</CODE>. |
| */ |
| public Status (NbProcessDescriptor cmd, Process p, URL url) { |
| this. cmd = cmd; |
| this.p = p; |
| this.url = url; |
| } |
| |
| /** Checks whether process is correctly executed. |
| * If it returns bad exit code or prints know error message |
| * it is re-executed once again. |
| * If the execution is not finished during timeout message is displayed. |
| */ |
| public void run () { |
| try { |
| // wait for process to finish before testing exit status: |
| p.waitFor(); |
| } catch (InterruptedException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| |
| boolean retried = false; |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Retried: " + retried); // NOI18N |
| } |
| int exitStatus = 1; |
| Reader r = new InputStreamReader (p.getErrorStream ()); |
| try { |
| exitStatus = p.exitValue(); |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Command executed. exitValue = " + exitStatus); // NOI18N |
| } |
| } catch (IllegalThreadStateException ex) { |
| retries--; |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Retries: " + retries); // NOI18N |
| ExtWebBrowser.getEM().log(Level.FINE, "Time: " + System.currentTimeMillis()); // NOI18N |
| } |
| if (retries > 0) { |
| RP.post(this, 1000); |
| return; |
| } else { |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Command not finished yet"); // NOI18N |
| } |
| } |
| } |
| |
| // hack : Netscape exits with 0 on Linux even if there is no window |
| if (exitStatus == 0 && org.openide.util.Utilities.getOperatingSystem() == org.openide.util.Utilities.OS_LINUX) { |
| final int LEN = 2048; |
| char [] buff = new char [LEN]; |
| int l; |
| StringBuffer sb = new StringBuffer (); |
| try { |
| while ((l = r.read (buff, 0, LEN)) != -1) { |
| sb.append (buff, 0, l); |
| } |
| if (sb.toString ().indexOf (FAILURE_MSG) >= 0) { |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Browser output: \"" + FAILURE_MSG + "\""); // NOI18N |
| } |
| exitStatus = 2; |
| } |
| } catch (java.io.IOException ioe) { |
| // suppose it was executed |
| ExtWebBrowser.getEM().log(Level.WARNING, null, ioe); |
| } |
| } |
| |
| // mozilla & netscape exits with 1 on Linux if there's mail window present, |
| // but there's no browser window - the URL is shown correctly, though |
| if (exitStatus == 1 && org.openide.util.Utilities.getOperatingSystem() == org.openide.util.Utilities.OS_LINUX) { |
| final int LEN = 2048; |
| char [] buff = new char [LEN]; |
| int l; |
| StringBuffer sb = new StringBuffer (); |
| try { |
| while ((l = r.read (buff, 0, LEN)) != -1) { |
| sb.append (buff, 0, l); |
| } |
| if (sb.toString ().indexOf (FAILURE_MSG_BADWINDOW) >= 0) { |
| if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) { |
| ExtWebBrowser.getEM().log(Level.FINE, "Browser output: \"" + FAILURE_MSG_BADWINDOW + "\""); // NOI18N |
| } |
| exitStatus = 0; |
| } |
| } catch (java.io.IOException ioe) { |
| // suppose it was executed |
| ExtWebBrowser.getEM().log(Level.WARNING, null, ioe); |
| } |
| } |
| |
| if (exitStatus == 2) { |
| try { |
| NbProcessDescriptor startCmd = UnixBrowserImpl.createPatchedExecutable(cmd); |
| if (startCmd != null) { |
| retried = true; |
| StatusDisplayer.getDefault(). |
| setStatusText (NbBundle.getMessage (UnixBrowserImpl.class, "MSG_Running_command", startCmd.getProcessName ())); |
| Process pr = startCmd.exec (new ExtWebBrowser.UnixBrowserFormat (url.toString ())); |
| |
| // do not care about result now |
| // RequestProcessor.getDefault ().post (new Status (startCmd, pr, null), 1000); |
| } |
| } |
| catch (java.io.IOException ioe) { |
| // suppose it was executed |
| ExtWebBrowser.getEM().log(Level.WARNING, null, ioe); |
| } |
| } |
| |
| // #219040 - Running page in Chrome shows warning dialog. |
| // Ignore exitStatus 23 to workaround it - it is a Chrome's bug |
| // http://code.google.com/p/chromium/issues/detail?id=146762 |
| if (exitStatus != 0 && !retried && exitStatus != 23) { |
| BrowserUtils.notifyMissingBrowser(cmd.getProcessName()); |
| return; |
| } |
| |
| } |
| } |
| } |