diff options
Diffstat (limited to 'plugins/com.windriver.tcf.api')
59 files changed, 9764 insertions, 0 deletions
diff --git a/plugins/com.windriver.tcf.api/.classpath b/plugins/com.windriver.tcf.api/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.tcf.api/.cvsignore b/plugins/com.windriver.tcf.api/.cvsignore new file mode 100644 index 000000000..c5e82d745 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.cvsignore @@ -0,0 +1 @@ +bin
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/.project b/plugins/com.windriver.tcf.api/.project new file mode 100644 index 000000000..48171f9bd --- /dev/null +++ b/plugins/com.windriver.tcf.api/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.tcf.api</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs b/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d598b3c83 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +#Mon Sep 10 12:26:22 PDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,.svn/ +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error diff --git a/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF b/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF new file mode 100644 index 000000000..87bb955c3 --- /dev/null +++ b/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.tcf.api +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.tcf.api.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Eclipse-LazyStart: true +Export-Package: com.windriver.tcf.api.core, + com.windriver.tcf.api.protocol, + com.windriver.tcf.api.services, + com.windriver.tcf.api.util diff --git a/plugins/com.windriver.tcf.api/about.html b/plugins/com.windriver.tcf.api/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.tcf.api/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/build.properties b/plugins/com.windriver.tcf.api/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/plugins/com.windriver.tcf.api/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/plugins/com.windriver.tcf.api/plugin.properties b/plugins/com.windriver.tcf.api/plugin.properties new file mode 100644 index 000000000..8c23febbe --- /dev/null +++ b/plugins/com.windriver.tcf.api/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 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 implementation +############################################################################### +pluginName = Target Communication Framework (TCF) +providerName = Eclipse.org + diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java new file mode 100644 index 000000000..97ead437f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +import com.windriver.tcf.api.protocol.Protocol; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.tcf.api"; + + // The shared instance + private static Activator plugin; + + public Activator() { + plugin = this; + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + Protocol.setEventQueue(new EventQueue()); + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Send error message into Eclipse log. + * @param msg - error message test + * @param err - exception + */ + public static void log(String msg, Throwable err) { + if (plugin == null || plugin.getLog() == null) { + err.printStackTrace(); + } + else { + plugin.getLog().log(new Status(IStatus.ERROR, + getDefault().getBundle().getSymbolicName(), IStatus.OK, msg, err)); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java new file mode 100644 index 000000000..e26cfa100 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api; + +public class ErrorCodes { + + public static final int + ERR_TERMINATE = 1; + +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java new file mode 100644 index 000000000..fec45e8dd --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api; + +import java.util.LinkedList; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobChangeListener; +import org.eclipse.core.runtime.jobs.Job; + +import com.windriver.tcf.api.protocol.IEventQueue; + +/** + * Implementation of Target Communication Framework event queue. + * This implementation is intended for Eclipse environment. + */ +class EventQueue implements IEventQueue, Runnable { + + private final boolean debug = Platform.inDebugMode(); + private final LinkedList<Runnable> queue = new LinkedList<Runnable>(); + private final Thread thread; + private boolean waiting; + private int job_cnt; + + EventQueue() { + thread = new Thread(this); + thread.setDaemon(true); + thread.setName("TCF Event Dispatch"); + thread.start(); + // Need to monitor jobs to detect congestion + Job.getJobManager().addJobChangeListener(new IJobChangeListener() { + + public void aboutToRun(IJobChangeEvent event) { + job_cnt++; + } + + public void awake(IJobChangeEvent event) { + //job_cnt++; + } + + public void done(IJobChangeEvent event) { + job_cnt--; + if (Job.getJobManager().isIdle()) job_cnt = 0; + } + + public void running(IJobChangeEvent event) { + } + + public void scheduled(IJobChangeEvent event) { + } + + public void sleeping(IJobChangeEvent event) { + //job_cnt--; + } + }); + } + + private void error(Throwable x) { + if (debug) x.printStackTrace(); + Activator.log("Unhandled excetion in TCF event dispatch", x); + } + + public void run() { + for (;;) { + try { + Runnable r = null; + synchronized (this) { + while (queue.isEmpty()) { + waiting = true; + wait(); + } + r = queue.removeFirst(); + } + r.run(); + } + catch (Throwable x) { + error(x); + } + } + } + + public synchronized void invokeLater(final Runnable r) { + queue.add(r); + if (waiting) { + waiting = false; + notifyAll(); + } + } + + public boolean isDispatchThread() { + return Thread.currentThread() == thread; + } + + public synchronized int getCongestion() { + int l0 = job_cnt / 100 - 100; + int l1 = queue.size() / 100 - 100; + if (l1 > l0) l0 = l1; + if (l0 > 100) l0 = 100; + return l0; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java new file mode 100644 index 000000000..767f8b153 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java @@ -0,0 +1,796 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.DiagnosticsService; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.internal.services.remote.GenericProxy; +import com.windriver.tcf.api.internal.services.remote.LocatorProxy; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +public abstract class AbstractChannel implements IChannel { + + public interface TraceListener { + + public void onMessageReceived(char type, String token, + String service, String name, byte[] data); + + public void onMessageSent(char type, String token, + String service, String name, byte[] data); + + public void onChannelClosed(Throwable error); + } + + private static class Message { + final char type; + Token token; + String service; + String name; + byte[] data; + + boolean is_sent; + boolean is_canceled; + + Collection<TraceListener> trace; + + Message(char type) { + this.type = type; + } + + public String toString() { + try { + StringBuffer bf = new StringBuffer(); + bf.append('[');; + bf.append(type); + if (token != null) { + bf.append(' '); + bf.append(token.getID()); + } + if (service != null) { + bf.append(' '); + bf.append(service); + } + if (name != null) { + bf.append(' '); + bf.append(name); + } + if (data != null) { + int i = 0; + while (i < data.length) { + int j = i; + while (j < data.length && data[j] != 0) j++; + bf.append(' '); + bf.append(new String(data, i, j - i, "UTF8")); + if (j < data.length && data[j] == 0) j++; + i = j; + } + } + bf.append(']'); + return bf.toString(); + } + catch (Exception x) { + return x.toString(); + } + } + } + + private static IChannelListener[] listeners_array = new IChannelListener[64]; + + private final LinkedList<String> redirect_queue = new LinkedList<String>(); + private final Map<Class<?>,IService> local_service_by_class = new HashMap<Class<?>,IService>(); + private final Map<Class<?>,IService> remote_service_by_class = new HashMap<Class<?>,IService>(); + private final Map<String,IService> local_service_by_name = new HashMap<String,IService>(); + private final Map<String,IService> remote_service_by_name = new HashMap<String,IService>(); + private final LinkedList<Message> out_queue = new LinkedList<Message>(); + private final Collection<IChannelListener> channel_listeners = new ArrayList<IChannelListener>(); + private final Map<String,IChannel.IEventListener[]> event_listeners = new HashMap<String,IChannel.IEventListener[]>(); + private final Map<String,IChannel.ICommandServer> command_servers = new HashMap<String,IChannel.ICommandServer>(); + private final Map<String,Message> inp_tokens = new HashMap<String,Message>(); + private final Map<String,Message> out_tokens = new HashMap<String,Message>(); + private final Thread inp_thread; + private final Thread out_thread; + private boolean shutdown; + private int state = STATE_OPENNING; + private IToken redirect_command; + private IPeer peer; + + private static final int pending_command_limit = 10; + private int local_congestion_level = -100; + private int remote_congestion_level = -100; + private long local_congestion_time; + private int inp_queue_size = 0; + private Collection<TraceListener> trace_listeners; + + public static final int + EOS = -1, + EOM = -2; + + protected AbstractChannel(IPeer peer) { + assert Protocol.isDispatchThread(); + assert Protocol.getLocator().getPeers().get(peer.getID()) == peer; + this.peer = peer; + + addLocalService(Protocol.getLocator()); + addLocalService(new DiagnosticsService(this)); + + inp_thread = new Thread() { + + byte[] buf = new byte[1024]; + byte[] eos; + + private void error() throws IOException { + throw new IOException("Protocol syntax error"); + } + + private byte[] readBytes(int end) throws IOException { + int len = 0; + for (;;) { + int n = read(); + if (n == end) break; + if (n < 0) error(); + if (len >= buf.length) { + byte[] tmp = new byte[buf.length * 2]; + System.arraycopy(buf, 0, tmp, 0, len); + buf = tmp; + } + buf[len++] = (byte)n; + } + 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"); + } + + public void run() { + try { + while (true) { + int n = read(); + if (n == EOM) continue; + if (n == EOS) { + eos = readBytes(EOM); + break; + } + final Message msg = new Message((char)n); + if (read() != 0) error(); + switch (msg.type) { + case 'C': + msg.token = new Token(readBytes(0)); + msg.service = readString(); + msg.name = readString(); + msg.data = readBytes(EOM); + break; + case 'R': + msg.token = new Token(readBytes(0)); + msg.data = readBytes(EOM); + break; + case 'E': + msg.service = readString(); + msg.name = readString(); + msg.data = readBytes(EOM); + break; + case 'F': + msg.data = readBytes(EOM); + break; + default: + error(); + } + long delay = 0; + synchronized (out_queue) { + inp_queue_size++; + if (inp_queue_size > 32) delay = inp_queue_size; + } + Protocol.invokeLater(new Runnable() { + public void run() { + handleInput(msg); + } + }); + if (delay > 0) sleep(delay); + } + Protocol.invokeLater(new Runnable() { + public void run() { + if (out_tokens.isEmpty()) { + close(); + } + else { + IOException x = new IOException("Connection reset by peer"); + try { + Object[] args = JSON.parseSequence(eos); + int error_code = ((Number)args[0]).intValue(); + if (error_code != 0) { + x = new IOException(Command.toErrorString(args[1])); + } + } + catch (IOException e) { + x = e; + } + terminate(x); + } + } + }); + } + catch (final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + terminate(x); + } + }); + } + } + }; + + out_thread = new Thread() { + + public void run() { + try { + while (true) { + Message msg = null; + boolean last = false; + synchronized (out_queue) { + while (out_queue.isEmpty()) out_queue.wait(); + msg = out_queue.removeFirst(); + if (msg == null) break; + last = out_queue.isEmpty(); + if (msg.is_canceled) { + if (last) flush(); + continue; + } + msg.is_sent = true; + } + if (msg.trace != null) { + final Message m = msg; + Protocol.invokeLater(new Runnable() { + public void run() { + for (TraceListener l : m.trace) { + try { + l.onMessageSent(m.type, m.token == null ? null : m.token.getID(), + m.service, m.name, m.data); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + }); + } + write(msg.type); + write(0); + if (msg.token != null) { + write(msg.token.getBytes()); + write(0); + } + if (msg.service != null) { + write(msg.service.getBytes("UTF8")); + write(0); + } + if (msg.name != null) { + write(msg.name.getBytes("UTF8")); + write(0); + } + if (msg.data != null) { + write(msg.data); + } + write(EOM); + int delay = 0; + int level = remote_congestion_level; + if (level > 0) delay = level * 10; + if (last || delay > 0) flush(); + if (delay > 0) sleep(delay); + else yield(); + } + write(EOS); + flush(); + } + catch (final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + terminate(x); + } + }); + } + } + }; + inp_thread.setName("TCF Channel Receiver"); + out_thread.setName("TCF Channel Transmitter"); + + try { + Object[] args = new Object[]{ local_service_by_name.keySet() }; + sendEvent(Protocol.getLocator(), "Hello", JSON.toJSONSequence(args)); + } + catch (IOException x) { + throw new Error(x); + } + } + + protected void start() { + assert Protocol.isDispatchThread(); + inp_thread.start(); + out_thread.start(); + LocatorService.channelStarted(this); + } + + public void redirect(String peer_id) { + assert Protocol.isDispatchThread(); + if (state == STATE_OPENNING) { + assert redirect_command == null; + redirect_queue.add(peer_id); + } + else { + assert state == STATE_OPEN; + state = STATE_OPENNING; + try { + onLocatorHello(new ArrayList<String>()); + } + catch (Throwable x) { + terminate(x); + } + } + } + + @SuppressWarnings("unchecked") + public void onLocatorHello(Collection<String> c) throws IOException { + if (state != STATE_OPENNING) throw new IOException("Invalid event: Locator.Hello"); + remote_service_by_class.clear(); + String pkg_name = LocatorProxy.class.getPackage().getName(); + for (Iterator<String> i = c.iterator(); i.hasNext();) { + String service_name = i.next(); + try { + Class<?> cls = Class.forName(pkg_name + "." + service_name + "Proxy"); + IService service = (IService)cls.getConstructor(IChannel.class).newInstance(this); + for (Class<?> fs : cls.getInterfaces()) { + if (fs.equals(IService.class)) continue; + if (!IService.class.isAssignableFrom(fs)) continue; + remote_service_by_class.put(fs, service); + } + assert service_name.equals(service.getName()); + remote_service_by_name.put(service_name, service); + } + catch (Exception x) { + IService service = new GenericProxy(this, service_name); + remote_service_by_name.put(service_name, service); + } + } + assert redirect_command == null; + if (redirect_queue.size() > 0) { + String id = redirect_queue.removeFirst(); + ILocator l = (ILocator)remote_service_by_class.get(ILocator.NAME); + if (l == null) throw new IOException("Peer " + peer.getID() + " has no locator service"); + peer = l.getPeers().get(id); + if (peer == null) throw new IOException("Unknown peer ID: " + id); + redirect_command = l.redirect(id, new ILocator.DoneRedirect() { + public void doneRedirect(IToken token, Exception x) { + assert redirect_command == token; + assert state == STATE_OPENNING; + redirect_command = null; + remote_congestion_level = 0; + if (x != null) terminate(x); + // Wait for next "Hello" + } + }); + } + else { + state = STATE_OPEN; + Transport.channelOpened(this); + Protocol.invokeLater(new Runnable() { + public void run() { + listeners_array = channel_listeners.toArray(listeners_array); + for (int i = 0; i < listeners_array.length && listeners_array[i] != null; i++) { + listeners_array[i].onChannelOpened(); + } + } + }); + } + } + + public final int getState() { + return state; + } + + public void addChannelListener(IChannelListener listener) { + assert Protocol.isDispatchThread(); + channel_listeners.add(listener); + } + + public void removeChannelListener(IChannelListener listener) { + assert Protocol.isDispatchThread(); + channel_listeners.remove(listener); + } + + public void addTraceListener(TraceListener listener) { + if (trace_listeners == null) { + trace_listeners = new ArrayList<TraceListener>(); + } + else { + trace_listeners = new ArrayList<TraceListener>(trace_listeners); + } + trace_listeners.add(listener); + } + + public void removeTraceListener(TraceListener listener) { + trace_listeners = new ArrayList<TraceListener>(trace_listeners); + trace_listeners.remove(listener); + if (trace_listeners.isEmpty()) trace_listeners = null; + } + + public void addEventListener(IService service, IChannel.IEventListener listener) { + assert Protocol.isDispatchThread(); + IChannel.IEventListener[] list = event_listeners.get(service.getName()); + IChannel.IEventListener[] next = new IChannel.IEventListener[list == null ? 1 : list.length + 1]; + if (list != null) System.arraycopy(list, 0, next, 0, list.length); + next[next.length - 1] = listener; + event_listeners.put(service.getName(), next); + } + + public void removeEventListener(IService service, IChannel.IEventListener listener) { + assert Protocol.isDispatchThread(); + IChannel.IEventListener[] list = event_listeners.get(service.getName()); + for (int i = 0; i < list.length; i++) { + if (list[i] == listener) { + if (list.length == 1) { + event_listeners.remove(service.getName()); + } + else { + IChannel.IEventListener[] next = new IChannel.IEventListener[list.length - 1]; + System.arraycopy(list, 0, next, 0, i - 1); + System.arraycopy(list, i + 1, next, i, next.length - i); + event_listeners.put(service.getName(), next); + } + return; + } + } + } + + public void addCommandServer(IService service, IChannel.ICommandServer listener) { + assert Protocol.isDispatchThread(); + if (command_servers.put(service.getName(), listener) != null) { + throw new Error("Only one command server per service is allowed"); + } + } + + public void removeCommandServer(IService service, IChannel.ICommandServer listener) { + assert Protocol.isDispatchThread(); + if (command_servers.remove(service.getName()) != listener) { + throw new Error("Invalid command server"); + } + } + + private void sendEndOfStream() { + if (shutdown) return; + shutdown = true; + synchronized (out_queue) { + out_queue.clear(); + out_queue.add(0, null); + out_queue.notify(); + } + } + + public void close() { + assert Protocol.isDispatchThread(); + try { + sendEndOfStream(); + out_thread.join(10000); + stop(); + inp_thread.join(10000); + terminate(null); + } + catch (Exception x) { + terminate(x); + } + } + + public void terminate(final Throwable error) { + assert Protocol.isDispatchThread(); + sendEndOfStream(); + if (state == STATE_CLOSED) return; + if (error != null) Activator.log("TCF channel terminated", error); + state = STATE_CLOSED; + Transport.channelClosed(this, error); + Protocol.invokeLater(new Runnable() { + public void run() { + if (!out_tokens.isEmpty()) { + Exception x = null; + if (error instanceof Exception) x = (Exception)error; + else if (error != null) x = new Exception(error); + else x = new IOException("Channel is closed"); + for (Message msg : out_tokens.values()) { + String s = msg.toString(); + if (s.length() > 72) s = s.substring(0, 72) + "...]"; + IOException y = new IOException("Command " + s + " aborted"); + y.initCause(x); + msg.token.getListener().terminated(msg.token, y); + } + out_tokens.clear(); + } + listeners_array = channel_listeners.toArray(listeners_array); + for (int i = 0; i < listeners_array.length && listeners_array[i] != null; i++) { + listeners_array[i].onChannelClosed(error); + } + if (trace_listeners != null) { + for (TraceListener l : trace_listeners) { + try { + l.onChannelClosed(error); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + } + }); + } + + public int getCongestion() { + assert Protocol.isDispatchThread(); + int level = out_tokens.size() * 100 / pending_command_limit - 100; + if (remote_congestion_level > level) level = remote_congestion_level; + if (level > 100) level = 100; + return level; + } + + public IPeer getLocalPeer() { + assert Protocol.isDispatchThread(); + return LocatorService.getLocalPeer(); + } + + public IPeer getRemotePeer() { + assert Protocol.isDispatchThread(); + return peer; + } + + private void addLocalService(IService service) { + for (Class<?> fs : service.getClass().getInterfaces()) { + if (fs.equals(IService.class)) continue; + if (!IService.class.isAssignableFrom(fs)) continue; + local_service_by_class.put(fs, service); + } + local_service_by_name.put(service.getName(), service); + } + + public Collection<String> getLocalServices() { + assert Protocol.isDispatchThread(); + return local_service_by_name.keySet(); + } + + public Collection<String> getRemoteServices() { + return remote_service_by_name.keySet(); + } + + @SuppressWarnings("unchecked") + public <V extends IService> V getLocalService(Class<V> cls) { + return (V)local_service_by_class.get(cls); + } + + @SuppressWarnings("unchecked") + public <V extends IService> V getRemoteService(Class<V> cls) { + return (V)remote_service_by_class.get(cls); + } + + public IService getLocalService(String service_name) { + return local_service_by_name.get(service_name); + } + + public IService getRemoteService(String service_name) { + return remote_service_by_name.get(service_name); + } + + private void addToOutQueue(Message msg) { + msg.trace = trace_listeners; + synchronized (out_queue) { + out_queue.add(msg); + out_queue.notify(); + } + } + + public IToken sendCommand(IService service, String name, byte[] args, ICommandListener listener) { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) throw new Error("Channel is closed"); + final Message msg = new Message('C'); + msg.service = service.getName(); + msg.name = name; + msg.data = args; + Token token = new Token(listener) { + public boolean cancel() { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) return false; + synchronized (out_queue) { + if (msg.is_sent) return false; + msg.is_canceled = true; + } + out_tokens.remove(msg.token.getID()); + return true; + } + }; + msg.token = token; + out_tokens.put(token.getID(), msg); + addToOutQueue(msg); + return token; + } + + public void sendResult(IToken token, byte[] results) { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) { + throw new Error("Channel is closed"); + } + Message msg = new Message('R'); + msg.data = results; + msg.token = (Token)token; + inp_tokens.remove(((Token)token).getID()); + addToOutQueue(msg); + } + + public void sendEvent(IService service, String name, byte[] args) { + assert Protocol.isDispatchThread(); + if (!(state == STATE_OPEN || state == STATE_OPENNING && service instanceof ILocator)) { + throw new Error("Channel is closed"); + } + Message msg = new Message('E'); + msg.service = service.getName(); + msg.name = name; + msg.data = args; + addToOutQueue(msg); + } + + private void handleInput(Message msg) { + assert Protocol.isDispatchThread(); + synchronized (out_queue) { + inp_queue_size--; + } + if (state == STATE_CLOSED) return; + if (trace_listeners != null) { + for (TraceListener l : trace_listeners) { + try { + l.onMessageReceived(msg.type, + msg.token != null ? msg.token.getID() : null, + msg.service, msg.name, msg.data); + } + catch (Throwable x) { + x.printStackTrace(); + } + } + } + try { + Token token = null; + IChannel.IEventListener[] list = null; + IChannel.ICommandServer cmds = null; + switch (msg.type) { + case 'C': + token = msg.token; + inp_tokens.put(token.getID(), msg); + cmds = command_servers.get(msg.service); + if (cmds != null) { + cmds.command(token, msg.name, msg.data); + } + else { + throw new IOException("Unknown command " + msg.service + "." + msg.name); + } + sendCongestionLevel(); + break; + case 'P': + token = out_tokens.get(msg.token.getID()).token; + token.getListener().progress(token, msg.data); + break; + case 'R': + token = out_tokens.remove(msg.token.getID()).token; + token.getListener().result(token, msg.data); + break; + case 'E': + list = event_listeners.get(msg.service); + if (list != null) { + for (int i = 0; i < list.length; i++) { + list[i].event(msg.name, msg.data); + } + } + sendCongestionLevel(); + break; + case 'F': + remote_congestion_level = Integer.parseInt(new String(msg.data, "UTF8")); + break; + default: + assert false; + break; + } + } + catch (Throwable x) { + terminate(x); + } + } + + private void sendCongestionLevel() throws IOException { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) return; + int level = Protocol.getEventQueue().getCongestion(); + int n = inp_tokens.size() * 100 / pending_command_limit - 100; + if (n > level) level = n; + if (level > 100) level = 100; + if (level == local_congestion_level) return; + long time = System.currentTimeMillis(); + if (level < local_congestion_level) { + if (time - local_congestion_time < 500) return; + int i = (local_congestion_level - level) / 4; + if (i <= 0) i = 1; + local_congestion_level -= i; + } + else { + local_congestion_level = level; + } + local_congestion_time = time; + synchronized (out_queue) { + Message msg = out_queue.isEmpty() ? null : out_queue.get(0); + if (msg == null || msg.type != 'F') { + msg = new Message('F'); + out_queue.add(0, msg); + out_queue.notify(); + } + msg.data = Integer.toString(local_congestion_level).getBytes("UTF8"); + msg.trace = trace_listeners; + } + } + + /** + * Read one byte from the channel input stream. + * @return next data byte or -1 if end of stream is reached. + * @throws IOException + */ + protected abstract int read() throws IOException; + + /** + * Write one byte into the channel output stream. + * The stream can put the byte into a buffer instead of transmitting it right away. + * @param n - the data byte. + * @throws IOException + */ + protected abstract void write(int n) throws IOException; + + /** + * Flush the channel output stream. + * All buffered data should be transmitted immediately. + * @throws IOException + */ + protected abstract void flush() throws IOException; + + /** + * Stop (close) channel underlying streams. + * If a thread is blocked by read() or write(), it should be + * resumed (or interrupted). + * @throws IOException + */ + protected abstract void stop() throws IOException; + + /** + * Write array of bytes into the channel output stream. + * The stream can put bytes into a buffer instead of transmitting it right away. + * @param buf + * @throws IOException + */ + protected void write(byte[] buf) throws IOException { + assert Thread.currentThread() == out_thread; + for (int i = 0; i < buf.length; i++) write(buf[i]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java new file mode 100644 index 000000000..314d9b03a --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; + +public abstract class AbstractPeer implements IPeer { + + private final Map<String, String> ro_attrs; + private final Map<String, String> rw_attrs; + + public AbstractPeer(Map<String, String> attrs) { + if (attrs != null) { + rw_attrs = new HashMap<String, String>(attrs); + } + else { + rw_attrs = new HashMap<String, String>(); + } + ro_attrs = new ReadOnlyMap<String, String>(rw_attrs); + assert getID() != null; + LocatorService.addPeer(this); + } + + protected Map<String, String> getAttributesStorage() { + assert Protocol.isDispatchThread(); + return rw_attrs; + } + + public void dispose() { + assert Protocol.isDispatchThread(); + Transport.peerDisposed(this); + LocatorService.removePeer(this); + } + + public Map<String, String> getAttributes() { + assert Protocol.isDispatchThread(); + return ro_attrs; + } + + public String getID() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_ID); + } + + public String getName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_NAME); + } + + public String getOSName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_OS_NAME); + } + + public String getTransportName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_TRANSPORT_NAME); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java new file mode 100644 index 000000000..498104fac --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +/** + * Methods for translating Base64 encoded strings to byte arrays and back. + */ +public class Base64 { + + public static char[] toBase64(byte[] buf, int pos, int len) { + char[] out_buf = new char[4 * ((len + 2) / 3)]; + int end = pos + len; + int out_pos = 0; + while (pos < end) { + int byte0 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[byte0 >> 2]; + if (pos == end) { + out_buf[out_pos++] = int2char[(byte0 << 4) & 0x3f]; + out_buf[out_pos++] = '='; + out_buf[out_pos++] = '='; + } + else { + int byte1 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[(byte0 << 4) & 0x3f | (byte1 >> 4)]; + if (pos == end) { + out_buf[out_pos++] = int2char[(byte1 << 2) & 0x3f]; + out_buf[out_pos++] = '='; + } + else { + int byte2 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[(byte1 << 2) & 0x3f | (byte2 >> 6)]; + out_buf[out_pos++] = int2char[byte2 & 0x3f]; + } + } + } + assert out_pos == out_buf.length; + return out_buf; + } + + public static void toByteArray(byte[] buf, int offs, int size, char[] inp) { + int out_pos = offs; + if (inp != null) { + int inp_len = inp.length; + if (inp_len % 4 != 0) { + throw new IllegalArgumentException( + "BASE64 string length must be a multiple of four."); + } + int out_len = inp_len / 4 * 3; + if (inp_len > 0 && inp[inp_len - 1] == '=') { + out_len--; + if (inp[inp_len - 2] == '=') { + out_len--; + } + } + if (out_len > size) { + throw new IllegalArgumentException( + "BASE64 data array is longer then destination buffer."); + } + int inp_pos = 0; + while (inp_pos < inp_len) { + int n0, n1, n2, n3; + char ch0 = inp[inp_pos++]; + char ch1 = inp[inp_pos++]; + char ch2 = inp[inp_pos++]; + char ch3 = inp[inp_pos++]; + if (ch0 >= char2int.length || (n0 = char2int[ch0]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch0); + } + if (ch1 >= char2int.length || (n1 = char2int[ch1]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch1); + } + buf[out_pos++] = (byte)((n0 << 2) | (n1 >> 4)); + if (ch2 == '=') break; + if (ch2 >= char2int.length || (n2 = char2int[ch2]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch2); + } + buf[out_pos++] = (byte)((n1 << 4) | (n2 >> 2)); + if (ch3 == '=') break; + if (ch3 >= char2int.length || (n3 = char2int[ch3]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch3); + } + buf[out_pos++] = (byte)((n2 << 6) | n3); + } + assert out_pos == offs + out_len; + } + while (out_pos < offs + size) buf[out_pos++] = 0; + } + + public static byte[] toByteArray(char[] inp) { + int inp_len = inp.length; + int out_len = inp_len / 4 * 3; + if (inp_len > 0 && inp[inp_len - 1] == '=') { + out_len--; + if (inp[inp_len - 2] == '=') { + out_len--; + } + } + byte[] buf = new byte[out_len]; + toByteArray(buf, 0, buf.length, inp); + return buf; + } + + /* + * See RFC 2045. + */ + private static final char int2char[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /* + * See RFC 2045 + */ + private static final byte char2int[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51 + }; +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java new file mode 100644 index 000000000..c50a5ad03 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; + +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; + +public class ChannelTCP extends StreamChannel { + + private Socket socket; + private InputStream inp; + private OutputStream out; + private boolean closed; + + public ChannelTCP(IPeer peer, final String host, final int port) { + super(peer); + Thread thread = new Thread() { + public void run() { + try { + socket = new Socket(host, port); + socket.setTcpNoDelay(true); + inp = new BufferedInputStream(socket.getInputStream()); + out = new BufferedOutputStream(socket.getOutputStream()); + /* Uncomment for testing of buffers. + inp = new BufferedInputStream(new FilterInputStream(socket.getInputStream()) { + public int read() throws IOException { + System.out.println("Inp 1"); + return in.read(); + } + public int read(byte b[]) throws IOException { + int n = in.read(b); + System.out.println("Inp " + n); + return n; + } + public int read(byte b[], int off, int len) throws IOException { + int n = in.read(b, off, len); + System.out.println("Inp " + n); + return n; + } + }); + out = new BufferedOutputStream(new FilterOutputStream(socket.getOutputStream()){ + public void write(int b) throws IOException { + System.out.println("Out 1"); + out.write(b); + } + public void write(byte b[]) throws IOException { + System.out.println("Out " + b.length); + out.write(b); + } + public void write(byte b[], int off, int len) throws IOException { + System.out.println("Out " + len); + out.write(b, off, len); + } + }); + */ + Protocol.invokeLater(new Runnable() { + public void run() { + ChannelTCP.this.start(); + } + }); + } + catch (final IOException x) { + Protocol.invokeLater(new Runnable() { + public void run() { + ChannelTCP.this.terminate(x); + } + }); + } + } + }; + thread.setName("TCF Socket Connect"); + thread.start(); + } + + @Override + protected final int get() throws IOException { + try { + if (closed) return -1; + return inp.read(); + } + catch (SocketException x) { + if (closed) return -1; + throw x; + } + } + + @Override + protected final void put(int b) throws IOException { + assert b >= 0 && b <= 0xff; + if (closed) return; + out.write(b); + } + + @Override + protected final void flush() throws IOException { + if (closed) return; + out.flush(); + } + + @Override + protected void stop() throws IOException { + closed = true; + socket.close(); + out.close(); + inp.close(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java new file mode 100644 index 000000000..d704ba96b --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; + +/** + * This is utility class that helps to implement sending a command and receiving + * command result over TCF communication channel. The class uses JSON to encode + * command arguments and to decode result data. Clients are expected to subclass + * <code>Command</code> and override <code>done</code> method. + * + * Note: most clients don't need to handle protocol commands directly and + * can use service APIs instead. Service API does all command encoding/decoding + * for a client. + * + * Typical usage example: + * + * public IToken getContext(String id, final DoneGetContext done) { + * return new Command(channel, IService.this, "getContext", new Object[]{ id }) { + * @Override + * public void done(Exception error, Object[] args) { + * Context ctx = null; + * if (error == null) { + * assert args.length == 3; + * error = JSON.toError(args[0], args[1]); + * if (args[2] != null) ctx = new Context(args[2]); + * } + * done.doneGetContext(token, error, ctx); + * } + * }.token; + * } + */ +public abstract class Command implements IChannel.ICommandListener { + + private final IService service; + private final String command; + private final Object[] args; + + public final IToken token; + + private boolean done; + + public Command(IChannel channel, IService service, String command, Object[] args) { + this.service = service; + this.command = command; + this.args = args; + IToken t = null; + try { + t = channel.sendCommand(service, command, JSON.toJSONSequence(args), this); + } + catch (Throwable y) { + t = new Token(); + final Exception x = y instanceof Exception ? (Exception)y : new Exception(y); + Protocol.invokeLater(new Runnable() { + public void run() { + assert !done; + done = true; + done(x, null); + } + }); + } + token = t; + } + + public void progress(IToken token, byte[] data) { + assert this.token == token; + } + + public void result(IToken token, byte[] data) { + assert this.token == token; + Exception error = null; + Object[] args = null; + try { + args = JSON.parseSequence(data); + } + catch (Exception e) { + error = e; + } + assert !done; + done = true; + done(error, args); + } + + public void terminated(IToken token, Exception error) { + assert this.token == token; + assert !done; + done = true; + done(error, null); + } + + public abstract void done(Exception error, Object[] args); + + public String getCommandString() { + StringBuffer buf = new StringBuffer(); + buf.append(service.getName()); + buf.append(' '); + buf.append(command); + if (args != null) { + for (int i = 0; i < args.length; i++) { + buf.append(i == 0 ? " " : ", "); + try { + buf.append(JSON.toJSON(args[i])); + } + catch (IOException x) { + buf.append("***"); + buf.append(x.getMessage()); + buf.append("***"); + } + } + } + return buf.toString(); + } + + @SuppressWarnings("unchecked") + public static String toErrorString(Object data) { + if (data instanceof String) { + return (String)data; + } + else if (data != null) { + Map<String,Object> map = (Map<String,Object>)data; + Collection<Object> c = (Collection<Object>)map.get("params"); + return new MessageFormat((String)map.get("format")).format(c.toArray()); + } + return null; + } + + public Exception toError(Object code, Object data) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 72) cmd = cmd.substring(0, 72) + "..."; + return new Exception( + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java new file mode 100644 index 000000000..a6b871740 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.core; + +import java.io.IOException; + +import com.windriver.tcf.api.protocol.IPeer; + +public abstract class StreamChannel extends AbstractChannel { + + public static final int ESC = 3; + + public StreamChannel(IPeer peer) { + super(peer); + } + + protected abstract int get() throws IOException; + protected abstract void put(int n) throws IOException; + + @Override + protected final int read() throws IOException { + int res = get(); + if (res < 0) return EOS; + assert res >= 0 && res <= 0xff; + if (res != ESC) return res; + int n = get(); + switch (n) { + case 0: return ESC; + case 1: return EOM; + case 2: return EOS; + default: + if (n < 0) return EOS; + assert false; + return 0; + } + } + + @Override + protected final void write(int n) throws IOException { + switch (n) { + case ESC: put(ESC); put(0); break; + case EOM: put(ESC); put(1); break; + case EOS: put(ESC); put(2); break; + default: + assert n >= 0 && n <= 0xff; + put(n); + } + } + + @Override + protected void write(byte[] buf) throws IOException { + for (int i = 0; i < buf.length; i++) { + int n = buf[i] & 0xff; + put(n); + if (n == ESC) put(0); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java new file mode 100644 index 000000000..1bb1cd141 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import com.windriver.tcf.api.core.StreamChannel; +import com.windriver.tcf.api.protocol.IPeer; + +public class ChannelLoop extends StreamChannel { + + private final byte[] buf = new byte[0x1000]; + private int buf_inp; + private int buf_out; + private boolean waiting; + private boolean closed; + + ChannelLoop(IPeer peer) { + super(peer); + start(); + } + + @Override + protected synchronized int get() throws IOException { + try { + while (buf_inp == buf_out) { + if (closed) return -1; + waiting = true; + wait(); + } + int b = buf[buf_out] & 0xff; + buf_out = (buf_out + 1) % buf.length; + if (waiting) { + waiting = false; + notifyAll(); + } + return b; + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + @Override + protected synchronized void put(int b) throws IOException { + assert b >=0 && b <= 0xff; + try { + for (;;) { + int nxt_inp = (buf_inp + 1) % buf.length; + if (nxt_inp != buf_out) { + buf[buf_inp] = (byte)b; + buf_inp = nxt_inp; + break; + } + if (closed) return; + waiting = true; + wait(); + } + if (waiting) { + waiting = false; + notifyAll(); + } + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + @Override + protected void flush() throws IOException { + } + + @Override + protected synchronized void stop() throws IOException { + closed = true; + if (waiting) { + waiting = false; + notifyAll(); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java new file mode 100644 index 000000000..9300e3ad4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.protocol.IChannel; + +public class LocalPeer extends AbstractPeer { + + private static Map<String, String> createAttributes() { + Map<String, String> attrs = new HashMap<String, String>(); + attrs.put(ATTR_ID, "TCFLocal"); + attrs.put(ATTR_NAME, "Local Peer"); + attrs.put(ATTR_OS_NAME, System.getProperty("os.name")); + attrs.put(ATTR_TRANSPORT_NAME, "Loop"); + return attrs; + } + + public LocalPeer() { + super(createAttributes()); + } + + public IChannel openChannel() { + return new ChannelLoop(this); + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java new file mode 100644 index 000000000..cc8aeb422 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +public class ReadOnlyCollection<E> implements Set<E> { + + private final Collection<E> base; + + public ReadOnlyCollection(Collection<E> base) { + this.base = base; + } + + private void error() { + throw new Error("Read only Collection"); + } + + public boolean add(E e) { + error(); + return false; + } + + public boolean addAll(Collection<? extends E> c) { + error(); + return false; + } + + public void clear() { + error(); + } + + public boolean contains(Object o) { + return base.contains(o); + } + + public boolean containsAll(Collection<?> c) { + return base.containsAll(c); + } + + public boolean isEmpty() { + return base.isEmpty(); + } + + public Iterator<E> iterator() { + final Iterator<E> iterator = base.iterator(); + return new Iterator<E>() { + + public boolean hasNext() { + return iterator.hasNext(); + } + + public E next() { + return iterator.next(); + } + + public void remove() { + error(); + } + }; + } + + public boolean remove(Object o) { + error(); + return false; + } + + public boolean removeAll(Collection<?> c) { + error(); + return false; + } + + public boolean retainAll(Collection<?> c) { + error(); + return false; + } + + public int size() { + return base.size(); + } + + public Object[] toArray() { + return base.toArray(); + } + + public <T> T[] toArray(T[] a) { + return base.toArray(a); + } + + public boolean equals(Object o) { + return base.equals(o); + } + + public int hashCode() { + return base.hashCode(); + } + + public String toString() { + return base.toString(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java new file mode 100644 index 000000000..458d27f72 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class ReadOnlyMap<K,V> implements Map<K,V> { + + private final Map<K,V> base; + private Set<K> key_set; + private Set<Map.Entry<K, V>> entry_set; + private Collection<V> values; + + public ReadOnlyMap(Map<K,V> base) { + this.base = base; + } + + private void error() { + throw new Error("Read only Map"); + } + + public void clear() { + error(); + } + + public boolean containsKey(Object key) { + return base.containsKey(key); + } + + public boolean containsValue(Object value) { + return base.containsValue(value); + } + + public Set<Map.Entry<K, V>> entrySet() { + if (entry_set == null) entry_set = new ReadOnlyCollection<Map.Entry<K, V>>(base.entrySet()); + return entry_set; + } + + public V get(Object key) { + return base.get(key); + } + + public boolean isEmpty() { + return base.isEmpty(); + } + + public Set<K> keySet() { + if (key_set == null) key_set = new ReadOnlyCollection<K>(base.keySet()); + return key_set; + } + + public V put(K key, V value) { + error(); + return null; + } + + public void putAll(Map<? extends K, ? extends V> m) { + error(); + } + + public V remove(Object key) { + error(); + return null; + } + + public int size() { + return base.size(); + } + + public Collection<V> values() { + if (values == null) values = new ReadOnlyCollection<V>(base.values()); + return values; + } + + public boolean equals(Object o) { + return base.equals(o); + } + + public int hashCode() { + return base.hashCode(); + } + + public String toString() { + return base.toString(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java new file mode 100644 index 000000000..fa4c93eef --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.core.ChannelTCP; +import com.windriver.tcf.api.protocol.IChannel; + +public class RemotePeer extends AbstractPeer { + + public RemotePeer(Map<String,String> attrs) { + super(attrs); + } + + public boolean updateAttributes(Map<String,String> attrs1) { + boolean equ = true; + Map<String,String> attrs0 = getAttributesStorage(); + assert attrs1.get(ATTR_ID).equals(attrs0.get(ATTR_ID)); + for (Iterator<String> i = attrs0.keySet().iterator(); i.hasNext();) { + String key = i.next(); + if (!attrs0.get(key).equals(attrs1.get(key))) { + equ = false; + break; + } + } + for (Iterator<String> i = attrs1.keySet().iterator(); i.hasNext();) { + String key = i.next(); + if (!attrs1.get(key).equals(attrs0.get(key))) { + equ = false; + break; + } + } + if (!equ) { + attrs0.clear(); + attrs0.putAll(attrs1); + } + return !equ; + } + + public IChannel openChannel() { + String transport = getTransportName(); + if (transport.equals("TCP")) { + Map<String,String> attrs = getAttributes(); + String host = attrs.get(ATTR_IP_HOST); + String port = attrs.get(ATTR_IP_PORT); + if (host == null) throw new Error("No host name"); + if (port == null) throw new Error("No port number"); + return new ChannelTCP(this, host, Integer.parseInt(port)); + } + else { + throw new Error("Unknow transport name: " + transport); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java new file mode 100644 index 000000000..9892b9cbd --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.io.UnsupportedEncodingException; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; + +public class Token implements IToken { + + private static int cnt = 0; + + private final String id; + private final byte[] bytes; + private final IChannel.ICommandListener listener; + + public Token() { + id = null; + bytes = null; + listener = null; + } + + public Token(IChannel.ICommandListener listener) { + this.listener = listener; + id = Integer.toString(cnt++); + try { + bytes = id.getBytes("ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new Error(e); + } + } + + public Token(byte[] bytes) { + this.bytes = bytes; + listener = null; + try { + id = new String(bytes, "ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new Error(e); + } + } + + public boolean cancel() { + return false; + } + + public String getID() { + return id; + } + + public byte[] getBytes() { + return bytes; + } + + public IChannel.ICommandListener getListener() { + return listener; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java new file mode 100644 index 000000000..3e2d1840e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.core.AbstractChannel; +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ILocator; + +public class Transport { + + private static final Collection<AbstractChannel> channels = + new LinkedList<AbstractChannel>(); + private static final Collection<Protocol.ChannelOpenListener> listeners = + new LinkedList<Protocol.ChannelOpenListener>(); + + public static void channelOpened(final AbstractChannel channel) { + channels.add(channel); + for (Protocol.ChannelOpenListener l : listeners) { + try { + l.onChannelOpen(channel); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + + public static void channelClosed(final AbstractChannel channel, final Throwable x) { + channels.remove(channel); + } + + public static IChannel[] getOpenChannels() { + return channels.toArray(new IChannel[channels.size()]); + } + + public static void addChanalOpenListener(Protocol.ChannelOpenListener listener) { + listeners.add(listener); + } + + public static void removeChanalOpenListener(Protocol.ChannelOpenListener listener) { + listeners.remove(listener); + } + + public static void peerDisposed(AbstractPeer peer) { + Collection<AbstractChannel> bf = new ArrayList<AbstractChannel>(channels); + for (Iterator<AbstractChannel> i = bf.iterator(); i.hasNext();) { + AbstractChannel c = i.next(); + if (c.getRemotePeer() != peer) continue; + c.close(); + } + } + + /** + * Transmit TCF event message. + * The message is sent to all open communication channels – broadcasted. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + public static void sendEvent(String service_name, String event_name, byte[] data) { + for (Iterator<AbstractChannel> i = channels.iterator(); i.hasNext();) { + AbstractChannel channel = i.next(); + IService s = channel.getRemoteService(service_name); + if (s != null) channel.sendEvent(s, event_name, data); + } + } + + /** + * Call back after TCF messages sent by this host up to this moment are delivered + * to their intended targets. This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need cross channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + public static void sync(final Runnable done) { + final Set<IToken> set = new HashSet<IToken>(); + ILocator.DoneSync done_sync = new ILocator.DoneSync() { + public void doneSync(IToken token) { + assert set.contains(token); + set.remove(token); + if (set.isEmpty()) done.run(); + } + }; + for (Iterator<AbstractChannel> i = channels.iterator(); i.hasNext();) { + AbstractChannel channel = i.next(); + ILocator s = channel.getRemoteService(ILocator.class); + if (s != null) set.add(s.sync(done_sync)); + } + if (set.isEmpty()) Protocol.invokeLater(done); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java new file mode 100644 index 000000000..88ad937bb --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.local; + +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IDiagnostics; + +public class DiagnosticsService implements IDiagnostics { + + private final IChannel channel; + + private class CommandServer implements IChannel.ICommandServer { + + public void command(IToken token, String name, byte[] data) { + try { + if (name.equals("echo")) { + channel.sendResult(token, data); + } + else if (name.equals("getTestList")) { + channel.sendResult(token, JSON.toJSONSequence(new Object[]{ + new Integer(0), null, new String[0]})); + } + else { + channel.terminate(new Exception("Illegal command: " + name)); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + } + + public DiagnosticsService(IChannel channel) { + this.channel = channel; + channel.addCommandServer(this, new CommandServer()); + } + + public String getName() { + return NAME; + } + + public IToken echo(final String s, final DoneEcho done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneEcho(token, null, s); + } + }); + return token; + } + + public IToken getTestList(final DoneGetTestList done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneGetTestList(token, null, new String[0]); + } + }); + return token; + } + + public IToken runTest(final String s, final DoneRunTest done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneRunTest(token, new Exception("Test suite not found: " + s), null); + } + }); + return token; + } + + public IToken cancelTest(String context_id, final DoneCancelTest done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneCancelTest(token, null); + } + }); + return token; + } + + public IToken getSymbol(String context_id, String symbol_name, final DoneGetSymbol done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneGetSymbol(token, new Exception("Invalid context"), null); + } + }); + return token; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java new file mode 100644 index 000000000..9672433ea --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.local; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.core.AbstractChannel; +import com.windriver.tcf.api.internal.core.LocalPeer; +import com.windriver.tcf.api.internal.core.RemotePeer; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +/** + * Locator service uses transport layer to search + * for peers and to collect and maintain up-to-date + * data about peer’s attributes and capabilities (services). + */ +public class LocatorService implements ILocator { + + private static LocatorService locator; + private static final Map<String,IPeer> peers = new HashMap<String,IPeer>(); + private static final Collection<LocatorListener> listeners = new ArrayList<LocatorListener>(); + + private static LocalPeer local_peer; + + private DatagramSocket socket; + + private Thread output_thread = new Thread() { + public void run() { + for (;;) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + sendPeerInfoRequest(); + } + }); + try { + sleep(5 * 1000); + } + catch (InterruptedException x) { + break; + } + } + } + }; + + private Thread input_thread = new Thread() { + public void run() { + for (;;) { + try { + byte[] buf = new byte[0x1000]; + final DatagramPacket p = new DatagramPacket(buf, buf.length); + socket.receive(p); + Protocol.invokeAndWait(new Runnable() { + public void run() { + handleDatagramPacket(p); + } + }); + } + catch (Exception x) { + Activator.log("Cannot read from datagram socket", x); + break; + } + } + } + }; + + public LocatorService() { + locator = this; + try { + socket = new DatagramSocket(); + socket.setBroadcast(true); + input_thread.setName("TCF Locator Receiver"); + output_thread.setName("TCF Locator Transmitter"); + input_thread.start(); + output_thread.start(); + } + catch (Exception x) { + Activator.log("Cannot create datagram socket", x); + } + } + + public static LocalPeer getLocalPeer() { + return local_peer; + } + + public static void addPeer(IPeer peer) { + assert peers.get(peer.getID()) == null; + if (peer instanceof LocalPeer) local_peer = (LocalPeer)peer; + peers.put(peer.getID(), peer); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerAdded(peer); + } + } + + public static void removePeer(IPeer peer) { + assert peers.get(peer.getID()) == peer; + peers.remove(peer); + String id = peer.getID(); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerRemoved(id); + } + } + + private void notifyPeer(IPeer peer) { + assert peers.get(peer.getID()) == peer; + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerChanged(peer); + } + } + + public static void channelStarted(final AbstractChannel channel) { + channel.addEventListener(locator, new IChannel.IEventListener() { + public void event(String name, byte[] data) { + locator.event(channel, name, data); + } + }); + channel.addCommandServer(locator, new IChannel.ICommandServer() { + public void command(IToken token, String name, byte[] data) { + locator.command(channel, token, name, data); + } + }); + } + + @SuppressWarnings("unchecked") + private void event(AbstractChannel channel, String name, byte[] data) { + try { + if (name.equals("Hello")) { + Collection<String> c = (Collection<String>)JSON.parseSequence(data)[0]; + channel.onLocatorHello(c); + } + else { + throw new IOException("Unknown event: Locator." + name); + } + } + catch (IOException e) { + channel.terminate(e); + } + } + + private void command(AbstractChannel channel, IToken token, String name, byte[] data) { + try { + if (name.equals("redirect")) { + // String peer_id = (String)JSON.parseSequence(data)[0]; + // TODO: perform local ILocator.redirect + channel.sendResult(token, JSON.toJSONSequence(new Object[]{ + new Integer(0), null })); + } + else if (name.equals("sync")) { + channel.sendResult(token, null); + } + else { + channel.terminate(new Exception("Illegal command: " + name)); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + + private void sendPeerInfoRequest() { + try { + byte[] buf = new byte[8]; + int i = 0; + buf[i++] = 'T'; + buf[i++] = 'C'; + buf[i++] = 'F'; + buf[i++] = '1'; + buf[i++] = CONF_REQ_INFO; + buf[i++] = 0; + buf[i++] = 0; + buf[i++] = 0; + for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements();) { + NetworkInterface f = e.nextElement(); + /* TODO: Class InterfaceAddress does not exists in Java versions before 1.6. + * When support for old Java versions is not needed any more, + * the code below should be replaced with: + * for (InterfaceAddress ia : f.getInterfaceAddresses()) { + * socket.send(new DatagramPacket(buf, buf.length, ia.getBroadcast(), 1534)); + * } + */ + Enumeration<InetAddress> n = f.getInetAddresses(); + while (n.hasMoreElements()) { + InetAddress ina = n.nextElement(); + byte[] adr = ina.getAddress(); + if (adr.length != 4) { + // TODO: Support IPv6 + // System.out.println("Dont support IPv6: " + ina); + continue; + } + /* Since we don't know actual broadcast address, + * lets try different combinations. + * Hopefully one of them will work. + */ + int h = adr[0] & 0xff; + if (h >= 1 && h <= 127 && h != 38) { + adr[3] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[2] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[1] = (byte)255; + } + else if (h >= 128 && h <= 191) { + adr[3] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[2] = (byte)255; + } + else { + adr[3] = (byte)(adr[3] | 0x0f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x01f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x03f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x07f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)255; + } + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + } + } + } + catch (Exception x) { + Activator.log("Cannot send datagram packet", x); + } + } + + private void handleDatagramPacket(DatagramPacket p) { + try { + byte[] buf = p.getData(); + int len = p.getLength(); + if (len < 8) return; + if (buf[0] != 'T') return; + if (buf[1] != 'C') return; + if (buf[2] != 'F') return; + if (buf[3] != '1') return; + switch (buf[4]) { + case CONF_PEER_INFO: + handlePeerInfoPacket(p); + break; + case CONF_REQ_INFO: + handleReqInfoPacket(p); + break; + } + } + catch (Throwable x) { + Activator.log("Invalid datagram packet received", x); + } + } + + private void handlePeerInfoPacket(DatagramPacket p) throws Exception { + Map<String,String> map = new HashMap<String,String>(); + String s = new String(p.getData(), 8, p.getLength() - 8, "UTF8"); + int len = s.length(); + int i = 0; + while (i < len) { + int i0 = i; + while (i < len && s.charAt(i) != '=' && s.charAt(i) != 0) i++; + int i1 = i; + if (i < len && s.charAt(i) == '=') i++; + int i2 = i; + while (i < len && s.charAt(i) != 0) i++; + int i3 = i; + if (i < len && s.charAt(i) == 0) i++; + String key = s.substring(i0, i1); + String val = s.substring(i2, i3); + map.put(key, val); + } + String id = map.get(IPeer.ATTR_ID); + if (id == null) throw new Exception("Invalid peer info: no ID"); + IPeer peer = peers.get(id); + if (peer instanceof RemotePeer) { + if (((RemotePeer)peer).updateAttributes(map)) { + notifyPeer(peer); + } + } + else { + new RemotePeer(map); + } + } + + private void handleReqInfoPacket(DatagramPacket p) { + byte[] buf = p.getData(); + int len = p.getLength(); + // TODO: handleReqInfoPacket() + } + + /*----------------------------------------------------------------------------------*/ + + /* + * Return local instance of Locator service + */ + public static LocatorService getLocator() { + return locator; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#getName() + */ + public String getName() { + return NAME; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#getPeers() + */ + public Map<String,IPeer> getPeers() { + return peers; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#redirect() + */ + public IToken redirect(String peer_id, DoneRedirect done) { + throw new Error("Channel redirect cannot be done on local peer"); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#sync() + */ + public IToken sync(DoneSync done) { + throw new Error("Channel sync cannot be done on local peer"); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#addListener(com.windriver.tcf.api.protocol.ILocator.Listener) + */ + public void addListener(LocatorListener listener) { + listeners.add(listener); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#removeListener(com.windriver.tcf.api.protocol.ILocator.Listener) + */ + public void removeListener(LocatorListener listener) { + listeners.remove(listener); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java new file mode 100644 index 000000000..475ff79fe --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IBreakpoints; + +public class BreakpointsProxy implements IBreakpoints { + + private final IChannel channel; + private final Map<BreakpointsListener,IChannel.IEventListener> listeners = + new HashMap<BreakpointsListener,IChannel.IEventListener>(); + + public BreakpointsProxy(IChannel channel) { + this.channel = channel; + } + + public IToken set(Map<String, Object>[] properties, final DoneCommand done) { + return new Command(channel, this, "set", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken add(Map<String, Object> properties, final DoneCommand done) { + return new Command(channel, this, "add", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken change(Map<String, Object> properties, final DoneCommand done) { + return new Command(channel, this, "change", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken disable(String[] ids, final DoneCommand done) { + return new Command(channel, this, "disable", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken enable(String[] ids, final DoneCommand done) { + return new Command(channel, this, "enable", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken remove(String[] ids, final DoneCommand done) { + return new Command(channel, this, "remove", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken getIDs(final DoneGetIDs done) { + return new Command(channel, this, "getIDs", null) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetIDs(token, error, arr); + } + }.token; + } + + public IToken getProperties(String id, final DoneGetProperties done) { + return new Command(channel, this, "getProperties", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,Object> map = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + map = (Map<String,Object>)args[2]; + } + done.doneGetProperties(token, error, map); + } + }.token; + } + + public IToken getStatus(String id, final DoneGetStatus done) { + return new Command(channel, this, "getStatus", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,Object> map = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + map = (Map<String,Object>)args[2]; + } + done.doneGetStatus(token, error, map); + } + }.token; + } + + public String getName() { + return NAME; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + public void addListener(final BreakpointsListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("status")) { + assert args.length == 2; + listener.breakpointStatusChanged((String)args[0], (Map<String,Object>)args[1]); + } + else { + throw new IOException("Breakpoints service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(BreakpointsListener listener) { + IChannel.IEventListener l = listeners.get(listener); + if (l != null) channel.removeEventListener(this, l); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java new file mode 100644 index 000000000..cafcb845e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IDiagnostics; + +public class DiagnosticsProxy implements IDiagnostics { + + private final IChannel channel; + + private static class Symbol implements ISymbol { + + private final Map<String,Object> props; + + Symbol(Map<String,Object> props) { + this.props = props; + } + + public String getSectionName() { + return (String)props.get("Section"); + } + + public Number getValue() { + return (Number)props.get("Value"); + } + + public boolean isAbs() { + Boolean b = (Boolean)props.get("Abs"); + return b != null && b.booleanValue(); + } + + public boolean isCommon() { + String s = (String)props.get("Storage"); + return s != null && s.equals("COMMON"); + } + + public boolean isGlobal() { + String s = (String)props.get("Storage"); + return s != null && s.equals("GLOBAL"); + } + + public boolean isLocal() { + String s = (String)props.get("Storage"); + return s != null && s.equals("LOCAL"); + } + + public boolean isUndef() { + String s = (String)props.get("Storage"); + return s != null && s.equals("UNDEF"); + } + } + + public DiagnosticsProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken echo(String s, final DoneEcho done) { + return new Command(channel, this, "echo", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + String str = null; + if (error == null) { + assert args.length == 1; + str = (String)args[0]; + } + done.doneEcho(token, error, str); + } + }.token; + } + + public IToken getTestList(final DoneGetTestList done) { + return new Command(channel, this, "getTestList", null) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetTestList(token, error, arr); + } + }.token; + } + + public IToken runTest(String s, final DoneRunTest done) { + return new Command(channel, this, "runTest", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + String str = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + str = (String)args[2]; + } + done.doneRunTest(token, error, str); + } + }.token; + } + + public IToken cancelTest(String s, final DoneCancelTest done) { + return new Command(channel, this, "cancelTest", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCancelTest(token, error); + } + }.token; + } + + public IToken getSymbol(String context_id, String symbol_name, final DoneGetSymbol done) { + return new Command(channel, this, "getSymbol", new Object[]{ context_id, symbol_name }) { + @Override + public void done(Exception error, Object[] args) { + ISymbol sym = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + sym = toSymbol(args[2]); + } + done.doneGetSymbol(token, error, sym); + } + }.token; + } + + private String[] toStringArray(Collection<String> c) { + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + @SuppressWarnings("unchecked") + private ISymbol toSymbol(Object o) { + if (o == null) return null; + return new Symbol((Map<String,Object>)o); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java new file mode 100644 index 000000000..38d8678ef --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java @@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Base64; +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IFileSystem; + +public class FileSystemProxy implements IFileSystem { + + private final class FileHandle implements IFileHandle { + final String id; + + FileHandle(String id) { + this.id = id; + } + + public IFileSystem getService() { + return FileSystemProxy.this; + } + + public String toString() { + return "[File Handle '" + id + "']"; + } + } + + private static final class Status extends FileSystemException { + + private static final long serialVersionUID = -1636567076145085980L; + + private final int status; + + Status(int status, String message) { + super(message); + this.status = status; + } + + Status(Exception x) { + super(x); + this.status = STATUS_FAILURE; + } + + public int getStatus() { + return status; + } + } + + private abstract class FileSystemCommand extends Command { + + FileSystemCommand(String command, Object[] args) { + super(channel, FileSystemProxy.this, command, args); + } + + public Status toFSError(Object code, Object data) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 32) cmd = cmd.substring(0, 32) + "..."; + return new Status(error_code, + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code); + } + } + + private final IChannel channel; + + public FileSystemProxy(IChannel channel) { + this.channel = channel; + } + + public IToken close(IFileHandle handle, final DoneClose done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("close", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneClose(token, s); + } + }.token; + } + + public IToken setstat(String path, FileAttrs attrs, final DoneSetStat done) { + Object dt = toObject(attrs); + return new FileSystemCommand("setstat", new Object[]{ path, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSetStat(token, s); + } + }.token; + } + + public IToken fsetstat(IFileHandle handle, FileAttrs attrs, final DoneSetStat done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + Object dt = toObject(attrs); + return new FileSystemCommand("fsetstat", new Object[]{ id, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSetStat(token, s); + } + }.token; + } + + public IToken stat(String path, final DoneStat done) { + return new FileSystemCommand("stat", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken fstat(IFileHandle handle, final DoneStat done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("fstat", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken lstat(String path, final DoneStat done) { + return new FileSystemCommand("lstat", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken mkdir(String path, FileAttrs attrs, final DoneMkDir done) { + Object dt = toObject(attrs); + return new FileSystemCommand("mkdir", new Object[]{ path, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneMkDir(token, s); + } + }.token; + } + + public IToken open(String file_name, int flags, FileAttrs attrs, final DoneOpen done) { + Object dt = toObject(attrs); + return new FileSystemCommand("open", new Object[]{ file_name, new Integer(flags), dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileHandle h = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + h = toFileHandle(args[2]); + } + } + done.doneOpen(token, s, h); + } + }.token; + } + + public IToken opendir(String path, final DoneOpen done) { + return new FileSystemCommand("opendir", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileHandle h = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + h = toFileHandle(args[2]); + } + } + done.doneOpen(token, s, h); + } + }.token; + } + + public IToken read(IFileHandle handle, long offset, int len, final DoneRead done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("read", new Object[]{ + id, Long.valueOf(offset), Integer.valueOf(len) }) { + public void done(Exception error, Object[] args) { + Status s = null; + byte[] b = null; + boolean eof = false; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 4; + s = toFSError(args[1], args[2]); + if (s == null) { + String str = (String)args[0]; + if (str != null) b = Base64.toByteArray(str.toCharArray()); + eof = ((Boolean)args[3]).booleanValue(); + } + } + done.doneRead(token, s, b, eof); + } + }.token; + } + + public IToken readdir(IFileHandle handle, final DoneReadDir done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("readdir", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + DirEntry[] b = null; + boolean eof = false; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 4; + s = toFSError(args[1], args[2]); + if (s == null) { + b = toDirEntryArray(args[0]); + eof = ((Boolean)args[3]).booleanValue(); + } + } + done.doneReadDir(token, s, b, eof); + } + }.token; + } + + public IToken roots(final DoneRoots done) { + return new FileSystemCommand("roots", null) { + public void done(Exception error, Object[] args) { + Status s = null; + DirEntry[] b = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[1], args[2]); + if (s == null) { + b = toDirEntryArray(args[0]); + } + } + done.doneRoots(token, s, b); + } + }.token; + } + + public IToken readlink(String path, final DoneReadLink done) { + return new FileSystemCommand("readlink", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + String p = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + p = (String)args[2]; + } + } + done.doneReadLink(token, s, p); + } + }.token; + } + + public IToken realpath(String path, final DoneRealPath done) { + return new FileSystemCommand("realpath", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + String p = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + p = (String)args[2]; + } + } + done.doneRealPath(token, s, p); + } + }.token; + } + + public IToken remove(String file_name, final DoneRemove done) { + return new FileSystemCommand("remove", new Object[]{ file_name }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRemove(token, s); + } + }.token; + } + + public IToken rename(String old_path, String new_path, final DoneRename done) { + return new FileSystemCommand("rename", new Object[]{ old_path, new_path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRename(token, s); + } + }.token; + } + + public IToken rmdir(String path, final DoneRemove done) { + return new FileSystemCommand("rmdir", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRemove(token, s); + } + }.token; + } + + public IToken symlink(String link_path, String target_path, final DoneSymLink done) { + return new FileSystemCommand("symlink", new Object[]{ link_path, target_path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSymLink(token, s); + } + }.token; + } + + public IToken write(IFileHandle handle, long offset, byte[] data, + int data_pos, int data_size, final DoneWrite done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("write", new Object[]{ + id, Long.valueOf(offset), Base64.toBase64(data, data_pos, data_size) }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneWrite(token, s); + } + }.token; + } + + public IToken copy(String src_path, String dst_path, + boolean copy_permissions, boolean copy_uidgid, final DoneCopy done) { + return new FileSystemCommand("copy", new Object[]{ + src_path, dst_path, Boolean.valueOf(copy_permissions), + Boolean.valueOf(copy_uidgid) }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneCopy(token, s); + } + }.token; + } + + public IToken user(final DoneUser done) { + return new FileSystemCommand("user", null) { + @Override + public void done(Exception error, Object[] args) { + Status s = null; + int r_uid = 0; + int e_uid = 0; + int r_gid = 0; + int e_gid = 0; + String home = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 5; + r_uid = ((Number)args[0]).intValue(); + e_uid = ((Number)args[1]).intValue(); + r_gid = ((Number)args[2]).intValue(); + e_gid = ((Number)args[3]).intValue(); + home = (String)args[4]; + } + done.doneUser(token, s, r_uid, e_uid, r_gid, e_gid, home); + } + }.token; + } + + public String getName() { + return NAME; + } + + private Object toObject(FileAttrs attrs) { + if (attrs == null) return null; + Map<String,Object> m = new HashMap<String,Object>(); + if (attrs.attributes != null) m.putAll(attrs.attributes); + if ((attrs.flags & ATTR_SIZE) != 0) { + m.put("Size", Long.valueOf(attrs.size)); + } + if ((attrs.flags & ATTR_UIDGID) != 0) { + m.put("UID", Integer.valueOf(attrs.uid)); + m.put("GID", Integer.valueOf(attrs.gid)); + } + if ((attrs.flags & ATTR_PERMISSIONS) != 0) { + m.put("Permissions", Integer.valueOf(attrs.permissions)); + } + if ((attrs.flags & ATTR_ACMODTIME) != 0) { + m.put("ATime", Long.valueOf(attrs.atime)); + m.put("MTime", Long.valueOf(attrs.mtime)); + } + return m; + } + + @SuppressWarnings("unchecked") + private FileAttrs toFileAttrs(Object o) { + if (o == null) return null; + Map<String,Object> m = new HashMap<String,Object>((Map<String,Object>)o); + int flags = 0; + long size = 0; + int uid = 0; + int gid = 0; + int permissions = 0; + long atime = 0; + long mtime = 0; + Number n = (Number)m.remove("Size"); + if (n != null) { + size = n.longValue(); + flags |= ATTR_SIZE; + } + Number n1 = (Number)m.remove("UID"); + Number n2 = (Number)m.remove("GID"); + if (n1 != null && n2 != null) { + uid = n1.intValue(); + gid = n2.intValue(); + flags |= ATTR_UIDGID; + } + n = (Number)m.remove("Permissions"); + if (n != null) { + permissions = n.intValue(); + flags |= ATTR_PERMISSIONS; + } + n1 = (Number)m.remove("ATime"); + n2 = (Number)m.remove("MTime"); + if (n1 != null && n2 != null) { + atime = n1.longValue(); + mtime = n2.longValue(); + flags |= ATTR_ACMODTIME; + } + return new FileAttrs(flags, size, uid, gid, permissions, atime, mtime, m); + } + + private FileHandle toFileHandle(Object o) { + if (o == null) return null; + return new FileHandle(o.toString()); + } + + @SuppressWarnings("unchecked") + private DirEntry[] toDirEntryArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + DirEntry[] res = new DirEntry[c.size()]; + int i = 0; + for (Map<String,Object> m : c) { + res[i++] = new DirEntry( + (String)m.get("FileName"), + (String)m.get("LongName"), + toFileAttrs(m.get("Attrs"))); + } + return res; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java new file mode 100644 index 000000000..36293b5c2 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; + +/** + * Objects of GenericProxy class represent remote services, which don't + * have a proxy class defined for them. + * Clients still can use such services, but framework will not provide + * service specific utility methods for message formatting and parsing. + */ +public class GenericProxy implements IService { + + private final IChannel channel; + private final String name; + + public GenericProxy(IChannel channel, String name) { + this.channel = channel; + this.name = name; + } + + public String getName() { + return name; + } + + public IChannel getChannel() { + return channel; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java new file mode 100644 index 000000000..51e32cd7f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java @@ -0,0 +1,79 @@ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ILineNumbers; + +public class LineNumbersProxy implements ILineNumbers { + + private final IChannel channel; + + public LineNumbersProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken mapToSource(String context_id, Number start_address, + Number end_address, final DoneMapToSource done) { + return new Command(channel, this, "mapToSource", new Object[]{ context_id, + start_address, end_address }) { + @Override + public void done(Exception error, Object[] args) { + CodeArea[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toTextAreaArray(args[2]); + } + done.doneMapToSource(token, error, arr); + } + }.token; + } + + private static int getInteger(Map<String,Object> map, String name, int def) { + Number n = (Number)map.get(name); + if (n == null) return def; + return n.intValue(); + } + + private static String getString(Map<String,Object> map, String name, String def) { + String s = (String)map.get(name); + if (s == null) return def; + return s; + } + + private static boolean getBoolean(Map<String,Object> map, String name) { + Boolean b = (Boolean)map.get(name); + if (b == null) return false; + return b.booleanValue(); + } + + @SuppressWarnings("unchecked") + private CodeArea[] toTextAreaArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + CodeArea[] arr = new CodeArea[c.size()]; + String directory = null; + String file = null; + for (Map<String,Object> area : c) { + directory = getString(area, "Dir", directory); + file = getString(area, "File", file); + arr[n++] = new CodeArea(directory, file, + getInteger(area, "SLine", 0), getInteger(area, "SCol", 0), + getInteger(area, "ELine", 0), getInteger(area, "ECol", 0), + (Number)area.get("SAddr"), (Number)area.get("EAddr"), + getInteger(area, "ISA", 0), + getBoolean(area, "IsStmt"), getBoolean(area, "BasicBlock"), + getBoolean(area, "PrologueEnd"), getBoolean(area, "EpilogueBegin")); + } + return arr; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java new file mode 100644 index 000000000..91dccfb38 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +public class LocatorProxy implements ILocator { + + private final IChannel channel; + private final Map<String,IPeer> peers = new HashMap<String,IPeer>(); + private final Collection<LocatorListener> listeners = new ArrayList<LocatorListener>(); + + private class Peer implements IPeer { + + private final Map<String, String> attrs; + + Peer(Map<String,String> attrs) { + this.attrs = attrs; + } + + public Map<String, String> getAttributes() { + assert Protocol.isDispatchThread(); + return attrs; + } + + public String getID() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_ID); + } + + public String getName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_NAME); + } + + public String getOSName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_OS_NAME); + } + + public String getTransportName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_TRANSPORT_NAME); + } + + public IChannel openChannel() { + assert Protocol.isDispatchThread(); + IChannel c = channel.getRemotePeer().openChannel(); + c.redirect(getID()); + return c; + } + }; + + private final IChannel.IEventListener event_listener = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("peerAdded")) { + assert args.length == 1; + IPeer peer = new Peer((Map<String,String>)args[0]); + peers.put(peer.getID(), peer); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerAdded(peer); + } + } + else if (name.equals("peerChanged")) { + assert args.length == 1; + Map<String,String> m = (Map<String,String>)args[0]; + if (m == null) throw new Error("Locator service: invalid peerChanged event - no peer ID"); + IPeer peer = peers.get(m.get(IPeer.ATTR_ID)); + if (peer == null) throw new Error("Invalid peerChanged event: unknown peer ID"); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerChanged(peer); + } + } + else if (name.equals("peerRemoved")) { + assert args.length == 1; + String id = (String)args[0]; + IPeer peer = peers.get(id); + if (peer == null) throw new Error("Locator service: invalid peerRemoved event - unknown peer ID"); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerRemoved(id); + } + } + else { + throw new IOException("Locator service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + + public LocatorProxy(IChannel channel) { + this.channel = channel; + channel.addEventListener(this, event_listener); + } + + public String getName() { + return NAME; + } + + public Map<String,IPeer> getPeers() { + return peers; + } + + public IToken redirect(String peer_id, final DoneRedirect done) { + return new Command(channel, this, "redirect", new Object[]{ peer_id }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneRedirect(token, error); + } + }.token; + } + + public IToken sync(final DoneSync done) { + return new Command(channel, this, "sync", null) { + @Override + public void done(Exception error, Object[] args) { + if (error != null) channel.terminate(error); + done.doneSync(token); + } + }.token; + } + + public void addListener(LocatorListener listener) { + listeners.add(listener); + } + + public void removeListener(LocatorListener listener) { + listeners.remove(listener); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java new file mode 100644 index 000000000..19b3e1aa4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java @@ -0,0 +1,366 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.Base64; +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IMemory; + +public class MemoryProxy implements IMemory { + + private final IChannel channel; + private final Map<MemoryListener,IChannel.IEventListener> listeners = + new HashMap<MemoryListener,IChannel.IEventListener>(); + + private static class Range implements Comparable<Range> { + int offs; + int size; + int stat; + String msg; + + public int compareTo(Range o) { + if (offs < o.offs) return -1; + if (offs > o.offs) return +1; + return 0; + } + } + + private class MemoryErrorReport extends MemoryError implements ErrorOffset { + + private static final long serialVersionUID = 796525409870265390L; + private final Range[] ranges; + + @SuppressWarnings("unchecked") + MemoryErrorReport(String msg, Number addr, Object ranges) { + super(msg); + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)ranges; + this.ranges = new Range[c.size()]; + int n = 0; + BigInteger addr_bi = addr instanceof BigInteger ? + (BigInteger)addr : new BigInteger(addr.toString()); + for (Map<String,Object> m : c) { + Range r = new Range(); + Number x = (Number)m.get("addr"); + BigInteger y = x instanceof BigInteger ? + (BigInteger)x : new BigInteger(x.toString()); + r.offs = addr_bi.subtract(y).intValue(); + r.size = ((Number)m.get("size")).intValue(); + r.stat = ((Number)m.get("stat")).intValue(); + r.msg = Command.toErrorString(m.get("msg")); + assert r.offs >= 0; + assert r.size >= 0; + this.ranges[n++] = r; + } + Arrays.sort(this.ranges); + } + + public String getMessage(int offset) { + int l = 0; + int h = ranges.length - 1; + while (l <= h) { + int n = (l + h) / 2; + Range r = ranges[n]; + if (r.offs > offset) { + h = n - 1; + } + else if (offset >= r.offs + r.size) { + l = n + 1; + } + else { + return r.msg; + } + } + return null; + } + + public int getStatus(int offset) { + int l = 0; + int h = ranges.length - 1; + while (l <= h) { + int n = (l + h) / 2; + Range r = ranges[n]; + if (r.offs > offset) { + h = n - 1; + } + else if (offset >= r.offs + r.size) { + l = n + 1; + } + else { + return r.stat; + } + } + return BYTE_UNKNOWN; + } + } + + private class MemContext implements MemoryContext { + + private final Map<String,Object> props; + + MemContext(Map<String,Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + String s = (String)props.get(PROP_PARENT_ID); + if (s == null) return ""; + return s; + } + + public int getAddressSize() { + Number n = (Number)props.get(PROP_ADDRESS_SIZE); + if (n == null) return 0; + return n.intValue(); + } + + public int getProcessID() { + Number n = (Number)props.get(PROP_PROCESS_ID); + if (n == null) return 0; + return n.intValue(); + } + + public boolean isBigEndian() { + Boolean n = (Boolean)props.get(PROP_BIG_ENDIAN); + if (n == null) return false; + return n.booleanValue(); + } + + public Map<String, Object> getProperties() { + return props; + } + + public IToken fill(final Number addr, int word_size, + byte[] value, int size, int mode, final DoneMemory done) { + return new MemoryCommand("fill", new Object[] { + getID(), addr, word_size, size, mode, value + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 3; + e = toMemoryError(addr, args[0], args[1], args[2]); + } + done.doneMemory(token, e); + } + }.token; + } + + public IToken get(final Number addr, int word_size, + final byte[] buf, final int offs, final int size, + int mode, final DoneMemory done) { + return new MemoryCommand("get", new Object[] { + getID(), addr, word_size, size, mode + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 4; + String str = (String)args[0]; + if (str != null) Base64.toByteArray(buf, offs, size, str.toCharArray()); + e = toMemoryError(addr, args[1], args[2], args[3]); + } + done.doneMemory(token, e); + } + }.token; + } + + public IToken set(final Number addr, int word_size, + byte[] buf, int offs, int size, int mode, final DoneMemory done) { + return new MemoryCommand("set", new Object[] { + getID(), addr, word_size, size, mode, Base64.toBase64(buf, offs, size) + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 3; + e = toMemoryError(addr, args[0], args[1], args[2]); + } + done.doneMemory(token, e); + } + }.token; + } + + public String toString() { + return "[Memory Context " + props.toString() + "]"; + } + } + + public MemoryProxy(IChannel channel) { + this.channel = channel; + } + + public void addListener(final MemoryListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextAdded")) { + assert args.length == 1; + listener.contextAdded(toContextArray(args[0])); + } + else if (name.equals("contextChanged")) { + assert args.length == 1; + listener.contextChanged(toContextArray(args[0])); + } + else if (name.equals("contextRemoved")) { + assert args.length == 1; + listener.contextRemoved(toStringArray(args[0])); + } + else if (name.equals("memoryChanged")) { + assert args.length == 2; + listener.memoryChanged((String)args[0], + toAddrArray(args[1]), toSizeArray(args[1])); + } + else { + throw new IOException("Memory service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(MemoryListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + public IToken getContext(String context_id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + MemContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new MemContext((Map<String,Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public String getName() { + return NAME; + } + + private abstract class MemoryCommand extends Command { + + MemoryCommand(String cmd, Object[] args) { + super(channel, MemoryProxy.this, cmd, args); + } + + MemoryError toMemoryError(Number addr, Object code, Object data, Object ranges) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 72) cmd = cmd.substring(0, 72) + "..."; + return new MemoryErrorReport( + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code, addr, ranges); + } + } + + @SuppressWarnings("unchecked") + private MemoryContext[] toContextArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return new MemoryContext[0]; + int n = 0; + MemoryContext[] ctx = new MemoryContext[c.size()]; + for (Iterator<Map<String,Object>> i = c.iterator(); i.hasNext();) { + ctx[n++] = new MemContext(i.next()); + } + return ctx; + } + + @SuppressWarnings("unchecked") + private long[] toSizeArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + long[] a = new long[c.size()]; + int n = 0; + for (Map<String,Object> m : c) { + Number sz = (Number)m.get("size"); + a[n++] = sz == null ? 0 : sz.longValue(); + } + return a; + } + + @SuppressWarnings("unchecked") + private Number[] toAddrArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + Number[] a = new Number[c.size()]; + int n = 0; + for (Map<String,Object> m : c) { + a[n++] = (Number)m.get("addr"); + } + return a; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java new file mode 100644 index 000000000..4db81a295 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IProcesses; + +public class ProcessesProxy implements IProcesses { + + private final IChannel channel; + + private class ProcessContext implements IProcesses.ProcessContext { + + private final Map<String,Object> props; + + ProcessContext(Map<String,Object> props) { + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + return (String)props.get(PROP_PARENTID); + } + + public boolean canTerminate() { + Boolean b = (Boolean)props.get(PROP_CAN_TERMINATE); + return b != null && b.booleanValue(); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public boolean isAttached() { + Boolean b = (Boolean)props.get(PROP_ATTACHED); + return b != null && b.booleanValue(); + } + + public IToken attach(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "attach", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken detach(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "detach", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken signal(int signal, final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "signal", new Object[]{ getID(), signal }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken terminate(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "terminate", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public Map<String, Object> getProperties() { + return props; + } + + public String toString() { + return "[Processes Context " + props.toString() + "]"; + } + } + + public ProcessesProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, boolean attached_only, final DoneGetChildren done) { + return new Command(channel, this, + "getChildren", new Object[]{ parent_context_id, attached_only }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] ids = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + ids = toStringArray(args[2]); + } + done.doneGetChildren(token, error, ids); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, + "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + ProcessContext ctx = null; + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new ProcessContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getEnvironment(final DoneGetEnvironment done) { + return new Command(channel, this, "getEnvironment", null) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,String> env = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + env = toEnvMap(args[2]); + } + done.doneGetEnvironment(token, error, env); + } + }.token; + } + + public IToken start(String directory, String file, + String[] command_line, Map<String,String> environment, + boolean attach, final DoneStart done) { + return new Command(channel, this, + "start", new Object[]{ directory, file, command_line, + toEnvStringArray(environment), attach }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + ProcessContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new ProcessContext((Map<String, Object>)args[2]); + } + } + done.doneStart(token, error, ctx); + } + }.token; + } + + @SuppressWarnings("unchecked") + private static String[] toStringArray(Object o) { + if (o == null) return new String[0]; + Collection<String> c = (Collection<String>)o; + return (String[])c.toArray(new String[c.size()]); + } + + private static String[] toEnvStringArray(Map<String,String> m) { + if (m == null) return new String[0]; + int n = 0; + String[] arr = new String[m.size()]; + for (String s : m.keySet()) { + arr[n++] = s + "=" + m.get(s); + } + return arr; + } + + @SuppressWarnings("unchecked") + private static Map<String,String> toEnvMap(Object o) { + Map<String,String> m = new HashMap<String,String>(); + if (o == null) return m; + Collection<String> c = (Collection<String>)o; + for (String s : c) { + int i = s.indexOf('='); + if (i >= 0) m.put(s.substring(0, i), s.substring(i + 1)); + else m.put(s, ""); + } + return m; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java new file mode 100644 index 000000000..e313078ec --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IRegisters; + +public class RegistersProxy implements IRegisters { + + private final IChannel channel; + private final Map<RegistersListener,IChannel.IEventListener> listeners = + new HashMap<RegistersListener,IChannel.IEventListener>(); + + private class Context implements RegistersContext { + + private final Map<String,Object> props; + + Context(Map<String,Object> props) { + this.props = props; + } + + public String[] getAvailableFormats() { + return toStringArray(props.get(PROP_FORMATS)); + } + + public int[] getBitNumbers() { + return toIntArray(props.get(PROP_BITS)); + } + + public String getDescription() { + return (String)props.get(PROP_DESCRIPTION); + } + + public int getFirstBitNumber() { + Number n = (Number)props.get(PROP_FIST_BIT); + if (n == null) return 0; + return n.intValue(); + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public NamedValue[] getNamedValues() { + return toValuesArray(props.get(PROP_VALUES)); + } + + public String getParentID() { + return (String)props.get(PROP_PARENT_ID); + } + + public Map<String, Object> getProperties() { + return props; + } + + public boolean hasSideEffects() { + Boolean n = (Boolean)props.get(PROP_SIDE_EFFECTS); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isBigEndian() { + Boolean n = (Boolean)props.get(PROP_BIG_ENDIAN); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isFloat() { + Boolean n = (Boolean)props.get(PROP_FLOAT); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isLeftToRight() { + Boolean n = (Boolean)props.get(PROP_LEFT_TO_RIGHT); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isReadOnce() { + Boolean n = (Boolean)props.get(PROP_READ_ONCE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isReadable() { + Boolean n = (Boolean)props.get(PROP_READBLE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isVolatile() { + Boolean n = (Boolean)props.get(PROP_VOLATILE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isWriteOnce() { + Boolean n = (Boolean)props.get(PROP_WRITE_ONCE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isWriteable() { + Boolean n = (Boolean)props.get(PROP_WRITEABLE); + if (n == null) return false; + return n.booleanValue(); + } + + public IToken get(String format, final DoneGet done) { + return new Command(channel, RegistersProxy.this, "get", + new Object[]{ getID(), format }) { + @Override + public void done(Exception error, Object[] args) { + String val = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + val = (String)args[2]; + } + done.doneGet(token, error, val); + } + }.token; + } + + public IToken set(String format, String value, final DoneSet done) { + return new Command(channel, RegistersProxy.this, "set", + new Object[]{ getID(), format, value }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneSet(token, error); + } + }.token; + } + + public String toString() { + return "[Registers Context " + props.toString() + "]"; + } + } + + public RegistersProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Context ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new Context((Map<String,Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public void addListener(final RegistersListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextChanged")) { + listener.contextChanged(); + } + else if (name.equals("registerChanged")) { + assert args.length == 1; + listener.registerChanged((String)args[0]); + } + else { + throw new IOException("Registers service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(RegistersListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + @SuppressWarnings("unchecked") + private int[] toIntArray(Object o) { + Collection<Number> c = (Collection<Number>)o; + if (c == null) return null; + int i = 0; + int[] arr = new int[c.size()]; + for (Number n : c) arr[i++] = n.intValue(); + return arr; + } + + @SuppressWarnings("unchecked") + private NamedValue[] toValuesArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + int i = 0; + NamedValue[] arr = new NamedValue[c.size()]; + for (final Map<String,Object> m : c) { + arr[i++] = new NamedValue() { + + public String getDescription() { + return (String)m.get("Description"); + } + + public String getName() { + return (String)m.get("Name"); + } + + public Number getValue() { + return (Number)m.get("Value"); + } + }; + } + return arr; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java new file mode 100644 index 000000000..774a5e6ec --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IRunControl; + +public class RunControlProxy implements IRunControl { + + private final IChannel channel; + private final Map<RunControlListener,IChannel.IEventListener> listeners = + new HashMap<RunControlListener,IChannel.IEventListener>(); + + private class RunContext implements IRunControl.RunControlContext { + + private final Map<String, Object> props; + + RunContext(Map<String, Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public Map<String, Object> getProperties() { + return props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + String s = (String)props.get(PROP_PARENT_ID); + if (s == null) return ""; + return s; + } + + public boolean isContainer() { + Boolean b = (Boolean)props.get(PROP_IS_CONTAINER); + return b != null && b.booleanValue(); + } + + public boolean hasState() { + Boolean b = (Boolean)props.get(PROP_HAS_STATE); + return b != null && b.booleanValue(); + } + + public boolean canResume(int mode) { + if (props.containsKey(PROP_CAN_RESUME)) { + int b = ((Number)props.get(PROP_CAN_RESUME)).intValue(); + return (b & (1 << mode)) != 0; + } + return false; + } + + public boolean canCount(int mode) { + if (props.containsKey(PROP_CAN_COUNT)) { + int b = ((Number)props.get(PROP_CAN_COUNT)).intValue(); + return (b & (1 << mode)) != 0; + } + return false; + } + + public boolean canSuspend() { + Boolean b = (Boolean)props.get(PROP_CAN_SUSPEND); + return b != null && b.booleanValue(); + } + + public boolean canTerminate() { + Boolean b = (Boolean)props.get(PROP_CAN_TERMINATE); + return b != null && b.booleanValue(); + } + + public IToken getState(final DoneGetState done) { + return new Command(channel, RunControlProxy.this, "getState", new Object[]{ getID() }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + boolean susp = false; + String pc = null; + String reason = null; + Map<String,Object> map = null; + if (error == null) { + assert args.length == 6; + error = toError(args[0], args[1]); + susp = ((Boolean)args[2]).booleanValue(); + if (args[3] != null) pc = ((Number)args[3]).toString(); + reason = (String)args[4]; + map = (Map<String,Object>)args[5]; + } + done.doneGetState(token, error, susp, pc, reason, map); + } + }.token; + } + + public IToken resume(int mode, int count, DoneCommand done) { + return command("resume", new Object[]{ getID(), mode, count }, done); + } + + public IToken suspend(DoneCommand done) { + return command("suspend", new Object[]{ getID() }, done); + } + + public IToken terminate(DoneCommand done) { + return command("terminate", new Object[]{ getID() }, done); + } + + private IToken command(String cmd, Object[] args, final DoneCommand done) { + return new Command(channel, RunControlProxy.this, cmd, args) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public String toString() { + return "[Run Control Context " + props.toString() + "]"; + } + } + + public RunControlProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public void addListener(final RunControlListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextSuspended")) { + assert args.length == 4; + listener.contextSuspended( + (String)args[0], + args[1] == null ? null : ((Number)args[1]).toString(), + (String)args[2], (Map<String,Object>)args[3]); + } + else if (name.equals("contextResumed")) { + assert args.length == 1; + listener.contextResumed((String)args[0]); + } + else if (name.equals("contextAdded")) { + assert args.length == 1; + listener.contextAdded(toContextArray(args[0])); + } + else if (name.equals("contextChanged")) { + assert args.length == 1; + listener.contextChanged(toContextArray(args[0])); + } + else if (name.equals("contextRemoved")) { + assert args.length == 1; + listener.contextRemoved(toStringArray(args[0])); + } + else if (name.equals("contextException")) { + assert args.length == 2; + listener.contextException((String)args[0], (String)args[1]); + } + else if (name.equals("containerSuspended")) { + assert args.length == 5; + listener.containerSuspended( + (String)args[0], + args[1] == null ? null : ((Number)args[1]).toString(), + (String)args[2], (Map)args[3], + toStringArray(args[4])); + } + else if (name.equals("containerResumed")) { + assert args.length == 1; + listener.containerResumed(toStringArray(args[0])); + } + else { + throw new IOException("RunControl service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(RunControlListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + public IToken getContext(String context_id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + RunControlContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new RunContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + @SuppressWarnings("unchecked") + private RunControlContext[] toContextArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + RunControlContext[] ctx = new RunControlContext[c.size()]; + for (Map<String, Object> m : c) ctx[n++] = new RunContext(m); + return ctx; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java new file mode 100644 index 000000000..5385c5d1f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IStackTrace; + +public class StackTraceProxy implements IStackTrace { + + private final IChannel channel; + + private class Context implements StackTraceContext { + + private final Map<String,Object> props; + + Context(Map<String,Object> props) { + this.props = props; + } + + public Number getArgumentsAddress() { + return (Number)props.get(PROP_ARGUMENTS_ADDRESS); + } + + public int getArgumentsCount() { + Number n = (Number)props.get(PROP_ARGUMENTS_COUNT); + if (n == null) return 0; + return n.intValue(); + } + + public Number getFrameAddress() { + return (Number)props.get(PROP_FRAME_ADDRESS); + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public String getParentID() { + return (String)props.get(PROP_PARENT_ID); + } + + public Number getReturnAddress() { + return (Number)props.get(PROP_RETURN_ADDRESS); + } + + public Map<String, Object> getProperties() { + return props; + } + } + + public StackTraceProxy(IChannel channel) { + this.channel = channel; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String[] id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + StackTraceContext[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[1], args[2]); + arr = toContextArray(args[0]); + } + done.doneGetContext(token, error, arr); + } + }.token; + } + + public String getName() { + return NAME; + } + + @SuppressWarnings("unchecked") + private StackTraceContext[] toContextArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + StackTraceContext[] ctx = new StackTraceContext[c.size()]; + for (Map<String,Object> m : c) ctx[n++] = new Context(m); + return ctx; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java new file mode 100644 index 000000000..6e4648bc8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ISysMonitor; + +public class SysMonitorProxy implements ISysMonitor { + + private final IChannel channel; + + private class SysMonitorContext implements ISysMonitor.SysMonitorContext { + + private final Map<String, Object> props; + + SysMonitorContext(Map<String, Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getCurrentWorkingDirectory() { + return (String)props.get(PROP_CWD); + } + + public String getFile() { + return (String)props.get(PROP_FILE); + } + + public String getParentID() { + return (String)props.get(PROP_PARENTID); + } + + public String getRoot() { + return (String)props.get(PROP_ROOT); + } + + public String getGroupName() { + return (String)props.get(PROP_GROUPNAME); + } + + public long getPGRP() { + if (!props.containsKey(PROP_PGRP)) return -1; + return ((Number)props.get(PROP_PGRP)).longValue(); + } + + public long getPID() { + if (!props.containsKey(PROP_PID)) return -1; + return ((Number)props.get(PROP_PID)).longValue(); + } + + public long getPPID() { + if (!props.containsKey(PROP_PPID)) return -1; + return ((Number)props.get(PROP_PPID)).longValue(); + } + + public long getRSS() { + if (!props.containsKey(PROP_RSS)) return -1; + return ((Number)props.get(PROP_RSS)).longValue(); + } + + public String getState() { + return (String)props.get(PROP_STATE); + } + + public long getTGID() { + if (!props.containsKey(PROP_TGID)) return -1; + return ((Number)props.get(PROP_TGID)).longValue(); + } + + public long getTracerPID() { + if (!props.containsKey(PROP_TRACERPID)) return -1; + return ((Number)props.get(PROP_TRACERPID)).longValue(); + } + + public long getUGID() { + if (!props.containsKey(PROP_UGID)) return -1; + return ((Number)props.get(PROP_UGID)).longValue(); + } + + public long getUID() { + if (!props.containsKey(PROP_UID)) return -1; + return ((Number)props.get(PROP_UID)).longValue(); + } + + public String getUserName() { + return (String)props.get(PROP_USERNAME); + } + + public long getVSize() { + if (!props.containsKey(PROP_VSIZE)) return -1; + return ((Number)props.get(PROP_VSIZE)).longValue(); + } + + public long getPSize() { + if (!props.containsKey(PROP_PSIZE)) return -1; + return ((Number)props.get(PROP_PSIZE)).longValue(); + } + + public Map<String, Object> getProperties() { + return props; + } + + public String toString() { + return "[Sys Monitor Context " + props.toString() + "]"; + } + } + + public SysMonitorProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + SysMonitorContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new SysMonitorContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getCommandLine(String id, final DoneGetCommandLine done) { + return new Command(channel, this, "getCommandLine", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetCommandLine(token, error, arr); + } + }.token; + } + + public IToken getEnvironment(String id, final DoneGetEnvironment done) { + return new Command(channel, this, "getEnvironment", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetEnvironment(token, error, arr); + } + }.token; + } + + private static String[] toStringArray(Collection<String> c) { + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java new file mode 100644 index 000000000..4b15660b8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +import java.util.Collection; + +/** + * IChannel represents communication link connecting two end points (peers). + * The channel asynchronously transmits messages: commands, results and events. + * A single channel may be used to communicate with multiple services. + * Multiple channels may be used to connect the same peers, however no command or event + * ordering is guaranteed across channels. + */ + +public interface IChannel { + + /** + * Channel state IDs + */ + static final int + STATE_OPENNING = 0, + STATE_OPEN = 1, + STATE_CLOSED = 2; + + /** + * @return channel state, see STATE_* + */ + int getState(); + + /** + * Send command message to remote peer for execution. Commands can be queued + * locally before transmission. Sending commands 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 service - a remote service that will be sent the command + * @param name - command name + * @param args - command arguments encoded into array of bytes + * @param done - call back object + * @return pending command handle + */ + IToken sendCommand(IService service, String name, byte[] args, ICommandListener done); + + /** + * Command listener interface. Clients implement this interface to + * receive command results. + */ + interface ICommandListener { + + /** + * Called when progress message (intermediate result) is received + * from remote peer. + * @param token - command handle + * @param data - progress message arguments encoded into array of bytes + */ + void progress(IToken token, byte[] data); + + /** + * Called when command result received from remote peer. + * @param token - command handle + * @param data - command result message arguments encoded into array of bytes + */ + void result(IToken token, byte[] data); + + /** + * Called when communication channel was closed while command was waiting for result. + * @param token - command handle + * @param error - exception that forced the channel to close + */ + void terminated(IToken token, Exception error); + } + + /** + * Send result message to remote peer. 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 sendResult(IToken token, byte[] results); + + /** + * Get current level of outbound traffic congestion. + * + * @return integer value in range –100..100, where –100 means no pending + * messages (no traffic), 0 means optimal load, and positive numbers + * indicate level of congestion. + * + * Note: inbound traffic congestion is detected by framework and reported to + * remote peer without client needed to be involved. + */ + int getCongestion(); + + /** + * Channel listener interface. + */ + interface IChannelListener { + + /** + * Called when a channel is opened. + */ + void onChannelOpened(); + + /** + * Called when channel closed. If it is closed because of an error, + * ‘error’ parameter will describe the error. ‘error’ is null if channel + * is closed normally by calling Channel.close(). + * @param error - channel exception or null + */ + void onChannelClosed(Throwable error); + + /** + * Notifies listeners about channel congestion level changes. + * When level > 0 client should delay sending more messages. + * @param level - current congestion level + */ + void congestionLevel(int level); + } + + /** + * Subscribe a channel listener. The listener will be notified about changes of + * channel state and changes of outbound traffic congestion level. + * @param listener - channel listener implementation + */ + void addChannelListener(IChannelListener listener); + + /** + * Remove a channel listener. + * @param listener - channel listener implementation + */ + void removeChannelListener(IChannelListener listener); + + /** + * Command server interface. + * This interface is to be implemented by service providers. + */ + interface ICommandServer { + + /** + * Called every time a command is received from remote peer. + * @param token - command handle + * @param name - command name + * @param data - command arguments encoded into array of bytes + */ + void command(IToken token, String name, byte[] data); + } + + /** + * Subscribe a command server. The server will be notified about command + * messages received through this channel for given service. + * @param service - local service implementation + * @param server - implementation of service commands listener + */ + void addCommandServer(IService service, ICommandServer server); + + /** + * Remove a command server. + * @param service - local service implementation + * @param server - implementation of service commands listener + */ + void removeCommandServer(IService service, ICommandServer server); + + /** + * A generic interface for service event listener. + * Services usually define a service specific event listener interface. + * Clients should user service specific listener interface, unless no such interface is defined. + */ + interface IEventListener { + /** + * Called when service event message is received + * @param name - event name + * @param data - event arguments encode as array of bytes + */ + void event(String name, byte[] data); + } + + /** + * Subscribe an event listener for given service. + * @param service - remote service proxy + * @param server - implementation of service event listener + */ + void addEventListener(IService service, IEventListener listener); + + /** + * Unsubscribe an event listener for given service. + * @param service - remote service proxy + * @param server - implementation of service event listener + */ + void removeEventListener(IService service, IEventListener listener); + + /** + * @return IPeer object representing local endpoint of communication channel. + */ + IPeer getLocalPeer(); + + /** + * @return IPeer object representing remote endpoint of communication channel. + */ + IPeer getRemotePeer(); + + /** + * @return collection of services available on local peer. + */ + Collection<String> getLocalServices(); + + /** + * @return an object representing a service from local peer. + * Return null if the service is not available. + */ + IService getLocalService(String service_name); + + /** + * @return an object representing a service from local peer. + * Service object should implement given interface. + * Return null if implementation of the interface is not available. + */ + <V extends IService> V getLocalService(Class<V> service_interface); + + /** + * @return collection of services available on remote peer. + */ + Collection<String> getRemoteServices(); + + /** + * @return an object representing a service from remote peer. + * Return null if the service is not available. + */ + IService getRemoteService(String service_name); + + /** + * @return an object representing a service from remote peer. + * Service object should implement given interface. + * Return null if implementation of the interface is not available. + */ + <V extends IService> V getRemoteService(Class<V> service_interface); + + /** + * Close communication channel. + */ + void close(); + + /** + * Close channel in case of communication error. + * @param error - cause of channel termination + */ + void terminate(Throwable error); + + /** + * Redirect this channel to given peer using this channel remote peer locator service as a proxy. + * @param peer_id - peer that will become new remote communication endpoint of this channel + */ + void redirect(String peer_id); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java new file mode 100644 index 000000000..0ace6e635 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +/** + * Clients of the framework should implement this interface and call Protocol.setEventQueue. + * Implementation should encapsulate a queue and asynchronous event dispatch machinery, which + * extracts events from the queue and dispatches them by calling event's run() method. + * The implementation is used by framework to queue and dispatch all events. + */ +public interface IEventQueue { + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of this event queue. + * Events are dispatched in same order as queued. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed asynchronously. + */ + void invokeLater(Runnable runnable); + + /** + * Returns true if the calling thread is this event queue's dispatch thread. + * Use this call the ensure that a given task is being executed (or not being) on dispatch thread. + * + * @return true if running on the dispatch thread. + */ + boolean isDispatchThread(); + + /** + * Get current level of queue congestion. + * + * @return integer value in range –100..100, where –100 means no pending + * messages (no traffic), 0 means optimal load, and positive numbers + * indicate level of congestion. + */ + public int getCongestion(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java new file mode 100644 index 000000000..7abc75e2c --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +import java.util.Map; + +/** + * Both hosts and targets are represented by objects + * implementing IPeer interface. A peer can act as host or + * target depending on services it implements. + * List of currently known peers can be retrieved by + * calling ILocator.getPeers() + */ +public interface IPeer { + + /** + * Peer property names. Implementation can define additional properties. + */ + static final String + ATTR_ID = "ID", + ATTR_NAME = "Name", + ATTR_OS_NAME = "OSName", + ATTR_TRANSPORT_NAME = "TransportName", + ATTR_IP_HOST = "Host", + ATTR_IP_ALIASES = "Aliases", + ATTR_IP_ADDRESSES = "Addresses", + ATTR_IP_PORT = "Port"; + + + /** + * @return map of peer attributes + */ + Map<String, String> getAttributes(); + + /** + * @return peer unique ID, same as getAttributes().get(ATTR_ID) + */ + String getID(); + + /** + * @return peer name, same as getAttributes().get(ATTR_NAME) + */ + String getName(); + + /** + * Same as getAttributes().get(ATTR_OS_NAME) + */ + String getOSName(); + + /** + * Same as getAttributes().get(ATTR_TRANSPORT_NAME) + */ + String getTransportName(); + + /** + * Open channel to communicate with this peer. + * Note: the channel is not fully open yet when this method returns. + * It’s state is IChannel.STATE_OPENNING. + * Protocol.Listener will be called when the channel will be opened or closed. + */ + IChannel openChannel(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java new file mode 100644 index 000000000..d6eb3d4f1 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +/** + * Base interface for all service interfaces. A client can get list of available services + * by calling Peer.getServices() + */ + +public interface IService { + + /** + * Get unique name of this service. + * @return service name. + */ + String getName(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java new file mode 100644 index 000000000..b42645107 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +/** + * IToken is created by the framework for each command sent to a remote peer. + * It is used to match results to commands and to cancel pending commands. + */ +public interface IToken { + + /** + * Try to cancel a command associated with given token. A command can be + * canceled by this method only if it was not transmitted yet to remote peer + * for execution. Successfully canceled command does not produce any result + * messages. + * + * @return true if successful. + */ + boolean cancel(); + +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java new file mode 100644 index 000000000..16fdcb5e8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java @@ -0,0 +1,516 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.ReadOnlyCollection; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; + +/** + * JSON is TCF preferred marshaling. This class implements generation and parsing of JSON strings. + * The code is optimized for speed since it is a time-critical part of the framework. + */ +public class JSON { + + private static char[] tmp_buf = new char[0x1000]; + private static byte[] tmp_bbf = new byte[0x1000]; + private static int tmp_buf_pos; + + private static Reader reader; + private static final char[] cur_buf = new char[0x1000]; + private static int cur_buf_pos; + private static int cur_buf_len; + private static int cur_ch; + + // This buffer is used to create nice error reports + private static final char[] err_buf = new char[100]; + private static int err_buf_pos; + private static int err_buf_cnt; + + private static void write(char ch) { + if (tmp_buf_pos >= tmp_buf.length) { + char[] tmp = new char[tmp_buf.length * 2]; + System.arraycopy(tmp_buf, 0, tmp, 0, tmp_buf_pos); + tmp_buf = tmp; + } + tmp_buf[tmp_buf_pos++] = ch; + } + + private static void write(String s) { + int l = s.length(); + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + if (tmp_buf_pos >= tmp_buf.length) write(ch); + else tmp_buf[tmp_buf_pos++] = ch; + } + } + + private static void writeUInt(int n) { + assert n >= 0; + if (n >= 10) writeUInt(n / 10); + write((char)('0' + n % 10)); + } + + private static void read() throws IOException { + if (cur_buf_pos >= cur_buf_len) { + cur_buf_len = reader.read(cur_buf); + cur_buf_pos = 0; + if (cur_buf_len < 0) { + cur_buf_len = 0; + cur_ch = -1; + return; + } + } + cur_ch = cur_buf[cur_buf_pos++]; + err_buf[err_buf_pos++] = (char)cur_ch; + if (err_buf_pos >= err_buf.length) { + err_buf_pos = 0; + err_buf_cnt++; + } + } + + private static void error() throws IOException { + error("syntax error"); + } + + private static void error(String msg) throws IOException { + StringBuffer bf = new StringBuffer(); + bf.append("JSON " + msg + ":"); + int cnt = 0; + boolean nl = true; + for (int i = 0;; i++) { + char ch = 0; + if (err_buf_cnt == 0 && i < err_buf_pos) { + ch = err_buf[i]; + } + else if (err_buf_cnt > 0 && i < err_buf.length) { + ch = err_buf[(err_buf_pos + i) % err_buf.length]; + } + else { + int n = reader.read(); + if (n < 0) break; + ch = (char)n; + } + if (nl) { + bf.append("\n "); + if (err_buf_cnt == 0) bf.append(cnt); + else bf.append('*'); + bf.append(": "); + if (cnt == 0 && err_buf_cnt > 0) bf.append("..."); + nl = false; + } + if (ch == 0) { + cnt++; + nl = true; + continue; + } + bf.append(ch); + } + throw new IOException(bf.toString()); + } + + private static int readHexDigit() throws IOException { + int n = 0; + if (cur_ch >= '0' && cur_ch <= '9') n = cur_ch - '0'; + else if (cur_ch >= 'A' && cur_ch <= 'F') n = cur_ch - 'A' + 10; + else if (cur_ch >= 'a' && cur_ch <= 'f') n = cur_ch - 'a' + 10; + else error(); + read(); + return n; + } + + private static Object readNestedObject() throws IOException { + switch (cur_ch) { + case '"': + read(); + tmp_buf_pos = 0; + for (;;) { + if (cur_ch <= 0) error(); + if (cur_ch == '"') break; + if (cur_ch == '\\') { + read(); + if (cur_ch <= 0) error(); + switch (cur_ch) { + case '"': + case '\\': + case '/': + break; + case 'b': + cur_ch = '\b'; + break; + case 'f': + cur_ch = '\f'; + break; + case 'n': + cur_ch = '\n'; + break; + case 'r': + cur_ch = '\r'; + break; + case 't': + cur_ch = '\t'; + break; + case 'u': + read(); + int n = 0; + n |= readHexDigit() << 12; + n |= readHexDigit() << 8; + n |= readHexDigit() << 4; + n |= readHexDigit(); + write((char)n); + continue; + default: + error(); + break; + } + } + if (tmp_buf_pos >= tmp_buf.length) { + write((char)cur_ch); + } + else { + tmp_buf[tmp_buf_pos++] = (char)cur_ch; + } + if (cur_buf_pos >= cur_buf_len) { + read(); + } + else { + cur_ch = cur_buf[cur_buf_pos++]; + err_buf[err_buf_pos++] = (char)cur_ch; + if (err_buf_pos >= err_buf.length) { + err_buf_pos = 0; + err_buf_cnt++; + } + } + } + read(); + return new String(tmp_buf, 0, tmp_buf_pos); + case '[': + Collection<Object> l = new ArrayList<Object>(); + read(); + if (cur_ch <= 0) error(); + if (cur_ch != ']') { + for (;;) { + l.add(readNestedObject()); + if (cur_ch == ']') break; + if (cur_ch != ',') error(); + read(); + } + } + read(); + return new ReadOnlyCollection<Object>(l); + case '{': + Map<String,Object> m = new HashMap<String,Object>(); + read(); + if (cur_ch <= 0) error(); + if (cur_ch != '}') { + for (;;) { + String key = (String)readNestedObject(); + if (cur_ch != ':') error(); + read(); + Object val = readNestedObject(); + m.put(key, val); + if (cur_ch == '}') break; + if (cur_ch != ',') error(); + read(); + } + } + read(); + return new ReadOnlyMap<String,Object>(m); + case 'n': + read(); + if (cur_ch != 'u') error(); + read(); + if (cur_ch != 'l') error(); + read(); + if (cur_ch != 'l') error(); + read(); + return null; + case 'f': + read(); + if (cur_ch != 'a') error(); + read(); + if (cur_ch != 'l') error(); + read(); + if (cur_ch != 's') error(); + read(); + if (cur_ch != 'e') error(); + read(); + return Boolean.FALSE; + case 't': + read(); + if (cur_ch != 'r') error(); + read(); + if (cur_ch != 'u') error(); + read(); + if (cur_ch != 'e') error(); + read(); + return Boolean.TRUE; + default: + boolean neg = cur_ch == '-'; + if (neg) read(); + if (cur_ch >= '0' && cur_ch <= '9') { + // TODO: float number + int v = 0; + while (v <= 0x7fffffff / 10 - 1) { + v = v * 10 + (cur_ch - '0'); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new Integer(neg ? -v : v); + } + } + long vl = v; + while (vl < 0x7fffffffffffffffl / 10 - 1) { + vl = vl * 10 + (cur_ch - '0'); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new Long(neg ? -vl : vl); + } + } + StringBuffer sb = new StringBuffer(); + if (neg) sb.append('-'); + sb.append(vl); + while (true) { + sb.append(cur_ch); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new BigInteger(sb.toString()); + } + } + } + error(); + return null; + } + } + + private static Object readObject() throws IOException { + Object o = readNestedObject(); + if (cur_ch >= 0) error(); + return o; + } + + private static Object[] readSequence() throws IOException { + List<Object> l = new ArrayList<Object>(); + while (cur_ch >= 0) { + l.add(readNestedObject()); + if (cur_ch != 0) error("missing \\0 terminator"); + read(); + } + return l.toArray(); + } + + @SuppressWarnings("unchecked") + private static void writeObject(Object o) throws IOException { + if (o == null) { + write("null"); + } + else if (o instanceof Boolean) { + write(o.toString()); + } + else if (o instanceof Number) { + write(o.toString()); + } + else if (o instanceof String) { + String s = (String)o; + char[] arr = new char[s.length()]; + s.getChars(0, arr.length, arr, 0); + writeObject(arr); + } + else if (o instanceof char[]) { + char[] s = (char[])o; + write('"'); + int l = s.length; + for (int i = 0; i < l; i++) { + char ch = s[i]; + switch (ch) { + case 0: + write("\\u0000"); + break; + case '\r': + write("\\r"); + break; + case '\n': + write("\\n"); + break; + case '\t': + write("\\t"); + break; + case '\b': + write("\\b"); + break; + case '\f': + write("\\f"); + break; + case '"': + case '\\': + write('\\'); + default: + if (tmp_buf_pos >= tmp_buf.length) write(ch); + else tmp_buf[tmp_buf_pos++] = ch; + } + } + write('"'); + } + else if (o instanceof byte[]) { + write('['); + byte[] arr = (byte[])o; + boolean comma = false; + for (int i = 0; i < arr.length; i++) { + if (comma) write(','); + writeUInt(arr[i] & 0xff); + comma = true; + } + write(']'); + } + else if (o instanceof Object[]) { + write('['); + Object[] arr = (Object[])o; + boolean comma = false; + for (int i = 0; i < arr.length; i++) { + if (comma) write(','); + writeObject(arr[i]); + comma = true; + } + write(']'); + } + else if (o instanceof Collection) { + write('['); + boolean comma = false; + for (Iterator<Object> i = ((Collection<Object>)o).iterator(); i.hasNext();) { + if (comma) write(','); + writeObject(i.next()); + comma = true; + } + write(']'); + } + else if (o instanceof Map) { + Map<String,Object> map = (Map<String,Object>)o; + write('{'); + boolean comma = false; + for (Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator(); i.hasNext();) { + if (comma) write(','); + Map.Entry<String,Object> e = i.next(); + writeObject(e.getKey()); + write(':'); + writeObject(e.getValue()); + comma = true; + } + write('}'); + } + else { + throw new IOException("JSON: unsupported object type"); + } + } + + private static byte[] toBytes() throws UnsupportedEncodingException { + int inp_pos = 0; + int out_pos = 0; + while (inp_pos < tmp_buf_pos) { + if (out_pos >= tmp_bbf.length - 4) { + byte[] tmp = new byte[tmp_bbf.length * 2]; + System.arraycopy(tmp_bbf, 0, tmp, 0, out_pos); + tmp_bbf = tmp; + } + int ch = tmp_buf[inp_pos++]; + if (ch < 0x80) { + tmp_bbf[out_pos++] = (byte)ch; + } + else if (ch < 0x800) { + tmp_bbf[out_pos++] = (byte)((ch >> 6) | 0xc0); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + else if (ch < 0x10000) { + tmp_bbf[out_pos++] = (byte)((ch >> 12) | 0xe0); + tmp_bbf[out_pos++] = (byte)((ch >> 6) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + else { + tmp_bbf[out_pos++] = (byte)((ch >> 18) | 0xf0); + tmp_bbf[out_pos++] = (byte)((ch >> 12) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)((ch >> 6) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + } + byte[] res = new byte[out_pos]; + System.arraycopy(tmp_bbf, 0, res, 0, out_pos); + return res; + } + + public static String toJSON(Object o) throws IOException { + assert Protocol.isDispatchThread(); + tmp_buf_pos = 0; + writeObject(o); + return new String(tmp_buf, 0, tmp_buf_pos); + } + + public static byte[] toJASONBytes(Object o) throws IOException { + assert Protocol.isDispatchThread(); + tmp_buf_pos = 0; + writeObject(o); + return toBytes(); + } + + public static byte[] toJSONSequence(Object[] o) throws IOException { + assert Protocol.isDispatchThread(); + if (o == null || o.length == 0) return null; + tmp_buf_pos = 0; + for (int i = 0; i < o.length; i++) { + writeObject(o[i]); + write((char)0); + } + return toBytes(); + } + + public static Object parseOne(String s) throws IOException { + assert Protocol.isDispatchThread(); + reader = new StringReader(s); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readObject(); + } + + public static Object parseOne(byte[] b) throws IOException { + assert Protocol.isDispatchThread(); + reader = new InputStreamReader(new ByteArrayInputStream(b), "UTF8"); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readObject(); + } + + public static Object[] parseSequence(byte[] b) throws IOException { + assert Protocol.isDispatchThread(); + reader = new InputStreamReader(new ByteArrayInputStream(b), "UTF8"); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readSequence(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java new file mode 100644 index 000000000..7e577ccf5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.protocol; + +import com.windriver.tcf.api.internal.core.LocalPeer; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.services.ILocator; + +/** + * + * Class Protocol provides static methods to access Target Communication Framework root objects: + * 1. the framework event queue and dispatch thread; + * 2. local instance of Locator service, which maintains a list of available targets; + * 3. list of open communication channels. + */ +public class Protocol { + + private static IEventQueue event_queue; + + /** + * Before TCF can be used it should be given an object implementing IEventQueue interface. + * The implementation maintains a queue of objects implementing Runnable interface and + * executes <code>run</code> methods of that objects in a sequence by a single thread. + * The thread in referred as TCF event dispatch thread. Objects in the queue are called TCF events. + * Executing <code>run</code> method of an event is also called dispatching of event. + * + * Only few methods in TCF APIs are thread safe - can be invoked from any thread. + * If a method description does not say "can be invoked from any thread" explicitly - + * the method must be invoked from TCF event dispatch thread. All TCF listeners are + * invoked from the dispatch thread. + * + * @param event_queue - IEventQueue implementation. + */ + public static void setEventQueue(IEventQueue event_queue) { + assert Protocol.event_queue == null; + Protocol.event_queue = event_queue; + event_queue.invokeLater(new Runnable() { + + public void run() { + new LocatorService(); + new LocalPeer(); + } + }); + } + + /** + * @return instance of IEventQueue that is used for TCF events. + */ + public static IEventQueue getEventQueue() { + return event_queue; + } + + /** + * Returns true if the calling thread is TCF event dispatch thread. + * Use this call the ensure that a given task is being executed (or not being) + * on dispatch thread. + * + * @return true if running on the dispatch thread. + */ + public static boolean isDispatchThread() { + return event_queue != null && event_queue.isDispatchThread(); + } + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of the framework. + * Events are dispatched in same order as queued. + * If invokeLater is called from the dispatching thread + * the <i>runnable.run()</i> will still be deferred until + * all pending events have been processed. + * + * This method can be invoked from any thread. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed asynchronously. + */ + public static void invokeLater(Runnable runnable) { + event_queue.invokeLater(runnable); + } + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of the framework. + * Calling thread is suspended until the method is executed. + * + * This method can be invoked from any thread. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed on dispatch thread. + */ + public static void invokeAndWait(final Runnable runnable) { + if (event_queue.isDispatchThread()) { + runnable.run(); + } + else { + Runnable r = new Runnable() { + public void run() { + try { + runnable.run(); + } + finally { + synchronized (this) { + notify(); + } + } + } + }; + synchronized (r) { + event_queue.invokeLater(r); + try { + r.wait(); + } + catch (InterruptedException x) { + throw new Error(x); + } + } + } + } + + /** + * Get instance of the framework locator service. + * The service can be used to discover available remote peers. + * + * @return instance of ILocator. + */ + public static ILocator getLocator() { + return LocatorService.getLocator(); + } + + /** + * Return an array of all open channels. + * @return an array of IChannel + */ + public static IChannel[] getOpenChannels() { + return Transport.getOpenChannels(); + } + + /** + * Interface to be implemented by clients willing to be notified when + * new TCF communication channel is opened. + */ + public interface ChannelOpenListener { + public void onChannelOpen(IChannel channel); + } + + /** + * Add a listener that will be notified when new channel is opened. + * @param listener + */ + public static void addChannelOpenListener(ChannelOpenListener listener) { + Transport.addChanalOpenListener(listener); + } + + /** + * Remove channel opening listener. + * @param listener + */ + public static void removeChannelOpenListener(ChannelOpenListener listener) { + Transport.removeChanalOpenListener(listener); + } + + /** + * Transmit TCF event message. + * The message is sent to all open communication channels – broadcasted. + */ + public static void sendEvent(String service_name, String event_name, byte[] data) { + Transport.sendEvent(service_name, event_name, data); + } + + /** + * Call back after TCF messages sent by this host up to this moment are delivered + * to their intended target. This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need cross channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + */ + public static void sync(Runnable done) { + Transport.sync(done); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java new file mode 100644 index 000000000..fbcb5e0b5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * Breakpoint is represented by unique identifier and set of properties. + * Breakpoint identifier (String id) needs to be unique across all hosts and targets. + * + * Breakpoint properties (Map<String,Object>) is extendable collection of named attributes, + * which define breakpoint location and behavior. This module defines some common + * attribute names (see PROP_*), host tools and target agents may support additional attributes. + * + * For each breakpoint a target agent maintains another extendable collection of named attributes: + * breakpoint status (Map<String,Object>, see STATUS_*). While breakpoint properties are + * persistent and represent user input, breakpoint status reflects dynamic target agent reports + * about breakpoint current state, like actual addresses where breakpoint is planted or planting errors. + */ +public interface IBreakpoints extends IService { + + /** + * Service name. + */ + static final String NAME = "Breakpoints"; + + /** + * Breakpoint property names. + */ + static final String + PROP_ID = "ID", // String + PROP_ENABLED = "Enabled", // Boolean + PROP_ADDRESS = "Address", // String + PROP_CONDITION = "Condition", // String + PROP_FILE = "File", // String + PROP_LINE = "Line", // Number + PROP_COLUMN = "Column"; // Number + + /** + * Breakpoint status field names. + */ + static final String + STATUS_PLANTED = "Planted", // Array of addresses + STATUS_ERROR = "Error", // String + STATUS_FILE = "File", // String + STATUS_LINE = "Line", // Number + STATUS_COLUMN = "Column"; // Number + + /** + * Call back interface for breakpoint service commands. + */ + interface DoneCommand { + void doneCommand(IToken token, Exception error); + } + + /** + * Download breakpoints data to target agent. + * The command is intended to be used only to initialize target breakpoints table + * when communication channel is open. After that, host should + * notify target about (incremental) changes in breakpoint data by sending + * add, change and remove commands. + * + * @param properties - array of breakpoints. + * @param done - command result call back object. + */ + IToken set(Map<String,Object>[] properties, DoneCommand done); + + /** + * Called when breakpoint is added into breakpoints table. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */ + IToken add(Map<String,Object> properties, DoneCommand done); + + /** + * Called when breakpoint properties are changed. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */ + IToken change(Map<String,Object> properties, DoneCommand done); + + /** + * Tell target to change (only) PROP_ENABLED breakpoint property 'true'. + * @param ids - array of enabled breakpoint identifiers. + * @param done - command result call back object. + */ + IToken enable(String[] ids, DoneCommand done); + + /** + * Tell target to change (only) PROP_ENABLED breakpoint property to 'false'. + * @param ids - array of disabled breakpoint identifiers. + * @param done - command result call back object. + */ + IToken disable(String[] ids, DoneCommand done); + + /** + * Tell target to remove breakpoint. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken remove(String[] ids, DoneCommand done); + + /** + * Upload IDs of breakpoints known to target agent. + * @param done - command result call back object. + */ + IToken getIDs(DoneGetIDs done); + + interface DoneGetIDs { + void doneGetIDs(IToken token, Exception error, String[] ids); + } + + /** + * Upload properties of given breakpoint from target agent breakpoint table. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken getProperties(String id, DoneGetProperties done); + + interface DoneGetProperties { + void doneGetProperties(IToken token, Exception error, Map<String,Object> properties); + } + + /** + * Upload status of given breakpoint from target agent. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken getStatus(String id, DoneGetStatus done); + + interface DoneGetStatus { + void doneGetStatus(IToken token, Exception error, Map<String,Object> status); + } + + /** + * Breakpoints service events listener. + */ + interface BreakpointsListener { + + /** + * Called when breakpoint status changes. + * @param id - unique breakpoint identifier. + * @param status - breakpoint status. + */ + void breakpointStatusChanged(String id, Map<String,Object> status); + } + + void addListener(BreakpointsListener listener); + + void removeListener(BreakpointsListener listener); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java new file mode 100644 index 000000000..9552c4a6d --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * This is optional service that can be implemented by a peer. + * If implemented, the service can be used for testing of the peer and + * communication channel functionality and reliability. + */ + +public interface IDiagnostics extends IService { + + public static final String NAME = "Diagnostics"; + + public IToken echo(String s, DoneEcho done); + + public interface DoneEcho { + public void doneEcho(IToken token, Throwable error, String s); + } + + public IToken getTestList(DoneGetTestList done); + + public interface DoneGetTestList { + public void doneGetTestList(IToken token, Throwable error, String[] list); + } + + public IToken runTest(String s, DoneRunTest done); + + public interface DoneRunTest { + public void doneRunTest(IToken token, Throwable error, String context_id); + } + + public IToken cancelTest(String context_id, DoneCancelTest done); + + public interface DoneCancelTest { + public void doneCancelTest(IToken token, Throwable error); + } + + public IToken getSymbol(String context_id, String symbol_name, DoneGetSymbol done); + + public interface DoneGetSymbol { + public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol); + } + + public interface ISymbol { + public String getSectionName(); + public Number getValue(); + public boolean isUndef(); + public boolean isCommon(); + public boolean isGlobal(); + public boolean isLocal(); + public boolean isAbs(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java new file mode 100644 index 000000000..70213469b --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java @@ -0,0 +1,696 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.io.IOException; +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * File System service provides file transfer (and more generally file + * system access) functionality in TCF. The service design is + * derived from SSH File Transfer Protocol specifications. + * + * Request Synchronization and Reordering + * + * The protocol and implementations MUST process requests relating to + * the same file in the order in which they are received. In other + * words, if an application submits multiple requests to the server, the + * results in the responses will be the same as if it had sent the + * requests one at a time and waited for the response in each case. For + * example, the server may process non-overlapping read/write requests + * to the same file in parallel, but overlapping reads and writes cannot + * be reordered or parallelized. However, there are no ordering + * restrictions on the server for processing requests from two different + * file transfer connections. The server may interleave and parallelize + * them at will. + * + * There are no restrictions on the order in which responses to + * outstanding requests are delivered to the client, except that the + * server must ensure fairness in the sense that processing of no + * request will be indefinitely delayed even if the client is sending + * other requests so that there are multiple outstanding requests all + * the time. + * + * There is no limit on the number of outstanding (non-acknowledged) + * requests that the client may send to the server. In practice this is + * limited by the buffering available on the data stream and the queuing + * performed by the server. If the server's queues are full, it should + * not read any more data from the stream, and flow control will prevent + * the client from sending more requests. + * + * File Names + * + * This protocol represents file names as strings. File names are + * assumed to use the slash ('/') character as a directory separator. + * + * File names starting with a slash are "absolute", and are relative to + * the root of the file system. Names starting with any other character + * are relative to the user's default directory (home directory). Client + * can use 'user()' command to retrieve current user home directory. + * + * Servers SHOULD interpret a path name component ".." as referring to + * the parent directory, and "." as referring to the current directory. + * If the server implementation limits access to certain parts of the + * file system, it must be extra careful in parsing file names when + * enforcing such restrictions. There have been numerous reported + * security bugs where a ".." in a path name has allowed access outside + * the intended area. + * + * An empty path name is valid, and it refers to the user's default + * directory (usually the user's home directory). + * + * Otherwise, no syntax is defined for file names by this specification. + * Clients should not make any other assumptions; however, they can + * splice path name components returned by readdir() together + * using a slash ('/') as the separator, and that will work as expected. + */ +public interface IFileSystem extends IService { + + /** + * Service name. + */ + static final String NAME = "FileSystem"; + + /** + * Flags to be used with open() method. + */ + static final int + + /** + * Open the file for reading. + */ + O_READ = 0x00000001, + + /** + * Open the file for writing. If both this and O_READ are + * specified, the file is opened for both reading and writing. + */ + O_WRITE = 0x00000002, + + /** + * Force all writes to append data at the end of the file. + */ + O_APPEND = 0x00000004, + + /** + * If this flag is specified, then a new file will be created if one + * does not already exist (if O_TRUNC is specified, the new file will + * be truncated to zero length if it previously exists). + */ + O_CREAT = 0x00000008, + + /** + * Forces an existing file with the same name to be truncated to zero + * length when creating a file by specifying O_CREAT. + * O_CREAT MUST also be specified if this flag is used. + */ + O_TRUNC = 0x00000010, + + /** + * Causes the request to fail if the named file already exists. + * O_CREAT MUST also be specified if this flag is used. + */ + O_EXCL = 0x00000020; + + /** + * Flags to be used together with FileAttrs. + * The flags specify which of the fields are present. Those fields + * for which the corresponding flag is not set are not present (not + * included in the message). + */ + static final int + ATTR_SIZE = 0x00000001, + ATTR_UIDGID = 0x00000002, + ATTR_PERMISSIONS = 0x00000004, + ATTR_ACMODTIME = 0x00000008; + + /** + * FileAttrs is used both when returning file attributes from + * the server and when sending file attributes to the server. When + * sending it to the server, the flags field specifies which attributes + * are included, and the server will use default values for the + * remaining attributes (or will not modify the values of remaining + * attributes). When receiving attributes from the server, the flags + * specify which attributes are included in the returned data. The + * server normally returns all attributes it knows about. + */ + final static class FileAttrs { + + /** + * The `flags' specify which of the fields are present. + */ + public final int flags; + + /** + * The `size' field specifies the size of the file in bytes. + */ + public final long size; + + /** + * The `uid' and `gid' fields contain numeric Unix-like user and group + * identifiers, respectively. + */ + public final int uid; + public final int gid; + + /** + * The `permissions' field contains a bit mask of file permissions as + * defined by posix [1]. + */ + public final int permissions; + + /** + * The `atime' and `mtime' contain the access and modification times of + * the files, respectively. They are represented as milliseconds from + * midnight Jan 1, 1970 in UTC. + */ + public final long atime; + public final long mtime; + + /** + * Additional (non-standard) attributes. + */ + public final Map<String,Object> attributes; + + public FileAttrs(int flags, long size, int uid, int gid, + int permissions, long atime, long mtime, Map<String,Object> attributes) { + this.flags = flags; + this.size = size; + this.uid = uid; + this.gid = gid; + this.permissions = permissions; + this.atime = atime; + this.mtime = mtime; + this.attributes = attributes; + } + + /** + * Determines if the file system object is a file on the remote file system. + * + * @return true if and only if the object on the remote system can be considered to have "contents" that + * have the potential to be read and written as a byte stream. + */ + public boolean isFile() { + if ((flags & ATTR_PERMISSIONS) == 0) return false; + return (permissions & S_IFMT) == S_IFREG; + } + + /** + * Determines if the file system object is a directory on the remote file system. + * + * @return true if and only if the object on the remote system is a directory. + * That is, it contains entries that can be interpreted as other files. + */ + public boolean isDirectory() { + if ((flags & ATTR_PERMISSIONS) == 0) return false; + return (permissions & S_IFMT) == S_IFDIR; + } + } + + /** + * The following flags are defined for the 'permissions' field: + */ + static final int + S_IFMT = 0170000, // bitmask for the file type bitfields + S_IFSOCK = 0140000, // socket + S_IFLNK = 0120000, // symbolic link + S_IFREG = 0100000, // regular file + S_IFBLK = 0060000, // block device + S_IFDIR = 0040000, // directory + S_IFCHR = 0020000, // character device + S_IFIFO = 0010000, // fifo + S_ISUID = 0004000, // set UID bit + S_ISGID = 0002000, // set GID bit (see below) + S_ISVTX = 0001000, // sticky bit (see below) + S_IRWXU = 00700, // mask for file owner permissions + S_IRUSR = 00400, // owner has read permission + S_IWUSR = 00200, // owner has write permission + S_IXUSR = 00100, // owner has execute permission + S_IRWXG = 00070, // mask for group permissions + S_IRGRP = 00040, // group has read permission + S_IWGRP = 00020, // group has write permission + S_IXGRP = 00010, // group has execute permission + S_IRWXO = 00007, // mask for permissions for others (not in group) + S_IROTH = 00004, // others have read permission + S_IWOTH = 00002, // others have write permisson + S_IXOTH = 00001; // others have execute permission + + final static class DirEntry { + /** + * `filename' is a file name being returned. It is a relative name within + * the directory, without any path components; + */ + public final String filename; + + /** + * `longname' is an expanded format for the file name, similar to what + * is returned by "ls -l" on Unix systems. + * The format of the `longname' field is unspecified by this protocol. + * It MUST be suitable for use in the output of a directory listing + * command (in fact, the recommended operation for a directory listing + * command is to simply display this data). However, clients SHOULD NOT + * attempt to parse the longname field for file attributes; they SHOULD + * use the attrs field instead. + */ + public final String longname; + + /** + * `attrs' is the attributes of the file. + */ + public final FileAttrs attrs; + + public DirEntry(String filename, String longname, FileAttrs attrs) { + this.filename = filename; + this.longname = longname; + this.attrs = attrs; + } + } + + /** + * Opaque representation of open file handle. + * Note: open file handle can be used only with service instance that + * created the handle. + */ + interface IFileHandle { + IFileSystem getService(); + } + + /** + * Status codes. + */ + static final int + + /** + * Indicates successful completion of the operation. + */ + STATUS_OK = 0, + + /** + * Indicates end-of-file condition; for read() it means that no + * more data is available in the file, and for readdir() it + * indicates that no more files are contained in the directory. + */ + STATUS_EOF = 1, + + /** + * This code is returned when a reference is made to a file which + * should exist but doesn't. + */ + STATUS_NO_SUCH_FILE = 2, + + /** + * is returned when the authenticated user does not have sufficient + * permissions to perform the operation. + */ + STATUS_PERMISSION_DENIED = 3, + + /** + * is a generic catch-all error message; it should be returned if an + * error occurs for which there is no more specific error code. + * + */ + STATUS_FAILURE = 4, + + /** + * may be returned if a badly formatted packet or protocol + * incompatibility is detected. + */ + STATUS_BAD_MESSAGE = 5, + + /** + * is a pseudo-error which indicates that the client has no + * connection to the server (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */ + STATUS_NO_CONNECTION = 6, + + /** + * is a pseudo-error which indicates that the connection to the + * server has been lost (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */ + STATUS_CONNECTION_LOST = 7, + + /** + * indicates that an attempt was made to perform an operation which + * is not supported for the server (it may be generated locally by + * the client if e.g. the version number exchange indicates that a + * required feature is not supported by the server, or it may be + * returned by the server if the server does not implement an + * operation). + */ + STATUS_OP_UNSUPPORTED = 8; + + abstract static class FileSystemException extends IOException { + + protected FileSystemException(String message) { + super(message); + } + + protected FileSystemException(Exception x) { + super(x.getMessage()); + initCause(x); + } + + public abstract int getStatus(); + } + + /** + * Open or create a file on a remote system. + * + * @param file_name specifies the file name. See 'File Names' for more information. + * @param flags is a bit mask of O_* flags. + * @param attrs specifies the initial attributes for the file. + * Default values will be used for those attributes that are not specified. + * @param done is call back object. + * @return pending command handle. + */ + IToken open(String file_name, int flags, FileAttrs attrs, DoneOpen done); + + interface DoneOpen { + void doneOpen(IToken token, FileSystemException error, IFileHandle handle); + } + + /** + * Close a file on a remote system. + * + * @param handle is a handle previously returned in the response to + * open() or opendir(). + * @param done is call back object. + * @return pending command handle. + */ + IToken close(IFileHandle handle, DoneClose done); + + interface DoneClose { + void doneClose(IToken token, FileSystemException error); + } + + /** + * Read bytes from an open file. + * In response to this request, the server will read as many bytes as it + * can from the file (up to `len'), and return them in a byte array. + * If an error occurs or EOF is encountered, the server may return + * fewer bytes then requested. Call back method doneRead() argument 'error' + * will be not null in case of error, and argument 'eof' will be + * true in case of EOF. For normal disk files, it is guaranteed + * that this will read the specified number of bytes, or up to end of file + * or error. For e.g. device files this may return fewer bytes than requested. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start reading. + * @param len is the maximum number of bytes to read. + * @param done is call back object. + * @return pending command handle. + */ + IToken read(IFileHandle handle, long offset, int len, DoneRead done); + + interface DoneRead { + void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof); + } + + /** + * Write bytes into an open file. + * The write will extend the file if writing beyond the end of the file. + * It is legal to write way beyond the end of the file; the semantics + * are to write zeroes from the end of the file to the specified offset + * and then the data. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start writing. + * @param data is byte array that contains data for writing. + * @param data_pos if offset in 'data' of first byte to write. + * @param data_size is the number of bytes to write. + * @param done is call back object. + * @return pending command handle. + */ + IToken write(IFileHandle handle, long offset, + byte[] data, int data_pos, int data_size, DoneWrite done); + + interface DoneWrite { + void doneWrite(IToken token, FileSystemException error); + } + + /** + * Retrieve file attributes. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */ + IToken stat(String path, DoneStat done); + + /** + * Retrieve file attributes. + * Unlike 'stat()', 'lstat()' does not follow symbolic links. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */ + IToken lstat(String path, DoneStat done); + + /** + * Retrieve file attributes for an open file (identified by the file handle). + * + * @param handle is a file handle returned by 'open()'. + * @param done is call back object. + * @return pending command handle. + */ + IToken fstat(IFileHandle handle, DoneStat done); + + interface DoneStat { + void doneStat(IToken token, FileSystemException error, FileAttrs attrs); + } + + /** + * Set file attributes. + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * An error will be returned if the specified file system object does + * not exist or the user does not have sufficient rights to modify the + * specified attributes. + * + * @param path specifies the file system object (e.g. file or directory) + * whose attributes are to be modified. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */ + IToken setstat(String path, FileAttrs attrs, DoneSetStat done); + + /** + * Set file attributes for an open file (identified by the file handle). + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * + * @param handle is a file handle returned by 'open()'. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */ + IToken fsetstat(IFileHandle handle, FileAttrs attrs, DoneSetStat done); + + interface DoneSetStat { + void doneSetStat(IToken token, FileSystemException error); + } + + /** + * The opendir() command opens a directory for reading. + * Once the directory has been successfully opened, files (and + * directories) contained in it can be listed using readdir() requests. + * When the client no longer wishes to read more names from the + * directory, it SHOULD call close() for the handle. The handle + * should be closed regardless of whether an error has occurred or not. + + * @param path - name of the directory to be listed (without any trailing slash). + * @param done - result call back object. + * @return pending command handle. + */ + IToken opendir(String path, DoneOpen done); + + /** + * The files in a directory can be listed using the opendir() and + * readdir() requests. Each readdir() request returns one + * or more file names with full file attributes for each file. The + * client should call readdir() repeatedly until it has found the + * file it is looking for or until the server responds with a + * message indicating an error or end of file. The client should then + * close the handle using the close() request. + * Note: directory entries "." and ".." are NOT included into readdir() + * response. + * @param handle - file handle created by opendir() + * @param done - result call back object. + * @return pending command handle. + */ + IToken readdir(IFileHandle handle, DoneReadDir done); + + interface DoneReadDir { + void doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof); + } + + /** + * Create a directory on the server. + * + * @param path - specifies the directory to be created. + * @param attrs - new directory attributes. + * @param done - result call back object. + * @return pending command handle. + */ + IToken mkdir(String path, FileAttrs attrs, DoneMkDir done); + + interface DoneMkDir { + void doneMkDir(IToken token, FileSystemException error); + } + + /** + * Remove a directory. + * An error will be returned if no directory + * with the specified path exists, or if the specified directory is not + * empty, or if the path specified a file system object other than a + * directory. + * + * @param path - specifies the directory to be removed. + * @param done - result call back object. + * @return pending command handle. + */ + IToken rmdir(String path, DoneRemove done); + + interface DoneRemove { + void doneRemove(IToken token, FileSystemException error); + } + + /** + * Retrieve file system roots - top level file system objects. + * UNIX file system can report just one root with path "/". Other types of systems + * can have more the one root. For example, Windows server can return multiple roots: + * one per disc (e.g. "/C:/", "/D:/", etc.). Note: even Windows implementation of + * the service must use forward slash as directory separator, and must start + * absolute path with "/". Server should implement proper translation of + * protocol file names to OS native names and back. + * + * @param done - result call back object. + * @return pending command handle. + */ + IToken roots(DoneRoots done); + + interface DoneRoots { + void doneRoots(IToken token, FileSystemException error, DirEntry[] entries); + } + + /** + * Remove a file or symbolic link. + * This request cannot be used to remove directories. + * + * @param file_name is the name of the file to be removed. + * @param done - result call back object. + * @return pending command handle. + */ + IToken remove(String file_name, DoneRemove done); + + /** + * Canonicalize any given path name to an absolute path. + * This is useful for converting path names containing ".." components or + * relative pathnames without a leading slash into absolute paths. + * + * @param path specifies the path name to be canonicalized. + * @param done - result call back object. + * @return pending command handle. + */ + IToken realpath(String path, DoneRealPath done); + + interface DoneRealPath { + void doneRealPath(IToken token, FileSystemException error, String path); + } + + /** + * Rename a file. + * It is an error if there already exists a file + * with the name specified by 'new_path'. The server may also fail rename + * requests in other situations, for example if `old_path' and `new_path' + * point to different file systems on the server. + * + * @param old_path is the name of an existing file or directory. + * @param new_path is the new name for the file or directory. + * @param done - result call back object. + * @return pending command handle. + */ + IToken rename(String old_path, String new_path, DoneRename done); + + interface DoneRename { + void doneRename(IToken token, FileSystemException error); + } + + /** + * Read the target of a symbolic link. + * + * @param path specifies the path name of the symbolic link to be read. + * @param done - result call back object. + * @return pending command handle. + */ + IToken readlink(String path, DoneReadLink done); + + interface DoneReadLink { + void doneReadLink(IToken token, FileSystemException error, String path); + } + + /** + * Create a symbolic link on the server. + * + * @param link_path specifies the path name of the symbolic link to be created. + * @param target_path specifies the target of the symbolic link. + * @param done - result call back object. + * @return pending command handle. + */ + IToken symlink(String link_path, String target_path, DoneSymLink done); + + interface DoneSymLink { + void doneSymLink(IToken token, FileSystemException error); + } + + /** + * Copy a file on remote system. + * + * @param src_path specifies the path name of the file to be copied. + * @param dst_path specifies destination file name. + * @param copy_permissions - if true then copy source file permissions. + * @param copy_ownership - if true then copy source file UID and GID. + * @param done - result call back object. + * @return pending command handle. + */ + IToken copy(String src_path, String dst_path, + boolean copy_permissions, boolean copy_ownership, DoneCopy done); + + interface DoneCopy { + void doneCopy(IToken token, FileSystemException error); + } + + /** + * Retrieve information about user account, which is used by server + * to access file system on behalf of the client. + * + * @param done - result call back object. + * @return pending command handle. + */ + IToken user(DoneUser done); + + interface DoneUser { + void doneUser(IToken token, FileSystemException error, + int real_uid, int effective_uid, int real_gid, int effective_gid, + String home); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java new file mode 100644 index 000000000..2547e6f1e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * Line numbers service associates locations in the source files with the corresponding + * machine instruction addresses in the executable object. + */ +public interface ILineNumbers extends IService { + + static final String NAME = "LineNumbers"; + + /** + * TextArea represent a continues area in source text mapped to + * continues range of code addresses. + * Line and columns are counted starting from 0. + * File name can be relative path, in such case client should + * use TextArea directory name as origin for the path. + * File and directory names are valid on a host where code was compiled. + * It is client responsibility to map names to this host file system. + */ + final class CodeArea { + public final String directory; + public final String file; + public final int start_line; + public final int start_column; + public final int end_line; + public final int end_column; + public final Number start_address; + public final Number end_address; + public final int isa; + public final boolean is_statement; + public final boolean basic_block; + public final boolean prologue_end; + public final boolean epilogue_begin; + + public CodeArea(String directory, String file, int start_line, int start_column, + int end_line, int end_column, Number start_address, Number end_address, int isa, + boolean is_statement, boolean basic_block, + boolean prologue_end, boolean epilogue_begin) { + this.directory = directory; + this.file = file; + this.start_line = start_line; + this.start_column = start_column; + this.end_line = end_line; + this.end_column = end_column; + this.start_address = start_address; + this.end_address = end_address; + this.isa = isa; + this.is_statement = is_statement; + this.basic_block = basic_block; + this.prologue_end = prologue_end; + this.epilogue_begin = epilogue_begin; + } + } + + IToken mapToSource(String context_id, Number start_address, Number end_address, DoneMapToSource done); + + interface DoneMapToSource { + void doneMapToSource(IToken token, Exception error, CodeArea[] areas); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java new file mode 100644 index 000000000..9116d9d7f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * ILocator service uses transport layer to search for peers and to collect data about + * peer’s attributes and capabilities (services). Discovery mechanism depends on transport protocol + * and is part of that protocol handler. Targets, known to other hosts, can be found through + * remote instances of ILocator service. Automatically discovered targets require no further + * configuration. Additional targets can be configured manually. + * + * Clients should use Protocol.getLocator() to obtain local instance of ILocator, + * then ILocator.getPeers() can be used to get list of available peers (hosts and targets). + */ + +public interface ILocator extends IService { + + static final String NAME = "Locator"; + + /** + * Auto-configuration command and response codes. + */ + static final int + CONF_REQ_INFO = 1, + CONF_PEER_INFO = 2; + + /** + * @return Locator service name: "Locator" + */ + String getName(); + + /** + * Get map (ID -> IPeer) of available peers (hosts and targets). + * The method return cached (currently known to the framework) list of peers. + * The list is updated according to event received from transport layer + */ + Map<String,IPeer> getPeers(); + + /** + * Redirect this service channel to given peer using this service as a proxy. + * @param peer_id - Peer ID. + */ + IToken redirect(String peer_id, DoneRedirect done); + + interface DoneRedirect { + void doneRedirect(IToken token, Exception error); + } + + /** + * Call back after TCF messages sent to this target up to this moment are delivered. + * This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + IToken sync(DoneSync done); + + interface DoneSync { + void doneSync(IToken token); + } + + /** + * Add a listener for ILocator service events. + */ + void addListener(LocatorListener listener); + + /** + * Remove a listener for ILocator service events. + */ + void removeListener(LocatorListener listener); + + interface LocatorListener { + void peerAdded(IPeer peer); + + void peerChanged(IPeer peer); + + void peerRemoved(String id); + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java new file mode 100644 index 000000000..0f01c4a95 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IMemory service provides basic operations to read/write memory on a target. + */ +public interface IMemory extends IService { + + static final String NAME = "Memory"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_BIG_ENDIAN = "BigEndian", + PROP_ADDRESS_SIZE = "AddressSize"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, MemoryContext context); + } + + /** + * Retrieve contexts available for memory commands. + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Memory access mode: + * Carry on when some of the memory cannot be accessed and + * return MemoryError at the end if any of the bytes + * were not processed correctly. + */ + final static int MODE_CONTINUEONERROR = 0x1; + + /** + * Memory access mode: + * Verify result of memory operations (by reading and comparing). + */ + final static int MODE_VERIFY = 0x2; + + interface MemoryContext { + + /** + * Retrieve context ID. + * Same as (String)getProperties().get(“ID”) + */ + String getID(); + + /** + * Retrieve parent context ID. + * Same as (String)getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Retrieves process ID, if applicable. + * @return process system ID. + */ + int getProcessID(); + + /** + * Retrieve memory endianess. + * @return true if memory id big-endian. + */ + boolean isBigEndian(); + + /** + * Retrieve memory address size. + * @return number of bytes used to store memory address value. + */ + int getAddressSize(); + + /** + * Retrieve context properties. + */ + Map<String,Object> getProperties(); + + /** + * Set target memory. + * If 'word_size' is 0 it means client does not care about word size. + */ + IToken set(Number addr, int word_size, byte[] buf, + int offs, int size, int mode, DoneMemory done); + + /** + * Read target memory. + */ + IToken get(Number addr, int word_size, byte[] buf, + int offs, int size, int mode, DoneMemory done); + + /** + * Fill target memory with given pattern. + * 'size' is number of bytes to fill. + */ + IToken fill(Number addr, int word_size, byte[] value, + int size, int mode, DoneMemory done); + } + + /** + * Client call back interface for set(), get() and fill() commands. + */ + interface DoneMemory { + public void doneMemory(IToken token, MemoryError error); + } + + class MemoryError extends Exception { + + private static final long serialVersionUID = 1L; + + public MemoryError(String msg) { + super(msg); + } + } + + /** + * ErrorOffset interface can be implemented by MemoryError object, + * which is returned by get, set and fill commands. + * + * get/set/fill () returns this exception when reading failed + * for some but not all bytes, and MODE_CONTINUEONERROR + * has been set in mode. (For example, when only part of the request + * translates to valid memory addresses.) + * Exception.getMessage can be used for generalized message of the + * possible reasons of partial memory operation. + */ + interface ErrorOffset { + + // Error may have per byte information + final static int + BYTE_VALID = 0x00, + BYTE_UNKNOWN = 0x01, // e.g. out of range + BYTE_INVALID = 0x02, + BYTE_CANNOT_READ = 0x04, + BYTE_CANNOT_WRITE = 0x08; + + int getStatus(int offset); + + /** + * Returns the detail message string about the + * byte associated with specified location. + * @return the detail error message string. + */ + String getMessage(int offset); + + } + + /** + * Add memory service event listener. + * @param listener - event listener implementation. + */ + void addListener(MemoryListener listener); + + /** + * Remove memory service event listener. + * @param listener - event listener implementation. + */ + void removeListener(MemoryListener listener); + + /** + * Memory event listener is notified when memory context hierarchy + * changes, and when memory is modified by memory service commands. + */ + interface MemoryListener { + + /** + * Called when a new memory access context(s) is created. + */ + void contextAdded(MemoryContext[] contexts); + + /** + * Called when a memory access context(s) properties changed. + */ + void contextChanged(MemoryContext[] contexts); + + /** + * Called when memory access context(s) is removed. + */ + void contextRemoved(String[] context_ids); + + /** + * Called when target memory content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached memory data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + * ‘addr’ and ‘size’ can be null if unknown. + */ + void memoryChanged(String context_id, Number[] addr, long[] size); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java new file mode 100644 index 000000000..531e8e57a --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IProcesses service provides access to the target OS's process + * information, allows to start and terminate a process, and allows + * to attach and detach a process for debugging. Debug services, + * like IMemory and IRunControl, require a process to be attached + * before they can access it. + */ +public interface IProcesses extends IService { + + static final String NAME = "Processes"; + + /** + * Retrieve context info for given context ID. + * A context corresponds to an execution thread, process, address space, etc. + * Context IDs are valid across TCF services, so it is allowed to issue + * 'IProcesses.getContext' command with a context that was obtained, + * for example, from Memory service. + * However, 'Processes.getContext' is supposed to return only process specific data, + * If the ID is not a process ID, 'IProcesses.getContext' may not return any + * useful information + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, ProcessContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, boolean attached_only, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Context property names. + */ + static final String + /** The TCF context ID */ + PROP_ID = "ID", + + /** The TCF parent context ID */ + PROP_PARENTID = "ParentID", + + /** Is the context attached */ + PROP_ATTACHED = "Attached", + + /** Can terminate the context */ + PROP_CAN_TERMINATE = "CanTerminate", + + /** Process name. Client UI can show this name to a user */ + PROP_NAME = "Name"; + + interface ProcessContext { + + /** + * Get context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get process name. + * Client UI can show this name to a user. + * Same as getProperties().get(“Name”) + */ + String getName(); + + /** + * Utility method to read context property PROP_ATTACHED. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * @return value of PROP_ATTACHED. + */ + boolean isAttached(); + + /** + * Utility method to read context property PROP_CAN_TERMINATE. + * @return value of PROP_CAN_TERMINATE. + */ + boolean canTerminate(); + + /** + * Get all available context properties. + * @return Map 'property name' -> 'property value' + */ + Map<String, Object> getProperties(); + + /** + * Attach debugger to a process. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken attach(DoneCommand done); + + /** + * Detach debugger from a process. + * Process execution will continue without debugger supervision. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken detach(DoneCommand done); + + /** + * Terminate a process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken terminate(DoneCommand done); + + /** + * Send a signal to a process. + * @param signal - signal ID. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken signal(int signal, DoneCommand done); + } + + interface DoneCommand { + void doneCommand(IToken token, Exception error); + } + + /** + * Get default set of environment variables used to start a new process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken getEnvironment(DoneGetEnvironment done); + + interface DoneGetEnvironment { + void doneGetEnvironment(IToken token, Exception error, Map<String,String> environment); + } + + /** + * Start a new process on remote machine. + * @param directory - initial value of working directory for the process. + * @param file - process image file. + * @param command_line - command line arguments for the process. + * @param environment - map of environment variables for the process, + * if null then default set of environment variables will be used. + * @param attach - if true debugger should be attached to the process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken start(String directory, String file, + String[] command_line, Map<String,String> environment, + boolean attach, DoneStart done); + + interface DoneStart { + void doneStart(IToken token, Exception error, ProcessContext process); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java new file mode 100644 index 000000000..2cf4de1f4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java @@ -0,0 +1,318 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IRegisters service provides access to target CPU register values and properties. + */ +public interface IRegisters extends IService { + + static final String NAME = "Registers"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_DESCRIPTION = "Description", + PROP_FORMATS = "Formats", + PROP_READBLE = "Readable", + PROP_READ_ONCE = "ReadOnce", + PROP_WRITEABLE = "Writeable", + PROP_WRITE_ONCE = "WriteOnce", + PROP_SIDE_EFFECTS = "SideEffects", + PROP_VOLATILE = "Volatile", + PROP_FLOAT = "Float", + PROP_BIG_ENDIAN = "BigEndian", + PROP_LEFT_TO_RIGHT = "LeftToRight", + PROP_FIST_BIT = "FirstBit", + PROP_BITS = "Bits", + PROP_VALUES = "Values"; + + /** + * Standard known formats for register data. + */ + static final String + FORMAT_BINARY = "Binary", + FORMAT_OCTAL = "Octal", + FORMAT_DECIMAL = "Decimal", + FORMAT_HEX = "Hex", + FORMAT_NATURAL = "Natural"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, RegistersContext context); + } + + /** + * Retrieve contexts available for registers commands. + * A context corresponds to an execution thread, stack frame, registers group, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * RegistersContext objects represent register groups, registers and bit fields. + */ + interface RegistersContext { + /** + * Get Context ID. + * @return context ID. + */ + String getID(); + + /** + * Get parent context ID. + * @return parent context ID. + */ + String getParentID(); + + /** + * Get context (register, register group, bit field) name. + * @return context name. + */ + String getName(); + + /** + * Get context description. + * @return context description. + */ + String getDescription(); + + /** + * Get value formats available for register get/set commands. + * See FORMAT_* for knows format IDs definition. + * @return array of supported format IDs. + */ + String[] getAvailableFormats(); + + /** + * Check if context value can be read. + * @return true if can read value of the context. + */ + boolean isReadable(); + + /** + * Check if reading the context (register) destroys its current value - + * it can be read only once. + * @return true if read-once register. + */ + boolean isReadOnce(); + + /** + * Check if context value can be written. + * @return true if can write value of the context. + */ + boolean isWriteable(); + + /** + * Check if register value can not be overwritten - every write counts. + * @return true if write-once register. + */ + boolean isWriteOnce(); + + /** + * Check if writing the context can change values of other registers. + * @return true if has side effects. + */ + boolean hasSideEffects(); + + /** + * Check if the register value can change even when target is stopped. + * @return true if the register value can change at any time. + */ + boolean isVolatile(); + + /** + * Check if the register value is a floating-point value. + * @return true if a floating-point register. + */ + boolean isFloat(); + + /** + * Check endianess of the context. + * Big endian means decreasing numeric significance with increasing bit number. + * @return true if big endian. + */ + boolean isBigEndian(); + + /** + * Check if the lowest numbered bit (i.e. bit #0 or bit #1 depending on + * getFirstBitNumber() value) should be shown to user as the left-most bit or + * the right-most bit. + * @return true if the first bit is left-most bit. + */ + boolean isLeftToRight(); + + /** + * If the context has bit field children, bit positions of the fields + * can be zero-based or 1-based. + * @return first bit position - 0 or 1. + */ + int getFirstBitNumber(); + + /** + * If context is a bit field, get the field bit numbers in parent context. + * @return array of bit numbers. + */ + int[] getBitNumbers(); + + /** + * A context can have predefined names (mnemonics) for some its values. + * This method returns a list of such named values. + * @return array of named values or null. + */ + NamedValue[] getNamedValues(); + + /** + * Get complete map of context properties. + * @return map of context properties. + */ + Map<String,Object> getProperties(); + + /** + * Read value of the context. + * @param format - ID of a format to use for result value. + * @param done - call back object. + * @return - pending command handle. + */ + IToken get(String format, DoneGet done); + + /** + * Set value of the context. + * @param format - ID of a format used for value. + * @param value - value to write into the context. + * @param done - call back object. + * @return - pending command handle. + */ + IToken set(String format, String value, DoneSet done); + } + + /** + * A register context can have predefined names (mnemonics) for some its values. + * NamedValue objects represent such values. + */ + interface NamedValue { + /** + * Get number associated with this named value. + * @return the value as a number. + */ + Number getValue(); + + /** + * Get name (mnemonic) of the value. + * @return value name. + */ + String getName(); + + /** + * Get human readable description of the value. + * @return value description. + */ + String getDescription(); + } + + /** + * 'get' command call back interface. + */ + interface DoneGet { + void doneGet(IToken token, Exception error, String value); + } + + /** + * 'set' command call back interface. + */ + interface DoneSet { + void doneSet(IToken token, Exception error); + } + + /** + * Add registers service event listener. + * @param listener - event listener implementation. + */ + void addListener(RegistersListener listener); + + /** + * Remove registers service event listener. + * @param listener - event listener implementation. + */ + void removeListener(RegistersListener listener); + + /** + * Registers event listener is notified when registers context hierarchy + * changes, and when a register is modified by the service commands. + */ + interface RegistersListener { + + /** + * Called when register context properties changed. + * Most targets have static set of registers and register properties. + * Such targets never generate this event. However, some targets, + * for example, JTAG probes, allow user to modify register definitions. + * Clients should flush all cached register context data. + */ + void contextChanged(); + + /** + * Called when register content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached registers data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + */ + void registerChanged(String context_id); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java new file mode 100644 index 000000000..fc8befbcc --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +public interface IRunControl extends IService { + + static final String NAME = "RunControl"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_IS_CONTAINER = "IsContainer", + PROP_HAS_STATE = "HasState", + PROP_CAN_RESUME = "CanResume", + PROP_CAN_COUNT = "CanCount", + PROP_CAN_SUSPEND = "CanSuspend", + PROP_CAN_TERMINATE = "CanTerminate"; + + /** + * Context resume modes. + */ + static final int + RM_RESUME = 0, + RM_STEP_OVER = 1, + RM_STEP_INTO = 2, + RM_STEP_OVER_LINE = 3, + RM_STEP_INTO_LINE = 4, + RM_STEP_OUT = 5; + + /** + * State change reason of a context. + * Reason can be any text, but if it is one of predefined strings, + * a generic client might be able to handle it better. + */ + static final String + REASON_USER_REQUEST = "Suspended", + REASON_STEP = "Step", + REASON_BREAKPOINT = "Breakpoint", + REASON_EXCEPTION = "Exception", + REASON_CONTAINER = "Container", + REASON_WATCHPOINT = "Watchpoint", + REASON_SIGNAL = "Signal", + REASON_SHAREDLIB = "Shared Library", + REASON_ERROR = "Error"; + + /** + * Retrieve context properties for given context ID. + * + * @param id – context ID. + * @param done - callback interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client callback interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, RunControlContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - callback interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client callback interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */ + interface RunControlContext { + + /** + * Retrieve context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Retrieve parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get context properties. See PROP_* definitions for property names. + * Context properties are read only, clients should not try to modify them. + * @return Map of context properties. + */ + Map<String,Object> getProperties(); + + /** + * Utility method to read context property PROP_IS_CONTAINER. + * Executing resume or suspend command on a container causes all its children to resume or suspend. + * @return value of PROP_IS_CONTAINER. + */ + boolean isContainer(); + + /** + * Utility method to read context property PROP_HAS_STATE. + * Only context that has a state can be resumed or suspended. + * @return value of PROP_HAS_STATE. + */ + boolean hasState(); + + /** + * Utility method to read context property PROP_CAN_SUSPEND. + * Value 'true' means suspend command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already suspended. + * @return value of PROP_CAN_SUSPEND. + */ + boolean canSuspend(); + + /** + * Utility method to read a 'mode' bit in context property PROP_CAN_RESUME. + * Value 'true' means resume command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * @param mode - resume mode, see RM_*. + * @return value of requested bit of PROP_CAN_RESUME. + */ + boolean canResume(int mode); + + /** + * Utility method to read a 'mode' bit in context property PROP_CAN_COUNT. + * Value 'true' means resume command with count other then 1 is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * @param mode - resume mode, see RM_*. + * @return value of requested bit of PROP_CAN_COUNT. + */ + boolean canCount(int mode); + + /** + * Utility method to read context property PROP_CAN_TERMINATE. + * Value 'true' means terminate command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already exited. + * @return value of PROP_CAN_SUSPEND. + */ + boolean canTerminate(); + + /** + * Send a command to retrieve current state of a context. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken getState(DoneGetState done); + + /** + * Send a command to suspend a context. + * Also suspends children if context is a container. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken suspend(DoneCommand done); + + /** + * Send a command to resume a context. + * Also resumes children if context is a container. + * @param mode - defines how to resume the context, see RM_*. + * @param count - if mode implies stepping, defines how many steps to perform. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken resume(int mode, int count, DoneCommand done); + + /** + * Send a command to terminate a context. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken terminate(DoneCommand done); + } + + class RunControlError extends Exception { + + private static final long serialVersionUID = 1L; + } + + interface DoneGetState { + void doneGetState(IToken token, Exception error, boolean suspended, String pc, + String reason, Map<String,Object> params); + } + + interface DoneCommand { + /** + * Called when run control command execution is complete. + * @param token - pending command handle. + * @param error - command execution error or null. + */ + void doneCommand(IToken token, Exception error); + } + + /** + * Add run control event listener. + * @param listener - run control event listener to add. + */ + void addListener(RunControlListener listener); + + /** + * Remove run control event listener. + * @param listener - run control event listener to remove. + */ + void removeListener(RunControlListener listener); + + /** + * Service events listener interface. + */ + interface RunControlListener { + + /** + * Called when new contexts are created. + * @param contexts - array of new context properties. + */ + void contextAdded(RunControlContext contexts[]); + + /** + * Called when a context properties changed. + * @param contexts - array of new context properties. + */ + void contextChanged(RunControlContext contexts[]); + + /** + * Called when contexts are removed. + * @param context_ids - aray of removed context IDs. + */ + void contextRemoved(String context_ids[]); + + /** + * Called when a thread is suspended. + * @param context - ID of a context that was suspended. + * @param pc - program counter of the context, can be null. + * @param reason - human readable description of suspend reason. + * @param params - additional, target specific data about suspended context. + */ + void contextSuspended(String context, String pc, + String reason, Map<String,Object> params); + + /** + * Called when a thread is resumed. + * @param context - ID of a context that was resumed. + */ + void contextResumed(String context); + + /** + * Called when target simultaneously suspends multiple threads in a container + * (process, core, etc.). + * + * @param context - ID of a context responsible for the event. It can be container ID or + * any one of container children, for example, it can be thread that hit "suspend all" breakpoint. + * Client expected to move focus (selection) to this context. + * @param pc - program counter of the context. + * @param reason - human readable description of suspend reason. + * @param params - additional target specific data about suspended context. + * @param suspended_ids - full list of all contexts that were suspended. + */ + void containerSuspended(String context, String pc, + String reason, Map<String,Object> params, String[] suspended_ids); + + /** + * Called when target simultaneously resumes multiple threads in a container (process, + * core, etc.). + * + * @param context_ids - full list of all contexts that were resumed. + */ + void containerResumed(String[] context_ids); + + /** + * Called when an exception is detected in a target thread. + * @param context - ID of a context that caused an exception. + * @param msg - human readable description of the exception. + */ + void contextException(String context, String msg); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java new file mode 100644 index 000000000..a3657bcab --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +public interface IStackTrace extends IService { + + static final String NAME = "StackTrace"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_FRAME_ADDRESS = "FP", + PROP_RETURN_ADDRESS = "RP", + PROP_ARGUMENTS_COUNT = "ArgsCnt", + PROP_ARGUMENTS_ADDRESS = "ArgsAddr"; + + /** + * Retrieve context info for given context IDs. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * @param id – array of context IDs. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String[] id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – array of context data or null if error. + */ + void doneGetContext(IToken token, Exception error, StackTraceContext[] context); + } + + /** + * Retrieve stack trace context list. + * Parent context usually corresponds to an execution thread. + * Some targets have more then one stack. In such case children of a thread + * are stacks, and stack frames are deeper in the hierarchy - they can be + * retrieved with additional getChildren commands. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * @param parent_context_id – parent context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + * Stack frames are ordered from stack bottom to top. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * StackTraceContext represents stack trace objects - stacks and stack frames. + */ + interface StackTraceContext { + + /** + * Get Context ID. + * @return context ID. + */ + String getID(); + + /** + * Get parent context ID. + * @return parent context ID. + */ + String getParentID(); + + /** + * Get context name - if context represents a stack. + * @return context name or null. + */ + String getName(); + + /** + * Get memory address of this frame. + * @return address or null if not a stack frame. + */ + Number getFrameAddress(); + + /** + * Get program counter saved in this stack frame - + * it is address of instruction to be executed when the function returns. + * @return return address or null if not a stack frame. + */ + Number getReturnAddress(); + + /** + * Get number of function arguments for this frame. + * @return function arguments count. + */ + int getArgumentsCount(); + + /** + * Get address of function arguments area in memory. + * @return function arguments address or null if not available. + */ + Number getArgumentsAddress(); + + /** + * Get complete map of context properties. + * @return map of context properties. + */ + Map<String,Object> getProperties(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java new file mode 100644 index 000000000..f154c9c0e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java @@ -0,0 +1,378 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * This is optional service that can be implemented by a peer. + * If implemented, the service can be used for monitoring system activity and utilization. + * It provides list of running processes, different process attributes like command line, environment, etc., + * and some resource utilization data. The service can be used by a client to provide functionality + * similar to Unix 'top' utility or Windows 'Task Manager'. + */ +public interface ISysMonitor extends IService { + + static final String NAME = "SysMonitor"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - callback interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client callback interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, SysMonitorContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - callback interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client callback interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Context property names. + */ + static final String + /** The TCF context ID */ + PROP_ID = "ID", + + /** The TCF parent context ID */ + PROP_PARENTID = "ParentID", + + /** Current working directory of the process */ + PROP_CWD = "CWD", + + /** The process's root directory (as set by chroot) */ + PROP_ROOT = "Root", + + /** User ID of the process owner */ + PROP_UID = "UID", + + /** Group ID of the process owner */ + PROP_UGID = "UGID", + + /** User name of the process owner */ + PROP_USERNAME = "UserName", + + /** Group name of the process owner */ + PROP_GROUPNAME = "GroupName", + + /** System process ID */ + PROP_PID = "PID", + + /** Executable file of the process */ + PROP_FILE = "File", + + /** One character from the string "RSDZTW" where R is running, S is + * sleeping in an interruptible wait, D is waiting in uninterruptible + * disk sleep, Z is zombie, T is traced or stopped (on a signal), and W + * is paging.*/ + PROP_STATE = "State", + + /** System ID of the parent process */ + PROP_PPID = "PPID", + + /** The process group ID of the process */ + PROP_PGRP = "PGRP", + + /** The session ID of the process */ + PROP_SESSION = "Session", + + /** The tty the process uses */ + PROP_TTY = "TTY", + + /** The process group ID of the process which currently owns the tty that + * the process is connected to. */ + PROP_TGID = "TGID", + + /** ID of a process that has attached this process for tracing or debugging */ + PROP_TRACERPID = "TracerPID", + + /** The kernel flags word of the process. Details depend on the kernel */ + PROP_FLAGS = "Flags", + + /** The number of minor faults the process has made which have not + * required loading a memory page from disk */ + PROP_MINFLT = "MinFlt", + + /** The number of minor faults that the process's waited-for children have made */ + PROP_CMINFLT = "CMinFlt", + + /** The number of major faults the process has made which have required + * loading a memory page from disk */ + PROP_MAJFLT = "MajFlt", + + /** The number of major faults that the process's waited-for children + * have made */ + PROP_CMAJFLT = "CMajFlt", + + /** The number of milliseconds that this process has been scheduled in user mode */ + PROP_UTIME = "UTime", + + /** The number of milliseconds that this process has been scheduled in kernel mode */ + PROP_STIME = "STime", + + /** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */ + PROP_CUTIME = "CUTime", + + /** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */ + PROP_CSTIME = "CSTime", + + /** The standard nice value */ + PROP_PRIORITY = "Priority", + + /** The nice value */ + PROP_NICE = "Nice", + + /** The time in milliseconds before the next SIGALRM is sent to the process + * due to an interval timer */ + PROP_ITREALVALUE = "ITRealValue", + + /** The time in milliseconds the process started after system boot */ + PROP_STARTTIME = "StartTime", + + /** Virtual memory size in bytes */ + PROP_VSIZE = "VSize", + + /** Memory pages size in bytes */ + PROP_PSIZE = "PSize", + + /** Resident Set Size: number of pages the process has in real memory, + * minus used for administrative purposes. This is just the pages which + * count towards text, data, or stack space. This does not include + * pages which have not been demand-loaded in, or which are swapped out */ + PROP_RSS = "RSS", + + /** Current limit in bytes on the rss of the process */ + PROP_RLIMIT = "RLimit", + + /** The address above which program text can run */ + PROP_CODESTART = "CodeStart", + + /** The address below which program text can run */ + PROP_CODEEND = "CodeEnd", + + /** The address of the start of the stack */ + PROP_STACKSTART = "StackStart", + + /** The bitmap of pending signals */ + PROP_SIGNALS = "Signals", + + /** The bitmap of blocked signals */ + PROP_SIGBLOCK = "SigBlock", + + /** The bitmap of ignored signals */ + PROP_SIGIGNORE = "SigIgnore", + + /** The bitmap of caught signals */ + PROP_SIGCATCH = "SigCatch", + + /** This is the "channel" in which the process is waiting. It is the + * address of a system call, and can be looked up in a name list if you + * need a textual name */ + PROP_WCHAN = "WChan", + + /** Number of pages swapped */ + PROP_NSWAP = "NSwap", + + /** Cumulative NSwap for child processes */ + PROP_CNSWAP = "CNSwap", + + /** Signal to be sent to parent when this process exits */ + PROP_EXITSIGNAL = "ExitSignal", + + /** CPU number last executed on */ + PROP_PROCESSOR = "Processor", + + /** Real-time scheduling priority */ + PROP_RTPRIORITY = "RTPriority", + + /** Scheduling policy */ + PROP_POLICY = "Policy"; + + + /** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */ + interface SysMonitorContext { + + /** + * Get context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get process group ID. + * Same as getProperties().get(“PGRP”) + */ + long getPGRP(); + + /** + * Get process ID. + * Same as getProperties().get(“PID”) + */ + long getPID(); + + /** + * Get process parent ID. + * Same as getProperties().get(“PPID”) + */ + long getPPID(); + + /** + * Get process TTY group ID. + * Same as getProperties().get(“TGID”) + */ + long getTGID(); + + /** + * Get tracer process ID. + * Same as getProperties().get(“TracerPID”) + */ + long getTracerPID(); + + /** + * Get process owner user ID. + * Same as getProperties().get(“UID”) + */ + long getUID(); + + /** + * Get process owner user name. + * Same as getProperties().get(“UserName”) + */ + String getUserName(); + + /** + * Get process owner user group ID. + * Same as getProperties().get(“UGID”) + */ + long getUGID(); + + /** + * Get process owner user group name. + * Same as getProperties().get(“GroupName”) + */ + String getGroupName(); + + /** + * Get process state. + * Same as getProperties().get(“State”) + */ + String getState(); + + /** + * Get process virtual memory size in bytes. + * Same as getProperties().get(“VSize”) + */ + long getVSize(); + + /** + * Get process virtual memory page size in bytes. + * Same as getProperties().get(“PSize”) + */ + long getPSize(); + + /** + * Get number of memory pages in process resident set. + * Same as getProperties().get(“RSS”) + */ + long getRSS(); + + /** + * Get context executable file. + * Same as getProperties().get(“File”) + */ + String getFile(); + + /** + * Get context current file system root. + * Same as getProperties().get(“Root”) + */ + String getRoot(); + + /** + * Get context current working directory. + * Same as getProperties().get(“CWD”) + */ + String getCurrentWorkingDirectory(); + + /** + * Get all available context properties. + * @return Map 'property name' -> 'property value' + */ + Map<String,Object> getProperties(); + } + + /** + * Get context command line. + */ + IToken getCommandLine(String id, DoneGetCommandLine done); + + interface DoneGetCommandLine { + void doneGetCommandLine(IToken token, Exception error, String[] cmd_line); + } + + /** + * Get context environment variables. + */ + IToken getEnvironment(String id, DoneGetEnvironment done); + + interface DoneGetEnvironment { + void doneGetEnvironment(IToken token, Exception error, String[] environment); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java new file mode 100644 index 000000000..da81ce5e0 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java @@ -0,0 +1,246 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.LinkedList; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; + +/** + * TCFFileInputStream is high performance InputStream implementation over TCF FileSystem service. + * The class uses read-ahead buffers to achieve maximum throughput. + */ +public final class TCFFileInputStream extends InputStream { + + private static final int BUF_SIZE = 0x1000; + private static final int MAX_READ_AHEAD = 8; + + private static class Buffer { + + final long seq; + + IToken token; + byte[] buf; + boolean eof; + + Buffer(long seq) { + this.seq = seq; + } + } + + private final IFileHandle handle; + private long mark = 0; + private Buffer buf; + private int buf_pos = 0; + private long buf_seq = 0; + private boolean closed = false; + + private boolean suspend_read_ahead; + private Runnable waiting_client; + private final LinkedList<Buffer> read_ahead_buffers = new LinkedList<Buffer>(); + + public TCFFileInputStream(IFileHandle handle) { + this.handle = handle; + } + + private void startReadAhead(Buffer prv) { + if (prv.eof) return; + if (suspend_read_ahead) return; + if (read_ahead_buffers.size() > 0) { + prv = read_ahead_buffers.getLast(); + if (prv.eof) return; + } + long seq = prv.seq + 1; + while (read_ahead_buffers.size() < MAX_READ_AHEAD) { + final Buffer buf = new Buffer(seq); + IFileSystem fs = handle.getService(); + buf.token = fs.read(handle, buf.seq * BUF_SIZE, BUF_SIZE, new IFileSystem.DoneRead() { + public void doneRead(IToken token, FileSystemException error, + byte[] data, boolean eof) { + assert buf.token == token; + assert read_ahead_buffers.contains(buf); + buf.token = null; + if (error == null) { + assert data != null && data.length <= BUF_SIZE; + assert eof || data.length == BUF_SIZE; + buf.buf = data; + buf.eof = eof; + startReadAhead(buf); + } + else { + suspend_read_ahead = true; + read_ahead_buffers.remove(buf); + } + if (waiting_client != null) { + Protocol.invokeLater(waiting_client); + waiting_client = null; + } + } + }); + read_ahead_buffers.add(buf); + seq++; + } + } + + private boolean stopReadAhead(Runnable done) { + suspend_read_ahead = true; + for (Iterator<Buffer> i = read_ahead_buffers.iterator(); i.hasNext();) { + Buffer buf = i.next(); + if (buf.token == null || buf.token.cancel()) i.remove(); + } + if (read_ahead_buffers.size() > 0) { + assert waiting_client == null; + waiting_client = done; + return false; + } + return true; + } + + @Override + public synchronized int read() throws IOException { + if (closed) throw new IOException("Stream is closed"); + while (buf == null || buf_pos >= buf.buf.length) { + if (buf != null && buf.eof) return -1; + long offset = buf_seq * BUF_SIZE + buf_pos; + buf_seq = offset / BUF_SIZE; + buf_pos = (int)(offset % BUF_SIZE); + buf = new TCFTask<Buffer>() { + public void run() { + assert waiting_client == null; + while (read_ahead_buffers.size() > 0) { + Buffer buf = read_ahead_buffers.getFirst(); + if (buf.seq == buf_seq) { + if (buf.token != null) { + waiting_client = this; + } + else { + read_ahead_buffers.remove(buf); + startReadAhead(buf); + done(buf); + } + return; + } + suspend_read_ahead = true; + if (buf.token != null && buf.token.cancel()) buf.token = null; + if (buf.token != null) { + waiting_client = this; + return; + } + read_ahead_buffers.remove(buf); + } + IFileSystem fs = handle.getService(); + fs.read(handle, buf_seq * BUF_SIZE, BUF_SIZE, new IFileSystem.DoneRead() { + public void doneRead(IToken token, FileSystemException error, + byte[] data, boolean eof) { + if (error != null) { + error(error); + return; + } + assert data != null && data.length <= BUF_SIZE; + assert eof || data.length == BUF_SIZE; + Buffer buf = new Buffer(buf_seq); + buf.buf = data; + buf.eof = eof; + if (!eof) { + suspend_read_ahead = false; + startReadAhead(buf); + } + done(buf); + } + }); + } + }.getIO(); + assert buf.token == null; + assert buf.eof || buf.buf.length == BUF_SIZE; + } + assert buf.seq == buf_seq; + return buf.buf[buf_pos++] & 0xff; + } + + @Override + public synchronized int read(final byte arr[], final int off, final int len) throws IOException { + if (closed) throw new IOException("Stream is closed"); + if (arr == null) throw new NullPointerException(); + if (off < 0 || len < 0 || len > arr.length - off) throw new IndexOutOfBoundsException(); + int pos = 0; + while (pos < len) { + if (buf != null && buf_pos < buf.buf.length) { + int buf_len = buf.buf.length; + int n = len - pos < buf_len - buf_pos ? len - pos : buf_len - buf_pos; + System.arraycopy(buf.buf, buf_pos, arr, off + pos, n); + pos += n; + buf_pos += n; + } + else { + int c = read(); + if (c == -1) { + if (pos == 0) return -1; + break; + } + arr[off + pos++] = (byte)c; + } + } + return pos; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void reset() throws IOException { + if (closed) throw new IOException("Stream is closed"); + new TCFTask<Object>() { + public void run() { + if (!stopReadAhead(this)) return; + done(this); + } + }.getIO(); + buf_seq = mark / BUF_SIZE; + buf_pos = (int)(mark % BUF_SIZE); + if (buf != null && buf.seq != buf_seq) buf = null; + } + + @Override + public synchronized void mark(int readlimit) { + mark = buf_seq * BUF_SIZE + buf_pos; + } + + @Override + public synchronized void close() throws IOException { + if (closed) return; + new TCFTask<Object>() { + public void run() { + if (!stopReadAhead(this)) return; + assert read_ahead_buffers.isEmpty(); + IFileSystem fs = handle.getService(); + fs.close(handle, new IFileSystem.DoneClose() { + public void doneClose(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(this); + } + }); + } + }.getIO(); + closed = true; + buf_seq = 0; + buf_pos = 0; + buf = null; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java new file mode 100644 index 000000000..486e119e6 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.util; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; + +/** + * TCFFileOutputStream is high performance OutputStream implementation over TCF FileSystem service. + * The class uses write-back buffers to achieve maximum throughput. + */ +public final class TCFFileOutputStream extends OutputStream { + + private static final int BUF_SIZE = 0x1000; + private static final int MAX_WRITE_BACK = 8; + + private final IFileHandle handle; + private final Set<IToken> write_commands = new HashSet<IToken>(); + private final int[] dirty = new int[1]; + private final byte[] buf = new byte[BUF_SIZE]; + private int buf_pos = 0; + private long offset = 0; + private IOException flush_error; + private boolean closed; + + public TCFFileOutputStream(IFileHandle handle) { + this.handle = handle; + } + + @Override + public synchronized void write(int b) throws IOException { + if (closed) throw new IOException("Stream is closed"); + if (buf_pos == BUF_SIZE) flush(); + buf[buf_pos++] = (byte)b; + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (len == 0) return; + if (b == null) throw new NullPointerException(); + if (off < 0 || off > b.length || len < 0 || + off + len > b.length || off + len < 0) + throw new IndexOutOfBoundsException(); + while (len > 0) { + if (buf_pos == BUF_SIZE) flush(); + if (buf_pos == 0 && len > BUF_SIZE) { + flush(b, off, len); + return; + } + int n = BUF_SIZE - buf_pos; + if (len < n) n = len; + System.arraycopy(b, off, buf, buf_pos, n); + off += n; + len -= n; + buf_pos += n; + } + } + + @Override + public synchronized void flush() throws IOException { + if (buf_pos == 0) return; + flush(buf, 0, buf_pos); + buf_pos = 0; + } + + private void flush(final byte[] buf, final int off, final int len) throws IOException { + synchronized (dirty) { + if (flush_error != null) throw flush_error; + while (dirty[0] >= MAX_WRITE_BACK) { + try { + dirty.wait(); + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + new TCFTask<Object>() { + public void run() { + IFileSystem fs = handle.getService(); + write_commands.add(fs.write(handle, offset, buf, off, len, new IFileSystem.DoneWrite() { + public void doneWrite(IToken token, FileSystemException error) { + assert write_commands.contains(token); + write_commands.remove(token); + if (error != null) { + for (Iterator<IToken> i = write_commands.iterator(); i.hasNext();) { + if (i.next().cancel()) i.remove(); + } + } + synchronized (dirty) { + if (error != null && flush_error == null) flush_error = error; + dirty[0] = write_commands.size(); + dirty.notifyAll(); + } + } + })); + synchronized (dirty) { + dirty[0] = write_commands.size(); + } + done(this); + } + }.getIO(); + offset += len; + } + + @Override + public synchronized void close() throws IOException { + if (closed) return; + flush(); + synchronized (dirty) { + while (dirty[0] > 0) { + try { + dirty.wait(); + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + new TCFTask<Object>() { + public void run() { + IFileSystem fs = handle.getService(); + fs.close(handle, new IFileSystem.DoneClose() { + public void doneClose(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(this); + } + }); + } + }.getIO(); + closed = true; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java new file mode 100644 index 000000000..abda52d02 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007 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 com.windriver.tcf.api.util; + +import java.io.IOException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.windriver.tcf.api.protocol.Protocol; + +public abstract class TCFTask<V> implements Runnable, Future<V> { + + private V result; + private Throwable error; + private boolean canceled; + + public TCFTask() { + Protocol.invokeLater(new Runnable() { + public void run() { + try { + TCFTask.this.run(); + } + catch (Throwable x) { + if (result == null && error == null) error(x); + } + } + }); + } + + public synchronized void done(V result) { + if (canceled) return; + assert Protocol.isDispatchThread(); + assert this.error == null; + assert this.result == null; + this.result = result; + notifyAll(); + } + + public synchronized void error(Throwable error) { + assert Protocol.isDispatchThread(); + if (canceled) return; + assert this.error == null; + assert this.result == null; + this.error = error; + //System.err.print("TCFTask exception: "); + //error.printStackTrace(); + notifyAll(); + } + + public synchronized boolean cancel(boolean mayInterruptIfRunning) { + if (isDone()) return false; + canceled = true; + error = new CancellationException(); + notifyAll(); + return true; + } + + public V get() throws InterruptedException, ExecutionException { + assert !Protocol.isDispatchThread(); + synchronized (this) { + if (!isDone()) wait(); + if (error instanceof ExecutionException) throw (ExecutionException)error; + if (error instanceof InterruptedException) throw (InterruptedException)error; + if (error != null) throw new ExecutionException(error); + return result; + } + } + + public V getE() { + try { + return get(); + } + catch (Throwable e) { + if (e instanceof Error) throw (Error)e; + throw new Error(e); + } + } + + public V getIO() throws IOException { + try { + return get(); + } + catch (Throwable e) { + if (e instanceof IOException) throw (IOException)e; + IOException y = new IOException(); + y.initCause(e); + throw y; + } + } + + public synchronized V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + unit.toNanos(timeout); + // TODO Auto-generated method stub + assert false; + return null; + } + + public synchronized boolean isCancelled() { + return canceled; + } + + public synchronized boolean isDone() { + return canceled || error != null || result != null; + } +} |