This is an automated email from the ASF dual-hosted git repository. duncangrant pushed a commit to branch prettyPrintWinrmXml in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
commit 4b59a9ee17f971bdcca0f5036bb3b21234f3d9aa Author: Duncan Grant <duncan.gr...@cloudsoft.io> AuthorDate: Mon Jan 18 16:36:05 2021 +0000 Now pretty prints XML Fails if stream breaks mid tag - I don't expect this to happen --- .../internal/winrm/winrm4j/PrettyXmlWriter.java | 96 +++++++++++++++ .../core/internal/winrm/winrm4j/Winrm4jTool.java | 7 +- .../winrm/winrm4j/PrettyXmlWriterTest.java | 136 +++++++++++++++++++++ 3 files changed, 234 insertions(+), 5 deletions(-) diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriter.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriter.java new file mode 100644 index 0000000..4c26646 --- /dev/null +++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriter.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.util.core.internal.winrm.winrm4j; + +import java.io.IOException; +import java.io.Writer; + +public class PrettyXmlWriter extends Writer { + private Writer wrappedWriter; + private boolean tagClosed = Boolean.FALSE; + private int indentLevel = 0; + private boolean newLine = true; + private char lastChar = '\n'; + + public PrettyXmlWriter(Writer writer) { + super(writer); + wrappedWriter = writer; + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + for (int i = off; i < off + len; i++) { + char c = cbuf[i]; + if (c == '<') { + lastChar = '<'; + if(!newLine) { + indentLevel--; + } else if (i + 1 < off + len && cbuf[i + 1] == '/') { + indentLevel--; + printIndent(); + indentLevel--; + } else { + printIndent(); + } + } + writeChar(c); + if ('>' == c || tagClosed) { + if (i + 1 < off + len) { + if (cbuf[i + 1] == '<') { + writeNewLine(); + if (lastChar != '/') { + indentLevel++; + } + } + } else { + tagClosed = true; + } + } else { + tagClosed = false; + } + lastChar = c; + } + } + + private void writeNewLine() throws IOException { + wrappedWriter.write('\n'); + newLine = true; + } + + private void writeChar(char c) throws IOException { + wrappedWriter.write(c); + newLine = false; + } + + private void printIndent() throws IOException { + for (int j = 0; j < indentLevel; j++) { + wrappedWriter.write('\t'); + } + } + + @Override + public void flush() throws IOException { + wrappedWriter.flush(); + } + + @Override + public void close() throws IOException { + wrappedWriter.close(); + } +} diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java index 0ce9bc5..5cb7824 100644 --- a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java +++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java @@ -30,14 +30,11 @@ import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.config.Sanitizer; -import org.apache.brooklyn.core.internal.BrooklynProperties; import org.apache.brooklyn.core.mgmt.ManagementContextInjectable; -import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.internal.ssh.ShellTool; import org.apache.brooklyn.util.core.internal.winrm.WinRmException; import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.javalang.Threads; import org.apache.brooklyn.util.text.Strings; import org.apache.brooklyn.util.time.Duration; import org.apache.brooklyn.util.time.Time; @@ -119,7 +116,7 @@ public class Winrm4jTool implements org.apache.brooklyn.util.core.internal.winrm OutputStream outputStream = bag.get(ShellTool.PROP_OUT_STREAM); OutputStream errorStream = bag.get(ShellTool.PROP_ERR_STREAM); Writer out = outputStream != null ? new BufferedWriter(new OutputStreamWriter(outputStream)): new StringWriter(); - Writer err = errorStream != null ? new BufferedWriter(new OutputStreamWriter(errorStream)): new StringWriter(); + Writer err = errorStream != null ? new BufferedWriter(new PrettyXmlWriter(new OutputStreamWriter(errorStream))): new StringWriter(); return tool.executeCommand(commands, out, err); }); } @@ -136,7 +133,7 @@ public class Winrm4jTool implements org.apache.brooklyn.util.core.internal.winrm OutputStream outputStream = bag.get(ShellTool.PROP_OUT_STREAM); OutputStream errorStream = bag.get(ShellTool.PROP_ERR_STREAM); Writer out = outputStream != null ? new BufferedWriter(new OutputStreamWriter(outputStream)): new StringWriter(); - Writer err = errorStream != null ? new BufferedWriter(new OutputStreamWriter(errorStream)): new StringWriter(); + Writer err = errorStream != null ? new BufferedWriter(new PrettyXmlWriter(new OutputStreamWriter(errorStream))): new StringWriter(); return tool.executePs(commands, out, err); }); } diff --git a/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriterTest.java b/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriterTest.java new file mode 100644 index 0000000..7e28e35 --- /dev/null +++ b/software/winrm/src/test/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/PrettyXmlWriterTest.java @@ -0,0 +1,136 @@ +package org.apache.brooklyn.util.core.internal.winrm.winrm4j; + +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; + +import static org.testng.Assert.*; + +public class PrettyXmlWriterTest { + + public static final String XML = "<?xml version=\"1.0\" ?><Objs xmlns=\"http://schemas.microsoft.com/powershell/2004/04\" Version=\"1.1.0.1\"><Obj S=\"progress\" RefId=\"0\"><TN RefId=\"0\"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N=\"SourceId\">1</I64><PR N=\"Record\"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil/><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD/></PR></MS></Obj></Objs>"; + private RecordingWriter writer; + private PrettyXmlWriter prettyXmlWriter; + + @BeforeMethod + public void setUp() { + writer = new RecordingWriter(); + prettyXmlWriter = new PrettyXmlWriter(writer); + } + + @Test + public void testInsertNewLines() throws IOException { + prettyXmlWriter.write(XML, 0, XML.length()); + + int newLines = countNewLines(); + assertEquals(newLines,21); + } + + @Test + public void testNoNewLineIfTagSurroundsText() throws IOException { + String xml = "<tag>fdskljdfsljkfsd</tag>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + int newLines = countNewLines(); + assertEquals(newLines,0); + } + + @Test + public void testNewLineIfPartialString() throws IOException { + String xmlPart1 = "<t1><t2>fdskljfds</t2>"; + String xmlPart2 = "<t3>djskdsjk</t3></t1>"; + + prettyXmlWriter.write(xmlPart1, 0, xmlPart1.length()); + prettyXmlWriter.write(xmlPart2, 0, xmlPart2.length()); + + int newLines = countNewLines(); + assertEquals(newLines,3); + } + + @Test + public void testIndentOnNewLine() throws IOException { + String xml = "<t1><t2>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>"); + } + + @Test + public void testIncreaseIndentOnEachOpenTag() throws IOException { + String xml = "<t1><t2><t3>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>\n\t\t<t3>"); + } + + @Test + public void testOutdentBeforeClosingTag() throws IOException { + String xml = "<t1><t2></t2>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>\n\t</t2>"); + } + + @Test + public void testDontIndentAfterClosingTag() throws IOException { + String xml = "<t1><t2></t2></t1>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>\n\t</t2>\n</t1>"); + } + + @Test + public void testDontIndentWithoutNewLine() throws IOException { + String xml = "<t1><t2><t3>Some Text</t3></t2></t1>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>\n\t\t<t3>Some Text</t3>\n\t</t2>\n</t1>"); + } + + @Test + public void testDontIndentSelfClosingTag() throws IOException { + String xml = "<t1><t2><t3/></t2></t1>"; + prettyXmlWriter.write(xml, 0, xml.length()); + + assertEquals(writer.out.toString(), "<t1>\n\t<t2>\n\t\t<t3/>\n\t</t2>\n</t1>"); + } + + private int countNewLines() { + int newLines = 0; + String s = writer.out.toString(); + for (char c : s.toCharArray()) { + if(c=='\n') newLines++; + } + return newLines; + } + + static class RecordingWriter extends Writer { + + StringBuffer out = new StringBuffer(); + + @Override + public void write(int c) throws IOException { + out.append((char) c); + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + out.append(Arrays.copyOfRange(cbuf, off, off + len)); + } + + @Override + public void flush() throws IOException { + + } + + @Override + public void close() throws IOException { + + } + } +} \ No newline at end of file