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