/*
* Copyright (c) 2005 - 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.carbon.event.input.adapter.kafka;
import kafka.consumer.ConsumerConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.event.input.adapter.core.EventAdapterConstants;
import org.wso2.carbon.event.input.adapter.core.InputEventAdapter;
import org.wso2.carbon.event.input.adapter.core.InputEventAdapterConfiguration;
import org.wso2.carbon.event.input.adapter.core.InputEventAdapterListener;
import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterException;
import org.wso2.carbon.event.input.adapter.core.exception.InputEventAdapterRuntimeException;
import org.wso2.carbon.event.input.adapter.core.exception.TestConnectionNotSupportedException;
import org.wso2.carbon.event.input.adapter.kafka.internal.util.KafkaEventAdapterConstants;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
public final class KafkaEventAdapter implements InputEventAdapter {
private static final Log log = LogFactory.getLog(KafkaEventAdapter.class);
private final InputEventAdapterConfiguration eventAdapterConfiguration;
private final Map<String, String> globalProperties;
private InputEventAdapterListener eventAdaptorListener;
private final String id = UUID.randomUUID().toString();
private int tenantId;
private ConsumerKafkaAdaptor consumerKafkaAdaptor;
public KafkaEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration, Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
}
@Override
public void init(InputEventAdapterListener eventAdaptorListener) throws InputEventAdapterException {
validateInputEventAdapterConfigurations();
this.eventAdaptorListener = eventAdaptorListener;
}
@Override
public void testConnect() throws TestConnectionNotSupportedException {
throw new TestConnectionNotSupportedException("not-supported");
}
@Override
public void connect() {
tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
createKafkaAdaptorListener(eventAdaptorListener, eventAdapterConfiguration);
}
@Override
public void disconnect() {
if (consumerKafkaAdaptor != null) {
consumerKafkaAdaptor.shutdown();
String topic = eventAdapterConfiguration.getProperties().get(KafkaEventAdapterConstants.ADAPTER_MESSAGE_TOPIC);
log.debug("Adapter " + eventAdapterConfiguration.getName() + " disconnected " + topic);
}
}
@Override
public void destroy() {
}
public InputEventAdapterListener getEventAdaptorListener() {
return eventAdaptorListener;
}
@Override
public int hashCode() {
return id.hashCode();
}
private static ConsumerConfig createConsumerConfig(String zookeeper, String groupId,
String optionalConfigs) {
try {
Properties props = new Properties();
props.put(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_ZOOKEEPER_CONNECT, zookeeper);
props.put(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_GROUP_ID, groupId);
if (optionalConfigs != null) {
String[] optionalProperties = optionalConfigs.split(",");
if (optionalProperties != null) {
for (String header : optionalProperties) {
String[] configPropertyWithValue = header.split(":", 2);
if (configPropertyWithValue.length == 2) {
props.put(configPropertyWithValue[0], configPropertyWithValue[1]);
} else {
log.warn("Optional configuration property not defined in the correct format.\nRequired - property_name1:property_value1,property_name2:property_value2\nFound - " + optionalConfigs);
}
}
}
}
return new ConsumerConfig(props);
} catch (NoClassDefFoundError e) {
throw new InputEventAdapterRuntimeException("Cannot access kafka context due to missing jars", e);
}
}
private void createKafkaAdaptorListener(
InputEventAdapterListener inputEventAdapterListener,
InputEventAdapterConfiguration inputEventAdapterConfiguration) {
Map<String, String> brokerProperties = new HashMap<String, String>();
brokerProperties.putAll(inputEventAdapterConfiguration.getProperties());
String zkConnect = brokerProperties.get(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_ZOOKEEPER_CONNECT);
String groupID = brokerProperties.get(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_GROUP_ID);
String threadsStr = brokerProperties.get(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_THREADS);
String optionalConfiguration = brokerProperties.get(KafkaEventAdapterConstants.ADAPTOR_OPTIONAL_CONFIGURATION_PROPERTIES);
int threads = Integer.parseInt(threadsStr);
String topic = inputEventAdapterConfiguration.getProperties().get(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_TOPIC);
consumerKafkaAdaptor = new ConsumerKafkaAdaptor(topic, tenantId,
KafkaEventAdapter.createConsumerConfig(zkConnect, groupID, optionalConfiguration));
consumerKafkaAdaptor.run(threads, inputEventAdapterListener);
}
@Override
public boolean isEventDuplicatedInCluster() {
return Boolean.parseBoolean(eventAdapterConfiguration.getProperties().get(EventAdapterConstants.EVENTS_DUPLICATED_IN_CLUSTER));
}
@Override
public boolean isPolling() {
return true;
}
private void validateInputEventAdapterConfigurations() throws InputEventAdapterException {
String threadsProperty = eventAdapterConfiguration.getProperties().get(KafkaEventAdapterConstants.ADAPTOR_SUSCRIBER_THREADS);
try{
Integer.parseInt(threadsProperty);
} catch (NumberFormatException e){
throw new InputEventAdapterException("Invalid value set for property 'Threads': " + threadsProperty, e);
}
}
}