/**
* Copyright 2016 Yahoo Inc.
*
* 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.yahoo.pulsar.client.api;
import static org.apache.bookkeeper.test.PortManager.nextFreePort;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.naming.AuthenticationException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.apache.bookkeeper.test.PortManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.yahoo.pulsar.broker.PulsarService;
import com.yahoo.pulsar.broker.ServiceConfiguration;
import com.yahoo.pulsar.broker.authentication.AuthenticationDataSource;
import com.yahoo.pulsar.broker.authentication.AuthenticationProvider;
import com.yahoo.pulsar.broker.loadbalance.LoadManager;
import com.yahoo.pulsar.broker.loadbalance.impl.SimpleResourceUnit;
import com.yahoo.pulsar.broker.namespace.NamespaceService;
import com.yahoo.pulsar.client.api.ProducerConfiguration.MessageRoutingMode;
import com.yahoo.pulsar.client.impl.auth.AuthenticationTls;
import com.yahoo.pulsar.common.naming.DestinationName;
import com.yahoo.pulsar.common.naming.ServiceUnitId;
import com.yahoo.pulsar.common.policies.data.ClusterData;
import com.yahoo.pulsar.common.policies.data.PropertyAdmin;
import com.yahoo.pulsar.common.util.SecurityUtility;
import com.yahoo.pulsar.discovery.service.DiscoveryService;
import com.yahoo.pulsar.discovery.service.server.ServiceConfig;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
public class BrokerServiceLookupTest extends ProducerConsumerBase {
private static final Logger log = LoggerFactory.getLogger(BrokerServiceLookupTest.class);
@BeforeMethod
@Override
protected void setup() throws Exception {
super.init();
com.yahoo.pulsar.client.api.ClientConfiguration clientConf = new com.yahoo.pulsar.client.api.ClientConfiguration();
clientConf.setStatsInterval(0, TimeUnit.SECONDS);
URI brokerServiceUrl = new URI("pulsar://localhost:" + BROKER_PORT);
pulsarClient = PulsarClient.create(brokerServiceUrl.toString(), clientConf);
super.producerBaseSetup();
}
@AfterMethod
@Override
protected void cleanup() throws Exception {
super.internalCleanup();
}
/**
* UsecaseL Multiple Broker => Lookup Redirection test
*
* 1. Broker1 is a leader
* 2. Lookup request reaches to Broker2 which redirects to leader (Broker1) with authoritative = false
* 3. Leader (Broker1) finds out least loaded broker as Broker2 and redirects request to Broker2 with authoritative = true
* 4. Broker2 receives final request to own a bundle with authoritative = true and client connects to Broker2
*
* @throws Exception
*/
@Test
public void testMultipleBrokerLookup() throws Exception {
log.info("-- Starting {} test --", methodName);
/**** start broker-2 ****/
ServiceConfiguration conf2 = new ServiceConfiguration();
conf2.setBrokerServicePort(PortManager.nextFreePort());
conf2.setBrokerServicePortTls(PortManager.nextFreePort());
conf2.setWebServicePort(PortManager.nextFreePort());
conf2.setWebServicePortTls(PortManager.nextFreePort());
conf2.setAdvertisedAddress("localhost");
conf2.setClusterName(conf.getClusterName());
PulsarService pulsar2 = startBroker(conf2);
pulsar.getLoadManager().get().writeLoadReportOnZookeeper();
pulsar2.getLoadManager().get().writeLoadReportOnZookeeper();
LoadManager loadManager1 = spy(pulsar.getLoadManager().get());
LoadManager loadManager2 = spy(pulsar2.getLoadManager().get());
Field loadManagerField = NamespaceService.class.getDeclaredField("loadManager");
loadManagerField.setAccessible(true);
// mock: redirect request to leader [2]
doReturn(true).when(loadManager2).isCentralized();
loadManagerField.set(pulsar2.getNamespaceService(), new AtomicReference<>(loadManager2));
// mock: return Broker2 as a Least-loaded broker when leader receies request [3]
doReturn(true).when(loadManager1).isCentralized();
SimpleResourceUnit resourceUnit = new SimpleResourceUnit(pulsar2.getWebServiceAddress(), null);
doReturn(resourceUnit).when(loadManager1).getLeastLoaded(any(ServiceUnitId.class));
loadManagerField.set(pulsar.getNamespaceService(), new AtomicReference<>(loadManager1));
/**** started broker-2 ****/
URI brokerServiceUrl = new URI("pulsar://localhost:" + conf2.getBrokerServicePort());
PulsarClient pulsarClient2 = PulsarClient.create(brokerServiceUrl.toString(), new ClientConfiguration());
// load namespace-bundle by calling Broker2
Consumer consumer = pulsarClient2.subscribe("persistent://my-property/use/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Producer producer = pulsarClient.createProducer("persistent://my-property/use/my-ns/my-topic1", new ProducerConfiguration());
for (int i = 0; i < 10; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 10; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
String expectedMessage = "my-message-" + i;
testMessageOrderAndDuplicates(messageSet, receivedMessage, expectedMessage);
}
// Acknowledge the consumption of all messages at once
consumer.acknowledgeCumulative(msg);
consumer.close();
producer.close();
pulsarClient2.close();
pulsar2.close();
loadManager1 = null;
loadManager2 = null;
}
/**
* Usecase: Redirection due to different cluster
* 1. Broker1 runs on cluster: "use" and Broker2 runs on cluster: "use2"
* 2. Broker1 receives "use2" cluster request => Broker1 reads "/clusters" from global-zookkeeper and
* redirects request to Broker2 whch serves "use2"
* 3. Broker2 receives redirect request and own namespace bundle
*
* @throws Exception
*/
@Test
public void testMultipleBrokerDifferentClusterLookup() throws Exception {
log.info("-- Starting {} test --", methodName);
/**** start broker-2 ****/
final String newCluster = "use2";
final String property = "my-property2";
ServiceConfiguration conf2 = new ServiceConfiguration();
conf2.setBrokerServicePort(PortManager.nextFreePort());
conf2.setBrokerServicePortTls(PortManager.nextFreePort());
conf2.setWebServicePort(PortManager.nextFreePort());
conf2.setWebServicePortTls(PortManager.nextFreePort());
conf2.setAdvertisedAddress("localhost");
conf2.setClusterName(newCluster); // Broker2 serves newCluster
String broker2ServiceUrl = "pulsar://localhost:" + conf2.getBrokerServicePort();
admin.clusters().createCluster(newCluster, new ClusterData("http://127.0.0.1:" + BROKER_WEBSERVICE_PORT, null, broker2ServiceUrl, null));
admin.properties().createProperty(property,
new PropertyAdmin(Lists.newArrayList("appid1", "appid2"), Sets.newHashSet(newCluster)));
admin.namespaces().createNamespace(property + "/" + newCluster + "/my-ns");
PulsarService pulsar2 = startBroker(conf2);
pulsar.getLoadManager().get().writeLoadReportOnZookeeper();
pulsar2.getLoadManager().get().writeLoadReportOnZookeeper();
URI brokerServiceUrl = new URI(broker2ServiceUrl);
PulsarClient pulsarClient2 = PulsarClient.create(brokerServiceUrl.toString(), new ClientConfiguration());
// enable authorization: so, broker can validate cluster and redirect if finds different cluster
pulsar.getConfiguration().setAuthorizationEnabled(true);
// restart broker with authorization enabled: it initialize AuthorizationManager
stopBroker();
startBroker();
LoadManager loadManager2 = spy(pulsar2.getLoadManager().get());
Field loadManagerField = NamespaceService.class.getDeclaredField("loadManager");
loadManagerField.setAccessible(true);
// mock: return Broker2 as a Least-loaded broker when leader receies request
doReturn(true).when(loadManager2).isCentralized();
SimpleResourceUnit resourceUnit = new SimpleResourceUnit(pulsar2.getWebServiceAddress(), null);
doReturn(resourceUnit).when(loadManager2).getLeastLoaded(any(ServiceUnitId.class));
loadManagerField.set(pulsar.getNamespaceService(), new AtomicReference<>(loadManager2));
/**** started broker-2 ****/
// load namespace-bundle by calling Broker2
Consumer consumer = pulsarClient.subscribe("persistent://my-property2/use2/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Producer producer = pulsarClient2.createProducer("persistent://my-property2/use2/my-ns/my-topic1", new ProducerConfiguration());
for (int i = 0; i < 10; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 10; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
String expectedMessage = "my-message-" + i;
testMessageOrderAndDuplicates(messageSet, receivedMessage, expectedMessage);
}
// Acknowledge the consumption of all messages at once
consumer.acknowledgeCumulative(msg);
consumer.close();
producer.close();
// disable authorization
pulsar.getConfiguration().setAuthorizationEnabled(false);
pulsarClient2.close();
pulsar2.close();
loadManager2 = null;
}
/**
* Create #PartitionedTopic and let it served by multiple brokers which requries
* a. tcp partitioned-metadata-lookup
* b. multiple topic-lookup
* c. partitioned producer-consumer
*
* @throws Exception
*/
@Test
public void testPartitionTopicLookup() throws Exception {
log.info("-- Starting {} test --", methodName);
int numPartitions = 8;
DestinationName dn = DestinationName.get("persistent://my-property/use/my-ns/my-partitionedtopic1");
ConsumerConfiguration conf = new ConsumerConfiguration();
conf.setSubscriptionType(SubscriptionType.Exclusive);
admin.persistentTopics().createPartitionedTopic(dn.toString(), numPartitions);
/**** start broker-2 ****/
ServiceConfiguration conf2 = new ServiceConfiguration();
conf2.setBrokerServicePort(PortManager.nextFreePort());
conf2.setBrokerServicePortTls(PortManager.nextFreePort());
conf2.setWebServicePort(PortManager.nextFreePort());
conf2.setWebServicePortTls(PortManager.nextFreePort());
conf2.setAdvertisedAddress("localhost");
conf2.setClusterName(pulsar.getConfiguration().getClusterName());
PulsarService pulsar2 = startBroker(conf2);
pulsar.getLoadManager().get().writeLoadReportOnZookeeper();
pulsar2.getLoadManager().get().writeLoadReportOnZookeeper();
LoadManager loadManager1 = spy(pulsar.getLoadManager().get());
LoadManager loadManager2 = spy(pulsar2.getLoadManager().get());
Field loadManagerField = NamespaceService.class.getDeclaredField("loadManager");
loadManagerField.setAccessible(true);
// mock: return Broker2 as a Least-loaded broker when leader receies request
doReturn(true).when(loadManager1).isCentralized();
loadManagerField.set(pulsar.getNamespaceService(), new AtomicReference<>(loadManager1));
// mock: redirect request to leader
doReturn(true).when(loadManager2).isCentralized();
loadManagerField.set(pulsar2.getNamespaceService(), new AtomicReference<>(loadManager2));
/**** broker-2 started ****/
ProducerConfiguration producerConf = new ProducerConfiguration();
producerConf.setMessageRoutingMode(MessageRoutingMode.RoundRobinPartition);
Producer producer = pulsarClient.createProducer(dn.toString(), producerConf);
Consumer consumer = pulsarClient.subscribe(dn.toString(), "my-partitioned-subscriber", conf);
for (int i = 0; i < 20; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 20; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
Assert.assertNotNull(msg, "Message should not be null");
consumer.acknowledge(msg);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
Assert.assertTrue(messageSet.add(receivedMessage), "Message " + receivedMessage + " already received");
}
producer.close();
consumer.unsubscribe();
consumer.close();
admin.persistentTopics().deletePartitionedTopic(dn.toString());
pulsar2.close();
loadManager2 = null;
log.info("-- Exiting {} test --", methodName);
}
/**
* 1. Start broker1 and broker2 with tls enable
* 2. Hit HTTPS lookup url at broker2 which redirects to HTTPS broker1
*
* @throws Exception
*/
@Test
public void testWebserviceServiceTls() throws Exception {
log.info("-- Starting {} test --", methodName);
final String TLS_SERVER_CERT_FILE_PATH = "./src/test/resources/certificate/server.crt";
final String TLS_SERVER_KEY_FILE_PATH = "./src/test/resources/certificate/server.key";
final String TLS_CLIENT_CERT_FILE_PATH = "./src/test/resources/certificate/client.crt";
final String TLS_CLIENT_KEY_FILE_PATH = "./src/test/resources/certificate/client.key";
/**** start broker-2 ****/
ServiceConfiguration conf2 = new ServiceConfiguration();
conf2.setBrokerServicePort(PortManager.nextFreePort());
conf2.setBrokerServicePortTls(PortManager.nextFreePort());
conf2.setWebServicePort(PortManager.nextFreePort());
conf2.setWebServicePortTls(PortManager.nextFreePort());
conf2.setAdvertisedAddress("localhost");
conf2.setTlsAllowInsecureConnection(true);
conf2.setTlsEnabled(true);
conf2.setTlsCertificateFilePath(TLS_SERVER_CERT_FILE_PATH);
conf2.setTlsKeyFilePath(TLS_SERVER_KEY_FILE_PATH);
conf2.setClusterName(conf.getClusterName());
PulsarService pulsar2 = startBroker(conf2);
// restart broker1 with tls enabled
conf.setTlsAllowInsecureConnection(true);
conf.setTlsEnabled(true);
conf.setTlsCertificateFilePath(TLS_SERVER_CERT_FILE_PATH);
conf.setTlsKeyFilePath(TLS_SERVER_KEY_FILE_PATH);
stopBroker();
startBroker();
pulsar.getLoadManager().get().writeLoadReportOnZookeeper();
pulsar2.getLoadManager().get().writeLoadReportOnZookeeper();
LoadManager loadManager1 = spy(pulsar.getLoadManager().get());
LoadManager loadManager2 = spy(pulsar2.getLoadManager().get());
Field loadManagerField = NamespaceService.class.getDeclaredField("loadManager");
loadManagerField.setAccessible(true);
// mock: redirect request to leader [2]
doReturn(true).when(loadManager2).isCentralized();
loadManagerField.set(pulsar2.getNamespaceService(), new AtomicReference<>(loadManager2));
// mock: return Broker2 as a Least-loaded broker when leader receies
// request [3]
doReturn(true).when(loadManager1).isCentralized();
SimpleResourceUnit resourceUnit = new SimpleResourceUnit(pulsar2.getWebServiceAddress(), null);
doReturn(resourceUnit).when(loadManager1).getLeastLoaded(any(ServiceUnitId.class));
loadManagerField.set(pulsar.getNamespaceService(), new AtomicReference<>(loadManager1));
/**** started broker-2 ****/
URI brokerServiceUrl = new URI("pulsar://localhost:" + conf2.getBrokerServicePort());
PulsarClient pulsarClient2 = PulsarClient.create(brokerServiceUrl.toString(), new ClientConfiguration());
final String lookupResourceUrl = "/lookup/v2/destination/persistent/my-property/use/my-ns/my-topic1";
// set client cert_key file
KeyManager[] keyManagers = null;
Certificate[] tlsCert = SecurityUtility.loadCertificatesFromPemFile(TLS_CLIENT_CERT_FILE_PATH);
PrivateKey tlsKey = SecurityUtility.loadPrivateKeyFromPemFile(TLS_CLIENT_KEY_FILE_PATH);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setKeyEntry("private", tlsKey, "".toCharArray(), tlsCert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "".toCharArray());
keyManagers = kmf.getKeyManagers();
TrustManager[] trustManagers = InsecureTrustManagerFactory.INSTANCE.getTrustManagers();
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(keyManagers, trustManagers, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx.getSocketFactory());
// hit broker2 url
URLConnection con = new URL(pulsar2.getWebServiceAddressTls() + lookupResourceUrl).openConnection();
log.info("orignal url: {}", con.getURL());
con.connect();
log.info("connected url: {} ", con.getURL());
// assert connect-url: broker2-https
Assert.assertEquals(con.getURL().getPort(), conf2.getWebServicePortTls());
InputStream is = con.getInputStream();
// assert redirect-url: broker1-https only
log.info("redirected url: {}", con.getURL());
Assert.assertEquals(con.getURL().getPort(), conf.getWebServicePortTls());
is.close();
pulsarClient2.close();
pulsar2.close();
loadManager1 = null;
loadManager2 = null;
}
/**
* Discovery-Service lookup over binary-protocol
* 1. Start discovery service
* 2. start broker
* 3. Create Producer/Consumer: by calling Discovery service for partitionedMetadata and topic lookup
*
* @throws Exception
*/
@Test
public void testDiscoveryLookup() throws Exception {
// (1) start discovery service
ServiceConfig config = new ServiceConfig();
config.setServicePort(nextFreePort());
config.setBindOnLocalhost(true);
DiscoveryService discoveryService = spy(new DiscoveryService(config));
doReturn(mockZooKeeperClientFactory).when(discoveryService).getZooKeeperClientFactory();
discoveryService.start();
// (2) lookup using discovery service
final String discoverySvcUrl = discoveryService.getServiceUrl();
ClientConfiguration clientConfig = new ClientConfiguration();
PulsarClient pulsarClient2 = PulsarClient.create(discoverySvcUrl, clientConfig);
Consumer consumer = pulsarClient2.subscribe("persistent://my-property2/use2/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Producer producer = pulsarClient2.createProducer("persistent://my-property2/use2/my-ns/my-topic1", new ProducerConfiguration());
for (int i = 0; i < 10; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 10; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
String expectedMessage = "my-message-" + i;
testMessageOrderAndDuplicates(messageSet, receivedMessage, expectedMessage);
}
// Acknowledge the consumption of all messages at once
consumer.acknowledgeCumulative(msg);
consumer.close();
producer.close();
}
/**
* Verify discovery-service binary-proto lookup using tls
*
* @throws Exception
*/
@Test
public void testDiscoveryLookupTls() throws Exception {
final String TLS_SERVER_CERT_FILE_PATH = "./src/test/resources/certificate/server.crt";
final String TLS_SERVER_KEY_FILE_PATH = "./src/test/resources/certificate/server.key";
final String TLS_CLIENT_CERT_FILE_PATH = "./src/test/resources/certificate/client.crt";
final String TLS_CLIENT_KEY_FILE_PATH = "./src/test/resources/certificate/client.key";
// (1) restart broker1 with tls enabled
conf.setTlsAllowInsecureConnection(true);
conf.setTlsEnabled(true);
conf.setTlsCertificateFilePath(TLS_SERVER_CERT_FILE_PATH);
conf.setTlsKeyFilePath(TLS_SERVER_KEY_FILE_PATH);
stopBroker();
startBroker();
// (2) start discovery service
ServiceConfig config = new ServiceConfig();
config.setServicePort(nextFreePort());
config.setServicePortTls(nextFreePort());
config.setTlsEnabled(true);
config.setBindOnLocalhost(true);
config.setTlsCertificateFilePath(TLS_SERVER_CERT_FILE_PATH);
config.setTlsKeyFilePath(TLS_SERVER_KEY_FILE_PATH);
DiscoveryService discoveryService = spy(new DiscoveryService(config));
doReturn(mockZooKeeperClientFactory).when(discoveryService).getZooKeeperClientFactory();
discoveryService.start();
// (3) lookup using discovery service
final String discoverySvcUrl = discoveryService.getServiceUrlTls();
ClientConfiguration clientConfig = new ClientConfiguration();
Map<String, String> authParams = new HashMap<>();
authParams.put("tlsCertFile", TLS_CLIENT_CERT_FILE_PATH);
authParams.put("tlsKeyFile", TLS_CLIENT_KEY_FILE_PATH);
Authentication auth = new AuthenticationTls();
auth.configure(authParams);
clientConfig.setAuthentication(auth);
clientConfig.setUseTls(true);
clientConfig.setTlsAllowInsecureConnection(true);
PulsarClient pulsarClient2 = PulsarClient.create(discoverySvcUrl, clientConfig);
Consumer consumer = pulsarClient2.subscribe("persistent://my-property2/use2/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Producer producer = pulsarClient2.createProducer("persistent://my-property2/use2/my-ns/my-topic1", new ProducerConfiguration());
for (int i = 0; i < 10; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 10; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
String expectedMessage = "my-message-" + i;
testMessageOrderAndDuplicates(messageSet, receivedMessage, expectedMessage);
}
// Acknowledge the consumption of all messages at once
consumer.acknowledgeCumulative(msg);
consumer.close();
producer.close();
}
@Test
public void testDiscoveryLookupAuthAndAuthSuccess() throws Exception {
// (1) start discovery service
ServiceConfig config = new ServiceConfig();
config.setServicePort(nextFreePort());
config.setBindOnLocalhost(true);
// add Authentication Provider
Set<String> providersClassNames = Sets.newHashSet(MockAuthenticationProvider.class.getName());
config.setAuthenticationProviders(providersClassNames);
// enable authentication and authorization
config.setAuthenticationEnabled(true);
config.setAuthorizationEnabled(true);
DiscoveryService discoveryService = spy(new DiscoveryService(config));
doReturn(mockZooKeeperClientFactory).when(discoveryService).getZooKeeperClientFactory();
discoveryService.start();
// (2) lookup using discovery service
final String discoverySvcUrl = discoveryService.getServiceUrl();
ClientConfiguration clientConfig = new ClientConfiguration();
// set authentication data
clientConfig.setAuthentication(new Authentication() {
@Override
public void close() throws IOException {
}
@Override
public String getAuthMethodName() {
return "auth";
}
@Override
public AuthenticationDataProvider getAuthData() throws PulsarClientException {
return new AuthenticationDataProvider() {
};
}
@Override
public void configure(Map<String, String> authParams) {
}
@Override
public void start() throws PulsarClientException {
}
});
PulsarClient pulsarClient = PulsarClient.create(discoverySvcUrl, clientConfig);
Consumer consumer = pulsarClient.subscribe("persistent://my-property/use2/my-ns/my-topic1",
"my-subscriber-name", new ConsumerConfiguration());
Producer producer = pulsarClient.createProducer("persistent://my-property/use2/my-ns/my-topic1",
new ProducerConfiguration());
for (int i = 0; i < 10; i++) {
String message = "my-message-" + i;
producer.send(message.getBytes());
}
Message msg = null;
Set<String> messageSet = Sets.newHashSet();
for (int i = 0; i < 10; i++) {
msg = consumer.receive(5, TimeUnit.SECONDS);
String receivedMessage = new String(msg.getData());
log.debug("Received message: [{}]", receivedMessage);
String expectedMessage = "my-message-" + i;
testMessageOrderAndDuplicates(messageSet, receivedMessage, expectedMessage);
}
// Acknowledge the consumption of all messages at once
consumer.acknowledgeCumulative(msg);
consumer.close();
producer.close();
}
@Test
public void testDiscoveryLookupAuthenticationFailure() throws Exception {
// (1) start discovery service
ServiceConfig config = new ServiceConfig();
config.setServicePort(nextFreePort());
config.setBindOnLocalhost(true);
// set Authentication provider which fails authentication
Set<String> providersClassNames = Sets.newHashSet(MockAuthenticationProviderFail.class.getName());
config.setAuthenticationProviders(providersClassNames);
// enable authentication
config.setAuthenticationEnabled(true);
config.setAuthorizationEnabled(true);
DiscoveryService discoveryService = spy(new DiscoveryService(config));
doReturn(mockZooKeeperClientFactory).when(discoveryService).getZooKeeperClientFactory();
discoveryService.start();
// (2) lookup using discovery service
final String discoverySvcUrl = discoveryService.getServiceUrl();
ClientConfiguration clientConfig = new ClientConfiguration();
// set authentication data
clientConfig.setAuthentication(new Authentication() {
@Override
public void close() throws IOException {
}
@Override
public String getAuthMethodName() {
return "auth";
}
@Override
public AuthenticationDataProvider getAuthData() throws PulsarClientException {
return new AuthenticationDataProvider() {
};
}
@Override
public void configure(Map<String, String> authParams) {
}
@Override
public void start() throws PulsarClientException {
}
});
PulsarClient pulsarClient = PulsarClient.create(discoverySvcUrl, clientConfig);
try {
pulsarClient.subscribe("persistent://my-property/use2/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Assert.fail("should have failed due to authentication");
} catch (PulsarClientException e) {
// Ok: expected
}
}
@Test
public void testDiscoveryLookupAuthorizationFailure() throws Exception {
// (1) start discovery service
ServiceConfig config = new ServiceConfig();
config.setServicePort(nextFreePort());
config.setBindOnLocalhost(true);
// set Authentication provider which returns "invalid" appid so, authorization fails
Set<String> providersClassNames = Sets.newHashSet(MockAuthorizationProviderFail.class.getName());
config.setAuthenticationProviders(providersClassNames);
// enable authentication
config.setAuthenticationEnabled(true);
config.setAuthorizationEnabled(true);
DiscoveryService discoveryService = spy(new DiscoveryService(config));
doReturn(mockZooKeeperClientFactory).when(discoveryService).getZooKeeperClientFactory();
discoveryService.start();
// (2) lookup using discovery service
final String discoverySvcUrl = discoveryService.getServiceUrl();
ClientConfiguration clientConfig = new ClientConfiguration();
// set authentication data
clientConfig.setAuthentication(new Authentication() {
@Override
public void close() throws IOException {
}
@Override
public String getAuthMethodName() {
return "auth";
}
@Override
public AuthenticationDataProvider getAuthData() throws PulsarClientException {
return new AuthenticationDataProvider() {
};
}
@Override
public void configure(Map<String, String> authParams) {
}
@Override
public void start() throws PulsarClientException {
}
});
PulsarClient pulsarClient = PulsarClient.create(discoverySvcUrl, clientConfig);
try {
pulsarClient.subscribe("persistent://my-property/use2/my-ns/my-topic1", "my-subscriber-name",
new ConsumerConfiguration());
Assert.fail("should have failed due to authentication");
} catch (PulsarClientException e) {
// Ok: expected
Assert.assertTrue(e instanceof PulsarClientException.LookupException);
}
}
/**** helper classes ****/
public static class MockAuthenticationProvider implements AuthenticationProvider {
@Override
public void close() throws IOException {
}
@Override
public void initialize(ServiceConfiguration config) throws IOException {
}
@Override
public String getAuthMethodName() {
return "auth";
}
@Override
public String authenticate(AuthenticationDataSource authData) throws AuthenticationException {
return "appid1";
}
}
public static class MockAuthenticationProviderFail extends MockAuthenticationProvider {
@Override
public String authenticate(AuthenticationDataSource authData) throws AuthenticationException {
throw new AuthenticationException("authentication failed");
}
}
public static class MockAuthorizationProviderFail extends MockAuthenticationProvider {
@Override
public String authenticate(AuthenticationDataSource authData) throws AuthenticationException {
return "invalid";
}
}
}