ImplementationFinder.java

  1. package fr.sii.ogham.testing.assertion.internal.helper;

  2. import static org.apache.commons.lang3.reflect.FieldUtils.readField;

  3. import java.util.HashSet;
  4. import java.util.List;
  5. import java.util.Set;

  6. import fr.sii.ogham.core.message.Message;
  7. import fr.sii.ogham.core.sender.ConditionalSender;
  8. import fr.sii.ogham.core.sender.ContentTranslatorSender;
  9. import fr.sii.ogham.core.sender.MessageSender;
  10. import fr.sii.ogham.core.sender.MultiImplementationSender;
  11. import fr.sii.ogham.core.sender.MultiImplementationSender.Implementation;
  12. import fr.sii.ogham.core.service.CleanableMessagingService;
  13. import fr.sii.ogham.core.service.EverySupportingMessagingService;
  14. import fr.sii.ogham.core.service.MessagingService;
  15. import fr.sii.ogham.core.service.WrapExceptionMessagingService;
  16. import fr.sii.ogham.core.template.parser.AutoDetectTemplateParser;
  17. import fr.sii.ogham.core.template.parser.TemplateParser;
  18. import fr.sii.ogham.core.template.parser.AutoDetectTemplateParser.TemplateImplementation;
  19. import fr.sii.ogham.core.translator.content.ContentTranslator;
  20. import fr.sii.ogham.core.translator.content.EveryContentTranslator;
  21. import fr.sii.ogham.core.translator.content.MultiContentTranslator;
  22. import fr.sii.ogham.core.translator.content.TemplateContentTranslator;
  23. import fr.sii.ogham.email.message.Email;
  24. import fr.sii.ogham.email.sender.EmailSender;
  25. import fr.sii.ogham.sms.message.Sms;
  26. import fr.sii.ogham.sms.sender.SmsSender;

  27. /**
  28.  * Utility class to find implementations used by Ogham service.
  29.  *
  30.  * @author AurĂ©lien Baudet
  31.  *
  32.  */
  33. public final class ImplementationFinder {
  34.     private static final String DELEGATE_FIELD = "delegate";

  35.     /**
  36.      * Find recursively a finder of the provided class
  37.      *
  38.      * @param <T>
  39.      *            the type of the found sender
  40.      * @param service
  41.      *            the messaging service
  42.      * @param clazz
  43.      *            the class of the sender to find
  44.      * @return the found sender
  45.      */
  46.     public static <T extends MessageSender> T findSender(MessagingService service, Class<T> clazz) {
  47.         Set<T> found = findSenders(service, clazz);
  48.         if (found.isEmpty()) {
  49.             throw new IllegalStateException("Failed to find MessageSender of " + clazz.getTypeName());
  50.         }
  51.         if (found.size() == 1) {
  52.             return found.iterator().next();
  53.         }
  54.         throw new IllegalStateException("Several matching MessageSender for " + clazz.getTypeName() + " found");
  55.     }

  56.     /**
  57.      * Find all senders for the given type
  58.      *
  59.      * @param <T>
  60.      *            the type of the senders to find
  61.      * @param service
  62.      *            the messaging service
  63.      * @param clazz
  64.      *            the class of the senders to find
  65.      * @return the found senders
  66.      */
  67.     @SuppressWarnings("unchecked")
  68.     public static <T extends MessageSender> Set<T> findSenders(MessagingService service, Class<T> clazz) {
  69.         try {
  70.             Set<T> found = new HashSet<>();
  71.             List<ConditionalSender> senders = (List<ConditionalSender>) readField(getRealService(service), "senders", true);
  72.             for (ConditionalSender sender : senders) {
  73.                 found.addAll(findSenders(sender, clazz));
  74.             }
  75.             return found;
  76.         } catch (IllegalAccessException e) {
  77.             throw new IllegalStateException("Failed to find senders of type " + clazz.getTypeName(), e);
  78.         }
  79.     }

  80.     /**
  81.      * Find template parsers of the given type.
  82.      *
  83.      * @param <T>
  84.      *            the type of the template parsers to find
  85.      * @param service
  86.      *            the messaging service
  87.      * @param clazz
  88.      *            the class of the template parsers to find
  89.      * @return the found parsers
  90.      */
  91.     public static <T extends TemplateParser> Set<FoundParser<T>> findParsers(MessagingService service, Class<T> clazz) {
  92.         try {
  93.             Set<FoundParser<T>> found = new HashSet<>();
  94.             Set<ContentTranslatorSender> translatorSenders = findSenders(service, ContentTranslatorSender.class);
  95.             for (ContentTranslatorSender sender : translatorSenders) {
  96.                 Set<TemplateContentTranslator> translators = findTranslators(sender, TemplateContentTranslator.class);
  97.                 for (TemplateContentTranslator translator : translators) {
  98.                     found.addAll(findParsers(clazz, translator, (MessageSender) readField(sender, DELEGATE_FIELD, true)));
  99.                 }
  100.             }
  101.             return found;
  102.         } catch (IllegalAccessException e) {
  103.             throw new IllegalStateException("Failed to find parser of type " + clazz.getTypeName(), e);
  104.         }
  105.     }

  106.     private static MessagingService getRealService(MessagingService service) {
  107.         try {
  108.             if (service instanceof WrapExceptionMessagingService) {
  109.                 return getRealService((MessagingService) readField(service, DELEGATE_FIELD, true));
  110.             }
  111.             if (service instanceof CleanableMessagingService) {
  112.                 return getRealService((MessagingService) readField(service, DELEGATE_FIELD, true));
  113.             }
  114.             if (service instanceof EverySupportingMessagingService) {
  115.                 return service;
  116.             }
  117.             throw new IllegalStateException("Unknown MessagingService implementation, please add it here");
  118.         } catch (IllegalAccessException e) {
  119.             throw new IllegalStateException("Failed to find real MessagingService", e);
  120.         }
  121.     }

  122.     @SuppressWarnings("unchecked")
  123.     private static <T extends MessageSender> Set<T> findSenders(MessageSender sender, Class<T> clazz) {
  124.         try {
  125.             Set<T> found = new HashSet<>();
  126.             if (clazz.isAssignableFrom(sender.getClass())) {
  127.                 found.add((T) sender);
  128.             }
  129.             // Any sender that delegates in the chain (FillerSender,
  130.             // AttachmentResourceTranslatorSender, ContentTranslatorSender,
  131.             // PhoneNumberTranslatorSender)
  132.             // TODO: FallbackSender
  133.             if (delegates(sender)) {
  134.                 MessageSender delegate = (MessageSender) readField(sender, DELEGATE_FIELD, true);
  135.                 found.addAll(findSenders(delegate, clazz));
  136.             }
  137.             if (sender instanceof MultiImplementationSender<?>) {
  138.                 found.addAll(findSenders((MultiImplementationSender<?>) sender, clazz));
  139.             }
  140.             return found;
  141.         } catch (IllegalAccessException e) {
  142.             throw new IllegalStateException("Failed to find senders of type " + clazz.getTypeName(), e);
  143.         }
  144.     }

  145.     private static boolean delegates(MessageSender sender) {
  146.         try {
  147.             Object value = readField(sender, DELEGATE_FIELD, true);
  148.             return value instanceof MessageSender;
  149.         } catch (IllegalAccessException | IllegalArgumentException e) { // NOSONAR
  150.             return false;
  151.         }
  152.     }

  153.     @SuppressWarnings("unchecked")
  154.     private static <T extends MessageSender> Set<T> findSenders(MultiImplementationSender<?> sender, Class<T> clazz) {
  155.         Set<T> found = new HashSet<>();
  156.         List<Implementation> implementations = sender.getImplementations();
  157.         for (Implementation impl : implementations) {
  158.             if (clazz.isAssignableFrom(impl.getSender().getClass())) {
  159.                 found.add((T) impl.getSender());
  160.             }
  161.         }
  162.         return found;
  163.     }

  164.     @SuppressWarnings("unchecked")
  165.     private static <T extends TemplateParser> Set<FoundParser<T>> findParsers(Class<T> clazz, TemplateContentTranslator translator, MessageSender sender) throws IllegalAccessException {
  166.         Set<FoundParser<T>> found = new HashSet<>();
  167.         TemplateParser parser = (TemplateParser) readField(translator, "parser", true);
  168.         if (clazz.isAssignableFrom(parser.getClass())) {
  169.             found.add(new FoundParser<>((T) parser, getMessageType(sender)));
  170.         }
  171.         if (parser instanceof AutoDetectTemplateParser) {
  172.             found.addAll(findParsers(clazz, (AutoDetectTemplateParser) parser, sender));
  173.         }
  174.         return found;
  175.     }

  176.     @SuppressWarnings("unchecked")
  177.     private static <T extends TemplateParser> Set<FoundParser<T>> findParsers(Class<T> clazz, AutoDetectTemplateParser parser, MessageSender sender) throws IllegalAccessException {
  178.         Set<FoundParser<T>> found = new HashSet<>();
  179.         List<TemplateImplementation> implementations = (List<TemplateImplementation>) readField(parser, "implementations", true);
  180.         for (TemplateImplementation impl : implementations) {
  181.             if (clazz.isAssignableFrom(impl.getParser().getClass())) {
  182.                 found.add(new FoundParser<>((T) impl.getParser(), getMessageType(sender)));
  183.             }
  184.         }
  185.         return found;
  186.     }

  187.     private static Class<? extends Message> getMessageType(MessageSender sender) {
  188.         Set<EmailSender> emailSenders = findSenders(sender, EmailSender.class);
  189.         if (!emailSenders.isEmpty()) {
  190.             return Email.class;
  191.         }
  192.         Set<SmsSender> smsSenders = findSenders(sender, SmsSender.class);
  193.         if (!smsSenders.isEmpty()) {
  194.             return Sms.class;
  195.         }
  196.         throw new IllegalStateException("Failed to find message type");
  197.     }

  198.     private static <T extends ContentTranslator> Set<T> findTranslators(ContentTranslatorSender translatorSender, Class<T> clazz) {
  199.         try {
  200.             ContentTranslator translator = (ContentTranslator) readField(translatorSender, "translator", true);
  201.             return findTranslators(translator, clazz);
  202.         } catch (IllegalAccessException e) {
  203.             throw new IllegalStateException("Failed to find translator of type " + clazz.getTypeName(), e);
  204.         }
  205.     }

  206.     private static <T extends ContentTranslator> Set<T> findTranslators(ContentTranslator translator, Class<T> clazz) {
  207.         try {
  208.             Set<T> found = new HashSet<>();
  209.             if (translator instanceof EveryContentTranslator) {
  210.                 found.addAll(findTranslators((EveryContentTranslator) translator, clazz));
  211.             }
  212.             if (translator instanceof MultiContentTranslator) {
  213.                 found.addAll(findTranslators((ContentTranslator) readField(translator, DELEGATE_FIELD, true), clazz));
  214.             }
  215.             return found;
  216.         } catch (IllegalAccessException e) {
  217.             throw new IllegalStateException("Failed to read 'delegate' of MultiContentTranslator", e);
  218.         }
  219.     }

  220.     @SuppressWarnings("unchecked")
  221.     private static <T extends ContentTranslator> Set<T> findTranslators(EveryContentTranslator translator, Class<T> clazz) {
  222.         Set<T> found = new HashSet<>();
  223.         for (ContentTranslator t : translator.getTranslators()) {
  224.             if (clazz.isAssignableFrom(t.getClass())) {
  225.                 found.add((T) t);
  226.             }
  227.         }
  228.         return found;
  229.     }

  230.     private ImplementationFinder() {
  231.         super();
  232.     }
  233. }