From e742bf9b8d1bc5ee7a97586510643db6fd3174f2 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Nov 2019 14:53:44 +0100 Subject: Implement basic API (#15) --- .../java/pl/edu/mimuw/cloudatlas/agent/Agent.java | 5 +- .../mimuw/cloudatlas/agent/ApiImplementation.java | 71 ++++++++++++-- src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java | 6 +- .../pl/edu/mimuw/cloudatlas/interpreter/Main.java | 10 +- .../pl/edu/mimuw/cloudatlas/model/Attribute.java | 4 +- .../edu/mimuw/cloudatlas/model/AttributesMap.java | 3 +- .../java/pl/edu/mimuw/cloudatlas/model/Type.java | 6 +- .../edu/mimuw/cloudatlas/model/TypePrimitive.java | 6 ++ .../java/pl/edu/mimuw/cloudatlas/model/Value.java | 4 +- .../edu/mimuw/cloudatlas/model/ValueDuration.java | 5 +- .../pl/edu/mimuw/cloudatlas/model/ValueList.java | 1 + .../pl/edu/mimuw/cloudatlas/model/ValueQuery.java | 70 ++++++++++++++ .../java/pl/edu/mimuw/cloudatlas/model/ZMI.java | 35 +++++++ .../pl/edu/mimuw/cloudatlas/agent/AgentTest.java | 106 +++++++++++++++++++-- 14 files changed, 298 insertions(+), 34 deletions(-) create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java (limited to 'src') diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java index 95ede6e..8eb8b4f 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java @@ -6,11 +6,14 @@ import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import pl.edu.mimuw.cloudatlas.api.Api; +import pl.edu.mimuw.cloudatlas.interpreter.Main; +import pl.edu.mimuw.cloudatlas.model.ZMI; public class Agent { public static void main(String[] args) { try { - ApiImplementation api = new ApiImplementation(); + ZMI root = Main.createTestHierarchy2(); + ApiImplementation api = new ApiImplementation(root); Api apiStub = (Api) UnicastRemoteObject.exportObject(api, 0); Registry registry = LocateRegistry.getRegistry(); diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java index fd52052..12682a6 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/ApiImplementation.java @@ -5,32 +5,91 @@ import java.rmi.RemoteException; import java.util.Set; import java.util.HashSet; +import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.AttributesMap; +import pl.edu.mimuw.cloudatlas.model.PathName; import pl.edu.mimuw.cloudatlas.model.ValueContact; import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; import pl.edu.mimuw.cloudatlas.model.ValueSet; import pl.edu.mimuw.cloudatlas.model.ValueNull; import pl.edu.mimuw.cloudatlas.model.TypePrimitive; +import pl.edu.mimuw.cloudatlas.model.ZMI; import pl.edu.mimuw.cloudatlas.api.Api; public class ApiImplementation implements Api { + ZMI root; + Set contacts; + + public ApiImplementation(ZMI root) { + this.root = root; + this.contacts = new HashSet(); + } + public Set getZoneSet() throws RemoteException { - return null; + Set zones = new HashSet(); + collectZoneNames(root, zones); + return zones; + } + + private void collectZoneNames(ZMI zone, Set names) { + names.add(zone.getPathName().toString()); + for (ZMI son : zone.getSons()) { + collectZoneNames(son, names); + } } - public AttributesMap getZoneAttributeValue(String zoneName) throws RemoteException { - return null; + public AttributesMap getZoneAttributeValues(String zoneName) throws RemoteException { + try { + ZMI zmi = root.findDescendant(new PathName(zoneName)); + return zmi.getAttributes(); + } catch (ZMI.NoSuchZoneException e) { + throw new RemoteException("Zone not found", e); + } } - public void installQuery(String queryName, String query) throws RemoteException { + public void installQuery(String name, String queryCode) throws RemoteException { + try { + ValueQuery query = new ValueQuery(queryCode); + Attribute attributeName = new Attribute(name); + installQueryInHierarchy(root, attributeName, query); + } catch (Exception e) { + throw new RemoteException("Failed to install query", e); + } + } + + private void installQueryInHierarchy(ZMI zmi, Attribute queryName, ValueQuery query) { + if (!zmi.getSons().isEmpty()) { + zmi.getAttributes().addOrChange(queryName, query); + for (ZMI son : zmi.getSons()) { + installQueryInHierarchy(son, queryName, query); + } + } } public void uninstallQuery(String queryName) throws RemoteException { + uninstallQueryInHierarchy(root, new Attribute(queryName)); + } + + private void uninstallQueryInHierarchy(ZMI zmi, Attribute queryName) { + if (!zmi.getSons().isEmpty()) { + zmi.getAttributes().remove(queryName); + for (ZMI son : zmi.getSons()) { + uninstallQueryInHierarchy(son, queryName); + } + } } - public void setAttributeValue(String attributeName, Value value) throws RemoteException { + public void setAttributeValue(String zoneName, String attributeName, Value value) throws RemoteException { + try { + ZMI zmi = root.findDescendant(new PathName(zoneName)); + zmi.getAttributes().addOrChange(new Attribute(attributeName), value); + } catch (ZMI.NoSuchZoneException e) { + throw new RemoteException("Zone not found", e); + } } - public void setFallbackContacts(Set serializedContacts) throws RemoteException { + public void setFallbackContacts(Set contacts) throws RemoteException { + this.contacts = contacts; } } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java b/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java index c5a4581..c62ee39 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/api/Api.java @@ -25,14 +25,14 @@ public interface Api extends Remote { public Set getZoneSet() throws RemoteException; - public AttributesMap getZoneAttributeValue(String zoneName) throws RemoteException; + public AttributesMap getZoneAttributeValues(String zoneName) throws RemoteException; public void installQuery(String queryName, String query) throws RemoteException; public void uninstallQuery(String queryName) throws RemoteException; - public void setAttributeValue(String attributeName, Value value) throws RemoteException; + public void setAttributeValue(String zoneName, String attributeName, Value value) throws RemoteException; - public void setFallbackContacts(Set serializedContacts) throws RemoteException; + public void setFallbackContacts(Set contacts) throws RemoteException; } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java index ea9fdb1..979aa92 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java @@ -65,11 +65,6 @@ public class Main { scanner.close(); } - private static PathName getPathName(ZMI zmi) { - String name = ((ValueString)zmi.getAttributes().get("name")).getValue(); - return zmi.getFather() == null? PathName.ROOT : getPathName(zmi.getFather()).levelDown(name); - } - private static void executeQueries(ZMI zmi, String query, PrintStream out) throws Exception { if(!zmi.getSons().isEmpty()) { for(ZMI son : zmi.getSons()) @@ -78,7 +73,7 @@ public class Main { Yylex lex = new Yylex(new ByteArrayInputStream(query.getBytes())); try { List result = interpreter.interpretProgram((new parser(lex)).pProgram()); - PathName zone = getPathName(zmi); + PathName zone = zmi.getPathName(); for(QueryResult r : result) { out.println(zone + ": " + r); zmi.getAttributes().addOrChange(r.getName(), r.getValue()); @@ -249,7 +244,7 @@ public class Main { return root; } - private static ZMI createTestHierarchy2() throws ParseException, UnknownHostException { + public static ZMI createTestHierarchy2() throws ParseException, UnknownHostException { ValueContact violet07Contact = createContact("/uw/violet07", (byte)10, (byte)1, (byte)1, (byte)10); ValueContact khaki13Contact = createContact("/uw/khaki13", (byte)10, (byte)1, (byte)1, (byte)38); ValueContact khaki31Contact = createContact("/uw/khaki31", (byte)10, (byte)1, (byte)1, (byte)39); @@ -368,6 +363,7 @@ public class Main { khaki13.getAttributes().add("creation", new ValueTime((Long)null)); khaki13.getAttributes().add("cpu_usage", new ValueDouble(0.1)); khaki13.getAttributes().add("num_cores", new ValueInt(null)); + khaki13.getAttributes().add("num_processes", new ValueInt(107l)); khaki13.getAttributes().add("has_ups", new ValueBoolean(true)); list = Arrays.asList(new Value[] {}); khaki13.getAttributes().add("some_names", new ValueList(list, TypePrimitive.STRING)); diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java index aa0cb64..eb916be 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java @@ -24,6 +24,8 @@ package pl.edu.mimuw.cloudatlas.model; +import java.io.Serializable; + /** * Represents an attribute (without value, name only). *

@@ -32,7 +34,7 @@ package pl.edu.mimuw.cloudatlas.model; *

* This class is immutable. */ -public class Attribute { +public class Attribute implements Serializable { private final String name; /** diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java index 4065ad6..c74c1df 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java @@ -24,6 +24,7 @@ package pl.edu.mimuw.cloudatlas.model; +import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -32,7 +33,7 @@ import java.util.Map.Entry; /** * Represents a map from Attribute to Value. It cannot contain duplicate keys. */ -public class AttributesMap implements Iterable>, Cloneable { +public class AttributesMap implements Iterable>, Cloneable, Serializable { private Map map = new HashMap(); private void checkNulls(Attribute attribute, Value value) { diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java index 986db71..0994cba 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java @@ -24,16 +24,18 @@ package pl.edu.mimuw.cloudatlas.model; +import java.io.Serializable; + /** * A type of a value that may be stored as an attribute. */ -public abstract class Type { +public abstract class Type implements Serializable { /** * A primary type. This is a characteristic that every type has. It can be extended: for instance a collection may * be parameterized with a type of stored values. */ public static enum PrimaryType { - BOOLEAN, CONTACT, DOUBLE, DURATION, INT, LIST, NULL, SET, STRING, TIME, + BOOLEAN, CONTACT, DOUBLE, DURATION, INT, LIST, NULL, SET, STRING, TIME, QUERY } private final PrimaryType primaryType; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java index ab28cb4..ad07c0a 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java @@ -73,6 +73,11 @@ public class TypePrimitive extends Type { */ public static final TypePrimitive TIME = new TypePrimitive(PrimaryType.TIME); + /** + * Query type. + */ + public static final TypePrimitive QUERY = new TypePrimitive(PrimaryType.QUERY); + private TypePrimitive(PrimaryType primaryType) { super(primaryType); switch(primaryType) { @@ -84,6 +89,7 @@ public class TypePrimitive extends Type { case NULL: case STRING: case TIME: + case QUERY: break; default: throw new IllegalArgumentException( diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java index c4054cf..55353c1 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java @@ -26,10 +26,12 @@ package pl.edu.mimuw.cloudatlas.model; import pl.edu.mimuw.cloudatlas.model.Value; +import java.io.Serializable; + /** * A single value stored as an attribute. */ -public abstract class Value { +public abstract class Value implements Serializable { /** * An operation that may be performed on values. */ diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java index 7022bbd..7a74776 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java @@ -248,7 +248,8 @@ public class ValueDuration extends ValueSimple { return new ValueDuration(-getValue()); } - public String toString() { + + private String makeString() { long remainingUnits = getValue(); boolean positive = remainingUnits >= 0; remainingUnits = positive ? remainingUnits : -remainingUnits; @@ -272,7 +273,7 @@ public class ValueDuration extends ValueSimple { public Value convertTo(Type type) { switch(type.getPrimaryType()) { case STRING: - return getValue() == null? ValueString.NULL_STRING : new ValueString(toString()); + return getValue() == null? ValueString.NULL_STRING : new ValueString(makeString()); case DURATION: return this; default: diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java index 8414cc4..c7f1036 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java @@ -24,6 +24,7 @@ package pl.edu.mimuw.cloudatlas.model; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java new file mode 100644 index 0000000..d9cbe4c --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueQuery.java @@ -0,0 +1,70 @@ +package pl.edu.mimuw.cloudatlas.model; + +import java.io.ByteArrayInputStream; + +import pl.edu.mimuw.cloudatlas.interpreter.query.Absyn.Program; +import pl.edu.mimuw.cloudatlas.interpreter.query.parser; +import pl.edu.mimuw.cloudatlas.interpreter.query.Yylex; +import pl.edu.mimuw.cloudatlas.model.Value; + +/** + * A class that holds a CloudAtlas query. + */ +public class ValueQuery extends Value { + // Original source code + private String code; + // Parsed query + private Program query; + /** + * Constructs a new ValueQuery object. + * + * @param name the name of the query + * @param query the code of the query + */ + public ValueQuery(String query) throws Exception { + this.code = query; + Yylex lex = new Yylex(new ByteArrayInputStream(query.getBytes())); + this.query = (new parser(lex)).pProgram(); + } + + private ValueQuery() { + this.code = null; + this.query = null; + } + + @Override + public Type getType() { + return TypePrimitive.QUERY; + } + + @Override + public boolean isNull() { + return query == null || code == null; + } + + public Value isEqual(Value value) { + sameTypesOrThrow(value, Operation.EQUAL); + if(isNull() && value.isNull()) + return new ValueBoolean(true); + else if(isNull() || value.isNull()) + return new ValueBoolean(false); + return new ValueBoolean(code.equals(((ValueQuery)value).code)); + } + + @Override + public Value getDefaultValue() { + return new ValueQuery(); + } + + @Override + public Value convertTo(Type type) { + switch(type.getPrimaryType()) { + case QUERY: + return this; + case STRING: + return isNull() ? ValueString.NULL_STRING : new ValueString(code); + default: + throw new UnsupportedConversionException(getType(), type); + } + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java index 5a560ae..a311c61 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java @@ -41,6 +41,11 @@ import com.esotericsoftware.kryo.io.Output; * references to its father and sons in the tree. */ public class ZMI implements Cloneable { + public class NoSuchZoneException extends Exception { + public NoSuchZoneException(PathName path) { + super("No such zone: " + path); + } + } private final AttributesMap attributes = new AttributesMap(); private final List sons = new ArrayList(); @@ -85,6 +90,26 @@ public class ZMI implements Cloneable { this.father = father; } + public ZMI findDescendant(PathName path) throws NoSuchZoneException { + ZMI descendant = this; + for (String component : path.getComponents()) { + boolean foundNextSon = false; + for (ZMI son : descendant.getSons()) { + if (son.getAttributes().get("name").equals(new ValueString(component))) { + descendant = son; + foundNextSon = true; + break; + } + } + + if (!foundNextSon) { + throw new NoSuchZoneException(path); + } + } + + return descendant; + } + /** * Gets the list of sons of this ZMI. Modifying a value in the returned list will cause an exception. * @@ -170,6 +195,16 @@ public class ZMI implements Cloneable { return attributes.toString(); } + /** + * Gets the PathName representing this zone. + * + * @return a PathName object representing this zone + */ + public PathName getPathName() { + String name = ((ValueString)getAttributes().get("name")).getValue(); + return getFather() == null? PathName.ROOT : getFather().getPathName().levelDown(name); + } + public static ZMI deserialize(InputStream in) { Kryo kryo = new Kryo(); Input kryoInput = new Input(in); diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/AgentTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/AgentTest.java index 24924a7..8c9c495 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/AgentTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/AgentTest.java @@ -4,6 +4,9 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.hasItems; + import java.lang.Runtime; import java.lang.Process; @@ -11,6 +14,8 @@ import java.lang.Thread; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.rmi.registry.LocateRegistry; @@ -19,17 +24,33 @@ import java.rmi.registry.Registry; import java.math.BigDecimal; import pl.edu.mimuw.cloudatlas.api.Api; +import pl.edu.mimuw.cloudatlas.model.AttributesMap; +import pl.edu.mimuw.cloudatlas.model.TypePrimitive; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueDouble; +import pl.edu.mimuw.cloudatlas.model.ValueInt; +import pl.edu.mimuw.cloudatlas.model.ValueList; +import pl.edu.mimuw.cloudatlas.model.ValueNull; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.model.ValueString; +import pl.edu.mimuw.cloudatlas.model.ValueTime; public class AgentTest { private static Process registryProcess; private static Process agentProcess; + private static Registry registry; + private static Api api; + @BeforeClass public static void bindApi() throws Exception { registryProcess = Runtime.getRuntime().exec("./scripts/registry"); Thread.sleep(10000); agentProcess = Runtime.getRuntime().exec("./gradlew runAgent"); Thread.sleep(10000); + + registry = LocateRegistry.getRegistry("localhost"); + api = (Api) registry.lookup("Api"); } @AfterClass @@ -43,15 +64,80 @@ public class AgentTest { } @Test - public void testGetZoneSet() { - try { - Registry registry = LocateRegistry.getRegistry("localhost"); - Api api = (Api) registry.lookup("Api"); - Set set = api.getZoneSet(); - assertEquals(null, set); - } catch (Exception e) { - e.printStackTrace(); - assertTrue(false); - } + public void testGetZoneSet() throws Exception { + Set set = api.getZoneSet(); + assertEquals(8, set.size()); + assertThat(set, hasItems("/")); + assertThat(set, hasItems("/uw")); + assertThat(set, hasItems("/uw/violet07", "/uw/khaki31", "/uw/khaki13")); + assertThat(set, hasItems("/pjwstk")); + assertThat(set, hasItems("/pjwstk/whatever01", "/pjwstk/whatever02")); + } + + @Test + public void testRootGetZoneAttributeValue() throws Exception { + AttributesMap rootAttributes = api.getZoneAttributeValues("/"); + assertEquals(new ValueInt(0l), rootAttributes.get("level")); + assertEquals(ValueNull.getInstance(), rootAttributes.get("name")); + } + + @Test + public void testIntermediateGetZoneAttributeValue() throws Exception { + AttributesMap attributes = api.getZoneAttributeValues("/uw"); + assertEquals(new ValueInt(1l), attributes.get("level")); + assertEquals(new ValueString("uw"), attributes.get("name")); + } + + @Test + public void testLeafGetZoneAttributeValue() throws Exception { + AttributesMap attributes = api.getZoneAttributeValues("/pjwstk/whatever01"); + assertEquals(new ValueInt(2l), attributes.get("level")); + assertEquals(new ValueString("whatever01"), attributes.get("name")); + assertEquals(new ValueString("/pjwstk/whatever01"), attributes.get("owner")); + assertEquals(new ValueTime("2012/11/09 21:12:00.000"), attributes.get("timestamp")); + assertEquals(new ValueInt(1l), attributes.get("cardinality")); + assertEquals(new ValueTime("2012/10/18 07:03:00.000"), attributes.get("creation")); + assertEquals(new ValueDouble(0.1), attributes.get("cpu_usage")); + assertEquals(new ValueInt(7l), attributes.get("num_cores")); + assertEquals(new ValueInt(215l), attributes.get("num_processes")); + + List phpModules = new ArrayList(); + phpModules.add(new ValueString("rewrite")); + assertEquals(new ValueList(phpModules, TypePrimitive.STRING), attributes.get("php_modules")); + } + + @Test + public void testInstallQuery() throws Exception { + String name = "&query"; + String queryCode = "SELECT 1 AS one"; + api.installQuery(name, queryCode); + AttributesMap attributes = api.getZoneAttributeValues("/pjwstk"); + assertEquals(new ValueQuery(queryCode), attributes.get(name)); + } + + @Test + public void testUninstallQuery() throws Exception { + String name = "&query"; + String queryCode = "SELECT 1 AS one"; + api.installQuery(name, queryCode); + api.uninstallQuery(name); + AttributesMap attributes = api.getZoneAttributeValues("/pjwstk"); + assertNull(attributes.getOrNull(name)); + } + + @Test + public void testSetAttributeValueChange() throws Exception { + Value numProcesses = new ValueInt(42l); + api.setAttributeValue("/uw/khaki13", "num_processes", numProcesses); + AttributesMap attributes = api.getZoneAttributeValues("/uw/khaki13"); + assertEquals(numProcesses, attributes.get("num_processes")); + } + + @Test + public void testSetAttributeValueAdd() throws Exception { + Value numProcesses = new ValueInt(42l); + api.setAttributeValue("/uw/khaki13", "an_attribute", numProcesses); + AttributesMap attributes = api.getZoneAttributeValues("/uw/khaki13"); + assertEquals(numProcesses, attributes.get("an_attribute")); } } -- cgit v1.2.3