|
1 /* |
|
2 * ==================================================================== |
|
3 * Licensed to the Apache Software Foundation (ASF) under one |
|
4 * or more contributor license agreements. See the NOTICE file |
|
5 * distributed with this work for additional information |
|
6 * regarding copyright ownership. The ASF licenses this file |
|
7 * to you under the Apache License, Version 2.0 (the |
|
8 * "License"); you may not use this file except in compliance |
|
9 * with the License. You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, |
|
14 * software distributed under the License is distributed on an |
|
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
16 * KIND, either express or implied. See the License for the |
|
17 * specific language governing permissions and limitations |
|
18 * under the License. |
|
19 * ==================================================================== |
|
20 * |
|
21 * This software consists of voluntary contributions made by many |
|
22 * individuals on behalf of the Apache Software Foundation. For more |
|
23 * information on the Apache Software Foundation, please see |
|
24 * <http://www.apache.org/>. |
|
25 * |
|
26 */ |
|
27 package ch.boye.httpclientandroidlib.conn.scheme; |
|
28 |
|
29 import java.util.Locale; |
|
30 |
|
31 import ch.boye.httpclientandroidlib.annotation.Immutable; |
|
32 |
|
33 import ch.boye.httpclientandroidlib.util.LangUtils; |
|
34 |
|
35 /** |
|
36 * Encapsulates specifics of a protocol scheme such as "http" or "https". Schemes are identified |
|
37 * by lowercase names. Supported schemes are typically collected in a {@link SchemeRegistry |
|
38 * SchemeRegistry}. |
|
39 * <p> |
|
40 * For example, to configure support for "https://" URLs, you could write code like the following: |
|
41 * <pre> |
|
42 * Scheme https = new Scheme("https", 443, new MySecureSocketFactory()); |
|
43 * SchemeRegistry.DEFAULT.register(https); |
|
44 * </pre> |
|
45 * |
|
46 * @since 4.0 |
|
47 */ |
|
48 @Immutable |
|
49 public final class Scheme { |
|
50 |
|
51 /** The name of this scheme, in lowercase. (e.g. http, https) */ |
|
52 private final String name; |
|
53 |
|
54 /** The socket factory for this scheme */ |
|
55 private final SchemeSocketFactory socketFactory; |
|
56 |
|
57 /** The default port for this scheme */ |
|
58 private final int defaultPort; |
|
59 |
|
60 /** Indicates whether this scheme allows for layered connections */ |
|
61 private final boolean layered; |
|
62 |
|
63 /** A string representation, for {@link #toString toString}. */ |
|
64 private String stringRep; |
|
65 /* |
|
66 * This is used to cache the result of the toString() method |
|
67 * Since the method always generates the same value, there's no |
|
68 * need to synchronize, and it does not affect immutability. |
|
69 */ |
|
70 |
|
71 /** |
|
72 * Creates a new scheme. |
|
73 * Whether the created scheme allows for layered connections |
|
74 * depends on the class of <code>factory</code>. |
|
75 * |
|
76 * @param name the scheme name, for example "http". |
|
77 * The name will be converted to lowercase. |
|
78 * @param port the default port for this scheme |
|
79 * @param factory the factory for creating sockets for communication |
|
80 * with this scheme |
|
81 * |
|
82 * @since 4.1 |
|
83 */ |
|
84 public Scheme(final String name, final int port, final SchemeSocketFactory factory) { |
|
85 if (name == null) { |
|
86 throw new IllegalArgumentException("Scheme name may not be null"); |
|
87 } |
|
88 if ((port <= 0) || (port > 0xffff)) { |
|
89 throw new IllegalArgumentException("Port is invalid: " + port); |
|
90 } |
|
91 if (factory == null) { |
|
92 throw new IllegalArgumentException("Socket factory may not be null"); |
|
93 } |
|
94 this.name = name.toLowerCase(Locale.ENGLISH); |
|
95 this.socketFactory = factory; |
|
96 this.defaultPort = port; |
|
97 this.layered = factory instanceof LayeredSchemeSocketFactory; |
|
98 } |
|
99 |
|
100 /** |
|
101 * Creates a new scheme. |
|
102 * Whether the created scheme allows for layered connections |
|
103 * depends on the class of <code>factory</code>. |
|
104 * |
|
105 * @param name the scheme name, for example "http". |
|
106 * The name will be converted to lowercase. |
|
107 * @param factory the factory for creating sockets for communication |
|
108 * with this scheme |
|
109 * @param port the default port for this scheme |
|
110 * |
|
111 * @deprecated Use {@link #Scheme(String, int, SchemeSocketFactory)} |
|
112 */ |
|
113 @Deprecated |
|
114 public Scheme(final String name, |
|
115 final SocketFactory factory, |
|
116 final int port) { |
|
117 |
|
118 if (name == null) { |
|
119 throw new IllegalArgumentException |
|
120 ("Scheme name may not be null"); |
|
121 } |
|
122 if (factory == null) { |
|
123 throw new IllegalArgumentException |
|
124 ("Socket factory may not be null"); |
|
125 } |
|
126 if ((port <= 0) || (port > 0xffff)) { |
|
127 throw new IllegalArgumentException |
|
128 ("Port is invalid: " + port); |
|
129 } |
|
130 |
|
131 this.name = name.toLowerCase(Locale.ENGLISH); |
|
132 if (factory instanceof LayeredSocketFactory) { |
|
133 this.socketFactory = new LayeredSchemeSocketFactoryAdaptor( |
|
134 (LayeredSocketFactory) factory); |
|
135 this.layered = true; |
|
136 } else { |
|
137 this.socketFactory = new SchemeSocketFactoryAdaptor(factory); |
|
138 this.layered = false; |
|
139 } |
|
140 this.defaultPort = port; |
|
141 } |
|
142 |
|
143 /** |
|
144 * Obtains the default port. |
|
145 * |
|
146 * @return the default port for this scheme |
|
147 */ |
|
148 public final int getDefaultPort() { |
|
149 return defaultPort; |
|
150 } |
|
151 |
|
152 |
|
153 /** |
|
154 * Obtains the socket factory. |
|
155 * If this scheme is {@link #isLayered layered}, the factory implements |
|
156 * {@link LayeredSocketFactory LayeredSocketFactory}. |
|
157 * |
|
158 * @return the socket factory for this scheme |
|
159 * |
|
160 * @deprecated Use {@link #getSchemeSocketFactory()} |
|
161 */ |
|
162 @Deprecated |
|
163 public final SocketFactory getSocketFactory() { |
|
164 if (this.socketFactory instanceof SchemeSocketFactoryAdaptor) { |
|
165 return ((SchemeSocketFactoryAdaptor) this.socketFactory).getFactory(); |
|
166 } else { |
|
167 if (this.layered) { |
|
168 return new LayeredSocketFactoryAdaptor( |
|
169 (LayeredSchemeSocketFactory) this.socketFactory); |
|
170 } else { |
|
171 return new SocketFactoryAdaptor(this.socketFactory); |
|
172 } |
|
173 } |
|
174 } |
|
175 |
|
176 /** |
|
177 * Obtains the socket factory. |
|
178 * If this scheme is {@link #isLayered layered}, the factory implements |
|
179 * {@link LayeredSocketFactory LayeredSchemeSocketFactory}. |
|
180 * |
|
181 * @return the socket factory for this scheme |
|
182 * |
|
183 * @since 4.1 |
|
184 */ |
|
185 public final SchemeSocketFactory getSchemeSocketFactory() { |
|
186 return this.socketFactory; |
|
187 } |
|
188 |
|
189 /** |
|
190 * Obtains the scheme name. |
|
191 * |
|
192 * @return the name of this scheme, in lowercase |
|
193 */ |
|
194 public final String getName() { |
|
195 return name; |
|
196 } |
|
197 |
|
198 /** |
|
199 * Indicates whether this scheme allows for layered connections. |
|
200 * |
|
201 * @return <code>true</code> if layered connections are possible, |
|
202 * <code>false</code> otherwise |
|
203 */ |
|
204 public final boolean isLayered() { |
|
205 return layered; |
|
206 } |
|
207 |
|
208 /** |
|
209 * Resolves the correct port for this scheme. |
|
210 * Returns the given port if it is valid, the default port otherwise. |
|
211 * |
|
212 * @param port the port to be resolved, |
|
213 * a negative number to obtain the default port |
|
214 * |
|
215 * @return the given port or the defaultPort |
|
216 */ |
|
217 public final int resolvePort(int port) { |
|
218 return port <= 0 ? defaultPort : port; |
|
219 } |
|
220 |
|
221 /** |
|
222 * Return a string representation of this object. |
|
223 * |
|
224 * @return a human-readable string description of this scheme |
|
225 */ |
|
226 @Override |
|
227 public final String toString() { |
|
228 if (stringRep == null) { |
|
229 StringBuilder buffer = new StringBuilder(); |
|
230 buffer.append(this.name); |
|
231 buffer.append(':'); |
|
232 buffer.append(Integer.toString(this.defaultPort)); |
|
233 stringRep = buffer.toString(); |
|
234 } |
|
235 return stringRep; |
|
236 } |
|
237 |
|
238 @Override |
|
239 public final boolean equals(Object obj) { |
|
240 if (this == obj) return true; |
|
241 if (obj instanceof Scheme) { |
|
242 Scheme that = (Scheme) obj; |
|
243 return this.name.equals(that.name) |
|
244 && this.defaultPort == that.defaultPort |
|
245 && this.layered == that.layered; |
|
246 } else { |
|
247 return false; |
|
248 } |
|
249 } |
|
250 |
|
251 @Override |
|
252 public int hashCode() { |
|
253 int hash = LangUtils.HASH_SEED; |
|
254 hash = LangUtils.hashCode(hash, this.defaultPort); |
|
255 hash = LangUtils.hashCode(hash, this.name); |
|
256 hash = LangUtils.hashCode(hash, this.layered); |
|
257 return hash; |
|
258 } |
|
259 |
|
260 } |