Moved component and receivers companion sources to the Chainsaw source tree - those companions won't be released.
Project: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/commit/08c7be5c Tree: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/tree/08c7be5c Diff: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/diff/08c7be5c Branch: refs/heads/master Commit: 08c7be5cdb56374f77d297a0e90440aeef541b8a Parents: decd6ab Author: Scott Deboy <[email protected]> Authored: Mon Oct 3 06:15:40 2011 +0000 Committer: Scott Deboy <[email protected]> Committed: Mon Oct 3 06:15:40 2011 +0000 ---------------------------------------------------------------------- HOWTOBUILD.txt | 13 - pom.xml | 38 +- .../apache/log4j/LoggerRepositoryExImpl.java | 697 ++++++++++++ src/main/java/org/apache/log4j/ULogger.java | 203 ++++ .../org/apache/log4j/db/ConnectionSource.java | 69 ++ .../log4j/db/ConnectionSourceSkeleton.java | 150 +++ .../apache/log4j/db/CustomSQLDBReceiver.java | 468 ++++++++ .../java/org/apache/log4j/db/DBAppender.java | 403 +++++++ src/main/java/org/apache/log4j/db/DBHelper.java | 68 ++ .../java/org/apache/log4j/db/DBReceiver.java | 140 +++ .../java/org/apache/log4j/db/DBReceiverJob.java | 230 ++++ .../log4j/db/DataSourceConnectionSource.java | 105 ++ .../log4j/db/DriverManagerConnectionSource.java | 133 +++ .../apache/log4j/db/JNDIConnectionSource.java | 143 +++ .../apache/log4j/db/dialect/HSQLDBDialect.java | 31 + .../apache/log4j/db/dialect/MsSQLDialect.java | 34 + .../apache/log4j/db/dialect/MySQLDialect.java | 32 + .../apache/log4j/db/dialect/OracleDialect.java | 33 + .../log4j/db/dialect/PostgreSQLDialect.java | 35 + .../org/apache/log4j/db/dialect/SQLDialect.java | 27 + .../apache/log4j/db/dialect/SybaseDialect.java | 32 + .../java/org/apache/log4j/db/dialect/Util.java | 125 +++ .../java/org/apache/log4j/db/dialect/db2.sql | 64 ++ .../java/org/apache/log4j/db/dialect/db2l.sql | 61 + .../java/org/apache/log4j/db/dialect/hsqldb.sql | 60 + .../java/org/apache/log4j/db/dialect/mssql.sql | 61 + .../java/org/apache/log4j/db/dialect/mysql.sql | 71 ++ .../java/org/apache/log4j/db/dialect/oracle.sql | 77 ++ .../org/apache/log4j/db/dialect/postgresql.sql | 63 ++ src/main/java/org/apache/log4j/db/package.html | 36 + .../org/apache/log4j/helpers/Constants.java | 126 +++ .../apache/log4j/helpers/MessageFormatter.java | 154 +++ .../apache/log4j/helpers/UtilLoggingLevel.java | 238 ++++ .../java/org/apache/log4j/net/AddressBased.java | 36 + .../java/org/apache/log4j/net/JMSReceiver.java | 301 +++++ .../apache/log4j/net/JMSReceiverBeanInfo.java | 52 + .../org/apache/log4j/net/MulticastAppender.java | 345 ++++++ .../org/apache/log4j/net/MulticastReceiver.java | 276 +++++ .../log4j/net/MulticastReceiverBeanInfo.java | 51 + .../java/org/apache/log4j/net/NetworkBased.java | 39 + .../java/org/apache/log4j/net/PortBased.java | 34 + .../org/apache/log4j/net/SocketHubReceiver.java | 409 +++++++ .../java/org/apache/log4j/net/SocketNode13.java | 299 +++++ .../log4j/net/SocketNodeEventListener.java | 43 + .../org/apache/log4j/net/SocketReceiver.java | 479 ++++++++ .../java/org/apache/log4j/net/UDPAppender.java | 330 ++++++ .../java/org/apache/log4j/net/UDPReceiver.java | 281 +++++ .../org/apache/log4j/net/XMLSocketNode.java | 205 ++++ .../org/apache/log4j/net/XMLSocketReceiver.java | 316 ++++++ .../org/apache/log4j/plugins/Pauseable.java | 39 + .../java/org/apache/log4j/plugins/Plugin.java | 144 +++ .../org/apache/log4j/plugins/PluginEvent.java | 47 + .../apache/log4j/plugins/PluginListener.java | 43 + .../apache/log4j/plugins/PluginRegistry.java | 303 +++++ .../apache/log4j/plugins/PluginSkeleton.java | 222 ++++ .../java/org/apache/log4j/plugins/Receiver.java | 131 +++ .../apache/log4j/rewrite/MapRewritePolicy.java | 85 ++ .../log4j/rewrite/PropertyRewritePolicy.java | 93 ++ .../log4j/rewrite/ReflectionRewritePolicy.java | 89 ++ .../apache/log4j/rewrite/RewriteAppender.java | 199 ++++ .../org/apache/log4j/rewrite/RewritePolicy.java | 37 + .../java/org/apache/log4j/scheduler/Job.java | 36 + .../org/apache/log4j/scheduler/Scheduler.java | 307 ++++++ .../java/org/apache/log4j/spi/Component.java | 37 + .../org/apache/log4j/spi/ComponentBase.java | 126 +++ src/main/java/org/apache/log4j/spi/Decoder.java | 63 ++ .../java/org/apache/log4j/spi/ErrorItem.java | 172 +++ .../java/org/apache/log4j/spi/Log4JULogger.java | 229 ++++ .../apache/log4j/spi/LoggerEventListener.java | 59 + .../spi/LoggerRepositoryEventListener.java | 53 + .../apache/log4j/spi/LoggerRepositoryEx.java | 198 ++++ .../java/org/apache/log4j/spi/NOPULogger.java | 200 ++++ .../org/apache/log4j/spi/SimpleULogger.java | 305 +++++ .../org/apache/log4j/spi/Thresholdable.java | 58 + .../apache/log4j/varia/ListModelAppender.java | 79 ++ .../log4j/varia/LogFilePatternReceiver.java | 1043 ++++++++++++++++++ .../varia/LogFilePatternReceiverBeanInfo.java | 53 + .../apache/log4j/xml/LogFileXMLReceiver.java | 310 ++++++ .../log4j/xml/UtilLoggingEntityResolver.java | 50 + .../apache/log4j/xml/UtilLoggingXMLDecoder.java | 468 ++++++++ .../java/org/apache/log4j/xml/XMLDecoder.java | 495 +++++++++ .../log4j/chainsaw/help/release-notes.html | 1 + .../java/org/apache/log4j/VectorAppender.java | 91 ++ .../org/apache/log4j/db/FullCycleDBTest.java | 327 ++++++ .../log4j/helpers/UtilLoggingLevelTest.java | 46 + .../log4j/rewrite/RewriteAppenderTest.java | 132 +++ .../java/org/apache/log4j/util/Compare.java | 202 ++++ .../org/apache/log4j/xml/XMLDecoderTest.java | 86 ++ .../log4j/db/append-with-drivermanager1.xml | 48 + .../log4j/db/read-with-drivermanager1.xml | 55 + .../resources/org/apache/log4j/rewrite/map.log | 3 + .../resources/org/apache/log4j/rewrite/map.xml | 38 + .../org/apache/log4j/rewrite/property.log | 2 + .../org/apache/log4j/rewrite/property.xml | 40 + .../org/apache/log4j/rewrite/reflection.log | 3 + .../org/apache/log4j/rewrite/reflection.xml | 38 + .../org/apache/log4j/xml/xmlLayout.1.xml | 162 +++ .../org/apache/log4j/xml/xsltLayout.1.xml | 139 +++ 98 files changed, 14874 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/HOWTOBUILD.txt ---------------------------------------------------------------------- diff --git a/HOWTOBUILD.txt b/HOWTOBUILD.txt index 7375489..ebbd4bb 100644 --- a/HOWTOBUILD.txt +++ b/HOWTOBUILD.txt @@ -23,19 +23,6 @@ Firstly, you'll need maven 2.0.9+ to build Chainsaw: http://maven.apache.org -Next, an interim step is required until the 'companions' are voted on and released: - -* SVN check out the following projects: - log4j-component - http://svn.apache.org/repos/asf/logging/log4j/companions/component/trunk/ - log4j-extras - http://svn.apache.org/repos/asf/logging/log4j/companions/extras/trunk/ - log4j-receivers - http://svn.apache.org/repos/asf/logging/log4j/companions/receivers/trunk/ - - In each of these checkout directories: - mvn install - - This will install into your local maven repository the correct releases of the required dependencies. - These dependencies are not yet available in the standard maven repositories, and so failure to complete this step will prevent you from building Chainsaw. - * cd to Chainsaw project checkout directory mvn install http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index f434fcd..18c0f43 100644 --- a/pom.xml +++ b/pom.xml @@ -118,8 +118,7 @@ <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> - <source>1.2</source> - <target>1.1</target> + <source>1.4</source> </configuration> </plugin> <plugin> @@ -220,7 +219,7 @@ <artifactId>ant-contrib</artifactId> <version>1.0b2</version> </dependency> - </dependencies> + </dependencies> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> @@ -324,6 +323,14 @@ <iconFile>${basedir}/src/main/resources/logo.icns</iconFile> <bundleName>Chainsaw</bundleName> </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>bundle</goal> + </goals> + </execution> + </executions> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> @@ -386,17 +393,6 @@ </dependency> <dependency> <groupId>log4j</groupId> - <artifactId>apache-log4j-receivers</artifactId> - <version>1.0-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>log4j</groupId> - <artifactId>apache-log4j-receivers</artifactId> - <version>1.0-SNAPSHOT</version> - <classifier>javadoc</classifier> - </dependency> - <dependency> - <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> @@ -437,6 +433,18 @@ <artifactId>jsch</artifactId> <version>0.1.42</version> </dependency> + <dependency> + <groupId>hsqldb</groupId> + <artifactId>hsqldb</artifactId> + <version>1.8.0.7</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jms_1.1_spec</artifactId> + <version>1.0</version> + <optional>true</optional> + </dependency> </dependencies> <reporting> <excludeDefaults>true</excludeDefaults> @@ -480,7 +488,7 @@ </plugin> </plugins> </reporting> - <distributionManagement> + <distributionManagement> <repository> <id>logging.repo</id> <url>scp://people.apache.org/www/people.apache.org/builds/logging/repo/</url> http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/LoggerRepositoryExImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/LoggerRepositoryExImpl.java b/src/main/java/org/apache/log4j/LoggerRepositoryExImpl.java new file mode 100644 index 0000000..c2f5aaf --- /dev/null +++ b/src/main/java/org/apache/log4j/LoggerRepositoryExImpl.java @@ -0,0 +1,697 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j; + +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.or.ObjectRenderer; +import org.apache.log4j.or.RendererMap; +import org.apache.log4j.plugins.Plugin; +import org.apache.log4j.plugins.PluginRegistry; +import org.apache.log4j.scheduler.Scheduler; +import org.apache.log4j.spi.ErrorItem; +import org.apache.log4j.spi.HierarchyEventListener; +import org.apache.log4j.spi.LoggerEventListener; +import org.apache.log4j.spi.LoggerFactory; +import org.apache.log4j.spi.LoggerRepository; +import org.apache.log4j.spi.LoggerRepositoryEventListener; +import org.apache.log4j.spi.LoggerRepositoryEx; +import org.apache.log4j.spi.RendererSupport; +import org.apache.log4j.xml.UnrecognizedElementHandler; +import org.apache.log4j.xml.DOMConfigurator; +import org.w3c.dom.Element; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + + +/** + * This class implements LoggerRepositoryEx by + * wrapping an existing LoggerRepository implementation + * and implementing the newly added capabilities. +*/ +public final class LoggerRepositoryExImpl + implements LoggerRepositoryEx, + RendererSupport, + UnrecognizedElementHandler { + + /** + * Wrapped logger repository. + */ + private final LoggerRepository repo; + + /** + * Logger factory. Does not affect class of logger + * created by underlying repository. + */ + private LoggerFactory loggerFactory; + + /** + * Renderer support. + */ + private final RendererSupport rendererSupport; + + /** + * List of repository event listeners. + */ + private final ArrayList repositoryEventListeners = new ArrayList(); + /** + * Map of HierarchyEventListener keyed by LoggingEventListener. + */ + private final Map loggerEventListeners = new HashMap(); + /** + * Name of hierarchy. + */ + private String name; + /** + * Plug in registry. + */ + private PluginRegistry pluginRegistry; + /** + * Properties. + */ + private final Map properties = new Hashtable(); + /** + * Scheduler. + */ + private Scheduler scheduler; + + /** The repository can also be used as an object store + * for various objects used by log4j components. + */ + private Map objectMap = new HashMap(); + + + /** + * Error list. + */ + private List errorList = new Vector(); + + /** + * True if hierarchy has not been modified. + */ + private boolean pristine = true; + + /** + Constructs a new logger hierarchy. + + @param repository Base implementation of repository. + + */ + public LoggerRepositoryExImpl(final LoggerRepository repository) { + super(); + if (repository == null) { + throw new NullPointerException("repository"); + } + repo = repository; + if (repository instanceof RendererSupport) { + rendererSupport = (RendererSupport) repository; + } else { + rendererSupport = new RendererSupportImpl(); + } + } + + + /** + Add a {@link LoggerRepositoryEventListener} to the repository. The + listener will be called when repository events occur. + @param listener listener + */ + public void addLoggerRepositoryEventListener( + final LoggerRepositoryEventListener listener) { + synchronized (repositoryEventListeners) { + if (repositoryEventListeners.contains(listener)) { + LogLog.warn( + "Ignoring attempt to add a previously " + + "registered LoggerRepositoryEventListener."); + } else { + repositoryEventListeners.add(listener); + } + } + } + + + /** + Remove a {@link LoggerRepositoryEventListener} from the repository. + @param listener listener + */ + public void removeLoggerRepositoryEventListener( + final LoggerRepositoryEventListener listener) { + synchronized (repositoryEventListeners) { + if (!repositoryEventListeners.contains(listener)) { + LogLog.warn( + "Ignoring attempt to remove a " + + "non-registered LoggerRepositoryEventListener."); + } else { + repositoryEventListeners.remove(listener); + } + } + } + + /** + Add a {@link LoggerEventListener} to the repository. The listener + will be called when repository events occur. + @param listener listener + */ + public void addLoggerEventListener(final LoggerEventListener listener) { + synchronized (loggerEventListeners) { + if (loggerEventListeners.get(listener) != null) { + LogLog.warn( + "Ignoring attempt to add a previously registerd LoggerEventListener."); + } else { + HierarchyEventListenerProxy proxy = + new HierarchyEventListenerProxy(listener); + loggerEventListeners.put(listener, proxy); + repo.addHierarchyEventListener(proxy); + } + } + } + + /** + Add a {@link org.apache.log4j.spi.HierarchyEventListener} + event to the repository. + @param listener listener + @deprecated Superceded by addLoggerEventListener + */ + public + void addHierarchyEventListener(final HierarchyEventListener listener) { + repo.addHierarchyEventListener(listener); + } + + + /** + Remove a {@link LoggerEventListener} from the repository. + @param listener listener to be removed + */ + public void removeLoggerEventListener(final LoggerEventListener listener) { + synchronized (loggerEventListeners) { + HierarchyEventListenerProxy proxy = + (HierarchyEventListenerProxy) loggerEventListeners.get(listener); + if (proxy == null) { + LogLog.warn( + "Ignoring attempt to remove a non-registered LoggerEventListener."); + } else { + loggerEventListeners.remove(listener); + proxy.disable(); + } + } + } + + /** + * Issue warning that there are no appenders in hierarchy. + * @param cat logger, not currently used. + */ + public void emitNoAppenderWarning(final Category cat) { + repo.emitNoAppenderWarning(cat); + } + + /** + Check if the named logger exists in the hierarchy. If so return + its reference, otherwise returns <code>null</code>. + + @param loggerName The name of the logger to search for. + @return true if logger exists. + */ + public Logger exists(final String loggerName) { + return repo.exists(loggerName); + } + + /** + * Return the name of this hierarchy. + * @return name of hierarchy + */ + public String getName() { + return name; + } + + /** + * Set the name of this repository. + * + * Note that once named, a repository cannot be rerenamed. + * @param repoName name of hierarchy + */ + public void setName(final String repoName) { + if (name == null) { + name = repoName; + } else if (!name.equals(repoName)) { + throw new IllegalStateException( + "Repository [" + name + "] cannot be renamed as [" + repoName + "]."); + } + } + + /** + * {@inheritDoc} + */ + public Map getProperties() { + return properties; + } + + /** + * {@inheritDoc} + */ + public String getProperty(final String key) { + return (String) properties.get(key); + } + + /** + * Set a property by key and value. The property will be shared by all + * events in this repository. + * @param key property name + * @param value property value + */ + public void setProperty(final String key, + final String value) { + properties.put(key, value); + } + + /** + The string form of {@link #setThreshold(Level)}. + @param levelStr symbolic name for level + */ + public void setThreshold(final String levelStr) { + repo.setThreshold(levelStr); + } + + /** + Enable logging for logging requests with level <code>l</code> or + higher. By default all levels are enabled. + + @param l The minimum level for which logging requests are sent to + their appenders. */ + public void setThreshold(final Level l) { + repo.setThreshold(l); + } + + /** + * {@inheritDoc} + */ + public PluginRegistry getPluginRegistry() { + if (pluginRegistry == null) { + pluginRegistry = new PluginRegistry(this); + } + return pluginRegistry; + } + + + /** + Requests that a appender added event be sent to any registered + {@link LoggerEventListener}. + @param logger The logger to which the appender was added. + @param appender The appender added to the logger. + */ + public void fireAddAppenderEvent(final Category logger, + final Appender appender) { + repo.fireAddAppenderEvent(logger, appender); + } + + + /** + Requests that a appender removed event be sent to any registered + {@link LoggerEventListener}. + @param logger The logger from which the appender was removed. + @param appender The appender removed from the logger. + */ + public void fireRemoveAppenderEvent(final Category logger, + final Appender appender) { + if (repo instanceof Hierarchy) { + ((Hierarchy) repo).fireRemoveAppenderEvent(logger, appender); + } + } + + + /** + Requests that a level changed event be sent to any registered + {@link LoggerEventListener}. + @param logger The logger which changed levels. + */ + public void fireLevelChangedEvent(final Logger logger) { + } + + /** + * + * Requests that a configuration changed event be sent to any registered + * {@link LoggerRepositoryEventListener}. + * + */ + public void fireConfigurationChangedEvent() { + } + + + /** + Returns the current threshold. + @return current threshold level + + @since 1.2 */ + public Level getThreshold() { + return repo.getThreshold(); + } + + + /** + Return a new logger instance named as the first parameter using + the default factory. + + <p>If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated and + then linked with its existing ancestors as well as children. + + @param loggerName The name of the logger to retrieve. + @return logger + + */ + public Logger getLogger(final String loggerName) { + return repo.getLogger(loggerName); + } + + /** + Return a new logger instance named as the first parameter using + <code>factory</code>. + + <p>If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated by the + <code>factory</code> parameter and linked with its existing + ancestors as well as children. + + @param loggerName The name of the logger to retrieve. + @param factory The factory that will make the new logger instance. + @return logger + + */ + public Logger getLogger(final String loggerName, + final LoggerFactory factory) { + return repo.getLogger(loggerName, factory); + } + + /** + Returns all the currently defined categories in this hierarchy as + an {@link java.util.Enumeration Enumeration}. + + <p>The root logger is <em>not</em> included in the returned + {@link Enumeration}. + @return enumerator of current loggers + */ + public Enumeration getCurrentLoggers() { + return repo.getCurrentLoggers(); + } + + /** + * Return the the list of previously encoutered {@link ErrorItem error items}. + * @return list of errors + */ + public List getErrorList() { + return errorList; + } + + /** + * Add an error item to the list of previously encountered errors. + * @param errorItem error to add to list of errors. + */ + public void addErrorItem(final ErrorItem errorItem) { + getErrorList().add(errorItem); + } + + /** + * Get enumerator over current loggers. + * @return enumerator over current loggers + @deprecated Please use {@link #getCurrentLoggers} instead. + */ + public Enumeration getCurrentCategories() { + return repo.getCurrentCategories(); + } + + /** + Get the renderer map for this hierarchy. + @return renderer map + */ + public RendererMap getRendererMap() { + return rendererSupport.getRendererMap(); + } + + /** + Get the root of this hierarchy. + + @since 0.9.0 + @return root of hierarchy + */ + public Logger getRootLogger() { + return repo.getRootLogger(); + } + + /** + This method will return <code>true</code> if this repository is + disabled for <code>level</code> value passed as parameter and + <code>false</code> otherwise. See also the {@link + #setThreshold(Level) threshold} method. + @param level numeric value for level. + @return true if disabled for specified level + */ + public boolean isDisabled(final int level) { + return repo.isDisabled(level); + } + + /** + Reset all values contained in this hierarchy instance to their + default. This removes all appenders from all categories, sets + the level of all non-root categories to <code>null</code>, + sets their additivity flag to <code>true</code> and sets the level + of the root logger to DEBUG. Moreover, + message disabling is set its default "off" value. + + <p>Existing categories are not removed. They are just reset. + + <p>This method should be used sparingly and with care as it will + block all logging until it is completed.</p> + + @since 0.8.5 */ + public void resetConfiguration() { + repo.resetConfiguration(); + } + + /** + Used by subclasses to add a renderer to the hierarchy passed as parameter. + @param renderedClass class + @param renderer object used to render class. + */ + public void setRenderer(final Class renderedClass, + final ObjectRenderer renderer) { + rendererSupport.setRenderer(renderedClass, renderer); + } + + /** + * {@inheritDoc} + */ + public boolean isPristine() { + return pristine; + } + + /** + * {@inheritDoc} + */ + public void setPristine(final boolean state) { + pristine = state; + } + + /** + Shutting down a hierarchy will <em>safely</em> close and remove + all appenders in all categories including the root logger. + + <p>Some appenders such as org.apache.log4j.net.SocketAppender + and AsyncAppender need to be closed before the + application exists. Otherwise, pending logging events might be + lost. + + <p>The <code>shutdown</code> method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + @since 1.0 */ + public void shutdown() { + repo.shutdown(); + } + + + /** + * Return this repository's own scheduler. + * The scheduler is lazily instantiated. + * @return this repository's own scheduler. + */ + public Scheduler getScheduler() { + if (scheduler == null) { + scheduler = new Scheduler(); + scheduler.setDaemon(true); + scheduler.start(); + } + return scheduler; + } + + /** + * Puts object by key. + * @param key key, may not be null. + * @param value object to associate with key. + */ + public void putObject(final String key, + final Object value) { + objectMap.put(key, value); + } + + /** + * Get object by key. + * @param key key, may not be null. + * @return object associated with key or null. + */ + public Object getObject(final String key) { + return objectMap.get(key); + } + + /** + * Set logger factory. + * @param factory logger factory. + */ + public void setLoggerFactory(final LoggerFactory factory) { + if (factory == null) { + throw new NullPointerException(); + } + this.loggerFactory = factory; + } + + /** + * Get logger factory. + * @return logger factory. + */ + public LoggerFactory getLoggerFactory() { + return loggerFactory; + } + + /** {@inheritDoc} */ + public boolean parseUnrecognizedElement( + final Element element, + final Properties props) throws Exception { + if ("plugin".equals(element.getNodeName())) { + Object instance = + DOMConfigurator.parseElement(element, props, Plugin.class); + if (instance instanceof Plugin) { + Plugin plugin = (Plugin) instance; + String pluginName = DOMConfigurator.subst(element.getAttribute("name"), props); + if (pluginName.length() > 0) { + plugin.setName(pluginName); + } + getPluginRegistry().addPlugin(plugin); + plugin.setLoggerRepository(this); + + LogLog.debug("Pushing plugin on to the object stack."); + plugin.activateOptions(); + return true; + } + } + return false; + } + + + + /** + * Implementation of RendererSupportImpl if not + * provided by LoggerRepository. + */ + private static final class RendererSupportImpl implements RendererSupport { + /** + * Renderer map. + */ + private final RendererMap renderers = new RendererMap(); + + /** + * Create new instance. + */ + public RendererSupportImpl() { + super(); + } + + /** {@inheritDoc} */ + public RendererMap getRendererMap() { + return renderers; + } + + /** {@inheritDoc} */ + public void setRenderer(final Class renderedClass, + final ObjectRenderer renderer) { + renderers.put(renderedClass, renderer); + } + } + + /** + * Proxy that implements HierarchyEventListener + * and delegates to LoggerEventListener. + */ + private static final class HierarchyEventListenerProxy + implements HierarchyEventListener { + /** + * Wrapper listener. + */ + private LoggerEventListener listener; + + /** + * Creates new instance. + * @param l listener + */ + public HierarchyEventListenerProxy(final LoggerEventListener l) { + super(); + if (l == null) { + throw new NullPointerException("l"); + } + listener = l; + } + + /** {@inheritDoc} */ + public void addAppenderEvent(final Category cat, + final Appender appender) { + if (isEnabled() && cat instanceof Logger) { + listener.appenderAddedEvent((Logger) cat, appender); + } + } + + /** {@inheritDoc} */ + public void removeAppenderEvent(final Category cat, + final Appender appender) { + if (isEnabled() && cat instanceof Logger) { + listener.appenderRemovedEvent((Logger) cat, appender); + } + } + + /** + * Disable forwarding of notifications to + * simulate removal of listener. + */ + public synchronized void disable() { + listener = null; + } + + /** + * Gets whether proxy is enabled. + * @return true if proxy is enabled. + */ + private synchronized boolean isEnabled() { + return listener != null; + } + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/ULogger.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/ULogger.java b/src/main/java/org/apache/log4j/ULogger.java new file mode 100644 index 0000000..83a6bb8 --- /dev/null +++ b/src/main/java/org/apache/log4j/ULogger.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j; + + +/** + * A proxy for org.slf4j.ULogger. In slf4j implementing builds, this + * interface will extend org.slf4j.ULogger and add no additional methods. + * + * @author Ceki Gülcü + * @author Curt Arnold + */ + public interface ULogger { + + + /** + * Is the logger instance enabled for the DEBUG level? + * @return true if debug is enabled. + */ + boolean isDebugEnabled(); + + /** + * Log a message object with the DEBUG level. + * @param msg - the message object to be logged + */ + void debug(Object msg); + + + /** + * Log a parameterized message object at the DEBUG level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the parameter + */ + void debug(Object parameterizedMsg, Object param1); + + /** + * Log a parameterized message object at the DEBUG level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the first parameter + * @param param2 - the second parameter + */ + void debug(String parameterizedMsg, Object param1, Object param2); + /** + * Log a message object with the <code>DEBUG</code> level including the + * stack trace of the {@link Throwable}<code>t</code> passed as parameter. + * + * + * @param msg the message object to log. + * @param t the exception to log, including its stack trace. + */ + void debug(Object msg, Throwable t); + + + /** + * Is the logger instance enabled for the INFO level? + * @return true if debug is enabled. + */ + boolean isInfoEnabled(); + /** + * Log a message object with the INFO level. + * @param msg - the message object to be logged + */ + void info(Object msg); + /** + * Log a parameterized message object at the INFO level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the parameter + */ + void info(Object parameterizedMsg, Object param1); + /** + * Log a parameterized message object at the INFO level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the first parameter + * @param param2 - the second parameter + */ + void info(String parameterizedMsg, Object param1, Object param2); + /** + * Log a message object with the <code>INFO</code> level including the + * stack trace of the {@link Throwable}<code>t</code> passed as parameter. + * + * + * @param msg the message object to log. + * @param t the exception to log, including its stack trace. + */ + void info(Object msg, Throwable t); + + + /** + * Is the logger instance enabled for the WARN level? + * @return true if debug is enabled. + */ + boolean isWarnEnabled(); + /** + * Log a message object with the WARN level. + * @param msg - the message object to be logged + */ + void warn(Object msg); + /** + * Log a parameterized message object at the WARN level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the parameter + */ + void warn(Object parameterizedMsg, Object param1); + /** + * Log a parameterized message object at the WARN level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the first parameter + * @param param2 - the second parameter + */ + void warn(String parameterizedMsg, Object param1, Object param2); + /** + * Log a message object with the <code>WARN</code> level including the + * stack trace of the {@link Throwable}<code>t</code> passed as parameter. + * + * + * @param msg the message object to log. + * @param t the exception to log, including its stack trace. + */ + void warn(Object msg, Throwable t); + + + /** + * Is the logger instance enabled for the ERROR level? + * @return true if debug is enabled. + */ + boolean isErrorEnabled(); + /** + * Log a message object with the ERROR level. + * @param msg - the message object to be logged + */ + void error(Object msg); + /** + * Log a parameterized message object at the ERROR level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the parameter + */ + void error(Object parameterizedMsg, Object param1); + /** + * Log a parameterized message object at the ERROR level. + * + * <p>This form is useful in avoiding the superflous object creation + * problem when invoking this method while it is disabled. + * </p> + * @param parameterizedMsg - the parameterized message object + * @param param1 - the first parameter + * @param param2 - the second parameter + */ + void error(String parameterizedMsg, Object param1, Object param2); + + /** + * Log a message object with the <code>ERROR</code> level including the + * stack trace of the {@link Throwable}<code>t</code> passed as parameter. + * + * + * @param msg the message object to log. + * @param t the exception to log, including its stack trace. + */ + void error(Object msg, Throwable t); + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/ConnectionSource.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/ConnectionSource.java b/src/main/java/org/apache/log4j/db/ConnectionSource.java new file mode 100644 index 0000000..0578734 --- /dev/null +++ b/src/main/java/org/apache/log4j/db/ConnectionSource.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import org.apache.log4j.spi.Component; +import org.apache.log4j.spi.OptionHandler; + +import java.sql.Connection; +import java.sql.SQLException; + + +/** + * The <id>ConnectionSource</id> interface provides a pluggable means of + * transparently obtaining JDBC {@link java.sql.Connection}s for log4j classes + * that require the use of a {@link java.sql.Connection}. + * + * @author <a href="mailto:[email protected]">Ray DeCampo</a> + */ +public interface ConnectionSource extends Component, OptionHandler { + + final int UNKNOWN_DIALECT = 0; + final int POSTGRES_DIALECT = 1; + final int MYSQL_DIALECT = 2; + final int ORACLE_DIALECT = 3; + final int MSSQL_DIALECT = 4; + final int HSQL_DIALECT = 5; + /** + * Obtain a {@link java.sql.Connection} for use. The client is + * responsible for closing the {@link java.sql.Connection} when it is no + * longer required. + * + * @throws SQLException if a {@link java.sql.Connection} could not be + * obtained + */ + Connection getConnection() throws SQLException; + + /** + * Get the SQL dialect that should be used for this connection. Note that the + * dialect is not needed if the JDBC driver supports the getGeneratedKeys + * method. + */ + int getSQLDialectCode(); + + /** + * If the connection supports the JDBC 3.0 getGeneratedKeys method, then + * we do not need any specific dialect support. + */ + boolean supportsGetGeneratedKeys(); + + /** + * If the connection does not support batch updates, we will avoid using them. + */ + public boolean supportsBatchUpdates(); +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/ConnectionSourceSkeleton.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/ConnectionSourceSkeleton.java b/src/main/java/org/apache/log4j/db/ConnectionSourceSkeleton.java new file mode 100644 index 0000000..a5abecb --- /dev/null +++ b/src/main/java/org/apache/log4j/db/ConnectionSourceSkeleton.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.apache.log4j.db.dialect.Util; +import org.apache.log4j.spi.ComponentBase; + + +/** + * @author Ceki Gülcü + */ +public abstract class ConnectionSourceSkeleton extends ComponentBase implements ConnectionSource { + + private Boolean overriddenSupportsGetGeneratedKeys = null; + + private String user = null; + private String password = null; + + // initially we have an unkonw dialect + private int dialectCode = UNKNOWN_DIALECT; + private boolean supportsGetGeneratedKeys = false; + private boolean supportsBatchUpdates = false; + + + /** + * Learn relevant information about this connection source. + * + */ + public void discoverConnnectionProperties() { + Connection connection = null; + try { + connection = getConnection(); + if (connection == null) { + getLogger().warn("Could not get a conneciton"); + return; + } + DatabaseMetaData meta = connection.getMetaData(); + Util util = new Util(); + util.setLoggerRepository(repository); + if (overriddenSupportsGetGeneratedKeys != null) { + supportsGetGeneratedKeys = overriddenSupportsGetGeneratedKeys + .booleanValue(); + } else { + supportsGetGeneratedKeys = util.supportsGetGeneratedKeys(meta); + } + supportsBatchUpdates = util.supportsBatchUpdates(meta); + dialectCode = Util.discoverSQLDialect(meta); + } catch (SQLException se) { + getLogger().warn("Could not discover the dialect to use.", se); + } finally { + DBHelper.closeConnection(connection); + } + } + + /** + * Does this connection support the JDBC Connection.getGeneratedKeys method? + */ + public final boolean supportsGetGeneratedKeys() { + return supportsGetGeneratedKeys; + } + + public final int getSQLDialectCode() { + return dialectCode; + } + + /** + * Get the password for this connection source. + */ + public final String getPassword() { + return password; + } + + /** + * Sets the password. + * @param password The password to set + */ + public final void setPassword(final String password) { + this.password = password; + } + + /** + * Get the user for this connection source. + */ + public final String getUser() { + return user; + } + + /** + * Sets the username. + * @param username The username to set + */ + public final void setUser(final String username) { + this.user = username; + } + + /** + * Returns the "overridden" value of "supportsGetGeneratedKeys" property of + * the JDBC driver. In certain cases, getting (e.g. Oracle 10g) generated keys + * does not work because it returns the ROWID, not the value of the sequence. + * + * @return A non null string, with "true" or "false" value, if overridden, + * <code>null</code> if not overridden. + */ + public String getOverriddenSupportsGetGeneratedKeys() { + return overriddenSupportsGetGeneratedKeys != null ? overriddenSupportsGetGeneratedKeys + .toString() + : null; + } + + /** + * Sets the "overridden" value of "supportsGetGeneratedKeys" property of the + * JDBC driver. In certain cases, getting (e.g. Oracle 10g) generated keys + * does not work because it returns the ROWID, not the value of the sequence. + * + * @param overriddenSupportsGetGeneratedKeys + * A non null string, with "true" or "false" value, if overridden, + * <code>null</code> if not overridden. + */ + public void setOverriddenSupportsGetGeneratedKeys( + String overriddenSupportsGetGeneratedKeys) { + this.overriddenSupportsGetGeneratedKeys = Boolean + .valueOf(overriddenSupportsGetGeneratedKeys); + } + + /** + * Does this connection support batch updates? + */ + public final boolean supportsBatchUpdates() { + return supportsBatchUpdates; + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/CustomSQLDBReceiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/CustomSQLDBReceiver.java b/src/main/java/org/apache/log4j/db/CustomSQLDBReceiver.java new file mode 100644 index 0000000..6c2d78b --- /dev/null +++ b/src/main/java/org/apache/log4j/db/CustomSQLDBReceiver.java @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.plugins.Pauseable; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.scheduler.Job; +import org.apache.log4j.scheduler.Scheduler; +import org.apache.log4j.spi.LocationInfo; +import org.apache.log4j.spi.LoggerRepositoryEx; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.spi.ThrowableInformation; +import org.apache.log4j.xml.DOMConfigurator; +import org.apache.log4j.xml.UnrecognizedElementHandler; +import org.w3c.dom.Element; + +/** + * Converts log data stored in a database into LoggingEvents. + * <p> + * <b>NOTE:</b> This receiver cannot yet be created through Chainsaw's receiver panel. + * It must be created through an XML configuration file. + * <p> + * This receiver supports database configuration via ConnectionSource, in the + * org.apache.log4j.db package: DriverManagerConnectionSource, + * DataSourceConnectionSource, JNDIConnectionSource + * <p> + * This database receiver differs from DBReceiver in that this receiver relies + * on custom SQL to retrieve logging event data, where DBReceiver requires the + * use of a log4j-defined schema. + * <p> + * A 'refreshMillis' int parameter controls SQL execution. If 'refreshMillis' is + * zero (the default), the receiver will run only one time. If it is set to any + * other numeric value, the SQL will be executed on a recurring basis every + * 'refreshMillis' milliseconds. + * <p> + * The receiver closes the connection and acquires a new connection on each + * execution of the SQL (use pooled connections if possible). + * <p> + * If the SQL will be executing on a recurring basis, specify the IDField param - + * the column name holding the unique identifier (int) representing the logging + * event. + * <p> + * As events are retrieved, the column represented by IDField is examined and + * the largest value is held and used by the next execution of the SQL statement + * to avoid retrieving previously processed events. + * <p> + * As an example, the IDField references a 'COUNTER' (int, auto-increment, + * unique) column. The first execution of the SQL statement returns 500 rows, + * with a final value in the COUNTER field of 500. + * <p> + * The SQL statement is manipulated prior to the next execution, adding ' WHERE + * COUNTER > 500' to the statement to avoid retrieval of previously processed + * events. + * <p> + * The select statement must provide ALL fields which define a LoggingEvent. + * <p> + * The SQL statement MUST include the columns: LOGGER, TIMESTAMP, LEVEL, THREAD, + * MESSAGE, NDC, MDC, CLASS, METHOD, FILE, LINE, PROPERTIES, THROWABLE + * <p> + * Use ' AS ' in the SQL statement to alias the SQL's column names to match your + * database schema. (see example below). + * <p> + * Include all fields in the SQL statement, even if you don't have data for the + * field (specify an empty string as the value for columns which you don't have + * data). + * <p> + * The TIMESTAMP column must be a datetime. + * <p> + * Both a PROPERTIES column and an MDC column are supported. These fields + * represent Maps on the logging event, but require the use of string + * concatenation database functions to hold the (possibly multiple) name/value + * pairs in the column. + * <p> + * For example, to include both 'userid' and 'lastname' properties in the + * logging event (from either the PROPERTIES or MDC columns), the name/value + * pairs must be concatenated together by your database. + * <p> + * The resulting PROPERTIES or MDC column must have data in this format: {{name, + * value, name2, value2}} + * <p> + * The resulting PROPERTIES column would contain this text: {{userid, someone, + * lastname, mylastname}} + * <p> + * Here is an example of concatenating a PROPERTIES or MDC column using MySQL's + * concat function, where the 'application' and 'hostname' parameters were fixed + * text, but the 'log4jid' key's value is the value of the COUNTER column: + * <p> + * concat("{{application,databaselogs,hostname,mymachine,log4jid,", COUNTER, + * "}}") as PROPERTIES + * <p> + * log4jid is a special property that is used by Chainsaw to represent an 'ID' + * field. Specify this property to ensure you can map events in Chainsaw to + * events in the database if you need to go back and view events at a later time + * or save the events to XML for later analysis. + * <p> + * Here is a complete MySQL SQL statement which can be used to provide events to + * Chainsaw (note how in the example below, there is no column in logtable representing the throwable, so an + * empty string is passed in and an ALIAS is still defined): + * <p> + * select myloggercolumn as LOGGER, mytimestampcolumn as TIMESTAMP, mylevelcolumn as LEVEL, mythreadcolumn as + * THREAD, mymessagecolumn as MESSAGE, myndccolumn as NDC, mymdccolumn as MDC, myclasscolumn as CLASS, mymethodcolumn as + * METHOD, myfilecolumn as FILE, mylinecolumn as LINE, + * concat("{{application,databaselogs,hostname,mymachine, log4jid,", + * COUNTER,"}}") as PROPERTIES, "" as THROWABLE from logtable + * <p> + * @author Scott Deboy <[email protected]> + * <p> + */ +public class CustomSQLDBReceiver extends Receiver implements Pauseable, UnrecognizedElementHandler { + + protected volatile Connection connection = null; + + protected String sqlStatement = ""; + + /** + * By default we refresh data every 1000 milliseconds. + * + * @see #setRefreshMillis + */ + static int DEFAULT_REFRESH_MILLIS = 1000; + + int refreshMillis = DEFAULT_REFRESH_MILLIS; + + protected String idField = null; + + int lastID = -1; + + private static final String WHERE_CLAUSE = " WHERE "; + + private static final String AND_CLAUSE = " AND "; + + private boolean whereExists = false; + + private boolean paused = false; + + private ConnectionSource connectionSource; + + public static final String LOG4J_ID_KEY = "log4jid"; + + private Job customReceiverJob; + + public void activateOptions() { + + if(connectionSource == null) { + throw new IllegalStateException( + "CustomSQLDBReceiver cannot function without a connection source"); + } + whereExists = (sqlStatement.toUpperCase().indexOf(WHERE_CLAUSE) > -1); + + customReceiverJob = new CustomReceiverJob(); + + if(this.repository == null) { + throw new IllegalStateException( + "CustomSQLDBReceiver cannot function without a reference to its owning repository"); + } + + + + if (repository instanceof LoggerRepositoryEx) { + Scheduler scheduler = ((LoggerRepositoryEx) repository).getScheduler(); + + scheduler.schedule( + customReceiverJob, System.currentTimeMillis() + 500, refreshMillis); + } + + } + + void closeConnection() { + if (connection != null) { + try { + // LogLog.warn("closing the connection. ", new Exception("x")); + connection.close(); + } catch (SQLException sqle) { + // nothing we can do here + } + } + } + + public void setRefreshMillis(int refreshMillis) { + this.refreshMillis = refreshMillis; + } + + public int getRefreshMillis() { + return refreshMillis; + } + + /** + * @return Returns the connectionSource. + */ + public ConnectionSource getConnectionSource() { + return connectionSource; + } + + /** + * @param connectionSource + * The connectionSource to set. + */ + public void setConnectionSource(ConnectionSource connectionSource) { + this.connectionSource = connectionSource; + } + + public void close() { + try { + if ((connection != null) && !connection.isClosed()) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + connection = null; + } + } + + public void finalize() throws Throwable { + super.finalize(); + close(); + } + + /* + * (non-Javadoc) + * + * @see org.apache.log4j.plugins.Plugin#shutdown() + */ + public void shutdown() { + getLogger().info("removing receiverJob from the Scheduler."); + + if(this.repository instanceof LoggerRepositoryEx) { + Scheduler scheduler = ((LoggerRepositoryEx) repository).getScheduler(); + scheduler.delete(customReceiverJob); + } + + lastID = -1; + } + + public void setSql(String s) { + sqlStatement = s; + } + + public String getSql() { + return sqlStatement; + } + + public void setIDField(String id) { + idField = id; + } + + public String getIDField() { + return idField; + } + + public synchronized void setPaused(boolean p) { + paused = p; + } + + public synchronized boolean isPaused() { + return paused; + } + + class CustomReceiverJob implements Job { + public void execute() { + int oldLastID = lastID; + try { + connection = connectionSource.getConnection(); + Statement statement = connection.createStatement(); + + Logger eventLogger = null; + long timeStamp = 0L; + String level = null; + String threadName = null; + Object message = null; + String ndc = null; + Hashtable mdc = null; + String[] throwable = null; + String className = null; + String methodName = null; + String fileName = null; + String lineNumber = null; + Hashtable properties = null; + + String currentSQLStatement = sqlStatement; + if (whereExists) { + currentSQLStatement = sqlStatement + AND_CLAUSE + idField + + " > " + lastID; + } else { + currentSQLStatement = sqlStatement + WHERE_CLAUSE + idField + + " > " + lastID; + } + + ResultSet rs = statement.executeQuery(currentSQLStatement); + + int i = 0; + while (rs.next()) { + // add a small break every 1000 received events + if (++i == 1000) { + synchronized (this) { + try { + // add a delay + wait(300); + } catch (InterruptedException ie) { + } + i = 0; + } + } + eventLogger = Logger.getLogger(rs.getString("LOGGER")); + timeStamp = rs.getTimestamp("TIMESTAMP").getTime(); + + level = rs.getString("LEVEL"); + threadName = rs.getString("THREAD"); + message = rs.getString("MESSAGE"); + ndc = rs.getString("NDC"); + + String mdcString = rs.getString("MDC"); + mdc = new Hashtable(); + + if (mdcString != null) { + // support MDC being wrapped in {{name, value}} + // or + // just name, value + if ((mdcString.indexOf("{{") > -1) + && (mdcString.indexOf("}}") > -1)) { + mdcString = mdcString + .substring(mdcString.indexOf("{{") + 2, + mdcString.indexOf("}}")); + } + + StringTokenizer tok = new StringTokenizer(mdcString, + ","); + + while (tok.countTokens() > 1) { + mdc.put(tok.nextToken(), tok.nextToken()); + } + } + + throwable = new String[] { rs.getString("THROWABLE") }; + className = rs.getString("CLASS"); + methodName = rs.getString("METHOD"); + fileName = rs.getString("FILE"); + lineNumber = rs.getString("LINE"); + + // if properties are provided in the + // SQL they can be used here (for example, to route + // events to a unique tab in + // Chainsaw if the machinename and/or appname + // property + // are set) + String propertiesString = rs.getString("PROPERTIES"); + properties = new Hashtable(); + + if (propertiesString != null) { + // support properties being wrapped in {{name, + // value}} or just name, value + if ((propertiesString.indexOf("{{") > -1) + && (propertiesString.indexOf("}}") > -1)) { + propertiesString = propertiesString.substring( + propertiesString.indexOf("{{") + 2, + propertiesString.indexOf("}}")); + } + + StringTokenizer tok2 = new StringTokenizer( + propertiesString, ","); + while (tok2.countTokens() > 1) { + String tokenName = tok2.nextToken(); + String value = tok2.nextToken(); + if (tokenName.equals(LOG4J_ID_KEY)) { + try { + int thisInt = Integer.parseInt(value); + value = String.valueOf(thisInt); + if (thisInt > lastID) { + lastID = thisInt; + } + } catch (Exception e) { + } + } + properties.put(tokenName, value); + } + } + + Level levelImpl = Level.toLevel(level); + + + LocationInfo locationInfo = new LocationInfo(fileName, + className, methodName, lineNumber); + + ThrowableInformation throwableInfo = new ThrowableInformation( + throwable); + + properties.putAll(mdc); + + LoggingEvent event = new LoggingEvent(eventLogger.getName(), + eventLogger, timeStamp, levelImpl, message, + threadName, + throwableInfo, + ndc, + locationInfo, + properties); + + doPost(event); + } + //log when rows are retrieved + if (lastID != oldLastID) { + getLogger().debug("lastID: " + lastID); + oldLastID = lastID; + } + + statement.close(); + statement = null; + } catch (SQLException sqle) { + getLogger() + .error("*************Problem receiving events", sqle); + } finally { + closeConnection(); + } + + // if paused, loop prior to executing sql query + synchronized (this) { + while (isPaused()) { + try { + wait(1000); + } catch (InterruptedException ie) { + } + } + } + } + } + + /** + * @{inheritDoc} + */ + public boolean parseUnrecognizedElement(Element element, Properties props) throws Exception { + if ("connectionSource".equals(element.getNodeName())) { + Object instance = + DOMConfigurator.parseElement(element, props, ConnectionSource.class); + if (instance instanceof ConnectionSource) { + ConnectionSource source = (ConnectionSource) instance; + source.activateOptions(); + setConnectionSource(source); + } + return true; + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/DBAppender.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/DBAppender.java b/src/main/java/org/apache/log4j/db/DBAppender.java new file mode 100644 index 0000000..15deac3 --- /dev/null +++ b/src/main/java/org/apache/log4j/db/DBAppender.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.db.dialect.SQLDialect; +import org.apache.log4j.db.dialect.Util; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.spi.LocationInfo; +import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.xml.DOMConfigurator; +import org.apache.log4j.xml.UnrecognizedElementHandler; +import org.w3c.dom.Element; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + + +/** + * The DBAppender inserts loggin events into three database tables in a format + * independent of the Java programming language. The three tables that + * DBAppender inserts to must exists before DBAppender can be used. These tables + * may be created with the help of SQL scripts found in the + * <em>src/java/org/apache/log4j/db/dialect</em> directory. There is a + * specific script for each of the most popular database systems. If the script + * for your particular type of database system is missing, it should be quite + * easy to write one, taking example on the already existing scripts. If you + * send them to us, we will gladly include missing scripts in future releases. + * + * <p> + * If the JDBC driver you are using supports the + * {@link java.sql.Statement#getGeneratedKeys}method introduced in JDBC 3.0 + * specification, then you are all set. Otherwise, there must be an + * {@link SQLDialect}appropriate for your database system. Currently, we have + * dialects for PostgreSQL, MySQL, Oracle and MsSQL. As mentioed previously, an + * SQLDialect is required only if the JDBC driver for your database system does + * not support the {@link java.sql.Statement#getGeneratedKeys getGeneratedKeys} + * method. + * </p> + * + * <table border="1" cellpadding="4"> + * <tr> + * <th>RDBMS</th> + * <th>supports <br/><code>getGeneratedKeys()</code> method</th> + * <th>specific <br/>SQLDialect support</th> + * <tr> + * <tr> + * <td>PostgreSQL</td> + * <td align="center">NO</td> + * <td>present and used</td> + * <tr> + * <tr> + * <td>MySQL</td> + * <td align="center">YES</td> + * <td>present, but not actually needed or used</td> + * <tr> + * <tr> + * <td>Oracle</td> + * <td align="center">YES</td> + * <td>present, but not actually needed or used</td> + * <tr> + * <tr> + * <td>DB2</td> + * <td align="center">YES</td> + * <td>not present, and not needed or used</td> + * <tr> + * <tr> + * <td>MsSQL</td> + * <td align="center">YES</td> + * <td>not present, and not needed or used</td> + * <tr> + * <tr> + * <td>HSQL</td> + * <td align="center">NO</td> + * <td>present and used</td> + * <tr> + * + * </table> + * <p> + * <b>Performance: </b> Experiments show that writing a single event into the + * database takes approximately 50 milliseconds, on a "standard" PC. If pooled + * connections are used, this figure drops to under 10 milliseconds. Note that + * most JDBC drivers already ship with connection pooling support. + * </p> + * + * + * + * <p> + * <b>Configuration </b> DBAppender can be configured programmatically, or using + * {@link org.apache.log4j.xml.DOMConfigurator JoranConfigurator}. Example + * scripts can be found in the <em>tests/input/db</em> directory. + * + * @author Ceki Gülcü + * @author Ray DeCampo + */ +public class DBAppender extends AppenderSkeleton implements UnrecognizedElementHandler { + static final String insertPropertiesSQL = + "INSERT INTO logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)"; + static final String insertExceptionSQL = + "INSERT INTO logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)"; + static final String insertSQL; + private static final Method GET_GENERATED_KEYS_METHOD; + + + static { + StringBuffer sql = new StringBuffer(); + sql.append("INSERT INTO logging_event ("); + sql.append("sequence_number, "); + sql.append("timestamp, "); + sql.append("rendered_message, "); + sql.append("logger_name, "); + sql.append("level_string, "); + sql.append("ndc, "); + sql.append("thread_name, "); + sql.append("reference_flag, "); + sql.append("caller_filename, "); + sql.append("caller_class, "); + sql.append("caller_method, "); + sql.append("caller_line) "); + sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?)"); + insertSQL = sql.toString(); + // + // PreparedStatement.getGeneratedKeys added in JDK 1.4 + // + Method getGeneratedKeysMethod; + try { + getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", null); + } catch(Exception ex) { + getGeneratedKeysMethod = null; + } + GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod; + } + + ConnectionSource connectionSource; + boolean cnxSupportsGetGeneratedKeys = false; + boolean cnxSupportsBatchUpdates = false; + SQLDialect sqlDialect; + boolean locationInfo = false; + + + public DBAppender() { + super(false); + } + + public void activateOptions() { + LogLog.debug("DBAppender.activateOptions called"); + + if (connectionSource == null) { + throw new IllegalStateException( + "DBAppender cannot function without a connection source"); + } + + sqlDialect = Util.getDialectFromCode(connectionSource.getSQLDialectCode()); + if (GET_GENERATED_KEYS_METHOD != null) { + cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys(); + } else { + cnxSupportsGetGeneratedKeys = false; + } + cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates(); + if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) { + throw new IllegalStateException( + "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect"); + } + + // all nice and dandy on the eastern front + super.activateOptions(); + } + + /** + * @return Returns the connectionSource. + */ + public ConnectionSource getConnectionSource() { + return connectionSource; + } + + /** + * @param connectionSource + * The connectionSource to set. + */ + public void setConnectionSource(ConnectionSource connectionSource) { + LogLog.debug("setConnectionSource called for DBAppender"); + this.connectionSource = connectionSource; + } + + protected void append(LoggingEvent event) { + Connection connection = null; + try { + connection = connectionSource.getConnection(); + connection.setAutoCommit(false); + + PreparedStatement insertStatement; + if (cnxSupportsGetGeneratedKeys) { + insertStatement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS); + } else { + insertStatement = connection.prepareStatement(insertSQL); + } + +/* insertStatement.setLong(1, event.getSequenceNumber());*/ + insertStatement.setLong(1, 0); + + insertStatement.setLong(2, event.getTimeStamp()); + insertStatement.setString(3, event.getRenderedMessage()); + insertStatement.setString(4, event.getLoggerName()); + insertStatement.setString(5, event.getLevel().toString()); + insertStatement.setString(6, event.getNDC()); + insertStatement.setString(7, event.getThreadName()); + insertStatement.setShort(8, DBHelper.computeReferenceMask(event)); + + LocationInfo li; + + if (event.locationInformationExists() || locationInfo) { + li = event.getLocationInformation(); + } else { + li = LocationInfo.NA_LOCATION_INFO; + } + + insertStatement.setString(9, li.getFileName()); + insertStatement.setString(10, li.getClassName()); + insertStatement.setString(11, li.getMethodName()); + insertStatement.setString(12, li.getLineNumber()); + + int updateCount = insertStatement.executeUpdate(); + if (updateCount != 1) { + LogLog.warn("Failed to insert loggingEvent"); + } + + ResultSet rs = null; + Statement idStatement = null; + boolean gotGeneratedKeys = false; + if (cnxSupportsGetGeneratedKeys) { + try { + rs = (ResultSet) GET_GENERATED_KEYS_METHOD.invoke(insertStatement, null); + gotGeneratedKeys = true; + } catch(InvocationTargetException ex) { + Throwable target = ex.getTargetException(); + if (target instanceof SQLException) { + throw (SQLException) target; + } + throw ex; + } catch(IllegalAccessException ex) { + LogLog.warn("IllegalAccessException invoking PreparedStatement.getGeneratedKeys", ex); + } + } + + if (!gotGeneratedKeys) { + insertStatement.close(); + insertStatement = null; + + idStatement = connection.createStatement(); + idStatement.setMaxRows(1); + rs = idStatement.executeQuery(sqlDialect.getSelectInsertId()); + } + + // A ResultSet cursor is initially positioned before the first row; the + // first call to the method next makes the first row the current row + rs.next(); + int eventId = rs.getInt(1); + + rs.close(); + + // we no longer need the insertStatement + if(insertStatement != null) { + insertStatement.close(); + insertStatement = null; + } + + if(idStatement != null) { + idStatement.close(); + idStatement = null; + } + + Set propertiesKeys = event.getPropertyKeySet(); + + if (propertiesKeys.size() > 0) { + PreparedStatement insertPropertiesStatement = + connection.prepareStatement(insertPropertiesSQL); + + for (Iterator i = propertiesKeys.iterator(); i.hasNext();) { + String key = (String) i.next(); + String value = (String) event.getProperty(key); + + //LogLog.info("id " + eventId + ", key " + key + ", value " + value); + insertPropertiesStatement.setInt(1, eventId); + insertPropertiesStatement.setString(2, key); + insertPropertiesStatement.setString(3, value); + + if (cnxSupportsBatchUpdates) { + insertPropertiesStatement.addBatch(); + } else { + insertPropertiesStatement.execute(); + } + } + + if (cnxSupportsBatchUpdates) { + insertPropertiesStatement.executeBatch(); + } + + insertPropertiesStatement.close(); + insertPropertiesStatement = null; + } + + String[] strRep = event.getThrowableStrRep(); + + if (strRep != null) { + LogLog.debug("Logging an exception"); + + PreparedStatement insertExceptionStatement = + connection.prepareStatement(insertExceptionSQL); + + for (short i = 0; i < strRep.length; i++) { + insertExceptionStatement.setInt(1, eventId); + insertExceptionStatement.setShort(2, i); + insertExceptionStatement.setString(3, strRep[i]); + if (cnxSupportsBatchUpdates) { + insertExceptionStatement.addBatch(); + } else { + insertExceptionStatement.execute(); + } + } + if (cnxSupportsBatchUpdates) { + insertExceptionStatement.executeBatch(); + } + insertExceptionStatement.close(); + insertExceptionStatement = null; + } + + connection.commit(); + } catch (Throwable sqle) { + LogLog.error("problem appending event", sqle); + } finally { + DBHelper.closeConnection(connection); + } + } + + public void close() { + closed = true; + } + + /** + * Returns value of the <b>LocationInfo </b> property which determines whether + * caller's location info is written to the database. + */ + public boolean getLocationInfo() { + return locationInfo; + } + + /** + * If true, the information written to the database will include caller's + * location information. Due to performance concerns, by default no location + * information is written to the database. + */ + public void setLocationInfo(boolean locationInfo) { + this.locationInfo = locationInfo; + } + + /** + * Gets whether appender requires a layout. + * @return false + */ + public boolean requiresLayout() { + return false; + } + + /** + * @{inheritDoc} + */ + public boolean parseUnrecognizedElement(Element element, Properties props) throws Exception { + if ("connectionSource".equals(element.getNodeName())) { + Object instance = + DOMConfigurator.parseElement(element, props, ConnectionSource.class); + if (instance instanceof ConnectionSource) { + ConnectionSource source = (ConnectionSource) instance; + source.activateOptions(); + setConnectionSource(source); + } + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/DBHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/DBHelper.java b/src/main/java/org/apache/log4j/db/DBHelper.java new file mode 100644 index 0000000..b400506 --- /dev/null +++ b/src/main/java/org/apache/log4j/db/DBHelper.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Set; + +import org.apache.log4j.spi.LoggingEvent; + +/** + * @author Ceki Gülcü + * + */ +public class DBHelper { + + public final static short PROPERTIES_EXIST = 0x01; + public final static short EXCEPTION_EXISTS = 0x02; + + public static short computeReferenceMask(LoggingEvent event) { + short mask = 0; + Set propertiesKeys = event.getPropertyKeySet(); + if(propertiesKeys.size() > 0) { + mask = PROPERTIES_EXIST; + } + String[] strRep = event.getThrowableStrRep(); + if(strRep != null) { + mask |= EXCEPTION_EXISTS; + } + return mask; + } + + static public void closeConnection(Connection connection) { + if(connection != null) { + try { + connection.close(); + } catch(SQLException sqle) { + // static utility classes should not log without an explicit repository + // reference + } + } + } + + public static void closeStatement(Statement statement) { + if(statement != null) { + try { + statement.close(); + } catch(SQLException sqle) { + } + } + } +} http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/db/DBReceiver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/db/DBReceiver.java b/src/main/java/org/apache/log4j/db/DBReceiver.java new file mode 100644 index 0000000..eee1068 --- /dev/null +++ b/src/main/java/org/apache/log4j/db/DBReceiver.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.db; + +import org.apache.log4j.plugins.Pauseable; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.scheduler.Scheduler; +import org.apache.log4j.spi.LoggerRepositoryEx; +import org.apache.log4j.xml.DOMConfigurator; +import org.apache.log4j.xml.UnrecognizedElementHandler; +import org.w3c.dom.Element; + +import java.util.Properties; + +/** + * + * @author Scott Deboy <[email protected]> + * @author Ceki Gülcü + * + */ +public class DBReceiver extends Receiver implements Pauseable, UnrecognizedElementHandler { + /** + * By default we refresh data every 1000 milliseconds. + * @see #setRefreshMillis + */ + static int DEFAULT_REFRESH_MILLIS = 1000; + ConnectionSource connectionSource; + int refreshMillis = DEFAULT_REFRESH_MILLIS; + DBReceiverJob receiverJob; + boolean paused = false; + + public void activateOptions() { + + if(connectionSource == null) { + throw new IllegalStateException( + "DBAppender cannot function without a connection source"); + } + + receiverJob = new DBReceiverJob(this); + receiverJob.setLoggerRepository(repository); + + if(this.repository == null) { + throw new IllegalStateException( + "DBAppender cannot function without a reference to its owning repository"); + } + + if (repository instanceof LoggerRepositoryEx) { + Scheduler scheduler = ((LoggerRepositoryEx) repository).getScheduler(); + + scheduler.schedule( + receiverJob, System.currentTimeMillis() + 500, refreshMillis); + } + + } + + public void setRefreshMillis(int refreshMillis) { + this.refreshMillis = refreshMillis; + } + + public int getRefreshMillis() { + return refreshMillis; + } + + + /** + * @return Returns the connectionSource. + */ + public ConnectionSource getConnectionSource() { + return connectionSource; + } + + + /** + * @param connectionSource The connectionSource to set. + */ + public void setConnectionSource(ConnectionSource connectionSource) { + this.connectionSource = connectionSource; + } + + + /* (non-Javadoc) + * @see org.apache.log4j.plugins.Plugin#shutdown() + */ + public void shutdown() { + getLogger().info("removing receiverJob from the Scheduler."); + + if(this.repository instanceof LoggerRepositoryEx) { + Scheduler scheduler = ((LoggerRepositoryEx) repository).getScheduler(); + scheduler.delete(receiverJob); + } + } + + + /* (non-Javadoc) + * @see org.apache.log4j.plugins.Pauseable#setPaused(boolean) + */ + public void setPaused(boolean paused) { + this.paused = paused; + } + + /* (non-Javadoc) + * @see org.apache.log4j.plugins.Pauseable#isPaused() + */ + public boolean isPaused() { + return paused; + } + + /** + * @{inheritDoc} + */ + public boolean parseUnrecognizedElement(Element element, Properties props) throws Exception { + if ("connectionSource".equals(element.getNodeName())) { + Object instance = + DOMConfigurator.parseElement(element, props, ConnectionSource.class); + if (instance instanceof ConnectionSource) { + ConnectionSource source = (ConnectionSource) instance; + source.activateOptions(); + setConnectionSource(source); + } + return true; + } + return false; + } + +}
