001/** 002 * 003 * Copyright © 2015 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jxmpp.xml.splitter; 018 019import java.io.IOException; 020import java.util.Collections; 021import java.util.Map; 022 023/** 024 * A XML splitter for XMPP. Unlike {@link XmlSplitter}, this splitter is aware 025 * of the special semantics of XMPP's {@code <stream:stream>} element. 026 */ 027public class XmppXmlSplitter extends XmlSplitter { 028 029 private final XmppElementCallback xmppElementCallback; 030 private final int maxElementSize; 031 032 private String streamPrefix; 033 034 /** 035 * Construct a new XMPP XML splitter with a max element size of 10000. 036 * <p> 037 * RFC 6120 § 13.12 4. requires XMPP servers to use nothing less then 10000 as maximum stanza size. 038 * </p> 039 * @param xmppElementCallback the callback invoked once a complete element has been processed. 040 */ 041 public XmppXmlSplitter(XmppElementCallback xmppElementCallback) { 042 this(10000, xmppElementCallback); 043 } 044 045 /** 046 * Construct a new XMPP XML splitter with a max element size of 10000. 047 * <p> 048 * RFC 6120 § 13.12 4. requires XMPP servers to use nothing less then 10000 as maximum stanza size. 049 * </p> 050 * @param xmppElementCallback the callback invoked once a complete element has been processed. 051 * @param declarationCallback a optional callback for XML Declarations. 052 * @param processingInstructionCallback a optional callback for XML Processing Instructions. 053 */ 054 public XmppXmlSplitter(XmppElementCallback xmppElementCallback, DeclarationCallback declarationCallback, 055 ProcessingInstructionCallback processingInstructionCallback) { 056 this(10000, xmppElementCallback, declarationCallback, processingInstructionCallback); 057 } 058 059 /** 060 * Construct a new XMPP XML splitter. 061 * 062 * @param maxElementSize the maximum size of a single top level element in bytes. 063 * @param xmppElementCallback the callback invoked once a complete element has been processed. 064 */ 065 public XmppXmlSplitter(int maxElementSize, XmppElementCallback xmppElementCallback) { 066 this(maxElementSize, xmppElementCallback, null, null); 067 } 068 069 /** 070 * Construct a new XMPP XML splitter. 071 * 072 * @param maxElementSize the maximum size of a single top level element in bytes. 073 * @param xmppElementCallback the callback invoked once a complete element has been processed. 074 * @param declarationCallback a optional callback for XML Declarations. 075 * @param processingInstructionCallback a optional callback for XML Processing Instructions. 076 */ 077 public XmppXmlSplitter(int maxElementSize, XmppElementCallback xmppElementCallback, DeclarationCallback declarationCallback, 078 ProcessingInstructionCallback processingInstructionCallback) { 079 super(maxElementSize, xmppElementCallback, declarationCallback, processingInstructionCallback); 080 this.maxElementSize = maxElementSize; 081 this.xmppElementCallback = xmppElementCallback; 082 } 083 084 @Override 085 protected void onNextChar() throws IOException { 086 if (getCurrentSplittedPartSize() >= maxElementSize) { 087 throw new IOException("Max element size exceeded"); 088 } 089 } 090 091 @Override 092 protected void onStartTag(String prefix, String localpart, Map<String, String> attributes) { 093 if (!"stream".equals(localpart)) { 094 // If the open tag's name is not 'stream' then we are not interested. 095 return; 096 } 097 098 if ("http://etherx.jabber.org/streams".equals(attributes.get("xmlns:" + prefix))) { 099 streamPrefix = prefix; 100 newSplittedPart(); 101 xmppElementCallback.streamOpened(prefix, Collections.unmodifiableMap(attributes)); 102 } 103 } 104 105 @Override 106 protected void onEndTag(String qName) { 107 if (streamPrefix == null || !qName.startsWith(streamPrefix)) { 108 // Shortcut if streamPrefix is not yet set or if qName does not even 109 // start with it. 110 return; 111 } 112 113 if ((streamPrefix + ":stream").equals(qName)) { 114 xmppElementCallback.streamClosed(); 115 } 116 } 117}