com.hortonworks.registries.schemaregistry.state.SchemaVersionLifecycleStateMachine.java Source code

Java tutorial

Introduction

Here is the source code for com.hortonworks.registries.schemaregistry.state.SchemaVersionLifecycleStateMachine.java

Source

/*
 * Copyright 2016 Hortonworks.
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.hortonworks.registries.schemaregistry.state;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.Pair;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

import static com.hortonworks.registries.schemaregistry.state.SchemaVersionLifecycleState.INBUILT_STATE_ID_MAX;

/**
 * This class represents schema version lifecycle state machine registered in SchemaRegistry server. Users can customize
 * the state by configuring {@link CustomSchemaStateExecutor} and custom states and transitions can be configured using {@link Builder}
 * in callbacks{@link CustomSchemaStateExecutor#init(Builder, Byte, Byte, Map)}
 */
public class SchemaVersionLifecycleStateMachine {

    private final Map<Byte, SchemaVersionLifecycleState> states;
    private final Map<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> transitions;
    private final Map<SchemaVersionLifecycleStateTransition, List<SchemaVersionLifecycleStateTransitionListener>> listeners;

    private SchemaVersionLifecycleStateMachine(Map<Byte, SchemaVersionLifecycleState> states,
            Map<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> transitions,
            Map<SchemaVersionLifecycleStateTransition, ConcurrentLinkedQueue<SchemaVersionLifecycleStateTransitionListener>> listeners) {
        this.states = Collections.unmodifiableMap(states);
        this.transitions = Collections.unmodifiableMap(transitions);
        this.listeners = Collections.unmodifiableMap(listeners).entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                transitionWithListener -> Lists.newArrayList(transitionWithListener.getValue().iterator())));
    }

    public Map<Byte, SchemaVersionLifecycleState> getStates() {
        return states;
    }

    public Map<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> getTransitions() {
        return transitions;
    }

    public Map<SchemaVersionLifecycleStateTransition, List<SchemaVersionLifecycleStateTransitionListener>> getListeners() {
        return listeners;
    }

    public SchemaVersionLifecycleStateMachineInfo toConfig() {
        return new SchemaVersionLifecycleStateMachineInfo(states.values(), transitions.keySet());
    }

    public static Builder newBuilder() {
        return new Builder();
    }

    public static class Builder {
        ConcurrentMap<Byte, SchemaVersionLifecycleState> states = new ConcurrentHashMap<>();
        ConcurrentMap<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> transitionsWithActions = new ConcurrentHashMap<>();
        ConcurrentMap<SchemaVersionLifecycleStateTransition, ConcurrentLinkedQueue<SchemaVersionLifecycleStateTransitionListener>> transitionsWithListeners = new ConcurrentHashMap<>();

        public Builder() {
            registerInBuiltStates();
        }

        private void registerInBuiltStates() {
            List<Pair<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction>> transitionActions = new ArrayList<>();

            Field[] declaredFields = SchemaVersionLifecycleStates.class.getDeclaredFields();
            for (Field field : declaredFields) {
                if (Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers())
                        && InbuiltSchemaVersionLifecycleState.class.isAssignableFrom(field.getType())) {
                    InbuiltSchemaVersionLifecycleState state = null;
                    try {
                        state = (InbuiltSchemaVersionLifecycleState) field.get(null);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                    register(state);

                    transitionActions.addAll(state.getTransitionActions());
                }
            }

            // register transitions for inbuilt states
            for (Pair<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> transitionAction : transitionActions) {
                transition(transitionAction.getLeft(), transitionAction.getRight());
            }
        }

        /**
         * Registers the given state with REGISTRY.
         *
         * @param state state to be registered.
         *
         * @throws IllegalArgumentException if the given state is already registered.
         */
        public void register(SchemaVersionLifecycleState state) {
            checkForInbuiltStateIds(state);
            SchemaVersionLifecycleState prevState = states.putIfAbsent(state.getId(), state);
            if (prevState != null) {
                throw new IllegalArgumentException("Given state is already registered as " + prevState);
            }
        }

        public Map<Byte, SchemaVersionLifecycleState> getStates() {
            return Collections.unmodifiableMap(states);
        }

        public Builder transition(SchemaVersionLifecycleStateTransition transition,
                SchemaVersionLifecycleStateAction action) {
            Byte sourceStateId = transition.getSourceStateId();
            Byte targetStateId = transition.getTargetStateId();
            checkStatesRegistered(sourceStateId, targetStateId);

            SchemaVersionLifecycleStateAction existingTransitionAction = transitionsWithActions
                    .putIfAbsent(transition, action);
            if (existingTransitionAction != null) {
                throw new IllegalArgumentException("Given transition already exists, from: [" + sourceStateId
                        + "] to: [" + targetStateId + "]");
            }

            return this;
        }

        public void registerListener(SchemaVersionLifecycleStateTransition transition,
                SchemaVersionLifecycleStateTransitionListener listener) {
            SchemaVersionLifecycleStateAction schemaVersionLifecycleStateAction = transitionsWithActions
                    .get(transition);
            if (schemaVersionLifecycleStateAction == null)
                throw new IllegalArgumentException("Given transition doesn't have any action associated with it");
            ConcurrentLinkedQueue<SchemaVersionLifecycleStateTransitionListener> listeners = transitionsWithListeners
                    .computeIfAbsent(transition, missingTransition -> new ConcurrentLinkedQueue<>());
            listeners.add(listener);
            transitionsWithListeners.put(transition, listeners);
        }

        private void checkForInbuiltStateIds(SchemaVersionLifecycleState state) {
            if (!(state instanceof InbuiltSchemaVersionLifecycleState)) {
                if (state.getId() <= INBUILT_STATE_ID_MAX) {
                    throw new IllegalArgumentException("Given custom state id should be more than 32");
                }
            }
        }

        private void checkStatesRegistered(Byte... stateIds) {
            for (Byte stateId : stateIds) {
                if (!this.states.containsKey(stateId)) {
                    throw new IllegalArgumentException("Given state [" + stateId + "] is not yet registered.");
                }
            }
        }

        public Map<SchemaVersionLifecycleStateTransition, SchemaVersionLifecycleStateAction> getTransitionsWithActions() {
            return Collections.unmodifiableMap(transitionsWithActions);
        }

        public SchemaVersionLifecycleStateMachine build() {
            return new SchemaVersionLifecycleStateMachine(states, transitionsWithActions, transitionsWithListeners);
        }
    }

}