diff options
author | eutarass | 2009-03-04 01:58:27 +0000 |
---|---|---|
committer | eutarass | 2009-03-04 01:58:27 +0000 |
commit | 88caf8c9c877b34d651b15e963d9f666c8027fdc (patch) | |
tree | 5eaf883a57061fa05844f8c674fff4ae844edd79 | |
parent | 72723936ad2c9e203c577fa9f2645c564bdef0d3 (diff) | |
download | org.eclipse.tcf-88caf8c9c877b34d651b15e963d9f666c8027fdc.tar.gz org.eclipse.tcf-88caf8c9c877b34d651b15e963d9f666c8027fdc.tar.xz org.eclipse.tcf-88caf8c9c877b34d651b15e963d9f666c8027fdc.zip |
Implemented better handling of unrecognized commands in TCF protocol: instead of terminating a connection, peers now can send special "command is not recognized" message.
Added TCF error code "command is not recognized".
Fixed agent build error on CygWin.
12 files changed, 191 insertions, 55 deletions
diff --git a/docs/TCF Specification.html b/docs/TCF Specification.html index ade4f1fe6..d86eb5791 100644 --- a/docs/TCF Specification.html +++ b/docs/TCF Specification.html @@ -540,7 +540,8 @@ string as returned by Service.getName().</p> <p>Command name interpretation depends on a service.</p> <p>A command should always be answered with result packed. Result does not have to -be positive – it can include an error code, but there always must be one. Since client +be positive – it can include an error code, or it can be special "N" result that indicates that command was not recognized, +but there always must be one. Since client cannot detect that a response is missing, if for some reasons peer is not able to answer a command, it should consider such situation a fatal communication error and it must shutdown the communication channel. It is not necessary to wait for result @@ -554,13 +555,14 @@ store the requests and time to process them.</p> <pre><b><font face="Courier New" size=2 color=#333399> <i><result></i> + ⇒ N • <i><token></i> • ⇒ R • <i><token></i> • <i><byte array: result data></i> - ⇒ P • <i><token></i> • <i><byte array: result data></i> + ⇒ P • <i><token></i> • <i><byte array: progress data></i> </font></b></pre> -<p>Result packets start with string “P” for intermediate result and “R” for final -result. Receiving of “R” result concludes execution of corresponding command. -There should be exactly one “R” result for each command. In addition, command execution can produce any number of +<p>Result packets start with string “P” for intermediate result, “R” for final +result, and “N” if command is not recognized. Receiving of “R” or “N” result concludes execution of corresponding command. +There should be exactly one “R” or “N” result for each command. In addition, command execution can produce any number of intermediate “P” results. “P” results can be sent before “R”, and it can serve, for example, as command execution progress report when execution takes long time.</p> diff --git a/examples/org.eclipse.tm.tcf.examples.daytime/.classpath b/examples/org.eclipse.tm.tcf.examples.daytime/.classpath index 2d1a4302f..64c5e31b7 100644 --- a/examples/org.eclipse.tm.tcf.examples.daytime/.classpath +++ b/examples/org.eclipse.tm.tcf.examples.daytime/.classpath @@ -1,7 +1,7 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java index 597d38e76..14364896e 100644 --- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java +++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java @@ -13,6 +13,7 @@ package org.eclipse.tm.internal.tcf.debug.tests; import java.util.LinkedList; import org.eclipse.tm.tcf.protocol.IChannel; +import org.eclipse.tm.tcf.protocol.IErrorReport; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IDiagnostics; @@ -33,7 +34,20 @@ class TestEcho implements ITCFTest, IDiagnostics.DoneEcho { test_suite.done(this, null); } else { - for (int i = 0; i < 32; i++) sendMessage(); + diag.not_implemented_command(new IDiagnostics.DoneNotImplementedCommand() { + + public void doneNotImplementedCommand(IToken token, Throwable error) { + if (!(error instanceof IErrorReport)) { + test_suite.done(TestEcho.this, new Exception("Invalid responce to unimplemented command")); + return; + } + if (((IErrorReport)error).getErrorCode() != IErrorReport.TCF_ERROR_INV_COMMAND) { + test_suite.done(TestEcho.this, new Exception("Invalid error code in responce to unimplemented command")); + return; + } + for (int i = 0; i < 32; i++) sendMessage(); + } + }); } } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/DiagnosticsService.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/DiagnosticsService.java index e77eefadc..76f4dcc94 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/DiagnosticsService.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/DiagnosticsService.java @@ -51,7 +51,7 @@ public class DiagnosticsService implements IDiagnostics { channel.sendResult(token, JSON.toJSONSequence(new Object[]{ null, new String[0] })); } else { - throw new Exception("Illegal command: " + name); + channel.rejectCommand(token); } } } @@ -144,4 +144,14 @@ public class DiagnosticsService implements IDiagnostics { }); return token; } + + public IToken not_implemented_command(final DoneNotImplementedCommand done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneNotImplementedCommand(token, new Exception("Not implemented")); + } + }); + return token; + } } diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java index 97093cb27..9dea9b0f9 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java @@ -314,7 +314,7 @@ public class LocatorService implements ILocator { channel.sendResult(token, JSON.toJSONSequence(new Object[]{ null, arr })); } else { - channel.terminate(new Exception("Illegal command: " + name)); + channel.rejectCommand(token); } } catch (Throwable x) { diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/DiagnosticsProxy.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/DiagnosticsProxy.java index 8fbb7b927..e3a5dd8cb 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/DiagnosticsProxy.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/DiagnosticsProxy.java @@ -196,6 +196,15 @@ public class DiagnosticsProxy implements IDiagnostics { }.token; } + public IToken not_implemented_command(final DoneNotImplementedCommand done) { + return new Command(channel, this, "not implemented command", null) { + @Override + public void done(Exception error, Object[] args) { + done.doneNotImplementedCommand(token, error); + } + }.token; + } + @SuppressWarnings("unchecked") private String[] toStringArray(Object o) { if (o == null) return null; diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java index 3070c8896..1c5d575ba 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java @@ -23,6 +23,7 @@ import org.eclipse.tm.internal.tcf.core.TransportManager; import org.eclipse.tm.internal.tcf.services.local.LocatorService; import org.eclipse.tm.internal.tcf.services.remote.GenericProxy; import org.eclipse.tm.tcf.protocol.IChannel; +import org.eclipse.tm.tcf.protocol.IErrorReport; import org.eclipse.tm.tcf.protocol.IPeer; import org.eclipse.tm.tcf.protocol.IService; import org.eclipse.tm.tcf.protocol.IToken; @@ -159,6 +160,7 @@ public abstract class AbstractChannel implements IChannel { inp_thread = new Thread() { + final byte[] empty_byte_array = new byte[0]; byte[] buf = new byte[1024]; byte[] eos; @@ -170,8 +172,11 @@ public abstract class AbstractChannel implements IChannel { int len = 0; for (;;) { int n = read(); - if (n == end) break; - if (n < 0) throw new IOException("Communication channel is closed by remote peer"); + if (n <= 0) { + if (n == end) break; + if (n == EOM) throw new IOException("Unexpected end of message"); + if (n < 0) throw new IOException("Communication channel is closed by remote peer"); + } if (len >= buf.length) { byte[] tmp = new byte[buf.length * 2]; System.arraycopy(buf, 0, tmp, 0, len); @@ -179,13 +184,29 @@ public abstract class AbstractChannel implements IChannel { } buf[len++] = (byte)n; } + if (len == 0) return empty_byte_array; byte[] res = new byte[len]; System.arraycopy(buf, 0, res, 0, len); return res; } private String readString() throws IOException { - return new String(readBytes(0), "UTF8"); + int len = 0; + for (;;) { + int n = read(); + if (n <= 0) { + if (n == 0) break; + if (n == EOM) throw new IOException("Unexpected end of message"); + if (n < 0) throw new IOException("Communication channel is closed by remote peer"); + } + if (len >= buf.length) { + byte[] tmp = new byte[buf.length * 2]; + System.arraycopy(buf, 0, tmp, 0, len); + buf = tmp; + } + buf[len++] = (byte)n; + } + return new String(buf, 0, len, "UTF8"); } public void run() { @@ -206,7 +227,9 @@ public abstract class AbstractChannel implements IChannel { msg.name = readString(); msg.data = readBytes(EOM); break; + case 'P': case 'R': + case 'N': msg.token = new Token(readBytes(0)); msg.data = readBytes(EOM); break; @@ -711,6 +734,14 @@ public abstract class AbstractChannel implements IChannel { addToOutQueue(msg); } + public void rejectCommand(IToken token) { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) throw new Error("Channel is closed"); + Message msg = new Message('N'); + msg.token = (Token)token; + addToOutQueue(msg); + } + public void sendEvent(IService service, String name, byte[] args) { assert Protocol.isDispatchThread(); if (!(state == STATE_OPEN || state == STATE_OPENNING && service instanceof ILocator)) { @@ -756,7 +787,7 @@ public abstract class AbstractChannel implements IChannel { cmds.command(token, msg.name, msg.data); } else { - throw new IOException("Unknown command " + msg.service + "." + msg.name); + rejectCommand(token); } } break; @@ -770,6 +801,11 @@ public abstract class AbstractChannel implements IChannel { token.getListener().result(token, msg.data); sendCongestionLevel(); break; + case 'N': + token = out_tokens.remove(msg.token.getID()).token; + token.getListener().terminated(token, new ErrorReport( + "Command is not recognized", IErrorReport.TCF_ERROR_INV_COMMAND)); + break; case 'E': boolean hello = msg.service.equals(ILocator.NAME) && msg.name.equals("Hello"); if (hello) { diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/Command.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/Command.java index 593f1a036..7748e15c7 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/Command.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/Command.java @@ -68,39 +68,6 @@ public abstract class Command implements IChannel.ICommandListener { private static final SimpleDateFormat timestamp_format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - private class ErrorReport extends Exception implements IErrorReport { - - private static final long serialVersionUID = 3687543884858739977L; - private final Map<String,Object> attrs; - - ErrorReport(String msg, Map<String,Object> attrs) { - super(msg); - this.attrs = attrs; - Object caused_by = attrs.get(IErrorReport.ERROR_CAUSED_BY); - if (caused_by != null) initCause(toError(caused_by, false)); - } - - public int getErrorCode() { - Number n = (Number)attrs.get(ERROR_CODE); - if (n == null) return 0; - return n.intValue(); - } - - public int getAltCode() { - Number n = (Number)attrs.get(ERROR_ALT_CODE); - if (n == null) return 0; - return n.intValue(); - } - - public String getAltOrg() { - return (String)attrs.get(ERROR_ALT_ORG); - } - - public Map<String, Object> getAttributes() { - return attrs; - } - } - public Command(IChannel channel, IService service, String command, Object[] args) { this.service = service; this.command = command; @@ -196,7 +163,7 @@ public abstract class Command implements IChannel.ICommandListener { return "Invalid error report format"; } - private static void appendErrorProps(StringBuffer bf, Map<String,Object> map) { + static void appendErrorProps(StringBuffer bf, Map<String,Object> map) { Number time = (Number)map.get(IErrorReport.ERROR_TIME); Number code = (Number)map.get(IErrorReport.ERROR_CODE); String service = (String)map.get(IErrorReport.ERROR_SERVICE); diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/ErrorReport.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/ErrorReport.java new file mode 100644 index 000000000..56dbca80e --- /dev/null +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/ErrorReport.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2007-2009 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.tcf.core; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tm.tcf.protocol.IErrorReport; + +class ErrorReport extends Exception implements IErrorReport { + + private static final long serialVersionUID = 3687543884858739977L; + private final Map<String,Object> attrs; + + @SuppressWarnings("unchecked") + ErrorReport(String msg, Map<String,Object> attrs) { + super(msg); + this.attrs = attrs; + Object caused_by = attrs.get(IErrorReport.ERROR_CAUSED_BY); + if (caused_by != null) { + Map<String,Object> map = (Map<String,Object>)caused_by; + StringBuffer bf = new StringBuffer(); + bf.append("TCF error report:"); + bf.append('\n'); + Command.appendErrorProps(bf, map); + initCause(new ErrorReport(bf.toString(), map)); + } + } + + ErrorReport(String msg, int code) { + super(msg); + attrs = new HashMap<String,Object>(); + attrs.put(ERROR_CODE, code); + attrs.put(ERROR_TIME, System.currentTimeMillis()); + attrs.put(ERROR_FORMAT, msg); + attrs.put(ERROR_SEVERITY, SEVERITY_ERROR); + } + + public int getErrorCode() { + Number n = (Number)attrs.get(ERROR_CODE); + if (n == null) return 0; + return n.intValue(); + } + + public int getAltCode() { + Number n = (Number)attrs.get(ERROR_ALT_CODE); + if (n == null) return 0; + return n.intValue(); + } + + public String getAltOrg() { + return (String)attrs.get(ERROR_ALT_ORG); + } + + public Map<String, Object> getAttributes() { + return attrs; + } +}
\ No newline at end of file diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IChannel.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IChannel.java index 92168ec43..23f6e59b7 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IChannel.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IChannel.java @@ -73,7 +73,8 @@ public interface IChannel { void result(IToken token, byte[] data); /** - * Called when communication channel was closed while command was waiting for result. + * Called when command is terminated because communication channel was closed or + * command is not recognized by remote peer. * @param token - command handle * @param error - exception that forced the channel to close */ @@ -91,6 +92,18 @@ public interface IChannel { void sendResult(IToken token, byte[] results); /** + * Reject a command by sending "N" result message to remote peer. + * Clients should reject commands that they don't recognize. + * Messages can be queued locally before + * transmission. Sending messages too fast can fill up communication channel + * buffers. Calling thread will be blocked until enough buffer space is + * freed up by transmitting pending messages. + * @param token - command handle + * @param results - result message arguments encoded into array of bytes + */ + void rejectCommand(IToken token); + + /** * Get current level of out-bound traffic congestion. * * @return integer value in range –100..100, where –100 means no pending diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IErrorReport.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IErrorReport.java index fe6eedfcc..86b85a1f9 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IErrorReport.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IErrorReport.java @@ -86,7 +86,8 @@ public interface IErrorReport { TCF_ERROR_INV_DWARF = 21, TCF_ERROR_SYM_NOT_FOUND = 22, TCF_ERROR_UNSUPPORTED = 23, - TCF_ERROR_INV_DATA_TYPE = 24; + TCF_ERROR_INV_DATA_TYPE = 24, + TCF_ERROR_INV_COMMAND = 25; public int getErrorCode(); diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IDiagnostics.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IDiagnostics.java index 04f433519..52694f303 100644 --- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IDiagnostics.java +++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IDiagnostics.java @@ -224,4 +224,22 @@ public interface IDiagnostics extends IService { */ void doneDisposeTestStream(IToken token, Throwable error); } + + /** + * Send a command that is not implemented by peer. + * Used to test handling of 'N' messages by communication channel. + * @param done - command result call back object. + * @return - pending command handle. + */ + IToken not_implemented_command(DoneNotImplementedCommand done); + + interface DoneNotImplementedCommand { + + /** + * Called when 'not_implemented_command' command is done. + * @param token - command handle. + * @param error - error object. + */ + void doneNotImplementedCommand(IToken token, Throwable error); + } } |