com.jivesoftware.os.tasmo.lib.process.traversal.InitiateTraversal.java Source code

Java tutorial

Introduction

Here is the source code for com.jivesoftware.os.tasmo.lib.process.traversal.InitiateTraversal.java

Source

/*
 * $Revision$
 * $Date$
 *
 * Copyright (C) 1999-$year$ Jive Software. All rights reserved.
 *
 * This software is the proprietary information of Jive Software. Use is subject to license terms.
 */
package com.jivesoftware.os.tasmo.lib.process.traversal;

import com.google.common.collect.ListMultimap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.jivesoftware.os.jive.utils.base.interfaces.CallbackStream;
import com.jivesoftware.os.jive.utils.logger.MetricLogger;
import com.jivesoftware.os.jive.utils.logger.MetricLoggerFactory;
import com.jivesoftware.os.tasmo.id.ObjectId;
import com.jivesoftware.os.tasmo.id.TenantIdAndCentricId;
import com.jivesoftware.os.tasmo.lib.concur.ConcurrencyChecker;
import com.jivesoftware.os.tasmo.lib.process.WrittenEventContext;
import com.jivesoftware.os.tasmo.lib.process.WrittenEventProcessor;
import com.jivesoftware.os.tasmo.lib.write.CommitChangeException;
import com.jivesoftware.os.tasmo.lib.write.PathId;
import com.jivesoftware.os.tasmo.lib.write.ViewFieldChange;
import com.jivesoftware.os.tasmo.model.process.WrittenEvent;
import com.jivesoftware.os.tasmo.model.process.WrittenInstance;
import com.jivesoftware.os.tasmo.reference.lib.ReferenceStore;
import com.jivesoftware.os.tasmo.reference.lib.ReferenceWithTimestamp;
import com.jivesoftware.os.tasmo.reference.lib.concur.ConcurrencyStore;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class InitiateTraversal implements WrittenEventProcessor {

    private static final MetricLogger LOG = MetricLoggerFactory.getLogger();
    private final ConcurrencyChecker concurrencyChecker;
    private final ListMultimap<InitiateTraverserKey, PathTraverser> valueTraversers;

    private final ReferenceStore referenceStore;
    private final ListMultimap<InitiateTraverserKey, PathTraverser> forwardRefTraversers;
    private final ListMultimap<InitiateTraverserKey, PathTraverser> backRefTraversers;
    private final ListeningExecutorService executorService;

    public InitiateTraversal(ListeningExecutorService executorService, ConcurrencyChecker concurrencyChecker,
            ListMultimap<InitiateTraverserKey, PathTraverser> traversers, ReferenceStore referenceStore,
            ListMultimap<InitiateTraverserKey, PathTraverser> forwardRefTraversers,
            ListMultimap<InitiateTraverserKey, PathTraverser> backRefTraversers) {
        this.executorService = executorService;
        this.concurrencyChecker = concurrencyChecker;
        this.valueTraversers = traversers;
        this.referenceStore = referenceStore;
        this.forwardRefTraversers = forwardRefTraversers;
        this.backRefTraversers = backRefTraversers;
    }

    @Override
    public void process(WrittenEventContext batchContext, TenantIdAndCentricId tenantIdAndCentricId,
            WrittenEvent writtenEvent, long threadTimestamp) throws Exception {

        if (valueTraversers != null && !valueTraversers.isEmpty()) {
            LOG.startTimer("values");
            try {
                processValues(batchContext, tenantIdAndCentricId, writtenEvent, threadTimestamp);
            } finally {
                LOG.stopTimer("values");
            }
        }

        LOG.startTimer("refs");
        try {
            processRefs(batchContext, tenantIdAndCentricId, writtenEvent, threadTimestamp);
        } finally {
            LOG.stopTimer("refs");
        }

    }

    private void processValues(final WrittenEventContext writtenEventContext,
            final TenantIdAndCentricId tenantIdAndCentricId, final WrittenEvent writtenEvent,
            final long threadTimestamp) throws Exception {

        final long timestamp = writtenEvent.getEventId();
        WrittenInstance writtenInstance = writtenEvent.getWrittenInstance();
        final ObjectId instanceId = writtenInstance.getInstanceId();

        if (writtenInstance.isDeletion()) {
            long highest = concurrencyChecker.highestVersion(tenantIdAndCentricId, instanceId, "*exists*",
                    timestamp);
            if (highest <= timestamp) {
                List<ListenableFuture<List<ViewFieldChange>>> futures = new ArrayList<>();
                for (InitiateTraverserKey key : valueTraversers.keySet()) {
                    if (writtenInstance.hasField(key.getTriggerFieldName())) {
                        for (final PathTraverser pathTraverser : valueTraversers.get(key)) {
                            futures.add(executorService.submit(new Callable<List<ViewFieldChange>>() {

                                @Override
                                public List<ViewFieldChange> call() throws Exception {
                                    PathTraversalContext context = pathTraverser.createContext(writtenEventContext,
                                            writtenEvent, threadTimestamp, true);
                                    context.setPathId(pathTraverser.getPathIndex(), instanceId, timestamp);

                                    List<ReferenceWithTimestamp> valueVersions = context.populateLeafNodeFields(
                                            tenantIdAndCentricId, instanceId, pathTraverser.getInitialFieldNames());
                                    context.addVersions(pathTraverser.getPathIndex(), valueVersions);

                                    //long start = System.currentTimeMillis();
                                    pathTraverser.traverse(tenantIdAndCentricId, context,
                                            new PathId(instanceId, timestamp));
                                    //writtenEventContext.getProcessingStats().latency("PATH", pathTraverser.toString(), System.currentTimeMillis() - start);
                                    writtenEventContext.valuePaths++;
                                    return context.takeChanges(); // TODO add auto flush if writeableChanges is getting to be to large.
                                }
                            }));
                        }
                    }
                }
                commitChanges(writtenEventContext, tenantIdAndCentricId, futures);
            }

        } else {
            final List<ConcurrencyStore.FieldVersion> want = new ArrayList<>();
            List<ListenableFuture<List<ViewFieldChange>>> futures = new ArrayList<>();
            for (InitiateTraverserKey key : valueTraversers.keySet()) {
                if (writtenInstance.hasField(key.getTriggerFieldName())) {
                    for (final PathTraverser pathTraverser : valueTraversers.get(key)) {
                        futures.add(executorService.submit(new Callable<List<ViewFieldChange>>() {

                            @Override
                            public List<ViewFieldChange> call() throws Exception {
                                PathTraversalContext context = pathTraverser.createContext(writtenEventContext,
                                        writtenEvent, threadTimestamp, false);
                                List<ReferenceWithTimestamp> valueVersions = context.populateLeafNodeFields(
                                        tenantIdAndCentricId, instanceId, pathTraverser.getInitialFieldNames());

                                context.setPathId(pathTraverser.getPathIndex(), instanceId, timestamp);
                                context.addVersions(pathTraverser.getPathIndex(), valueVersions);
                                //long start = System.currentTimeMillis();
                                pathTraverser.traverse(tenantIdAndCentricId, context,
                                        new PathId(instanceId, timestamp));
                                //writtenEventContext.getProcessingStats().latency("PATH", pathTraverser.toString(), System.currentTimeMillis() - start);
                                writtenEventContext.valuePaths++;
                                for (ReferenceWithTimestamp valueVersion : valueVersions) {
                                    want.add(new ConcurrencyStore.FieldVersion(instanceId,
                                            valueVersion.getFieldName(), valueVersion.getTimestamp()));
                                }
                                return context.takeChanges(); //TODO add auto flush if writeableChanges is getting to be to large.
                            }
                        }));

                    }
                }
            }
            commitChanges(writtenEventContext, tenantIdAndCentricId, futures);
            concurrencyChecker.checkIfModifiedOutFromUnderneathMe(tenantIdAndCentricId, want);
        }
    }

    private void processRefs(final WrittenEventContext writtenEventContext,
            final TenantIdAndCentricId tenantIdAndCentricId, final WrittenEvent writtenEvent,
            final long threadTimestamp) throws Exception {

        final long timestamp = writtenEvent.getEventId();
        WrittenInstance writtenInstance = writtenEvent.getWrittenInstance();
        final ObjectId instanceId = writtenInstance.getInstanceId();

        Set<InitiateTraverserKey> allKeys = new HashSet();
        allKeys.addAll(forwardRefTraversers.keySet());
        allKeys.addAll(backRefTraversers.keySet());

        List<InitiateTraverserKey> keys = new ArrayList<>();
        List<String> accumulateRefFieldNames = new ArrayList<>();
        for (final InitiateTraverserKey key : allKeys) {
            if (writtenInstance.hasField(key.getTriggerFieldName())
                    && (writtenInstance.hasField(key.getRefFieldName()) || writtenInstance.isDeletion())) {
                keys.add(key);
                accumulateRefFieldNames.add(key.getRefFieldName());
            }
        }

        String[] refFieldNames = accumulateRefFieldNames.toArray(new String[accumulateRefFieldNames.size()]);
        List<Long> highestVersions = concurrencyChecker.highestVersions(tenantIdAndCentricId, instanceId,
                refFieldNames);

        List<ListenableFuture<List<ViewFieldChange>>> futures = new ArrayList<>();
        for (int i = 0; i < refFieldNames.length; i++) {
            final InitiateTraverserKey key = keys.get(i);
            final String refFieldName = refFieldNames[i];
            final long highest = highestVersions.get(i) == null ? timestamp : highestVersions.get(i);
            futures.add(executorService.submit(new Callable<List<ViewFieldChange>>() {

                @Override
                public List<ViewFieldChange> call() throws Exception {
                    final List<ViewFieldChange> writeableChanges = new ArrayList<>();
                    referenceStore.unlink(tenantIdAndCentricId, Math.max(timestamp, highest), instanceId,
                            refFieldName, threadTimestamp, new CallbackStream<ReferenceWithTimestamp>() {
                                @Override
                                public ReferenceWithTimestamp callback(ReferenceWithTimestamp to) throws Exception {
                                    if (to != null && to.getTimestamp() < timestamp) {
                                        traverse(writtenEventContext, tenantIdAndCentricId, writtenEvent, key,
                                                instanceId, refFieldName, to, threadTimestamp, true,
                                                writeableChanges);
                                    }
                                    return to;
                                }
                            });
                    return writeableChanges;
                }
            }));
        }
        commitChanges(writtenEventContext, tenantIdAndCentricId, futures);

        if (!writtenInstance.isDeletion()) {
            List<ConcurrencyStore.FieldVersion> fieldVersions = new ArrayList<>();
            for (int i = 0; i < refFieldNames.length; i++) {
                long highest = highestVersions.get(i) == null ? timestamp : highestVersions.get(i);
                fieldVersions.add(new ConcurrencyStore.FieldVersion(instanceId, refFieldNames[i], highest));
            }
            concurrencyChecker.checkIfModifiedOutFromUnderneathMe(tenantIdAndCentricId, fieldVersions);

            futures.clear();
            for (int i = 0; i < refFieldNames.length; i++) {
                final InitiateTraverserKey key = keys.get(i);
                final String refFieldName = refFieldNames[i];
                futures.add(executorService.submit(new Callable<List<ViewFieldChange>>() {

                    @Override
                    public List<ViewFieldChange> call() throws Exception {
                        final List<ViewFieldChange> writeableChanges = new ArrayList<>();
                        referenceStore.streamForwardRefs(tenantIdAndCentricId, instanceId.getClassName(),
                                refFieldName, instanceId, threadTimestamp,
                                new CallbackStream<ReferenceWithTimestamp>() {

                                    @Override
                                    public ReferenceWithTimestamp callback(ReferenceWithTimestamp to)
                                            throws Exception {
                                        if (to != null) {
                                            traverse(writtenEventContext, tenantIdAndCentricId, writtenEvent, key,
                                                    instanceId, refFieldName, to, threadTimestamp, false,
                                                    writeableChanges);
                                        }
                                        return to;
                                    }
                                });
                        return writeableChanges;
                    }
                }));
            }
            commitChanges(writtenEventContext, tenantIdAndCentricId, futures);
            concurrencyChecker.checkIfModifiedOutFromUnderneathMe(tenantIdAndCentricId, fieldVersions);
        }

    }

    private void traverse(final WrittenEventContext writtenEventContext,
            final TenantIdAndCentricId tenantIdAndCentricId, final WrittenEvent writtenEvent,
            InitiateTraverserKey key, final ObjectId instanceId, String refFieldName,
            final ReferenceWithTimestamp to, final long threadTimestamp, final boolean removal,
            List<ViewFieldChange> writeableChanges) throws Exception {

        final ReferenceWithTimestamp from = new ReferenceWithTimestamp(instanceId, refFieldName, to.getTimestamp());

        for (final PathTraverser pathTraverser : forwardRefTraversers.get(key)) {
            PathTraversalContext context = pathTraverser.createContext(writtenEventContext, writtenEvent,
                    threadTimestamp, removal);
            context.setPathId(pathTraverser.getPathIndex(), from.getObjectId(), from.getTimestamp());
            context.addVersion(pathTraverser.getPathIndex(), from);

            pathTraverser.traverse(tenantIdAndCentricId, context, new PathId(to.getObjectId(), to.getTimestamp()));
            writtenEventContext.refPaths++;
            writeableChanges.addAll(context.takeChanges()); // TODO add auto flush if writeableChanges is getting to be to large.
        }

        for (final PathTraverser pathTraverser : backRefTraversers.get(key)) {
            PathTraversalContext context = pathTraverser.createContext(writtenEventContext, writtenEvent,
                    threadTimestamp, removal);
            context.setPathId(pathTraverser.getPathIndex(), to.getObjectId(), to.getTimestamp());
            context.addVersion(pathTraverser.getPathIndex(), from);

            pathTraverser.traverse(tenantIdAndCentricId, context, new PathId(instanceId, to.getTimestamp()));
            writtenEventContext.backRefPaths++;
            writeableChanges.addAll(context.takeChanges()); // TODO add auto flush if writeableChanges is getting to be to large.
        }

    }

    private void commitChanges(WrittenEventContext writtenEventContext, TenantIdAndCentricId tenantIdAndCentricId,
            List<ListenableFuture<List<ViewFieldChange>>> futures)
            throws InterruptedException, ExecutionException, CommitChangeException {
        ListenableFuture<List<List<ViewFieldChange>>> allAsList = Futures.allAsList(futures);
        List<ViewFieldChange> writeableChanges = new ArrayList<>();
        for (List<ViewFieldChange> changes : allAsList.get()) {
            writeableChanges.addAll(changes);
        }
        writtenEventContext.getCommitChange().commitChange(writtenEventContext, tenantIdAndCentricId,
                writeableChanges);
    }

}