Bug 42042: Copy org.apache.log4j.helpers to component project
git-svn-id: https://svn.apache.org/repos/asf/logging/sandbox/log4j/component@527667 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java b/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
new file mode 100644
index 0000000..bfd1b09
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999,2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ Formats a {@link Date} in the format "HH:mm:ss,SSS" for example,
+ "15:49:37,459".
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+ @deprecated use java.text.SimpleDateFormat to perform date conversion
+ or use org.apache.log4j.helpers.CachedDateFormat to optimize
+ high-frequency date formatting.
+*/
+public class AbsoluteTimeDateFormat extends DateFormat {
+ /**
+ String constant used to specify the Absolute time and date format.
+ */
+ public static final String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+ /**
+ String constant used to specify the "Date and Time" date format.
+ */
+ public static final String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+ /**
+ String constant used to specify the ISO8601 format.
+ */
+ public static final String ISO8601_DATE_FORMAT = "ISO8601";
+
+ /**
+ Equivalent SimpleDateFormat pattern.
+ */
+ public static final String PATTERN = "HH:mm:ss,SSS";
+
+ /**
+ * SimpleDateFormat used to perform format requests.
+ */
+ private final SimpleDateFormat format;
+
+ /**
+ * Create a new instance of AbsoluteTimeDateFormat.
+ */
+ public AbsoluteTimeDateFormat() {
+ format = new SimpleDateFormat(PATTERN);
+ }
+
+ /**
+ * Create a new instance of AbsoluteTimeDateFormat.
+ * @param timeZone time zone used in conversion, may not be null.
+ */
+ public AbsoluteTimeDateFormat(final TimeZone timeZone) {
+ format = new SimpleDateFormat(PATTERN);
+ format.setTimeZone(timeZone);
+ }
+
+ /**
+ * Create a new instance of AbsoluteTimeDateFormat.
+ * @param pattern SimpleDateFormat pattern.
+ */
+ protected AbsoluteTimeDateFormat(final String pattern) {
+ format = new SimpleDateFormat(pattern);
+ }
+
+ /**
+ * Create a new instance of AbsoluteTimeDateFormat.
+ * @param pattern SimpleDateFormat pattern.
+ * @param timeZone time zone used in conversion, may not be null.
+ */
+ protected AbsoluteTimeDateFormat(final String pattern,
+ final TimeZone timeZone) {
+ format = new SimpleDateFormat(pattern);
+ format.setTimeZone(timeZone);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public StringBuffer format(
+ Date date, StringBuffer sbuf, FieldPosition fieldPosition) {
+ return format.format(date, sbuf, fieldPosition);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Date parse(String s, ParsePosition pos) {
+ return format.parse(s, pos);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
new file mode 100644
index 0000000..7d40478
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+
+/**
+ A straightforward implementation of the {@link AppenderAttachable}
+ interface.
+
+ @author Ceki Gülcü
+ @since version 0.9.1 */
+public class AppenderAttachableImpl implements AppenderAttachable {
+ /** Array of appenders. */
+ protected Vector appenderList;
+
+ /**
+ Attach an appender. If the appender is already in the list in
+ won't be added again.
+ */
+ public void addAppender(Appender newAppender) {
+ // Null values for newAppender parameter are strictly forbidden.
+ if (newAppender == null) {
+ return;
+ }
+
+ if (appenderList == null) {
+ appenderList = new Vector(1);
+ }
+
+ if (!appenderList.contains(newAppender)) {
+ appenderList.addElement(newAppender);
+ }
+ }
+
+ /**
+ Call the <code>doAppend</code> method on all attached appenders. */
+ public int appendLoopOnAppenders(LoggingEvent event) {
+ int size = 0;
+ Appender appender;
+
+ if (appenderList != null) {
+ size = appenderList.size();
+
+ for (int i = 0; i < size; i++) {
+ appender = (Appender) appenderList.elementAt(i);
+ appender.doAppend(event);
+ }
+ }
+
+ return size;
+ }
+
+ /**
+ Get all attached appenders as an Enumeration. If there are no
+ attached appenders <code>null</code> is returned.
+
+ @return Enumeration An enumeration of attached appenders.
+ */
+ public Enumeration getAllAppenders() {
+ if (appenderList == null) {
+ return null;
+ } else {
+ return appenderList.elements();
+ }
+ }
+
+ /**
+ Look for an attached appender named as <code>name</code>.
+
+ <p>Return the appender with that name if in the list. Return null
+ otherwise.
+
+ */
+ public Appender getAppender(String name) {
+ if ((appenderList == null) || (name == null)) {
+ return null;
+ }
+
+ int size = appenderList.size();
+ Appender appender;
+
+ for (int i = 0; i < size; i++) {
+ appender = (Appender) appenderList.elementAt(i);
+
+ if (name.equals(appender.getName())) {
+ return appender;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ Returns <code>true</code> if the specified appender is in the
+ list of attached appenders, <code>false</code> otherwise.
+
+ @since 1.2 */
+ public boolean isAttached(Appender appender) {
+ if ((appenderList == null) || (appender == null)) {
+ return false;
+ }
+
+ int size = appenderList.size();
+ Appender a;
+
+ for (int i = 0; i < size; i++) {
+ a = (Appender) appenderList.elementAt(i);
+
+ if (a == appender) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove and close all previously attached appenders.
+ * */
+ public void removeAllAppenders() {
+ if (appenderList != null) {
+ int len = appenderList.size();
+
+ for (int i = 0; i < len; i++) {
+ Appender a = (Appender) appenderList.elementAt(i);
+ a.close();
+ }
+
+ appenderList.removeAllElements();
+ appenderList = null;
+ }
+ }
+
+ /**
+ Remove the appender passed as parameter form the list of attached
+ appenders. */
+ public void removeAppender(Appender appender) {
+ if ((appender == null) || (appenderList == null)) {
+ return;
+ }
+
+ appenderList.removeElement(appender);
+ }
+
+ /**
+ Remove the appender with the name passed as parameter form the
+ list of appenders.
+ */
+ public void removeAppender(String name) {
+ if ((name == null) || (appenderList == null)) {
+ return;
+ }
+
+ int size = appenderList.size();
+
+ for (int i = 0; i < size; i++) {
+ if (name.equals(((Appender) appenderList.elementAt(i)).getName())) {
+ appenderList.removeElementAt(i);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java b/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
new file mode 100644
index 0000000..479593d
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+// Contributors: Mathias Bogaert
+// joelr@viair.com
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ <code>BoundedFIFO</code> serves as the bounded first-in-first-out
+ buffer previously used by the {@link org.apache.log4j.AsyncAppender}.
+
+ @author Ceki Gülcü
+ @since version 0.9.1
+
+ @deprecated Since 1.3.
+
+ */
+public class BoundedFIFO {
+
+ LoggingEvent[] buf;
+ int numElements = 0;
+ int first = 0;
+ int next = 0;
+ int maxSize;
+
+ /**
+ Instantiate a new BoundedFIFO with a maximum size passed as argument.
+ */
+ public
+ BoundedFIFO(int maxSize) {
+ if(maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument ("+maxSize+
+ ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ buf = new LoggingEvent[maxSize];
+ }
+
+ /**
+ Get the first element in the buffer. Returns <code>null</code> if
+ there are no elements in the buffer. */
+ public
+ LoggingEvent get() {
+ if(numElements == 0)
+ return null;
+
+ LoggingEvent r = buf[first];
+ buf[first] = null; // help garbage collection
+
+ if(++first == maxSize) {
+ first = 0;
+ }
+ numElements--;
+ return r;
+ }
+
+ /**
+ Place a {@link LoggingEvent} in the buffer. If the buffer is full
+ then the event is <b>silently dropped</b>. It is the caller's
+ responsability to make sure that the buffer has free space. */
+ public
+ void put(LoggingEvent o) {
+ if(numElements != maxSize) {
+ buf[next] = o;
+ if(++next == maxSize) {
+ next = 0;
+ }
+ numElements++;
+ }
+ }
+
+ /**
+ Get the maximum size of the buffer.
+ */
+ public
+ int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ Return <code>true</code> if the buffer is full, that is the
+ number of elements in the buffer equals the buffer size. */
+ public
+ boolean isFull() {
+ return numElements == maxSize;
+ }
+
+ /**
+ Get the number of elements in the buffer. This number is
+ guaranteed to be in the range 0 to <code>maxSize</code>
+ (inclusive).
+ */
+ public
+ int length() {
+ return numElements;
+ }
+
+
+ int min(int a, int b) {
+ return a < b ? a : b;
+ }
+
+
+ /**
+ Resize the buffer to a new size. If the new size is smaller than
+ the old size events might be lost.
+
+ @since 1.1
+ */
+ synchronized
+ public
+ void resize(int newSize) {
+ if(newSize == maxSize)
+ return;
+
+
+ LoggingEvent[] tmp = new LoggingEvent[newSize];
+
+ // we should not copy beyond the buf array
+ int len1 = maxSize - first;
+
+ // we should not copy beyond the tmp array
+ len1 = min(len1, newSize);
+
+ // er.. how much do we actually need to copy?
+ // We should not copy more than the actual number of elements.
+ len1 = min(len1, numElements);
+
+ // Copy from buf starting a first, to tmp, starting at position 0, len1 elements.
+ System.arraycopy(buf, first, tmp, 0, len1);
+
+ // Are there any uncopied elements and is there still space in the new array?
+ int len2 = 0;
+ if((len1 < numElements) && (len1 < newSize)) {
+ len2 = numElements - len1;
+ len2 = min(len2, newSize - len1);
+ System.arraycopy(buf, 0, tmp, len1, len2);
+ }
+
+ this.buf = tmp;
+ this.maxSize = newSize;
+ this.first=0;
+ this.numElements = len1+len2;
+ this.next = this.numElements;
+ if(this.next == this.maxSize) // this should never happen, but again, it just might.
+ this.next = 0;
+ }
+
+
+ /**
+ Returns <code>true</code> if there is just one element in the
+ buffer. In other words, if there were no elements before the last
+ {@link #put} operation completed. */
+ public
+ boolean wasEmpty() {
+ return numElements == 1;
+ }
+
+ /**
+ Returns <code>true</code> if the number of elements in the
+ buffer plus 1 equals the maximum buffer size, returns
+ <code>false</code> otherwise. */
+ public
+ boolean wasFull() {
+ return (numElements+1 == maxSize);
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/Constants.java b/src/main/java/org/apache/log4j/helpers/Constants.java
new file mode 100644
index 0000000..bff79fd
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/Constants.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+
+/**
+ * Constants used internally throughout log4j.
+ *
+ * @since 1.3
+ */
+public interface Constants {
+
+ static final String LOG4J_PACKAGE_NAME = "org.apache.log4j";
+
+ /**
+ * The name of the default repository is "default" (without the quotes).
+ */
+ static final String DEFAULT_REPOSITORY_NAME = "default";
+
+
+ static final String APPLICATION_KEY = "application";
+ static final String HOSTNAME_KEY = "hostname";
+ static final String RECEIVER_NAME_KEY = "receiver";
+ static final String LOG4J_ID_KEY = "log4jid";
+ public static final String TIMESTAMP_RULE_FORMAT = "yyyy/MM/dd HH:mm:ss";
+
+ /*
+ * The default property file name for automatic configuration.
+ */
+ static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+ /*
+ * The default XML configuration file name for automatic configuration.
+ */
+ static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+ static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+ static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+ static final String JNDI_CONTEXT_NAME = "java:comp/env/log4j/context-name";
+
+ static final String TEMP_LIST_APPENDER_NAME = "TEMP_LIST_APPENDER";
+ static final String TEMP_CONSOLE_APPENDER_NAME = "TEMP_CONSOLE_APPENDER";
+ static final String CODES_HREF = "http://logging.apache.org/log4j/docs/codes.html";
+
+
+ public static final String ABSOLUTE_FORMAT = "ABSOLUTE";
+ public static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
+
+
+ public static final String DATE_AND_TIME_FORMAT = "DATE";
+ public static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
+
+ public static final String ISO8601_FORMAT = "ISO8601";
+ public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
+}
diff --git a/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java b/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
new file mode 100644
index 0000000..6d30fcb
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999,2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.io.Writer;
+import java.io.IOException;
+
+import org.apache.log4j.spi.ErrorCode;
+
+/**
+ Counts the number of bytes written.
+
+ @author Heinz Richter, heinz.richter@frogdot.com
+ @since 0.8.1
+ @deprecated
+
+ */
+public class CountingQuietWriter extends QuietWriter {
+
+ protected long count;
+
+ /**
+ * @deprecated
+ * @param writer
+ * @param eh
+ */
+ public
+ CountingQuietWriter(Writer writer, org.apache.log4j.spi.ErrorHandler eh) {
+ super(writer, eh);
+ }
+
+ public
+ void write(String string) {
+ try {
+ out.write(string);
+ count += string.length();
+ }
+ catch(IOException e) {
+ errorHandler.error("Write failure.", e, ErrorCode.WRITE_FAILURE);
+ }
+ }
+
+ public
+ long getCount() {
+ return count;
+ }
+
+ public
+ void setCount(long count) {
+ this.count = count;
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java b/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
new file mode 100644
index 0000000..216d7f9
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+ CyclicBuffer is used by other appenders to hold {@link LoggingEvent
+ LoggingEvents} for immediate or differed display.
+
+ <p>This buffer gives read access to any element in the buffer not
+ just the first or last element.
+
+ @author Ceki Gülcü
+ @since 0.9.0
+
+ */
+public class CyclicBuffer {
+
+ LoggingEvent[] ea;
+ int first;
+ int last;
+ int numElems;
+ int maxSize;
+
+ /**
+ Instantiate a new CyclicBuffer of at most <code>maxSize</code> events.
+
+ The <code>maxSize</code> argument must a positive integer.
+
+ @param maxSize The maximum number of elements in the buffer.
+ */
+ public CyclicBuffer(int maxSize) throws IllegalArgumentException {
+ if(maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument ("+maxSize+
+ ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ ea = new LoggingEvent[maxSize];
+ first = 0;
+ last = 0;
+ numElems = 0;
+ }
+
+ /**
+ Add an <code>event</code> as the last event in the buffer.
+
+ */
+ public
+ void add(LoggingEvent event) {
+ ea[last] = event;
+ if(++last == maxSize)
+ last = 0;
+
+ if(numElems < maxSize)
+ numElems++;
+ else if(++first == maxSize)
+ first = 0;
+ }
+
+
+ /**
+ Get the <i>i</i>th oldest event currently in the buffer. If
+ <em>i</em> is outside the range 0 to the number of elements
+ currently in the buffer, then <code>null</code> is returned.
+
+
+ */
+ public
+ LoggingEvent get(int i) {
+ if(i < 0 || i >= numElems)
+ return null;
+
+ return ea[(first + i) % maxSize];
+ }
+
+ public
+ int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ Get the oldest (first) element in the buffer. The oldest element
+ is removed from the buffer.
+ */
+ public
+ LoggingEvent get() {
+ LoggingEvent r = null;
+ if(numElems > 0) {
+ numElems--;
+ r = ea[first];
+ ea[first] = null;
+ if(++first == maxSize)
+ first = 0;
+ }
+ return r;
+ }
+
+ /**
+ Get the number of elements in the buffer. This number is
+ guaranteed to be in the range 0 to <code>maxSize</code>
+ (inclusive).
+ */
+ public
+ int length() {
+ return numElems;
+ }
+
+ /**
+ Resize the cyclic buffer to <code>newSize</code>.
+
+ @throws IllegalArgumentException if <code>newSize</code> is negative.
+ */
+ public
+ void resize(int newSize) {
+ if(newSize < 0) {
+ throw new IllegalArgumentException("Negative array size ["+newSize+
+ "] not allowed.");
+ }
+ if(newSize == numElems)
+ return; // nothing to do
+
+ LoggingEvent[] temp = new LoggingEvent[newSize];
+
+ int loopLen = newSize < numElems ? newSize : numElems;
+
+ for(int i = 0; i < loopLen; i++) {
+ temp[i] = ea[first];
+ ea[first] = null;
+ if(++first == numElems)
+ first = 0;
+ }
+ ea = temp;
+ first = 0;
+ numElems = loopLen;
+ maxSize = newSize;
+ if (loopLen == newSize) {
+ last = 0;
+ } else {
+ last = loopLen;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/DateLayout.java b/src/main/java/org/apache/log4j/helpers/DateLayout.java
new file mode 100644
index 0000000..c358215
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/DateLayout.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+// Contributors: Christopher Williams
+// Mathias Bogaert
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ This abstract layout takes care of all the date related options and
+ formatting work.
+
+
+ @author Ceki Gülcü
+ @deprecated since 1.3
+ */
+abstract public class DateLayout extends Layout {
+
+ /**
+ String constant designating no time information. Current value of
+ this constant is <b>NULL</b>.
+
+ */
+ public final static String NULL_DATE_FORMAT = "NULL";
+
+ /**
+ String constant designating relative time. Current value of
+ this constant is <b>RELATIVE</b>.
+ */
+ public final static String RELATIVE_TIME_DATE_FORMAT = "RELATIVE";
+
+ protected FieldPosition pos = new FieldPosition(0);
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ <em>near</em> term.
+ */
+ final static public String DATE_FORMAT_OPTION = "DateFormat";
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ <em>near</em> term.
+ */
+ final static public String TIMEZONE_OPTION = "TimeZone";
+
+ private String timeZoneID;
+ private String dateFormatOption;
+
+ protected DateFormat dateFormat;
+ protected Date date = new Date();
+
+
+ /**
+ * Instantiate a DateLayout object with in the ISO8601 format as the date
+ * formatter.
+ * */
+ public DateLayout() {
+ }
+
+ /**
+ Instantiate a DateLayout object using the local time zone. The
+ DateFormat used will depend on the <code>dateFormatType</code>.
+
+ <p>This constructor just calls the {@link #setDateFormat} method.
+ */
+ public DateLayout(final String dateFormatType) {
+ this.setDateFormat(dateFormatType);
+ }
+
+
+ /**
+ @deprecated Use the setter method for the option directly instead
+ of the generic <code>setOption</code> method.
+ */
+ public
+ String[] getOptionStrings() {
+ return new String[] {DATE_FORMAT_OPTION, TIMEZONE_OPTION};
+ }
+
+ /**
+ @deprecated Use the setter method for the option directly instead
+ of the generic <code>setOption</code> method.
+ */
+ public
+ void setOption(final String option, final String value) {
+ if(option.equalsIgnoreCase(DATE_FORMAT_OPTION)) {
+ dateFormatOption = value.toUpperCase();
+ } else if(option.equalsIgnoreCase(TIMEZONE_OPTION)) {
+ timeZoneID = value;
+ }
+ }
+
+ /**
+ The value of the <b>DateFormat</b> option should be either an
+ argument to the constructor of {@link SimpleDateFormat} or one of
+ the srings "NULL", "RELATIVE", "ABSOLUTE", "DATE" or "ISO8601.
+ */
+ public
+ void setDateFormat(String dateFormat) {
+ if (dateFormat != null) {
+ dateFormatOption = dateFormat;
+ }
+ setDateFormat(dateFormatOption, TimeZone.getDefault());
+ }
+
+
+
+ /**
+ Returns value of the <b>DateFormat</b> option.
+ */
+ public
+ String getDateFormat() {
+ return dateFormatOption;
+ }
+
+ /**
+ The <b>TimeZoneID</b> option is a time zone ID string in the format
+ expected by the {@link TimeZone#getTimeZone} method.
+ */
+ public
+ void setTimeZone(String timeZone) {
+ this.timeZoneID = timeZone;
+ }
+
+ /**
+ Returns value of the <b>TimeZone</b> option.
+ */
+ public
+ String getTimeZone() {
+ return timeZoneID;
+ }
+
+
+ public void activateOptions() {
+ }
+
+ /**
+ Sets the {@link DateFormat} used to format time and date in the
+ zone determined by <code>timeZone</code>.
+ */
+ public
+ void setDateFormat(DateFormat dateFormat, TimeZone timeZone) {
+ this.dateFormat = dateFormat;
+ this.dateFormat.setTimeZone(timeZone);
+ }
+
+
+ public void setDateFormat(final String dateFormatStr, final TimeZone timeZone) {
+ if (dateFormatStr == null) {
+ this.dateFormat = null;
+ return;
+ }
+
+ if (!dateFormatStr.equalsIgnoreCase("NULL")) {
+ if (dateFormatStr.equalsIgnoreCase(RELATIVE_TIME_DATE_FORMAT)) {
+ this.dateFormat = new RelativeTimeDateFormat();
+ } else {
+ if (dateFormatStr.equalsIgnoreCase(Constants.ABSOLUTE_FORMAT)) {
+ dateFormat = new SimpleDateFormat(Constants.ABSOLUTE_TIME_PATTERN);
+ } else if (dateFormatStr.equalsIgnoreCase(Constants.DATE_AND_TIME_FORMAT)) {
+ dateFormat = new SimpleDateFormat(Constants.DATE_AND_TIME_PATTERN);
+ } else if (dateFormatStr.equalsIgnoreCase(Constants.ISO8601_FORMAT)) {
+ dateFormat = new SimpleDateFormat(Constants.ISO8601_PATTERN);
+ } else {
+ dateFormat = new SimpleDateFormat(dateFormatStr);
+ }
+ if (timeZone != null) {
+ dateFormat.setTimeZone(timeZone);
+ }
+ }
+ }
+ }
+
+
+ public
+ void dateFormat(StringBuffer buf, LoggingEvent event) {
+ if(dateFormat != null) {
+ date.setTime(event.timeStamp);
+ dateFormat.format(date, buf, this.pos);
+ buf.append(' ');
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java b/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
new file mode 100644
index 0000000..4eda99f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.TimeZone;
+import java.util.Date;
+import java.text.DateFormatSymbols;
+
+/**
+ * Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example,
+ * "06 Nov 1994 15:49:37,459".
+ *
+ * @author Ceki Gülcü
+ * @since 0.7.5
+ * @deprecated
+ */
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+ /**
+ * Equivalent format string for SimpleDateFormat.
+ */
+ private final static String PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
+ /** Short names for the months. */
+ String[] shortMonths = new DateFormatSymbols().getShortMonths();
+
+ /**
+ * Create a new instance of DateTimeDateFormat.
+ */
+ public DateTimeDateFormat() {
+ super(PATTERN);
+ }
+
+
+ /**
+ * Create a new instance of DateTimeDateFormat.
+ *
+ * @param timeZone time zone used in conversion, may not be null.
+ */
+ public DateTimeDateFormat(final TimeZone timeZone) {
+ super(PATTERN, timeZone);
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/FileWatchdog.java b/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
new file mode 100644
index 0000000..0d45212
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+// Contributors: Mathias Bogaert
+package org.apache.log4j.helpers;
+
+import java.io.File;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+
+/**
+ Check every now and then that a certain file has not changed. If it
+ has, then call the {@link #doOnChange} method.
+
+ This class has been deprecated and is no longer used by either
+ PropertyConfigurator or DOMConfigurator.
+
+ @author Ceki Gülcü
+ @since version 0.9.1
+ @deprecated Use org.apache.log4j.watchdog.FileWatchdog instead.
+*/
+public abstract class FileWatchdog extends Thread {
+ /**
+ The default delay between every file modification check, set to 60
+ seconds. */
+ public static final long DEFAULT_DELAY = 60000;
+ /**
+ The name of the file to observe for changes.
+ */
+ protected String filename;
+
+ private Logger logger = LogManager.getLogger(SyslogWriter.class);
+
+ /**
+ The delay to observe between every check. By default set {@link
+ #DEFAULT_DELAY}. */
+ protected long delay = DEFAULT_DELAY;
+ File file;
+ long lastModif = 0;
+ boolean warnedAlready = false;
+ boolean interrupted = false;
+
+ protected FileWatchdog(String filename) {
+ this.filename = filename;
+ file = new File(filename);
+ setDaemon(true);
+ checkAndConfigure();
+ }
+
+ /**
+ Set the delay to observe between each check of the file changes.
+ */
+ public void setDelay(long delay) {
+ this.delay = delay;
+ }
+
+ protected abstract void doOnChange();
+
+ protected void checkAndConfigure() {
+ boolean fileExists;
+ try {
+ fileExists = file.exists();
+ } catch (SecurityException e) {
+ logger.warn(
+ "Was not allowed to read check file existance, file:[" + filename
+ + "].");
+ interrupted = true; // there is no point in continuing
+ return;
+ }
+
+ if (fileExists) {
+ long l = file.lastModified(); // this can also throw a SecurityException
+ if (l > lastModif) { // however, if we reached this point this
+ lastModif = l; // is very unlikely.
+ doOnChange();
+ warnedAlready = false;
+ }
+ } else {
+ if (!warnedAlready) {
+ logger.debug("[" + filename + "] does not exist.");
+ warnedAlready = true;
+ }
+ }
+ }
+
+ public void run() {
+ while (!interrupted) {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ // no interruption expected
+ }
+ checkAndConfigure();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/FormattingInfo.java b/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
new file mode 100644
index 0000000..5f27c3c
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+
+/**
+ FormattingInfo instances contain the information obtained when parsing
+ formatting modifiers in conversion modifiers.
+
+ @author <a href=mailto:jim_cakalic@na.biomerieux.com>Jim Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ @deprecated Since 1.3
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = Integer.MAX_VALUE;
+ boolean leftAlign = false;
+
+ void reset() {
+ min = -1;
+ max = Integer.MAX_VALUE;
+ leftAlign = false;
+ }
+
+ void dump() {
+ LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign);
+ }
+}
+
diff --git a/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java b/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
new file mode 100644
index 0000000..cf0411c
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.TimeZone;
+
+// Contributors: Arndt Schoenewald <arndt@ibm23093i821.mc.schoenewald.de>
+
+/**
+ * Formats a {@link java.util.Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example
+ * "1999-11-27 15:49:37,459".
+ * <p/>
+ * <p>Refer to the <a
+ * href=http://www.cl.cam.ac.uk/~mgk25/iso-time.html>summary of the
+ * International Standard Date and Time Notation</a> for more
+ * information on this format.
+ *
+ * @author Ceki Gülcü
+ * @author Andrew Vajoczki
+ * @since 0.7.5
+ * @deprecated
+ */
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+ /**
+ * Equivalent format string for SimpleDateFormat.
+ */
+ private static final String FORMAT = "yyyy-MM-dd HH:mm:ss,SSS";
+
+ /**
+ * Create a new instance of ISO8601DateFormat.
+ */
+ public ISO8601DateFormat() {
+ super(FORMAT);
+ }
+
+ /**
+ * Create a new instance of ISO8601DateFormat.
+ *
+ * @param timeZone time zone used in conversion, may not be null.
+ */
+ public ISO8601DateFormat(final TimeZone timeZone) {
+ super(FORMAT, timeZone);
+ }
+
+}
+
diff --git a/src/main/java/org/apache/log4j/helpers/IntializationUtil.java b/src/main/java/org/apache/log4j/helpers/IntializationUtil.java
new file mode 100644
index 0000000..497a0b8
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/IntializationUtil.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 1999,2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggerRepositoryEx;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+
+/**
+ * This class groups certain internally used methods.
+ *
+ * @author Ceki Gulcu
+ * @since 1.3
+ */
+public class IntializationUtil {
+
+
+ public static void log4jInternalConfiguration(LoggerRepository repository) {
+ // This method does not do anoything currently. It might become useful
+ // when sub-domains are added to log4j.
+
+// Logger logger = repository.getLogger("LOG4J");
+// logger.setAdditivity(false);
+// logger.addAppender(
+// new ConsoleAppender(
+// new PatternLayout("log4j-internal: %r %-22c{2} - %m%n")));
+ }
+
+ /**
+ * Configure <code>repository</code> using <code>configuratonResourceStr</code>
+ * and <code>configuratorClassNameStr</code>.
+ *
+ * If <code>configuratonResourceStr</code> is not a URL it will be searched
+ * as a resource from the classpath.
+ *
+ * @param repository The repository to configre
+ * @param configuratonResourceStr URL to the configuration resourc
+ * @param configuratorClassNameStr The name of the class to use as
+ * the configrator. This parameter can be null.
+ * */
+ public static void initialConfiguration(LoggerRepository repository,
+ String configuratonResourceStr,
+ String configuratorClassNameStr) {
+
+ if(configuratonResourceStr == null) {
+ return;
+ }
+ URL url = null;
+
+ try {
+ url = new URL(configuratonResourceStr);
+ } catch (MalformedURLException ex) {
+ // so, resource is not a URL:
+ // attempt to get the resource from the class loader path
+ // Please refer to Loader.getResource documentation.
+ url = Loader.getResource(configuratonResourceStr);
+ }
+
+ // If we have a non-null url, then delegate the rest of the
+ // configuration to the OptionConverter.selectAndConfigure
+ // method.
+ if (url != null) {
+ if (repository instanceof LoggerRepositoryEx) {
+ LogLog.info(
+ "Using URL [" + url
+ + "] for automatic log4j configuration of repository named ["+
+ ((LoggerRepositoryEx) repository).getName()+"].");
+ } else {
+ LogLog.info(
+ "Using URL [" + url
+ + "] for automatic log4j configuration of unnamed repository.");
+ }
+
+ OptionConverter.selectAndConfigure(url, configuratorClassNameStr, repository);
+ }
+ }
+
+ /*
+ public static void initialConfiguration(LoggerRepository repository) {
+ String configurationOptionStr =
+ OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null);
+
+ String configuratorClassName =
+ OptionConverter.getSystemProperty(CONFIGURATOR_CLASS_KEY, null);
+
+ URL url = null;
+
+ // if the user has not specified the log4j.configuration
+ // property, we search first for the file "log4j.xml" and then
+ // "log4j.properties"
+ if (configurationOptionStr == null) {
+ url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
+
+ if (url == null) {
+ url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
+ }
+ } else {
+ try {
+ url = new URL(configurationOptionStr);
+ } catch (MalformedURLException ex) {
+ // so, resource is not a URL:
+ // attempt to get the resource from the class path
+ url = Loader.getResource(configurationOptionStr);
+ }
+ }
+
+ // If we have a non-null url, then delegate the rest of the
+ // configuration to the OptionConverter.selectAndConfigure
+ // method.
+ if (url != null) {
+ LogLog.debug(
+ "Using URL [" + url + "] for automatic log4j configuration.");
+ OptionConverter.selectAndConfigure(
+ url, configuratorClassName, repository);
+ } else {
+ LogLog.debug(
+ "Could not find resources to perform automatic configuration.");
+ }
+ }
+ */
+}
diff --git a/src/main/java/org/apache/log4j/helpers/JNDIUtil.java b/src/main/java/org/apache/log4j/helpers/JNDIUtil.java
new file mode 100644
index 0000000..a40355f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/JNDIUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+
+/**
+ *
+ * @author Ceki Gulcu
+ */
+public class JNDIUtil {
+ public static Context getInitialContext() throws NamingException {
+ return new InitialContext();
+ }
+
+ public static String lookup(Context ctx, String name) {
+ if (ctx == null) {
+ return null;
+ }
+ try {
+ return (String) ctx.lookup(name);
+ } catch (NamingException e) {
+ //LogLog.warn("Failed to get "+name);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/Loader.java b/src/main/java/org/apache/log4j/helpers/Loader.java
new file mode 100644
index 0000000..ab0504a
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/Loader.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.net.URL;
+
+
+
+/**
+ Load resources (or images) from various sources.
+
+ @author Ceki Gülcü
+ */
+public class Loader {
+ static final String TSTR =
+ "Caught Exception while in Loader.getResource. This may be innocuous.";
+
+ // We conservatively assume that we are running under Java 1.x
+ private static boolean java1 = true;
+ private static boolean ignoreTCL = false;
+
+ static {
+ String prop = OptionConverter.getSystemProperty("java.version", null);
+
+ if (prop != null) {
+ int i = prop.indexOf('.');
+
+ if (i != -1) {
+ if (prop.charAt(i + 1) != '1') {
+ java1 = false;
+ }
+ }
+ }
+
+ String ignoreTCLProp =
+ OptionConverter.getSystemProperty("log4j.ignoreTCL", null);
+
+ if (ignoreTCLProp != null) {
+ ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);
+ }
+ }
+
+ /**
+ * Get a resource by delegating to getResource(String).
+ * @param resource resource name
+ * @param clazz class, ignored.
+ * @return URL to resource or null.
+ * @deprecated as of 1.2.
+ */
+ public static URL getResource(String resource, Class clazz) {
+ return getResource(resource);
+ }
+
+ /**
+ This method will search for <code>resource</code> in different
+ places. The search order is as follows:
+
+ <ol>
+
+ <p><li>Search for <code>resource</code> using the thread context
+ class loader under Java2. This step is performed only if the <code>
+ skipTCL</code> parameter is false.</li>
+
+ <p><li>If the previous step failed, search for <code>resource</code> using
+ the class loader that loaded this class (<code>Loader</code>).</li>
+
+ <p><li>Try one last time with
+ <code>ClassLoader.getSystemResource(resource)</code>, that is is
+ using the system class loader in JDK 1.2 and virtual machine's
+ built-in class loader in JDK 1.1.
+
+ </ol>
+ */
+ public static URL getResource(String resource) {
+ ClassLoader classLoader = null;
+ URL url = null;
+
+ try {
+ classLoader = getTCL();
+
+ if (classLoader != null) {
+// LogLog.debug(
+// "Trying to find [" + resource + "] using context classloader "
+// + classLoader + ".");
+ url = classLoader.getResource(resource);
+
+ if (url != null) {
+ return url;
+ }
+ }
+
+ // We could not find resource. Ler us now try with the
+ // classloader that loaded this class.
+ classLoader = Loader.class.getClassLoader();
+
+ if (classLoader != null) {
+// LogLog.debug(
+// "Trying to find [" + resource + "] using " + classLoader
+// + " class loader.");
+ url = classLoader.getResource(resource);
+
+ if (url != null) {
+ return url;
+ }
+ }
+ } catch (Throwable t) {
+ // LogLog.warn(TSTR, t);
+ }
+
+ // Last ditch attempt: get the resource from the class path. It
+ // may be the case that clazz was loaded by the Extentsion class
+ // loader which the parent of the system class loader. Hence the
+ // code below.
+// LogLog.debug(
+// "Trying to find [" + resource
+// + "] using ClassLoader.getSystemResource().");
+
+ return ClassLoader.getSystemResource(resource);
+ }
+
+ /**
+ Are we running under JDK 1.x?
+ */
+ public static boolean isJava1() {
+ return java1;
+ }
+
+ /**
+ * Get the Thread Context Loader which is a JDK 1.2 feature. If we
+ * are running under JDK 1.1 or anything else goes wrong the method
+ * returns <code>null<code>.
+ *
+ * */
+ private static ClassLoader getTCL() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ /**
+ * If running under JDK 1.2 load the specified class using the
+ * <code>Thread</code> <code>contextClassLoader</code> if that
+ * fails try Class.forname. Under JDK 1.1 only Class.forName is
+ * used.
+ *
+ */
+ public static Class loadClass(String clazz) throws ClassNotFoundException {
+ // Just call Class.forName(clazz) if we are running under JDK 1.1
+ // or if we are instructed to ignore the TCL.
+ if (java1 || ignoreTCL) {
+ return Class.forName(clazz);
+ } else {
+ try {
+ return getTCL().loadClass(clazz);
+ } catch (Throwable e) {
+ // we reached here because tcl was null or because of a
+ // security exception, or because clazz could not be loaded...
+ // In any case we now try one more time
+ return Class.forName(clazz);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/LogLog.java b/src/main/java/org/apache/log4j/helpers/LogLog.java
new file mode 100644
index 0000000..3c83a29
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/LogLog.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+
+/**This class used to output log statements from within the log4j package.
+
+ <p>Log4j components cannot make log4j logging calls. However, it is
+ sometimes useful for the user to learn about what log4j is
+ doing. You can enable log4j internal logging by defining the
+ <b>log4j.configDebug</b> variable.
+
+ <p>All log4j internal debug calls go to <code>System.out</code>
+ where as internal error messages are sent to
+ <code>System.err</code>. All internal messages are prepended with
+ the string "log4j: ".
+
+ @since 0.8.2
+ @author Ceki Gülcü
+*/
+public class LogLog {
+ /**
+ Defining this value makes log4j print log4j-internal debug
+ statements to <code>System.out</code>.
+
+ <p> The value of this string is <b>log4j.debug</b>.
+
+ <p>Note that the search for all option names is case sensitive. */
+ public static final String DEBUG_KEY="log4j.debug";
+
+
+ /**
+ Defining this value makes log4j components print log4j-internal
+ debug statements to <code>System.out</code>.
+
+ <p> The value of this string is <b>log4j.configDebug</b>.
+
+ <p>Note that the search for all option names is case sensitive.
+
+ @deprecated Use {@link #DEBUG_KEY} instead.
+ */
+ public static final String CONFIG_DEBUG_KEY="log4j.configDebug";
+
+ /**
+ Defining this value makes log4j print log4j-internal debug
+ statements to <code>System.out</code>.
+
+ <p> The value of this string is <b>log4j.debug</b>.
+
+ <p>Note that the search for all option names is case sensitive. */
+ public static final String CORE_DEBUG_KEY = "log4j.coreDebug";
+
+ protected static boolean debugEnabled = false;
+
+ private static final String PREFIX = "log4j: ";
+ private static final String ERR_PREFIX = "log4j:ERROR ";
+ private static final String INFO_PREFIX = "log4j:INFO ";
+ private static final String WARN_PREFIX = "log4j:WARN ";
+
+ static {
+ String key = OptionConverter.getSystemProperty(CORE_DEBUG_KEY, null);
+
+ if (key != null) {
+ debugEnabled = OptionConverter.toBoolean(key, true);
+ }
+ }
+
+ /**
+ Allows to enable/disable log4j internal logging.
+ */
+ public static void setInternalDebugging(boolean enabled) {
+ debugEnabled = enabled;
+ }
+
+ /**
+ This method is used to output log4j internal debug
+ statements. Output goes to <code>System.out</code>.
+ */
+ public static void debug(String msg) {
+ if (debugEnabled) {
+ System.out.println(PREFIX + msg);
+ }
+ }
+
+ public static void info(String msg) {
+ System.out.println(INFO_PREFIX + msg);
+ }
+
+ /**
+ This method is used to output log4j internal debug
+ statements. Output goes to <code>System.out</code>.
+ */
+ public static void debug(String msg, Throwable t) {
+ if (debugEnabled) {
+ System.out.println(PREFIX + msg);
+
+ if (t != null) {
+ t.printStackTrace(System.out);
+ }
+ }
+ }
+
+ /**
+ * This method is used to output log4j internal error statements. There is no
+ * way to disable error statements. Output goes to <code>System.err</code>.
+ * @deprecated Use {@link org.apache.log4j.Logger} instead.
+ */
+ public static void error(String msg) {
+ System.err.println(ERR_PREFIX + msg);
+ }
+
+ /**
+ * This method is used to output log4j internal error statements. There is no
+ * way to disable error statements. Output goes to <code>System.err</code>.
+ * @deprecated Use {@link org.apache.log4j.Logger} instead.
+ **/
+ public static void error(String msg, Throwable t) {
+ System.err.println(ERR_PREFIX + msg);
+
+ if (t != null) {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * In quite mode no LogLog generates strictly no output, not even
+ * for errors.
+ * @param quietMode A true for not
+ * @deprecated with no replacement
+ */
+ public static void setQuietMode(boolean quietMode) {
+ // nothing to do
+ }
+
+ /**
+ * This method is used to output log4j internal warning statements. There is
+ * no way to disable warning statements. Output goes to
+ * <code>System.err</code>.
+ *
+ * @deprecated Use {@link org.apache.log4j.Logger} instead.
+ * */
+ public static void warn(String msg) {
+ System.err.println(WARN_PREFIX + msg);
+ }
+
+ /**
+ * This method is used to output log4j internal warnings. There is no way to
+ * disable warning statements. Output goes to <code>System.err</code>.
+ *
+ * @deprecated Use {@link org.apache.log4j.Logger} instead.
+ * */
+ public static void warn(String msg, Throwable t) {
+ System.err.println(WARN_PREFIX + msg);
+
+ if (t != null) {
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/LoggerTraverse.java b/src/main/java/org/apache/log4j/helpers/LoggerTraverse.java
new file mode 100644
index 0000000..6661401
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/LoggerTraverse.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ This helper class can be used to extract/traverse logger information
+ for a given LoggerRepository. It is a work in progress and focus to
+ date has been functionality, not performance or efficiency.
+
+ The set of loggers can be retrieved in one of two ways:
+
+ 1) getLoggerNames() - A complete list of the loggers
+ 2) getLoggerPackageNames() - A list of package names, starting at a
+ given package name pattern.
+
+ If the second retrieval method is used, the caller can iteratively call
+ the LoggerTraverse to retrieve sub-packages and children.
+
+ This class is dependent on logger names that match Java fully qualified
+ class names.
+
+ It also provides methods for querying the current level of a given
+ logger and
+
+ NOTE: This class does not cause any side effects in the LoggerRepository.
+ It does not inadvertantly create new Loggers in the process of parsing the
+ package names or accessing information.
+
+ NOTE: This class does not automatically keep track of changes in the given
+ LoggerRepository. The caller must call the update() method to get the
+ current set of loggers.
+
+ @author Mark Womack <mwomack@apache.org>
+*/
+public class LoggerTraverse {
+ /** A map of all the loggers in the LoggerRepository. */
+ private Map loggerMap = new TreeMap();
+
+ /** A reference to the root logger of the LoggerRepository. */
+ private Logger rootLogger;
+
+ /**
+ Empty constructor. */
+ public LoggerTraverse() {
+ }
+
+ /**
+ @param repository The LoggerRepository to traverse. */
+ public LoggerTraverse(LoggerRepository repository) {
+ update(repository);
+ }
+
+ /**
+ Updates the LoggerTraverse to the current information in the given
+ LoggerRepository.
+
+ @param repository LoggerRepository to use for Logger information. */
+ public void update(LoggerRepository repository) {
+ // clear any old information
+ loggerMap.clear();
+
+ // set the root logger
+ rootLogger = repository.getRootLogger();
+
+ // enumerate through the current set of loggers
+ // the key is the logger name, the value is the logger
+ Enumeration loggerEnum = repository.getCurrentLoggers();
+
+ while (loggerEnum.hasMoreElements()) {
+ Logger logger = (Logger) loggerEnum.nextElement();
+ loggerMap.put(logger.getName(), logger);
+ }
+ }
+
+ /**
+ Returns the list of all loggers, sorted by name.
+
+ @return List List of the current loggers. */
+ public List getLoggerNames() {
+ List loggerList = new ArrayList(loggerMap.size());
+ Iterator loggerIter = loggerMap.keySet().iterator();
+
+ while (loggerIter.hasNext()) {
+ loggerList.add((String) loggerIter.next());
+ }
+
+ return loggerList;
+ }
+
+ /**
+ Using a starting name pattern, returns the next level of package names
+ that start with that pattern. Returns a list, as there can be more than
+ one return value. Passing in an empty string for the starting pattern
+ will return a list of the top level package names.
+
+ For example, if the following logger names were defined: org.apache.log4j
+ and org.apache.log4j-extensions, then passing in an empty string would
+ return one item in the list with a value of "org". If the pattern
+ "org.apache" were passed in, then the list would contain two items,
+ "log4j" and "log4j-extensions".
+
+ @param startPattern The name pattern to match for Logger name.
+ @return List List of matching Logger names that start with the pattern. */
+ public List getLoggerPackageNames(String startPattern) {
+ String name = "";
+ List packageList = new ArrayList(1);
+
+ // iterate through the loggerMap, checking the name of each logger
+ // against the starting pattern. If name starts with pattern, then
+ // add the next part of the package name to the return list.
+ Iterator loggerIter = loggerMap.keySet().iterator();
+
+ while (loggerIter.hasNext()) {
+ String loggerName = (String) loggerIter.next();
+
+ // does the logger name start with the startPattern
+ if (loggerName.startsWith(startPattern)) {
+ loggerName = loggerName.substring(startPattern.length());
+
+ // is there part of the name left after the start pattern is removed?
+ if (loggerName.length() > 0) {
+ // if the left over string starts with '.'. remove it
+ if (loggerName.startsWith(".")) {
+ loggerName = loggerName.substring(1);
+ } else if (startPattern.length() > 0) {
+ break;
+ }
+
+ // find the next index of '.' and grab the part of the name before it
+ int index = loggerName.indexOf('.');
+
+ if (index != -1) {
+ //System.out.println("found . at " + index);
+ loggerName = loggerName.substring(0, index);
+ }
+
+ // if this is not a name we have previously encountered,
+ // put it in the return list.
+ if (!loggerName.equals(name)) {
+ packageList.add(loggerName);
+ name = loggerName;
+ }
+ }
+ }
+ }
+
+ return packageList;
+ }
+
+ /**
+ Returns true if the given package name appears to have sub-package.
+
+ @param startPattern The name pattern to match for Logger name.
+ @return boolean True if there are existing loggers that match. */
+ public boolean loggerHasSubPackages(String startPattern) {
+ int len = startPattern.length();
+
+ // iterate through logger names and first one that starts with
+ // pattern and the length is greater, return true.
+ Iterator loggerIter = loggerMap.keySet().iterator();
+
+ while (loggerIter.hasNext()) {
+ String loggerName = (String) loggerIter.next();
+
+ if (loggerName.startsWith(startPattern) && (loggerName.length() > len)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ Returns the level for the root logger.
+
+ @return Level The current Level for the root logger. */
+ public Level getLevelForRootLogger() {
+ return rootLogger.getEffectiveLevel();
+ }
+
+ /**
+ Returns the effective level for the given package name. If no level is
+ set for the given package, then search back through the package names
+ until one is found that is set or return the level of the root logger.
+
+ @param packageName The name of the logger to return the level for.
+ @return Level The level of the logger. */
+ public Level getLevelForPackage(String packageName) {
+ String name = packageName;
+ Logger logger = (Logger) loggerMap.get(packageName);
+
+ while ((logger == null) && (name != null)) {
+ int index = name.lastIndexOf('.');
+
+ if (index != -1) {
+ name = name.substring(0, index - 1);
+ logger = (Logger) loggerMap.get(packageName);
+ } else {
+ name = null;
+ }
+ }
+
+ if (logger != null) {
+ return logger.getEffectiveLevel();
+ } else {
+ return rootLogger.getEffectiveLevel();
+ }
+ }
+
+ /**
+ Returns true of the package has had its level set directly or
+ false if the level is inherited.
+
+ @param packageName The name of the logger to return the level for.
+ @return boolean True if the level has been explicitly configured. */
+ public boolean getLevelIsSetForPackage(String packageName) {
+ String name = packageName;
+ Logger logger = (Logger) loggerMap.get(packageName);
+
+ while ((logger == null) && (name != null)) {
+ int index = name.lastIndexOf('.');
+
+ if (index != -1) {
+ name = name.substring(0, index - 1);
+ logger = (Logger) loggerMap.get(packageName);
+ } else {
+ name = null;
+ }
+ }
+
+ if (logger != null) {
+ if (logger == rootLogger) {
+ return true;
+ } else {
+ return (logger.getLevel() != null);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ here is an example of using the hierarchical version, iterating
+ through all the package names, all the loggers.
+
+ @param args Parameters for main execution. */
+ public static void main(String[] args) {
+ // set the root to level warn
+ Logger.getRootLogger().setLevel(Level.ERROR);
+
+ // create some loggers
+ Logger.getLogger("org.womacknet.wgntool.Researcher");
+ Logger.getLogger("org.womacknet.wgntool.ResearcherList");
+ Logger.getLogger("org.womacknet.wgntool").setLevel(Level.WARN);
+ Logger.getLogger("org.womacknet.util.NameUtil");
+ Logger.getLogger("com.widgets_r_us.util.StringUtil").setLevel(Level.DEBUG);
+
+ LoggerTraverse info = new LoggerTraverse(LogManager.getLoggerRepository());
+ System.out.println("NOTE: '*' indicates the level has not been "
+ + "explicitly configured for that logger");
+ System.out.println("root - " + info.getLevelForRootLogger());
+ iteratePackages("", 1, info);
+ }
+
+ /**
+ Starting with a package name, iterate through all subpackages and loggers.
+
+ @param startPackageName The logger name to start iterating from.
+ @param level The indentation value for display of logger names.
+ @param info The TraverseInfo instance to iterate. */
+ static void iteratePackages(
+ String startPackageName, int level, LoggerTraverse info) {
+ List packageInfo = info.getLoggerPackageNames(startPackageName);
+ Iterator iter = packageInfo.iterator();
+
+ while (iter.hasNext()) {
+ String packageName = (String) iter.next();
+
+ for (int x = 0; x < level; x++) {
+ System.out.print(" ");
+ }
+
+ System.out.print(packageName);
+
+ String subpackageName;
+
+ if (startPackageName.length() > 0) {
+ subpackageName = startPackageName + "." + packageName;
+ } else {
+ subpackageName = packageName;
+ }
+
+ System.out.print(" - " + info.getLevelForPackage(subpackageName));
+ System.out.println(
+ (info.getLevelIsSetForPackage(subpackageName) ? "" : "*"));
+
+ if (info.loggerHasSubPackages(subpackageName)) {
+ iteratePackages(subpackageName, level + 1, info);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/MessageFormatter.java b/src/main/java/org/apache/log4j/helpers/MessageFormatter.java
new file mode 100644
index 0000000..88e4013
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/MessageFormatter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+
+/**
+ * Formats messages according to very simple rules.
+ * See {@link #format(String, Object)} and
+ * {@link #format(String, Object, Object)} for more details.
+ *
+ * @author Ceki Gülcü
+ */
+public class MessageFormatter {
+ static final char DELIM_START = '{';
+ static final char DELIM_STOP = '}';
+
+ /**
+ * Performs single argument substitution for the 'messagePattern' passed as
+ * parameter.
+ * <p>
+ * For example, <code>MessageFormatter.format("Hi {}.", "there");</code> will
+ * return the string "Hi there.".
+ * <p>
+ * The {} pair is called the formatting element. It serves to designate the
+ * location where the argument needs to be inserted within the pattern.
+ *
+ * @param messagePattern The message pattern which will be parsed and formatted
+ * @param argument The argument to be inserted instead of the formatting element
+ * @return The formatted message
+ */
+ public static String format(String messagePattern, Object argument) {
+ int j = messagePattern.indexOf(DELIM_START);
+ int len = messagePattern.length();
+ char escape = 'x';
+
+ // if there are no { characters or { is the last character of the messsage
+ // then we just return messagePattern
+ if (j == -1 || (j+1 == len)) {
+ return messagePattern;
+ } else {
+ if(j+1 == len) {
+ }
+
+ char delimStop = messagePattern.charAt(j + 1);
+ if (j > 0) {
+ escape = messagePattern.charAt(j - 1);
+ }
+ if ((delimStop != DELIM_STOP) || (escape == '\\')) {
+ // invalid DELIM_START/DELIM_STOP pair or espace character is
+ // present
+ return messagePattern;
+ } else {
+ StringBuffer sbuf = new StringBuffer(len + 20);
+ sbuf.append(messagePattern.substring(0, j));
+ sbuf.append(argument);
+ sbuf.append(messagePattern.substring(j + 2));
+ return sbuf.toString();
+ }
+ }
+ }
+
+ /**
+ * /**
+ * Performs a two argument substitution for the 'messagePattern' passed as
+ * parameter.
+ * <p>
+ * For example, <code>MessageFormatter.format("Hi {}. My name is {}.",
+ * "there", "David");</code> will return the string "Hi there. My name is David.".
+ * <p>
+ * The '{}' pair is called a formatting element. It serves to designate the
+ * location where the arguments need to be inserted within the message pattern.
+ *
+ * @param messagePattern The message pattern which will be parsed and formatted
+ * @param arg1 The first argument to replace the first formatting element
+ * @param arg2 The second argument to replace the second formatting element
+ * @return The formatted message
+ */
+ public static String format(String messagePattern, Object arg1, Object arg2) {
+ int i = 0;
+ int len = messagePattern.length();
+ int j = messagePattern.indexOf(DELIM_START);
+
+ StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50);
+
+ for (int L = 0; L < 2; L++) {
+ j = messagePattern.indexOf(DELIM_START, i);
+
+ if (j == -1 || (j+1 == len)) {
+ // no more variables
+ if (i == 0) { // this is a simple string
+ return messagePattern;
+ } else { // add the tail string which contains no variables and return the result.
+ sbuf.append(messagePattern.substring(i, messagePattern.length()));
+ return sbuf.toString();
+ }
+ } else {
+ char delimStop = messagePattern.charAt(j + 1);
+ if ((delimStop != DELIM_STOP)) {
+ // invalid DELIM_START/DELIM_STOP pair
+ sbuf.append(messagePattern.substring(i, messagePattern.length()));
+ return sbuf.toString();
+ }
+ sbuf.append(messagePattern.substring(i, j));
+ sbuf.append((L == 0) ? arg1 : arg2);
+ i = j + 2;
+ }
+ }
+ // append the characters following the second {} pair.
+ sbuf.append(messagePattern.substring(i, messagePattern.length()));
+ return sbuf.toString();
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/NullEnumeration.java b/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
new file mode 100644
index 0000000..3daf8c2
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+
+ An always-empty Enumerator.
+
+ @author Anders Kristensen
+ @since version 1.0
+ */
+public class NullEnumeration implements Enumeration {
+ private static final NullEnumeration instance = new NullEnumeration();
+
+ private
+ NullEnumeration() {
+ }
+
+ public static NullEnumeration getInstance() {
+ return instance;
+ }
+
+ public
+ boolean hasMoreElements() {
+ return false;
+ }
+
+ public
+ Object nextElement() {
+ throw new NoSuchElementException();
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java b/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
new file mode 100644
index 0000000..51567ca
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ <code>ErrorHandler</code> and its implementations are no longer
+ utilized by Log4j. The <code>ErrorHandler</code> interface and any
+ implementations of it are only here to provide binary runtime
+ compatibility with versions previous to 1.3, most specifically
+ 1.2.xx versions. All methods are NOP's.
+
+ @author Ceki Gülcü
+ @since 0.9.0
+ @deprecated As of 1.3
+ */
+public class OnlyOnceErrorHandler implements org.apache.log4j.spi.ErrorHandler {
+ public void setLogger(Logger logger) {}
+ public void activateOptions() {}
+ public void error(String message, Exception e, int errorCode) {}
+ public void error(String message, Exception e, int errorCode, LoggingEvent event) {}
+ public void error(String message) {}
+ public void setAppender(Appender appender) {}
+ public void setBackupAppender(Appender appender) {}
+}
diff --git a/src/main/java/org/apache/log4j/helpers/Option.java b/src/main/java/org/apache/log4j/helpers/Option.java
new file mode 100644
index 0000000..d53fbc5
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/Option.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+/**
+ *
+ *
+ * @author Ceki Gülcü
+ */
+public class Option {
+ static final String EMPTY_STR = "";
+
+ public static boolean isEmpty(String val) {
+ return ((val == null) || EMPTY_STR.equals(val));
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/src/main/java/org/apache/log4j/helpers/OptionConverter.java
new file mode 100644
index 0000000..499696e
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.config.ConfiguratorBase;
+import org.apache.log4j.joran.JoranConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.net.URL;
+
+import java.util.Properties;
+
+
+// Contributors: Avy Sharell
+// Matthieu Verbert
+// Colin Sampaleanu
+
+// Contributors: Avy Sharell
+// Matthieu Verbert
+// Colin Sampaleanu
+
+/**
+ * A convenience class to convert property values to specific types.
+ *
+ * @author Ceki Gülcü
+ * @author Simon Kitching;
+ * @author Anders Kristensen
+ * @author Avy Sharell
+*/
+public class OptionConverter {
+ static String DELIM_START = "${";
+ static char DELIM_STOP = '}';
+ static int DELIM_START_LEN = 2;
+ static int DELIM_STOP_LEN = 1;
+
+ // TODO: this method should be removed if OptionConverter becomes a static
+ static Logger getLogger() {
+ return LogManager.getLogger(OptionConverter.class);
+ }
+
+ // TODO: this method should be removed if OptionConverter becomes totally static
+ public static void setLoggerRepository(LoggerRepository lr) {
+
+ }
+
+
+ public static String[] concatanateArrays(String[] l, String[] r) {
+ int len = l.length + r.length;
+ String[] a = new String[len];
+
+ System.arraycopy(l, 0, a, 0, l.length);
+ System.arraycopy(r, 0, a, l.length, r.length);
+
+ return a;
+ }
+
+ public static String convertSpecialChars(String s) {
+ char c;
+ int len = s.length();
+ StringBuffer sbuf = new StringBuffer(len);
+
+ int i = 0;
+
+ while (i < len) {
+ c = s.charAt(i++);
+
+ if (c == '\\') {
+ c = s.charAt(i++);
+
+ if (c == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'f') {
+ c = '\f';
+ } else if (c == '\b') {
+ c = '\b';
+ } else if (c == '\"') {
+ c = '\"';
+ } else if (c == '\'') {
+ c = '\'';
+ } else if (c == '\\') {
+ c = '\\';
+ }
+ }
+
+ sbuf.append(c);
+ }
+
+ return sbuf.toString();
+ }
+
+ /**
+ Very similar to <code>System.getProperty</code> except
+ that the {@link SecurityException} is hidden.
+
+ @param key The key to search for.
+ @param def The default value to return.
+ @return the string value of the system property, or the default
+ value if there is no property with that key.
+
+ @since 1.1 */
+ public static String getSystemProperty(String key, String def) {
+ try {
+ return System.getProperty(key, def);
+ } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+ return def;
+ }
+ }
+
+ public static Object instantiateByKey(
+ Properties props, String key, Class superClass, Object defaultValue) {
+ // Get the value of the property in string form
+ String className = findAndSubst(key, props);
+
+ if (className == null) {
+ getLogger().error("Could not find value for key {}", key);
+
+ return defaultValue;
+ }
+
+ // Trim className to avoid trailing spaces that cause problems.
+ return instantiateByClassName(className.trim(), superClass, defaultValue);
+ }
+
+ /**
+ If <code>value</code> is "true", then <code>true</code> is
+ returned. If <code>value</code> is "false", then
+ <code>true</code> is returned. Otherwise, <code>default</code> is
+ returned.
+
+ <p>Case of value is unimportant. */
+ public static boolean toBoolean(String value, boolean dEfault) {
+ if (value == null) {
+ return dEfault;
+ }
+
+ String trimmedVal = value.trim();
+
+ if ("true".equalsIgnoreCase(trimmedVal)) {
+ return true;
+ }
+
+ if ("false".equalsIgnoreCase(trimmedVal)) {
+ return false;
+ }
+
+ return dEfault;
+ }
+
+ public static int toInt(String value, int dEfault) {
+ if (value != null) {
+ String s = value.trim();
+
+ try {
+ return Integer.valueOf(s).intValue();
+ } catch (NumberFormatException e) {
+ getLogger().error("[{}] is not in proper int form.", s);
+ }
+ }
+
+ return dEfault;
+ }
+
+ /**
+ Converts a standard or custom priority level to a Level
+ object. <p> If <code>value</code> is of form
+ "level#classname", then the specified class' toLevel method
+ is called to process the specified level string; if no '#'
+ character is present, then the default {@link org.apache.log4j.Level}
+ class is used to process the level value.
+
+ <p>As a special case, if the <code>value</code> parameter is
+ equal to the string "NULL", then the value <code>null</code> will
+ be returned.
+
+ <p> If any error occurs while converting the value to a level,
+ the <code>defaultValue</code> parameter, which may be
+ <code>null</code>, is returned.
+
+ <p> Case of <code>value</code> is insignificant for the level level, but is
+ significant for the class name part, if present.
+
+ @since 1.1 */
+ public static Level toLevel(String value, Level defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ value = value.trim();
+ int hashIndex = value.indexOf('#');
+
+ if (hashIndex == -1) {
+ if ("NULL".equalsIgnoreCase(value)) {
+ return null;
+ } else {
+ // no class name specified : use standard Level class
+ return (Level) Level.toLevel(value, defaultValue);
+ }
+ }
+
+ Level result = defaultValue;
+
+ String clazz = value.substring(hashIndex + 1);
+ String levelName = value.substring(0, hashIndex);
+
+ // This is degenerate case but you never know.
+ if ("NULL".equalsIgnoreCase(levelName)) {
+ return null;
+ }
+
+ try {
+ Class customLevel = Loader.loadClass(clazz);
+
+ // get a ref to the specified class' static method
+ // toLevel(String, org.apache.log4j.Level)
+ Class[] paramTypes =
+ new Class[] { String.class, org.apache.log4j.Level.class };
+ java.lang.reflect.Method toLevelMethod =
+ customLevel.getMethod("toLevel", paramTypes);
+
+ // now call the toLevel method, passing level string + default
+ Object[] params = new Object[] { levelName, defaultValue };
+ Object o = toLevelMethod.invoke(null, params);
+
+ result = (Level) o;
+ } catch (ClassNotFoundException e) {
+ getLogger().warn("custom level class [" + clazz + "] not found.");
+ } catch (NoSuchMethodException e) {
+ getLogger().warn(
+ "custom level class [" + clazz + "]"
+ + " does not have a class function toLevel(String, Level)", e);
+ } catch (java.lang.reflect.InvocationTargetException e) {
+ getLogger().warn(
+ "custom level class [" + clazz + "]" + " could not be instantiated", e);
+ } catch (ClassCastException e) {
+ getLogger().warn(
+ "class [" + clazz + "] is not a subclass of org.apache.log4j.Level", e);
+ } catch (IllegalAccessException e) {
+ getLogger().warn(
+ "class [" + clazz
+ + "] cannot be instantiated due to access restrictions", e);
+ } catch (Exception e) {
+ getLogger().warn(
+ "class [" + clazz + "], level [" + levelName + "] conversion failed.",
+ e);
+ }
+
+ return result;
+ }
+
+ public static long toFileSize(String value, long dEfault) {
+ if (value == null) {
+ return dEfault;
+ }
+
+ String s = value.trim().toUpperCase();
+ long multiplier = 1;
+ int index;
+
+ if ((index = s.indexOf("KB")) != -1) {
+ multiplier = 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("MB")) != -1) {
+ multiplier = 1024 * 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("GB")) != -1) {
+ multiplier = 1024 * 1024 * 1024;
+ s = s.substring(0, index);
+ }
+
+ if (s != null) {
+ try {
+ return Long.valueOf(s).longValue() * multiplier;
+ } catch (NumberFormatException e) {
+ getLogger().error("[{}] is not in proper int form.", s);
+ getLogger().error("[" + value + "] not in expected format.", e);
+ }
+ }
+
+ return dEfault;
+ }
+
+ /**
+ Find the value corresponding to <code>key</code> in
+ <code>props</code>. Then perform variable substitution on the
+ found value.
+
+ */
+ public static String findAndSubst(String key, Properties props) {
+ String value = props.getProperty(key);
+
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return substVars(value, props);
+ } catch (IllegalArgumentException e) {
+ getLogger().error("Bad option value [" + value + "].", e);
+
+ return value;
+ }
+ }
+
+ /**
+ Instantiate an object given a class name. Check that the
+ <code>className</code> is a subclass of
+ <code>superClass</code>. If that test fails or the object could
+ not be instantiated, then <code>defaultValue</code> is returned.
+
+ @param className The fully qualified class name of the object to instantiate.
+ @param superClass The class to which the new object should belong.
+ @param defaultValue The object to return in case of non-fulfillment
+ */
+ public static Object instantiateByClassName(
+ String className, Class superClass, Object defaultValue) {
+ if (className != null) {
+ try {
+ Class classObj = Loader.loadClass(className);
+
+ if (!superClass.isAssignableFrom(classObj)) {
+ getLogger().error(
+ "A \"" + className + "\" object is not assignable to a \""
+ + superClass.getName() + "\" variable.");
+ getLogger().error(
+ "The class \"" + superClass.getName() + "\" was loaded by ");
+ getLogger().error(
+ "[" + superClass.getClassLoader() + "] whereas object of type ");
+ getLogger().error(
+ "\"" + classObj.getName() + "\" was loaded by ["
+ + classObj.getClassLoader() + "].");
+
+ return defaultValue;
+ }
+
+ //System.out.println("About to call classObj.newInstance(), "+classObj.getName());
+
+ return classObj.newInstance();
+ } catch(NoClassDefFoundError ncfe) {
+ getLogger().error("Could not instantiate object of class [" + className + "].", ncfe);
+ } catch (Throwable e) {
+ getLogger().error("Could not instantiate object of class [" + className + "].", e);
+ }
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Perform variable substitution in string <code>val</code> from the values of
+ * keys found the properties passed as parameter or in the system propeties.
+ *
+ * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
+ *
+ * <p>For example, if the properties parameter contains a property "key1" set
+ * as "value1", then the call <pre>
+ * String s = OptionConverter.substituteVars("Value of key is ${key1}.");</pre>
+ * will set the variable <code>s</code> to "Value of key is value1.".
+ *
+ * <p>If no value could be found for the specified key, then the
+ * system properties are searched, if the value could not be found
+ * there, then substitution defaults to the empty string.
+ *
+ * <p>For example, if system propeties contains no value for the key
+ * "inexistentKey", then the call
+ * <pre>
+ * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]"); </pre>
+ * will set <code>s</code> to "Value of inexistentKey is []".
+ *
+ * <p>Nevertheless, it is possible to specify a default substitution value
+ * using the ":-" operator. For example, the call
+ * <pre>
+ * String s = OptionConverter.subsVars("Value of key is [${key2:-val2}]");</pre>
+ * will set <code>s</code> to "Value of key is [val2]" even if the "key2"
+ * property is unset.
+ *
+ * <p>An {@link java.lang.IllegalArgumentException} is thrown if
+ * <code>val</code> contains a start delimeter "${" which is not
+ * balanced by a stop delimeter "}". </p>
+ *
+ * @param val The string on which variable substitution is performed.
+ * @throws IllegalArgumentException if <code>val</code> is malformed.
+ */
+ public static String substVars(String val, Properties props) {
+
+ StringBuffer sbuf = new StringBuffer();
+
+ int i = 0;
+ int j;
+ int k;
+
+ while (true) {
+ j = val.indexOf(DELIM_START, i);
+
+ if (j == -1) {
+ // no more variables
+ if (i == 0) { // this is a simple string
+
+ return val;
+ } else { // add the tail string which contails no variables and return the result.
+ sbuf.append(val.substring(i, val.length()));
+
+ return sbuf.toString();
+ }
+ } else {
+ sbuf.append(val.substring(i, j));
+ k = val.indexOf(DELIM_STOP, j);
+
+ if (k == -1) {
+ throw new IllegalArgumentException(
+ '"' + val + "\" has no closing brace. Opening brace at position "
+ + j + '.');
+ } else {
+ j += DELIM_START_LEN;
+
+ String rawKey = val.substring(j, k);
+
+ // Massage the key to extract a default replacement if there is one
+ String[] extracted = extractDefaultReplacement(rawKey);
+ String key = extracted[0];
+ String defaultReplacement = extracted[1]; // can be null
+
+ String replacement = null;
+
+ // first try the props passed as parameter
+ if(props != null) {
+ replacement = props.getProperty(key);
+ }
+
+ // then try in System properties
+ if (replacement == null) {
+ replacement = getSystemProperty(key, null);
+ }
+
+ // if replacement is still null, use the defaultReplacement which
+ // still be null
+ if(replacement == null) {
+ replacement = defaultReplacement;
+ }
+
+ if (replacement != null) {
+ // Do variable substitution on the replacement string
+ // such that we can solve "Hello ${x2}" as "Hello p1"
+ // where the properties are
+ // x1=p1
+ // x2=${x1}
+ String recursiveReplacement = substVars(replacement, props);
+ sbuf.append(recursiveReplacement);
+ }
+
+ i = k + DELIM_STOP_LEN;
+ }
+ }
+ }
+ }
+
+ static public String[] extractDefaultReplacement(String key) {
+ String[] result = new String[2];
+ result[0] = key;
+ int d = key.indexOf(":-");
+ if(d != -1) {
+ result[0] = key.substring(0, d);
+ result[1] = key.substring(d+2);
+ }
+ return result;
+ }
+
+ /**
+ * Replaces double backslashes (except the leading doubles in UNC's)
+ * with single backslashes for compatibility with existing path specifications
+ * that were working around use of OptionConverter.convertSpecialChars
+ * in XML configuration files.
+ *
+ * @param src source string
+ * @return source string with double backslashes replaced
+ *
+ * @since 1.3
+ */
+ public static String stripDuplicateBackslashes(final String src) {
+ int i = src.lastIndexOf('\\');
+ if (i > 0) {
+ StringBuffer buf = new StringBuffer(src);
+ for(; i > 0; i = src.lastIndexOf('\\', i - 1)) {
+ //
+ // if the preceding character is a slash then
+ // remove the preceding character
+ // and continue processing with the earlier part of the string
+ if(src.charAt(i - 1) == '\\') {
+ buf.deleteCharAt(i);
+ i--;
+ if (i == 0) break;
+ } else {
+ //
+ // if there was a single slash then
+ // the string was not trying to work around
+ // convertSpecialChars
+ //
+ return src;
+ }
+ }
+ return buf.toString();
+ }
+ return src;
+ }
+
+ /**
+ Configure log4j given a URL.
+
+ <p>The url must point to a file or resource which will be interpreted by
+ a new instance of a log4j configurator.
+
+ <p>All configurations steps are taken on the
+ <code>hierarchy</code> passed as a parameter.
+
+ <p>
+ @param url The location of the configuration file or resource.
+ @param clazz The classname, of the log4j configurator which will parse
+ the file or resource at <code>url</code>. This must be a subclass of
+ {@link Configurator}, or null. If this value is null then a default
+ configurator of {@link PropertyConfigurator} is used, unless the
+ filename pointed to by <code>url</code> ends in '.xml', in which case
+ {@link org.apache.log4j.xml.DOMConfigurator} is used.
+ @param repository The {@link LoggerRepository} to act on.
+
+ @since 1.1.4 */
+ public static void selectAndConfigure(
+ URL url, String clazz, LoggerRepository repository) {
+ Configurator configurator = null;
+ String filename = url.getFile();
+
+ if ((clazz == null) && (filename != null) && filename.endsWith(".xml")) {
+ clazz = JoranConfigurator.class.getName();
+ }
+
+ if (clazz != null) {
+ Logger logger = repository.getLogger(OptionConverter.class.getName());
+ logger.info("Preferred configurator class: " + clazz);
+
+ configurator =
+ (Configurator) instantiateByClassName(clazz, Configurator.class, null);
+
+ if (configurator == null) {
+ logger.error("Could not instantiate configurator [" + clazz + "].");
+
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(url, repository);
+ if(configurator instanceof ConfiguratorBase) {
+ ((ConfiguratorBase)configurator).dumpErrors();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/src/main/java/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 0000000..e2ffd44
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+ <p>PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+ <p>Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ @deprecated Since 1.3
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = Integer.MAX_VALUE;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(LoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, LoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<<i)) != 0) {
+ sbuf.append(SPACES[i]);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/PatternParser.java b/src/main/java/org/apache/log4j/helpers/PatternParser.java
new file mode 100644
index 0000000..734b4f7
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/PatternParser.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+// Contributors: Nelson Minar <(nelson@monkey.org>
+// Igor E. Poteryaev <jah@mail.ru>
+// Reinhard Deschler <reinhard.deschler@web.de>
+
+/**
+ Most of the work of the {@link org.apache.log4j.PatternLayout} class
+ is delegated to the PatternParser class.
+
+ <p>It is this class that parses conversion patterns and creates
+ a chained list of {@link OptionConverter OptionConverters}.
+
+ @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.2
+ @deprecated Since 1.3
+*/
+public class PatternParser {
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int MINUS_STATE = 2;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public
+ PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private
+ void addToList(PatternConverter pc) {
+ if(head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected
+ String extractOption() {
+ if((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end+1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ The option is expected to be in decimal and positive. In case of
+ error, zero is returned. */
+ protected
+ int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if(opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if(r <= 0) {
+ LogLog.error(
+ "Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ }
+ catch (NumberFormatException e) {
+ LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public
+ PatternConverter parse() {
+ char c;
+ i = 0;
+ while(i < patternLength) {
+ c = pattern.charAt(i++);
+ switch(state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if(i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if(c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch(pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(Layout.LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(
+ currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ }
+ else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch(c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if(c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ }
+ else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min*10 + (c - '0');
+ else if(c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ }
+ else {
+ LogLog.error("Error occured in position "+i
+ +".\n Was expecting digit, instead got char \""+c+"\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max*10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected
+ void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch(c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CATEGORY converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CLASS_NAME converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if(dOpt != null)
+ dateFormatStr = dOpt;
+
+ if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ }
+ catch (IllegalArgumentException e) {
+ LogLog.error("Could not instantiate SimpleDateFormat with " +
+ dateFormatStr, e);
+ df = (DateFormat) OptionConverter.instantiateByClassName(
+ "org.apache.log4j.helpers.ISO8601DateFormat",
+ DateFormat.class, null);
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo,
+ FILE_LOCATION_CONVERTER);
+ //LogLog.debug("File name converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'l':
+ pc = new LocationPatternConverter(formattingInfo,
+ FULL_LOCATION_CONVERTER);
+ //LogLog.debug("Location converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo,
+ LINE_LOCATION_CONVERTER);
+ //LogLog.debug("LINE NUMBER converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ //LogLog.debug("MESSAGE converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo,
+ METHOD_LOCATION_CONVERTER);
+ //LogLog.debug("METHOD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ //LogLog.debug("LEVEL converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo,
+ RELATIVE_TIME_CONVERTER);
+ //LogLog.debug("RELATIVE time converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ //LogLog.debug("THREAD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'u':
+ if(i < patternLength) {
+ char cNext = pattern.charAt(i);
+ if(cNext >= '0' && cNext <= '9') {
+ pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+ LogLog.debug("USER converter ["+cNext+"].");
+ formattingInfo.dump();
+ currentLiteral.setLength(0);
+ i++;
+ }
+ else
+ LogLog.error("Unexpected char" +cNext+" at position "+i);
+ }
+ break;*/
+ case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ //LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;
+ default:
+ LogLog.error("Unexpected char [" +c+"] at position "+i
+ +" in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected
+ void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ switch(type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ case NDC_CONVERTER:
+ return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default: return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public
+ final
+ void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ date.setTime(event.timeStamp);
+ String converted = null;
+ try {
+ converted = df.format(date);
+ }
+ catch (Exception ex) {
+ LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private static class MDCPatternConverter extends PatternConverter {
+ private String key;
+
+ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+ super(formattingInfo);
+ this.key = key;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ Object val = event.getMDC(key);
+ if(val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+ }
+
+
+ private static class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ LocationInfo locationInfo = event.getLocationInformation();
+ switch(type) {
+ case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default: return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract
+ String getFullyQualifiedName(LoggingEvent event);
+
+ public
+ String convert(LoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if(precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len -1 ;
+ for(int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end-1);
+ if(end == -1)
+ return n;
+ }
+ return n.substring(end+1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
+
diff --git a/src/main/java/org/apache/log4j/helpers/PlatformInfo.java b/src/main/java/org/apache/log4j/helpers/PlatformInfo.java
new file mode 100644
index 0000000..1933bf7
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/PlatformInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.Properties;
+
+
+/**
+ * This class provides information about the runtime platform.
+ *
+ * @author Ceki Gulcu
+ * @since 1.3
+ * */
+public class PlatformInfo {
+ private static final int UNINITIALIZED = -1;
+
+ // Check if we are running in IBM's visual age.
+ private static int inVisualAge = UNINITIALIZED;
+ private static int onAS400 = UNINITIALIZED;
+ private static int hasStackTraceElement = UNINITIALIZED;
+
+ public static boolean isInVisualAge() {
+ if (inVisualAge == UNINITIALIZED) {
+ try {
+ Class dummy = Class.forName("com.ibm.uvm.tools.DebugSupport");
+ inVisualAge = 1;
+ } catch (Throwable e) {
+ inVisualAge = 0;
+ }
+ }
+ return (inVisualAge == 1);
+ }
+
+ /**
+ * Are we running on AS400?
+ */
+ public static boolean isOnAS400() {
+ if (onAS400 == UNINITIALIZED) {
+ try {
+ Properties p = System.getProperties();
+ String osname = p.getProperty("os.name");
+ if ((osname != null) && (osname.equals("OS/400"))) {
+ onAS400 = 1;
+ } else {
+ onAS400 = 0;
+ }
+ } catch (Throwable e) {
+ // This should not happen, but if it does, assume we are not on
+ // AS400.
+ onAS400 = 0;
+ }
+ }
+ return (onAS400 == 1);
+ }
+
+ public static boolean hasStackTraceElement() {
+ if (hasStackTraceElement == UNINITIALIZED) {
+ try {
+ Class.forName("java.lang.StackTraceElement");
+ hasStackTraceElement = 1;
+ } catch (Throwable e) {
+ // we are running on a JDK prior to 1.4
+ hasStackTraceElement = 0;
+ }
+ }
+ return (hasStackTraceElement == 1);
+ }
+
+ public static boolean isJDK14OrLater() {
+ return hasStackTraceElement();
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/src/main/java/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 0000000..66ef7fa
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/QuietWriter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.io.Writer;
+import java.io.FilterWriter;
+import java.io.IOException;
+import org.apache.log4j.spi.ErrorCode;
+
+
+/**
+ QuietWriter does not throw exceptions when things go
+ wrong. Instead, it delegates error handling to its {@link
+ org.apache.log4j.spi.ErrorHandler}.
+
+ @author Ceki Gülcü
+
+ @since 0.7.3
+ @deprecated
+*/
+public class QuietWriter extends FilterWriter {
+
+ /**
+ * @deprecated
+ */
+ protected org.apache.log4j.spi.ErrorHandler errorHandler;
+
+ /**
+ * @deprecated
+ * @param writer
+ * @param errorHandler
+ */
+ public
+ QuietWriter(Writer writer, org.apache.log4j.spi.ErrorHandler errorHandler) {
+ super(writer);
+ setErrorHandler(errorHandler);
+ }
+
+ public
+ void write(String string) {
+ try {
+ out.write(string);
+ } catch(IOException e) {
+ errorHandler.error("Failed to write ["+string+"].", e,
+ ErrorCode.WRITE_FAILURE);
+ }
+ }
+
+ public
+ void flush() {
+ try {
+ out.flush();
+ } catch(IOException e) {
+ errorHandler.error("Failed to flush writer,", e,
+ ErrorCode.FLUSH_FAILURE);
+ }
+ }
+
+
+ public
+ void setErrorHandler(org.apache.log4j.spi.ErrorHandler eh) {
+ if(eh == null) {
+ // This is a programming error on the part of the enclosing appender.
+ throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+ } else {
+ this.errorHandler = eh;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/ReaderWriterLock.java b/src/main/java/org/apache/log4j/helpers/ReaderWriterLock.java
new file mode 100644
index 0000000..3412774
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/ReaderWriterLock.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.io.PrintWriter;
+
+
+/**
+ *
+ * A RederWriterLock allows multiple readers to obtain the lock at the same time
+ * but allows only one writer at a time.
+ *
+ * When both readers and writers wait to obtain the lock, priority is given to
+ * waiting writers.
+ *
+ * This lock is not reentrant. It is possible for a writer in possession of a writer
+ * lock to fail to obtain a reader lock. The same goes for reader in possession of a
+ * reader lock. It can fail to obtain a writer lock.
+ *
+ * THIS LOCK IS NOT RENTRANT.
+ *
+ * It is the developer's responsability to retstrict the use of this lock to small segments
+ * of code where reentrancy can be avoided.
+ *
+ * Note that the RederWriterLock is only useful in cases where a resource:
+ *
+ * 1) Has many frequent read operations performed on it
+ * 2) Only rarely is the resource modified (written)
+ * 3) Read operations are invoked by many different threads
+ *
+ * If any of the above conditions are not met, it is better to avoid this fancy lock.
+ *
+ * @author Ceki Gülcü
+ *
+ */
+public class ReaderWriterLock {
+ int readers = 0;
+ int writers = 0;
+ int waitingWriters = 0;
+ PrintWriter printWriter;
+
+ public ReaderWriterLock() {
+ }
+
+ public ReaderWriterLock(PrintWriter pw) {
+ printWriter = pw;
+ }
+
+ public synchronized void getReadLock() {
+ if (printWriter != null) {
+ printMessage("Asking for read lock.");
+ }
+
+ while ((writers > 0) || (waitingWriters > 0)) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ if (printWriter != null) {
+ printMessage("Got read lock.");
+ }
+
+ readers++;
+ }
+
+ public synchronized void releaseReadLock() {
+ if (printWriter != null) {
+ printMessage("About to release read lock.");
+ }
+
+ readers--;
+
+ if (waitingWriters > 0) {
+ notifyAll();
+ }
+ }
+
+ public synchronized void getWriteLock() {
+ if (printWriter != null) {
+ printMessage("Asking for write lock.");
+ }
+
+ waitingWriters++;
+
+ while ((readers > 0) || (writers > 0)) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ if (printWriter != null) {
+ printMessage("Got write lock.");
+ }
+
+ waitingWriters--;
+ writers++;
+ }
+
+ public synchronized void releaseWriteLock() {
+ if (printWriter != null) {
+ printMessage("About to release write lock.");
+ }
+
+ writers--;
+ notifyAll();
+ }
+
+ void printMessage(String msg) {
+ //printWriter.print("[");
+ printWriter.println(Thread.currentThread().getName() + " " + msg);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
new file mode 100644
index 0000000..38b8d19
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.Date;
+import java.util.TimeZone;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.DateFormat;
+
+/**
+ Formats a {@link Date} by printing the number of milliseconds
+ elapsed since the construction of the format. This is the fastest
+ printing DateFormat in the package.
+
+ @author Ceki Gülcü
+ @since 0.7.5
+*/
+public class RelativeTimeDateFormat extends DateFormat {
+
+ protected final long startTime;
+
+ public
+ RelativeTimeDateFormat() {
+ this.startTime = System.currentTimeMillis();
+ }
+
+ /**
+ Appends to <code>sbuf</code> the number of milliseconds elapsed
+ since the start of the application.
+
+ @since 0.7.5
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+ //System.err.println(":"+ date.getTime() + " - " + startTime);
+ return sbuf.append((date.getTime() - startTime));
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+
+ /**
+ * Sets the timezone.
+ * Ignored by this formatter, but intercepted to prevent
+ * NullPointerException in superclass.
+ * @param tz TimeZone timezone
+ */
+ public void setTimeZone(final TimeZone tz) {
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java b/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java
new file mode 100644
index 0000000..817cbc3
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+
+
+import java.io.Writer;
+
+/**
+ SyslogQuietWriter extends QuietWriter by prepending the syslog
+ level code before each printed String.
+
+ @since 0.7.3
+ @deprecated
+*/
+public class SyslogQuietWriter extends QuietWriter {
+
+ int syslogFacility;
+ int level;
+
+ /**
+ * @deprecated
+ * @param writer
+ * @param syslogFacility
+ * @param eh
+ */
+ public
+ SyslogQuietWriter(Writer writer, int syslogFacility, org.apache.log4j.spi.ErrorHandler eh) {
+ super(writer, eh);
+ this.syslogFacility = syslogFacility;
+ }
+
+ public
+ void setLevel(int level) {
+ this.level = level;
+ }
+
+ public
+ void setSyslogFacility(int syslogFacility) {
+ this.syslogFacility = syslogFacility;
+ }
+
+ public
+ void write(String string) {
+ super.write("<"+(syslogFacility | level)+">" + string);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/SyslogWriter.java b/src/main/java/org/apache/log4j/helpers/SyslogWriter.java
new file mode 100644
index 0000000..bfbbcb8
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/SyslogWriter.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+
+/**
+ SyslogWriter is a wrapper around the java.net.DatagramSocket class
+ so that it behaves like a java.io.Writer.
+
+ @since 0.7.3
+*/
+public class SyslogWriter extends Writer {
+ private static final int SYSLOG_PORT = 514;
+ private InetAddress address;
+ private final int port;
+ private DatagramSocket ds;
+
+ private Logger logger = LogManager.getLogger(SyslogWriter.class);
+ private StringBuffer buf = new StringBuffer();
+
+ /**
+ * Constructs a new instance of SyslogWriter.
+ * @param syslogHost host name, may not be null. A port
+ * may be specified by following the name or IPv4 literal address with
+ * a colon and a decimal port number. To specify a port with an IPv6
+ * address, enclose the IPv6 address in square brackets before appending
+ * the colon and decimal port number.
+ */
+ public SyslogWriter(final String syslogHost) {
+ if (syslogHost == null) {
+ throw new NullPointerException("syslogHost");
+ }
+
+ String host = syslogHost;
+ int urlPort = -1;
+
+ //
+ // If not an unbracketed IPv6 address then
+ // parse as a URL
+ //
+ if (host.indexOf("[") != -1 || host.indexOf(':') == host.lastIndexOf(':')) {
+ try {
+ URL url = new URL("http://" + host);
+ if (url.getHost() != null) {
+ host = url.getHost();
+ // if host is a IPv6 literal, strip off the brackets
+ if(host.startsWith("[") && host.charAt(host.length() - 1) == ']') {
+ host = host.substring(1, host.length() - 1);
+ }
+ urlPort = url.getPort();
+ }
+ } catch(MalformedURLException e) {
+ logger.warn("Malformed URL: will attempt to interpret as InetAddress.", e);
+ }
+ }
+
+ if (urlPort == -1) {
+ urlPort = SYSLOG_PORT;
+ }
+ port = urlPort;
+
+ try {
+ this.address = InetAddress.getByName(host);
+ } catch (UnknownHostException e) {
+ logger.error(
+ "Could not find " + host + ". All logging will FAIL.", e);
+ }
+
+ try {
+ this.ds = new DatagramSocket();
+ } catch (SocketException e) {
+ e.printStackTrace();
+ logger.error(
+ "Could not instantiate DatagramSocket to " + host
+ + ". All logging will FAIL.", e);
+ }
+ }
+
+ public void write(char[] charArray, int offset, int len) throws IOException {
+ buf.append(charArray, offset, len);
+ }
+
+ public void write(String str) throws IOException {
+ buf.append(str);
+ }
+
+ /**
+ * Sends the pending data.
+ */
+ public void flush() throws IOException {
+ if (buf.length() == 0)
+ return;
+ // logging here can be problematic during shutdown when writing the footer
+ // logger.debug("Writing out [{}]", buf);
+ byte[] bytes = buf.toString().getBytes();
+ DatagramPacket packet =
+ new DatagramPacket(bytes, bytes.length, address, port);
+
+ ds.send(packet);
+
+ // clean up for next time
+ buf.setLength(0);
+ }
+
+ /**
+ * Closes the datagram socket.
+ */
+ public void close() {
+ try {
+ flush();
+ } catch (IOException e) {
+ // should throw it ... can't change method sig. though
+ }
+ ds.close();
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java b/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java
new file mode 100644
index 0000000..0736f0f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2006 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.Hashtable;
+
+/**
+ <code>ThreadLocalMap</code> extends {@link InheritableThreadLocal}
+ to bequeath a copy of the hashtable of the MDC of the parent
+ thread.
+
+ @author Ceki Gülcü
+ @since 1.2
+*/
+final public class ThreadLocalMap extends InheritableThreadLocal {
+
+ public final Object childValue(Object parentValue) {
+ Hashtable ht = (Hashtable) parentValue;
+ if(ht != null) {
+ return ht.clone();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/Transform.java b/src/main/java/org/apache/log4j/helpers/Transform.java
new file mode 100644
index 0000000..f865dd1
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/Transform.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.io.IOException;
+import java.io.Writer;
+
+
+/**
+ Utility class for transforming strings.
+
+ @author Ceki Gülcü
+ @author Michael A. McAngus
+ */
+public class Transform {
+ private static final String CDATA_START = "<![CDATA[";
+ private static final String CDATA_END = "]]>";
+ private static final String CDATA_PSEUDO_END = "]]>";
+ private static final String CDATA_EMBEDED_END =
+ CDATA_END + CDATA_PSEUDO_END + CDATA_START;
+ private static final int CDATA_END_LEN = CDATA_END.length();
+
+ /**
+ * This method takes a string which may contain HTML tags (ie,
+ * <b>, <table>, etc) and replaces any '<' and '>'
+ * characters with respective predefined entity references.
+ *
+ * @param input The text to be converted.
+ */
+ public static String escapeTags(final String input) {
+ //Check if the string is null or zero length -- if so, return
+ //what was sent in.
+ if ((input == null)
+ || (input.length() == 0)
+ || (input.indexOf("<") == -1 && input.indexOf(">") == -1)) {
+ return input;
+ }
+
+ StringBuffer buf = new StringBuffer(input);
+ for(int i = 0;i < buf.length(); i++) {
+ char ch = buf.charAt(i);
+ if (ch == '<') {
+ buf.replace(i, i + 1, "<");
+ } else if (ch == '>') {
+ buf.replace(i, i + 1, ">");
+ }
+ }
+ return buf.toString();
+ }
+
+ //public static void appendEscapingCDATA(StringBuffer buf, String str) {
+ //
+ //}
+
+ /**
+ * Ensures that embeded CDEnd strings (]]>) are handled properly
+ * within message, NDC and throwable tag text.
+ *
+ * @param output Writer. The
+ * initial CDSutart (<![CDATA[) and final CDEnd (]]>) of the CDATA
+ * section are the responsibility of the calling method.
+ *
+ * @param str The String that is inserted into an existing CDATA Section.
+ * */
+ public static void appendEscapingCDATA(StringBuffer output, String str) {
+ if (str == null) {
+ return;
+ }
+
+ int end = str.indexOf(CDATA_END);
+
+ if (end < 0) {
+ output.append(str);
+
+ return;
+ }
+
+ int start = 0;
+
+ while (end > -1) {
+ output.append(str.substring(start, end));
+ output.append(CDATA_EMBEDED_END);
+ start = end + CDATA_END_LEN;
+
+ if (start < str.length()) {
+ end = str.indexOf(CDATA_END, start);
+ } else {
+ return;
+ }
+ }
+
+ output.append(str.substring(start));
+ }
+}
diff --git a/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java b/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
new file mode 100644
index 0000000..c46b14f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed 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.helpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Level;
+
+/**
+ * An extension of the Level class that provides support for java.util.logging
+ * Levels.
+ *
+ * @author Scott Deboy <sdeboy@apache.org>
+ *
+ */
+
+public class UtilLoggingLevel extends Level {
+
+ private static final long serialVersionUID = 909301162611820211L;
+
+ public static final int SEVERE_INT = 17000;
+ public static final int WARNING_INT = 16000;
+ public static final int INFO_INT = 15000;
+ public static final int CONFIG_INT = 14000;
+ public static final int FINE_INT = 13000;
+ public static final int FINER_INT = 12000;
+ public static final int FINEST_INT = 11000;
+ public static final int UNKNOWN_INT = 10000;
+
+ public static final UtilLoggingLevel SEVERE = new UtilLoggingLevel(SEVERE_INT, "SEVERE", 0);
+ public static final UtilLoggingLevel WARNING = new UtilLoggingLevel(WARNING_INT, "WARNING", 4);
+ public static final UtilLoggingLevel INFO = new UtilLoggingLevel(INFO_INT, "INFO", 5);
+ public static final UtilLoggingLevel CONFIG = new UtilLoggingLevel(CONFIG_INT, "CONFIG", 6);
+ public static final UtilLoggingLevel FINE = new UtilLoggingLevel(FINE_INT, "FINE", 7);
+ public static final UtilLoggingLevel FINER = new UtilLoggingLevel(FINER_INT, "FINER", 8);
+ public static final UtilLoggingLevel FINEST = new UtilLoggingLevel(FINEST_INT, "FINEST", 9);
+
+ protected UtilLoggingLevel(int level, String levelStr, int syslogEquivalent) {
+ super(level, levelStr, syslogEquivalent);
+ }
+
+ /**
+ Convert an integer passed as argument to a level. If the
+ conversion fails, then this method returns the specified default.
+ */
+ public static UtilLoggingLevel toLevel(int val, UtilLoggingLevel defaultLevel) {
+ switch (val) {
+ case SEVERE_INT:
+ return SEVERE;
+
+ case WARNING_INT:
+ return WARNING;
+
+ case INFO_INT:
+ return INFO;
+
+ case CONFIG_INT:
+ return CONFIG;
+
+ case FINE_INT:
+ return FINE;
+
+ case FINER_INT:
+ return FINER;
+
+ case FINEST_INT:
+ return FINEST;
+
+ default:
+ return defaultLevel;
+ }
+ }
+
+ public static Level toLevel(int val) {
+ return toLevel(val, FINEST);
+ }
+
+ public static List getAllPossibleLevels() {
+ ArrayList list=new ArrayList();
+ list.add(FINE);
+ list.add(FINER);
+ list.add(FINEST);
+ list.add(INFO);
+ list.add(CONFIG);
+ list.add(WARNING);
+ list.add(SEVERE);
+ return list;
+ }
+
+ public static Level toLevel(String s) {
+ return toLevel(s, Level.DEBUG);
+ }
+
+ public static Level toLevel(String sArg, Level defaultLevel) {
+ if (sArg == null) {
+ return defaultLevel;
+ }
+
+ String s = sArg.toUpperCase();
+
+ if (s.equals("SEVERE")) {
+ return SEVERE;
+ }
+
+ //if(s.equals("FINE")) return Level.FINE;
+ if (s.equals("WARNING")) {
+ return WARNING;
+ }
+
+ if (s.equals("INFO")) {
+ return INFO;
+ }
+
+ if (s.equals("CONFI")) {
+ return CONFIG;
+ }
+
+ if (s.equals("FINE")) {
+ return FINE;
+ }
+
+ if (s.equals("FINER")) {
+ return FINER;
+ }
+
+ if (s.equals("FINEST")) {
+ return FINEST;
+ }
+ return defaultLevel;
+ }
+
+}
diff --git a/src/main/java/org/apache/log4j/helpers/package.html b/src/main/java/org/apache/log4j/helpers/package.html
new file mode 100644
index 0000000..870a8fd
--- /dev/null
+++ b/src/main/java/org/apache/log4j/helpers/package.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<title></title>
+</head>
+
+<body>
+
+<p>This package is used internally.
+
+
+<hr>
+<address></address>
+<!-- hhmts start -->
+Last modified: Sat Jul 3 15:12:58 MDT 1999
+<!-- hhmts end -->
+</body> </html>