/* * 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(); }