001: package com.protomatter.syslog;
002:
003: /**
004: * {{{ The Protomatter Software License, Version 1.0
005: * derived from The Apache Software License, Version 1.1
006: *
007: * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution,
022: * if any, must include the following acknowledgment:
023: * "This product includes software developed for the
024: * Protomatter Software Project
025: * (http://protomatter.sourceforge.net/)."
026: * Alternately, this acknowledgment may appear in the software itself,
027: * if and wherever such third-party acknowledgments normally appear.
028: *
029: * 4. The names "Protomatter" and "Protomatter Software Project" must
030: * not be used to endorse or promote products derived from this
031: * software without prior written permission. For written
032: * permission, please contact support@protomatter.com.
033: *
034: * 5. Products derived from this software may not be called "Protomatter",
035: * nor may "Protomatter" appear in their name, without prior written
036: * permission of the Protomatter Software Project
037: * (support@protomatter.com).
038: *
039: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
040: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
041: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
042: * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
043: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
044: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
045: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
046: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
047: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
048: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
049: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
050: * SUCH DAMAGE. }}}
051: */
052:
053: import java.util.*;
054: import java.text.MessageFormat;
055: import org.apache.oro.text.regex.*;
056:
057: /**
058: * A policy that can make decision on a per-class basis.
059: * It maintains a default log mask and channel list
060: * itself, but also has a list of "policy groups" that
061: * each have a log mask and channel list of their own
062: * in addition to a list of class names that their
063: * mask and channel list applies to. If a message
064: * coming from a class in one of those lists matches
065: * the mask and channel list, the message is logged. If
066: * not, but the message severity and channel match the
067: * default mask and channel list, it is also logged.
068: * Otherwise, the message is ignored.<P>
069: *
070: * This policy can be used to effectively say that
071: * messages from classes <TT>A</TT> and <TT>B</TT> should be logged
072: * if their severity is <TT>WARNING</TT> or greater, and that
073: * messages from classes <TT>C</TT> and <TT>D</TT> should be logged
074: * if their severity is <TT>INFO</TT> or greater and on a certain
075: * set of channels and that if all else fails, messages at
076: * or above the <TT>INFO</TT> level will be logged. It's
077: * very configurable.<P>
078: *
079: * Each "class name" is actually a Perl5 regular expression,
080: * so you can match things like "<tt>com.protomatter.syslog.*</tt>"
081: * and other stuff. This functionality uses the
082: * <a href="http://jakarta.apache.org/oro/index.html">ORO regular expression package</a>,
083: * now part of the
084: * <a href="http://jakarta.apache.org">Apache Jakarta</a> project.<P>
085: *
086: * @see com.protomatter.syslog.xml.PerClassPolicy_Helper XML configuration class
087: */
088: public class PerClassPolicy extends SimpleLogPolicy {
089: private List groupList = new ArrayList();
090: private static Perl5Compiler patternCompiler = new Perl5Compiler();
091:
092: /**
093: * Default constructor.
094: */
095: public PerClassPolicy() {
096: super ();
097: }
098:
099: /**
100: * Get the list of policy groups.
101: */
102: public List getGroupList() {
103: return this .groupList;
104: }
105:
106: /**
107: * Set the list of policy groups.
108: */
109: public void setGroupList(List list) {
110: this .groupList = list;
111: }
112:
113: /**
114: * Decide if the message should be logged. Each
115: * policy group is asked if it should be logged,
116: * and if none of them say yes, then we defer
117: * to our superclass. Each policy group maintains
118: * a channel list, log mask and a set of class names
119: * -- to decide if it should log the message,
120: * the policy group first checks the message's
121: * severity and channel against its log mask
122: * and channel list. If it passes this test,
123: * the policy group checks to see if the
124: * class name of the logger is in it's list.
125: */
126: public boolean shouldLog(SyslogMessage message) {
127: // if any of the policy groups say yes, let it through
128: if (message.loggerClassname != null) {
129: int size = groupList.size();
130: PolicyGroup group = null;
131: for (int i = 0; i < size; i++) {
132: group = (PolicyGroup) groupList.get(i);
133: if (group.shouldLog(message))
134: return true;
135: }
136: }
137:
138: // otherwise, defer to the superclass, which
139: // knows about channels and log masks.
140: return super .shouldLog(message);
141: }
142:
143: /**
144: * Get the list of policy groups.
145: */
146: public Iterator getPolicyGroups() {
147: return this .groupList.iterator();
148: }
149:
150: /**
151: * Add a policy group to our list.
152: */
153: public void addPolicyGroup(PolicyGroup group) {
154: this .groupList.add(group);
155: }
156:
157: /**
158: * Remove a policy group from our list.
159: */
160: public void removePolicyGroup(PolicyGroup group) {
161: this .groupList.remove(group);
162: }
163:
164: /**
165: * A policy within a policy -- this is exactly like
166: * the SimpleLogPolicy except that it also checks to
167: * see if the class issuing the log message is
168: * in some set.
169: *
170: * @see PerClassPolicy
171: */
172: public static class PolicyGroup extends SimpleLogPolicy {
173: private Set patternSet = new HashSet();
174: private Set classNameSet = new HashSet();
175: private Pattern pattern = null;
176: private Object lock = new Object();
177: private Perl5Matcher patternMatcher = new Perl5Matcher();
178:
179: /**
180: * Default constructor.
181: */
182: public PolicyGroup() {
183: super ();
184: }
185:
186: /**
187: * Get the set of class names (exact matches) we're listing to.
188: */
189: public Iterator getClassSet() {
190: return this .classNameSet.iterator();
191: }
192:
193: /**
194: * Clear out the set of class names
195: * we're listing to.
196: */
197: public void clearClassSet() {
198: this .classNameSet = new HashSet();
199: }
200:
201: /**
202: * Add a classname to the set of class names
203: * we're listening to.
204: */
205: public void addClass(String classname) {
206: this .classNameSet.add(classname);
207: }
208:
209: /**
210: * Remove a class name from the set of
211: * class names we're listening to.
212: */
213: public void removeClass(String classname) {
214: this .classNameSet.remove(classname);
215: }
216:
217: /**
218: * Get the set of class patterns we're listing to.
219: */
220: public Iterator getPatternSet() {
221: return this .patternSet.iterator();
222: }
223:
224: /**
225: * Clear out the set of class name patterns
226: * we're listing to.
227: */
228: public void clearPatternSet() {
229: this .patternSet = new HashSet();
230: this .pattern = null;
231: }
232:
233: /**
234: * Add a classname to the set of class name
235: * patterns we're listening to.
236: */
237: public void addClassPattern(String classname) {
238: this .patternSet.add(classname);
239: generatePattern();
240: }
241:
242: /**
243: * Remove a class name pattern from the set of
244: * class name patterns we're listening to.
245: */
246: public void removeClassPattern(String classname) {
247: this .patternSet.remove(classname);
248: generatePattern();
249: }
250:
251: /**
252: * Recompile the pattern each time something
253: * about the set of patterns changes.
254: */
255: private void generatePattern() throws IllegalArgumentException {
256: StringBuffer thePattern = new StringBuffer();
257: try {
258: Iterator patterns = getPatternSet();
259: while (patterns.hasNext()) {
260: thePattern.append(patterns.next());
261: if (patterns.hasNext())
262: thePattern.append("|");
263: }
264: pattern = patternCompiler
265: .compile(thePattern.toString());
266: } catch (MalformedPatternException x) {
267: throw new IllegalArgumentException(
268: MessageFormat
269: .format(
270: Syslog
271: .getResources()
272: .getString(
273: MessageConstants.INVALID_PATTERN_MESSAGE),
274: new Object[] { thePattern }));
275: }
276: }
277:
278: /**
279: * Determine if the given message should be
280: * logged. The message severity and channel
281: * are first checked by our superclass, then
282: * we see if the logger class name is in our
283: * set.
284: */
285: public boolean shouldLog(SyslogMessage m) {
286: // passes the channel/level test.
287: if (super .shouldLog(m)) {
288: // exact matches...
289: if (classNameSet.contains(m.loggerClassname)) {
290: return true;
291: } else if (pattern != null) // pattern matches
292: {
293: synchronized (lock) {
294: if (patternMatcher.contains(m.loggerClassname,
295: pattern)) {
296: return true;
297: }
298: }
299: }
300: }
301:
302: // if it's not in our list, we don't care about it.
303: return false;
304: }
305: }
306: }
|