Skip to content

Package: ServerEndpointConfig

ServerEndpointConfig

Coverage

1: /*
2: * Copyright (c) 2018, 2021 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package jakarta.websocket.server;
19:
20: import java.util.ArrayList;
21: import java.util.Collections;
22: import java.util.List;
23: import java.util.ServiceLoader;
24: import jakarta.websocket.Decoder;
25: import jakarta.websocket.Encoder;
26: import jakarta.websocket.EndpointConfig;
27: import jakarta.websocket.Extension;
28: import jakarta.websocket.HandshakeResponse;
29:
30: /**
31: * The ServerEndpointConfig is a special kind of endpoint configuration object that contains web socket configuration
32: * information specific only to server endpoints. For developers deploying programmatic endpoints, ServerEndpointConfig
33: * objects can be created using a {@link ServerEndpointConfig.Builder}. Certain configuration operations can be
34: * customized by providing a {@link ServerEndpointConfig.Configurator}
35: *
36: * @author dannycoward
37: */
38: public interface ServerEndpointConfig extends EndpointConfig {
39:
40: /**
41: * Returns the Class of the endpoint this configuration is configuring. If the endpoint is an annotated endpoint,
42: * the value is the class of the Java class annotated with @ServerEndpoint. if the endpoint is a programmatic, the
43: * value is the class of the subclass of Endpoint.
44: *
45: * @return the class of the endpoint, annotated or programmatic.
46: */
47: Class<?> getEndpointClass();
48:
49: /**
50: * Return the path for this endpoint configuration. The path is the URI or URI-template (level 1) relative to the
51: * websocket root of the server to which the endpoint using this configuration will be mapped. The path is always
52: * non-null and always begins with a leading "/".
53: *
54: * @return the relative path for this configuration.
55: */
56: String getPath();
57:
58: /**
59: * Return the websocket subprotocols configured.
60: *
61: * @return the list of subprotocols, the empty list if none
62: */
63: List<String> getSubprotocols();
64:
65: /**
66: * Return the websocket extensions configured.
67: *
68: * @return the list of extensions, the empty list if none.
69: */
70: List<Extension> getExtensions();
71:
72: /**
73: * Return the {@link ServerEndpointConfig.Configurator} this configuration is using. If none was set by calling
74: * {@link ServerEndpointConfig.Builder#configurator(jakarta.websocket.server.ServerEndpointConfig.Configurator) } this
75: * methods returns the platform default configurator.
76: *
77: * @return the configurator in use.
78: */
79: ServerEndpointConfig.Configurator getConfigurator();
80:
81: /**
82: * The ServerEndpointConfig.Configurator class may be extended by developers who want to provide custom
83: * configuration algorithms, such as intercepting the opening handshake, or providing arbitrary methods and
84: * algorithms that can be accessed from each endpoint instance configured with this configurator.
85: *
86: * The implementation must provide a platform default configurator loading using the service loader.
87: */
88: public class Configurator {
89: private ServerEndpointConfig.Configurator containerDefaultConfigurator;
90:
91: static ServerEndpointConfig.Configurator fetchContainerDefaultConfigurator() {
92: for (ServerEndpointConfig.Configurator impl : ServiceLoader
93: .load(jakarta.websocket.server.ServerEndpointConfig.Configurator.class)) {
94: return impl;
95: }
96: throw new RuntimeException("Cannot load platform configurator");
97: }
98:
99: /**
100: * Return the platform default configurator.
101: *
102: * @return the platform default configurator
103: */
104: public ServerEndpointConfig.Configurator getContainerDefaultConfigurator() {
105: if (this.containerDefaultConfigurator == null) {
106: this.containerDefaultConfigurator = fetchContainerDefaultConfigurator();
107: }
108: return this.containerDefaultConfigurator;
109:
110: }
111:
112: /**
113: * Return the subprotocol the server endpoint has chosen from the requested list supplied by a client who wishes
114: * to connect, or none if there wasn't one this server endpoint liked. See
115: * <a href="http://tools.ietf.org/html/rfc6455#section-4.2.2">Sending the Server's Opening Handshake</a>.
116: * Subclasses may provide custom algorithms based on other factors.
117: *
118: * <p>
119: * The default platform implementation of this method returns the first subprotocol in the list sent by the
120: * client that the server supports, or the empty string if there isn't one.
121: *
122: * @param requested the requested subprotocols from the client endpoint
123: * @param supported the subprotocols supported by the server endpoint
124: * @return the negotiated subprotocol or the empty string if there isn't one.
125: */
126: public String getNegotiatedSubprotocol(List<String> supported, List<String> requested) {
127: return this.getContainerDefaultConfigurator().getNegotiatedSubprotocol(supported, requested);
128: }
129:
130: /**
131: * Return the ordered list of extensions that t server endpoint will support given the requested extension list
132: * passed in, the empty list if none. See <a href="http://tools.ietf.org/html/rfc6455#section-9.1">Negotiating
133: * Extensions</a>
134: *
135: * <p>
136: * The default platform implementation of this method returns a list containing all of the requested extensions
137: * passed to this method that it supports, using the order in the requested extensions, the empty list if none.
138: *
139: * @param installed the installed extensions on the implementation.
140: * @param requested the requested extensions, in the order they were requested by the client
141: * @return the list of extensions negotiated, the empty list if none.
142: */
143: public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested) {
144: return this.getContainerDefaultConfigurator().getNegotiatedExtensions(installed, requested);
145: }
146:
147: /**
148: * Check the value of the Origin header (<a href="http://tools.ietf.org/html/rfc6454">See Origin Header</a>) the
149: * client passed during the opening handshake.
150: *
151: * <p>
152: * The platform default implementation of this method makes a check of the validity of the Origin header sent
153: * along with the opening handshake following the recommendation at:
154: * <a href="http://tools.ietf.org/html/rfc6455#section-4.2">Sending the Server's Opening Handshake</a>.
155: *
156: * @param originHeaderValue the value of the origin header passed by the client.
157: * @return whether the check passed or not
158: */
159: public boolean checkOrigin(String originHeaderValue) {
160: return this.getContainerDefaultConfigurator().checkOrigin(originHeaderValue);
161: }
162:
163: /**
164: * Called by the container after it has formulated a handshake response resulting from a well-formed handshake
165: * request. The container has already checked that this configuration has a matching URI, determined the
166: * validity of the origin using the checkOrigin method, and filled out the negotiated subprotocols and
167: * extensions based on this configuration. Custom configurations may override this method in order to inspect
168: * the request parameters and modify the handshake response that the server has formulated. and the URI checking
169: * also.
170: *
171: * <p>
172: * If the developer does not override this method, no further modification of the request and response are made
173: * by the implementation.
174: * <p>
175: * The user properties made available via {@link ServerEndpointConfig#getUserProperties()} must be a per
176: * WebSocket connection (i.e. per {@link jakarta.websocket.Session}) copy of the user properties. This copy,
177: * including any modifications made to the user properties during the execution of this method must be used to
178: * populate the initial contents of {@link jakarta.websocket.Session#getUserProperties()}.
179: *
180: * @param sec the configuration object involved in the handshake
181: * @param request the opening handshake request.
182: * @param response the proposed opening handshake response
183: */
184: public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
185: // nothing.
186: }
187:
188: /**
189: * This method is called by the container each time a new client connects to the logical endpoint this
190: * configurator configures. Developers may override this method to control instantiation of endpoint instances
191: * in order to customize the initialization of the endpoint instance, or manage them in some other way. If the
192: * developer overrides this method, services like dependency injection that are otherwise supported, for
193: * example, when the implementation is part of the Java EE platform may not be available. The platform default
194: * implementation of this method returns a new endpoint instance per call, thereby ensuring that there is one
195: * endpoint instance per client, the default deployment cardinality.
196: *
197: * @param endpointClass the class of the endpoint
198: * @param <T> the type of the endpoint
199: * @return an instance of the endpoint that will handle all interactions from a new client.
200: * @throws InstantiationException if there was an error producing the endpoint instance.
201: */
202: public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
203: return this.getContainerDefaultConfigurator().getEndpointInstance(endpointClass);
204: }
205:
206: }
207:
208: /**
209: * The ServerEndpointConfig.Builder is a class used for creating {@link ServerEndpointConfig.Builder} objects for
210: * the purposes of deploying a server endpoint.
211: *
212: * <p>
213: * Here are some examples:
214: *
215: * <p>
216: * Building a plain configuration for an endpoint with just a path.
217: *
218: * <pre>
219: * <code>
220: * ServerEndpointConfig config = ServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class, "/foo").build();
221: * </code>
222: * </pre>
223: *
224: * <p>
225: * Building a configuration with no subprotocols and a custom configurator.
226: *
227: * <pre>
228: * <code>
229: * ServerEndpointConfig config = ServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class, "/bar")
230: * .subprotocols(subprotocols)
231: * .configurator(new MyServerConfigurator())
232: * .build();
233: * </code>
234: * </pre>
235: *
236: * @author dannycoward
237: */
238: public final class Builder {
239: private String path;
240: private Class<?> endpointClass;
241: private List<String> subprotocols = Collections.emptyList();
242: private List<Extension> extensions = Collections.emptyList();
243: private List<Class<? extends Encoder>> encoders = Collections.emptyList();
244: private List<Class<? extends Decoder>> decoders = Collections.emptyList();
245: private ServerEndpointConfig.Configurator serverEndpointConfigurator;
246:
247: /**
248: * Creates the builder with the mandatory information of the endpoint class (programmatic or annotated), the
249: * relative URI or URI-template to use, and with no subprotocols, extensions, encoders, decoders or custom
250: * configurator.
251: *
252: * @param endpointClass the class of the endpoint to configure
253: * @param path The URI or URI template where the endpoint will be deployed. A trailing "/" will be
254: * ignored and the path must begin with /.
255: * @return a new instance of ServerEndpointConfig.Builder
256: */
257: public static Builder create(Class<?> endpointClass, String path) {
258: return new Builder(endpointClass, path);
259: }
260:
261: // only one way to build them
262: private Builder() {
263:
264: }
265:
266: /**
267: * Builds the configuration object using the current attributes that have been set on this builder object.
268: *
269: * @return a new ServerEndpointConfig object.
270: */
271: public ServerEndpointConfig build() {
272: return new DefaultServerEndpointConfig(this.endpointClass, this.path, this.subprotocols, this.extensions,
273: this.encoders, this.decoders, this.serverEndpointConfigurator);
274: }
275:
276: private Builder(Class<?> endpointClass, String path) {
277: if (endpointClass == null) {
278: throw new IllegalArgumentException("endpointClass cannot be null");
279: }
280: this.endpointClass = endpointClass;
281: if (path == null || !path.startsWith("/")) {
282: throw new IllegalStateException("Path cannot be null and must begin with /");
283: }
284: this.path = path;
285: }
286:
287: /**
288: * Sets the list of encoder implementation classes for this builder.
289: *
290: * @param encoders the encoders
291: * @return this builder instance
292: */
293: public ServerEndpointConfig.Builder encoders(List<Class<? extends Encoder>> encoders) {
294: this.encoders = (encoders == null) ? new ArrayList<>() : encoders;
295: return this;
296: }
297:
298: /**
299: * Sets the decoder implementation classes to use in the configuration.
300: *
301: * @param decoders the decoders
302: * @return this builder instance.
303: */
304: public ServerEndpointConfig.Builder decoders(List<Class<? extends Decoder>> decoders) {
305: this.decoders = (decoders == null) ? new ArrayList<>() : decoders;
306: return this;
307: }
308:
309: /**
310: * Sets the subprotocols to use in the configuration.
311: *
312: * @param subprotocols the subprotocols.
313: * @return this builder instance
314: */
315: public ServerEndpointConfig.Builder subprotocols(List<String> subprotocols) {
316: this.subprotocols = (subprotocols == null) ? new ArrayList<>() : subprotocols;
317: return this;
318: }
319:
320: /**
321: * Sets the extensions to use in the configuration.
322: *
323: * @param extensions the extensions to use.
324: * @return this builder instance.
325: */
326: public ServerEndpointConfig.Builder extensions(List<Extension> extensions) {
327: this.extensions = (extensions == null) ? new ArrayList<>() : extensions;
328: return this;
329: }
330:
331: /**
332: * Sets the custom configurator to use on the configuration object built by this builder.
333: *
334: * @param serverEndpointConfigurator the configurator
335: * @return this builder instance
336: */
337: public ServerEndpointConfig.Builder configurator(ServerEndpointConfig.Configurator serverEndpointConfigurator) {
338: this.serverEndpointConfigurator = serverEndpointConfigurator;
339: return this;
340: }
341:
342: }
343:
344: }