From: Christopher O. <chr...@fu...> - 2010-04-20 22:24:14
|
I did test and improve the patch a bit. Give me a +1 (preferably after running a short test, maybe even one that fails) so we all can use this useful feature. [FEATURE] Test DTM via context menu Christopher ### Eclipse Workspace Patch 1.0 #P Saros Index: src/de/fu_berlin/inf/dpp/Saros.java =================================================================== --- src/de/fu_berlin/inf/dpp/Saros.java (revision 2136) +++ src/de/fu_berlin/inf/dpp/Saros.java (working copy) @@ -112,6 +112,7 @@ import de.fu_berlin.inf.dpp.net.business.RequestForActivityHandler; import de.fu_berlin.inf.dpp.net.business.UserListHandler; import de.fu_berlin.inf.dpp.net.internal.ActivitiesExtensionProvider; +import de.fu_berlin.inf.dpp.net.internal.ConnectionTestManager; import de.fu_berlin.inf.dpp.net.internal.DataTransferManager; import de.fu_berlin.inf.dpp.net.internal.DefaultInvitationInfo; import de.fu_berlin.inf.dpp.net.internal.DiscoveryManager; @@ -344,6 +345,7 @@ this.container.addComponent(RequestForActivityHandler.class); this.container.addComponent(ConsistencyWatchdogHandler.class); this.container.addComponent(ActivitiesHandler.class); + this.container.addComponent(ConnectionTestManager.class); // Extensions this.container.addComponent(CancelInviteExtension.class); Index: src/de/fu_berlin/inf/dpp/net/internal/ConnectionTestManager.java =================================================================== --- src/de/fu_berlin/inf/dpp/net/internal/ConnectionTestManager.java (revision 0) +++ src/de/fu_berlin/inf/dpp/net/internal/ConnectionTestManager.java (revision 0) @@ -0,0 +1,247 @@ +package de.fu_berlin.inf.dpp.net.internal; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.log4j.Logger; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.picocontainer.annotations.Inject; + +import de.fu_berlin.inf.dpp.Saros; +import de.fu_berlin.inf.dpp.exceptions.SarosCancellationException; +import de.fu_berlin.inf.dpp.net.IncomingTransferObject; +import de.fu_berlin.inf.dpp.net.JID; +import de.fu_berlin.inf.dpp.net.IncomingTransferObject.IncomingTransferObjectExtensionProvider; +import de.fu_berlin.inf.dpp.net.internal.DataTransferManager.NetTransferMode; +import de.fu_berlin.inf.dpp.net.internal.TransferDescription.FileTransferType; +import de.fu_berlin.inf.dpp.util.StoppWatch; +import de.fu_berlin.inf.dpp.util.Util; + +/** + * Class responsible for testing the data transfer connection using the data + * transfer manager. + * + * @author sszuecs + * @author coezbek + */ +public class ConnectionTestManager { + + private static final Logger log = Logger + .getLogger(ConnectionTestManager.class); + + /** + * Data returned from the user who received a {@link TransferDescription} + * with type {@value FileTransferType#CONNECTION_TEST} + */ + public static class ConnectionTestResponse { + + public long dataHash; + + public String errorMessage; + + public NetTransferMode transferMode; + } + + protected XStreamExtensionProvider<ConnectionTestResponse> responseProvider = new XStreamExtensionProvider<ConnectionTestResponse>( + "sarosConnectionTestResult", ConnectionTestResponse.class); + + @Inject + protected Saros saros; + + @Inject + protected DataTransferManager dataTransferManager; + + @Inject + protected XMPPTransmitter transmitter; + + @Inject + protected DiscoveryManager discoManager; + + public ConnectionTestManager( + XMPPReceiver receiver, + final IncomingTransferObjectExtensionProvider incomingTransferObjectExtensionProvider) { + + receiver.addPacketListener(new PacketListener() { + + public void processPacket(Packet packet) { + + IncomingTransferObject ito = incomingTransferObjectExtensionProvider + .getPayload(packet); + + ConnectionTestResponse result = new ConnectionTestResponse(); + result.transferMode = ito.getTransferMode(); + + try { + byte[] data = ito.accept(SubMonitor + .convert(new NullProgressMonitor())); + + result.dataHash = Arrays.hashCode(data); + + log.info(Util.prefix(new JID(packet.getFrom())) + + "Connection Test Data received: " + data.length + + " bytes, hashCode==" + result.dataHash); + } catch (SarosCancellationException e) { + log + .error( + "Connection Test failed because of an CancelationException", + e); + result.errorMessage = "SarosCancellationException: " + + Util.getMessage(e); + } catch (IOException e) { + log.error( + "Connection Test failed because of an IOException", e); + result.errorMessage = "IOException: " + Util.getMessage(e); + } + + try { + IQ iqResponse = responseProvider.createIQ(result); + iqResponse.setTo(packet.getFrom()); + iqResponse.setPacketID(ito.getTransferDescription().testID); + + saros.getConnection().sendPacket(iqResponse); + } catch (Exception e) { + log.error("Could not send test results to " + + Util.prefix(new JID(packet.getFrom())), e); + } + } + }, new PacketFilter() { + + public boolean accept(Packet packet) { + IncomingTransferObject payload = incomingTransferObjectExtensionProvider + .getPayload(packet); + + if (payload == null) + return false; + + return FileTransferType.CONNECTION_TEST.equals(payload + .getTransferDescription().type); + } + }); + + } + + /** + * Create a byte[] filled with random data + */ + public static byte[] getTestArray(int size) { + + byte[] result = new byte[size]; + Saros.RANDOM.nextBytes(result); + return result; + } + + /** + * Result of a ConnectionTest + */ + public static class TestResult { + + public NetTransferMode mode; + + public long transferTime; + + public int dataSize; + + } + + /** + * Run a connection test with the given user by transmitting a random byte[] + * of the given size using the {@link DataTransferManager}. + * + * Progress will be reported via the given monitor, errors via + * XMPPExceptions and results of a successful test via the returned + * TestResult + */ + public TestResult runConnectionTest(JID plainJID, int size, + SubMonitor progress) throws XMPPException { + + TestResult result = new TestResult(); + result.dataSize = size; + + progress.beginTask("Connection Test with user " + plainJID, 68); + try { + XMPPConnection connection = saros.getConnection(); + if (connection == null || !connection.isConnected()) + throw new XMPPException("Connection is not established!"); + progress.worked(1); + + String id = Packet.nextID(); + + progress.subTask("Checking if remote user is using Saros"); + JID user = discoManager.getSupportingPresence(plainJID, + Saros.NAMESPACE); + if (user == null) + throw new XMPPException("User " + plainJID + + " is not using Saros"); + progress.worked(1); + + TransferDescription transferData = TransferDescription + .createTestTransferDescription(user, id, saros.getMyJID()); + + progress.subTask("Generating Test Data"); + byte[] testData = getTestArray(size); + progress.worked(1); + + // Create a packet collector to listen for a response. + PacketCollector collector = connection + .createPacketCollector(new PacketIDFilter(id)); + + StoppWatch watch = new StoppWatch().start(); + + try { + try { + progress.subTask("Sending Data"); + dataTransferManager.sendData(transferData, testData, + progress.newChild(40)); + } catch (IOException e) { + throw new XMPPException("IOException sending data", e); + } catch (SarosCancellationException e) { + throw new XMPPException( + "CancellationException sending data", e); + } + + progress.subTask("Waiting for reply"); + ConnectionTestResponse response = null; + for (int i = 0; i < 15; i++) { + response = responseProvider.getPayload(collector + .nextResult(1000)); + if (response != null) + break; + progress.worked(1); + } + + result.transferTime = watch.stop().getTime(); + + if (response == null) + throw new XMPPException("Timeout after 15s"); + + if (response.errorMessage != null) + throw new XMPPException("An remote error occurred: " + + response.errorMessage); + + int localDataHash = Arrays.hashCode(testData); + if (response.dataHash != localDataHash) + throw new XMPPException( + "Hash results don't match: Received==" + + response.dataHash + " expected==" + localDataHash); + + result.mode = response.transferMode; + + } finally { + collector.cancel(); + } + + } finally { + progress.done(); + } + return result; + } +} Index: src/de/fu_berlin/inf/dpp/net/internal/TransferDescription.java =================================================================== --- src/de/fu_berlin/inf/dpp/net/internal/TransferDescription.java (revision 2134) +++ src/de/fu_berlin/inf/dpp/net/internal/TransferDescription.java (working copy) @@ -83,7 +83,11 @@ /** * meta data for a stream */ - STREAM_META + STREAM_META, + /** + * test data for connection tests + */ + CONNECTION_TEST } public FileTransferType type; @@ -124,6 +128,16 @@ */ public int objectid = -1; + /** + * If this TransferDescription is of type + * {@link FileTransferType#CONNECTION_TEST} then testID is set to the ID of + * the IQ packet to send in reply to receiving test data. + * + * testID is null if this TransferDescription is not of type + * {@link FileTransferType#CONNECTION_TEST} + */ + protected String testID = null; + @Override public String toString() { @@ -146,6 +160,8 @@ case STREAM_META: return "Stream metadata from " + Util.prefix(getSender()) + ": stream= " + file_project_path + " [SID=" + sessionID + "]"; + case CONNECTION_TEST: + return "Connection test from " + Util.prefix(getSender()); default: return "Not a valid FileTransferType"; } @@ -163,6 +179,16 @@ return result; } + public static TransferDescription createTestTransferDescription( + JID recipient, String testID, JID sender) { + TransferDescription result = new TransferDescription(); + result.recipient = recipient; + result.sender = sender; + result.testID = testID; + result.type = FileTransferType.CONNECTION_TEST; + return result; + } + public static TransferDescription createFileTransferDescription( JID recipient, JID sender, IPath path, String sessionID) { Index: src/de/fu_berlin/inf/dpp/ui/RosterView.java =================================================================== --- src/de/fu_berlin/inf/dpp/ui/RosterView.java (revision 2134) +++ src/de/fu_berlin/inf/dpp/ui/RosterView.java (working copy) @@ -73,6 +73,7 @@ import de.fu_berlin.inf.dpp.net.ITransferModeListener; import de.fu_berlin.inf.dpp.net.JID; import de.fu_berlin.inf.dpp.net.RosterTracker; +import de.fu_berlin.inf.dpp.net.internal.ConnectionTestManager; import de.fu_berlin.inf.dpp.net.internal.DataTransferManager; import de.fu_berlin.inf.dpp.net.internal.DiscoveryManager; import de.fu_berlin.inf.dpp.net.internal.DataTransferManager.NetTransferMode; @@ -90,6 +91,7 @@ import de.fu_berlin.inf.dpp.ui.actions.NewContactAction; import de.fu_berlin.inf.dpp.ui.actions.RenameContactAction; import de.fu_berlin.inf.dpp.ui.actions.SkypeAction; +import de.fu_berlin.inf.dpp.ui.actions.ConnectionTestAction; import de.fu_berlin.inf.dpp.util.Util; import de.fu_berlin.inf.dpp.util.ValueChangeListener; @@ -120,6 +122,8 @@ protected RenameContactAction renameContactAction; + protected ConnectionTestAction testAction; + protected DeleteContactAction deleteContactAction; protected SkypeAction skypeAction; @@ -156,6 +160,9 @@ protected DataTransferManager dataTransferManager; @Inject + protected ConnectionTestManager connectionTestManager; + + @Inject protected RosterTracker rosterTracker; @Inject @@ -842,6 +849,7 @@ manager.add(new Separator()); manager.add(this.renameContactAction); manager.add(this.deleteContactAction); + manager.add(this.testAction); // Other plug-ins can contribute there actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); @@ -854,5 +862,7 @@ this.viewer, discoManager, invitationProcesses); this.renameContactAction = new RenameContactAction(saros, this.viewer); this.deleteContactAction = new DeleteContactAction(saros, this.viewer); + this.testAction = new ConnectionTestAction(saros, connectionTestManager, + this.viewer); } } \ No newline at end of file Index: src/de/fu_berlin/inf/dpp/ui/actions/ConnectionTestAction.java =================================================================== --- src/de/fu_berlin/inf/dpp/ui/actions/ConnectionTestAction.java (revision 0) +++ src/de/fu_berlin/inf/dpp/ui/actions/ConnectionTestAction.java (revision 0) @@ -0,0 +1,141 @@ +/* + * DPP - Serious Distributed Pair Programming + * (c) Freie Universitaet Berlin - Fachbereich Mathematik und Informatik - 2006 + * (c) Riad Djemili - 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package de.fu_berlin.inf.dpp.ui.actions; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.log4j.Logger; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.actions.SelectionProviderAction; +import org.jivesoftware.smack.RosterEntry; +import org.jivesoftware.smack.XMPPException; + +import de.fu_berlin.inf.dpp.Saros; +import de.fu_berlin.inf.dpp.editor.internal.EditorAPI; +import de.fu_berlin.inf.dpp.net.JID; +import de.fu_berlin.inf.dpp.net.internal.ConnectionTestManager; +import de.fu_berlin.inf.dpp.net.internal.DataTransferManager; +import de.fu_berlin.inf.dpp.net.internal.ConnectionTestManager.TestResult; +import de.fu_berlin.inf.dpp.ui.RosterView.TreeItem; +import de.fu_berlin.inf.dpp.util.Util; + +/** + * Action to start a test run using the {@link DataTransferManager} + * + * @author szuecs + */ +public class ConnectionTestAction extends SelectionProviderAction { + + private static final Logger log = Logger + .getLogger(ConnectionTestAction.class); + + protected RosterEntry rosterEntry; + + protected Saros saros; + + protected ConnectionTestManager connectionTestManager; + + public ConnectionTestAction(Saros saros, + ConnectionTestManager connectionTestManager, ISelectionProvider provider) { + super(provider, "Test data transfer connection..."); + + this.saros = saros; + this.connectionTestManager = connectionTestManager; + + selectionChanged((IStructuredSelection) provider.getSelection()); + + setToolTipText("Test the data transfer connection to the selected user."); + } + + /** + * @review runSafe OK + */ + @Override + public void run() { + + if (rosterEntry == null) { + log.error("RosterEntry should not be null at this point!"); + return; + } + + final JID recipient = new JID(rosterEntry.getUser()); + + final TestResult[] testResult = new TestResult[1]; + try { + new ProgressMonitorDialog(null).run(true, true, + new IRunnableWithProgress() { + + public void run(IProgressMonitor progress) + throws InvocationTargetException, InterruptedException { + try { + testResult[0] = connectionTestManager + .runConnectionTest(recipient, 65536, SubMonitor + .convert(progress)); + } catch (XMPPException e) { + throw new InvocationTargetException(e); + } + } + }); + } catch (InvocationTargetException e) { + ErrorDialog.openError(EditorAPI.getShell(), + "Connection Test failed", "Connection Test with user " + + recipient + " failed", new Status(IStatus.ERROR, + "de.fu_berlin.inf.dpp", IStatus.ERROR, e.getCause() + .getMessage(), e.getCause())); + return; + } catch (InterruptedException e) { + log.error(e); + return; + } + MessageDialog.openInformation(EditorAPI.getShell(), + "Connection test successful", "Connection Test with user " + + recipient + + " using " + + testResult[0].mode.toString() + + " " + + Util.throughput(testResult[0].dataSize, + testResult[0].transferTime)); + + } + + protected RosterEntry getSelectedForTest(IStructuredSelection selection) { + + if (selection.size() != 1) + return null; + + TreeItem selected = (TreeItem) selection.getFirstElement(); + return selected.getRosterEntry(); + } + + @Override + public void selectionChanged(IStructuredSelection selection) { + this.rosterEntry = getSelectedForTest(selection); + setEnabled(this.rosterEntry != null); + } +} On Tue, 20 Apr 2010 22:55:49 +0300, Sandor Szücs <san...@fu...> wrote: > Should I commit or is there anyone that want to look into this patch? > > On 13.04.2010, at 17:16, Sandor Szücs wrote: > >> >> On 13.04.2010, at 13:27, Beecher, Karl wrote: >> >>> That's a great and useful patch. I won't pretend to be able to >>> review the code thoroughly... but I've run the feature and it >>> works :) >> >> I have reviewed my patch and found unecessary stuff in there. >> Btw. ResendException and ActivitySequencer stuff was not necessary >> for this patch. >> Maybe the mix was not readable enough. I hope the attached is more >> understandable. >> I have removed some of the logs which were not useful for the project. >> >>> Just one tiny GUI-oriented suggestion: I think the option in the >>> context menu should be called something like "Test connection..." >>> rather than just "Test..." >> >> Yeah you are right, done. >> >> >> An OK message would be: >> Jingle/TCP : OK >> or >> Jingle/UDP : OK >> or >> IBB : OK >> >> What do you think about the returnMessage? >> I have 3 categories: >> - OK >> - CANCEL >> - IOE >> >> Is this ok or should IOE be better the whole Exception? >> Maybe the following is better: returnMessage.append("IOE" + e); >> >> What do others think? >> >> Regards, Sandor Szücs >> -- >> >> <testDTMviaContextMenu02.patch> >> ------------------------------------------------------------------------------ >> Download Intel® Parallel Studio Eval >> Try the new software tools for yourself. Speed compiling, find bugs >> proactively, and fine-tune applications for parallel performance. >> See why Intel Parallel Studio got high marks during beta. >> http://p.sf.net/sfu/intel-sw-dev_______________________________________________ >> Dpp-devel mailing list >> Dpp...@li... >> https://lists.sourceforge.net/lists/listinfo/dpp-devel > > Viele Grüße, Sandor Szücs > -- > > > > > ------------------------------------------------------------------------------ > _______________________________________________ > Dpp-devel mailing list > Dpp...@li... > https://lists.sourceforge.net/lists/listinfo/dpp-devel -- Christopher Oezbek | Freie Universität Berlin | Takustr. 9, 14195 Berlin http://www.inf.fu-berlin.de/~oezbek/ | +49 30 838 75242 | Raum 008 |