Motivation: A `DefaultChannelId` has final `hashCode` field calculated in the constructor. We can use it in `equals` to the fast return for different objects. Modifications: Use `hashCode` field in `DefaultChannelId.equals()`. Result: Fast `equals` on negative scenarios.
292 lines
9.7 KiB
Java
292 lines
9.7 KiB
Java
/*
|
|
* Copyright 2013 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you 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 io.netty.channel;
|
|
|
|
import io.netty.buffer.ByteBufUtil;
|
|
import io.netty.util.internal.EmptyArrays;
|
|
import io.netty.util.internal.MacAddressUtil;
|
|
import io.netty.util.internal.PlatformDependent;
|
|
import io.netty.util.internal.SystemPropertyUtil;
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
|
|
import java.lang.reflect.Method;
|
|
import java.util.Arrays;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import static io.netty.util.internal.MacAddressUtil.defaultMachineId;
|
|
import static io.netty.util.internal.MacAddressUtil.parseMAC;
|
|
|
|
/**
|
|
* The default {@link ChannelId} implementation.
|
|
*/
|
|
public final class DefaultChannelId implements ChannelId {
|
|
|
|
private static final long serialVersionUID = 3884076183504074063L;
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelId.class);
|
|
private static final byte[] MACHINE_ID;
|
|
private static final int PROCESS_ID_LEN = 4;
|
|
private static final int PROCESS_ID;
|
|
private static final int SEQUENCE_LEN = 4;
|
|
private static final int TIMESTAMP_LEN = 8;
|
|
private static final int RANDOM_LEN = 4;
|
|
|
|
private static final AtomicInteger nextSequence = new AtomicInteger();
|
|
|
|
/**
|
|
* Returns a new {@link DefaultChannelId} instance.
|
|
*/
|
|
public static DefaultChannelId newInstance() {
|
|
return new DefaultChannelId();
|
|
}
|
|
|
|
static {
|
|
int processId = -1;
|
|
String customProcessId = SystemPropertyUtil.get("io.netty.processId");
|
|
if (customProcessId != null) {
|
|
try {
|
|
processId = Integer.parseInt(customProcessId);
|
|
} catch (NumberFormatException e) {
|
|
// Malformed input.
|
|
}
|
|
|
|
if (processId < 0) {
|
|
processId = -1;
|
|
logger.warn("-Dio.netty.processId: {} (malformed)", customProcessId);
|
|
} else if (logger.isDebugEnabled()) {
|
|
logger.debug("-Dio.netty.processId: {} (user-set)", processId);
|
|
}
|
|
}
|
|
|
|
if (processId < 0) {
|
|
processId = defaultProcessId();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("-Dio.netty.processId: {} (auto-detected)", processId);
|
|
}
|
|
}
|
|
|
|
PROCESS_ID = processId;
|
|
|
|
byte[] machineId = null;
|
|
String customMachineId = SystemPropertyUtil.get("io.netty.machineId");
|
|
if (customMachineId != null) {
|
|
try {
|
|
machineId = parseMAC(customMachineId);
|
|
} catch (Exception e) {
|
|
logger.warn("-Dio.netty.machineId: {} (malformed)", customMachineId, e);
|
|
}
|
|
if (machineId != null) {
|
|
logger.debug("-Dio.netty.machineId: {} (user-set)", customMachineId);
|
|
}
|
|
}
|
|
|
|
if (machineId == null) {
|
|
machineId = defaultMachineId();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("-Dio.netty.machineId: {} (auto-detected)", MacAddressUtil.formatAddress(machineId));
|
|
}
|
|
}
|
|
|
|
MACHINE_ID = machineId;
|
|
}
|
|
|
|
private static int defaultProcessId() {
|
|
ClassLoader loader = null;
|
|
String value;
|
|
try {
|
|
loader = PlatformDependent.getClassLoader(DefaultChannelId.class);
|
|
// Invoke java.lang.management.ManagementFactory.getRuntimeMXBean().getName()
|
|
Class<?> mgmtFactoryType = Class.forName("java.lang.management.ManagementFactory", true, loader);
|
|
Class<?> runtimeMxBeanType = Class.forName("java.lang.management.RuntimeMXBean", true, loader);
|
|
|
|
Method getRuntimeMXBean = mgmtFactoryType.getMethod("getRuntimeMXBean", EmptyArrays.EMPTY_CLASSES);
|
|
Object bean = getRuntimeMXBean.invoke(null, EmptyArrays.EMPTY_OBJECTS);
|
|
Method getName = runtimeMxBeanType.getMethod("getName", EmptyArrays.EMPTY_CLASSES);
|
|
value = (String) getName.invoke(bean, EmptyArrays.EMPTY_OBJECTS);
|
|
} catch (Throwable t) {
|
|
logger.debug("Could not invoke ManagementFactory.getRuntimeMXBean().getName(); Android?", t);
|
|
try {
|
|
// Invoke android.os.Process.myPid()
|
|
Class<?> processType = Class.forName("android.os.Process", true, loader);
|
|
Method myPid = processType.getMethod("myPid", EmptyArrays.EMPTY_CLASSES);
|
|
value = myPid.invoke(null, EmptyArrays.EMPTY_OBJECTS).toString();
|
|
} catch (Throwable t2) {
|
|
logger.debug("Could not invoke Process.myPid(); not Android?", t2);
|
|
value = "";
|
|
}
|
|
}
|
|
|
|
int atIndex = value.indexOf('@');
|
|
if (atIndex >= 0) {
|
|
value = value.substring(0, atIndex);
|
|
}
|
|
|
|
int pid;
|
|
try {
|
|
pid = Integer.parseInt(value);
|
|
} catch (NumberFormatException e) {
|
|
// value did not contain an integer.
|
|
pid = -1;
|
|
}
|
|
|
|
if (pid < 0) {
|
|
pid = PlatformDependent.threadLocalRandom().nextInt();
|
|
logger.warn("Failed to find the current process ID from '{}'; using a random value: {}", value, pid);
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
private final byte[] data;
|
|
private final int hashCode;
|
|
|
|
private transient String shortValue;
|
|
private transient String longValue;
|
|
|
|
private DefaultChannelId() {
|
|
data = new byte[MACHINE_ID.length + PROCESS_ID_LEN + SEQUENCE_LEN + TIMESTAMP_LEN + RANDOM_LEN];
|
|
int i = 0;
|
|
|
|
// machineId
|
|
System.arraycopy(MACHINE_ID, 0, data, i, MACHINE_ID.length);
|
|
i += MACHINE_ID.length;
|
|
|
|
// processId
|
|
i = writeInt(i, PROCESS_ID);
|
|
|
|
// sequence
|
|
i = writeInt(i, nextSequence.getAndIncrement());
|
|
|
|
// timestamp (kind of)
|
|
i = writeLong(i, Long.reverse(System.nanoTime()) ^ System.currentTimeMillis());
|
|
|
|
// random
|
|
int random = PlatformDependent.threadLocalRandom().nextInt();
|
|
i = writeInt(i, random);
|
|
assert i == data.length;
|
|
|
|
hashCode = Arrays.hashCode(data);
|
|
}
|
|
|
|
private int writeInt(int i, int value) {
|
|
data[i ++] = (byte) (value >>> 24);
|
|
data[i ++] = (byte) (value >>> 16);
|
|
data[i ++] = (byte) (value >>> 8);
|
|
data[i ++] = (byte) value;
|
|
return i;
|
|
}
|
|
|
|
private int writeLong(int i, long value) {
|
|
data[i ++] = (byte) (value >>> 56);
|
|
data[i ++] = (byte) (value >>> 48);
|
|
data[i ++] = (byte) (value >>> 40);
|
|
data[i ++] = (byte) (value >>> 32);
|
|
data[i ++] = (byte) (value >>> 24);
|
|
data[i ++] = (byte) (value >>> 16);
|
|
data[i ++] = (byte) (value >>> 8);
|
|
data[i ++] = (byte) value;
|
|
return i;
|
|
}
|
|
|
|
@Override
|
|
public String asShortText() {
|
|
String shortValue = this.shortValue;
|
|
if (shortValue == null) {
|
|
this.shortValue = shortValue = ByteBufUtil.hexDump(data, data.length - RANDOM_LEN, RANDOM_LEN);
|
|
}
|
|
return shortValue;
|
|
}
|
|
|
|
@Override
|
|
public String asLongText() {
|
|
String longValue = this.longValue;
|
|
if (longValue == null) {
|
|
this.longValue = longValue = newLongValue();
|
|
}
|
|
return longValue;
|
|
}
|
|
|
|
private String newLongValue() {
|
|
StringBuilder buf = new StringBuilder(2 * data.length + 5);
|
|
int i = 0;
|
|
i = appendHexDumpField(buf, i, MACHINE_ID.length);
|
|
i = appendHexDumpField(buf, i, PROCESS_ID_LEN);
|
|
i = appendHexDumpField(buf, i, SEQUENCE_LEN);
|
|
i = appendHexDumpField(buf, i, TIMESTAMP_LEN);
|
|
i = appendHexDumpField(buf, i, RANDOM_LEN);
|
|
assert i == data.length;
|
|
return buf.substring(0, buf.length() - 1);
|
|
}
|
|
|
|
private int appendHexDumpField(StringBuilder buf, int i, int length) {
|
|
buf.append(ByteBufUtil.hexDump(data, i, length));
|
|
buf.append('-');
|
|
i += length;
|
|
return i;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return hashCode;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(final ChannelId o) {
|
|
if (this == o) {
|
|
// short circuit
|
|
return 0;
|
|
}
|
|
if (o instanceof DefaultChannelId) {
|
|
// lexicographic comparison
|
|
final byte[] otherData = ((DefaultChannelId) o).data;
|
|
int len1 = data.length;
|
|
int len2 = otherData.length;
|
|
int len = Math.min(len1, len2);
|
|
|
|
for (int k = 0; k < len; k++) {
|
|
byte x = data[k];
|
|
byte y = otherData[k];
|
|
if (x != y) {
|
|
// treat these as unsigned bytes for comparison
|
|
return (x & 0xff) - (y & 0xff);
|
|
}
|
|
}
|
|
return len1 - len2;
|
|
}
|
|
|
|
return asLongText().compareTo(o.asLongText());
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
}
|
|
if (!(obj instanceof DefaultChannelId)) {
|
|
return false;
|
|
}
|
|
DefaultChannelId other = (DefaultChannelId) obj;
|
|
return hashCode == other.hashCode && Arrays.equals(data, other.data);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return asShortText();
|
|
}
|
|
}
|