org.silverpeas.core.notification.sse.SilverpeasAsyncContext.java Source code

Java tutorial

Introduction

Here is the source code for org.silverpeas.core.notification.sse.SilverpeasAsyncContext.java

Source

/*
 * Copyright (C) 2000 - 2018 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.core.notification.sse;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.util.logging.SilverLogger;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE;
import static org.silverpeas.core.notification.sse.ServerEventDispatcherTask.unregisterAsyncContext;

/**
 * This is a wrap of a {@link AsyncContext} instance.
 * @author Yohann Chastagnier
 */
public class SilverpeasAsyncContext implements AsyncContext {

    private final AsyncContext wrappedInstance;
    private final String sessionId;
    private final User user;
    private Long lastServerEventId;
    private boolean heartbeat = false;
    private boolean complete = false;
    private boolean timeout = false;
    private boolean error = false;

    /**
     * Hidden constructor.
     */
    private SilverpeasAsyncContext(final AsyncContext wrappedInstance, final String sessionId, final User user) {
        this.wrappedInstance = wrappedInstance;
        this.sessionId = sessionId;
        this.user = user;
    }

    /**
     * Wraps the given instance. Nothing is wrapped if the given instance is a wrapped one.
     * @param silverLogger the sse silverpeas logger.
     * @param asyncContext the instance to wrap.
     * @param userSessionId the identifier or the user session.
     * @param user the identifier of th user linked to the async request.
     * @return the wrapped given instance.
     */
    public static SilverpeasAsyncContext wrap(final SilverLogger silverLogger, AsyncContext asyncContext,
            final String userSessionId, User user) {
        if (asyncContext instanceof SilverpeasAsyncContext) {
            return (SilverpeasAsyncContext) asyncContext;
        }

        final SilverpeasAsyncContext context = new SilverpeasAsyncContext(asyncContext, userSessionId, user);
        final ServletRequest request = asyncContext.getRequest();
        final ServletResponse response = asyncContext.getResponse();
        context.addListener(new SilverpeasAsyncListener(silverLogger, context), request, response);
        return context;
    }

    /**
     * Gets the mutex which is the instance itself.
     * @return the mutex.
     */
    public Object getMutex() {
        return this;
    }

    /**
     * Indicates if the send is possible according to the state of the context.
     * @return true if send is possible, false otherwise.
     */
    boolean isSendPossible() {
        return !isComplete() && !isTimeout() && !isError();
    }

    /**
     * Gets the request URI behind the async context.
     * @return a request URI as string.
     */
    public String getRequestURI() {
        return ((HttpServletRequest) getRequest()).getRequestURI();
    }

    /**
     * Gets the session if linked to this async request.
     * @return a session identifier as string.
     */
    public String getSessionId() {
        return sessionId;
    }

    /**
     * Gets the user identifier linked to this async request.
     * @return a user identifier as string.
     */
    public User getUser() {
        return user;
    }

    /**
     * Gets the last server event identifier known before a network breakdown.
     * @return an identifier as string.
     */
    Long getLastServerEventId() {
        synchronized (this) {
            return lastServerEventId;
        }
    }

    /**
     * Sets the last server event identifier.
     * @param lastServerEventId a last event identifier as long, can be null.
     */
    public void setLastServerEventId(final Long lastServerEventId) {
        synchronized (this) {
            this.lastServerEventId = lastServerEventId;
        }
    }

    @Override
    public ServletRequest getRequest() {
        return wrappedInstance.getRequest();
    }

    @Override
    public ServletResponse getResponse() {
        return wrappedInstance.getResponse();
    }

    @Override
    public boolean hasOriginalRequestAndResponse() {
        return wrappedInstance.hasOriginalRequestAndResponse();
    }

    @Override
    public void dispatch() {
        wrappedInstance.dispatch();
    }

    @Override
    public void dispatch(String path) {
        wrappedInstance.dispatch(path);
    }

    @Override
    public void dispatch(ServletContext context, String path) {
        wrappedInstance.dispatch(context, path);
    }

    @Override
    public void complete() {
        setComplete(true);
        wrappedInstance.complete();
        unregisterAsyncContext(this);
    }

    boolean isHeartbeat() {
        synchronized (getMutex()) {
            return heartbeat;
        }
    }

    public void setHeartbeat(final boolean heartbeat) {
        synchronized (getMutex()) {
            this.heartbeat = heartbeat;
        }
    }

    boolean isComplete() {
        synchronized (getMutex()) {
            return complete;
        }
    }

    void setComplete(final boolean complete) {
        synchronized (getMutex()) {
            this.complete = complete;
        }
    }

    boolean isTimeout() {
        synchronized (getMutex()) {
            return timeout;
        }
    }

    void setTimeout(final boolean timeout) {
        synchronized (getMutex()) {
            this.timeout = timeout;
        }
    }

    boolean isError() {
        synchronized (getMutex()) {
            return error;
        }
    }

    void setError(final boolean error) {
        synchronized (getMutex()) {
            this.error = error;
        }
    }

    @Override
    public void start(Runnable run) {
        wrappedInstance.start(run);
    }

    @Override
    public void addListener(AsyncListener listener) {
        wrappedInstance.addListener(listener);
    }

    @Override
    public void addListener(AsyncListener listener, ServletRequest servletRequest,
            ServletResponse servletResponse) {
        wrappedInstance.addListener(listener, servletRequest, servletResponse);
    }

    @Override
    public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException {
        return wrappedInstance.createListener(clazz);
    }

    @Override
    public long getTimeout() {
        return wrappedInstance.getTimeout();
    }

    @Override
    public void setTimeout(long timeout) {
        wrappedInstance.setTimeout(timeout);
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(final Object obj) {
        return super.equals(obj);
    }

    @Override
    public String toString() {
        ToStringBuilder tsb = new ToStringBuilder(this, SHORT_PREFIX_STYLE);
        tsb.append("on", getRequestURI());
        tsb.append("sessionId", getSessionId());
        tsb.append("userId", getUser().getId());
        tsb.append("timeout", getTimeout());
        if (getLastServerEventId() != null) {
            tsb.append("lastServerEventId", getLastServerEventId());
        }
        return tsb.toString();
    }

    private static class SilverpeasAsyncListener implements AsyncListener {
        private SilverLogger silverLogger;
        private final SilverpeasAsyncContext context;

        SilverpeasAsyncListener(final SilverLogger silverLogger, final SilverpeasAsyncContext context) {
            this.silverLogger = silverLogger;
            this.context = context;
        }

        @Override
        public void onComplete(final AsyncEvent event) throws IOException {
            context.setComplete(true);
            silverLogger.debug("Async context is completed ({0})", context);
            unregisterAsyncContext(context);
        }

        @Override
        public void onTimeout(final AsyncEvent event) throws IOException {
            context.setTimeout(true);
            silverLogger.debug("Async context is timed out ({0})", context);
            unregisterAsyncContext(context);
        }

        @Override
        public void onError(final AsyncEvent event) throws IOException {
            context.setError(true);
            silverLogger.debug("Async context thrown an error ({0})", context);
            unregisterAsyncContext(context);
        }

        @Override
        public void onStartAsync(final AsyncEvent event) throws IOException {
            silverLogger.debug("Async context is starting ({0})", context);
        }
    }
}