michael@0: /*
michael@0: * ====================================================================
michael@0: * Licensed to the Apache Software Foundation (ASF) under one
michael@0: * or more contributor license agreements. See the NOTICE file
michael@0: * distributed with this work for additional information
michael@0: * regarding copyright ownership. The ASF licenses this file
michael@0: * to you under the Apache License, Version 2.0 (the
michael@0: * "License"); you may not use this file except in compliance
michael@0: * with the License. You may obtain a copy of the License at
michael@0: *
michael@0: * http://www.apache.org/licenses/LICENSE-2.0
michael@0: *
michael@0: * Unless required by applicable law or agreed to in writing,
michael@0: * software distributed under the License is distributed on an
michael@0: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
michael@0: * KIND, either express or implied. See the License for the
michael@0: * specific language governing permissions and limitations
michael@0: * under the License.
michael@0: * ====================================================================
michael@0: *
michael@0: * This software consists of voluntary contributions made by many
michael@0: * individuals on behalf of the Apache Software Foundation. For more
michael@0: * information on the Apache Software Foundation, please see
michael@0: *
michael@0: * For example, to configure support for "https://" URLs, you could write code like the following: michael@0: *
michael@0: * Scheme https = new Scheme("https", 443, new MySecureSocketFactory()); michael@0: * SchemeRegistry.DEFAULT.register(https); michael@0: *michael@0: * michael@0: * @since 4.0 michael@0: */ michael@0: @Immutable michael@0: public final class Scheme { michael@0: michael@0: /** The name of this scheme, in lowercase. (e.g. http, https) */ michael@0: private final String name; michael@0: michael@0: /** The socket factory for this scheme */ michael@0: private final SchemeSocketFactory socketFactory; michael@0: michael@0: /** The default port for this scheme */ michael@0: private final int defaultPort; michael@0: michael@0: /** Indicates whether this scheme allows for layered connections */ michael@0: private final boolean layered; michael@0: michael@0: /** A string representation, for {@link #toString toString}. */ michael@0: private String stringRep; michael@0: /* michael@0: * This is used to cache the result of the toString() method michael@0: * Since the method always generates the same value, there's no michael@0: * need to synchronize, and it does not affect immutability. michael@0: */ michael@0: michael@0: /** michael@0: * Creates a new scheme. michael@0: * Whether the created scheme allows for layered connections michael@0: * depends on the class of
factory
.
michael@0: *
michael@0: * @param name the scheme name, for example "http".
michael@0: * The name will be converted to lowercase.
michael@0: * @param port the default port for this scheme
michael@0: * @param factory the factory for creating sockets for communication
michael@0: * with this scheme
michael@0: *
michael@0: * @since 4.1
michael@0: */
michael@0: public Scheme(final String name, final int port, final SchemeSocketFactory factory) {
michael@0: if (name == null) {
michael@0: throw new IllegalArgumentException("Scheme name may not be null");
michael@0: }
michael@0: if ((port <= 0) || (port > 0xffff)) {
michael@0: throw new IllegalArgumentException("Port is invalid: " + port);
michael@0: }
michael@0: if (factory == null) {
michael@0: throw new IllegalArgumentException("Socket factory may not be null");
michael@0: }
michael@0: this.name = name.toLowerCase(Locale.ENGLISH);
michael@0: this.socketFactory = factory;
michael@0: this.defaultPort = port;
michael@0: this.layered = factory instanceof LayeredSchemeSocketFactory;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Creates a new scheme.
michael@0: * Whether the created scheme allows for layered connections
michael@0: * depends on the class of factory
.
michael@0: *
michael@0: * @param name the scheme name, for example "http".
michael@0: * The name will be converted to lowercase.
michael@0: * @param factory the factory for creating sockets for communication
michael@0: * with this scheme
michael@0: * @param port the default port for this scheme
michael@0: *
michael@0: * @deprecated Use {@link #Scheme(String, int, SchemeSocketFactory)}
michael@0: */
michael@0: @Deprecated
michael@0: public Scheme(final String name,
michael@0: final SocketFactory factory,
michael@0: final int port) {
michael@0:
michael@0: if (name == null) {
michael@0: throw new IllegalArgumentException
michael@0: ("Scheme name may not be null");
michael@0: }
michael@0: if (factory == null) {
michael@0: throw new IllegalArgumentException
michael@0: ("Socket factory may not be null");
michael@0: }
michael@0: if ((port <= 0) || (port > 0xffff)) {
michael@0: throw new IllegalArgumentException
michael@0: ("Port is invalid: " + port);
michael@0: }
michael@0:
michael@0: this.name = name.toLowerCase(Locale.ENGLISH);
michael@0: if (factory instanceof LayeredSocketFactory) {
michael@0: this.socketFactory = new LayeredSchemeSocketFactoryAdaptor(
michael@0: (LayeredSocketFactory) factory);
michael@0: this.layered = true;
michael@0: } else {
michael@0: this.socketFactory = new SchemeSocketFactoryAdaptor(factory);
michael@0: this.layered = false;
michael@0: }
michael@0: this.defaultPort = port;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Obtains the default port.
michael@0: *
michael@0: * @return the default port for this scheme
michael@0: */
michael@0: public final int getDefaultPort() {
michael@0: return defaultPort;
michael@0: }
michael@0:
michael@0:
michael@0: /**
michael@0: * Obtains the socket factory.
michael@0: * If this scheme is {@link #isLayered layered}, the factory implements
michael@0: * {@link LayeredSocketFactory LayeredSocketFactory}.
michael@0: *
michael@0: * @return the socket factory for this scheme
michael@0: *
michael@0: * @deprecated Use {@link #getSchemeSocketFactory()}
michael@0: */
michael@0: @Deprecated
michael@0: public final SocketFactory getSocketFactory() {
michael@0: if (this.socketFactory instanceof SchemeSocketFactoryAdaptor) {
michael@0: return ((SchemeSocketFactoryAdaptor) this.socketFactory).getFactory();
michael@0: } else {
michael@0: if (this.layered) {
michael@0: return new LayeredSocketFactoryAdaptor(
michael@0: (LayeredSchemeSocketFactory) this.socketFactory);
michael@0: } else {
michael@0: return new SocketFactoryAdaptor(this.socketFactory);
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: /**
michael@0: * Obtains the socket factory.
michael@0: * If this scheme is {@link #isLayered layered}, the factory implements
michael@0: * {@link LayeredSocketFactory LayeredSchemeSocketFactory}.
michael@0: *
michael@0: * @return the socket factory for this scheme
michael@0: *
michael@0: * @since 4.1
michael@0: */
michael@0: public final SchemeSocketFactory getSchemeSocketFactory() {
michael@0: return this.socketFactory;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Obtains the scheme name.
michael@0: *
michael@0: * @return the name of this scheme, in lowercase
michael@0: */
michael@0: public final String getName() {
michael@0: return name;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Indicates whether this scheme allows for layered connections.
michael@0: *
michael@0: * @return true
if layered connections are possible,
michael@0: * false
otherwise
michael@0: */
michael@0: public final boolean isLayered() {
michael@0: return layered;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Resolves the correct port for this scheme.
michael@0: * Returns the given port if it is valid, the default port otherwise.
michael@0: *
michael@0: * @param port the port to be resolved,
michael@0: * a negative number to obtain the default port
michael@0: *
michael@0: * @return the given port or the defaultPort
michael@0: */
michael@0: public final int resolvePort(int port) {
michael@0: return port <= 0 ? defaultPort : port;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Return a string representation of this object.
michael@0: *
michael@0: * @return a human-readable string description of this scheme
michael@0: */
michael@0: @Override
michael@0: public final String toString() {
michael@0: if (stringRep == null) {
michael@0: StringBuilder buffer = new StringBuilder();
michael@0: buffer.append(this.name);
michael@0: buffer.append(':');
michael@0: buffer.append(Integer.toString(this.defaultPort));
michael@0: stringRep = buffer.toString();
michael@0: }
michael@0: return stringRep;
michael@0: }
michael@0:
michael@0: @Override
michael@0: public final boolean equals(Object obj) {
michael@0: if (this == obj) return true;
michael@0: if (obj instanceof Scheme) {
michael@0: Scheme that = (Scheme) obj;
michael@0: return this.name.equals(that.name)
michael@0: && this.defaultPort == that.defaultPort
michael@0: && this.layered == that.layered;
michael@0: } else {
michael@0: return false;
michael@0: }
michael@0: }
michael@0:
michael@0: @Override
michael@0: public int hashCode() {
michael@0: int hash = LangUtils.HASH_SEED;
michael@0: hash = LangUtils.hashCode(hash, this.defaultPort);
michael@0: hash = LangUtils.hashCode(hash, this.name);
michael@0: hash = LangUtils.hashCode(hash, this.layered);
michael@0: return hash;
michael@0: }
michael@0:
michael@0: }