package com.linkedin.databus.client.registration;
/*
*
* 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.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.codec.binary.Hex;
import org.apache.log4j.Logger;
import com.linkedin.databus.client.pub.RegistrationId;
import com.linkedin.databus.core.data_model.DatabusSubscription;
public class RegistrationIdGenerator
{
public static final Logger LOG = Logger.getLogger(RegistrationIdGenerator.class);
/**
* Database of Ids for registrations created so far
*/
private static Set<String> _registrationIdDatabase;
static {
_registrationIdDatabase = new HashSet<String>();
}
/**
* Queries the existing database of registrations inside the client and generates a new id different from
* any of them
*
* @param prefix : A String prefix which can be specified to precede the id to be generated. Typically this
* is the name of the Consumer class
* @param subsSources : List of subscriptions the consumer is interested in
* @return RegistrationId : Generated based on the prefix _ 8 byte md5 hash ( subscriptions ) _ count
*/
public static RegistrationId generateNewId(String prefix, Collection<DatabusSubscription> subsSources)
{
StringBuilder subscription = new StringBuilder();
for (DatabusSubscription ds : subsSources)
{
if (ds != null)
subscription.append(ds.generateSubscriptionString());
}
String id = generateUniqueString(prefix, subscription.toString());
RegistrationId rid = new RegistrationId(id);
return rid;
}
/**
* For a given regId , this API queries the existing database of registrations inside the client.
* If the id is available, it is returned as a registration id, otherwise a running counter
* is appended to the suggested regId.
*
* @param id : A suggested name by the caller to be used as regId. If the id is not available, a running counter
* is appended
* @return RegistrationId : Generated based on the id _count
*/
public static RegistrationId generateNewId(String id)
{
String r = generateUniqueString(id);
RegistrationId rid = new RegistrationId(r);
return rid;
}
/**
* Checks if the input rid can be used as a RegistrationId.
* Specifically, checks to see if the underlying id is already in use
* @return
*/
public static boolean isIdValid(RegistrationId rid)
{
String id = rid.getId();
synchronized (RegistrationIdGenerator.class)
{
if (_registrationIdDatabase.contains(id))
{
return false;
}
else
{
return true;
}
}
}
/**
* Adds an id into the RegistrationId database.
* This is useful for unit-testing / inserting an id out-of-band into the database, so that such an id
* would not get generated again
*
* @param id
*/
public static void insertId(RegistrationId rid)
{
String id = rid.getId();
synchronized (RegistrationIdGenerator.class)
{
_registrationIdDatabase.add(id);
}
}
/**
* Creates a unique string given a prefix ( which must appear as is ), a subscription string ( which is converted
* to an 8 byte string ) and a count if there are duplicates with the previous two
*/
private static String generateUniqueString(String prefix, String subscription)
{
final String delimiter = "_";
String baseId = prefix + delimiter + generateByteHash(subscription);
return generateUniqueString(baseId);
}
/**
* Creates a unique registration Id string given an id.
* A running counter is appended if there are duplicates
*/
private static String generateUniqueString(String id)
{
final String delimiter = "_";
String baseId = id;
boolean success = false;
boolean debugEnabled = LOG.isDebugEnabled();
synchronized (RegistrationIdGenerator.class)
{
int count = _registrationIdDatabase.size();
while (! success)
{
if (_registrationIdDatabase.contains(id))
{
if (debugEnabled)
LOG.debug("The generated id " + id + " already exists. Retrying ...");
id = baseId + delimiter + count;
count++;
}
else
{
if (debugEnabled)
LOG.debug("Obtained a new ID " + id);
_registrationIdDatabase.add(id);
success = true;
}
}
}
return id;
}
/**
* Generate a hash out of the String id
*
* @param id
* @return
*/
private static String generateByteHash(String id)
{
try {
final MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(id.getBytes(Charset.forName("UTF8")));
final byte[] resultsByte = messageDigest.digest();
String hash = new String(Hex.encodeHex(resultsByte));
final int length = 8;
if (hash.length() > length)
hash = hash.substring(0, length);
return hash;
}
catch (NoSuchAlgorithmException nse)
{
LOG.error("Unexpected error : Got NoSuchAlgorithm exception for MD5" );
return "";
}
}
}