/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.server.ui.launching.zend;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.osgi.util.NLS;
import org.eclipse.php.debug.ui.IDebugServerConnectionTest;
import org.eclipse.php.internal.debug.core.PHPDebugPlugin;
import org.eclipse.php.internal.debug.core.preferences.PHPDebugCorePreferenceNames;
import org.eclipse.php.internal.debug.core.zend.communication.DebuggerCommunicationDaemon;
import org.eclipse.php.internal.debug.core.zend.debugger.ZendDebuggerSettingsUtil;
import org.eclipse.php.internal.debug.core.zend.testConnection.DebugServerTestController;
import org.eclipse.php.internal.debug.core.zend.testConnection.DebugServerTestEvent;
import org.eclipse.php.internal.debug.core.zend.testConnection.IDebugServerTestListener;
import org.eclipse.php.internal.server.PHPServerUIMessages;
import org.eclipse.php.internal.server.core.Server;
import org.eclipse.php.internal.server.ui.Logger;
import org.eclipse.php.internal.server.ui.launching.Messages;
import org.eclipse.swt.widgets.Shell;
/**
* Default implementation for Zend Debugger server connectivity test.
*/
public class DefaultDebugServerConnectionTest implements IDebugServerConnectionTest, IDebugServerTestListener {
protected class TestPerformer implements IRunnableWithProgress {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse
* .core.runtime.IProgressMonitor)
*/
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
DebugServerTestController.getInstance().addListener(DefaultDebugServerConnectionTest.this);
monitor.beginTask(PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testingConnectivity"), //$NON-NLS-1$
IProgressMonitor.UNKNOWN);
try {
// Check existence of debugger owner and dummy.php
monitor.subTask(NLS.bind(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testingServerExistence"), //$NON-NLS-1$
fServer.getName()));
checkServer();
if (monitor.isCanceled()) {
return;
}
String[] clientHosts = getAllLocalHostsAddresses();
// Check connection with any of provided client IPs
for (String clientHost : clientHosts) {
if (monitor.isCanceled()) {
return;
}
fCurrentHost = clientHost;
// Build query to call debugger via GET
String debugQuery = generateDebugQuery(clientHost);
monitor.subTask(NLS.bind(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testingCommunication"), //$NON-NLS-1$
clientHost));
try {
// Calling the debugger client test
checkClient(monitor, debugQuery);
} catch (SocketTimeoutException ste) {
// Debugger caused timeout
if (!fIsFinished) {
fTimeoutServerList.add(clientHost);
continue;
}
}
// Go out if cancelled or finished
if (monitor.isCanceled() || fIsFinished) {
return;
}
/*
* The following condition test is due to immediate return,
* but the client host that was sent is unavailable, i.e the
* debugger will not return to 'Neon' version of debugger.
*/
if (!checkTimeout(monitor)) {
break;
}
fTimeoutServerList.add(clientHost);
}
// Go out if cancelled, succeeded or finished
if (monitor.isCanceled() || fIsFinished) {
return;
}
// All client IPs caused timeout
showCustomErrorDialog(
addTimeOutsMessage(PHPServerUIMessages.getString("DefaultDebugServerConnectionTest.1"))); //$NON-NLS-1$
} catch (FileNotFoundException fnfe) {
// dummy.php was not found
String dummyFile = Platform.getPreferencesService().getString(PHPDebugPlugin.ID,
PHPDebugCorePreferenceNames.ZEND_DEBUG_DUMMY_FILE, "", null);
showCustomErrorDialog(NLS.bind(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_theURLCouldNotBeFound"), fURL, //$NON-NLS-1$
dummyFile));
return;
} catch (SocketTimeoutException ste) {
// Timeout occurred when trying to connect to debugger owner
showCustomErrorDialog(NLS
.bind(PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_timeOutMessage"), fURL)); //$NON-NLS-1$
return;
} catch (ConnectException ce) {
// Usually when firewall blocks
showCustomErrorDialog(NLS.bind(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_webServerConnectionFailed"), //$NON-NLS-1$
fURL));
return;
} catch (IOException er) {
// Server not found / server is down
showCustomErrorDialog(NLS.bind(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_webServerConnectionFailed"), //$NON-NLS-1$
fURL));
return;
} catch (Exception ex) {
// Something unexpected happened, report it
Logger.logException(ex);
showCustomErrorDialog(
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_Unexpected_error_occurred")); //$NON-NLS-1$
}
finally {
DebugServerTestController.getInstance().removeListener(DefaultDebugServerConnectionTest.this);
}
}
private boolean checkTimeout(IProgressMonitor monitor) throws InterruptedException {
for (int i = 0; i < 10; i++) {
// Go out if cancelled
if (monitor.isCanceled())
return false;
Thread.sleep(DEFAULT_TIMEOUT / 10);
if (fIsFinished) {
return false;
}
}
return true;
}
private void checkClient(IProgressMonitor monitor, String debugQuery) throws Exception {
fClientTestLatch = new CountDownLatch(1);
fClientTest = new ClientTest(debugQuery);
fClientTest.schedule();
while (!monitor.isCanceled() && !fIsFinished && fClientTest.getState() != Job.NONE) {
try {
fClientTestLatch.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// Ignore
}
}
if (fClientTest.exception != null)
throw fClientTest.exception;
}
private void checkServer() throws MalformedURLException, IOException {
InputStream inputStream = null;
try {
// Check base URL (http://HOST_NAME) and dummy file existence
String dummyFile = Platform.getPreferencesService().getString(PHPDebugPlugin.ID,
PHPDebugCorePreferenceNames.ZEND_DEBUG_DUMMY_FILE, "", //$NON-NLS-1$
null);
final URL checkURL = new URL(fURL + '/' + dummyFile); // $NON-NLS-1$
URLConnection connection = checkURL.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(DEFAULT_TIMEOUT);
inputStream = connection.getInputStream();
/*
* this will fail when host not found and/or dummy.php not found
* (2 different exceptions)
*/
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
protected class ClientTest extends Job {
protected String query;
protected Exception exception;
public ClientTest(String query) {
super(""); //$NON-NLS-1$
this.query = query;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.
* IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
perform();
} catch (Exception e) {
exception = e;
} finally {
fClientTestLatch.countDown();
}
return Status.OK_STATUS;
}
private void perform() throws MalformedURLException, IOException {
InputStream inputStream = null;
try {
URL checkDebugURL = new URL(query);
final URLConnection debugConnection = checkDebugURL.openConnection();
debugConnection.setReadTimeout(DEFAULT_TIMEOUT);
inputStream = debugConnection.getInputStream();
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
private final static int DEFAULT_TIMEOUT = 10000;
protected Shell fShell;
protected Server fServer;
protected String fURL;
protected Boolean fIsFinished = false;
protected ProgressMonitorDialog fProgressDialog = null;
protected List<String> fTimeoutServerList = new ArrayList<String>();
protected ClientTest fClientTest;
protected CountDownLatch fClientTestLatch;
protected String fCurrentHost;
/*
* (non-Javadoc)
*
* @see
* org.eclipse.php.debug.ui.IDebugServerConnectionTest#testConnection(org
* .eclipse.php.internal.server.core.Server, org.eclipse.swt.widgets.Shell)
*/
public void testConnection(Server server, Shell shell) {
fServer = server;
fShell = shell;
fURL = server.getBaseURL();
IRunnableWithProgress runnableWithProgress = new TestPerformer();
fProgressDialog = new ProgressMonitorDialog(fShell);
fProgressDialog.setBlockOnOpen(false);
fProgressDialog.setCancelable(true);
fProgressDialog.open();
fProgressDialog.getShell()
.setText(PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_debugger_connection_test")); //$NON-NLS-1$
try {
fProgressDialog.run(true, true, runnableWithProgress);
} catch (Exception e) {
Logger.logException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.php.internal.debug.core.zend.testConnection.
* IDebugServerTestListener
* #testEventReceived(org.eclipse.php.internal.debug.
* core.zend.testConnection.DebugServerTestEvent)
*/
public void testEventReceived(final DebugServerTestEvent e) {
fIsFinished = true;
// Release the latch as we have event
fClientTestLatch.countDown();
switch (e.getEventType()) {
case DebugServerTestEvent.TEST_SUCCEEDED: {
showSuccessDialog();
break;
}
case DebugServerTestEvent.TEST_FAILED_DEBUGER_VERSION:
MessageDialog.openError(fShell,
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testDebugServer"), //$NON-NLS-1$
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_oldDebuggerVersion")); //$NON-NLS-1$
break;
case DebugServerTestEvent.TEST_TIMEOUT:
showCustomErrorDialog(
NLS.bind(PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_timeOutMessage"), fURL)); //$NON-NLS-1$
break;
}
}
/**
* Returns the last/current host that the test was performed for.
*
* @return last/current host name
*/
public String getCurrentHost() {
return fCurrentHost;
}
protected String generateDebugQuery(String host) {
StringBuilder debugQuery = new StringBuilder();
debugQuery.append(fURL);
String dummyFile = Platform.getPreferencesService().getString(PHPDebugPlugin.ID,
PHPDebugCorePreferenceNames.ZEND_DEBUG_DUMMY_FILE, "", //$NON-NLS-1$
null);
if (!dummyFile.startsWith("/")) { //$NON-NLS-1$
dummyFile = "/" + dummyFile; //$NON-NLS-1$
}
debugQuery.append(dummyFile).append("?start_debug=1&debug_port="); //$NON-NLS-1$
int port = getDebugPort();
debugQuery.append(port);
debugQuery.append("&debug_fastfile=1&debug_host="); //$NON-NLS-1$
debugQuery.append(host + "&testConnection=true"); //$NON-NLS-1$
boolean useSSL = InstanceScope.INSTANCE.getNode(PHPDebugPlugin.ID)
.getBoolean(PHPDebugCorePreferenceNames.ZEND_DEBUG_ENCRYPTED_SSL_DATA, false);
if (useSSL) {
debugQuery.append("&use_ssl=1"); //$NON-NLS-1$
}
return debugQuery.toString();
}
protected String getPort() {
String port = Integer.toString(PHPDebugPlugin.getDebugPort(DebuggerCommunicationDaemon.ZEND_DEBUGGER_ID));
// Set up custom port from server configuration
int customPort = ZendDebuggerSettingsUtil.getDebugPort(fServer.getUniqueId());
if (customPort != -1)
port = String.valueOf(customPort);
return port;
}
/**
* Returns debug port.
*
* @return debug port
*/
protected int getDebugPort() {
int port = -1;
port = PHPDebugPlugin.getDebugPort(DebuggerCommunicationDaemon.ZEND_DEBUGGER_ID);
// Set up custom port from server configuration
int customPort = ZendDebuggerSettingsUtil.getDebugPort(fServer.getUniqueId());
if (customPort != -1) {
port = customPort;
}
return port;
}
protected String[] getAllLocalHostsAddresses() {
String hosts = PHPDebugPlugin.getDebugHosts();
// Set up custom hosts from server configuration
String customHosts = ZendDebuggerSettingsUtil.getDebugHosts(fServer.getUniqueId());
if (!customHosts.isEmpty())
hosts = customHosts;
StringTokenizer tokenizer = new StringTokenizer(hosts, ", "); //$NON-NLS-1$
List<String> list = new ArrayList<String>();
while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken());
}
return list.toArray(new String[list.size()]);
}
protected String addTimeOutsMessage(String message) {
String result = message;
if (fTimeoutServerList.size() > 0) {
Iterator<String> iter = fTimeoutServerList.iterator();
StringBuilder stringBuilder = new StringBuilder();
while (iter.hasNext()) {
stringBuilder.append("\u2022 " + iter.next() + '\n'); //$NON-NLS-1$
}
result = result + stringBuilder.toString();
}
return result;
}
protected void showCustomErrorDialog(final String message) {
fShell.getDisplay().asyncExec(new Runnable() {
public void run() {
DefaultServerTestMessageDialog dialog = new DefaultServerTestMessageDialog(fShell,
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testDebugServer"), null, // accept //$NON-NLS-1$
message, MessageDialog.ERROR, new String[] { IDialogConstants.OK_LABEL }, 0);
dialog.open();
}
});
}
protected void showSuccessDialog() {
String message = MessageFormat.format(Messages.DebugServerConnectionTest_test_successfull, fCurrentHost);
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(message);
if (getAllLocalHostsAddresses().length > 1) {
stringBuilder.append('\n');
stringBuilder.append(Messages.DebugServerConnectionTest_some_IPs_seems_to_be_redundant);
StringBuilder addresses = new StringBuilder();
for (String address : getAllLocalHostsAddresses()) {
if (!address.equals(fCurrentHost)) {
addresses.append((addresses.length() != 0 ? ", " : "") //$NON-NLS-1$ //$NON-NLS-2$
+ '\'' + address + '\'');
}
}
stringBuilder.append(addresses.toString());
}
fShell.getDisplay().syncExec(new Runnable() {
public void run() {
MessageDialog.openInformation(fShell,
PHPServerUIMessages.getString("DefaultDebugServerConnectionTest_testDebugServer"), //$NON-NLS-1$
stringBuilder.toString());
}
});
}
}