001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.conn.scheme;
028
029import java.util.Locale;
030
031import org.apache.http.annotation.Contract;
032import org.apache.http.annotation.ThreadingBehavior;
033import org.apache.http.util.Args;
034import org.apache.http.util.LangUtils;
035
036/**
037 * Encapsulates specifics of a protocol scheme such as "http" or "https". Schemes are identified
038 * by lowercase names. Supported schemes are typically collected in a {@link SchemeRegistry
039 * SchemeRegistry}.
040 * <p>
041 * For example, to configure support for "https://" URLs, you could write code like the following:
042 * </p>
043 * <pre>
044 * Scheme https = new Scheme("https", 443, new MySecureSocketFactory());
045 * SchemeRegistry registry = new SchemeRegistry();
046 * registry.register(https);
047 * </pre>
048 *
049 * @since 4.0
050 *
051 * @deprecated (4.3) use {@link org.apache.http.conn.SchemePortResolver} for default port
052 * resolution and {@link org.apache.http.config.Registry} for socket factory lookups.
053 */
054@Contract(threading = ThreadingBehavior.IMMUTABLE)
055@Deprecated
056public final class Scheme {
057
058    /** The name of this scheme, in lowercase. (e.g. http, https) */
059    private final String name;
060
061    /** The socket factory for this scheme */
062    private final SchemeSocketFactory socketFactory;
063
064    /** The default port for this scheme */
065    private final int defaultPort;
066
067    /** Indicates whether this scheme allows for layered connections */
068    private final boolean layered;
069
070    /** A string representation, for {@link #toString toString}. */
071    private String stringRep;
072    /*
073     *  This is used to cache the result of the toString() method
074     *  Since the method always generates the same value, there's no
075     *  need to synchronize, and it does not affect immutability.
076    */
077
078    /**
079     * Creates a new scheme.
080     * Whether the created scheme allows for layered connections
081     * depends on the class of {@code factory}.
082     *
083     * @param name      the scheme name, for example "http".
084     *                  The name will be converted to lowercase.
085     * @param port      the default port for this scheme
086     * @param factory   the factory for creating sockets for communication
087     *                  with this scheme
088     *
089     * @since 4.1
090     */
091    public Scheme(final String name, final int port, final SchemeSocketFactory factory) {
092        Args.notNull(name, "Scheme name");
093        Args.check(port > 0 && port <= 0xffff, "Port is invalid");
094        Args.notNull(factory, "Socket factory");
095        this.name = name.toLowerCase(Locale.ENGLISH);
096        this.defaultPort = port;
097        if (factory instanceof SchemeLayeredSocketFactory) {
098            this.layered = true;
099            this.socketFactory = factory;
100        } else if (factory instanceof LayeredSchemeSocketFactory) {
101            this.layered = true;
102            this.socketFactory = new SchemeLayeredSocketFactoryAdaptor2((LayeredSchemeSocketFactory) factory);
103        } else {
104            this.layered = false;
105            this.socketFactory = factory;
106        }
107    }
108
109    /**
110     * Creates a new scheme.
111     * Whether the created scheme allows for layered connections
112     * depends on the class of {@code factory}.
113     *
114     * @param name      the scheme name, for example "http".
115     *                  The name will be converted to lowercase.
116     * @param factory   the factory for creating sockets for communication
117     *                  with this scheme
118     * @param port      the default port for this scheme
119     *
120     * @deprecated (4.1)  Use {@link #Scheme(String, int, SchemeSocketFactory)}
121     */
122    @Deprecated
123    public Scheme(final String name,
124                  final SocketFactory factory,
125                  final int port) {
126
127        Args.notNull(name, "Scheme name");
128        Args.notNull(factory, "Socket factory");
129        Args.check(port > 0 && port <= 0xffff, "Port is invalid");
130
131        this.name = name.toLowerCase(Locale.ENGLISH);
132        if (factory instanceof LayeredSocketFactory) {
133            this.socketFactory = new SchemeLayeredSocketFactoryAdaptor(
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 (4.1)  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} if layered connections are possible,
202     *         {@code false} 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(final 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            final 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(final Object obj) {
240        if (this == obj) {
241            return true;
242        }
243        if (obj instanceof Scheme) {
244            final Scheme that = (Scheme) obj;
245            return this.name.equals(that.name)
246                && this.defaultPort == that.defaultPort
247                && this.layered == that.layered;
248        } else {
249            return false;
250        }
251    }
252
253    @Override
254    public int hashCode() {
255        int hash = LangUtils.HASH_SEED;
256        hash = LangUtils.hashCode(hash, this.defaultPort);
257        hash = LangUtils.hashCode(hash, this.name);
258        hash = LangUtils.hashCode(hash, this.layered);
259        return hash;
260    }
261
262}