org.apache.wicket.protocol.ws.WebSocketSettings.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.protocol.ws.WebSocketSettings.java

Source

/*
 * 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 org.apache.wicket.protocol.ws;

import org.apache.wicket.Application;
import org.apache.wicket.MetaDataKey;
import org.apache.wicket.Page;
import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
import org.apache.wicket.protocol.ws.api.IWebSocketConnectionFilter;
import org.apache.wicket.protocol.ws.api.ServletRequestCopy;
import org.apache.wicket.protocol.ws.api.WebSocketConnectionFilterCollection;
import org.apache.wicket.protocol.ws.api.WebSocketRequest;
import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
import org.apache.wicket.protocol.ws.api.WebSocketResponse;
import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
import org.apache.wicket.protocol.ws.api.registry.SimpleWebSocketConnectionRegistry;
import org.apache.wicket.protocol.ws.concurrent.Executor;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

/**
 * Web Socket related settings.
 *
 * More documentation is available about each setting in the setter method for the property.
 */
public class WebSocketSettings {
    private static final Logger LOG = LoggerFactory.getLogger(WebSocketSettings.class);

    private static final MetaDataKey<WebSocketSettings> KEY = new MetaDataKey<WebSocketSettings>() {
    };

    /**
     * A flag indicating whether JavaxWebSocketFilter is in use.
     * When using JSR356 based implementations the ws:// url should not
     * use the WicketFilter's filterPath because JSR356 Upgrade connections
     * are never passed to the Servlet Filters.
     */
    private static boolean USING_JAVAX_WEB_SOCKET = false;
    static {
        try {
            Class.forName("org.apache.wicket.protocol.ws.javax.JavaxWebSocketFilter");
            USING_JAVAX_WEB_SOCKET = true;
            LOG.debug("Using JSR356 Native WebSocket implementation!");
        } catch (ClassNotFoundException e) {
            LOG.debug("Using non-JSR356 Native WebSocket implementation!");
        }
    }

    private final AtomicReference<CharSequence> filterPrefix = new AtomicReference<>();
    private final AtomicReference<CharSequence> contextPath = new AtomicReference<>();
    private final AtomicReference<CharSequence> baseUrl = new AtomicReference<>();
    private final AtomicInteger port = new AtomicInteger();
    private final AtomicInteger securePort = new AtomicInteger();

    /**
     * Holds this WebSocketSettings in the Application's metadata.
     * This way wicket-core module doesn't have reference to wicket-native-websocket.
     */
    public static final class Holder {
        public static WebSocketSettings get(Application application) {
            WebSocketSettings settings = application.getMetaData(KEY);
            if (settings == null) {
                synchronized (application) {
                    if (settings == null) {
                        settings = new WebSocketSettings();
                        set(application, settings);
                    }
                }
            }
            return settings;
        }

        public static void set(Application application, WebSocketSettings settings) {
            application.setMetaData(KEY, settings);
        }
    }

    /**
     * The executor that handles the processing of Web Socket push message broadcasts.
     */
    private Executor webSocketPushMessageExecutor = new SameThreadExecutor();

    /**
     * The executor that handles broadcast of the {@link org.apache.wicket.protocol.ws.api.event.WebSocketPayload}
     * via Wicket's event bus.
     */
    private Executor sendPayloadExecutor = new SameThreadExecutor();

    /**
     * Tracks all currently connected WebSocket clients
     */
    private IWebSocketConnectionRegistry connectionRegistry = new SimpleWebSocketConnectionRegistry();

    /**
     * A filter that may reject an incoming connection
     */
    private IWebSocketConnectionFilter connectionFilter;

    /**
     * A function that decides whether to notify the page/resource on
     * web socket connection closed event.
     * The page notification leads to deserialization of the page instance from
     * the page store and sometimes this is not wanted.
     */
    private Function<Integer, Boolean> notifyOnCloseEvent = (code) -> true;

    public boolean shouldNotifyOnCloseEvent(int closeCode) {
        return notifyOnCloseEvent == null || notifyOnCloseEvent.apply(closeCode);
    }

    public void setNotifyOnCloseEvent(Function<Integer, Boolean> notifyOnCloseEvent) {
        this.notifyOnCloseEvent = notifyOnCloseEvent;
    }

    /**
     * Set the executor for processing websocket push messages broadcasted to all sessions.
     * Default executor does all the processing in the caller thread. Using a proper thread pool is adviced
     * for applications that send push events from ajax calls to avoid page level deadlocks.
     *
     * @param executor
     *            The executor used for processing push messages.
     */
    public WebSocketSettings setWebSocketPushMessageExecutor(Executor executor) {
        Args.notNull(executor, "executor");
        this.webSocketPushMessageExecutor = executor;
        return this;
    }

    /**
     * @return the executor for processing websocket push messages broadcasted to all sessions.
     */
    public Executor getWebSocketPushMessageExecutor() {
        return webSocketPushMessageExecutor;
    }

    /**
     * @return The registry that tracks all currently connected WebSocket clients
     */
    public IWebSocketConnectionRegistry getConnectionRegistry() {
        return connectionRegistry;
    }

    /**
     * Sets the connection registry
     *
     * @param connectionRegistry
     *              The registry that tracks all currently connected WebSocket clients
     * @return {@code this}, for method chaining
     */
    public WebSocketSettings setConnectionRegistry(IWebSocketConnectionRegistry connectionRegistry) {
        Args.notNull(connectionRegistry, "connectionRegistry");
        this.connectionRegistry = connectionRegistry;
        return this;
    }

    /**
     * The executor that broadcasts the {@link org.apache.wicket.protocol.ws.api.event.WebSocketPayload}
     * via Wicket's event bus.
     * Default executor does all the processing in the caller thread.
     *
     * @param sendPayloadExecutor
     *            The executor used for broadcasting the events with web socket payloads to
     *            {@link org.apache.wicket.protocol.ws.api.WebSocketBehavior}s and
     *            {@link org.apache.wicket.protocol.ws.api.WebSocketResource}s.
     */
    public WebSocketSettings setSendPayloadExecutor(Executor sendPayloadExecutor) {
        Args.notNull(sendPayloadExecutor, "sendPayloadExecutor");
        this.sendPayloadExecutor = sendPayloadExecutor;
        return this;
    }

    /**
     * The executor that broadcasts the {@link org.apache.wicket.protocol.ws.api.event.WebSocketPayload}
     * via Wicket's event bus.
     *
     * @return
     *            The executor used for broadcasting the events with web socket payloads to
     *            {@link org.apache.wicket.protocol.ws.api.WebSocketBehavior}s and
     *            {@link org.apache.wicket.protocol.ws.api.WebSocketResource}s.
     */
    public Executor getSendPayloadExecutor() {
        return sendPayloadExecutor;
    }

    /**
     * Sets the filter for checking the incoming connections
     * @param connectionFilter
     *              the filter for checking the incoming connections
     * @see WebSocketConnectionFilterCollection
     */
    public void setConnectionFilter(IWebSocketConnectionFilter connectionFilter) {
        this.connectionFilter = connectionFilter;
    }

    /**
     * @return the filter for checking the incoming connections
     * @see WebSocketConnectionFilterCollection
     */
    public IWebSocketConnectionFilter getConnectionFilter() {
        return this.connectionFilter;
    }

    /**
     * A factory method for the {@link org.apache.wicket.request.http.WebResponse}
     * that should be used to write the response back to the client/browser
     *
     * @param connection
     *              The active web socket connection
     * @return the response object that should be used to write the response back to the client
     */
    public WebResponse newWebSocketResponse(IWebSocketConnection connection) {
        return new WebSocketResponse(connection);
    }

    /**
     * A factory method for creating instances of {@link org.apache.wicket.protocol.ws.api.WebSocketRequestHandler}
     * for processing a web socket request
     *
     * @param page
     *          The page with the web socket client. A dummy page in case of usage of
     *          {@link org.apache.wicket.protocol.ws.api.WebSocketResource}
     * @param connection
     *          The active web socket connection
     * @return a new instance of WebSocketRequestHandler for processing a web socket request
     */
    public WebSocketRequestHandler newWebSocketRequestHandler(Page page, IWebSocketConnection connection) {
        return new WebSocketRequestHandler(page, connection);
    }

    /**
     * A factory method for the {@link org.apache.wicket.request.http.WebRequest}
     * that should be used in the WebSocket processing request cycle
     *
     * @param request
     *              The upgraded http request
     * @param filterPath
     *              The configured filter path of WicketFilter in web.xml
     * @return the request object that should be used in the WebSocket processing request cycle
     */
    public WebRequest newWebSocketRequest(HttpServletRequest request, String filterPath) {
        return new WebSocketRequest(new ServletRequestCopy(request), filterPath);
    }

    public void setFilterPrefix(final CharSequence filterPrefix) {
        this.filterPrefix.set(filterPrefix);
    }

    public CharSequence getFilterPrefix() {
        if (filterPrefix.get() == null) {
            if (USING_JAVAX_WEB_SOCKET) {
                filterPrefix.compareAndSet(null, "");
            } else {
                filterPrefix.compareAndSet(null, RequestCycle.get().getRequest().getFilterPath());
            }
        }
        return filterPrefix.get();
    }

    public void setContextPath(final CharSequence contextPath) {
        this.contextPath.set(contextPath);
    }

    public CharSequence getContextPath() {
        contextPath.compareAndSet(null, RequestCycle.get().getRequest().getContextPath());
        return contextPath.get();
    }

    public void setBaseUrl(final CharSequence baseUrl) {
        this.baseUrl.set(baseUrl);
    }

    public CharSequence getBaseUrl() {
        if (baseUrl.get() == null) {
            Url _baseUrl = RequestCycle.get().getUrlRenderer().getBaseUrl();
            return Strings.escapeMarkup(_baseUrl.toString());
        }
        return baseUrl.get();
    }

    /**
     * Sets the port that should be used for <code>ws:</code> connections.
     * If unset then the current HTTP port will be used.
     *
     * @param wsPort The custom port for WS connections
     */
    public void setPort(int wsPort) {
        this.port.set(wsPort);
    }

    /**
     * @return The custom port for WS connections
     */
    public Integer getPort() {
        return port.get();
    }

    /**
     * Sets the port that should be used for <code>wss:</code> connections.
     * If unset then the current HTTPS port will be used.
     *
     * @param wssPort The custom port for WSS connections
     */
    public void setSecurePort(int wssPort) {
        this.securePort.set(wssPort);
    }

    /**
     * @return The custom port for WSS connections
     */
    public Integer getSecurePort() {
        return securePort.get();
    }

    /**
     * Simple executor that runs the tasks in the caller thread.
     */
    public static class SameThreadExecutor implements Executor {
        @Override
        public void run(Runnable command) {
            command.run();
        }
    }
}