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