001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Delbosc Benoit
018 */
019package org.nuxeo.ecm.core.redis.contribs;
020
021import org.apache.commons.codec.binary.Base64;
022import org.nuxeo.ecm.core.storage.sql.Invalidations;
023
024import java.io.ByteArrayInputStream;
025import java.io.ByteArrayOutputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.ObjectInputStream;
029import java.io.ObjectOutputStream;
030
031/**
032 * invalidations and nodeId serializer/unserializer.
033 *
034 * @since 7.4
035 */
036public class RedisInvalidations {
037    private String nodeId;
038    private Invalidations invalidations;
039
040    public RedisInvalidations(String nodeId, Invalidations invals) {
041        assert nodeId != null : "nodeId required";
042        assert invals != null : "invals required";
043        this.nodeId = nodeId;
044        this.invalidations = invals;
045    }
046
047    public RedisInvalidations(String receiverNodeId, String message) {
048        assert receiverNodeId != null : "receiverNodeId required";
049        if (message == null || !message.contains(":")) {
050            throw new IllegalArgumentException("Invalid message: " + message);
051        }
052        String[] parts = message.split(":", 2);
053        nodeId = parts[0];
054        if (!receiverNodeId.equals(nodeId)) {
055            // only decode if it is a remote node
056            invalidations = deserializeInvalidations(parts[1]);
057        }
058    }
059
060    private Invalidations deserializeInvalidations(String invalsStr) {
061        InputStream bain = new ByteArrayInputStream(Base64.decodeBase64(invalsStr));
062        try (ObjectInputStream in = new ObjectInputStream(bain)) {
063            return (Invalidations) in.readObject();
064        } catch (IOException | ClassNotFoundException cause) {
065            throw new IllegalArgumentException("Cannot deserialize invalidations", cause);
066        }
067    }
068
069    public Invalidations getInvalidations() {
070        return invalidations;
071    }
072
073    public String serialize() throws IOException {
074        return nodeId + ":" + serializeInvalidations(invalidations);
075    }
076
077    private String serializeInvalidations(Invalidations invals) throws IOException {
078        ByteArrayOutputStream baout = new ByteArrayOutputStream();
079        ObjectOutputStream out = new ObjectOutputStream(baout);
080        out.writeObject(invals);
081        out.flush();
082        out.close();
083        // use base64 because Jedis don't have onMessage with bytes
084        return Base64.encodeBase64String(baout.toByteArray());
085    }
086
087    @Override
088    public String toString() {
089        if (invalidations == null) {
090            return "RedisInvalidationsInvalidations(local, discarded)";
091        }
092        return "RedisInvalidationsInvalidations(fromNode=" + nodeId + ", " + invalidations.toString() + ")";
093    }
094
095}