AFTIPCTopologyEvent.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* 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.newsclub.net.unix.tipc;
import static org.newsclub.net.unix.AFTIPCSocketAddress.AddressType.formatTIPCInt;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.newsclub.net.unix.AFTIPCSocketAddress;
import org.newsclub.net.unix.NamedInteger;
import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
/**
* A TIPC topology event received by the {@link AFTIPCTopologyWatcher} as a result of an
* {@link AFTIPCTopologySubscription}.
*
* @author Christian Kohlschütter
*/
@NonNullByDefault
public final class AFTIPCTopologyEvent {
private static final int MESSAGE_LENGTH = 48;
private final Type type;
private final int foundLower;
private final int foundUpper;
private final AFTIPCSocketAddress address;
private final AFTIPCTopologySubscription subscription;
/**
* Some TIPC error code.
*
* @author Christian Kohlschütter
*/
@SuppressWarnings("PMD.ShortClassName")
public static final class Type extends NamedInteger {
private static final long serialVersionUID = 1L;
/**
* Publication event.
*/
public static final Type TIPC_PUBLISHED;
/**
* Withdrawal event.
*/
public static final Type TIPC_WITHDRAWN;
/**
* Subscription timeout event.
*/
public static final Type TIPC_SUBSCR_TIMEOUT;
/**
* Undefined.
*/
public static final Type UNDEFINED;
private static final @NonNull Type[] VALUES = init(new @NonNull Type[] {
UNDEFINED = new Type("UNDEFINED", 0), //
TIPC_PUBLISHED = new Type("TIPC_PUBLISHED", 1), //
TIPC_WITHDRAWN = new Type("TIPC_WITHDRAWN", 2), //
TIPC_SUBSCR_TIMEOUT = new Type("TIPC_SUBSCR_TIMEOUT", 3), //
});
private Type(int id) {
super(id);
}
private Type(String name, int id) {
super(name, id);
}
/**
* Returns an {@link Type} instance given an integer.
*
* @param v The value.
* @return The instance.
*/
public static Type ofValue(int v) {
return ofValue(VALUES, Type::new, v);
}
}
private AFTIPCTopologyEvent(Type type, int foundLower, int foundUpper,
AFTIPCSocketAddress address, AFTIPCTopologySubscription subscription) {
this.type = type;
this.foundLower = foundLower;
this.foundUpper = foundUpper;
this.address = address;
this.subscription = subscription;
}
@SuppressWarnings("null")
static AFTIPCTopologyEvent readFromBuffer(ByteBuffer buf) throws SocketException {
buf = buf.order(ByteOrder.BIG_ENDIAN);
Type type = Type.ofValue(buf.getInt());
int foundLower = buf.getInt();
int foundUpper = buf.getInt();
AFTIPCSocketAddress address = AFTIPCSocketAddress.ofSocket(buf.getInt(), buf.getInt());
AFTIPCTopologySubscription sub = AFTIPCTopologySubscription.readFromBuffer(buf);
return new AFTIPCTopologyEvent(type, foundLower, foundUpper, address, sub);
}
@Override
public String toString() {
int lower = getFoundLower();
int upper = getFoundUpper();
String found;
if (lower == upper) {
found = formatTIPCInt(lower);
} else {
found = formatTIPCInt(lower) + "-" + formatTIPCInt(upper);
}
return super.toString() + "[" + getType() + ";found:" + found + ";addr=" + getAddress()
+ ";sub=" + getSubscription() + "]";
}
@SuppressWarnings("null")
ByteBuffer writeToBuffer(ByteBuffer buf) throws IOException {
buf = buf.order(ByteOrder.BIG_ENDIAN);
buf.putInt(getType().value());
buf.putInt(getFoundLower());
buf.putInt(getFoundUpper());
getAddress().writeNativeAddressTo(buf);
getSubscription().writeToBuffer(buf);
return buf;
}
/**
* Converts this event message to a new {@link ByteBuffer}.
*
* @return The new buffer, ready to read from.
* @throws IOException on error.
*/
@SuppressWarnings({"null", "cast"})
public ByteBuffer toBuffer() throws IOException {
return (ByteBuffer) writeToBuffer(ByteBuffer.allocate(MESSAGE_LENGTH)).flip();
}
/**
* The event type.
*
* @return The type.
*/
public Type getType() {
return type;
}
/**
* The found range's lower value.
*
* @return The lower value.
*/
public int getFoundLower() {
return foundLower;
}
/**
* The found range's upper value.
*
* @return The upper value.
*/
public int getFoundUpper() {
return foundUpper;
}
/**
* The corresponding socket address.
*
* @return The socket address.
*/
@SuppressFBWarnings("EI_EXPOSE_REP")
public AFTIPCSocketAddress getAddress() {
return address;
}
/**
* The corresponding subscription that found this event.
*
* @return The subscription.
*/
@SuppressFBWarnings("EI_EXPOSE_REP")
public AFTIPCTopologySubscription getSubscription() {
return subscription;
}
/**
* Returns {@code true} iff the event type is {@link Type#TIPC_PUBLISHED}.
*
* @return {@code true} if this a "published" event.
*/
public boolean isPublished() {
return type == Type.TIPC_PUBLISHED; // NOPMD.CompareObjectsWithEquals
}
/**
* Returns {@code true} iff the event type is {@link Type#TIPC_WITHDRAWN}.
*
* @return {@code true} if this a "withdrawn" event.
*/
public boolean isWithdrawn() {
return type == Type.TIPC_WITHDRAWN; // NOPMD.CompareObjectsWithEquals
}
/**
* Returns {@code true} iff the event type is {@link Type#TIPC_SUBSCR_TIMEOUT}.
*
* @return {@code true} if this a "timeout" event.
*/
public boolean isTimeout() {
return type == Type.TIPC_SUBSCR_TIMEOUT; // NOPMD.CompareObjectsWithEquals
}
/**
* Returns {@code true} iff the corresponding subscription has the
* {@link AFTIPCTopologySubscription.Flags#TIPC_SUB_PORTS} flag set.
*
* @return {@code true} if this a event referring to a "port" subscription.
*/
public boolean isPort() {
return subscription.isPort();
}
/**
* Returns {@code true} iff the corresponding subscription has the
* {@link AFTIPCTopologySubscription.Flags#TIPC_SUB_SERVICE} flag set.
*
* @return {@code true} if this a event referring to a "service" subscription.
*/
public boolean isService() {
return subscription.isService();
}
/**
* Returns {@code true} iff the corresponding subscription has the
* {@link AFTIPCTopologySubscription.Flags#TIPC_SUB_CANCEL} flag set.
*
* @return {@code true} if this a event referring to a "cancellation" subscription request.
*/
public boolean isCancellationRequest() {
return subscription.isCancellation();
}
@Override
public int hashCode() {
return Objects.hash(address, foundLower, foundUpper, subscription, type);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AFTIPCTopologyEvent)) {
return false;
}
AFTIPCTopologyEvent other = (AFTIPCTopologyEvent) obj;
return Objects.equals(address, other.address) && foundLower == other.foundLower
&& foundUpper == other.foundUpper && Objects.equals(subscription, other.subscription)
&& Objects.equals(type, other.type);
}
/**
* Returns the link name for a link state event requested by
* {@link AFTIPCTopologySubscription#TIPC_LINK_STATE} or
* {@link AFTIPCTopologyWatcher#addLinkStateSubscription()}.
*
* A link name is something like "f875a40e707d:eth0-8c1645f2ce27:eth0"
*
* @return The link name, or {@code null} if unsupported.
* @throws IOException on error.
*/
public @Nullable String getLinkName() throws IOException {
// this only works if getSubscription().getType() == AFTIPCSubscription.TIPC_LINK_STATE
int node = getFoundLower();
int ref = getAddress().getTIPCRef();
return AFTIPCSocket.getLinkName(node, ref & 0xFFFF);
}
}