phax / as2-lib

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

Stack overflow caused by retries #16

Closed undernorthernsky closed 8 years ago

undernorthernsky commented 8 years ago

I have this setup

      final AS2Session as2Session = new AS2Session();
      as2Session.setCertificateFactory(new KeyStoreServiceCertificateFactory(keyStoreService));
      as2Session.setPartnershipFactory(partnershipFactory);

      DefaultMessageProcessor processor = new DefaultMessageProcessor();
      processor.stopActiveModules();
      processor.initDynamicComponent(as2Session, new StringMap());

      ImmediateResenderModule resender = new ImmediateResenderModule();
      resender.initDynamicComponent(as2Session, new StringMap());
      processor.addModule(resender);

      final AS2SenderModule aSender = new AS2SenderModule();
      aSender.initDynamicComponent(as2Session, new StringMap());
      processor.addModule(aSender);

      as2Session.setMessageProcessor(processor);
      processor.startActiveModules();

Furthermore (in a specific test) the partnership config specifies 3 retries; and I am sending to an URL where there is no(!) server listening (I was trying to verify that retries are working by delaying listener startup). While working on that the test fails like this:

    at com.helger.as2lib.processor.sender.AS2SenderModule._sendViaHTTP(AS2SenderModule.java:566)
    at com.helger.as2lib.processor.sender.AS2SenderModule.handle(AS2SenderModule.java:647)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.resender.ImmediateResenderModule.handle(ImmediateResenderModule.java:93)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.sender.AbstractSenderModule.doResend(AbstractSenderModule.java:129)
    at com.helger.as2lib.processor.sender.AS2SenderModule.handle(AS2SenderModule.java:664)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.resender.ImmediateResenderModule.handle(ImmediateResenderModule.java:93)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.sender.AbstractSenderModule.doResend(AbstractSenderModule.java:129)
    at com.helger.as2lib.processor.sender.AS2SenderModule.handle(AS2SenderModule.java:664)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.resender.ImmediateResenderModule.handle(ImmediateResenderModule.java:93)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.sender.AbstractSenderModule.doResend(AbstractSenderModule.java:129)
    at com.helger.as2lib.processor.sender.AS2SenderModule.handle(AS2SenderModule.java:664)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.resender.ImmediateResenderModule.handle(ImmediateResenderModule.java:93)
    at com.helger.as2lib.processor.DefaultMessageProcessor.handle(DefaultMessageProcessor.java:142)
    at com.helger.as2lib.processor.sender.AbstractSenderModule.doResend(AbstractSenderModule.java:129)
    at com.helger.as2lib.processor.sender.AS2SenderModule.handle(AS2SenderModule.java:664)

The lowest line is from the actual call to "send"; the chain of bouncing from resender -> message-processor -> sender -> ... gets longer.

This seems to be caused by AbstractSenderModule::getRetryCount. The value passed via aOptions is ignored if checking aPartnership yields a result; I assume aOptions should have priority, otherwise decrementing the counter in the resender module has no effect?

This seems to work for me:

    List<Integer> retryValues = new ArrayList<>();

    if (aPartnership != null)
    {
      // Get from partnership
      final String sTriesLeft = aPartnership.getAttribute (IProcessorResenderModule.OPTION_RETRIES);
      if (sTriesLeft != null) {
        retryValues.add(Integer.parseInt(sTriesLeft));
      }
    }

    if ((aOptions != null) && (aOptions.get(IProcessorResenderModule.OPTION_RETRIES) != null))
    {
      // Provided in the options?
      final String  sTriesLeft = (String) aOptions.get (IProcessorResenderModule.OPTION_RETRIES);
      retryValues.add(Integer.parseInt(sTriesLeft));
    }

    final String fromAttr = getAttributeAsString(IProcessorResenderModule.OPTION_RETRIES);
    if (fromAttr != null)
    {
      // No. Provided as an attribute?
      retryValues.add(Integer.parseInt(fromAttr));
    }

    if (retryValues.isEmpty())
    {
      // Not provided. Use default.
      return IProcessorResenderModule.DEFAULT_RETRIES;
    }

    // Avoid returning negative values
    return retryValues.stream()
            .filter((v) -> v >= 0)
            .min(Comparator.naturalOrder()).orElse(0); 
phax commented 8 years ago

As a quick fix, I removed the support for reading the retries from the partnership. I think your version looks like a plan :)

phax commented 8 years ago

There is a now a Math.min version contained. Thanks again for pointing it out

phax commented 8 years ago

Fixed in release 2.2.5

undernorthernsky commented 8 years ago

Works fine, thank you for the quick fix.