com.cognifide.actions.msg.push.passive.PushServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.cognifide.actions.msg.push.passive.PushServlet.java

Source

/*--
 * #%L
 * Cognifide Actions
 * %%
 * Copyright (C) 2015 Cognifide
 * %%
 * Licensed 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.
 * #L%
 */

package com.cognifide.actions.msg.push.passive;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.settings.SlingSettingsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cognifide.actions.msg.push.api.PushSender;

@SlingServlet(methods = { "GET",
        "POST" }, paths = "/bin/cognifide/cq-actions", extensions = "txt", generateService = false)
@Service({ Servlet.class, PushSender.class })
public class PushServlet extends SlingAllMethodsServlet implements PushSender {

    private static final Logger LOG = LoggerFactory.getLogger(PushServlet.class);

    private static final long TIMEOUT = 5 * 1000;

    private static final long serialVersionUID = 661757446730532042L;

    private final Set<String> sentMessages = Collections.synchronizedSet(new HashSet<String>());

    private final Set<String> receivedConfirmations = Collections.synchronizedSet(new HashSet<String>());

    private Object connectionHold;

    private PrintWriter writer;

    @Reference
    private SlingSettingsService slingSettings;

    @Activate
    public void activate() {
        connectionHold = new Object();
    }

    @Deactivate
    public void deactivate() {
        synchronized (connectionHold) {
            connectionHold.notify();
        }
        connectionHold = null;
        writer = null;
        synchronized (receivedConfirmations) {
            receivedConfirmations.notifyAll();
        }
    }

    public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        if (!authenticate(request, response)) {
            return;
        }
        response.setContentType("text/plain; charset=utf-8");

        synchronized (connectionHold) {
            connectionHold.notifyAll();
        }
        synchronized (this) {
            writer = response.getWriter();
        }
        try {
            synchronized (connectionHold) {
                connectionHold.wait();
            }
        } catch (InterruptedException e) {
            throw new ServletException("Interrupted", e);
        }
    }

    public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        if (!authenticate(request, response)) {
            return;
        }

        final String msgId = StringUtils.removeStart(request.getRequestPathInfo().getSuffix(), "/");
        if (StringUtils.isBlank(msgId)) {
            throw new ServletException("No suffix found");
        }
        if (!sentMessages.contains(msgId)) {
            throw new ServletException("No one is waiting for confirmation for " + msgId);
        } else {
            receivedConfirmations.add(msgId);
            synchronized (receivedConfirmations) {
                receivedConfirmations.notifyAll();
            }
            response.getWriter().append("Message " + msgId + " confirmed");
            LOG.debug("Message " + msgId + " confirmed");
        }
    }

    @Override
    public boolean sendMessage(String topic, String msg) {
        final String msgId;
        synchronized (this) {
            if (msg.contains("\n")) {
                throw new IllegalArgumentException("Message can't contain new line character");
            }
            if (writer == null || writer.checkError()) {
                return false;
            }
            msgId = UUID.randomUUID().toString();
            sentMessages.add(msgId);
            writer.println(msgId);
            writer.println(topic);
            writer.println(msg);
            writer.flush();
            if (writer.checkError()) {
                sentMessages.remove(msgId);
                return false;
            }
        }

        LOG.debug("Waiting for confirmation for " + msgId);
        try {
            final long start = System.currentTimeMillis();
            while (!receivedConfirmations.remove(msgId)) {
                final long elapsed = System.currentTimeMillis() - start;
                if (elapsed > TIMEOUT || connectionHold == null) {
                    return false;
                }
                synchronized (receivedConfirmations) {
                    receivedConfirmations.wait(TIMEOUT - elapsed);
                }
            }
        } catch (InterruptedException e) {
            return false;
        } finally {
            sentMessages.remove(msgId);
        }
        return true;
    }

    private boolean isPublish() {
        return slingSettings.getRunModes().contains("publish");
    }

    private boolean authenticate(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws IOException {
        final String userId = request.getResourceResolver().getUserID();
        if (!isPublish()) {
            response.sendError(404);
            return false;
        } else if (!"admin".equals(userId)) {
            response.sendError(403);
            return false;
        } else {
            return true;
        }
    }
}