com.codebullets.sagalib.processing.SagaKeyReaderExtractor.java Source code

Java tutorial

Introduction

Here is the source code for com.codebullets.sagalib.processing.SagaKeyReaderExtractor.java

Source

/*
 * Copyright 2013 Stefan Domnanovits
 *
 * 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.
 */
package com.codebullets.sagalib.processing;

import com.codebullets.sagalib.KeyReader;
import com.codebullets.sagalib.Saga;
import com.codebullets.sagalib.context.LookupContext;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Uses the saga keyReaders() method to extract the saga
 * key of a message.
 */
@SuppressWarnings("unchecked")
public class SagaKeyReaderExtractor implements KeyExtractor {
    private static final Logger LOG = LoggerFactory.getLogger(SagaKeyReaderExtractor.class);
    private final SagaProviderFactory sagaProviderFactory;
    private final Cache<SagaMessageKey, Optional<KeyReader>> knownReaders;

    /**
     * Generates a new instance of SagaKeyReaderExtractor.
     */
    @Inject
    public SagaKeyReaderExtractor(final SagaProviderFactory sagaProviderFactory) {
        this.sagaProviderFactory = sagaProviderFactory;
        knownReaders = CacheBuilder.newBuilder().build();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object findSagaInstanceKey(final Class<? extends Saga> sagaClazz, final LookupContext context) {
        Object keyValue = null;

        KeyReader reader = tryGetKeyReader(sagaClazz, context.message());
        if (reader != null) {
            keyValue = reader.readKey(context.message(), context);
        }

        return keyValue;
    }

    /**
     * Does not throw an exception when accessing the loading cache for key readers.
     */
    private KeyReader tryGetKeyReader(final Class<? extends Saga> sagaClazz, final Object message) {
        KeyReader reader;

        try {
            Optional<KeyReader> cachedReader = knownReaders.get(SagaMessageKey.forMessage(sagaClazz, message),
                    () -> {
                        KeyReader foundReader = findReader(sagaClazz, message);
                        return Optional.fromNullable(foundReader);
                    });
            reader = cachedReader.orNull();
        } catch (Exception ex) {
            LOG.error("Error searching for reader to extract saga key. sagatype = {}, message = {}", sagaClazz,
                    message, ex);
            reader = null;
        }

        return reader;
    }

    private KeyReader findReader(final Class<? extends Saga> sagaClazz, final Object message) {
        KeyReader reader = null;

        Collection<KeyReader> readersOfSaga = findReader(sagaClazz, message.getClass());

        ClassTypeExtractor extractor = new ClassTypeExtractor(message.getClass());
        Iterable<Class<?>> messageTypesToConsider = extractor.allClassesAndInterfaces();

        for (Class<?> messageType : messageTypesToConsider) {
            reader = findReaderMatchingExactType(readersOfSaga, messageType);
            if (reader != null) {
                break;
            }
        }

        return reader;
    }

    /**
     * Search for reader based on message class.
     */
    private KeyReader findReaderMatchingExactType(final Iterable<KeyReader> readers, final Class<?> messageType) {
        KeyReader messageKeyReader = null;

        for (KeyReader reader : readers) {
            if (reader.getMessageClass().equals(messageType)) {
                messageKeyReader = reader;
                break;
            }
        }

        return messageKeyReader;
    }

    /**
     * Search for a reader based on saga type.
     */
    private Collection<KeyReader> findReader(final Class<? extends Saga> sagaClazz, final Class<?> messageClazz) {
        Saga saga = sagaProviderFactory.createProvider(sagaClazz).get();
        Collection<KeyReader> readers = saga.keyReaders();
        if (readers == null) {
            // return empty list in case saga returns null for any reason
            readers = new ArrayList<>();
        }

        return readers;
    }

    /**
     * Key combined of saga and message type.
     */
    private static final class SagaMessageKey {
        private final Class<?> saga;
        private final Class<?> message;

        private SagaMessageKey(final Class<?> saga, final Class<?> message) {
            this.saga = saga;
            this.message = message;
        }

        @Override
        public int hashCode() {
            return Objects.hash(saga, message);
        }

        @Override
        public boolean equals(final Object obj) {
            boolean equals = false;
            if (this == obj) {
                equals = true;
            } else if (obj instanceof SagaMessageKey) {
                SagaMessageKey other = (SagaMessageKey) obj;
                equals = Objects.equals(other.saga, saga) && Objects.equals(other.message, message);
            }

            return equals;
        }

        public static SagaMessageKey forMessage(final Class<?> saga, final Object message) {
            checkNotNull(saga, "saga type must not be null.");

            return new SagaMessageKey(saga, message.getClass());
        }
    }
}