/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package javax.websocket.server;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.HandshakeResponse;
/**
* The ServerEndpointConfig is a special kind of endpoint configuration object that contains web socket configuration
* information specific only to server endpoints. For developers deploying programmatic endpoints, ServerEndpointConfig
* objects can be created using a ServerEndpointConfig.Builder. Certain configuration operations can be customized by
* providing a ServerEndpointConfig.Configurator.
*/
public interface ServerEndpointConfig extends EndpointConfig {
/**
* The ServerEndpointConfig.Builder is a class used for creating ServerEndpointConfig.Builder objects for the
* purposes of deploying a server endpoint.
* <p/>
* Here are some examples:
* <p/>
* Building a plain configuration for an endpoint with just a path.
* <p/>
* <code>
* ServerEndpointConfig config = ServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class, "/foo").build();
* </code>
* <p/>
* Building a configuration with no subprotocols and a custom configurator. <code>
* ServerEndpointConfig config = ServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class, "/bar")
* .subprotocols(subprotocols)
* .configurator(new MyServerConfigurator())
* .build();
* </code>
*/
public static final class Builder {
private Class<?> endpointClass;
private String path;
private Configurator configurator;
private List<Class<? extends Decoder>> decoders;
private List<Class<? extends Encoder>> encoders;
private List<Extension> extensions;
private List<String> subprotocols;
private Map<String, Object> userProperties = new HashMap<String, Object>();
/**
* Creates the builder with the mandatory information of the endpoint class (programmatic or annotated), the
* relative URI or URI-template to use, and with no subprotocols, extensions, encoders, decoders or custom
* configurator.
*
* @param endpointClass
* the class of the endpoint to configure
* @param path
* The URI or URI template where the endpoint will be deployed. A trailing "/" will be ignored and
* the path must begin with /.
* @return a new instance of ServerEndpointConfig.Builder
*/
public static Builder create(Class<?> endpointClass, String path) {
if (endpointClass == null || path == null) {
throw new NullPointerException();
}
if (!path.startsWith("/")) {
throw new IllegalArgumentException("Path must start with a slash.");
}
int pathLength = path.length();
if (pathLength > 1 && path.endsWith("/")) {
path = path.substring(0, pathLength - 1);
}
Builder b = new Builder(endpointClass, path);
return b;
}
private Builder(Class<?> endpointClass, String path) {
this.endpointClass = endpointClass;
this.path = path;
}
/**
* Builds the configuration object using the current attributes that have been set on this builder object.
*
* @return a new ServerEndpointConfig object.
*/
public ServerEndpointConfig build() {
if (decoders == null)
decoders = Collections.emptyList();
if (encoders == null)
encoders = Collections.emptyList();
if (extensions == null)
extensions = Collections.emptyList();
if (subprotocols == null)
subprotocols = Collections.emptyList();
return new ServerEndpointConfig() {
@Override
public List<Class<? extends Encoder>> getEncoders() {
return encoders;
}
@Override
public List<Class<? extends Decoder>> getDecoders() {
return decoders;
}
@Override
public Map<String, Object> getUserProperties() {
return userProperties;
}
@Override
public Class<?> getEndpointClass() {
return endpointClass;
}
@Override
public String getPath() {
return path;
}
@Override
public List<String> getSubprotocols() {
return subprotocols;
}
@Override
public List<Extension> getExtensions() {
return extensions;
}
@Override
public Configurator getConfigurator() {
return configurator;
}
};
}
/**
* Sets the list of encoder implementation classes for this builder.
*
* @param encoders
* the encoders
* @return this builder instance
*/
public Builder encoders(List<Class<? extends Encoder>> encoders) {
this.encoders = encoders;
return this;
}
/**
* Sets the decoder implementation classes to use in the configuration.
*
* @param decoders
* the decoders
* @return this builder instance.
*/
public Builder decoders(List<Class<? extends Decoder>> decoders) {
this.decoders = decoders;
return this;
}
/**
* Sets the subprotocols to use in the configuration.
*
* @param subprotocols
* the subprotocols.
* @return this builder instance
*/
public Builder subprotocols(List<String> subprotocols) {
this.subprotocols = subprotocols;
return this;
}
/**
* Sets the extensions to use in the configuration.
*
* @param extensions
* the extensions to use.
* @return this builder instance.
*/
public Builder extensions(List<Extension> extensions) {
this.extensions = extensions;
return this;
}
/**
* Sets the custom configurator to use on the configuration object built by this builder.
*
* @param serverEndpointConfigurator
* the configurator
* @return this builder instance
*/
public Builder configurator(Configurator serverEndpointConfigurator) {
this.configurator = serverEndpointConfigurator;
return this;
}
}
/**
* The ServerEndpointConfig.Configurator class may be extended by developers who want to provide custom
* configuration algorithms, such as intercepting the opening handshake, or providing arbitrary methods and
* algorithms that can be accessed from each endpoint instance configured with this configurator. The implementation
* must provide a platform default configurator loading using the service loader.
*/
public static class Configurator {
public Configurator() {
}
/**
* Return the subprotocol the server endpoint has chosen from the requested list supplied by a client who wishes
* to connect, or none if there wasn't one this server endpoint liked. See Sending the Server's Opening
* Handshake. Subclasses may provide custom algorithms based on other factors.
* <p/>
* The default platform implementation of this method returns the first subprotocol in the list sent by the
* client that the server supports, or the empty string if there isn't one.
*
* @param supported
* the subprotocols supported by the server endpoint
* @param requested
* the requested subprotocols from the client endpoint
* @return the negotiated subprotocol or the empty string if there isn't one.
*/
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
return "";
}
/**
* Return the ordered list of extensions that t server endpoint will support given the requested extension list
* passed in, the empty list if none. See Negotiating Extensions
* <p/>
* The default platform implementation of this method returns a list containing all of the requested extensions
* passed to this method that it supports, using the order in the requested extensions, the empty list if none.
*
* @param installed
* the installed extensions on the implementation.
* @param requested
* the requested extensions, in the order they were requested by the client
* @return the list of extensions negotiated, the empty list if none.
*/
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
return Collections.emptyList();
}
/**
* Check the value of the Origin header (See Origin Header) the client passed during the opening handshake.
* <p/>
* The platform default implementation of this method makes a check of the validity of the Origin header sent
* along with the opening handshake following the recommendation at: Sending the Server's Opening Handshake.
*
* @param originHeaderValue
* the value of the origin header passed by the client.
* @return whether the check passed or not
*/
public boolean checkOrigin(String originHeaderValue) {
return originHeaderValue != null;
}
/**
* Called by the container after it has formulated a handshake response resulting from a well-formed handshake
* request. The container has already checked that this configuration has a matching URI, determined the
* validity of the origin using the checkOrigin method, and filled out the negotiated subprotocols and
* extensions based on this configuration. Custom configurations may override this method in order to inspect
* the request parameters and modify the handshake response that the server has formulated. and the URI checking
* also.
* <p/>
* If the developer does not override this method, no further modification of the request and response are made
* by the implementation.
*
* @param sec
* the configuration object involved in the handshake
* @param request
* the opening handshake request.
* @param response
* the proposed opening handshake response
*/
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
// no-op
}
/**
* This method is called by the container each time a new client connects to the logical endpoint this
* configurator configures. Developers may override this method to control instantiation of endpoint instances
* in order to customize the initialization of the endpoint instance, or manage them in some other way. If the
* developer overrides this method, services like dependency injection that are otherwise supported, for
* example, when the implementation is part of the Java EE platform may not be available. The platform default
* implementation of this method returns a new endpoint instance per call, thereby ensuring that there is one
* endpoint instance per client, the default deployment cardinality.
*
* @param endpointClass
* the class of the endpoint
* @return an instance of the endpoint that will handle all interactions from a new client.
* @throws InstantiationException
* if there was an error producing the endpoint instance.
*/
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
try {
return endpointClass.newInstance();
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
/**
* Returns the Class of the endpoint this configuration is configuring. If the endpoint is an annotated endpoint,
* the value is the class of the Java class annotated with @ServerEndpoint. if the endpoint is a programmatic, the
* value is the class of the subclass of Endpoint.
*
* @return the relative path for this configuration.
*/
Class<?> getEndpointClass();
/**
* Return the path for this endpoint configuration. The path is the URI or URI-template (level 1) relative to the
* websocket root of the server to which the endpoint using this configuration will be mapped. The path is always
* non-null and always begins with a leading "/".
*
* @return the relative path for this configuration.
*/
String getPath();
/**
* Return the websocket subprotocols configured.
*
* @return the list of subprotocols, the empty list if none
*/
List<String> getSubprotocols();
/**
* Return the websocket extensions configured.
*
* @return the list of extensions, the empty list if none.
*/
List<Extension> getExtensions();
/**
* Return the ServerEndpointConfig.Configurator this configuration is using. If none was set by calling
* ServerEndpointConfig.Builder.configurator(javax.websocket.server.ServerEndpointConfig.Configurator) this methods
* returns the platform default configurator.
*
* @return the configurator in use.
*/
ServerEndpointConfig.Configurator getConfigurator();
}