diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/LazyStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/LazyStringBuilder.java index d7df08411..638ada32d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/LazyStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/LazyStringBuilder.java @@ -17,6 +17,7 @@ package org.jivesoftware.smack.util; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class LazyStringBuilder implements Appendable, CharSequence { @@ -105,4 +106,17 @@ public class LazyStringBuilder implements Appendable, CharSequence { } return cache; } + + /** + * Get the List of CharSequences representation of this instance. The list is unmodifiable. If + * the resulting String was already cached, a list with a single String entry will be returned. + * + * @return a List of CharSequences representing this instance. + */ + public List getAsList() { + if (cache != null) { + return Collections.singletonList((CharSequence) cache); + } + return Collections.unmodifiableList(list); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ObservableWriter.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ObservableWriter.java index 600e10247..edb8aa1eb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/ObservableWriter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ObservableWriter.java @@ -28,9 +28,11 @@ import java.util.List; * @author Gaston Dombiak */ public class ObservableWriter extends Writer { + private static final int MAX_STRING_BUILDER_SIZE = 4096; Writer wrappedWriter = null; List listeners = new ArrayList(); + private final StringBuilder stringBuilder = new StringBuilder(MAX_STRING_BUILDER_SIZE); public ObservableWriter(Writer wrappedWriter) { this.wrappedWriter = wrappedWriter; @@ -39,10 +41,11 @@ public class ObservableWriter extends Writer { public void write(char[] cbuf, int off, int len) throws IOException { wrappedWriter.write(cbuf, off, len); String str = new String(cbuf, off, len); - notifyListeners(str); + maybeNotifyListeners(str); } public void flush() throws IOException { + notifyListeners(); wrappedWriter.flush(); } @@ -57,18 +60,25 @@ public class ObservableWriter extends Writer { public void write(char[] cbuf) throws IOException { wrappedWriter.write(cbuf); String str = new String(cbuf); - notifyListeners(str); + maybeNotifyListeners(str); } public void write(String str) throws IOException { wrappedWriter.write(str); - notifyListeners(str); + maybeNotifyListeners(str); } public void write(String str, int off, int len) throws IOException { wrappedWriter.write(str, off, len); str = str.substring(off, off + len); - notifyListeners(str); + maybeNotifyListeners(str); + } + + private void maybeNotifyListeners(String s) { + stringBuilder.append(s); + if (stringBuilder.length() > MAX_STRING_BUILDER_SIZE) { + notifyListeners(); + } } /** @@ -76,12 +86,14 @@ public class ObservableWriter extends Writer { * * @param str the written String to notify */ - private void notifyListeners(String str) { + private void notifyListeners() { WriterListener[] writerListeners = null; synchronized (listeners) { writerListeners = new WriterListener[listeners.size()]; listeners.toArray(writerListeners); } + String str = stringBuilder.toString(); + stringBuilder.setLength(0); for (int i = 0; i < writerListeners.length; i++) { writerListeners[i].write(str); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java index 15b895908..538fb7106 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smack.util; +import java.io.IOException; +import java.io.Writer; import java.util.Collection; import java.util.Date; @@ -459,4 +461,23 @@ public class XmlStringBuilder implements Appendable, CharSequence { public int hashCode() { return toString().hashCode(); } + + /** + * Write the contents of this XmlStringBuilder to a {@link Writer}. This will write + * the single parts one-by-one, avoiding allocation of a big continuous memory block holding the + * XmlStringBuilder contents. + * + * @param writer + * @throws IOException + */ + public void write(Writer writer) throws IOException { + for (CharSequence csq : sb.getAsList()) { + if (csq instanceof XmlStringBuilder) { + ((XmlStringBuilder) csq).write(writer); + } + else { + writer.write(csq.toString()); + } + } + } } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 4d5971ffb..70f02f364 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -73,6 +73,7 @@ import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.TLSUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.dns.HostAddress; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; @@ -1316,7 +1317,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { throw new IllegalStateException(e); } } - writer.write(element.toXML().toString()); + + CharSequence elementXml = element.toXML(); + if (elementXml instanceof XmlStringBuilder) { + ((XmlStringBuilder) elementXml).write(writer); + } + else { + writer.write(elementXml.toString()); + } + if (queue.isEmpty()) { writer.flush(); }