ReplicationServlet.java 11.9 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Session
 *  javax.servlet.ServletException
 *  javax.servlet.ServletInputStream
 *  javax.servlet.ServletOutputStream
 *  org.apache.commons.io.IOUtils
 *  org.apache.commons.io.output.NullOutputStream
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.sling.SlingServlet
 *  org.apache.jackrabbit.util.ISO8601
 *  org.apache.jackrabbit.util.Text
 *  org.apache.sling.api.SlingHttpServletRequest
 *  org.apache.sling.api.SlingHttpServletResponse
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.apache.sling.api.servlets.SlingAllMethodsServlet
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.replication.impl.servlets;

import com.day.cq.replication.AccessDeniedException;
import com.day.cq.replication.Outbox;
import com.day.cq.replication.OutboxManager;
import com.day.cq.replication.ReplicationAction;
import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.ReplicationException;
import com.day.cq.replication.ReplicationReceiver;
import com.day.cq.replication.impl.OutboxImpl;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Calendar;
import javax.jcr.Session;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.jackrabbit.util.ISO8601;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SlingServlet(paths={"/bin/receive"}, methods={"GET", "POST"})
public class ReplicationServlet
extends SlingAllMethodsServlet {
    private final Logger logger;
    private static final String NO_INSTALL = "noinstall";
    private static final String PN_TIMELINE = "timeline";
    private static final String PN_OUTBOX = "outbox";
    private static final String PN_SINK = "sink";
    @Reference
    protected OutboxManager outboxManager;
    @Reference
    protected ReplicationReceiver receiver;

    public ReplicationServlet() {
        this.logger = LoggerFactory.getLogger(this.getClass());
    }

    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");
        response.setCharacterEncoding("utf-8");
        try {
            ReplicationActionType actionType = ReplicationActionType.fromName(request.getHeader("Action"));
            if (actionType == null) {
                throw new ReplicationException("Illegal action: " + request.getHeader("Action"));
            }
            String path = request.getHeader("Path");
            if (path == null || path.length() == 0) {
                throw new ReplicationException("No replication path.");
            }
            path = Text.unescape((String)path);
            long start = System.currentTimeMillis();
            if ("true".equals(request.getParameter("sink"))) {
                this.logger.info("Sinking replication {} of {}", (Object)actionType, (Object)path);
                ServletInputStream in = request.getInputStream();
                IOUtils.copy((InputStream)in, (OutputStream)new NullOutputStream());
                response.getWriter().print("ReplicationAction " + (Object)((Object)actionType) + " ok.");
            } else {
                Session session = (Session)request.getResourceResolver().adaptTo(Session.class);
                String noInstall = request.getParameter("noinstall");
                boolean install = true;
                if (noInstall != null && !"".equals(noInstall)) {
                    try {
                        install = Boolean.valueOf(noInstall) == false;
                    }
                    catch (Exception e) {
                        this.logger.warn("Problem parsing {} parameter value : {}", new Object[]{"noinstall", noInstall});
                    }
                }
                String binaryLessValue = request.getParameter("binaryless");
                boolean binaryLess = false;
                if (binaryLessValue != null && !"".equals(binaryLessValue)) {
                    try {
                        binaryLess = Boolean.valueOf(binaryLessValue);
                    }
                    catch (Exception e) {
                        this.logger.warn("Problem parsing {} parameter value : {}", new Object[]{"binaryless", binaryLess});
                    }
                }
                PrintWriter out = response.getWriter();
                ServletInputStream is = request.getInputStream();
                if (path.equals("batch:")) {
                    this.logger.info("Processing batch replication");
                    StringBuilder sb = new StringBuilder();
                    byte[] lengthBytes = this.readByteArray((InputStream)is, 8);
                    long pathInfoSize = ByteBuffer.wrap(lengthBytes).order(ByteOrder.BIG_ENDIAN).getLong();
                    if (pathInfoSize > Integer.MAX_VALUE) {
                        throw new IOException("Can't process pathInfo larger than Integer.MAX_VALUE!");
                    }
                    byte[] pathInfoBytes = this.readByteArray((InputStream)is, (int)pathInfoSize);
                    Object[] paths = new String(pathInfoBytes, "UTF-8").split(":");
                    this.logger.debug("Received batch replication {}", (Object)Arrays.toString(paths));
                    for (int index = 0; index < paths.length; index += 2) {
                        String p = this.decode((String)paths[index]);
                        ReplicationActionType rType = ReplicationActionType.fromName((String)paths[index + 1]);
                        if (index > 0) {
                            sb.append(", ");
                        }
                        sb.append(p);
                        sb.append("(");
                        sb.append((Object)rType);
                        sb.append(")");
                        byte[] sizeBytes = this.readByteArray((InputStream)is, 8);
                        long size = ByteBuffer.wrap(sizeBytes).order(ByteOrder.BIG_ENDIAN).getLong();
                        this.logger.info("Processing replication: {}:{}, size: {}", new Object[]{rType, p, size});
                        ReplicationAction action = new ReplicationAction(rType, p);
                        this.receiver.receive(session, action, new LimitInputStream((InputStream)is, size), size, out, install, binaryLess);
                    }
                    IOUtils.closeQuietly((InputStream)is);
                    path = sb.toString();
                } else {
                    ReplicationAction action = new ReplicationAction(actionType, path);
                    this.receiver.receive(session, action, (InputStream)is, request.getContentLength(), out, install, binaryLess);
                }
            }
            long end = System.currentTimeMillis();
            this.logger.info("Processed replication action in {}ms: {} of {}", new Object[]{end - start, actionType, path});
        }
        catch (Exception e) {
            response.setStatus(400);
            this.logger.error("Error during replication: " + e.getMessage(), (Throwable)e);
            response.getWriter().print("error: " + e.toString());
        }
    }

    private byte[] readByteArray(InputStream is, int size) throws IOException {
        byte[] bytes = new byte[size];
        for (int read = 0; read < size; read += is.read((byte[])bytes, (int)read, (int)(size - read))) {
        }
        return bytes;
    }

    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
        ReplicationActionType action = ReplicationActionType.fromName(request.getHeader("Action"));
        if (action == ReplicationActionType.TEST) {
            response.setStatus(200);
            response.setContentType("text/plain");
            response.setCharacterEncoding("utf-8");
            response.getWriter().println("ok");
            response.flushBuffer();
            return;
        }
        Session session = (Session)request.getResourceResolver().adaptTo(Session.class);
        String outboxName = request.getParameter("outbox");
        try {
            Calendar timeline = null;
            String timelineS = request.getParameter("timeline");
            if (timelineS != null) {
                timeline = ISO8601.parse((String)timelineS);
            }
            Outbox outbox = outboxName == null || outboxName.length() == 0 ? this.outboxManager.getDefaultOutbox(session) : this.outboxManager.getOutbox(session, outboxName, false);
            response.setContentType("application/octet-stream");
            if (outbox == null) {
                this.logger.warn("Outbox '{}' does not exist. Either never created or wrongly configured", (Object)outboxName);
                OutboxImpl.writeOutbox(null, (OutputStream)response.getOutputStream());
            } else {
                outbox.fetch(timeline, (OutputStream)response.getOutputStream());
            }
        }
        catch (AccessDeniedException e) {
            response.setStatus(403);
        }
        catch (ReplicationException e) {
            response.setStatus(400);
            this.logger.error("Error while fetching outbox: " + e.getMessage(), (Throwable)e);
        }
    }

    private String decode(String value) {
        try {
            return URLDecoder.decode(value, "UTF-8");
        }
        catch (UnsupportedEncodingException var2_2) {
            return value;
        }
    }

    protected void bindOutboxManager(OutboxManager outboxManager) {
        this.outboxManager = outboxManager;
    }

    protected void unbindOutboxManager(OutboxManager outboxManager) {
        if (this.outboxManager == outboxManager) {
            this.outboxManager = null;
        }
    }

    protected void bindReceiver(ReplicationReceiver replicationReceiver) {
        this.receiver = replicationReceiver;
    }

    protected void unbindReceiver(ReplicationReceiver replicationReceiver) {
        if (this.receiver == replicationReceiver) {
            this.receiver = null;
        }
    }

    public final class LimitInputStream
    extends FilterInputStream {
        private long available;

        public LimitInputStream(InputStream in, long limit) {
            super(in);
            this.available = limit;
        }

        @Override
        public int available() throws IOException {
            return (int)Math.min((long)this.in.available(), this.available);
        }

        @Override
        public int read() throws IOException {
            if (this.available == 0) {
                return -1;
            }
            int result = this.in.read();
            if (result != -1) {
                --this.available;
            }
            return result;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            if (this.available == 0) {
                return -1;
            }
            int readLen = (int)Math.min((long)len, this.available);
            int result = this.in.read(b, off, readLen);
            if (result != -1) {
                this.available -= (long)result;
            }
            return result;
        }

        @Override
        public void close() throws IOException {
        }
    }

}