Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreutarass2009-03-04 01:58:27 +0000
committereutarass2009-03-04 01:58:27 +0000
commit88caf8c9c877b34d651b15e963d9f666c8027fdc (patch)
tree5eaf883a57061fa05844f8c674fff4ae844edd79
parent72723936ad2c9e203c577fa9f2645c564bdef0d3 (diff)
downloadorg.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.
-rw-r--r--docs/TCF Specification.html12
-rw-r--r--examples/org.eclipse.tm.tcf.examples.daytime/.classpath14
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java16
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/DiagnosticsService.java12
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java2
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/DiagnosticsProxy.java9
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/AbstractChannel.java44
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/Command.java35
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/core/ErrorReport.java66
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IChannel.java15
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/IErrorReport.java3
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IDiagnostics.java18
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 &ndash; it can include an error code, but there always must be one. Since client
+be positive &ndash; 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>&lt;result&gt;</i>
+ &rArr; N &bull; <i>&lt;token&gt;</i> &bull;
&rArr; R &bull; <i>&lt;token&gt;</i> &bull; <i>&lt;byte array: result data&gt;</i>
- &rArr; P &bull; <i>&lt;token&gt;</i> &bull; <i>&lt;byte array: result data&gt;</i>
+ &rArr; P &bull; <i>&lt;token&gt;</i> &bull; <i>&lt;byte array: progress data&gt;</i>
</font></b></pre>
-<p>Result packets start with string &ldquo;P&rdquo; for intermediate result and &ldquo;R&rdquo; for final
-result. Receiving of &ldquo;R&rdquo; result concludes execution of corresponding command.
-There should be exactly one &ldquo;R&rdquo; result for each command. In addition, command execution can produce any number of
+<p>Result packets start with string &ldquo;P&rdquo; for intermediate result, &ldquo;R&rdquo; for final
+result, and &ldquo;N&rdquo; if command is not recognized. Receiving of &ldquo;R&rdquo; or &ldquo;N&rdquo; result concludes execution of corresponding command.
+There should be exactly one &ldquo;R&rdquo; or &ldquo;N&rdquo; result for each command. In addition, command execution can produce any number of
intermediate &ldquo;P&rdquo; results. &ldquo;P&rdquo; results can be sent before &ldquo;R&rdquo;, 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);
+ }
}

Back to the top