phax / as2-lib

A generic Java AS2 library, servlet and server
107 stars 43 forks source link

Spring boot: possible to make custom handler a spring managed bean? #136

Closed jfrense closed 2 years ago

jfrense commented 2 years ago

I was wondering if its possible to load the custom handler module as a spring managed component on startup so that we have access to the application context in the custom handler?

I have the following config.xml with my custom handler (As2MessageHandler) defined:

<openas2>
    <certificates classname="com.helger.as2lib.cert.CertificateFactory"
                  type="pkcs12"
                  filename="%home%/certs.p12"
                  password="test"
                  interval="1000"/>
    <partnerships classname="com.helger.as2lib.partner.SelfFillingPartnershipFactory"
                  filename="%home%/partnerships.xml"/>

    <processor classname="com.helger.as2lib.processor.DefaultMessageProcessor"
               pendingMDN="data/pendingMDN"
               pendingMDNinfo="data/pendinginfoMDN">
        <!-- Required to receive messages -->
        <!-- port is required internally - simply ignore it for servlets -->
        <module classname="com.helger.as2servlet.util.AS2ServletReceiverModule"
                port="10080"
                errordir="data/inbox/error"
                errorformat="$msg.sender.as2_id$, $msg.receiver.as2_id$, $msg.headers.message-id$"/>

        <!-- Only needed to receive asynchronous MDNs -->
        <module classname="com.helger.as2servlet.util.AS2ServletMDNReceiverModule" />

        <!-- Custom Handler for incoming AS2 messages -->
        <module classname="com.as2example.myexample.handler.As2MessageHandler"
        />
    </processor>
</openas2>

And in my As2Handler class I would like to access the application context so I can access drop the message onto a spring integration channel for further downstream processing: As2MessageHandler class:

@Component
public class As2MessageHandler extends AbstractProcessorModule implements IProcessorStorageModule {

    @Autowired
    ApplicationContext context;

    private static final Logger LOGGER = LoggerFactory.getLogger(As2MessageHandler.class);

    @Override
    public boolean canHandle(@Nonnull String s, @Nonnull IMessage iMessage, @Nullable Map<String, Object> map) {
        LOGGER.info(" Handle Info:" + s);

        return s.equals(DO_STORE);
    }

    @Override
    public void handle(@Nonnull String s, @Nonnull IMessage iMessage, @Nullable Map<String, Object> map) throws AS2Exception {

        LOGGER.info("----- AS2 MESSAGE RECEIVED !!! ------");

        LOGGER.info(iMessage.getContentType());
        LOGGER.info(iMessage.getContentDisposition());
        LOGGER.info(iMessage.getAsString());

        ((As2MessageGateway) context.getBean("as2MessageGateway")).processMessage(iMessage);

    }

}

^^ When running the spring boot application I currently get context not defined which makes me thing that the As2 Session is not referencing the spring managed implementation of this class. Can you provide any insight on how to achieve this?

phax commented 2 years ago

Honestly I have no clue, how the dependency injection works with objects that are dynamically generated. Obviously your custom As2MessageHandler is instantiated using reflection by as2-lib. I think the best thing you can do, is to explicitly pass in the ApplicationContext via setter.

If you look at the example application, class ServletConfig you may iterate all objects of AS2_SESSION.getMessageProcessor ().getAllModules () and check with instanceof for your handler. Then call a setApplicationContext method you need to implement. hth

jfrense commented 2 years ago

Thanks for the insight! I was able to add the As2handler programmatically in the servlet config so it can be spring managed and thus easily access the spring application context.


    @Autowired
    private As2MessageHandler as2MessageHandler;

    private void initAS2Modules() {

        // Adding AS2 Handler Programmatically so it can be spring managed.

        try {
            AS2_SESSION.getMessageProcessor().addModule(as2MessageHandler);
            AS2_SESSION.getMessageProcessor().startActiveModules();
        } catch (final AS2Exception ex)
        {
            throw new InitializationException (ex);
        }

    }

    @Bean
    public ServletRegistrationBean <MyAS2ReceiveServlet> servletRegistrationBeanAS2 ()
    {
        initAS2Modules();

        _initScope ();
        return new ServletRegistrationBean <> (new MyAS2ReceiveServlet (), "/as2");
    }

    @Bean
    public ServletRegistrationBean <MyAS2MDNReceiveServlet> servletRegistrationBeanMDN ()
    {
        _initScope ();

        return new ServletRegistrationBean <> (new MyAS2MDNReceiveServlet (), "/as2mdn");
    }
phax commented 2 years ago

This way seems to be even smarter :) Thanks for sharing!