Skip to content

Mejora: separar las validaciones de doDecode() con Chain of Responsibility #124

Description

@gabordonp

Hola @jzyong,
ProtocolDecoderImpl.doDecode() realiza cuatro validaciones distintas (longitud mínima, longitud máxima, control de flujo/anti-flood y verificación de buffer completo) en un único método secuencial con múltiples returns y manejo manual del cursor del buffer.
Situación actual:
Agregar una nueva regla de validación (por ejemplo, una lista negra de IP) obliga a modificar este método ya complejo, con riesgo de introducir regresiones en el framing del protocolo.
Solución propuesta:
Aplicar el patrón Chain of Responsibility, extrayendo cada validación a una clase DecodeValidator independiente, encadenadas en una secuencia configurable (MinLengthValidator -> MaxLengthValidator -> FloodControlValidator -> ...).
Esto permite agregar o reordenar validaciones sin tocar la lógica de lectura del buffer existente. Quedamos atentos a sus comentarios.
1. Contexto y Contrato
// Clase para compartir estado entre validadores
public class DecodeContext {

private int length;
private int maxReadSize;
private Predicate<IoSession> overMaxReadSizeHandler;

public int getLength() { return length; }
public void setLength(int length) { this.length = length; }
public int getMaxReadSize() { return maxReadSize; }
public void setMaxReadSize(int maxReadSize) { this.maxReadSize = maxReadSize; }
public Predicate<IoSession> getOverMaxReadSizeHandler() { return overMaxReadSizeHandler; }
public void setOverMaxReadSizeHandler(Predicate<IoSession> h) { this.overMaxReadSizeHandler = h; }

}

// Interfaz para los eslabones de la cadena
public interface DecodeValidator {

/** true = continua la cadena, false = detiene el procesamiento del mensaje. */
boolean validate(IoSession session, IoBuffer ib, ProtocolDecoderOutput out, DecodeContext ctx);

}

2. Implementación de la Cadena en ProtocolDecoderImpl
public class ProtocolDecoderImpl extends CumulativeProtocolDecoder {

private final List<DecodeValidator> chain = Arrays.asList(
    new MinLengthValidator(),
    new MaxLengthValidator(),
    new FloodControlValidator(),
    new BufferCompletenessValidator()
);

@Override
protected boolean doDecode(IoSession session, IoBuffer ib, ProtocolDecoderOutput out) throws Exception {
    ib.mark();
    DecodeContext ctx = new DecodeContext();
    ctx.setMaxReadSize(maxReadSize);
    ctx.setOverMaxReadSizeHandler(overMaxReadSizeHandler);

    // Ejecución de la cadena de responsabilidad
    for (DecodeValidator validator : chain) {
        if (!validator.validate(session, ib, out, ctx)) {
            ib.reset();
            return false;
        }
    }
    // ... continuación de la lógica de decodificación ...
}

}

3. Eslabones Concretos (Validadores)
class MinLengthValidator implements DecodeValidator {

@Override
public boolean validate(IoSession s, IoBuffer ib, ProtocolDecoderOutput out, DecodeContext ctx) {
    return ib.remaining() >= 4;
}

}

class MaxLengthValidator implements DecodeValidator {

@Override
public boolean validate(IoSession s, IoBuffer ib, ProtocolDecoderOutput out, DecodeContext ctx) {
    return ctx.getLength() > 0 && ctx.getLength() <= ctx.getMaxReadSize();
}

}

class FloodControlValidator implements DecodeValidator {

@Override
public boolean validate(IoSession s, IoBuffer ib, ProtocolDecoderOutput out, DecodeContext ctx) {
    return ctx.getOverMaxReadSizeHandler() == null || !ctx.getOverMaxReadSizeHandler().test(s);
}

}

class BufferCompletenessValidator implements DecodeValidator {

@Override
public boolean validate(IoSession s, IoBuffer ib, ProtocolDecoderOutput out, DecodeContext ctx) {
    return ib.remaining() >= ctx.getLength();
}

}

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions