View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.rule;
19  
20  import java.util.Map;
21  import java.util.Stack;
22  
23  import org.apache.log4j.spi.LoggingEvent;
24  
25  
26  /***
27   * A Rule class supporting both infix and postfix expressions,
28   * accepting any rule which
29   * is supported by the <code>RuleFactory</code>.
30   *
31   * NOTE: parsing is supported through the use of
32   * <code>StringTokenizer</code>, which
33   * implies two limitations:
34   * 1: all tokens in the expression must be separated by spaces,
35   * including parenthesis
36   * 2: operands which contain spaces MUST be wrapped in single quotes.
37   *    For example, the expression:
38   *      msg == 'some msg'
39   *    is a valid expression.
40   * 3: To group expressions, use parentheses.
41   *    For example, the expression:
42   *      level >= INFO || ( msg == 'some msg' || logger == 'test' )
43   *    is a valid expression.
44   * See org.apache.log4j.rule.InFixToPostFix for a
45   * description of supported operators.
46   * See org.apache.log4j.spi.LoggingEventFieldResolver for field keywords.
47   *
48   * @author Scott Deboy (sdeboy@apache.org)
49   */
50  public class ExpressionRule extends AbstractRule {
51      /***
52       * Serialization ID.
53       */
54    static final long serialVersionUID = 5809121703146893729L;
55      /***
56       * Converter.
57       */
58    private static final InFixToPostFix CONVERTER = new InFixToPostFix();
59      /***
60       * Compiler.
61       */
62    private static final PostFixExpressionCompiler COMPILER =
63            new PostFixExpressionCompiler();
64      /***
65       * Rule.
66       */
67    private final Rule rule;
68  
69      /***
70       * Create new instance.
71       * @param r rule
72       */
73    private ExpressionRule(final Rule r) {
74      super();
75      this.rule = r;
76    }
77  
78      /***
79       * Get rule.
80       * @param expression expression.
81       * @return rule.
82       */
83    public static Rule getRule(final String expression) {
84        return getRule(expression, false);
85    }
86  
87      /***
88       * Get rule.
89       * @param expression expression.
90       * @param isPostFix If post-fix.
91       * @return rule
92       */
93    public static Rule getRule(final String expression,
94                               final boolean isPostFix) {
95      String postFix = expression;
96      if (!isPostFix) {
97        postFix = CONVERTER.convert(expression);
98      }
99  
100     return new ExpressionRule(COMPILER.compileExpression(postFix));
101   }
102 
103     /***
104      * {@inheritDoc}
105      */
106   public boolean evaluate(final LoggingEvent event, Map matches) {
107     return rule.evaluate(event, matches);
108   }
109 
110     /***
111      * {@inheritDoc}
112      */
113   public String toString() {
114       return rule.toString();
115   }
116 
117   /***
118    * Evaluate a boolean postfix expression.
119    *
120    */
121   static final class PostFixExpressionCompiler {
122       /***
123        * Compile expression.
124        * @param expression expression.
125        * @return rule.
126        */
127     public Rule compileExpression(final String expression) {
128       RuleFactory factory = RuleFactory.getInstance();
129 
130       Stack stack = new Stack();
131       InFixToPostFix.CustomTokenizer tokenizer = new InFixToPostFix.CustomTokenizer(expression);
132 
133           while (tokenizer.hasMoreTokens()) {
134             //examine each token
135             String token = tokenizer.nextToken();
136               if (token.startsWith("'") || token.startsWith("\"")) {
137                 String quoteChar = token.substring(0, 1);
138                 token = token.substring(1);
139                 while (!token.endsWith(quoteChar) && tokenizer.hasMoreTokens()) {
140                   token = token + " " + tokenizer.nextToken();
141                 }
142                 if (token.length() > 0) {
143                   token = token.substring(0, token.length() - 1);
144                 }
145               } else {
146                 //if a symbol is found, pop 2 off the stack,
147                   // evaluate and push the result
148                 if (factory.isRule(token)) {
149                   Rule r = factory.getRule(token, stack);
150                   stack.push(r);
151                   //null out the token so we don't try to push it below
152                   token = null;
153                 }
154             }
155               //variables or constants are pushed onto the stack
156               if (token != null && token.length() > 0) {
157                   stack.push(token);
158               }
159           }
160 
161           if ((stack.size() == 1) && (!(stack.peek() instanceof Rule))) {
162             //while this may be an attempt at creating an expression,
163             //for ease of use, convert this single entry to a partial-text
164             //match on the MSG field
165             Object o = stack.pop();
166             stack.push("MSG");
167             stack.push(o);
168             return factory.getRule("~=", stack);
169           }
170 
171           //stack should contain a single rule if the expression is valid
172           if ((stack.size() != 1) || (!(stack.peek() instanceof Rule))) {
173             throw new IllegalArgumentException("invalid expression: " + expression);
174           } else {
175             return (Rule) stack.pop();
176           }
177         }
178   }
179 }
180 
181