How to Build Custom smart-socket Plugins to Extend Framework Functionality

This article walks through the smart-socket plugin system, explaining the Plugin interface, its lifecycle, the AbstractPlugin base class, and step‑by‑step examples for creating message‑filter, connection‑limit, and heartbeat plugins, followed by registration, execution order, and best‑practice recommendations.

Three Knives
Three Knives
Three Knives
How to Build Custom smart-socket Plugins to Extend Framework Functionality

Plugin Interface and Lifecycle

Plugin<T> extends NetMonitor and defines two extension points: preProcess (called before MessageProcessor; returning false drops the message) and stateEvent (listens to state‑machine events such as session creation and closure).

public interface Plugin<T> extends NetMonitor {
    boolean preProcess(AioSession session, T t);
    void stateEvent(StateMachineEnum stateMachineEnum, AioSession session, Throwable throwable);
}

AbstractPlugin

AbstractPlugin<T> implements Plugin<T> with default implementations that return true for preProcess and perform no operation for stateEvent. All other NetMonitor methods have empty bodies, allowing developers to override only the needed methods.

public abstract class AbstractPlugin<T> implements Plugin<T> {
    @Override
    public boolean preProcess(AioSession session, T t) { return true; }
    @Override
    public void stateEvent(StateMachineEnum state, AioSession session, Throwable throwable) { }
    // other NetMonitor methods have empty implementations
}

Plugin Lifecycle

Connection acceptance – shouldAccept decides whether to accept a new connection.

Session creation – stateEvent receives NEW_SESSION.

Message handling – preProcess can filter or modify messages.

Session closure – stateEvent receives SESSION_CLOSED.

Custom Function Plugins

Message Filter Plugin

public class MessageFilterPlugin<T> extends AbstractPlugin<T> {
    private final Set<String> forbiddenWords;
    public MessageFilterPlugin(Set<String> forbiddenWords) {
        this.forbiddenWords = forbiddenWords;
    }
    @Override
    public boolean preProcess(AioSession session, T message) {
        if (message instanceof String) {
            String msg = (String) message;
            for (String word : forbiddenWords) {
                if (msg.contains(word)) {
                    System.err.println("Found illegal message: " + msg + ", contains keyword: " + word);
                    return false; // drop the message
                }
            }
        }
        return true; // allow processing
    }
}

Connection Limit Plugin

public class ConnectionLimitPlugin<T> extends AbstractPlugin<T> {
    private final int maxConnections;
    private final AtomicInteger currentConnections = new AtomicInteger(0);
    public ConnectionLimitPlugin(int maxConnections) {
        this.maxConnections = maxConnections;
    }
    @Override
    public AsynchronousSocketChannel shouldAccept(AsynchronousSocketChannel channel) {
        if (currentConnections.get() >= maxConnections) {
            System.err.println("Connection limit reached: " + maxConnections);
            try { channel.close(); } catch (IOException e) { e.printStackTrace(); }
            return null; // reject connection
        }
        currentConnections.incrementAndGet();
        return channel;
    }
    @Override
    public void stateEvent(StateMachineEnum state, AioSession session, Throwable throwable) {
        if (state == StateMachineEnum.SESSION_CLOSED) {
            currentConnections.decrementAndGet();
        }
    }
}

Heartbeat Detection Plugin

Extend the built‑in HeartPlugin to customize heartbeat request generation and detection.

processor.addPlugin(new HeartPlugin<String>(5, 7, TimeUnit.SECONDS) {
    @Override
    public void sendHeartRequest(AioSession session) throws IOException {
        WriteBuffer writeBuffer = session.writeBuffer();
        byte[] content = "heart message".getBytes();
        writeBuffer.writeInt(content.length);
        writeBuffer.write(content);
    }
    @Override
    public boolean isHeartMessage(AioSession session, String msg) {
        return "heart message".equals(msg);
    }
});

Plugin Registration and Execution Order

Plugins are registered with a MessageProcessor via addPlugin. Execution follows the registration order; the first added plugin runs first. If any plugin's preProcess returns false, subsequent plugins are skipped and the message is not delivered to the processor.

processor.addPlugin(plugin1); // first
processor.addPlugin(plugin2); // second
processor.addPlugin(plugin3); // third

Example Registration

public class PluginRegistrationExample {
    public static void main(String[] args) throws IOException {
        MessageProcessor<String> processor = new AbstractMessageProcessor<String>() {
            @Override
            public void process0(AioSession session, String msg) {
                System.out.println("Processing message: " + msg);
            }
            @Override
            public void stateEvent0(AioSession session, StateMachineEnum state, Throwable throwable) {}
        };
        processor.addPlugin(new MessageFilterPlugin<>(Set.of("badword1", "badword2")));
        processor.addPlugin(new ConnectionLimitPlugin<>(100));
        AioQuickServer<String> server = new AioQuickServer<>(8888, new StringProtocol(), processor);
        server.start();
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendJavapluginnetworkingsmart-socket
Three Knives
Written by

Three Knives

Every line of code you contribute to open source could help make the future better.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.