001/** 002 * 003 * Copyright © 2014-2021 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.jivesoftware.smack.sm.packet; 018 019import java.util.Collections; 020import java.util.List; 021 022import javax.xml.namespace.QName; 023 024import org.jivesoftware.smack.packet.ExtensionElement; 025import org.jivesoftware.smack.packet.Nonza; 026import org.jivesoftware.smack.packet.StanzaError; 027import org.jivesoftware.smack.packet.StanzaErrorTextElement; 028import org.jivesoftware.smack.util.XmlStringBuilder; 029 030public class StreamManagement { 031 public static final String NAMESPACE = "urn:xmpp:sm:3"; 032 033 public static final class StreamManagementFeature implements ExtensionElement { 034 035 public static final String ELEMENT = "sm"; 036 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 037 038 public static final StreamManagementFeature INSTANCE = new StreamManagementFeature(); 039 040 private StreamManagementFeature() { 041 } 042 043 @Override 044 public String getElementName() { 045 return QNAME.getLocalPart(); 046 } 047 048 @Override 049 public String getNamespace() { 050 return QNAME.getNamespaceURI(); 051 } 052 053 @Override 054 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 055 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 056 xml.closeEmptyElement(); 057 return xml; 058 } 059 } 060 061 private abstract static class AbstractEnable implements Nonza { 062 063 /** 064 * Preferred maximum resumption time in seconds (optional). 065 */ 066 protected int max = -1; 067 068 protected boolean resume = false; 069 070 protected void maybeAddResumeAttributeTo(XmlStringBuilder xml) { 071 if (resume) { 072 // XEP 198 never mentions the case where resume='false', it's either set to true or 073 // not set at all. We reflect this in this code part 074 xml.attribute("resume", "true"); 075 } 076 } 077 078 protected void maybeAddMaxAttributeTo(XmlStringBuilder xml) { 079 if (max > 0) { 080 xml.attribute("max", Integer.toString(max)); 081 } 082 } 083 084 public boolean isResumeSet() { 085 return resume; 086 } 087 088 /** 089 * Return the max resumption time in seconds. 090 * @return the max resumption time in seconds 091 */ 092 public int getMaxResumptionTime() { 093 return max; 094 } 095 096 @Override 097 public final String getNamespace() { 098 return NAMESPACE; 099 } 100 } 101 102 public static class Enable extends AbstractEnable { 103 public static final String ELEMENT = "enable"; 104 105 public static final Enable INSTANCE = new Enable(); 106 107 private Enable() { 108 } 109 110 public Enable(boolean resume) { 111 this.resume = resume; 112 } 113 114 public Enable(boolean resume, int max) { 115 this(resume); 116 this.max = max; 117 } 118 119 @Override 120 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 121 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 122 maybeAddResumeAttributeTo(xml); 123 maybeAddMaxAttributeTo(xml); 124 xml.closeEmptyElement(); 125 return xml; 126 } 127 128 @Override 129 public String getElementName() { 130 return ELEMENT; 131 } 132 } 133 134 /** 135 * A Stream Management 'enabled' element. 136 * <p> 137 * Here is a full example, all attributes besides 'xmlns' are optional. 138 * </p> 139 * <pre> 140 * {@code 141 * <enabled xmlns='urn:xmpp:sm:3' 142 * id='some-long-sm-id' 143 * location='[2001:41D0:1:A49b::1]:9222' 144 * resume='true'/> 145 * } 146 * </pre> 147 */ 148 public static class Enabled extends AbstractEnable { 149 public static final String ELEMENT = "enabled"; 150 151 /** 152 * The stream id ("SM-ID") 153 */ 154 private final String id; 155 156 /** 157 * The location where the server prefers reconnection. 158 */ 159 private final String location; 160 161 public Enabled(String id, boolean resume) { 162 this(id, resume, null, -1); 163 } 164 165 public Enabled(String id, boolean resume, String location, int max) { 166 this.id = id; 167 this.resume = resume; 168 this.location = location; 169 this.max = max; 170 } 171 172 public String getId() { 173 return id; 174 } 175 176 public String getLocation() { 177 return location; 178 } 179 180 @Override 181 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 182 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 183 xml.optAttribute("id", id); 184 maybeAddResumeAttributeTo(xml); 185 xml.optAttribute("location", location); 186 maybeAddMaxAttributeTo(xml); 187 xml.closeEmptyElement(); 188 return xml; 189 } 190 191 @Override 192 public String getElementName() { 193 return ELEMENT; 194 } 195 } 196 197 public static class Failed implements Nonza { 198 public static final String ELEMENT = "failed"; 199 200 private final StanzaError.Condition condition; 201 202 private final List<StanzaErrorTextElement> textElements; 203 204 205 public Failed() { 206 this(null, null); 207 } 208 209 public Failed(StanzaError.Condition condition, List<StanzaErrorTextElement> textElements) { 210 this.condition = condition; 211 if (textElements == null) { 212 this.textElements = Collections.emptyList(); 213 } else { 214 this.textElements = Collections.unmodifiableList(textElements); 215 } 216 } 217 218 public StanzaError.Condition getStanzaErrorCondition() { 219 return condition; 220 } 221 222 public List<StanzaErrorTextElement> getTextElements() { 223 return textElements; 224 } 225 226 @Override 227 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 228 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 229 if (condition == null && textElements.isEmpty()) { 230 xml.closeEmptyElement(); 231 } else { 232 if (condition != null) { 233 xml.rightAngleBracket(); 234 xml.append(condition.toString()); 235 xml.xmlnsAttribute(StanzaError.ERROR_CONDITION_AND_TEXT_NAMESPACE); 236 xml.closeEmptyElement(); 237 } 238 xml.append(textElements); 239 xml.closeElement(ELEMENT); 240 } 241 return xml; 242 } 243 244 @Override 245 public String getNamespace() { 246 return NAMESPACE; 247 } 248 249 @Override 250 public String getElementName() { 251 return ELEMENT; 252 } 253 254 } 255 256 private abstract static class AbstractResume implements Nonza { 257 258 private final long handledCount; 259 private final String previd; 260 261 private AbstractResume(long handledCount, String previd) { 262 this.handledCount = handledCount; 263 this.previd = previd; 264 } 265 266 public long getHandledCount() { 267 return handledCount; 268 } 269 270 public String getPrevId() { 271 return previd; 272 } 273 274 @Override 275 public final String getNamespace() { 276 return NAMESPACE; 277 } 278 279 @Override 280 public final XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 281 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 282 xml.attribute("h", Long.toString(handledCount)); 283 xml.attribute("previd", previd); 284 xml.closeEmptyElement(); 285 return xml; 286 } 287 } 288 289 public static class Resume extends AbstractResume { 290 public static final String ELEMENT = "resume"; 291 292 public Resume(long handledCount, String previd) { 293 super(handledCount, previd); 294 } 295 296 @Override 297 public String getElementName() { 298 return ELEMENT; 299 } 300 } 301 302 public static class Resumed extends AbstractResume { 303 public static final String ELEMENT = "resumed"; 304 305 public Resumed(long handledCount, String previd) { 306 super(handledCount, previd); 307 } 308 309 @Override 310 public String getElementName() { 311 return ELEMENT; 312 } 313 } 314 315 public static class AckAnswer implements Nonza { 316 public static final String ELEMENT = "a"; 317 318 private final long handledCount; 319 320 public AckAnswer(long handledCount) { 321 this.handledCount = handledCount; 322 } 323 324 public long getHandledCount() { 325 return handledCount; 326 } 327 328 @Override 329 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 330 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 331 xml.attribute("h", Long.toString(handledCount)); 332 xml.closeEmptyElement(); 333 return xml; 334 } 335 336 @Override 337 public String getNamespace() { 338 return NAMESPACE; 339 } 340 341 @Override 342 public String getElementName() { 343 return ELEMENT; 344 } 345 } 346 347 public static final class AckRequest implements Nonza { 348 public static final String ELEMENT = "r"; 349 public static final AckRequest INSTANCE = new AckRequest(); 350 351 private AckRequest() { 352 } 353 354 @Override 355 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 356 return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>"; 357 } 358 359 @Override 360 public String getNamespace() { 361 return NAMESPACE; 362 } 363 364 @Override 365 public String getElementName() { 366 return ELEMENT; 367 } 368 } 369}