/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.lens.server.query;
import static org.testng.Assert.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.lens.api.LensSessionHandle;
import org.apache.lens.api.query.QueryHandle;
import org.apache.lens.api.query.QueryStatus;
import org.apache.lens.server.EventServiceImpl;
import org.apache.lens.server.LensServerConf;
import org.apache.lens.server.LensServices;
import org.apache.lens.server.api.LensConfConstants;
import org.apache.lens.server.api.error.LensException;
import org.apache.lens.server.api.events.AsyncEventListener;
import org.apache.lens.server.api.events.LensEvent;
import org.apache.lens.server.api.events.LensEventListener;
import org.apache.lens.server.api.events.LensEventService;
import org.apache.lens.server.api.query.events.*;
import org.apache.lens.server.api.session.SessionClosed;
import org.apache.lens.server.api.session.SessionExpired;
import org.apache.lens.server.api.session.SessionOpened;
import org.apache.lens.server.api.session.SessionRestored;
import org.apache.lens.server.query.QueryExecutionServiceImpl.QueryStatusLogger;
import org.apache.lens.server.stats.event.query.QueryExecutionStatistics;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import lombok.extern.slf4j.Slf4j;
/**
* The Class TestEventService.
*/
@Test(groups = "unit-test")
@Slf4j
public class TestEventService {
/** The service. */
EventServiceImpl service;
/** The generic event listener. */
GenericEventListener genericEventListener;
/** The failed listener. */
MockFailedListener failedListener;
/** The queue position change listener. */
MockQueuePositionChange queuePositionChangeListener;
/** The ended listener. */
MockEndedListener endedListener;
/** the session opened listener */
MockerSessionOpened sessionOpenedListener;
/** the session closed listener */
MockerSessionClosed sessionClosedListener;
/** the session expired listener */
MockerSessionExpired sessionExpiredListner;
/** the session restored listener */
MockerSessionRestored sessionRestoredListener;
/** The latch. */
CountDownLatch latch;
/**
* The listener interface for receiving genericEvent events. The class that is interested in processing a genericEvent
* event implements this interface, and the object created with that class is registered with a component using the
* component's <code>addGenericEventListener<code> method. When
* the genericEvent event occurs, that object's appropriate
* method is invoked.
*
*/
class GenericEventListener extends AsyncEventListener<LensEvent> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.AsyncEventListener#process(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void process(LensEvent event) {
processed = true;
latch.countDown();
log.info("LensEvent: {}", event.getEventId());
}
}
/**
* The listener interface for receiving mockFailed events. The class that is interested in processing a mockFailed
* event implements this interface, and the object created with that class is registered with a component using the
* component's <code>addMockFailedListener<code> method. When
* the mockFailed event occurs, that object's appropriate
* method is invoked.
*
*/
class MockFailedListener implements LensEventListener<QueryFailed> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(QueryFailed change) throws LensException {
processed = true;
latch.countDown();
log.info("Query Failed event: {}", change);
}
}
/**
* The listener interface for receiving mockEnded events. The class that is interested in processing a mockEnded event
* implements this interface, and the object created with that class is registered with a component using the
* component's <code>addMockEndedListener<code> method. When
* the mockEnded event occurs, that object's appropriate
* method is invoked.
*
*/
class MockEndedListener implements LensEventListener<QueryEnded> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(QueryEnded change) throws LensException {
processed = true;
latch.countDown();
log.info("Query ended event: {}", change);
}
}
/**
* The Class MockQueuePositionChange.
*/
class MockQueuePositionChange implements LensEventListener<QueuePositionChange> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(QueuePositionChange change) throws LensException {
processed = true;
latch.countDown();
log.info("Query position changed: {}", change);
}
}
/**
* The Class MockerSessionOpened.
*/
class MockerSessionOpened implements LensEventListener<SessionOpened> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(SessionOpened event) throws LensException {
processed = true;
latch.countDown();
log.info("Session opened: {}", event);
}
}
/**
* The Class MockerSessionClosed.
*/
class MockerSessionClosed implements LensEventListener<SessionClosed> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(SessionClosed event) throws LensException {
processed = true;
latch.countDown();
log.info("Session closed: {}", event);
}
}
/**
* The Class MockerSessionExpired.
*/
class MockerSessionExpired implements LensEventListener<SessionExpired> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(SessionExpired event) throws LensException {
processed = true;
latch.countDown();
log.info("Session expired: {}", event);
}
}
/**
* The Class MockerSessionRestored.
*/
class MockerSessionRestored implements LensEventListener<SessionRestored> {
/** The processed. */
boolean processed = false;
/*
* (non-Javadoc)
*
* @see org.apache.lens.server.api.events.LensEventListener#onEvent(org.apache.lens.server.api.events.LensEvent)
*/
@Override
public void onEvent(SessionRestored event) throws LensException {
processed = true;
latch.countDown();
log.info("Session restored: {}", event);
}
}
/**
* Setup.
*
* @throws Exception the exception
*/
@BeforeClass
public void setup() throws Exception {
System.setProperty(LensConfConstants.CONFIG_LOCATION, "target/test-classes/");
LensServices.get().init(LensServerConf.getHiveConf());
LensServices.get().start();
service = LensServices.get().getService(LensEventService.NAME);
assertNotNull(service);
log.info("Service started {}", service);
}
/**
* Test add listener.
*/
@Test
public void testAddListener() {
int listenersBefore = service.getEventListeners().keySet().size();
genericEventListener = new GenericEventListener();
service.addListenerForType(genericEventListener, LensEvent.class);
endedListener = new MockEndedListener();
service.addListenerForType(endedListener, QueryEnded.class);
failedListener = new MockFailedListener();
service.addListenerForType(failedListener, QueryFailed.class);
queuePositionChangeListener = new MockQueuePositionChange();
service.addListenerForType(queuePositionChangeListener, QueuePositionChange.class);
sessionOpenedListener = new MockerSessionOpened();
service.addListenerForType(sessionOpenedListener, SessionOpened.class);
sessionClosedListener = new MockerSessionClosed();
service.addListenerForType(sessionClosedListener, SessionClosed.class);
sessionExpiredListner = new MockerSessionExpired();
service.addListenerForType(sessionExpiredListner, SessionExpired.class);
sessionRestoredListener = new MockerSessionRestored();
service.addListenerForType(sessionRestoredListener, SessionRestored.class);
assertTrue(service.getListeners(LensEvent.class).contains(genericEventListener));
assertTrue(service.getListeners(QueryFailed.class).contains(failedListener));
assertTrue(service.getListeners(QueryEnded.class).contains(endedListener));
assertTrue(service.getListeners(QueuePositionChange.class).contains(queuePositionChangeListener));
assertTrue(service.getListeners(SessionOpened.class).contains(sessionOpenedListener));
assertTrue(service.getListeners(SessionClosed.class).contains(sessionClosedListener));
}
/**
* Test remove listener.
*/
@Test
public void testRemoveListener() {
MockFailedListener toRemove = new MockFailedListener();
service.addListenerForType(toRemove, QueryFailed.class);
assertEquals(service.getListeners(QueryFailed.class).size(), 2);
service.removeListener(toRemove);
assertEquals(service.getListeners(QueryFailed.class).size(), 1);
}
/**
* Reset listeners.
*/
private void resetListeners() {
genericEventListener.processed = false;
endedListener.processed = false;
failedListener.processed = false;
queuePositionChangeListener.processed = false;
}
/**
* Reset session listeners.
*/
private void resetSessionListeners() {
genericEventListener.processed = false;
sessionOpenedListener.processed = false;
sessionClosedListener.processed = false;
sessionExpiredListner.processed = false;
sessionRestoredListener.processed = false;
}
/**
* Test handle event.
*
* @throws Exception
* the exception
*/
@Test
public void testSesionHandleEvent() throws Exception {
LensSessionHandle sessionHandle = new LensSessionHandle(UUID.randomUUID(), UUID.randomUUID());
String user = "user";
long now = System.currentTimeMillis();
SessionOpened sessionOpenedEvent = new SessionOpened(now, sessionHandle, user);
SessionClosed sessionClosedEvent = new SessionClosed(now, sessionHandle);
SessionRestored sessionRestored = new SessionRestored(now, sessionHandle);
SessionExpired sessionExpired = new SessionExpired(now, sessionHandle);
try {
latch = new CountDownLatch(3);
log.info("Sending session opened event: {}", sessionOpenedEvent);
service.notifyEvent(sessionOpenedEvent);
log.info("Sending session restored event: {}", sessionRestored);
service.notifyEvent(sessionRestored);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
assertTrue(sessionOpenedListener.processed);
assertTrue(sessionRestoredListener.processed);
resetSessionListeners();
LensEvent genEvent = new LensEvent(now) {
@Override
public String getEventId() {
return "TEST_EVENT";
}
};
latch = new CountDownLatch(2);
log.info("Sending generic event {}", genEvent.getEventId());
service.notifyEvent(genEvent);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
resetSessionListeners();
latch = new CountDownLatch(3);
log.info("Sending session closed event {}", sessionClosedEvent);
service.notifyEvent(sessionClosedEvent);
log.info("Sending session expired event {}", sessionExpired);
service.notifyEvent(sessionExpired);
latch.await(5, TimeUnit.SECONDS);
assertTrue(sessionClosedListener.processed);
assertTrue(sessionExpiredListner.processed);
assertFalse(sessionOpenedListener.processed);
assertFalse(sessionRestoredListener.processed);
} catch (LensException e) {
fail(e.getMessage());
}
}
/**
* Test handle event.
*
* @throws Exception the exception
*/
@Test
public void testHandleEvent() throws Exception {
QueryHandle query = new QueryHandle(UUID.randomUUID());
String user = "user";
long now = System.currentTimeMillis();
QueryFailed failed
= new QueryFailed(null, now, QueryStatus.Status.RUNNING, QueryStatus.Status.FAILED, query, user, null);
QuerySuccess success
= new QuerySuccess(null, now, QueryStatus.Status.RUNNING, QueryStatus.Status.SUCCESSFUL, query);
QueuePositionChange positionChange = new QueuePositionChange(now, 1, 0, query);
try {
latch = new CountDownLatch(3);
log.info("Sending event: {}", failed);
service.notifyEvent(failed);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
assertTrue(endedListener.processed);
assertTrue(failedListener.processed);
assertFalse(queuePositionChangeListener.processed);
resetListeners();
latch = new CountDownLatch(2);
log.info("Sending event : {}", success);
service.notifyEvent(success);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
assertTrue(endedListener.processed);
assertFalse(failedListener.processed);
assertFalse(queuePositionChangeListener.processed);
resetListeners();
latch = new CountDownLatch(2);
log.info("Sending event: {}", positionChange);
service.notifyEvent(positionChange);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
assertFalse(endedListener.processed);
assertFalse(failedListener.processed);
assertTrue(queuePositionChangeListener.processed);
resetListeners();
LensEvent genEvent = new LensEvent(now) {
@Override
public String getEventId() {
return "TEST_EVENT";
}
};
latch = new CountDownLatch(1);
log.info("Sending generic event {}", genEvent.getEventId());
service.notifyEvent(genEvent);
latch.await(5, TimeUnit.SECONDS);
assertTrue(genericEventListener.processed);
assertFalse(endedListener.processed);
assertFalse(failedListener.processed);
assertFalse(queuePositionChangeListener.processed);
} catch (LensException e) {
fail(e.getMessage());
}
}
@Test
public void testQueryStausLogger() throws Exception {
System.out.println("@@@ testQueryStatusLogger");
QueryStatusLogger logger = new QueryStatusLogger();
service.addListenerForType(logger, StatusChange.class);
// Catch all listener just to make sure that the query accepted and
// query exec stat events get through
final CountDownLatch latch = new CountDownLatch(2);
LensEventListener<LensEvent> eventListener = queryEventListener(latch);
service.addListenerForType(eventListener, LensEvent.class);
QueryHandle queryHandle = new QueryHandle(UUID.randomUUID());
QueryAccepted queryAccepted = new QueryAccepted(System.currentTimeMillis(), "beforeAccept", "afterAccept",
queryHandle);
QueryExecutionStatistics queryExecStats = new QueryExecutionStatistics(System.currentTimeMillis());
service.notifyEvent(queryAccepted);
service.notifyEvent(queryExecStats);
latch.await();
service.removeListener(eventListener);
}
private LensEventListener<LensEvent> queryEventListener(final CountDownLatch latch) {
return new LensEventListener<LensEvent>() {
@Override
public void onEvent(LensEvent event) throws LensException {
System.out.println("@@@@ Got Event: Type= " + event.getClass().getName() + " obj = " + event);
latch.countDown();
}
};
}
@Test
public void testAysncEventListenerPoolThreads(){
AsyncEventListener<QuerySuccess> ayncListener = new DummyAsncEventListener();
for(int i=0; i<10; i++){
try {
//A pool thread is created each time an event is submitted until core pool size is reached which is 5
//for this test case. @see org.apache.lens.server.api.events.AsyncEventListener.processor
ayncListener.onEvent(null);
} catch (LensException e) {
assert(false); //Not Expected
}
}
//Verify the core pool Threads after the events have been fired
ThreadGroup currentTG = Thread.currentThread().getThreadGroup();
int count = currentTG.activeCount();
Thread[] threads = new Thread[count];
currentTG.enumerate(threads);
Set<String> aysncThreadNames = new HashSet<String>();
for(Thread t : threads){
if (t.getName().contains("DummyAsncEventListener_AsyncThread")){
aysncThreadNames.add(t.getName());
}
}
assertTrue(aysncThreadNames.containsAll(Arrays.asList(
"DummyAsncEventListener_AsyncThread-1",
"DummyAsncEventListener_AsyncThread-2",
"DummyAsncEventListener_AsyncThread-3",
"DummyAsncEventListener_AsyncThread-4",
"DummyAsncEventListener_AsyncThread-5")));
}
/**
* Test synchronous events
* @throws Exception
*/
@Test
public void testNotifySync() throws Exception {
service.addListenerForType(new TestEventHandler(), TestEvent.class);
TestEvent testEvent = new TestEvent("ID");
service.notifyEventSync(testEvent);
assertTrue(testEvent.processed);
}
private static class TestEvent extends LensEvent{
String id;
boolean processed = false;
TestEvent(String id) {
super(System.currentTimeMillis());
this.id = id;
}
@Override
public String getEventId() {
return id;
}
}
private static class TestEventHandler implements LensEventListener<TestEvent> {
@Override
public void onEvent(TestEvent event) throws LensException {
event.processed = true;
}
}
private static class DummyAsncEventListener extends AsyncEventListener<QuerySuccess> {
DummyAsncEventListener(){
super(5); //core pool = 5
}
@Override
public void process(QuerySuccess event) {
throw new RuntimeException("Simulated Exception");
}
}
}