All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.collaborationengine.BeaconHandler Maven / Gradle / Ivy

/*
 * Copyright 2020-2022 Vaadin Ltd.
 *
 * This program is available under Commercial Vaadin Runtime License 1.0
 * (CVRLv1).
 *
 * For the full License, see http://vaadin.com/license/cvrl-1
 */
package com.vaadin.collaborationengine;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.server.Command;
import com.vaadin.flow.server.SynchronizedRequestHandler;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.VaadinResponse;
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.shared.ApplicationConstants;
import com.vaadin.flow.shared.Registration;

/**
 * A {@link SynchronizedRequestHandler} which notifies its listeners when the
 * browser tab is closed.
 *
 * @author Vaadin Ltd
 * @since 1.0
 */
class BeaconHandler extends SynchronizedRequestHandler {
    private static final String ID_PARAMETER = "id";
    private static final String REQUEST_TYPE = "beacon";

    private final String id = UUID.randomUUID().toString();
    private final List listeners;
    private final String beaconPath;

    public BeaconHandler(String beaconPath) {
        listeners = new ArrayList<>();
        this.beaconPath = beaconPath;
    }

    static BeaconHandler ensureInstalled(UI ui, String beaconPath) {
        BeaconHandler beaconHandler = ComponentUtil.getData(ui,
                BeaconHandler.class);
        if (beaconHandler != null) {
            // Already installed, return the existing handler
            return beaconHandler;
        }

        BeaconHandler newBeaconHandler = new BeaconHandler(beaconPath);

        ui.getElement().executeJs(getUnloadScript(),
                newBeaconHandler.createBeaconUrl());

        VaadinSession session = ui.getSession();
        session.addRequestHandler(newBeaconHandler);
        ComponentUtil.setData(ui, BeaconHandler.class, newBeaconHandler);

        ui.addDetachListener(
                detachEvent -> session.removeRequestHandler(newBeaconHandler));
        return newBeaconHandler;
    }

    String createBeaconUrl() {
        String requestTypeParameter = formatParameter(
                ApplicationConstants.REQUEST_TYPE_PARAMETER, REQUEST_TYPE);
        String beaconIdParameter = formatParameter(ID_PARAMETER, id);
        return "." + beaconPath + "?" + requestTypeParameter + "&"
                + beaconIdParameter;

    }

    private static String formatParameter(String name, String value) {
        return name + "=" + value;
    }

    private static String getUnloadScript() {
        //@formatter:off
        return "window.addEventListener('unload', function() {"
                + "  if (navigator.sendBeacon) {"
                + "    navigator.sendBeacon($0);"
                + "  } else {"
                + "    var xhr = new XMLHttpRequest();"
                + "    xhr.open(\"POST\", $0, false);"
                + "    xhr.send(\"\");"
                + "  }"
                + "})";
        //@formatter:on
    }

    @Override
    protected boolean canHandleRequest(VaadinRequest request) {
        if (!beaconPath.equals(request.getPathInfo())) {
            return false;
        }
        String requestType = request
                .getParameter(ApplicationConstants.REQUEST_TYPE_PARAMETER);
        String beaconId = request.getParameter(ID_PARAMETER);
        return REQUEST_TYPE.equals(requestType) && id.equals(beaconId);
    }

    @Override
    public boolean synchronizedHandleRequest(VaadinSession session,
            VaadinRequest request, VaadinResponse response) {
        new ArrayList<>(listeners).forEach(Command::execute);
        return true;
    }

    Registration addListener(Command listener) {
        listeners.add(listener);
        return () -> listeners.remove(listener);
    }

    List getListeners() {
        return new ArrayList<>(listeners);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy