Index: java/main/src/test/java/com/framsticks/communication/RequestTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/communication/RequestTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/communication/RequestTest.java	(revision 99)
@@ -7,4 +7,5 @@
 import com.framsticks.communication.queries.GetRequest;
 import com.framsticks.communication.queries.InfoRequest;
+import com.framsticks.communication.queries.RegisterRequest;
 import com.framsticks.communication.queries.SetRequest;
 import com.framsticks.communication.queries.UseRequest;
@@ -46,4 +47,5 @@
 			{ SetRequest.class, "set /test field value"},
 			{ SetRequest.class, "set /test field \"value with spaces\""},
+			{ RegisterRequest.class, "reg /test/field_changed"},
 			{ VersionRequest.class, "version 4"},
 			{ InfoRequest.class, "info /some/path"},
Index: java/main/src/test/java/com/framsticks/core/LocalTreeTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/core/LocalTreeTest.java	(revision 99)
+++ java/main/src/test/java/com/framsticks/core/LocalTreeTest.java	(revision 99)
@@ -0,0 +1,92 @@
+package com.framsticks.core;
+
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+// import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.framsticks.model.Joint;
+import com.framsticks.model.Model;
+import com.framsticks.model.ModelBuilder;
+import com.framsticks.model.f0.Schema;
+import com.framsticks.model.f0.SchemaBuilder;
+import com.framsticks.test.TestConfiguration;
+import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.Monitor;
+import com.framsticks.util.dispatching.RunAt;
+import com.framsticks.params.AccessInterface;
+import com.framsticks.params.ReflectionAccess;
+import com.framsticks.params.types.FloatParam;
+
+import static com.framsticks.core.TreeOperations.*;
+
+
+import static org.fest.assertions.Assertions.*;
+
+@Test
+public class LocalTreeTest extends TestConfiguration {
+
+	private Model model;
+	private Schema schema;
+	private LocalTree tree;
+	private Monitor monitor;
+
+	@BeforeClass
+	public void setUp() {
+		schema = new SchemaBuilder().stream(Schema.getDefaultDefinitionAsStream()).finish();
+		assertThat(schema.getFramsClasses().size()).isEqualTo(5);
+		assertThat(schema.getNeuroClasses().size()).isEqualTo(21);
+	}
+
+	@Test
+	public void loadModel() {
+		model = new ModelBuilder().schema(schema).stream(LocalTreeTest.class.getResourceAsStream("/parsers/f0_example.txt")).finish();
+
+		assertThat(model.getJoints().size()).isEqualTo(2);
+	}
+
+
+	@Test(dependsOnMethods = "loadModel", timeOut = 2000)
+	public void startTree() {
+		tree = new LocalTree();
+		tree.takeAllFrom(schema.getRegistry());
+
+		tree.setRootObject(model);
+
+		monitor = new Monitor(tree);
+		monitor.use();
+
+		assertThat(Dispatching.get(tree, new Dispatching.QueryHandler<String>(failOnException) {
+				@Override
+				public String get() {
+					return tree.getRootObject(Model.class).getParts().get(2).info;
+				}
+			})).isEqualTo("bla");
+
+		tree.dispatch(new RunAt<Tree>(failOnException) {
+			@Override
+			protected void runAt() {
+				assertThat(tree.getRootObject(Model.class).getNeuroDefinitions().get(2).details).isEqualTo("G");
+
+				Path path = Path.to(tree, "/");
+				assertThat(path.isResolved());
+				AccessInterface access = TreeOperations.bindAccess(path);
+				assertThat(access.get("se", Double.class)).isEqualTo(1.0);
+
+				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getDef(Double.class)).isEqualTo(1.0);
+				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getMax(Double.class)).isEqualTo(999.0);
+				assertThat(bindAccess(tree, "/parts/2")).isInstanceOf(ReflectionAccess.class);
+				assertThat(Path.to(tree, "/neurodefs").getTopObject()).isInstanceOf(ArrayList.class);
+				assertThat(Path.to(tree, "/joints/1").getTopObject()).isInstanceOf(Joint.class);
+				assertThat(bindAccess(tree, "/parts/2").get("i", String.class)).isEqualTo("bla");
+			}
+		});
+
+	}
+
+	@Test(dependsOnMethods = "startTree", timeOut = 2000)
+	public void stopTree() {
+		monitor.useFor(1.0).drop().join();
+	}
+}
Index: java/main/src/test/java/com/framsticks/core/ObjectTreeTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/core/ObjectTreeTest.java	(revision 98)
+++ 	(revision )
@@ -1,91 +1,0 @@
-package com.framsticks.core;
-
-import java.util.ArrayList;
-
-import org.testng.annotations.BeforeClass;
-// import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.framsticks.model.Joint;
-import com.framsticks.model.Model;
-import com.framsticks.model.ModelBuilder;
-import com.framsticks.model.f0.Schema;
-import com.framsticks.model.f0.SchemaBuilder;
-import com.framsticks.test.TestConfiguration;
-import com.framsticks.util.dispatching.Dispatching;
-import com.framsticks.util.dispatching.Monitor;
-import com.framsticks.util.dispatching.RunAt;
-import com.framsticks.params.AccessInterface;
-import com.framsticks.params.ReflectionAccess;
-import com.framsticks.params.types.FloatParam;
-
-import static com.framsticks.core.TreeOperations.*;
-
-
-import static org.fest.assertions.Assertions.*;
-
-@Test
-public class ObjectTreeTest extends TestConfiguration {
-
-	private Model model;
-	private Schema schema;
-	private ObjectTree tree;
-	private Monitor monitor;
-
-	@BeforeClass
-	public void setUp() {
-		schema = new SchemaBuilder().stream(Schema.getDefaultDefinitionAsStream()).finish();
-		assertThat(schema.getFramsClasses().size()).isEqualTo(5);
-		assertThat(schema.getNeuroClasses().size()).isEqualTo(21);
-	}
-
-	@Test
-	public void loadModel() {
-		model = new ModelBuilder().schema(schema).stream(ObjectTreeTest.class.getResourceAsStream("/parsers/f0_example.txt")).finish();
-
-		assertThat(model.getJoints().size()).isEqualTo(2);
-	}
-
-
-	@Test(dependsOnMethods = "loadModel", timeOut = 2000)
-	public void startTree() {
-		tree = new ObjectTree();
-		tree.takeAllFrom(schema.getRegistry());
-
-		tree.setRootObject(model);
-
-		monitor = new Monitor(tree);
-		monitor.use();
-
-		assertThat(Dispatching.get(tree, new Dispatching.QueryHandler<String>(failOnException) {
-				@Override
-				public String get() {
-					return tree.getRootObject(Model.class).getParts().get(2).info;
-				}
-			})).isEqualTo("bla");
-
-		tree.dispatch(new RunAt<Tree>(failOnException) {
-			@Override
-			protected void runAt() {
-				assertThat(tree.getRootObject(Model.class).getNeuroDefinitions().get(2).details).isEqualTo("G");
-
-				Path path = Path.to(tree, "/");
-				assertThat(path.isResolved());
-				AccessInterface access = TreeOperations.bindAccess(path);
-				assertThat(access.get("se", Double.class)).isEqualTo(1.0);
-
-				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getDef(Double.class)).isEqualTo(1.0);
-				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getMax(Double.class)).isEqualTo(999.0);
-				assertThat(bindAccess(tree, "/parts/2")).isInstanceOf(ReflectionAccess.class);
-				assertThat(Path.to(tree, "/neurodefs").getTopObject()).isInstanceOf(ArrayList.class);
-				assertThat(Path.to(tree, "/joints/1").getTopObject()).isInstanceOf(Joint.class);
-				assertThat(bindAccess(tree, "/parts/2").get("i", String.class)).isEqualTo("bla");
-			}
-		});
-	}
-
-	@Test(dependsOnMethods = "startTree", timeOut = 2000)
-	public void stopTree() {
-		monitor.useFor(1.0).drop().join();
-	}
-}
Index: java/main/src/test/java/com/framsticks/core/PathTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/core/PathTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/core/PathTest.java	(revision 99)
@@ -8,7 +8,11 @@
 
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import com.framsticks.test.TestConfiguration;
+import com.framsticks.util.lang.Pair;
+
+import static org.fest.assertions.Assertions.*;
 
 @Test
@@ -25,6 +29,36 @@
 	}
 
-	@Test
-	public void testPath() {
+	@Test(dataProvider = "pathValidationProvider")
+	public void pathValidation(String path, boolean ok) {
+		assertThat(Path.isValidString(path)).describedAs(path).isEqualTo(ok);
+	}
+
+	@Test(dataProvider = "pathSplitingProvider")
+	public void pathSpliting(String path, String prefix, String suffix) {
+		Pair<String, String> p = Path.removeLastElement(path);
+		assertThat(p.first).isEqualTo(prefix);
+		assertThat(p.second).isEqualTo(suffix);
+	}
+
+	@DataProvider
+	public Object[][] pathValidationProvider() {
+		return new Object[][] {
+			{ "/", true },
+			{ "/path", true },
+			{ "path", false },
+			{ "/path/to/", false },
+			{ "/path/to", true },
+			{ "/testClass/history_changed", true },
+			{ "/cli/events/e0", true }
+
+		};
+	}
+
+	@DataProvider
+	public Object[][] pathSplitingProvider() {
+		return new Object[][] {
+			{ "/event", "/", "event" },
+			{ "/path/event", "/path", "event" }
+		};
 	}
 
Index: java/main/src/test/java/com/framsticks/gui/BrowserBaseTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/BrowserBaseTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/gui/BrowserBaseTest.java	(revision 99)
@@ -50,5 +50,5 @@
 	protected void clickAndExpandPath(String path) {
 		tree.clickPath(path);
-		Dispatching.sleep(1.0);
+		Dispatching.sleep(2.0);
 		tree.expandPath(path);
 		robot.waitForIdle();
Index: java/main/src/test/java/com/framsticks/gui/BrowserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/BrowserTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/gui/BrowserTest.java	(revision 99)
@@ -12,5 +12,5 @@
 
 import com.framsticks.model.ModelPackage;
-import com.framsticks.remote.SimulatorTree;
+import com.framsticks.remote.RemoteTree;
 import com.framsticks.util.dispatching.Dispatching;
 
@@ -19,5 +19,5 @@
 	private static final Logger log = Logger.getLogger(BrowserTest.class);
 
-	SimulatorTree localhost;
+	RemoteTree localhost;
 
 	@Override
@@ -25,5 +25,5 @@
 		browser = new Browser();
 
-		localhost = new SimulatorTree();
+		localhost = new RemoteTree();
 		localhost.setName("localhost");
 		localhost.setAddress("localhost:9009");
@@ -37,11 +37,13 @@
 	public void testShow() {
 		Dispatching.synchronize(localhost, 1.0);
-		// Dispatching.sleep(0.5);
+		Dispatching.sleep(2.0);
 		log.info("testing");
-		tree.clickRow(0).expandRow(0);
-		robot.waitForIdle();
+		clickAndExpandPath("localhost");
+		// tree.clickRow(0).expandRow(0);
+		// robot.waitForIdle();
 
-		tree.clickRow(1).expandRow(1);
-		robot.waitForIdle();
+		clickAndExpandPath("localhost/simulator");
+		// tree.clickRow(1).expandRow(1);
+		// robot.waitForIdle();
 		assertThat(tree.valueAt(1)).isEqualTo("simulator");
 		robot.waitForIdle();
Index: java/main/src/test/java/com/framsticks/gui/LocalTreeBrowserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/LocalTreeBrowserTest.java	(revision 99)
+++ java/main/src/test/java/com/framsticks/gui/LocalTreeBrowserTest.java	(revision 99)
@@ -0,0 +1,54 @@
+package com.framsticks.gui;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+import org.testng.annotations.Test;
+
+import com.framsticks.core.Tree;
+import com.framsticks.core.LocalTree;
+import com.framsticks.model.Model;
+import com.framsticks.params.types.FloatParam;
+import com.framsticks.parsers.XmlLoader;
+import com.framsticks.util.dispatching.RunAt;
+
+import static com.framsticks.core.TreeOperations.*;
+
+@Test
+public class LocalTreeBrowserTest extends BrowserBaseTest {
+
+	LocalTree tree;
+
+	@Override
+	protected void configureBrowser() {
+		browser = new XmlLoader().load(Browser.class, getClass().getResourceAsStream("/configs/LocalTreeBrowserTest.xml"));
+
+		assertThat(browser.getTrees().size()).isEqualTo(1);
+		assertThat(browser.getTrees().get("model")).isInstanceOf(LocalTree.class);
+
+		tree = (LocalTree) browser.getTrees().get("model");
+	}
+
+	@Test(timeOut = 10000)
+	public void testShow() {
+		tree.dispatch(new RunAt<Tree>(failOnException) {
+			@Override
+			protected void runAt() {
+				assertThat(tree.getRootObject()).isInstanceOf(Model.class);
+			}
+		});
+
+		clickAndExpandPath("model");
+		clickAndExpandPath("model/parts");
+		clickAndExpandPath("model/parts/2");
+
+		tree.dispatch(new RunAt<Tree>(failOnException) {
+			@Override
+			protected void runAt() {
+				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getMax(Double.class)).isEqualTo(999.0);
+			}
+		});
+
+	}
+
+
+}
Index: java/main/src/test/java/com/framsticks/gui/ObjectTreeBrowserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/ObjectTreeBrowserTest.java	(revision 98)
+++ 	(revision )
@@ -1,54 +1,0 @@
-package com.framsticks.gui;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-import org.testng.annotations.Test;
-
-import com.framsticks.core.Tree;
-import com.framsticks.core.ObjectTree;
-import com.framsticks.model.Model;
-import com.framsticks.params.types.FloatParam;
-import com.framsticks.parsers.XmlLoader;
-import com.framsticks.util.dispatching.RunAt;
-
-import static com.framsticks.core.TreeOperations.*;
-
-@Test
-public class ObjectTreeBrowserTest extends BrowserBaseTest {
-
-	ObjectTree tree;
-
-	@Override
-	protected void configureBrowser() {
-		browser = new XmlLoader().load(Browser.class, getClass().getResourceAsStream("/configs/ObjectTreeBrowserTest.xml"));
-
-		assertThat(browser.getTrees().size()).isEqualTo(1);
-		assertThat(browser.getTrees().get("model")).isInstanceOf(ObjectTree.class);
-
-		tree = (ObjectTree) browser.getTrees().get("model");
-	}
-
-	@Test(timeOut = 10000)
-	public void testShow() {
-		tree.dispatch(new RunAt<Tree>(failOnException) {
-			@Override
-			protected void runAt() {
-				assertThat(tree.getRootObject()).isInstanceOf(Model.class);
-			}
-		});
-
-		clickAndExpandPath("model");
-		clickAndExpandPath("model/parts");
-		clickAndExpandPath("model/parts/2");
-
-		tree.dispatch(new RunAt<Tree>(failOnException) {
-			@Override
-			protected void runAt() {
-				assertThat(bindAccess(tree, "/parts/2").getFramsClass().getParamEntry("m", FloatParam.class).getMax(Double.class)).isEqualTo(999.0);
-			}
-		});
-
-	}
-
-
-}
Index: java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/gui/ProcedureBrowserTest.java	(revision 99)
@@ -7,12 +7,17 @@
 import org.testng.annotations.Test;
 
+import com.framsticks.core.Path;
 import com.framsticks.core.Tree;
-import com.framsticks.core.ObjectTree;
+import com.framsticks.core.LocalTree;
 import com.framsticks.params.AccessInterface;
+import com.framsticks.params.EventListener;
 import com.framsticks.params.FramsClass;
 import com.framsticks.params.ReflectionAccess;
+import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.StringParam;
 import com.framsticks.parsers.XmlLoader;
+import com.framsticks.test.ChangeEvent;
 import com.framsticks.test.TestClass;
+import com.framsticks.util.dispatching.FutureHandler;
 // import com.framsticks.util.dispatching.Dispatching;
 import com.framsticks.util.dispatching.RunAt;
@@ -22,5 +27,5 @@
 public class ProcedureBrowserTest extends BrowserBaseTest {
 
-	ObjectTree tree;
+	LocalTree tree;
 
 	@Override
@@ -29,7 +34,7 @@
 
 		assertThat(browser.getTrees().size()).isEqualTo(1);
-		assertThat(browser.getTrees().get("test")).isInstanceOf(ObjectTree.class);
+		assertThat(browser.getTrees().get("test")).isInstanceOf(LocalTree.class);
 
-		tree = (ObjectTree) browser.getTrees().get("test");
+		tree = (LocalTree) browser.getTrees().get("test");
 	}
 
@@ -60,9 +65,10 @@
 				assertThat(access).isInstanceOf(ReflectionAccess.class);
 				FramsClass framsClass = access.getFramsClass();
-				assertThat(framsClass.getParamCount()).isEqualTo(4);
+				assertThat(framsClass.getParamCount()).isEqualTo(5);
 				assertThat(framsClass.getParam(0).getId()).isEqualTo("name");
 				assertThat(framsClass.getParam(1).getId()).isEqualTo("history");
-				assertThat(framsClass.getParam(2).getId()).isEqualTo("appendHistory");
-				assertThat(framsClass.getParam(3).getId()).isEqualTo("resetHistory");
+				assertThat(framsClass.getParam(2).getId()).isEqualTo("history_changed");
+				assertThat(framsClass.getParam(3).getId()).isEqualTo("appendHistory");
+				assertThat(framsClass.getParam(4).getId()).isEqualTo("resetHistory");
 
 				assertThat(access.get("history", String.class)).isEqualTo("initial|");
@@ -77,9 +83,19 @@
 		waitForIdle();
 
+		final EventListener<ChangeEvent> listener = new EventListener<ChangeEvent>() {
+
+			@Override
+			public void action(ChangeEvent argument) {
+				assertThat(argument.history).isEqualTo("");
+			}
+		};
 
 		tree.dispatch(new RunAt<Tree>(failOnException) {
 			@Override
 			protected void runAt() {
-				assertThat(bindAccess(tree, "/").get("history", String.class)).isEqualTo("initial|Żółw|");
+				AccessInterface access = bindAccess(tree, "/");
+				assertThat(access.get("history", String.class)).isEqualTo("initial|Żółw|");
+
+				tree.addListener(Path.to(tree, "/"), access.getFramsClass().getParamEntry("history_changed", EventParam.class), listener, ChangeEvent.class, FutureHandler.doNothing(Void.class, failOnException));
 			}
 		});
@@ -91,5 +107,8 @@
 			@Override
 			protected void runAt() {
-				assertThat(bindAccess(tree, "/").get("history", String.class)).isEqualTo("");
+				AccessInterface access = bindAccess(tree, "/");
+				assertThat(access.get("history", String.class)).isEqualTo("");
+
+				tree.removeListener(Path.to(tree, "/"), access.getFramsClass().getParamEntry("history_changed", EventParam.class), listener, FutureHandler.doNothing(Void.class, failOnException));
 			}
 		});
Index: java/main/src/test/java/com/framsticks/hosting/ServerTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/hosting/ServerTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/hosting/ServerTest.java	(revision 99)
@@ -2,19 +2,26 @@
 
 // import org.apache.log4j.Logger;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
 import org.testng.annotations.Test;
 
-import com.framsticks.core.Mode;
-import com.framsticks.core.ObjectTree;
+import com.framsticks.core.LocalTree;
 import com.framsticks.core.Path;
+import com.framsticks.core.TreeOperations;
 import com.framsticks.core.XmlBasedTest;
 import com.framsticks.remote.RemoteTree;
 
+import com.framsticks.test.ChangeEvent;
 import com.framsticks.test.TestClass;
 import com.framsticks.core.Tree;
 import com.framsticks.params.FramsClass;
-import com.framsticks.util.dispatching.Dispatching;
 import com.framsticks.params.AccessInterface;
+import com.framsticks.params.EventListener;
 import com.framsticks.params.PrimitiveParam;
 import com.framsticks.params.PropertiesAccess;
+import com.framsticks.params.types.EventParam;
+// import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.util.dispatching.Dispatching.Waiter;
@@ -34,6 +41,8 @@
 
 	protected Server server;
-	protected ObjectTree hosted;
+	protected LocalTree hosted;
 	protected TestClass hostedObject;
+	protected EventListener<ChangeEvent> listener;
+	protected List<String> listenerArguments = new LinkedList<>();
 
 	@Override
@@ -50,6 +59,6 @@
 		server = (Server) framsticks.get("test");
 		remote = (RemoteTree) framsticks.get("remote");
-		assertThat(server.getHosted()).isInstanceOf(ObjectTree.class);
-		hosted = (ObjectTree) server.getHosted();
+		assertThat(server.getHosted()).isInstanceOf(LocalTree.class);
+		hosted = (LocalTree) server.getHosted();
 		assertThat(hosted.getRootObject()).isInstanceOf(TestClass.class);
 		hostedObject = hosted.getRootObject(TestClass.class);
@@ -58,18 +67,16 @@
 	@Test(dependsOnMethods = "runServer")
 	public void fetchInfo() {
-		remote.dispatch(new RunAt<Tree>(failOnException) {
+		final Waiter waiter = produceWaiter(1.0);
+
+		TreeOperations.tryGet(remote, "/testClass", new FutureHandler<Path>(failOnException) {
 			@Override
-			protected void runAt() {
-				remote.info(Path.to(remote, "/"), new FutureHandler<FramsClass>(failOnException) {
-					@Override
-					protected void result(FramsClass result) {
-						remoteTestFramsClass = result;
-						assertThat(result.getId()).isEqualTo("TestClass");
-					}
-				});
+			protected void result(Path path) {
+				assertThat(path.isResolved()).isTrue();
+				remoteTestFramsClass = bindAccess(path).getFramsClass();
+				assertThat(remoteTestFramsClass.getName()).isEqualTo("TestClass");
+				waiter.pass();
 			}
 		});
 
-		Dispatching.synchronize(remote, 1.0);
 	}
 
@@ -78,21 +85,13 @@
 		final Waiter waiter = produceWaiter(1.0);
 
-		remote.dispatch(new RunAt<Tree>(failOnException) {
+		TreeOperations.tryGet(remote, "/testClass", new FutureHandler<Path>(failOnException) {
 			@Override
-			protected void runAt() {
-				final Path path = Path.to(remote, "/");
-				assertThat(path.isResolved()).isFalse();
-
-				remote.get(path, Mode.FETCH, new FutureHandler<Path>(failOnException) {
-					@Override
-					protected void result(Path path) {
-						assertThat(path.isResolved()).isTrue();
-						remotePath = path;
-						AccessInterface access = bindAccess(path);
-						assertThat(access).isInstanceOf(PropertiesAccess.class);
-						assertThat(access.get("name", String.class)).isEqualTo("a test name");
-						waiter.pass();
-					}
-				});
+			protected void result(Path path) {
+				assertThat(path.isResolved()).isTrue();
+				remotePath = path;
+				AccessInterface access = bindAccess(path);
+				assertThat(access).isInstanceOf(PropertiesAccess.class);
+				assertThat(access.get("name", String.class)).isEqualTo("a test name");
+				waiter.pass();
 			}
 		});
@@ -120,14 +119,29 @@
 
 	@Test(dependsOnMethods = "setValueName")
+	public void registerListener() {
+		final Waiter waiter = produceWaiter(1.0);
+		listener = new EventListener<ChangeEvent>() {
+
+			@Override
+			public void action(ChangeEvent argument) {
+				listenerArguments.add(argument.history);
+			}
+		};
+
+		TreeOperations.tryGet(remote, "/cli/events", new FutureHandler<Path>(failOnException) {
+			@Override
+			protected void result(Path path) {
+				waiter.pass();
+			}
+		});
+
+		addListener(remotePath, remoteTestFramsClass.getParamEntry("history_changed", EventParam.class), listener, ChangeEvent.class, produceWaiter(1.0).passInFuture(Void.class));
+	}
+
+	@Test(dependsOnMethods = "registerListener")
 	public void callMethod() {
-		final Waiter firstWaiter = produceWaiter(2.0);
 		final Waiter waiter = produceWaiter(2.0);
 
-		call(remotePath, remoteTestFramsClass.getParamEntry("resetHistory", ProcedureParam.class), new Object[] {}, new FutureHandler<Object>(failOnException) {
-			@Override
-			protected void result(Object result) {
-				firstWaiter.pass();
-			}
-		});
+		call(remotePath, remoteTestFramsClass.getParamEntry("resetHistory", ProcedureParam.class), new Object[] {}, produceWaiter(2.0).passInFuture(Object.class));
 
 		call(remotePath, remoteTestFramsClass.getParamEntry("appendHistory", ProcedureParam.class), new Object[] {"next word"}, new FutureHandler<Object>(failOnException) {
@@ -143,5 +157,12 @@
 			}
 		});
+	}
 
+
+	@Test(dependsOnMethods = "callMethod")
+	public void deregisterListener() {
+		removeListener(remotePath, remoteTestFramsClass.getParamEntry("history_changed", EventParam.class), listener, produceWaiter(1.0).passInFuture(Void.class));
+
+		assertThat(listenerArguments).isEqualTo(Arrays.asList("", "next word|"));
 	}
 
Index: java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java
===================================================================
--- java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/params/FramsClassBuilderTest.java	(revision 99)
@@ -6,9 +6,13 @@
 import org.testng.annotations.Test;
 
+import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.params.types.StringParam;
 import com.framsticks.parsers.Savers;
+import com.framsticks.test.ChangeEvent;
 import com.framsticks.test.TestClass;
 import com.framsticks.test.TestConfiguration;
+import com.framsticks.util.lang.Holder;
+
 import static org.fest.assertions.Assertions.*;
 
@@ -30,8 +34,9 @@
 	@Test
 	public void checkProcedureParams() {
-		assertThat(framsClass.getParamCount()).isEqualTo(4);
+		assertThat(framsClass.getParamCount()).isEqualTo(5);
 
 		assertThat(framsClass.getParam("name")).isInstanceOf(StringParam.class);
 		assertThat(framsClass.getParam("history")).isInstanceOf(StringParam.class);
+		assertThat(framsClass.getParam("history_changed")).isInstanceOf(EventParam.class);
 
 		assertThat(framsClass.getParam("appendHistory")).isInstanceOf(ProcedureParam.class);
@@ -62,4 +67,9 @@
 					"",
 					"prop:",
+					"id:history_changed",
+					"name:HistoryListener",
+					"type:e ChangeEvent",
+					"",
+					"prop:",
 					"id:appendHistory",
 					"name:AppendHistory",
@@ -75,10 +85,12 @@
 	}
 
+	@Test(dependsOnMethods = "print")
+	public void createAccess() {
+		access = new ReflectionAccess(TestClass.class, framsClass);
+		access.select(test);
+	}
 
-	@Test(dependsOnMethods = "print")
+	@Test(dependsOnMethods = "createAccess")
 	public void callProcedures() {
-		access = new ReflectionAccess(TestClass.class, framsClass);
-
-		access.select(test);
 
 		assertThat(access.get("history", String.class)).isEqualTo("initial|first|");
@@ -93,5 +105,31 @@
 
 		assertThat(access.get("history", String.class)).isEqualTo("");
+	}
 
+	@Test(dependsOnMethods = "callProcedures")
+	public void listeners() {
+
+		final Holder<String> called = new Holder<>();
+
+		final EventListener<ChangeEvent> listener = new EventListener<ChangeEvent>() {
+
+			@Override
+			public void action(ChangeEvent argument) {
+				called.set(argument.history);
+			}
+		};
+
+		final EventParam eventParam = access.getFramsClass().getParamEntry("history_changed", EventParam.class);
+		access.reg(eventParam, listener);
+
+		final String currentHistory = access.get("history", String.class);
+		final String addition = "test";
+
+		access.call("appendHistory", new Object[] { addition });
+
+		String expected = currentHistory + addition + "|";
+		assertThat(access.get("history", String.class)).isEqualTo(expected);
+		assertThat(called.get()).isEqualTo(expected);
+		access.regRemove(eventParam, listener);
 	}
 
Index: java/main/src/test/java/com/framsticks/test/TestConfiguration.java
===================================================================
--- java/main/src/test/java/com/framsticks/test/TestConfiguration.java	(revision 98)
+++ java/main/src/test/java/com/framsticks/test/TestConfiguration.java	(revision 99)
@@ -17,5 +17,5 @@
 import com.framsticks.util.dispatching.ExceptionResultHandler;
 
-import static org.fest.assertions.Assertions.*;
+// import static org.fest.assertions.Assertions.*;
 
 public class TestConfiguration {
@@ -31,18 +31,25 @@
 	private final List<AssertionError> asyncAssertions = new LinkedList<>();
 
+	public static AssertionError wrapInAssertion(Throwable throwable) {
+		if (throwable instanceof AssertionError) {
+			return (AssertionError) throwable;
+		}
+
+		AssertionError ae = new AssertionError();
+		ae.initCause(throwable);
+		return ae;
+	}
+
+	public void addAsyncAssertion(Throwable throwable) {
+		synchronized (asyncAssertions) {
+			asyncAssertions.add(wrapInAssertion(throwable));
+		}
+	}
+
 	public ExceptionHandler createExceptionHandler() {
 		return new ExceptionHandler() {
 			@Override
 			public boolean handle(Dispatcher<?> dispatcher, Throwable throwable) {
-				AssertionError ae;
-				if (AssertionError.class.isInstance(throwable)) {
-					ae = AssertionError.class.cast(throwable);
-				} else {
-					ae = new AssertionError();
-					ae.initCause(throwable);
-				}
-				synchronized (asyncAssertions) {
-					asyncAssertions.add(ae);
-				}
+				addAsyncAssertion(throwable);
 				return true;
 			}
@@ -83,14 +90,14 @@
 
 	protected Dispatching.Waiter produceWaiter(double timeOut) {
-		Waiter waiter = new Waiter(timeOut);
+		Waiter waiter = new Waiter(timeOut, failOnException);
 		waiters.add(waiter);
 		return waiter;
 	}
 
-	public static final ExceptionResultHandler failOnException = new ExceptionResultHandler() {
+	public final ExceptionResultHandler failOnException = new ExceptionResultHandler() {
 		@Override
 		public void handle(FramsticksException e) {
-			e.printStackTrace();
-			assertThat(e).isNull();
+			log.error("passing exception as assertion in " + TestConfiguration.this.getClass(), e);
+			addAsyncAssertion(e);
 		}
 	};
Index: java/main/src/test/resources/configs/LocalTreeBrowserTest.xml
===================================================================
--- java/main/src/test/resources/configs/LocalTreeBrowserTest.xml	(revision 99)
+++ java/main/src/test/resources/configs/LocalTreeBrowserTest.xml	(revision 99)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Browser>
+	<import class="com.framsticks.model.ModelBuilder" />
+	<import class="com.framsticks.core.LocalTree" />
+	<!-- <import class="com.framsticks.model.ModelPackage" /> -->
+	<LocalTree name="model">
+		<ModelBuilder resource="/examples/f0_example.txt" />
+	</LocalTree>
+</Browser>
Index: java/main/src/test/resources/configs/ObjectTreeBrowserTest.xml
===================================================================
--- java/main/src/test/resources/configs/ObjectTreeBrowserTest.xml	(revision 98)
+++ 	(revision )
@@ -1,9 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Browser>
-	<import class="com.framsticks.model.ModelBuilder" />
-	<import class="com.framsticks.core.ObjectTree" />
-	<import class="com.framsticks.model.ModelPackage" />
-	<ObjectTree name="model">
-		<ModelBuilder resource="/examples/f0_example.txt" />
-	</ObjectTree>
-</Browser>
Index: java/main/src/test/resources/configs/ProcedureBrowserTest.xml
===================================================================
--- java/main/src/test/resources/configs/ProcedureBrowserTest.xml	(revision 98)
+++ java/main/src/test/resources/configs/ProcedureBrowserTest.xml	(revision 99)
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Browser>
-	<import class="com.framsticks.core.ObjectTree" />
+	<import class="com.framsticks.core.LocalTree" />
 	<import class="com.framsticks.test.TestClass" />
-	<ObjectTree name="test">
+	<LocalTree name="test">
 		<TestClass />
-	</ObjectTree>
+	</LocalTree>
 </Browser>
Index: java/main/src/test/resources/configs/ServerTest.xml
===================================================================
--- java/main/src/test/resources/configs/ServerTest.xml	(revision 98)
+++ java/main/src/test/resources/configs/ServerTest.xml	(revision 99)
@@ -3,11 +3,11 @@
 	<import class="com.framsticks.hosting.Server" />
 	<import class="com.framsticks.remote.RemoteTree" />
-	<import class="com.framsticks.core.ObjectTree" />
+	<import class="com.framsticks.core.LocalTree" />
 	<import class="com.framsticks.test.TestClass" />
 	<import class="com.framsticks.running.LoggingOutputListener" />
 	<Server name="server" port="9007">
-		<ObjectTree name="test">
+		<LocalTree name="test">
 			<TestClass name="a test name" />
-		</ObjectTree>
+		</LocalTree>
 	</Server>
 	<RemoteTree name="remote" address="localhost:9007" />
Index: java/main/src/test/resources/configs/test.xml
===================================================================
--- java/main/src/test/resources/configs/test.xml	(revision 98)
+++ java/main/src/test/resources/configs/test.xml	(revision 99)
@@ -2,7 +2,7 @@
 <Framsticks>
 	<import class="com.framsticks.gui.Browser" />
-	<import class="com.framsticks.remote.SimulatorTree" />
+	<import class="com.framsticks.remote.RemoteTree" />
 	<Browser name="browser">
-		<SimulatorTree name="localhost:9009" address="localhost:9009" />
+		<RemoteTree name="localhost:9009" address="localhost:9009" />
 	</Browser>
 </Framsticks>
Index: java/main/src/test/resources/log4j.properties
===================================================================
--- java/main/src/test/resources/log4j.properties	(revision 98)
+++ java/main/src/test/resources/log4j.properties	(revision 99)
@@ -28,5 +28,5 @@
 log4j.logger.com.framsticks=WARN
 log4j.logger.com.framsticks.test.TestConfiguration=INFO
-# log4j.logger.com.framsticks.core.TreeOperations=DEBUG
+# log4j.logger.com.framsticks.hosting.Cli=DEBUG
 # log4j.logger.com.framsticks.core.AbstractTree=DEBUG
 # log4j.logger.com.framsticks.remote.RemoteTree=DEBUG
