| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.websocket.server; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| |
| import javax.servlet.ServletContext; |
| |
| import org.eclipse.jetty.http.pathmap.MappedResource; |
| import org.eclipse.jetty.http.pathmap.PathMappings; |
| import org.eclipse.jetty.http.pathmap.PathSpec; |
| import org.eclipse.jetty.http.pathmap.RegexPathSpec; |
| import org.eclipse.jetty.http.pathmap.ServletPathSpec; |
| import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec; |
| import org.eclipse.jetty.util.component.ContainerLifeCycle; |
| import org.eclipse.jetty.util.component.Dumpable; |
| import org.eclipse.jetty.websocket.api.WebSocketException; |
| import org.eclipse.jetty.websocket.api.WebSocketPolicy; |
| import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; |
| import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; |
| import org.eclipse.jetty.websocket.servlet.WebSocketCreator; |
| |
| /** |
| * Interface for Configuring Jetty Server Native WebSockets |
| * <p> |
| * Only applicable if using {@link WebSocketUpgradeFilter} |
| * </p> |
| */ |
| public class NativeWebSocketConfiguration extends ContainerLifeCycle implements MappedWebSocketCreator, Dumpable |
| { |
| private final WebSocketServerFactory factory; |
| private final PathMappings<WebSocketCreator> mappings = new PathMappings<>(); |
| |
| public NativeWebSocketConfiguration(ServletContext context) |
| { |
| this(new WebSocketServerFactory(context)); |
| } |
| |
| public NativeWebSocketConfiguration(WebSocketServerFactory webSocketServerFactory) |
| { |
| this.factory = webSocketServerFactory; |
| addBean(this.factory); |
| } |
| |
| @Override |
| public void doStop() throws Exception |
| { |
| mappings.removeIf((mapped) -> !(mapped.getResource() instanceof PersistedWebSocketCreator)); |
| super.doStop(); |
| } |
| |
| @Override |
| public String dump() |
| { |
| return ContainerLifeCycle.dump(this); |
| } |
| |
| @Override |
| public void dump(Appendable out, String indent) throws IOException |
| { |
| // TODO: show factory/mappings ? |
| mappings.dump(out, indent); |
| } |
| |
| /** |
| * Get WebSocketServerFactory being used. |
| * |
| * @return the WebSocketServerFactory being used. |
| */ |
| public WebSocketServerFactory getFactory() |
| { |
| return this.factory; |
| } |
| |
| /** |
| * Get the matching {@link MappedResource} for the provided target. |
| * |
| * @param target the target path |
| * @return the matching resource, or null if no match. |
| */ |
| public MappedResource<WebSocketCreator> getMatch(String target) |
| { |
| return this.mappings.getMatch(target); |
| } |
| |
| /** |
| * Used to configure the Default {@link WebSocketPolicy} used by all endpoints that |
| * don't redeclare the values. |
| * |
| * @return the default policy for all WebSockets |
| */ |
| public WebSocketPolicy getPolicy() |
| { |
| return this.factory.getPolicy(); |
| } |
| |
| /** |
| * Manually add a WebSocket mapping. |
| * <p> |
| * If mapping is added before this configuration is started, then it is persisted through |
| * stop/start of this configuration's lifecycle. Otherwise it will be removed when |
| * this configuration is stopped. |
| * </p> |
| * |
| * @param pathSpec the pathspec to respond on |
| * @param creator the websocket creator to activate on the provided mapping. |
| */ |
| public void addMapping(PathSpec pathSpec, WebSocketCreator creator) |
| { |
| WebSocketCreator wsCreator = creator; |
| if (!isRunning()) |
| { |
| wsCreator = new PersistedWebSocketCreator(creator); |
| } |
| mappings.put(pathSpec, wsCreator); |
| } |
| |
| /** |
| * Manually add a WebSocket mapping. |
| * |
| * @param spec the pathspec to respond on |
| * @param creator the websocket creator to activate on the provided mapping |
| * @deprecated use {@link #addMapping(PathSpec, WebSocketCreator)} instead. |
| */ |
| @Deprecated |
| public void addMapping(org.eclipse.jetty.websocket.server.pathmap.PathSpec spec, WebSocketCreator creator) |
| { |
| if (spec instanceof org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec) |
| { |
| addMapping(new ServletPathSpec(spec.getSpec()), creator); |
| } |
| else if (spec instanceof org.eclipse.jetty.websocket.server.pathmap.RegexPathSpec) |
| { |
| addMapping(new RegexPathSpec(spec.getSpec()), creator); |
| } |
| else |
| { |
| throw new RuntimeException("Unsupported (Deprecated) PathSpec implementation type: " + spec.getClass().getName()); |
| } |
| } |
| |
| /** |
| * Manually add a WebSocket mapping. |
| * |
| * @param pathSpec the pathspec to respond on |
| * @param endpointClass the endpoint class to use for new upgrade requests on the provided |
| * pathspec (can be an {@link org.eclipse.jetty.websocket.api.annotations.WebSocket} annotated |
| * POJO, or implementing {@link org.eclipse.jetty.websocket.api.WebSocketListener}) |
| */ |
| public void addMapping(PathSpec pathSpec, final Class<?> endpointClass) |
| { |
| mappings.put(pathSpec, (req, resp) -> |
| { |
| try |
| { |
| return endpointClass.newInstance(); |
| } |
| catch (InstantiationException | IllegalAccessException e) |
| { |
| throw new WebSocketException("Unable to create instance of " + endpointClass.getName(), e); |
| } |
| }); |
| } |
| |
| |
| @Override |
| public void addMapping(String rawspec, WebSocketCreator creator) |
| { |
| PathSpec spec = toPathSpec(rawspec); |
| addMapping(spec, creator); |
| } |
| |
| private PathSpec toPathSpec(String rawspec) |
| { |
| // Determine what kind of path spec we are working with |
| if (rawspec.charAt(0) == '/' || rawspec.startsWith("*.") || rawspec.startsWith("servlet|")) |
| { |
| return new ServletPathSpec(rawspec); |
| } |
| else if (rawspec.charAt(0) == '^' || rawspec.startsWith("regex|")) |
| { |
| return new RegexPathSpec(rawspec); |
| } |
| else if (rawspec.startsWith("uri-template|")) |
| { |
| return new UriTemplatePathSpec(rawspec.substring("uri-template|".length())); |
| } |
| |
| // TODO: add ability to load arbitrary jetty-http PathSpec implementation |
| // TODO: perhaps via "fully.qualified.class.name|spec" style syntax |
| |
| throw new IllegalArgumentException("Unrecognized path spec syntax [" + rawspec + "]"); |
| } |
| |
| @Override |
| public WebSocketCreator getMapping(String rawspec) |
| { |
| PathSpec pathSpec = toPathSpec(rawspec); |
| for (MappedResource<WebSocketCreator> mapping : mappings) |
| { |
| if (mapping.getPathSpec().equals(pathSpec)) |
| return mapping.getResource(); |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean removeMapping(String rawspec) |
| { |
| PathSpec pathSpec = toPathSpec(rawspec); |
| boolean removed = false; |
| for (Iterator<MappedResource<WebSocketCreator>> iterator = mappings.iterator(); iterator.hasNext(); ) |
| { |
| MappedResource<WebSocketCreator> mapping = iterator.next(); |
| if (mapping.getPathSpec().equals(pathSpec)) |
| { |
| iterator.remove(); |
| removed = true; |
| } |
| } |
| return removed; |
| } |
| |
| /** |
| * Manually add a WebSocket mapping. |
| * |
| * @param rawspec the pathspec to map to (see {@link MappedWebSocketCreator#addMapping(String, WebSocketCreator)} for syntax details) |
| * @param endpointClass the endpoint class to use for new upgrade requests on the provided |
| * pathspec (can be an {@link org.eclipse.jetty.websocket.api.annotations.WebSocket} annotated |
| * POJO, or implementing {@link org.eclipse.jetty.websocket.api.WebSocketListener}) |
| */ |
| public void addMapping(String rawspec, final Class<?> endpointClass) |
| { |
| PathSpec pathSpec = toPathSpec(rawspec); |
| addMapping(pathSpec, endpointClass); |
| } |
| |
| private class PersistedWebSocketCreator implements WebSocketCreator |
| { |
| private final WebSocketCreator delegate; |
| |
| public PersistedWebSocketCreator(WebSocketCreator delegate) |
| { |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) |
| { |
| return delegate.createWebSocket(req, resp); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "Persisted[" + super.toString() + "]"; |
| } |
| } |
| } |