/*
* ToroDB
* Copyright © 2014 8Kdata Technology (www.8kdata.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.torodb.mongodb.language;
import com.eightkdata.mongowp.bson.BsonObjectId;
import com.eightkdata.mongowp.bson.impl.IntBasedBsonObjectId;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.primitives.UnsignedInteger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.management.ManagementFactory;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.concurrent.ThreadSafe;
/**
*
*/
@ThreadSafe
public class ObjectIdFactory {
private static final Logger LOGGER =
LogManager.getLogger(ObjectIdFactory.class);
private static final int MACHINE_ID = createMachineId();
private static final int PROCESS_ID = createProcessId();
private static final AtomicInteger COUNTER = new AtomicInteger(new SecureRandom().nextInt());
public BsonObjectId consumeObjectId() {
long secs = System.currentTimeMillis() / 1000;
return new IntBasedBsonObjectId(
UnsignedInteger.valueOf(secs).intValue(),
MACHINE_ID,
PROCESS_ID,
COUNTER.getAndIncrement() & 0xFFFFFF
);
}
private static int createMachineId() {
int machineId;
try {
Hasher hasher = Hashing.crc32c().newHasher();
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
boolean atLeastOne = false;
while (nics.hasMoreElements()) {
NetworkInterface ni = nics.nextElement();
if (ni != null) {
byte[] macAddress = ni.getHardwareAddress();
if (macAddress != null) {
for (byte b : macAddress) {
atLeastOne = true;
hasher.putByte(b);
}
}
}
}
if (!atLeastOne) {
LOGGER.warn("Failed to calculate the machine id. A random number is used");
machineId = new SecureRandom().nextInt();
} else {
machineId = hasher.hash().asInt();
}
} catch (SocketException ex) {
LOGGER.warn("Failed to calculate the machine id. A random number is used");
machineId = new SecureRandom().nextInt();
}
return machineId & 0xFFFFFF;
}
private static int createProcessId() {
int pid;
String name = ManagementFactory.getRuntimeMXBean().getName();
try {
pid = Integer.parseInt(name.substring(0, name.indexOf("@")));
} catch (Throwable ex) {
LOGGER.warn("Failed to calculate the process id. A random number is used");
pid = new SecureRandom().nextInt();
}
return pid & 0xFFFF;
}
}