diff --git a/SOURCES/0001-Backport-fix-for-CVE-2017-5645.patch b/SOURCES/0001-Backport-fix-for-CVE-2017-5645.patch
index 704ecfc..acb6e3d 100644
--- a/SOURCES/0001-Backport-fix-for-CVE-2017-5645.patch
+++ b/SOURCES/0001-Backport-fix-for-CVE-2017-5645.patch
@@ -1,20 +1,20 @@
-From ea4609eca531916ac347686c048bebdb7b4b6e0d Mon Sep 17 00:00:00 2001
+From 39f12f04d0b138377194b7aa43aa16a2ff401f98 Mon Sep 17 00:00:00 2001
From: Michael Simacek It is this class that parses conversion patterns and creates
+- a chained list of {@link OptionConverter OptionConverters}.
+-
+- @author James P. Cakalic
+- @author Ceki Gülcü
+- @author Anders Kristensen
+-
+- @since 0.8.2
+-*/
++ * Most of the work of the {@link org.apache.log4j.PatternLayout} class is
++ * delegated to the PatternParser class.
++ *
++ *
++ * It is this class that parses conversion patterns and creates a chained list
++ * of {@link OptionConverter OptionConverters}.
++ *
++ * @author James P. Cakalic
++ * @author Ceki Gülcü
++ * @author Anders Kristensen
++ *
++ * @since 0.8.2
++ */
+ 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 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;
++ private static final char LEFT_BRACKET = '{';
++ private static final char RIGHT_BRACKET = '}';
++ private static final char N_CHAR = 'n';
++ private static final char DOT_CHAR = '.';
++ private static final char DASH_CHAR = '-';
++ private static final char ESCAPE_CHAR = '%';
++
++ private static final int LITERAL_STATE = 0;
++ private static final int CONVERTER_STATE = 1;
++ 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;
+ }
+- }
+-
+- 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;
++
++ private void addToList(PatternConverter pc) {
++ if (head == null) {
++ head = tail = pc;
++ } else {
++ tail.next = pc;
++ tail = pc;
+ }
+- }
+- 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;
++
++ protected String extractOption() {
++ if ((i < patternLength) && (pattern.charAt(i) == LEFT_BRACKET)) {
++ int end = pattern.indexOf(RIGHT_BRACKET, i);
++ if (end > i) {
++ String r = pattern.substring(i + 1, end);
++ i = end + 1;
++ return r;
++ }
+ }
+- break;
+- } // switch
+- } // while
+- if(currentLiteral.length() != 0) {
+- addToList(new LiteralPatternConverter(currentLiteral.toString()));
+- //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
++ return null;
+ }
+- 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);
++
++ /**
++ * 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);
++ }
+ }
+- }
+- 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++;
++ 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_CHAR:
++ 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 DASH_CHAR:
++ formattingInfo.leftAlign = true;
++ break;
++ case DOT_CHAR:
++ 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+"\".");
+ }
+- 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);
++ return head;
+ }
+
+- 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;
++ 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);
+ }
+
+- 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;
+- }
++ 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();
+ }
+- }
+
+- private static class LiteralPatternConverter extends PatternConverter {
+- private String literal;
++ // ---------------------------------------------------------------------
++ // PatternConverters
++ // ---------------------------------------------------------------------
+
+- LiteralPatternConverter(String value) {
+- literal = value;
+- }
++ private static class BasicPatternConverter extends PatternConverter {
++ int type;
+
+- public
+- final
+- void format(StringBuffer sbuf, LoggingEvent event) {
+- sbuf.append(literal);
+- }
++ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
++ super(formattingInfo);
++ this.type = type;
++ }
+
+- public
+- String convert(LoggingEvent event) {
+- return literal;
++ 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 DatePatternConverter extends PatternConverter {
+- private DateFormat df;
+- private Date date;
++ private static class LiteralPatternConverter extends PatternConverter {
++ private String literal;
+
+- DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+- super(formattingInfo);
+- date = new Date();
+- this.df = df;
+- }
++ LiteralPatternConverter(String value) {
++ literal = value;
++ }
+
+- 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;
++ public final void format(StringBuffer sbuf, LoggingEvent event) {
++ sbuf.append(literal);
++ }
++
++ public String convert(LoggingEvent event) {
++ return literal;
++ }
+ }
+- }
+
+- private static class MDCPatternConverter extends PatternConverter {
+- private String key;
++ private static class DatePatternConverter extends PatternConverter {
++ private DateFormat df;
++ private Date date;
+
+- MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+- super(formattingInfo);
+- this.key = key;
+- }
++ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
++ super(formattingInfo);
++ date = new Date();
++ this.df = df;
++ }
+
+- public
+- String convert(LoggingEvent event) {
+- if (key == null) {
+- StringBuffer buf = new StringBuffer("{");
+- Map properties = event.getProperties();
+- if (properties.size() > 0) {
+- Object[] keys = properties.keySet().toArray();
+- Arrays.sort(keys);
+- for (int i = 0; i < keys.length; i++) {
+- buf.append('{');
+- buf.append(keys[i]);
+- buf.append(',');
+- buf.append(properties.get(keys[i]));
+- buf.append('}');
+- }
+- }
+- buf.append('}');
+- return buf.toString();
+- } else {
+- Object val = event.getMDC(key);
+- if(val == null) {
+- return null;
+- } else {
+- return val.toString();
+- }
+- }
++ 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;
+
+- private class LocationPatternConverter extends PatternConverter {
+- int type;
++ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
++ super(formattingInfo);
++ this.key = key;
++ }
+
+- LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+- super(formattingInfo);
+- this.type = type;
++ public String convert(LoggingEvent event) {
++ if (key == null) {
++ StringBuffer buf = new StringBuffer("{");
++ Map properties = event.getProperties();
++ if (properties.size() > 0) {
++ Object[] keys = properties.keySet().toArray();
++ Arrays.sort(keys);
++ for (int i = 0; i < keys.length; i++) {
++ buf.append('{');
++ buf.append(keys[i]);
++ buf.append(',');
++ buf.append(properties.get(keys[i]));
++ buf.append('}');
++ }
++ }
++ buf.append('}');
++ return buf.toString();
++ } else {
++ Object val = event.getMDC(key);
++ if (val == null) {
++ return null;
++ } else {
++ return val.toString();
++ }
++ }
++ }
+ }
+
+- 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 class LocationPatternConverter extends PatternConverter {
++ int type;
+
+- private static abstract class NamedPatternConverter extends PatternConverter {
+- int precision;
++ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
++ super(formattingInfo);
++ this.type = type;
++ }
+
+- NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+- super(formattingInfo);
+- this.precision = precision;
++ 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;
++ }
++ }
+ }
+
+- 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;
++ 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);
++ }
+ }
+- return n.substring(end+1, len);
+- }
+ }
+- }
+
+- private class ClassNamePatternConverter extends NamedPatternConverter {
++ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+- ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+- super(formattingInfo, precision);
+- }
++ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
++ super(formattingInfo, precision);
++ }
+
+- String getFullyQualifiedName(LoggingEvent event) {
+- return event.getLocationInformation().getClassName();
++ String getFullyQualifiedName(LoggingEvent event) {
++ return event.getLocationInformation().getClassName();
++ }
+ }
+- }
+
+- private class CategoryPatternConverter extends NamedPatternConverter {
++ private class CategoryPatternConverter extends NamedPatternConverter {
+
+- CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+- super(formattingInfo, precision);
+- }
++ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
++ super(formattingInfo, precision);
++ }
+
+- String getFullyQualifiedName(LoggingEvent event) {
+- return event.getLoggerName();
++ String getFullyQualifiedName(LoggingEvent event) {
++ return event.getLoggerName();
++ }
+ }
+- }
+ }
+-
+diff --git a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
+index ad35f657..6ba9b66c 100644
+--- a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
++++ b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
+@@ -18,381 +18,414 @@ package org.apache.log4j.jdbc;
+
+ import java.sql.Connection;
+ import java.sql.DriverManager;
++import java.sql.PreparedStatement;
+ import java.sql.SQLException;
+ import java.sql.Statement;
+ import java.util.ArrayList;
+-import java.util.Iterator;
+
+ import org.apache.log4j.PatternLayout;
++import org.apache.log4j.helpers.LogLog;
+ import org.apache.log4j.spi.ErrorCode;
+ import org.apache.log4j.spi.LoggingEvent;
+
+-
+ /**
+- The JDBCAppender provides for sending log events to a database.
+-
+- WARNING: This version of JDBCAppender
+- is very likely to be completely replaced in the future. Moreoever,
+- it does not log exceptions.
+-
+- Each append call adds to an The Overriding the {@link #getLogStatement} method allows more
+- explicit control of the statement used for logging.
+-
+- For use as a base class:
+-
+- ArrayList
buffer. When
+- the buffer is filled each log event is placed in a sql statement
+- (configurable) and executed.
+-
+- BufferSize, db URL, User, & Password are
+- configurable options in the standard log4j ways.
+-
+- setSql(String sql)
sets the SQL statement to be
+- used for logging -- this statement is sent to a
+- PatternLayout
(either created automaticly by the
+- appender or added by the user). Therefore by default all the
+- conversion patterns in PatternLayout
can be used
+- inside of the statement. (see the test cases for examples)
+-
+-
+-
+-
+-
+- @author Kevin Steppe (ksteppe@pacbell.net)
+-
+-*/
+-public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
+- implements org.apache.log4j.Appender {
+-
+- /**
+- * URL of the DB for default connection handling
+- */
+- protected String databaseURL = "jdbc:odbc:myDB";
+-
+- /**
+- * User to connect as for default connection handling
+- */
+- protected String databaseUser = "me";
+-
+- /**
+- * User to use for default connection handling
+- */
+- protected String databasePassword = "mypassword";
+-
+- /**
+- * Connection used by default. The connection is opened the first time it
+- * is needed and then held open until the appender is closed (usually at
+- * garbage collection). This behavior is best modified by creating a
+- * sub-class and overriding the getConnection()
to pass any connection
+- you want. Typically this is used to enable application wide
+- connection pooling.
+-
+- closeConnection(Connection con)
-- if
+- you override getConnection make sure to implement
+- closeConnection
to handle the connection you
+- generated. Typically this would return the connection to the
+- pool it came from.
+-
+- getLogStatement(LoggingEvent event)
to
+- produce specialized or dynamic statements. The default uses the
+- sql option value.
+-
+- getConnection
and
+- * closeConnection
methods.
+- */
+- protected Connection connection = null;
+-
+- /**
+- * Stores the string given to the pattern layout for conversion into a SQL
+- * statement, eg: insert into LogTable (Thread, Class, Message) values
+- * ("%t", "%c", "%m").
+- *
+- * Be careful of quotes in your messages!
+- *
+- * Also see PatternLayout.
+- */
+- protected String sqlStatement = "";
+-
+- /**
+- * size of LoggingEvent buffer before writting to the database.
+- * Default is 1.
+- */
+- protected int bufferSize = 1;
+-
+- /**
+- * ArrayList holding the buffer of Logging Events.
+- */
+- protected ArrayList buffer;
+-
+- /**
+- * Helper object for clearing out the buffer
+- */
+- protected ArrayList removes;
+-
+- private boolean locationInfo = false;
+-
+- public JDBCAppender() {
+- super();
+- buffer = new ArrayList(bufferSize);
+- removes = new ArrayList(bufferSize);
+- }
+-
+- /**
+- * Gets whether the location of the logging request call
+- * should be captured.
+- *
+- * @since 1.2.16
+- * @return the current value of the LocationInfo option.
+- */
+- public boolean getLocationInfo() {
+- return locationInfo;
+- }
+-
+- /**
+- * The LocationInfo option takes a boolean value. By default, it is
+- * set to false which means there will be no effort to extract the location
+- * information related to the event. As a result, the event that will be
+- * ultimately logged will likely to contain the wrong location information
+- * (if present in the log format).
+- *
+- *
+- * Location information extraction is comparatively very slow and should be
+- * avoided unless performance is not a concern.
+- *
++ * WARNING: This version of JDBCAppender is very likely ++ * to be completely replaced in the future. Moreoever, it does not log ++ * exceptions. ++ * ++ *
++ * Each append call adds to an ArrayList
buffer. When the buffer is
++ * filled each log event is placed in a sql statement (configurable) and
++ * executed.
++ *
++ * BufferSize, db URL, User, & Password are
++ * configurable options in the standard log4j ways.
++ *
++ *
++ * The setSql(String sql)
sets the SQL statement to be used for
++ * logging -- this statement is sent to a PatternLayout
(either
++ * created automatically by the appender or added by the user). Therefore by
++ * default all the conversion patterns in PatternLayout
can be used
++ * inside of the statement. (see the test cases for examples)
++ *
++ *
++ * Overriding the {@link #getLogStatement} method allows more explicit control ++ * of the statement used for logging. ++ * ++ *
++ * For use as a base class: ++ * ++ *
getConnection()
to pass any connection you want.
++ * Typically this is used to enable application wide connection pooling.
++ *
++ * closeConnection(Connection con)
-- if you override
++ * getConnection make sure to implement closeConnection
to handle
++ * the connection you generated. Typically this would return the connection to
++ * the pool it came from.
++ *
++ * getLogStatement(LoggingEvent event)
to produce
++ * specialized or dynamic statements. The default uses the sql option value.
++ *
++ * getConnection
and closeConnection
++ * methods.
++ */
++ protected Connection connection = null;
++
++ /**
++ * Stores the string given to the pattern layout for conversion into a SQL
++ * statement, eg: insert into LogTable (Thread, Class, Message) values ("%t",
++ * "%c", "%m").
++ *
++ * Be careful of quotes in your messages!
++ *
++ * Also see PatternLayout.
++ */
++ protected String sqlStatement = "";
++
++ private JdbcPatternParser preparedStatementParser;
++ /**
++ * size of LoggingEvent buffer before writting to the database. Default is 1.
++ */
++ protected int bufferSize = 1;
++
++ /**
++ * ArrayList holding the buffer of Logging Events.
++ */
++ protected ArrayList