/**
* 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.hadoop.hdfs.notifier.server;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import junit.framework.Assert;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.notifier.EventType;
import org.apache.hadoop.hdfs.notifier.NamespaceEvent;
import org.apache.hadoop.hdfs.notifier.NamespaceNotification;
import org.apache.hadoop.hdfs.notifier.TransactionIdTooOldException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestServerCore {
static private Logger LOG = LoggerFactory.getLogger(TestServerCore.class);
static Configuration conf;
@BeforeClass
public static void initConf() {
conf = NotifierTestUtil.initGenericConf();
}
@Test
public void testClientOperations() throws Exception {
ServerCore core = new ServerCore(conf);
core.init(new EmptyServerLogReader(), new EmptyServerHistory(),
new EmptyServerDispatcher(), new EmptyServerHandler());
long clientId = 1000;
EmptyClientHandler handler = new EmptyClientHandler();
core.addClient(new ClientData(clientId, handler, "host", 3000));
Assert.assertTrue(core.isRegistered(clientId));
Assert.assertEquals(handler, core.getClientData(clientId).handler);
Assert.assertEquals(1, core.getClients().size());
core.removeClient(clientId);
Assert.assertFalse(core.isRegistered(clientId));
Assert.assertEquals(0, core.getClients().size());
}
@Test
public void testSubscription() throws Exception {
ServerCore core = new ServerCore(conf);
core.init(new EmptyServerLogReader(), new TestSubscriptionHistory(),
new EmptyServerDispatcher(), new EmptyServerHandler());
long id1 = 1000, id2 = 2000;
EmptyClientHandler handler1 = new EmptyClientHandler(),
handler2 = new EmptyClientHandler();
NamespaceEvent event = new NamespaceEvent("/a",
EventType.FILE_ADDED.getByteValue());
core.addClient(new ClientData(id1, handler1, "host", 3000));
core.addClient(new ClientData(id2, handler2, "host", 3001));
Set<Long> clientsForNotification;
core.subscribeClient(id1, event, -1);
clientsForNotification = core.getClientsForNotification(
new NamespaceNotification("/a/b", event.type, 10));
Assert.assertEquals(1, clientsForNotification.size());
Assert.assertTrue(clientsForNotification.contains(id1));
core.subscribeClient(id2, event, -1);
clientsForNotification = core.getClientsForNotification(
new NamespaceNotification("/a/b", event.type, 10));
Assert.assertEquals(2, clientsForNotification.size());
Assert.assertTrue(clientsForNotification.contains(id1));
Assert.assertTrue(clientsForNotification.contains(id2));
core.unsubscribeClient(id1, event);
clientsForNotification = core.getClientsForNotification(
new NamespaceNotification("/a/b", event.type, 10));
Assert.assertEquals(1, clientsForNotification.size());
Assert.assertTrue(clientsForNotification.contains(id2));
core.unsubscribeClient(id2, event);
clientsForNotification = core.getClientsForNotification(
new NamespaceNotification("/a/b", event.type, 10));
if (clientsForNotification != null)
Assert.assertEquals(0, clientsForNotification.size());
// Test that TransactionIdTooOldException is thrown
try {
core.subscribeClient(id1, event, -2);
// We should get a transaction id too old exception
Assert.fail();
} catch (TransactionIdTooOldException e) {}
// Test that the notifications are queued correctly
core.subscribeClient(id1, event, -3);
Queue<NamespaceNotification> notificationQueue =
core.getClientNotificationQueue(id1);
Assert.assertEquals(2, notificationQueue.size());
Assert.assertEquals(20, notificationQueue.poll().txId);
Assert.assertEquals(30, notificationQueue.poll().txId);
core.unsubscribeClient(id1, event);
core.removeClient(id1);
core.removeClient(id2);
}
class TestSubscriptionHistory extends EmptyServerHistory {
@Override
public void addNotificationsToQueue(NamespaceEvent event, long txId,
Queue<NamespaceNotification> notifications)
throws TransactionIdTooOldException {
// Using -2 to mark a transaction which is too old
if (txId == -2)
throw new TransactionIdTooOldException();
// Using -3 to mark we should add these 2 notifications
if (txId == -3) {
notifications.add(new NamespaceNotification("/a/b",
EventType.FILE_ADDED.getByteValue(), 20));
notifications.add(new NamespaceNotification("/a/c",
EventType.FILE_ADDED.getByteValue(), 30));
}
}
}
@Test
public void testNotificationHandling() throws Exception {
ServerCore core = new ServerCore(conf);
TestNotificationHandlingHistory history = new TestNotificationHandlingHistory();
core.init(new EmptyServerLogReader(), history, new EmptyServerDispatcher(),
new EmptyServerHandler());
NamespaceEvent event1 = new NamespaceEvent("/a",
EventType.FILE_ADDED.getByteValue());
NamespaceEvent event2 = new NamespaceEvent("/b",
EventType.FILE_ADDED.getByteValue());
NamespaceEvent event3 = new NamespaceEvent("/c",
EventType.FILE_ADDED.getByteValue());
long txIdCount = 10;
long id1 = 1000, id2 = 2000;
EmptyClientHandler handler1 = new EmptyClientHandler(),
handler2 = new EmptyClientHandler();
core.addClient(new ClientData(id1, handler1, "host", 3000));
core.addClient(new ClientData(id2, handler2, "host", 3001));
core.subscribeClient(id1, event1, -1);
core.subscribeClient(id2, event2, -1);
core.subscribeClient(id1, event3, -1);
core.subscribeClient(id2, event3, -1);
String[] possiblePaths = {"/a/", "/b/", "/c/"};
Random generator = new Random();
Queue<NamespaceNotification> client1ExpectedQueue =
new LinkedList<NamespaceNotification>();
Queue<NamespaceNotification> client2ExpectedQueue =
new LinkedList<NamespaceNotification>();
Queue<NamespaceNotification> historyExpectedQueue =
new LinkedList<NamespaceNotification>();
for (long txId = 0; txId < txIdCount; txId ++) {
String basePath = possiblePaths[generator.nextInt(3)];
NamespaceNotification n = new NamespaceNotification(basePath + txId,
EventType.FILE_ADDED.getByteValue(), txId);
historyExpectedQueue.add(n);
if (basePath.equals("/a/") || basePath.equals("/c/"))
client1ExpectedQueue.add(n);
if (basePath.equals("/b/") || basePath.equals("/c/"))
client2ExpectedQueue.add(n);
core.handleNotification(n);
}
Assert.assertEquals(historyExpectedQueue.size(),
history.notifications.size());
Assert.assertEquals(client1ExpectedQueue.size(),
core.getClientNotificationQueue(id1).size());
Assert.assertEquals(client2ExpectedQueue.size(),
core.getClientNotificationQueue(id2).size());
while (!historyExpectedQueue.isEmpty())
Assert.assertEquals(historyExpectedQueue.poll(),
history.notifications.poll());
while (!client1ExpectedQueue.isEmpty())
Assert.assertEquals(client1ExpectedQueue.poll(),
core.getClientNotificationQueue(id1).poll());
while (!client2ExpectedQueue.isEmpty())
Assert.assertEquals(client2ExpectedQueue.poll(),
core.getClientNotificationQueue(id2).poll());
core.unsubscribeClient(id1, event1);
core.unsubscribeClient(id2, event2);
core.unsubscribeClient(id1, event3);
core.unsubscribeClient(id2, event3);
core.removeClient(id1);
core.removeClient(id2);
}
class TestNotificationHandlingHistory extends EmptyServerHistory {
ConcurrentLinkedQueue<NamespaceNotification> notifications =
new ConcurrentLinkedQueue<NamespaceNotification>();
@Override
public void storeNotification(NamespaceNotification notification) {
notifications.add(notification);
}
}
}