title: Is there a standard library/API/tool for implementing basic "rule chains" in Java?
body: <p>I am writing a servlet that will be conditionally modifying HTTP headers according to some user-definable rules. (<strong>Edit: these rules are defined in an XML file that is read at start-up.</strong>) For example, add &quot;X-UA-Compatible: IE=edge,chrome=1&quot; to a response header if it does not already exist and if the request specified a &quot;User-Agent&quot; header matching a known pattern. Not having any better ideas, I attempted to make my own POJOs representing these rules. It &quot;works&quot; but I feel like there must be a more standard or more flexible way to do this.</p>
  <p>Are there general-purpose libraries or tools (whether built-in or 3rd-party) that would solve this problem? I have heard and read a little about &quot;rules engines&quot; but they seem like much more complex/heavy tools not meant for problems as simple as mine.</p>
  <p>To illustrate what I'm trying to do, I've created a simplified program that applies &quot;rules&quot; to numbers based on &quot;conditions&quot; like &quot;is an even number&quot;. Here it is, sorry it's a bit lengthy.</p>
  <h2>Main.java</h2>
  <pre><code>package my.example;
  
  import java.util.*;
  
  import my.example.conditions.*;
  import my.example.rules.*;
  
  public class Main {
      
      public static void main(String args[]) {
          // Some sample objects to evaluate
          Collection&lt;Integer&gt; numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
          print(numbers);
          
          // Define rules
          Collection&lt;Rule&lt;Integer&gt;&gt; rules = new ArrayList&lt;Rule&lt;Integer&gt;&gt;();
          rules.add(createRuleToMultiplyEvenNumbersBy4());
          rules.add(createRuleToAdd1ToEveryNumber());
          
          // Process the rules for each sample object
          Collection&lt;Integer&gt; newNumbers = new ArrayList&lt;Integer&gt;();
          for (Integer number : numbers) {
              Integer newNumber = number;
              for (Rule&lt;Integer&gt; rule : rules)
                  newNumber = rule.apply(newNumber);
              newNumbers.add(newNumber);
          }
  
          print(newNumbers);
      }
  
      private static Rule&lt;Integer&gt; createRuleToMultiplyEvenNumbersBy4() {
          MultiplyNumberRule rule = new MultiplyNumberRule(true, 4);
          rule.addCondition(new NumberIsEvenCondition());
          return rule;
      }
      
      private static Rule&lt;Integer&gt; createRuleToAdd1ToEveryNumber() {
          AddNumberRule rule = new AddNumberRule(true, 1);
          rule.addCondition(new ConstantCondition&lt;Integer&gt;(true));
          return rule;
      }
      
      private static void print(Collection&lt;Integer&gt; numbers) {
          System.out.print(&quot;Numbers: &quot;);
          for (Integer number : numbers) {
              System.out.print(number);
              System.out.print(&quot; &quot;);
          }
          System.out.print(&quot;\r\n&quot;);
      }   
      
  
  }
  </code></pre>
  <h2>Condition.java</h2>
  <pre><code>package my.example.conditions;
  
  public interface Condition&lt;T&gt; {
      boolean appliesTo(T obj);
  }
  </code></pre>
  <h2>ConstantCondition.java</h2>
  <pre><code>package my.example.conditions;
  
  public class ConstantCondition&lt;T&gt; implements Condition&lt;T&gt; {
      private boolean constant;
      
      public ConstantCondition(boolean alwaysReturnThisValue) {
          constant = alwaysReturnThisValue;
      }
      
      @Override
      public boolean appliesTo(T target) {
          return constant;
      }
  }
  </code></pre>
  <h2>NumberIsEvenCondition.java</h2>
  <pre><code>package my.example.conditions;
  
  public class NumberIsEvenCondition implements Condition&lt;Integer&gt; {
      @Override
      public boolean appliesTo(Integer i) {
          return (i % 2 == 0);
      }
  }
  </code></pre>
  <h2>Rule.java</h2>
  <pre><code>package my.example.rules;
  
  public interface Rule&lt;T&gt; {
      T apply(T target);
  }
  </code></pre>
  <h2>AbstractRule.java</h2>
  <pre><code>package my.example.rules;
  
  import java.util.*;
  
  import my.example.conditions.Condition;
  public abstract class AbstractRule&lt;T&gt; implements Rule&lt;T&gt; {
      private Collection&lt;Condition&lt;T&gt;&gt; conditions;
      private boolean requireAllConditions;
      
      public AbstractRule(boolean requireAllConditions) {
          conditions = new ArrayList&lt;Condition&lt;T&gt;&gt;();
          this.requireAllConditions = requireAllConditions;
      }
  
      public void addCondition(Condition&lt;T&gt; condition) {
          conditions.add(condition);
      }
  
      @Override
      public T apply(T target) {
          boolean isApplicable;
          if (requireAllConditions)
              isApplicable = allConditionsSatisfied(target);
          else
              isApplicable = atLeastOneConditionSatisfied(target);
          
          if (isApplicable)
              target = process(target);
          
          return target;
      }
      
      // Check if all conditions are met
      protected boolean allConditionsSatisfied(T target) {
          for (Condition&lt;T&gt; condition : conditions) {
              if (!condition.appliesTo(target))
                  return false;
          }       
          return true;
      }
      
      // Check if any conditions are met
      protected boolean atLeastOneConditionSatisfied(T target) {
          for (Condition&lt;T&gt; condition : conditions) {
              if (condition.appliesTo(target))
                  return true;
          }       
          return false;
      }
  
      abstract T process(T target);
  
  }
  </code></pre>
  <h2>AddNumberRule.java</h2>
  <pre><code>package my.example.rules;
  
  public class AddNumberRule extends AbstractRule&lt;Integer&gt; {
      private Integer addend;
      
      public AddNumberRule(boolean requireAllConditions) {
          this(requireAllConditions, 0);
      }
      
      public AddNumberRule(boolean requireAllConditions, Integer addend) {
          super(requireAllConditions);
          this.addend = addend;
      }
      
      @Override
      public Integer process(Integer i) {
          return i + addend;
      }
  }
  </code></pre>
  <h2>MultiplyNumberRule.java</h2>
  <pre><code>package my.example.rules;
  
  public class MultiplyNumberRule extends AbstractRule&lt;Integer&gt; {
      private Integer factor;
      
      public MultiplyNumberRule(boolean requireAllConditions) {
          this(requireAllConditions, 1);
      }
      
      public MultiplyNumberRule(boolean requireAllConditions, Integer factor) {
          super(requireAllConditions);
          this.factor = factor;
      }
      
      @Override
      public Integer process(Integer i) {
          return i * factor;
      }
  }
  </code></pre>