/** * Copyright 2016-2017 Sixt GmbH & Co. Autovermietung KG * 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 com.sixt.service.framework.servicetest.eventing; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.protobuf.Message; import com.sixt.service.framework.kafka.EventReceivedCallback; import com.sixt.service.framework.kafka.KafkaSubscriber; import com.sixt.service.framework.kafka.KafkaSubscriberFactory; import com.sixt.service.framework.kafka.KafkaTopicInfo; import com.sixt.service.framework.protobuf.ProtobufUtil; import com.sixt.service.framework.util.Sleeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.*; import java.util.function.Predicate; @Singleton public class ServiceTestEventHandler implements EventReceivedCallback<String> { private static final Logger logger = LoggerFactory.getLogger(ServiceTestEventHandler.class); private static final String GROUP_ID = "service-integration-test"; private static final int TIMEOUT = 30; private final static int POLL_TIME = 200; private final static int CORE_POOL_SIZE = 1; private final static int MAX_POOL_SIZE = 1; private final static int KEEP_ALIVE_TIME = 15; private final KafkaSubscriberFactory<String> subscriberFactory; @VisibleForTesting protected KafkaSubscriber kafkaSubscriber; private ConcurrentLinkedQueue<JsonObject> readEvents = new ConcurrentLinkedQueue<>(); @Inject public ServiceTestEventHandler(KafkaSubscriberFactory<String> kafkaFactory) { this.subscriberFactory = kafkaFactory; } public void initialize(String topic) { this.kafkaSubscriber = subscriberFactory.newBuilder(topic, this) .withPollTime(POLL_TIME) .withGroupId(GROUP_ID) .withThreadPool(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME) .build(); } @Override public void eventReceived(String message, KafkaTopicInfo topicInfo) { if (message == null) { logger.debug("Received kafka message from topic {} with null body", topicInfo.getTopic()); } else { JsonObject jsonObject = new JsonParser().parse(message).getAsJsonObject(); readEvents.add(jsonObject); kafkaSubscriber.consume(topicInfo); } } public void clearReadEvents() { readEvents.clear(); } public List<JsonObject> getAllJsonEvents() { List<JsonObject> foundEvents = new ArrayList<>(); while (!readEvents.isEmpty()) { JsonObject poll = readEvents.poll(); if (poll != null) { foundEvents.add(poll); } } logger.info("Found {} events", foundEvents.size()); return foundEvents; } public <TYPE extends Message> List<TYPE> getEventsOfType(String eventName, Class<TYPE> eventClass) { List<TYPE> foundEvents = new ArrayList<>(); if (eventName != null && eventClass != null) { List<JsonObject> capturedEvents = Arrays.asList(readEvents.toArray(new JsonObject[0])); for (JsonObject event : capturedEvents) { logger.info("Found event: " + event.toString()); if (EventUtils.getEventName(event).equals(eventName)) { foundEvents.add(ProtobufUtil.jsonToProtobuf(event.toString(), eventClass)); readEvents.remove(event); } } } else { logger.error("Event name or event class is null"); } return foundEvents; } public <TYPE extends Message> List<TYPE> getEventsOfType(Class<TYPE> eventClass) { List<TYPE> foundEvents = new ArrayList<>(); if (eventClass != null) { List<JsonObject> capturedEvents = Arrays.asList(readEvents.toArray(new JsonObject[0])); for (JsonObject event : capturedEvents) { logger.info("Found event: " + event.toString()); if (EventUtils.getEventName(event).equals(eventClass.getSimpleName())) { foundEvents.add(ProtobufUtil.jsonToProtobuf(event.toString(), eventClass)); readEvents.remove(event); } } } else { logger.error("Event class is null"); } return foundEvents; } @SuppressWarnings("unchecked") public <T>T getEvent(String eventName, Class eventClass, Predicate<T> predicate, long timeout){ long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < timeout){ //the reversing is for starting with the newest events List<T> events = Lists.reverse(getEventsOfType(eventName, eventClass)); for (T event: events) { if(predicate.test(event)){ return event; } } new Sleeper().sleepNoException(10); } logger.error("The event {} is not found after {} ms", eventName, timeout); return null; } public Map<String, Message> getExpectedEvents(Map<String, Class> expectedEvents) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Map<String, Message>> future = executor.submit(new EventFinder(this, expectedEvents)); Map<String, Message> foundEvents = new HashMap<>(); try { foundEvents = future.get(TIMEOUT, TimeUnit.SECONDS); } catch (Exception ex) { logger.error("Could not find expected events: " + expectedEvents.keySet(), ex); future.cancel(true); } executor.shutdownNow(); // We clear the expected events to be ready for the next testcase expectedEvents.clear(); return foundEvents; } }