/**
*
*/
package com.linkedin.databus2.core.filter;
/*
*
* Copyright 2013 LinkedIn Corp. 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.
*
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import com.linkedin.databus.core.DbusEvent;
import com.linkedin.databus2.core.filter.KeyFilterConfig.IDConfigEntry;
import com.linkedin.databus2.core.filter.KeyFilterConfigHolder.PartitionType;
/**
* @author bvaradar
*
*Implements Server Side Filtering Logic for one SourceId
*
*Supports
*
*(1) Range Based Partitioning
*(2) Mod Based Partitioning
*
*In both the cases, clients can specify list/range of bucketIds (or) partition ranges that they are interested in. The keys in the DbusEvent have to be
* numeric (or convertible to numeric).
*The configuration specification is handled by the KeyBasedPartitioningConfig class.
*
*Further Details are available in : https://iwww.corp.linkedin.com/wiki/cf/display/ENGS/Databus+V2+Server+Side+Filtering+for+LIAR
*/
public class DbusKeyFilter implements DbusFilter
{
public static final String MODULE = DbusKeyFilter.class.getName();
public static final Logger LOG = Logger.getLogger(MODULE);
private static final Long UPPER_END_UNLIMITED = Long.MAX_VALUE;
private PartitionType partitionType;
private ArrayList<DbusFilter> filters;
public DbusKeyFilter(KeyFilterConfigHolder config)
{
partitionType = config.getPartitionType();
init(config);
}
public DbusKeyFilter()
{
partitionType = PartitionType.NONE;
filters = new ArrayList<DbusFilter>();
}
public PartitionType getPartitionType() {
return partitionType;
}
public void setPartitionType(PartitionType partitionType) {
this.partitionType = partitionType;
}
public ArrayList<DbusFilter> getFilters() {
return filters;
}
public void setFilters(ArrayList<DbusFilter> filters) {
this.filters = filters;
}
@Override
public String toString() {
return "DbusKeyFilter [partitionType=" + partitionType + ", filters="
+ filters + "]";
}
private void init(KeyFilterConfigHolder config)
{
boolean debugEnabled = LOG.isDebugEnabled();
if ( PartitionType.NONE == config.getPartitionType())
{
filters = new ArrayList<DbusFilter>();
return;
} else if (config.getPartitionType() == PartitionType.RANGE)
{
filters = new ArrayList<DbusFilter>();
KeyRangeFilterConfig filterConfig = (KeyRangeFilterConfig)(config.getFilterConfig());
long rangeSize = filterConfig.getRangeSize();
List<IDConfigEntry> partList = filterConfig.getRangeIds().getIdConfigs();
for (IDConfigEntry entry : partList)
{
long keyMin = entry.getIdMin()*rangeSize;
long keyMax = 0;
if ( entry.getIdMax() == UPPER_END_UNLIMITED )
{
keyMax = Long.MAX_VALUE;
} else {
keyMax = (entry.getIdMax() + 1)*rangeSize ;
}
if ( debugEnabled) LOG.debug("KeyBasedFilter: keyMin is:" + keyMin + ", keyMax is :" + keyMax);
filters.add(new KeyRangeFilter(keyMin,keyMax));
}
} else {
filters = new ArrayList<DbusFilter>();
KeyModFilterConfig filterConfig = (KeyModFilterConfig)(config.getFilterConfig());
long numBuckets = filterConfig.getNumBuckets();
if ( numBuckets <= 0)
{
throw new RuntimeException("NumBuckets for ModBasedFiltering should be positive. Config was :" + config);
}
List<IDConfigEntry> partList = filterConfig.getBuckets().getIdConfigs();
for (IDConfigEntry entry : partList)
{
long bktMin = entry.getIdMin();
long bktMax = 0;
if ( entry.getIdMax() == UPPER_END_UNLIMITED )
{
bktMax = Long.MAX_VALUE;
} else {
bktMax = (entry.getIdMax() + 1);
}
if (debugEnabled) LOG.debug("ModBasedFilter: StartBucket is:" + bktMin + ", BktMax is :" + bktMax);
filters.add(new KeyModFilter(bktMin,bktMax,numBuckets));
}
}
}
/* (non-Javadoc)
* @see com.linkedin.databus.core.DbusFilter#allow(com.linkedin.databus.core.DbusEvent)
*/
@Override
public boolean allow(DbusEvent e)
{
boolean allow = false;
if ( partitionType == PartitionType.NONE)
{
allow = true;
} else {
for (DbusFilter filter : filters )
{
allow = filter.allow(e);
if ( allow )
break;
}
}
return allow;
}
/*
* Merge Two DbusKeyFilters
*
* @param Filter to be merged
* @throws RuntimeException if both partitionTypes are not NONE and they do not match.
*/
public void merge(DbusKeyFilter filter)
{
if ( (filter.getPartitionType() != KeyFilterConfigHolder.PartitionType.NONE) &&
(partitionType != KeyFilterConfigHolder.PartitionType.NONE) &&
(partitionType != filter.getPartitionType() ) )
{
String msg = "Partition Type not same in merge. this.filter is :" + toString() + ", Merging Filter is :" + filter;
LOG.error(msg);
throw new RuntimeException(msg);
}
//TODO: Currently, a naive-way to merge is happening. Implement smarter way (DDSDBUS-106)
if ( null == filters)
filters = new ArrayList<DbusFilter>();
filters.addAll(filter.getFilters());
}
/*
* Dedupe Filters
*/
public void dedupe()
{
ArrayList<DbusFilter> dedupFilters = new ArrayList<DbusFilter>();
Set<DbusFilter> filterSet = new HashSet<DbusFilter>();
for(DbusFilter f : filters)
filterSet.add(f);
for (DbusFilter f : filterSet)
dedupFilters.add(f);
filters = dedupFilters;
}
}