Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xorg.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/LogController.java51
-rw-r--r--org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationAdminConfigurationProvider.java13
-rw-r--r--org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationProvider.java2
-rw-r--r--org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapper.java513
-rw-r--r--org.eclipse.virgo.medic.core/src/test/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapperTests.java193
-rw-r--r--org.eclipse.virgo.medic.core/src/test/resources/logback-test.xml9
6 files changed, 763 insertions, 18 deletions
diff --git a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/LogController.java b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/LogController.java
index 9c26d09..15632e6 100755
--- a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/LogController.java
+++ b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/LogController.java
@@ -199,6 +199,11 @@ public class LogController implements ConfigurationChangeListener {
return wrapper;
}
+ private PrintStream decoratePrintStream(PrintStream printStream, String loggerName, LoggingLevel loggingLevel, ExecutionStackAccessor stackAccessor, ConfigurationProvider configurationProvider, String configurationProperty) {
+ TeeLoggingPrintStreamWrapper decorator = new TeeLoggingPrintStreamWrapper(printStream, loggerName, loggingLevel, stackAccessor, configurationProvider, configurationProperty);
+ return decorator;
+ }
+
private ServiceRegistration<PrintStream> publishPrintStream(PrintStream printStream, String name) {
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put("org.eclipse.virgo.medic.log.printStream", name);
@@ -284,15 +289,22 @@ public class LogController implements ConfigurationChangeListener {
System.setOut(wrapPrintStream(System.out, LOGGER_NAME_SYSOUT, LoggingLevel.INFO, stackAccessor, configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSOUT));
} else {
- if (delegatingSysOutRegistration != null) {
- registrationTracker.unregister(delegatingSysOutRegistration);
- delegatingSysOutRegistration = null;
+ if (ConfigurationProvider.LOG_TEE_SYSSTREAMS.equals((String)configuration.get(ConfigurationProvider.KEY_LOG_WRAP_SYSOUT))) {
+ delegatingSysOutRegistration = publishDelegatingPrintStream(delegatingSysOut, LOGGER_NAME_SYSOUT_DELEGATE);
+ sysOutRegistration = publishPrintStream(this.sysOut, LOGGER_NAME_SYSOUT);
+
+ System.setOut(decoratePrintStream(System.out, LOGGER_NAME_SYSOUT, LoggingLevel.INFO, stackAccessor, configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSOUT));
+ } else {
+ if (delegatingSysOutRegistration != null) {
+ registrationTracker.unregister(delegatingSysOutRegistration);
+ delegatingSysOutRegistration = null;
+ }
+ if (sysOutRegistration != null) {
+ registrationTracker.unregister(sysOutRegistration);
+ sysOutRegistration = null;
+ }
+ System.setOut((PrintStream) delegatingSysOut);
}
- if (sysOutRegistration != null) {
- registrationTracker.unregister(sysOutRegistration);
- sysOutRegistration = null;
- }
- System.setOut((PrintStream)delegatingSysOut);
}
if (Boolean.valueOf((String)configuration.get(ConfigurationProvider.KEY_LOG_WRAP_SYSERR))) {
@@ -301,15 +313,22 @@ public class LogController implements ConfigurationChangeListener {
System.setErr(wrapPrintStream(System.err, LOGGER_NAME_SYSERR, LoggingLevel.ERROR, stackAccessor, configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSERR));
} else {
- if (delegatingSysErrRegistration != null) {
- registrationTracker.unregister(delegatingSysErrRegistration);
- delegatingSysErrRegistration = null;
- }
- if (sysErrRegistration != null) {
- registrationTracker.unregister(sysErrRegistration);
- sysErrRegistration = null;
+ if (ConfigurationProvider.LOG_TEE_SYSSTREAMS.equals((String)configuration.get(ConfigurationProvider.KEY_LOG_WRAP_SYSOUT))) {
+ delegatingSysErrRegistration = publishDelegatingPrintStream(delegatingSysErr, LOGGER_NAME_SYSERR_DELEGATE);
+ sysErrRegistration = publishPrintStream(this.sysErr, LOGGER_NAME_SYSERR);
+
+ System.setErr(decoratePrintStream(System.err, LOGGER_NAME_SYSERR, LoggingLevel.ERROR, stackAccessor, configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSERR));
+ } else {
+ if (delegatingSysErrRegistration != null) {
+ registrationTracker.unregister(delegatingSysErrRegistration);
+ delegatingSysErrRegistration = null;
+ }
+ if (sysErrRegistration != null) {
+ registrationTracker.unregister(sysErrRegistration);
+ sysErrRegistration = null;
+ }
+ System.setErr((PrintStream) delegatingSysErr);
}
- System.setErr((PrintStream)delegatingSysErr);
}
if (Boolean.valueOf((String)configuration.get(ConfigurationProvider.KEY_ENABLE_JUL_CONSOLE_HANDLER))) {
diff --git a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationAdminConfigurationProvider.java b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationAdminConfigurationProvider.java
index 76ef86d..a3fbdfe 100644
--- a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationAdminConfigurationProvider.java
+++ b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationAdminConfigurationProvider.java
@@ -25,6 +25,8 @@ import org.osgi.service.cm.ConfigurationListener;
public final class ConfigurationAdminConfigurationProvider implements ConfigurationProvider, ConfigurationListener {
+ private static final String TEE_MODE = ConfigurationProvider.LOG_TEE_SYSSTREAMS;
+
private static final String CONFIG_ADMIN_PID = "org.eclipse.virgo.medic";
private static final Dictionary<String, Object> DEFAULT_CONFIG = createDefaultConfiguration();
@@ -84,8 +86,15 @@ public final class ConfigurationAdminConfigurationProvider implements Configurat
private static Dictionary<String, Object> createDefaultConfiguration() {
Dictionary<String, Object> configuration = new Hashtable<String, Object>();
configuration.put(KEY_DUMP_ROOT_DIRECTORY, ".");
- configuration.put(KEY_LOG_WRAP_SYSOUT, Boolean.toString(Boolean.TRUE));
- configuration.put(KEY_LOG_WRAP_SYSERR, Boolean.toString(Boolean.TRUE));
+
+ String defaultConfiguration = System.getProperty("org.eclipse.virgo.default.log.wrapSystemStreams");
+ if (defaultConfiguration != null && !defaultConfiguration.isEmpty() && defaultConfiguration.equals(TEE_MODE)) {
+ configuration.put(KEY_LOG_WRAP_SYSOUT, TEE_MODE);
+ configuration.put(KEY_LOG_WRAP_SYSERR, TEE_MODE);
+ } else {
+ configuration.put(KEY_LOG_WRAP_SYSOUT, Boolean.toString(Boolean.TRUE));
+ configuration.put(KEY_LOG_WRAP_SYSERR, Boolean.toString(Boolean.TRUE));
+ }
return configuration;
}
diff --git a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationProvider.java b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationProvider.java
index fa0d29f..3a5bb3d 100644
--- a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationProvider.java
+++ b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/impl/config/ConfigurationProvider.java
@@ -21,6 +21,8 @@ public interface ConfigurationProvider {
public static final String KEY_LOG_WRAP_SYSERR = "log.wrapSysErr";
+ public static final String LOG_TEE_SYSSTREAMS = "tee";
+
public static final String KEY_LOG_DUMP_BUFFERSIZE = "log.dump.bufferSize";
public static final String KEY_LOG_DUMP_LEVEL = "log.dump.level";
diff --git a/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapper.java b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapper.java
new file mode 100644
index 0000000..fceac18
--- /dev/null
+++ b/org.eclipse.virgo.medic.core/src/main/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapper.java
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.medic.log.impl;
+
+import java.io.PrintStream;
+import java.util.Locale;
+
+import org.eclipse.virgo.medic.impl.config.ConfigurationProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A <code>LoggingPrintStreamWrapper</code> wraps a PrintStream instance and logs, via SLF4j,
+ * all data that is written to it.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public final class TeeLoggingPrintStreamWrapper extends PrintStream {
+
+ private static final class StringBuilderThreadLocal extends ThreadLocal<StringBuilder> {
+
+ @Override
+ public StringBuilder initialValue() {
+ return new StringBuilder();
+ }
+ }
+
+ private static final String GOGO_PACKAGE_NAME_PREFIX = "org.apache.felix.gogo";
+
+ private static final String TEE_LOGGING_PRINT_STREAM_WRAPPER_NAME = "org.eclipse.virgo.medic.log.impl.TeeLoggingPrintStreamWrapper";
+
+ private static final String LOGBACK_PACKAGE_NAME_PREFIX = "ch.qos.logback";
+
+ private final ThreadLocal<StringBuilder> entryBuilders;
+
+ private final Logger logger;
+
+ private final ExecutionStackAccessor executionStackAccessor;
+
+ private final ConfigurationProvider configurationProvider;
+
+ private final String configurationProperty;
+
+ private final PrintStream originalPrintStream;
+
+ private final LoggingLevel loggingLevel;
+
+ private static final String NULL_STRING = "null";
+
+ /**
+ * Creates a new LoggingPrintStreamWrapper for the given PrintStream. Data written to
+ * the stream is logged via SLF4j to a logger with the supplied <code>loggerName</code>.<br/>
+ * The logging level is determined by the parameter of type {@link LoggingLevel}.
+ *
+ * @param printStream The PrintStream instance to wrap
+ * @param loggerName The name of the logger
+ * @param loggingLevel The level of the log entries created
+ * @param executionStackAccessor
+ * @param configurationProvider
+ * @param configurationProperty
+ */
+ public TeeLoggingPrintStreamWrapper(PrintStream printStream, String loggerName, LoggingLevel loggingLevel, ExecutionStackAccessor executionStackAccessor, ConfigurationProvider configurationProvider, String configurationProperty) {
+ super(printStream);
+
+ this.logger = LoggerFactory.getLogger(loggerName);
+ this.loggingLevel = loggingLevel;
+
+ this.executionStackAccessor = executionStackAccessor;
+
+ this.entryBuilders = new StringBuilderThreadLocal();
+
+ this.configurationProvider = configurationProvider;
+
+ this.configurationProperty = configurationProperty;
+
+ this.originalPrintStream = printStream;
+ }
+
+ /**
+ * Creates a new LoggingPrintStreamWrapper for the given PrintStream. Data written to
+ * the stream is logged via SLF4j to a logger with the supplied <code>loggerName</code>.<br/>
+ * (The logging level is DEBUG by default.)
+ *
+ * @param printStream The PrintStream instance to wrap
+ * @param loggerName The name of the logger
+ * @param executionStackAccessor
+ * @param configurationProvider
+ * @param configurationProperty
+ */
+ public TeeLoggingPrintStreamWrapper(PrintStream printStream, String loggerName, ExecutionStackAccessor executionStackAccessor, ConfigurationProvider configurationProvider, String configurationProperty) {
+ this(printStream, loggerName, LoggingLevel.DEBUG, executionStackAccessor, configurationProvider, configurationProperty);
+ }
+
+ @Override
+ public PrintStream append(char c) {
+ super.append(c);
+ if (isLoggingEnabled()) {
+ this.internalAppend(c);
+ }
+ return this;
+ }
+
+ private boolean internalAppend(char c) {
+ if (c == '\n' || c == '\r') {
+ createEntryAndLog(entryBuilders.get());
+ return true;
+ } else {
+ entryBuilders.get().append(c);
+ return false;
+ }
+ }
+
+ @Override
+ public PrintStream append(CharSequence csq, int start, int end) {
+ super.append(csq, start, end);
+ if (isLoggingEnabled()) {
+ this.internalAppend(csq, start, end);
+ }
+
+ return this;
+ }
+
+ private void internalAppend(CharSequence csq, int start, int end) {
+ for (int i = start; i < end; i++) {
+ boolean loggedEntry = internalAppend(csq.charAt(i));
+ if (loggedEntry && i < (end - 1)) {
+ char c = csq.charAt(i + 1);
+ if (c == '\n' || c == '\r') {
+ i++;
+ }
+ }
+ }
+ }
+
+ @Override
+ public PrintStream append(CharSequence csq) {
+ super.append(csq);
+ if (isLoggingEnabled()) {
+ if(csq == null){
+ throw new NullPointerException("Character Sequence to be added to the printStream from source '" + this.logger.getName() + "' is null");
+ }
+ this.internalAppend(csq, 0, csq.length());
+ }
+ return this;
+ }
+
+ @Override
+ public boolean checkError() {
+ if (isLoggingEnabled()) {
+ return false;
+ }
+ return super.checkError();
+
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ }
+
+ @Override
+ public void flush() {
+ super.flush();
+ }
+
+ @Override
+ public PrintStream format(Locale l, String format, Object... args) {
+ super.format(l, format, args);
+ if (isLoggingEnabled()) {
+ this.internalPrint(String.format(l, format, args));
+ }
+ return this;
+ }
+
+ @Override
+ public PrintStream format(String format, Object... args) {
+ super.format(format, args);
+ if (isLoggingEnabled()) {
+ this.internalPrint(String.format(format, args));
+ }
+ return this;
+ }
+
+ @Override
+ public void print(boolean b) {
+ super.print(b);
+ if (isLoggingEnabled()) {
+ this.internalPrint(b);
+ }
+ }
+
+ private void internalPrint(boolean b) {
+ entryBuilders.get().append(b);
+ }
+
+ @Override
+ public void print(char c) {
+ super.print(c);
+ if (isLoggingEnabled()) {
+ this.internalAppend(c);
+ }
+ }
+
+ @Override
+ public void print(char[] ca) {
+ super.print(ca);
+ if (isLoggingEnabled()) {
+ this.internalPrint(ca);
+ }
+ }
+
+ private void internalPrint(char[] ca) {
+ final String s = new String(ca);
+ this.internalAppend(s, 0, s.length());
+ }
+
+ @Override
+ public void print(double d) {
+ super.print(d);
+ if (isLoggingEnabled()) {
+ this.internalPrint(d);
+ }
+ }
+
+ private void internalPrint(double d) {
+ entryBuilders.get().append(d);
+ }
+
+ @Override
+ public void print(float f) {
+ super.print(f);
+ if (isLoggingEnabled()) {
+ this.internalPrint(f);
+ }
+ }
+
+ private void internalPrint(float f) {
+ entryBuilders.get().append(f);
+ }
+
+ @Override
+ public void print(int i) {
+ super.print(i);
+ if (isLoggingEnabled()) {
+ this.internalPrint(i);
+ }
+ }
+
+ private void internalPrint(int i) {
+ entryBuilders.get().append(i);
+ }
+
+ @Override
+ public void print(long l) {
+ super.print(l);
+ if (isLoggingEnabled()) {
+ this.internalPrint(l);
+ }
+ }
+
+ private void internalPrint(long l) {
+ entryBuilders.get().append(l);
+ }
+
+ @Override
+ public void print(Object obj) {
+ super.print(obj);
+ if (isLoggingEnabled()) {
+ this.internalPrint(obj);
+ }
+ }
+
+ private void internalPrint(Object obj) {
+ if (obj == null) {
+ entryBuilders.get().append(NULL_STRING);
+ } else {
+ internalPrint(obj.toString().toCharArray());
+ }
+ }
+
+ @Override
+ public void print(String s) {
+ super.print(s);
+ if (isLoggingEnabled()) {
+ this.internalPrint(s);
+ }
+ }
+ private void internalPrint(String s) {
+ if (s == null) {
+ s = NULL_STRING;
+ }
+ this.internalAppend(s, 0, s.length());
+ }
+
+ @Override
+ public PrintStream printf(Locale l, String format, Object... args) {
+ super.printf(l, format, args);
+ if (isLoggingEnabled()) {
+ this.internalPrint(String.format(l, format, args));
+ }
+ return this;
+ }
+
+ @Override
+ public PrintStream printf(String format, Object... args) {
+ super.printf(format, args);
+ if (isLoggingEnabled()) {
+ this.internalPrint(String.format(format, args));
+ }
+ return this;
+ }
+
+ @Override
+ public void println() {
+ super.println();
+ if (isLoggingEnabled()) {
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(boolean x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(char x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalAppend(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(char[] x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(double x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(float x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(int x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(long x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(Object x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ public void println(String x) {
+ super.println(x);
+ if (isLoggingEnabled()) {
+ this.internalPrint(x);
+ createEntryAndLog(entryBuilders.get());
+ }
+ }
+
+ @Override
+ protected void setError() {
+ super.setError();
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ super.write(buf, off, len);
+ if (isLoggingEnabled()) {
+ byte[] outputBytes = new byte[len];
+ System.arraycopy(buf, off, outputBytes, 0, len);
+ this.internalPrint(new String(outputBytes));
+ }
+ }
+
+ @Override
+ public void write(int b) {
+ super.write(b);
+ if (isLoggingEnabled()) {
+ if (b == '\n' || b == '\r') {
+ createEntryAndLog(entryBuilders.get());
+ } else {
+ entryBuilders.get().append(new String(new byte[] {(byte)b}));
+ }
+ }
+ }
+
+ private void createEntryAndLog(final StringBuilder stringBuilder) {
+ final String string = stringBuilder.toString();
+ switch (this.loggingLevel) {
+ case DEBUG: this.logger.debug(string); break;
+ case ERROR: this.logger.error(string); break;
+ case INFO: this.logger.info(string); break;
+ case WARNING: this.logger.warn(string); break;
+ }
+ entryBuilders.remove();
+ }
+
+ private boolean isLoggingEnabled() {
+ return isEnabledInConfiguration() && !isWithinLogback() && !isWithinTeeOperation() && !isWithinGoGoCall();
+ }
+
+ private boolean isWithinLogback() {
+ return isWithinCallContainingPackage(LOGBACK_PACKAGE_NAME_PREFIX);
+ }
+
+ private boolean isWithinGoGoCall() {
+ return isWithinCallContainingPackage(GOGO_PACKAGE_NAME_PREFIX);
+ }
+
+ private boolean isWithinCallContainingPackage(String expectedPkg) {
+ Class<?>[] executionStack = this.executionStackAccessor.getExecutionStack();
+
+ for (Class<?> clazz : executionStack) {
+ Package pkg = clazz.getPackage();
+ if (pkg != null) {
+ String pkgName = pkg.getName();
+ if (pkgName != null && pkgName.startsWith(expectedPkg)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether the call already passed by this class and returned again here in the same stack trace. If so we
+ * don't need to log another logging event for these calls, because one was already fired.
+ *
+ * @return true if the call passed twice or more times by this class, false otherwise
+ */
+ private boolean isWithinTeeOperation() {
+ Class<?>[] executionStack = this.executionStackAccessor.getExecutionStack();
+
+ //Start from index 3 because if we come from this class for the first time
+ // there are at least three stack trace elements for this call passing:
+ // 1. PrintStream method
+ // 2. isLoggingEnabled
+ // 3. isWithinTeeOperation
+ for (int i = 3; i < executionStack.length; i++) {
+ Class<?> clazz = executionStack[i];
+ if (clazz != null) {
+ String className = clazz.getCanonicalName();
+ if (className != null && className.equals(TEE_LOGGING_PRINT_STREAM_WRAPPER_NAME)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isEnabledInConfiguration() {
+ return ConfigurationProvider.LOG_TEE_SYSSTREAMS.equals((String)this.configurationProvider.getConfiguration().get(this.configurationProperty));
+ }
+
+ public PrintStream getOriginalPrintStream() {
+ return this.originalPrintStream;
+ }
+}
diff --git a/org.eclipse.virgo.medic.core/src/test/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapperTests.java b/org.eclipse.virgo.medic.core/src/test/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapperTests.java
new file mode 100644
index 0000000..dd32a85
--- /dev/null
+++ b/org.eclipse.virgo.medic.core/src/test/java/org/eclipse/virgo/medic/log/impl/TeeLoggingPrintStreamWrapperTests.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.medic.log.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.eclipse.virgo.medic.impl.config.ConfigurationChangeListener;
+import org.eclipse.virgo.medic.impl.config.ConfigurationProvider;
+import org.eclipse.virgo.medic.log.impl.ExecutionStackAccessor;
+import org.eclipse.virgo.medic.log.impl.TeeLoggingPrintStreamWrapper;
+import org.eclipse.virgo.medic.log.impl.SecurityManagerExecutionStackAccessor;
+import org.junit.Before;
+import org.junit.Test;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.LoggingEvent;
+
+public class TeeLoggingPrintStreamWrapperTests {
+
+ private PrintStream teeWrapper;
+
+ @Test
+ public void test() {
+ produceOutput(this.teeWrapper);
+
+ List<LoggingEvent> loggingEvents = CapturingAppender.getAndResetLoggingEvents();
+ assertEquals(22, loggingEvents.size());
+
+ assertEquals("abcdefghij", loggingEvents.get(0).getMessage());
+ assertEquals("Three strings", loggingEvents.get(1).getMessage());
+ assertEquals("last one on a new line.", loggingEvents.get(2).getMessage());
+ assertEquals("3,1416", loggingEvents.get(3).getMessage());
+ assertEquals("trueklm", loggingEvents.get(4).getMessage());
+ assertEquals("a123.0456.078910toString", loggingEvents.get(5).getMessage());
+ assertEquals("abcdThree strings", loggingEvents.get(6).getMessage());
+ assertEquals("last one on a new line.", loggingEvents.get(7).getMessage());
+ assertEquals("3,1416", loggingEvents.get(8).getMessage());
+ assertEquals("false", loggingEvents.get(9).getMessage());
+ assertEquals("b", loggingEvents.get(10).getMessage());
+ assertEquals("", loggingEvents.get(11).getMessage());
+ assertEquals("", loggingEvents.get(12).getMessage());
+ assertEquals("abc", loggingEvents.get(13).getMessage());
+ assertEquals("de", loggingEvents.get(14).getMessage());
+ assertEquals("123.0", loggingEvents.get(15).getMessage());
+ assertEquals("456.0", loggingEvents.get(16).getMessage());
+ assertEquals("789", loggingEvents.get(17).getMessage());
+ assertEquals("101112", loggingEvents.get(18).getMessage());
+ assertEquals("toString", loggingEvents.get(19).getMessage());
+ assertEquals("A string with a", loggingEvents.get(20).getMessage());
+ assertEquals("new line in it.", loggingEvents.get(21).getMessage());
+ }
+
+ @Test
+ public void testOutputWithinLoggingCode() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(baos);
+ PrintStream decorator = new TeeLoggingPrintStreamWrapper(printStream, getClass().getName(), new ExecutionStackAccessor() {
+
+ public Class<?>[] getExecutionStack() {
+ return new Class[] {Logger.class};
+ }
+ }, new StubConfigurationProvider(), "theProperty");
+
+ produceOutput(decorator);
+
+ List<LoggingEvent> loggingEvents = CapturingAppender.getAndResetLoggingEvents();
+ assertEquals(0, loggingEvents.size());
+ }
+
+ private void produceOutput(PrintStream printStream) {
+ printStream.append('a');
+ printStream.append("bcd");
+ printStream.append("abcdefghij", 4, 10);
+ printStream.println();
+ printStream.format("%s %s%n%s%n", "Three", "strings", "last one on a new line.");
+ printStream.format(Locale.FRANCE, "%.4f%n", Math.PI);
+ printStream.print(true);
+ printStream.print('k');
+ printStream.print(new char[] {'l', 'm', '\r', 'a'});
+ printStream.print(123d);
+ printStream.print(456f);
+ printStream.print(7);
+ printStream.print(8910l);
+ printStream.print(new Object() {@Override public String toString() { return "toString";}});
+ printStream.append('\n');
+ printStream.print("abcd");
+ printStream.printf("%s %s%n%s%n", "Three", "strings", "last one on a new line.");
+ printStream.printf(Locale.FRANCE, "%.4f%n", Math.PI);
+ printStream.println(false);
+ printStream.println('b');
+ printStream.println('\n');
+ printStream.println(new char[] {'a', 'b', 'c', '\n', 'd', 'e'});
+ printStream.println(123d);
+ printStream.println(456f);
+ printStream.println(789);
+ printStream.println(101112l);
+ printStream.println(new Object() {@Override public String toString() { return "toString";}});
+ printStream.println("A string with a\nnew line in it.");
+ }
+
+ @Test
+ public void testByteArrayHandling() {
+ String string = "Some text to be turned into bytes.";
+ String stringWithNewLine = string + "\n";
+ byte[] stringBytes = stringWithNewLine.getBytes();
+
+ teeWrapper.write(stringBytes, 0, stringBytes.length);
+
+ List<LoggingEvent> loggingEvents = CapturingAppender.getAndResetLoggingEvents();
+ assertEquals(1, loggingEvents.size());
+
+ assertEquals("Some text to be turned into bytes.", loggingEvents.get(0).getMessage());
+ }
+
+ @Test
+ public void testSingleByteHandling() {
+ String string = "Some text to be turned into bytes.";
+ byte[] stringBytes = string.getBytes();
+
+ for (byte b: stringBytes) {
+ teeWrapper.write(b);
+ }
+ teeWrapper.println();
+
+ List<LoggingEvent> loggingEvents = CapturingAppender.getAndResetLoggingEvents();
+ assertEquals(1, loggingEvents.size());
+
+ assertEquals("Some text to be turned into bytes.", loggingEvents.get(0).getMessage());
+ }
+
+ @Test
+ public void testPrintNullString(){
+
+ String imNull = null;
+
+ teeWrapper.println(imNull);
+ teeWrapper.print(imNull);
+ teeWrapper.println();
+
+ List<LoggingEvent> loggingEvents = CapturingAppender.getAndResetLoggingEvents();
+ assertEquals(2, loggingEvents.size());
+
+ assertEquals("null", loggingEvents.get(0).getMessage());
+ assertEquals("null", loggingEvents.get(1).getMessage());
+ }
+
+ @Before
+ public void createDecorator() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(baos);
+ this.teeWrapper = new TeeLoggingPrintStreamWrapper(printStream, getClass().getName(), new SecurityManagerExecutionStackAccessor(), new StubConfigurationProvider(), "theProperty");
+ }
+
+ private final class StubConfigurationProvider implements ConfigurationProvider {
+
+ private final Properties configuration;
+
+ private StubConfigurationProvider() {
+ this.configuration = new Properties();
+ this.configuration.setProperty("theProperty", "tee");
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary getConfiguration() {
+ return this.configuration;
+ }
+
+ public void addChangeListener(ConfigurationChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean removeChangeListener(ConfigurationChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+}
diff --git a/org.eclipse.virgo.medic.core/src/test/resources/logback-test.xml b/org.eclipse.virgo.medic.core/src/test/resources/logback-test.xml
index ce1fca9..4dedac1 100644
--- a/org.eclipse.virgo.medic.core/src/test/resources/logback-test.xml
+++ b/org.eclipse.virgo.medic.core/src/test/resources/logback-test.xml
@@ -47,4 +47,13 @@
<appender-ref ref="STDOUT"/>
</logger>
+ <logger name="org.eclipse.virgo.medic.log.impl.TeeLoggingPrintStreamWrapperTests">
+ <appender name="capturingAppender" class="org.eclipse.virgo.medic.log.impl.CapturingAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <Pattern>%msg</Pattern>
+ </encoder>
+ </appender>
+ <appender-ref ref="STDOUT"/>
+ </logger>
+
</configuration>

Back to the top