0

I have a typical use case where i am consuming to a message broker. The messages are <^> delimited strings. I parse each and every message create POJO's and then apply different validations to understand if the message is useful for further processing. We call these validations as filters.

These filters are dynamically created by the users of the applications and these filters needs to be dynamically applied to the incoming messages.

As of now i am validating the messages using IF-ELSE loops. But, I would like to check if there are any design patterns which are already making this problem statement elegant.

A typical filter has a FilterCriteria which talks about the Conditions which needs to be checked against a message.

Code:

// load filters for a given party and apply on the rawSyslogMessage.
    private boolean applyFilter(RawSyslogMessage message) throws EntityNotFoundException {

        boolean isDropped = false;

        logger.info("---applyFilter()::rawSyslog :" + message);

        String partyID = message.getPartyID();

        // load all filters for the given party
        List<Filter> filters = filterService.getAll(partyID);

        if (filters != null) {
            for (Filter filter : filters) {

                FilterCriteria filterCriteria = filter.getFilterCriteria();
                String field = filterCriteria.getField();
                String condition = filterCriteria.getCondition();
                String action = filterCriteria.getAction();

                // FILTER. Consider applying all fitlers on a message.
                if (filter.getName().toUpperCase().equals("PRIORITY") && action.toUpperCase().equals("ALLOW")) {
                    if (condition.toUpperCase().equals("GREATER")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else {
                            logger.info("The message is sent for correlation");
                        }
                    } else if (condition.toUpperCase().equals("LESSER")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is sent for correlation");
                        } else {
                            logger.info("The message is dropped");
                            isDropped = true;
                        }
                    } else if (condition.toUpperCase().equals("EQUALS")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is sent for correlation");
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else {
                            logger.info("The message is dropped");
                            isDropped = true;
                        }
                    } else if (condition.toUpperCase().equals("BETWEEN")) {
                        String[] range = field.split("TO");
                        String _left = range[0];
                        String _right = range[1];
                        if (message.getSeverity() >= Integer.parseInt(_left)
                                && message.getSeverity() <= Integer.parseInt(_right)) {
                            logger.info("The message is sent for correlation");
                        } else {
                            logger.info("The message is dropped");
                            isDropped = true;
                        }
                    }
                } else if (filter.getName().toUpperCase().equals("PRIORITY")
                        && action.toUpperCase().equals("DISCARD")) {
                    if (condition.toUpperCase().equals("GREATER")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is sent for correlation");
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is sent for correlation");
                        } else {
                            logger.info("The message is dropped");
                            isDropped = true;
                        }
                    } else if (condition.toUpperCase().equals("LESSER")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is sent for correlation");
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else {
                            logger.info("The message is sent for correlation");
                        }
                    } else if (condition.toUpperCase().equals("EQUALS")) {
                        if (Long.toString(message.getSeverity()).equals(field)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else if (message.getSeverity() < Long.parseLong(field)) {
                            logger.info("The message is sent for correlation");
                        } else {
                            logger.info("The message is sent for correlation");
                        }
                    } else if (condition.toUpperCase().equals("BETWEEN")) {
                        String[] range = field.split("TO");
                        String _left = range[0];
                        String _right = range[1];
                        if (message.getSeverity() >= Integer.parseInt(_left)
                                && message.getSeverity() <= Integer.parseInt(_right)) {
                            logger.info("The message is dropped");
                            isDropped = true;
                        } else {
                            logger.info("The message is sent for correlation");
                        }
                    }
                }
            }
        }

        return isDropped;
    }

I have referred through the

https://en.wikipedia.org/wiki/Specification_pattern (as suggested in Style for control flow with validation checks)

https://en.wikipedia.org/wiki/Strategy_pattern

https://en.wikipedia.org/wiki/Command_pattern

But, none of these seem to be fitting my requirements. Please help.

  • Possible duplicate of Style for control flow with validation checks – gnat Jan 5 '18 at 12:36
  • @gnat: see my edit. – Doc Brown Jan 5 '18 at 13:33
  • thanks @DocBrown, upon a closer look this reads kind of a 'list of things' question – gnat Jan 5 '18 at 13:35
  • If your conditions, actions and filters were Java enums, that simplifies the mapping a bit and lets you have specific implementations to process whether something gets dropped or not. I've used that strategy for some parsers to good effect. That way you can have the filter call the action which in turn calls the condition to validate if the message is good or not. If it is not good, drop it. – Berin Loritsch Jan 5 '18 at 13:43
  • Just a reminder, enums in Java are objects, and the objects can be mapped by calling Enum.valueOf(): stackoverflow.com/questions/604424/lookup-enum-by-string-value – Berin Loritsch Jan 5 '18 at 13:45
2

Take a look at Intercepting filter pattern, it might fit your purpose. It provides flexibility and extensibility for possibly creating new filter types, and you could use it to bind together the desired filter combinations for specific tasks.

0

Well the most obvious improvement would be that instead of the Filter class just being a bag of properties, and your single long method doing the filtering bases on these properties, "Filter" would be an interface and you would have subclasses implementing it that would include the actual code for performing each type of filter.

I'm not sure exactly which design pattern this would be, probably the Strategy Pattern, which you already mention. What about that pattern makes you think it doesn't match your requirements?

0

Another approach to take is to use monads to hold the validation result. A good overview of the approach is provide in Functional Data Validation using monads and applicative functors. Basically, the monad object holds either the validated result, or a validation message that can logged, or used for more useful feedback.

For instance, using one of the Try monads for Java:

Try<String> validatedCondition = Try.apply( () -> validateCondition( condition ) );
Try<String> validatedField = Try.apply( () -> validateField( field ) );
...
if ( validatedFilterCriteria.isSuccess() &&
     validatedCondition.isSuccess() &&
     validatedField.isSuccess() ) {
  // now operate on the validated values
}
else {
  // a validation failed, so extract the failure message
  Try<String> result = validatedFilterCriteria
     .andThen( validatedCondition )
     .andThen( validatedField );
  log.error( "Validation failure: " + result.failureMessage() );
}

...
private String validateCondition( String condition ) {
  assert condition != null;
  assert !condition.isEqual("");
  ...
  if ( condition.isValid() ) {
     return condition;
  }
}
... // similarly for other validations

While Java is much more verbose than Scala, you can accomplish the same thing and make for more systematic validations.

0

A combination of the decorate and strategy patterns have been a very good solution for me when handling scanned questionnaires.

The validation is a strategy build from one or more strategies decorated around each other.

The user can then select each available strategy and the order of each strategy (ie. the order of how the strategies are decorated).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.