BOSHClientConfig.java revision d7955ce24d294fb2014c59d11fca184471056f44
1/*
2 * Copyright 2009 Mike Cumings
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.kenai.jbosh;
18
19import java.net.URI;
20import javax.net.ssl.SSLContext;
21
22/**
23 * BOSH client configuration information.  Instances of this class contain
24 * all information necessary to establish connectivity with a remote
25 * connection manager.
26 * <p/>
27 * Instances of this class are immutable, thread-safe,
28 * and can be re-used to configure multiple client session instances.
29 */
30public final class BOSHClientConfig {
31
32    /**
33     * Connection manager URI.
34     */
35    private final URI uri;
36
37    /**
38     * Target domain.
39     */
40    private final String to;
41
42    /**
43     * Client ID of this station.
44     */
45    private final String from;
46
47    /**
48     * Default XML language.
49     */
50    private final String lang;
51
52    /**
53     * Routing information for messages sent to CM.
54     */
55    private final String route;
56
57    /**
58     * Proxy host.
59     */
60    private final String proxyHost;
61
62    /**
63     * Proxy port.
64     */
65    private final int proxyPort;
66
67    /**
68     * SSL context.
69     */
70    private final SSLContext sslContext;
71
72    /**
73     * Flag indicating that compression should be attempted, if possible.
74     */
75    private final boolean compressionEnabled;
76
77    ///////////////////////////////////////////////////////////////////////////
78    // Classes:
79
80    /**
81     * Class instance builder, after the builder pattern.  This allows each
82     * {@code BOSHClientConfig} instance to be immutable while providing
83     * flexibility when building new {@code BOSHClientConfig} instances.
84     * <p/>
85     * Instances of this class are <b>not</b> thread-safe.  If template-style
86     * use is desired, see the {@code create(BOSHClientConfig)} method.
87     */
88    public static final class Builder {
89        // Required args
90        private final URI bURI;
91        private final String bDomain;
92
93        // Optional args
94        private String bFrom;
95        private String bLang;
96        private String bRoute;
97        private String bProxyHost;
98        private int bProxyPort;
99        private SSLContext bSSLContext;
100        private Boolean bCompression;
101
102        /**
103         * Creates a new builder instance, used to create instances of the
104         * {@code BOSHClientConfig} class.
105         *
106         * @param cmURI URI to use to contact the connection manager
107         * @param domain target domain to communicate with
108         */
109        private Builder(final URI cmURI, final String domain) {
110            bURI = cmURI;
111            bDomain = domain;
112        }
113
114        /**
115         * Creates a new builder instance, used to create instances of the
116         * {@code BOSHClientConfig} class.
117         *
118         * @param cmURI URI to use to contact the connection manager
119         * @param domain target domain to communicate with
120         * @return builder instance
121         */
122        public static Builder create(final URI cmURI, final String domain) {
123            if (cmURI == null) {
124                throw(new IllegalArgumentException(
125                        "Connection manager URI must not be null"));
126            }
127            if (domain == null) {
128                throw(new IllegalArgumentException(
129                        "Target domain must not be null"));
130            }
131            String scheme = cmURI.getScheme();
132            if (!("http".equals(scheme) || "https".equals(scheme))) {
133                throw(new IllegalArgumentException(
134                        "Only 'http' and 'https' URI are allowed"));
135            }
136            return new Builder(cmURI, domain);
137        }
138
139        /**
140         * Creates a new builder instance using the existing configuration
141         * provided as a starting point.
142         *
143         * @param cfg configuration to copy
144         * @return builder instance
145         */
146        public static Builder create(final BOSHClientConfig cfg) {
147            Builder result = new Builder(cfg.getURI(), cfg.getTo());
148            result.bFrom = cfg.getFrom();
149            result.bLang = cfg.getLang();
150            result.bRoute = cfg.getRoute();
151            result.bProxyHost = cfg.getProxyHost();
152            result.bProxyPort = cfg.getProxyPort();
153            result.bSSLContext = cfg.getSSLContext();
154            result.bCompression = cfg.isCompressionEnabled();
155            return result;
156        }
157
158        /**
159         * Set the ID of the client station, to be forwarded to the connection
160         * manager when new sessions are created.
161         *
162         * @param id client ID
163         * @return builder instance
164         */
165        public Builder setFrom(final String id) {
166            if (id == null) {
167                throw(new IllegalArgumentException(
168                        "Client ID must not be null"));
169            }
170            bFrom = id;
171            return this;
172        }
173
174        /**
175         * Set the default language of any human-readable content within the
176         * XML.
177         *
178         * @param lang XML language ID
179         * @return builder instance
180         */
181        public Builder setXMLLang(final String lang) {
182            if (lang == null) {
183                throw(new IllegalArgumentException(
184                        "Default language ID must not be null"));
185            }
186            bLang = lang;
187            return this;
188        }
189
190        /**
191         * Sets the destination server/domain that the client should connect to.
192         * Connection managers may be configured to enable sessions with more
193         * that one server in different domains.  When requesting a session with
194         * such a "proxy" connection manager, a client should use this method to
195         * specify the server with which it wants to communicate.
196         *
197         * @param protocol connection protocol (e.g, "xmpp")
198         * @param host host or domain to be served by the remote server.  Note
199         *  that this is not necessarily the host name or domain name of the
200         *  remote server.
201         * @param port port number of the remote server
202         * @return builder instance
203         */
204        public Builder setRoute(
205                final String protocol,
206                final String host,
207                final int port) {
208            if (protocol == null) {
209                throw(new IllegalArgumentException("Protocol cannot be null"));
210            }
211            if (protocol.contains(":")) {
212                throw(new IllegalArgumentException(
213                        "Protocol cannot contain the ':' character"));
214            }
215            if (host == null) {
216                throw(new IllegalArgumentException("Host cannot be null"));
217            }
218            if (host.contains(":")) {
219                throw(new IllegalArgumentException(
220                        "Host cannot contain the ':' character"));
221            }
222            if (port <= 0) {
223                throw(new IllegalArgumentException("Port number must be > 0"));
224            }
225            bRoute = protocol + ":" + host + ":" + port;
226            return this;
227        }
228
229        /**
230         * Specify the hostname and port of an HTTP proxy to connect through.
231         *
232         * @param hostName proxy hostname
233         * @param port proxy port number
234         * @return builder instance
235         */
236        public Builder setProxy(final String hostName, final int port) {
237            if (hostName == null || hostName.length() == 0) {
238                throw(new IllegalArgumentException(
239                        "Proxy host name cannot be null or empty"));
240            }
241            if (port <= 0) {
242                throw(new IllegalArgumentException(
243                        "Proxy port must be > 0"));
244            }
245            bProxyHost = hostName;
246            bProxyPort = port;
247            return this;
248        }
249
250        /**
251         * Set the SSL context to use for this session.  This can be used
252         * to configure certificate-based authentication, etc..
253         *
254         * @param ctx SSL context
255         * @return builder instance
256         */
257        public Builder setSSLContext(final SSLContext ctx) {
258            if (ctx == null) {
259                throw(new IllegalArgumentException(
260                        "SSL context cannot be null"));
261            }
262            bSSLContext = ctx;
263            return this;
264        }
265
266        /**
267         * Set whether or not compression of the underlying data stream
268         * should be attempted.  By default, compression is disabled.
269         *
270         * @param enabled set to {@code true} if compression should be
271         *  attempted when possible, {@code false} to disable compression
272         * @return builder instance
273         */
274        public Builder setCompressionEnabled(final boolean enabled) {
275            bCompression = Boolean.valueOf(enabled);
276            return this;
277        }
278
279        /**
280         * Build the immutable object instance with the current configuration.
281         *
282         * @return BOSHClientConfig instance
283         */
284        public BOSHClientConfig build() {
285            // Default XML language
286            String lang;
287            if (bLang == null) {
288                lang = "en";
289            } else {
290                lang = bLang;
291            }
292
293            // Default proxy port
294            int port;
295            if (bProxyHost == null) {
296                port = 0;
297            } else {
298                port = bProxyPort;
299            }
300
301            // Default compression
302            boolean compression;
303            if (bCompression == null) {
304                compression = false;
305            } else {
306                compression = bCompression.booleanValue();
307            }
308
309            return new BOSHClientConfig(
310                    bURI,
311                    bDomain,
312                    bFrom,
313                    lang,
314                    bRoute,
315                    bProxyHost,
316                    port,
317                    bSSLContext,
318                    compression);
319        }
320
321    }
322
323    ///////////////////////////////////////////////////////////////////////////
324    // Constructor:
325
326    /**
327     * Prevent direct construction.
328     *
329     * @param cURI URI of the connection manager to connect to
330     * @param cDomain the target domain of the first stream
331     * @param cFrom client ID
332     * @param cLang default XML language
333     * @param cRoute target route
334     * @param cProxyHost proxy host
335     * @param cProxyPort proxy port
336     * @param cSSLContext SSL context
337     * @param cCompression compression enabled flag
338     */
339    private BOSHClientConfig(
340            final URI cURI,
341            final String cDomain,
342            final String cFrom,
343            final String cLang,
344            final String cRoute,
345            final String cProxyHost,
346            final int cProxyPort,
347            final SSLContext cSSLContext,
348            final boolean cCompression) {
349        uri = cURI;
350        to = cDomain;
351        from = cFrom;
352        lang = cLang;
353        route = cRoute;
354        proxyHost = cProxyHost;
355        proxyPort = cProxyPort;
356        sslContext = cSSLContext;
357        compressionEnabled = cCompression;
358    }
359
360    /**
361     * Get the URI to use to contact the connection manager.
362     *
363     * @return connection manager URI.
364     */
365    public URI getURI() {
366        return uri;
367    }
368
369    /**
370     * Get the ID of the target domain.
371     *
372     * @return domain id
373     */
374    public String getTo() {
375        return to;
376    }
377
378    /**
379     * Get the ID of the local client.
380     *
381     * @return client id, or {@code null}
382     */
383    public String getFrom() {
384        return from;
385    }
386
387    /**
388     * Get the default language of any human-readable content within the
389     * XML.  Defaults to "en".
390     *
391     * @return XML language ID
392     */
393    public String getLang() {
394        return lang;
395    }
396
397    /**
398     * Get the routing information for messages sent to the CM.
399     *
400     * @return route attribute string, or {@code null} if no routing
401     *  info was provided.
402     */
403    public String getRoute() {
404        return route;
405    }
406
407    /**
408     * Get the HTTP proxy host to use.
409     *
410     * @return proxy host, or {@code null} if no proxy information was specified
411     */
412    public String getProxyHost() {
413        return proxyHost;
414    }
415
416    /**
417     * Get the HTTP proxy port to use.
418     *
419     * @return proxy port, or 0 if no proxy information was specified
420     */
421    public int getProxyPort() {
422        return proxyPort;
423    }
424
425    /**
426     * Get the SSL context to use for this session.
427     *
428     * @return SSL context instance to use, or {@code null} if no
429     *  context instance was provided.
430     */
431    public SSLContext getSSLContext() {
432        return sslContext;
433    }
434
435    /**
436     * Determines whether or not compression of the underlying data stream
437     * should be attempted/allowed.  Defaults to {@code false}.
438     *
439     * @return {@code true} if compression should be attempted, {@code false}
440     *  if compression is disabled or was not specified
441     */
442    boolean isCompressionEnabled() {
443        return compressionEnabled;
444    }
445
446}
447