001package org.nuxeo.ecm.core.redis.contribs;/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Delbosc Benoit
016 */
017
018import org.apache.commons.codec.binary.Base64;
019import org.nuxeo.ecm.core.storage.sql.Invalidations;
020
021import java.io.ByteArrayInputStream;
022import java.io.ByteArrayOutputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.ObjectInputStream;
026import java.io.ObjectOutputStream;
027
028/**
029 * invalidations and nodeId serializer/unserializer.
030 *
031 * @since 7.4
032 */
033public class RedisInvalidations {
034    private String nodeId;
035    private Invalidations invalidations;
036
037    public RedisInvalidations(String nodeId, Invalidations invals) {
038        assert nodeId != null : "nodeId required";
039        assert invals != null : "invals required";
040        this.nodeId = nodeId;
041        this.invalidations = invals;
042    }
043
044    public RedisInvalidations(String receiverNodeId, String message) {
045        assert receiverNodeId != null : "receiverNodeId required";
046        if (message == null || !message.contains(":")) {
047            throw new IllegalArgumentException("Invalid message: " + message);
048        }
049        String[] parts = message.split(":", 2);
050        nodeId = parts[0];
051        if (!receiverNodeId.equals(nodeId)) {
052            // only decode if it is a remote node
053            invalidations = deserializeInvalidations(parts[1]);
054        }
055    }
056
057    private Invalidations deserializeInvalidations(String invalsStr) {
058        InputStream bain = new ByteArrayInputStream(Base64.decodeBase64(invalsStr));
059        try (ObjectInputStream in = new ObjectInputStream(bain)) {
060            return (Invalidations) in.readObject();
061        } catch (IOException | ClassNotFoundException cause) {
062            throw new IllegalArgumentException("Cannot deserialize invalidations", cause);
063        }
064    }
065
066    public Invalidations getInvalidations() {
067        return invalidations;
068    }
069
070    public String serialize() throws IOException {
071        return nodeId + ":" + serializeInvalidations(invalidations);
072    }
073
074    private String serializeInvalidations(Invalidations invals) throws IOException {
075        ByteArrayOutputStream baout = new ByteArrayOutputStream();
076        ObjectOutputStream out = new ObjectOutputStream(baout);
077        out.writeObject(invals);
078        out.flush();
079        out.close();
080        // use base64 because Jedis don't have onMessage with bytes
081        return Base64.encodeBase64String(baout.toByteArray());
082    }
083
084    @Override
085    public String toString() {
086        if (invalidations == null) {
087            return "RedisInvalidationsInvalidations(local, discarded)";
088        }
089        return "RedisInvalidationsInvalidations(fromNode=" + nodeId + ", " + invalidations.toString() + ")";
090    }
091
092}