class AbstractSAML2MessageSender<T extends SAMLObject> implements SAML2MessageSender<T> {
    protected final SignatureSigningParametersProvider signatureSigningParametersProvider;
    protected final String destinationBindingType;
    protected final boolean signErrorResponses;
    protected final boolean isRequestSigned;

    /** {@inheritDoc} */
    @Override
    public void sendMessage(final SAML2MessageContext context,
                            final T request,
                            final Object relayState) {<FILL_FUNCTION_BODY>}

    /**
     * <p>storeMessage.</p>
     *
     * @param context a {@link SAML2MessageContext} object
     * @param request a T object
     */
    protected void storeMessage(final SAML2MessageContext context, final T request) {
        val messageStorage = context.getSamlMessageStore();
        if (messageStorage != null) {
            if (request instanceof RequestAbstractType requestAbstractType) {
                messageStorage.set(requestAbstractType.getID(), request);
            } else if (request instanceof StatusResponseType statusResponseType) {
                messageStorage.set(statusResponseType.getID(), request);
            }
        }
    }

    /**
     * <p>getEndpoint.</p>
     *
     * @param context a {@link SAML2MessageContext} object
     * @return a {@link Endpoint} object
     */
    protected abstract Endpoint getEndpoint(SAML2MessageContext context);

    /**
     * <p>invokeOutboundMessageHandlers.</p>
     *
     * @param spDescriptor a {@link SPSSODescriptor} object
     * @param idpssoDescriptor a {@link IDPSSODescriptor} object
     * @param messageContext a {@link MessageContext} object
     */
    protected void invokeOutboundMessageHandlers(final SPSSODescriptor spDescriptor,
                                                       final IDPSSODescriptor idpssoDescriptor,
                                                       final MessageContext messageContext) {
        try {
            MessageHandler handlerEnd =
                new EndpointURLSchemeSecurityHandler();
            handlerEnd.initialize();
            handlerEnd.invoke(messageContext);

            MessageHandler handlerDest =
                new SAMLOutboundDestinationHandler();
            handlerDest.initialize();
            handlerDest.invoke(messageContext);

            if (!destinationBindingType.equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) &&
                    mustSignRequest(spDescriptor, idpssoDescriptor)) {
                LOGGER.debug("Signing SAML2 outbound context...");
                val handler = new
                    SAMLOutboundProtocolMessageSigningHandler();
                handler.setSignErrorResponses(this.signErrorResponses);
                handler.initialize();
                handler.invoke(messageContext);
            }
        } catch (final Exception e) {
            throw new SAMLException(e);
        }
    }

    /**
     * <p>mustSignRequest.</p>
     *
     * @param spDescriptor a {@link SPSSODescriptor} object
     * @param idpssoDescriptor a {@link IDPSSODescriptor} object
     * @return a boolean
     */
    protected boolean mustSignRequest(final SPSSODescriptor spDescriptor, final IDPSSODescriptor idpssoDescriptor) {
        return isRequestSigned;
    }

    private MessageEncoder getMessageEncoder(final SPSSODescriptor spDescriptor,
            final IDPSSODescriptor idpssoDescriptor,
            final SAML2MessageContext ctx) {

        val adapter = ctx.getProfileRequestContextOutboundMessageTransportResponse();

        if (SAMLConstants.SAML2_POST_BINDING_URI.equals(destinationBindingType)) {
            val velocityEngine = VelocityEngineFactory.getEngine();
            val encoder = new Pac4jHTTPPostEncoder(adapter);
            encoder.setVelocityEngine(velocityEngine);
            return encoder;

        } else if (SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI.equals(destinationBindingType)) {
            val velocityEngine = VelocityEngineFactory.getEngine();
            val encoder = new Pac4jHTTPPostSimpleSignEncoder(adapter);
            encoder.setVelocityEngine(velocityEngine);
            return encoder;

        } else if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(destinationBindingType)) {
            return new Pac4jHTTPRedirectDeflateEncoder(adapter, mustSignRequest(spDescriptor, idpssoDescriptor));

        } else {
            throw new UnsupportedOperationException("Binding type - "
                + destinationBindingType + " is not supported");
        }
    }
}


        val spDescriptor = context.getSPSSODescriptor();
        val idpssoDescriptor = context.getIDPSSODescriptor();

        val acsService = context.getSPAssertionConsumerService();

        val encoder = getMessageEncoder(spDescriptor, idpssoDescriptor, context);

        val outboundContext = new SAML2MessageContext(context.getCallContext());
        outboundContext.setMessageContext(context.getMessageContext());
        outboundContext.getProfileRequestContext().setProfileId(outboundContext.getProfileRequestContext().getProfileId());

        outboundContext.getProfileRequestContext().setInboundMessageContext(
            context.getProfileRequestContext().getInboundMessageContext());
        outboundContext.getProfileRequestContext().setOutboundMessageContext(
            context.getProfileRequestContext().getOutboundMessageContext());

        outboundContext.getMessageContext().setMessage(request);
        outboundContext.getSAMLEndpointContext().setEndpoint(acsService);
        outboundContext.getSAMLPeerEndpointContext().setEndpoint(getEndpoint(outboundContext));

        outboundContext.getSAMLPeerEntityContext().setRole(outboundContext.getSAMLPeerEntityContext().getRole());
        outboundContext.getSAMLPeerEntityContext().setEntityId(outboundContext.getSAMLPeerEntityContext().getEntityId());
        outboundContext.getSAMLProtocolContext().setProtocol(outboundContext.getSAMLProtocolContext().getProtocol());
        outboundContext.getSecurityParametersContext()
            .setSignatureSigningParameters(this.signatureSigningParametersProvider.build(spDescriptor));

        if (relayState != null) {
            outboundContext.getSAMLBindingContext().setRelayState(relayState.toString());
        }

        try {
            val messageContext = outboundContext.getMessageContext();
            invokeOutboundMessageHandlers(spDescriptor, idpssoDescriptor, messageContext);

            encoder.setMessageContext(messageContext);
            encoder.initialize();
            encoder.prepareContext();
            encoder.encode();

            storeMessage(context, request);
            SAML2Utils.logProtocolMessage(request);
        } catch (final MessageEncodingException e) {
            throw new SAMLException("Error encoding saml message", e);
        } catch (final ComponentInitializationException e) {
            throw new SAMLException("Error initializing saml encoder", e);
        }
    
<no_super_class>