/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hawkular.inventory.bus;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import javax.jms.JMSException;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import org.hawkular.bus.common.MessageProcessor;
import org.hawkular.bus.common.consumer.ConsumerConnectionContext;
import org.hawkular.inventory.api.Action;
import org.hawkular.inventory.api.Query;
import org.hawkular.inventory.api.filters.With;
import org.hawkular.inventory.api.model.DataEntity;
import org.hawkular.inventory.api.model.Environment;
import org.hawkular.inventory.api.model.Feed;
import org.hawkular.inventory.api.model.Metric;
import org.hawkular.inventory.api.model.MetricDataType;
import org.hawkular.inventory.api.model.MetricType;
import org.hawkular.inventory.api.model.MetricUnit;
import org.hawkular.inventory.api.model.Relationship;
import org.hawkular.inventory.api.model.Resource;
import org.hawkular.inventory.api.model.ResourceType;
import org.hawkular.inventory.api.model.StructuredData;
import org.hawkular.inventory.api.model.Tenant;
import org.hawkular.inventory.api.paging.Order;
import org.hawkular.inventory.api.paging.PageContext;
import org.hawkular.inventory.api.paging.Pager;
import org.hawkular.inventory.bus.api.DataEntityEvent;
import org.hawkular.inventory.bus.api.EnvironmentEvent;
import org.hawkular.inventory.bus.api.FeedEvent;
import org.hawkular.inventory.bus.api.InventoryEvent;
import org.hawkular.inventory.bus.api.InventoryEventMessageListener;
import org.hawkular.inventory.bus.api.InventoryQueryRequestMessage;
import org.hawkular.inventory.bus.api.InventoryQueryResponseMessage;
import org.hawkular.inventory.bus.api.MetricEvent;
import org.hawkular.inventory.bus.api.MetricTypeEvent;
import org.hawkular.inventory.bus.api.RelationshipEvent;
import org.hawkular.inventory.bus.api.ResourceEvent;
import org.hawkular.inventory.bus.api.ResourceTypeEvent;
import org.hawkular.inventory.bus.api.ResultSet;
import org.hawkular.inventory.bus.api.TenantEvent;
import org.hawkular.inventory.paths.CanonicalPath;
import org.hawkular.inventory.paths.DataRole;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
//import org.hawkular.bus.common.test.SimpleTestListener;
//import org.hawkular.bus.common.test.VMEmbeddedBrokerWrapper;
/**
* @author Pavol Loffay
* @since 0.2.0
*/
public class BusTest {
private static final String PROP_KEY = "p1";
private static final String PROP_VALUE = "prop1";
private static final Map<String, Object> objectProperties = new HashMap<>();
private static final Map<String, String> messageHeaders = new HashMap<>();
@BeforeClass
public static void init() {
objectProperties.put(PROP_KEY, PROP_VALUE);
messageHeaders.put(PROP_KEY, PROP_VALUE);
}
@Test
public void messagesSerializationTest() {
Tenant tenant = new Tenant(CanonicalPath.fromString("/t;c"), "t", objectProperties);
MetricType metricType = new MetricType(CanonicalPath.fromString("/t;t/mt;mt"), null, null, null,
MetricUnit.MINUTES,
MetricDataType.GAUGE);
ResourceType resourceType = new ResourceType(CanonicalPath.fromString("/t;t/rt;rt"), null, null, null,
objectProperties);
Tenant tenant2 = new Tenant(CanonicalPath.fromString("/t;t"), null);
TenantEvent tenantEvent = new TenantEvent(Action.Enumerated.CREATED, tenant);
EnvironmentEvent environmentEvent = new EnvironmentEvent(Action.Enumerated.CREATED,
tenant2,
new Environment(CanonicalPath.fromString("/t;t/e;e"), null, objectProperties));
FeedEvent feedEvent = new FeedEvent(Action.Enumerated.UPDATED,
tenant2,
new Feed(CanonicalPath.fromString("/t;t/f;f"), null, null, null, objectProperties));
MetricEvent metricEvent = new MetricEvent(Action.Enumerated.DELETED,
tenant2,
new Metric(CanonicalPath.fromString("/t;t/e;e/m;m"), null, null, null, metricType, objectProperties));
MetricTypeEvent metricTypeEvent = new MetricTypeEvent(Action.Enumerated.COPIED, tenant2, metricType);
RelationshipEvent relationshipEvent = new RelationshipEvent(Action.Enumerated.REGISTERED,
tenant2,
new Relationship("id", "rel1", CanonicalPath.fromString("/t;t"), CanonicalPath.fromString("/t;t/e;e")
, objectProperties));
ResourceEvent resourceEvent = new ResourceEvent(Action.Enumerated.UPDATED,
tenant2,
new Resource(CanonicalPath.fromString("/t;t/e;e/r;r"), null, null, null, resourceType));
ResourceTypeEvent resourceTypeEvent = new ResourceTypeEvent(Action.Enumerated.COPIED, tenant2, resourceType);
DataEntityEvent dataEntityEvent = new DataEntityEvent(Action.Enumerated.DELETED,
tenant2,
new DataEntity(resourceType.getPath(), DataRole.ResourceType.configurationSchema,
StructuredData.get().undefined(), "hash", null, null));
String tenantJSON = tenantEvent.toJSON();
String envJSON = environmentEvent.toJSON();
String feedJSON = feedEvent.toJSON();
String metricJSON = metricEvent.toJSON();
String metricTypeJSON = metricTypeEvent.toJSON();
String relationshipJSON = relationshipEvent.toJSON();
String resourceJSON = resourceEvent.toJSON();
String resourceTypeJSON = resourceTypeEvent.toJSON();
String dataEntityJSON = dataEntityEvent.toJSON();
assertThat(tenantEvent.getObject(),
is(equalTo(TenantEvent.fromJSON(tenantJSON, TenantEvent.class).getObject())));
assertThat(environmentEvent.getObject(),
is(equalTo(EnvironmentEvent.fromJSON(envJSON, EnvironmentEvent.class).getObject())));
assertThat(feedEvent.getObject(),
is(equalTo(feedEvent.fromJSON(feedJSON, FeedEvent.class).getObject())));
assertThat(metricEvent.getObject(),
is(equalTo(MetricEvent.fromJSON(metricJSON, MetricEvent.class).getObject())));
assertThat(metricTypeEvent.getObject(),
is(equalTo(MetricTypeEvent.fromJSON(metricTypeJSON, MetricTypeEvent.class).getObject())));
assertThat(relationshipEvent.getObject(),
is(equalTo(RelationshipEvent.fromJSON(relationshipJSON, RelationshipEvent.class).getObject())));
assertThat(resourceEvent.getObject(),
is(equalTo(ResourceEvent.fromJSON(resourceJSON, ResourceEvent.class).getObject())));
assertThat(resourceTypeEvent.getObject(),
is(equalTo(ResourceTypeEvent.fromJSON(resourceTypeJSON, ResourceTypeEvent.class).getObject())));
assertThat(dataEntityEvent.getObject(), is(equalTo(DataEntityEvent.fromJSON(dataEntityJSON,
DataEntityEvent.class).getObject())));
}
@Test
public void createTenantEventFromJSON() {
Tenant tenant = new Tenant(CanonicalPath.fromString("/t;c"), null, objectProperties);
TenantEvent tenantEvent = new TenantEvent(Action.Enumerated.CREATED, tenant);
String tenantJSON = "{\"action\":\"CREATED\",\"object\":{\"path\":\"/t;c\",\"properties\":{\"p1\":\"prop1\"}}}";
TenantEvent tenantEventFromJSON = TenantEvent.fromJSON(tenantJSON, TenantEvent.class);
assertThat(tenantEvent.getObject().getPath(), is(equalTo(tenantEventFromJSON.getObject().getPath())));
}
@Test
public void createMetricEventFromJSON() {
Tenant tenant = new Tenant(CanonicalPath.fromString("/t;t"), null);
MetricType metricType = new MetricType(CanonicalPath.fromString("/t;t/mt;mt"), null, null, null,
MetricUnit.MINUTES,
MetricDataType.GAUGE);
MetricEvent metricEvent = new MetricEvent(Action.Enumerated.DELETED,
tenant,
new Metric(CanonicalPath.fromString("/t;t/e;e/m;m"), null, null, null, metricType, objectProperties));
String metricJSON = "{\"action\":\"DELETED\",\"object\":{\"path\":\"/t;t/e;e/m;m\",\"type\"" +
":{\"path\":\"/t;t/mt;mt\",\"properties\":null,\"unit\":\"MINUTES\",\"type\":\"GAUGE\"}," +
"\"properties\":{}}}";
MetricEvent metricEventFromJSON = MetricEvent.fromJSON(metricJSON, MetricEvent.class);
assertThat(metricEvent.getObject().getPath(), is(equalTo(metricEventFromJSON.getObject().getPath())));
}
@Test
public void sendTenantEvent() throws Exception {
// // this is the same test as testFilter except the headers will be put directly in BasicMessage
// ConnectionContextFactory consumerFactory = null;
// ConnectionContextFactory producerFactory = null;
//
// VMEmbeddedBrokerWrapper broker = new VMEmbeddedBrokerWrapper();
// broker.start();
//
// // set up message to send
// Tenant tenantToSend = new Tenant(CanonicalPath.fromString("/t;c"), objectProperties);
// // send one that will match the selector
// TenantEvent tenantEventToSend = new TenantEvent(Action.Enumerated.CREATED, tenantToSend);
// tenantEventToSend.setHeaders(messageHeaders);
//
// try {
// String brokerURL = broker.getBrokerURL();
// Endpoint endpoint = new Endpoint(Endpoint.Type.QUEUE, "testq");
//
// // mimic server-side
// consumerFactory = new ConnectionContextFactory(brokerURL);
// ConsumerConnectionContext consumerContext = consumerFactory.createConsumerConnectionContext(endpoint);
// // tenant listener
// SimpleTestListener<TenantEvent> tenantListener = new SimpleTestListener<TenantEvent>(TenantEvent.class);
// MessageProcessor serverSideTenantProcessor = new MessageProcessor();
// serverSideTenantProcessor.listen(consumerContext, tenantListener);
//
// // mimic client side
// producerFactory = new ConnectionContextFactory(brokerURL);
// ProducerConnectionContext producerContext = producerFactory.createProducerConnectionContext(endpoint);
// MessageProcessor clientSideProcessor = new MessageProcessor();
// clientSideProcessor.send(producerContext, tenantEventToSend);
//
// // wait for the message to flow - we should get it now
// tenantListener.waitForMessage(3);
// TenantEvent tenantReceivedMsg = tenantListener.getReceivedMessage();
// assertEquals("Should have received the message",
// tenantReceivedMsg.getObject().getProperties().get(PROP_KEY), PROP_VALUE);
// assertNotNull(tenantReceivedMsg.getHeaders());
// assertEquals(2, tenantReceivedMsg.getHeaders().size()); // the other one is bus api's own classname header
// assertEquals(PROP_VALUE, tenantReceivedMsg.getHeaders().get(PROP_KEY));
// } finally {
// // close everything
// producerFactory.close();
// consumerFactory.close();
// broker.stop();
// }
}
@Test
public void sendMetricEvent() throws Exception {
// // this is the same test as testFilter except the headers will be put directly in BasicMessage
// ConnectionContextFactory consumerFactory = null;
// ConnectionContextFactory producerFactory = null;
//
// VMEmbeddedBrokerWrapper broker = new VMEmbeddedBrokerWrapper();
// broker.start();
//
// // set up message to send
// Tenant tenant = new Tenant(CanonicalPath.fromString("/t;t"));
// MetricType metricType = new MetricType(CanonicalPath.fromString("/t;t/mt;mt"), MetricUnit.MINUTES,
// MetricDataType.GAUGE);
// MetricEvent metricEventToSend = new MetricEvent(Action.Enumerated.DELETED,
// tenant,
// new Metric(CanonicalPath.fromString("/t;t/e;e/m;m"), metricType, objectProperties));
// metricEventToSend.setHeaders(messageHeaders);
//
// try {
// String brokerURL = broker.getBrokerURL();
// Endpoint endpoint = new Endpoint(Endpoint.Type.QUEUE, "testq");
//
// // mimic server-side
// consumerFactory = new ConnectionContextFactory(brokerURL);
// ConsumerConnectionContext consumerContext = consumerFactory.createConsumerConnectionContext(endpoint);
//
// // metric listener
// SimpleTestListener<MetricEvent> metricListener = new SimpleTestListener<MetricEvent>(MetricEvent.class);
// MessageProcessor serverSideMetricProcessor = new MessageProcessor();
// serverSideMetricProcessor.listen(consumerContext, metricListener);
//
// // mimic client side
// producerFactory = new ConnectionContextFactory(brokerURL);
// ProducerConnectionContext producerContext = producerFactory.createProducerConnectionContext(endpoint);
// MessageProcessor clientSideProcessor = new MessageProcessor();
//
// //send data
// clientSideProcessor.send(producerContext, metricEventToSend);
// //receive data
// metricListener.waitForMessage(3);
// MetricEvent metricReceivedMsg = metricListener.getReceivedMessage();
//
// assertEquals("Should have received the message",
// metricReceivedMsg.getObject().getProperties().get(PROP_KEY), PROP_VALUE);
// assertNotNull(metricReceivedMsg.getHeaders());
// assertEquals(2, metricReceivedMsg.getHeaders().size()); // the other one is bus api's own classname header
// assertEquals(metricType.getType(), metricReceivedMsg.getObject().getType().getType());
// assertEquals(PROP_VALUE, metricReceivedMsg.getHeaders().get(PROP_KEY));
// } finally {
// // close everything
// producerFactory.close();
// consumerFactory.close();
// broker.stop();
// }
}
@Test
public void testBusIntegrationHeaderInjection() throws Exception {
// VMEmbeddedBrokerWrapper broker = new VMEmbeddedBrokerWrapper();
//
// System.setProperty(Context.INITIAL_CONTEXT_FACTORY, ContextFactory.class.getName());
//
// InitialContext namingContext = new InitialContext();
//
// Inventory inventory = ServiceLoader.load(Inventory.class).iterator().next();
// inventory.initialize(new Configuration(new RandomUUIDFeedIdStrategy(), null, Collections.emptyMap()));
//
// try (ConnectionContextFactory producerFactory = new ConnectionContextFactory(broker.getBrokerURL());
// ConnectionContextFactory consumerFactory = new ConnectionContextFactory(broker.getBrokerURL())) {
//
// broker.start();
//
// String topicName =
// org.hawkular.inventory.bus.Configuration.Property.INVENTORY_CHANGES_TOPIC_NAME.getDefaultValue();
// Endpoint topic = new Endpoint(Endpoint.Type.TOPIC, topicName);
//
// namingContext.bind(
// org.hawkular.inventory.bus.Configuration.Property.CONNECTION_FACTORY_JNDI_NAME.getDefaultValue(),
// new TopicConnectionFactory() {
// @Override
// public TopicConnection createTopicConnection() throws JMSException {
// return (TopicConnection) producerFactory.createProducerConnectionContext(topic)
// .getConnection();
// }
//
// @Override
// public TopicConnection createTopicConnection(String userName,
// String password) throws JMSException {
// return createTopicConnection();
// }
//
// @Override
// public Connection createConnection() throws JMSException {
// return createTopicConnection();
// }
//
// @Override
// public Connection createConnection(String userName, String password) throws JMSException {
// return createTopicConnection(userName, password);
// }
// });
//
// BusIntegration busIntegration = new BusIntegration(inventory);
// busIntegration.configure(org.hawkular.inventory.bus.Configuration.builder().build());
// busIntegration.start();
//
//
// ConsumerConnectionContext consumerContext = consumerFactory.createConsumerConnectionContext(topic);
//
// testHeaders(consumerContext, TenantEvent.class,
// () -> inventory.tenants().create(Tenant.Blueprint.builder().withId("t").build()),
// (headers) -> {
// assertThat(headers.size(), is(equalTo(4))); // the 4th is the bus API's own classname header
// assertThat(headers.get("path"), is(equalTo(CanonicalPath.of().tenant("t").get().toString())));
// assertThat(headers.get("action"), is(equalTo(Action.Enumerated.CREATED.name())));
// assertThat(headers.get("entityType"), is(equalTo("tenant")));
// });
//
// testHeaders(consumerContext, ResourceTypeEvent.class,
// () -> inventory.tenants().get("t").resourceTypes().create(ResourceType.Blueprint.builder()
// .withId("rt").build()),
// (headers) -> {
// assertThat(headers.size(), is(equalTo(4))); // the 4th is the bus API's own classname header
// assertThat(headers.get("path"), is(equalTo(CanonicalPath.of().tenant("t")
// .resourceType("rt").get().toString())));
// assertThat(headers.get("action"), is(equalTo(Action.Enumerated.CREATED.name())));
// assertThat(headers.get("entityType"), is(equalTo("resourceType")));
// });
//
// //data entity events declare one more header, so we need to check for that, too
// testHeaders(consumerContext, DataEntityEvent.class,
// () -> inventory.tenants().get("t").resourceTypes().get("rt").data()
// .create(DataEntity.Blueprint.<ResourceTypes.DataRole>builder()
// .withRole(ResourceTypes.DataRole.configurationSchema).build()),
// (headers) -> {
// assertThat(headers.size(), is(equalTo(5))); // the 5th is the bus API's own classname header
// assertThat(headers.get("path"), is(equalTo(CanonicalPath.of().tenant("t")
// .resourceType("rt").data(ResourceTypes.DataRole.configurationSchema).get()
// .toString())));
// assertThat(headers.get("action"), is(equalTo(Action.Enumerated.CREATED.name())));
// assertThat(headers.get("entityType"), is(equalTo("dataEntity")));
// assertThat(headers.get("dataRole"),
// is(equalTo(ResourceTypes.DataRole.configurationSchema.name())));
// });
// } finally {
// broker.stop();
// namingContext.close();
// inventory.close();
// }
}
@Test
public void testQueryRequestSerialization() throws IOException {
Query allTenants = Query.filter().with(With.type(Tenant.class)).get();
InventoryQueryRequestMessage<Tenant> requestMessage = new InventoryQueryRequestMessage<>(allTenants,
Tenant.class, Pager.single());
String json = requestMessage.toJSON();
InventoryQueryRequestMessage messageFromJson = InventoryQueryRequestMessage.fromJSON(json,
InventoryQueryRequestMessage.class);
assertThat(requestMessage.getQuery(), is(equalTo(messageFromJson.getQuery())));
}
@Test
public void testQueryResponseSerialization() {
List<Tenant> tenantList = Arrays.asList(new Tenant("name", CanonicalPath.fromString("/t;tenant"), null));
ResultSet<Tenant> resultSet = new ResultSet<>(tenantList, new PageContext(1, 15, Order.unspecified()), 156);
InventoryQueryResponseMessage<Tenant> response = new InventoryQueryResponseMessage<>(resultSet, Tenant.class);
String json = response.toJSON();
InventoryQueryResponseMessage<Tenant> responseFromJson = InventoryQueryResponseMessage.fromJSON(json,
InventoryQueryResponseMessage.class);
assertThat(responseFromJson.getResult().getPageContext(), is(equalTo(response.getResult().getPageContext())));
}
private void testHeaders(ConsumerConnectionContext consumerContext, Class<? extends InventoryEvent<?>> eventClass,
Runnable inventoryAction, Consumer<Map<String, String>> assertions)
throws JMSException, InterruptedException {
List<InventoryEvent<?>> receivedMessages = new ArrayList<>();
InventoryEventMessageListener listener = new InventoryEventMessageListener() {
@Override
protected void onBasicMessage(InventoryEvent<?> inventoryEvent) {
receivedMessages.add(inventoryEvent);
}
};
MessageProcessor receiver = new MessageProcessor();
receiver.listen(consumerContext, listener);
inventoryAction.run();
Thread.sleep(3000);
boolean checked = false;
for (InventoryEvent<?> e : receivedMessages) {
if (eventClass.isAssignableFrom(e.getClass())) {
assertions.accept(e.getHeaders());
checked = true;
}
}
if (!checked) {
Assert.fail("No event of type " + eventClass + " received. We obtained: " + receivedMessages);
}
}
public static class ContextFactory implements InitialContextFactory {
@Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
return new MapContext();
}
}
public static class MapContext implements Context {
private static final Map<String, Object> objects = new Hashtable<>();
@Override
public Object lookup(Name name) throws NamingException {
return lookup(name.toString());
}
@Override
public Object lookup(String name) throws NamingException {
return objects.get(name);
}
@Override
public void bind(Name name, Object obj) throws NamingException {
bind(name.toString(), obj);
}
@Override
public void bind(String name, Object obj) throws NamingException {
objects.put(name, obj);
}
@Override
public void rebind(Name name, Object obj) throws NamingException {
rebind(name.toString(), obj);
}
@Override
public void rebind(String name, Object obj) throws NamingException {
objects.put(name, obj);
}
@Override
public void unbind(Name name) throws NamingException {
unbind(name.toString());
}
@Override
public void unbind(String name) throws NamingException {
objects.remove(name);
}
@Override
public void rename(Name oldName, Name newName) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public void rename(String oldName, String newName) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public void destroySubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public void destroySubcontext(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Context createSubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Context createSubcontext(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Object lookupLink(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Object lookupLink(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NameParser getNameParser(Name name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public NameParser getNameParser(String name) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Name composeName(Name name, Name prefix) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public String composeName(String name, String prefix) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Object addToEnvironment(String propName, Object propVal) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Object removeFromEnvironment(String propName) throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public Hashtable<?, ?> getEnvironment() throws NamingException {
throw new UnsupportedOperationException();
}
@Override
public void close() throws NamingException {
}
@Override
public String getNameInNamespace() throws NamingException {
throw new UnsupportedOperationException();
}
}
}