MapSyncRpcHandler extends AbstractRpcInvocationHandler { @Override protected Optional<Runnable> handleNode(StateNode node, JsonObject invocationJson) { assert invocationJson.hasKey(JsonConstants.RPC_FEATURE); assert invocationJson.hasKey(JsonConstants.RPC_PROPERTY); assert invocationJson.hasKey(JsonConstants.RPC_PROPERTY_VALUE); int featureId = (int) invocationJson .getNumber(JsonConstants.RPC_FEATURE); Class<? extends NodeFeature> feature = NodeFeatureRegistry .getFeature(featureId); assert NodeMap.class.isAssignableFrom(feature); assert ElementPropertyMap.class.equals(feature); boolean isEnabled = node.isEnabled(); List<DisabledUpdateMode> seenUpdateModes = new ArrayList<>(); String property = invocationJson.getString(JsonConstants.RPC_PROPERTY); if (node.hasFeature(ElementListenerMap.class)) { DisabledUpdateMode eventMode = node .getFeature(ElementListenerMap.class) .getPropertySynchronizationMode(property); if (eventMode != null) { seenUpdateModes.add(eventMode); } } DisabledUpdateMode updateMode = seenUpdateModes.stream() .reduce(DisabledUpdateMode::mostPermissive).orElse(null); if (isEnabled) { return enqueuePropertyUpdate(node, invocationJson, feature, property); } else if (DisabledUpdateMode.ALWAYS.equals(updateMode)) { LoggerFactory.getLogger(MapSyncRpcHandler.class).trace( "Property update request for disabled element is received from the client side. " + "Change will be applied since the property '{}' always allows its update.", property); return enqueuePropertyUpdate(node, invocationJson, feature, property); } else { LoggerFactory.getLogger(MapSyncRpcHandler.class).warn( "Property update request for disabled element is received from the client side. " + "The property is '{}'. Request is ignored.", property); } return Optional.empty(); }  @Override String getRpcType();  }
@Test(expected = IllegalArgumentException.class) public void noSyncPropertiesFeature_noExplicitAllow_throws() { StateNode noSyncProperties = new StateNode(ElementPropertyMap.class); ElementPropertyMap map = noSyncProperties .getFeature(ElementPropertyMap.class); new MapSyncRpcHandler().handleNode(noSyncProperties, createSyncPropertyInvocation(noSyncProperties, TEST_PROPERTY, NEW_VALUE)); Assert.assertEquals(NEW_VALUE, map.getProperty(TEST_PROPERTY)); }
@Test public void propertyIsNotExplicitlyAllowed_throwsWithElementTagInfo() { exception.expect(IllegalArgumentException.class); exception.expectMessage(CoreMatchers.allOf( CoreMatchers.containsString("Element with tag 'foo'"), CoreMatchers.containsString("'" + TEST_PROPERTY + "'"))); Element element = new Element("foo"); new MapSyncRpcHandler().handleNode(element.getNode(), createSyncPropertyInvocation(element.getNode(), TEST_PROPERTY, NEW_VALUE)); }
@Test public void propertyIsNotExplicitlyAllowed_throwsWithComponentInfo() { exception.expect(IllegalArgumentException.class); exception.expectMessage(CoreMatchers.allOf( CoreMatchers.containsString( "Component " + TestComponent.class.getName()), CoreMatchers.containsString("'" + TEST_PROPERTY + "'"))); TestComponent component = new TestComponent(); Element element = component.getElement(); new MapSyncRpcHandler().handleNode(element.getNode(), createSyncPropertyInvocation(element.getNode(), TEST_PROPERTY, NEW_VALUE)); }
@Test public void propertyIsNotExplicitlyAllowed_subproperty_throwsWithComponentInfo() { exception.expect(IllegalArgumentException.class); exception.expectMessage(CoreMatchers.allOf( CoreMatchers.containsString( "Component " + TestComponent.class.getName()), CoreMatchers.containsString("'" + TEST_PROPERTY + "'"))); TestComponent component = new TestComponent(); Element element = component.getElement(); StateNode node = element.getNode(); ElementPropertyMap propertyMap = node .getFeature(ElementPropertyMap.class) .resolveModelMap("foo.bar"); new MapSyncRpcHandler().handleNode(propertyMap.getNode(), createSyncPropertyInvocation(propertyMap.getNode(), TEST_PROPERTY, NEW_VALUE)); }
@Test public void handleNode_callsElementPropertyMapDeferredUpdateFromClient() { AtomicInteger deferredUpdateInvocations = new AtomicInteger(); AtomicReference<String> deferredKey = new AtomicReference<>(); StateNode node = new StateNode(ElementPropertyMap.class) { private ElementPropertyMap map = new ElementPropertyMap(this) { @Override public Runnable deferredUpdateFromClient(String key, Serializable value) { deferredUpdateInvocations.incrementAndGet(); deferredKey.set(key); return () -> { }; } }; @Override public <F extends NodeFeature> F getFeature(Class<F> featureType) { if (featureType.equals(ElementPropertyMap.class)) { return featureType.cast(map); } return super.getFeature(featureType); } }; new MapSyncRpcHandler().handleNode(node, createSyncPropertyInvocation(node, TEST_PROPERTY, NEW_VALUE)); Assert.assertEquals(1, deferredUpdateInvocations.get()); Assert.assertEquals(TEST_PROPERTY, deferredKey.get()); }