All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ehcache.core.StatusTransitioner Maven / Gradle / Ivy

There is a newer version: 3.10.8
Show newest version
/*
 * Copyright Terracotta, Inc.
 *
 * 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 org.ehcache.core;

import org.ehcache.Status;
import org.ehcache.core.events.StateChangeListener;
import org.ehcache.StateTransitionException;
import org.ehcache.core.spi.LifeCycled;
import org.slf4j.Logger;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Alex Snaps
 */
final class StatusTransitioner {

  private final AtomicReference currentState = new AtomicReference<>(InternalStatus.initial());
  private volatile Thread maintenanceLease;
  private final Logger logger = EhcachePrefixLoggerFactory.getLogger(StatusTransitioner.class);

  private final CopyOnWriteArrayList hooks = new CopyOnWriteArrayList<>();
  private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>();

  Status currentStatus() {
    return currentState.get().get().toPublicStatus();
  }

  boolean isTransitioning() {
    return !currentState.get().done();
  }

  void checkAvailable() {
    final Status status = currentStatus();
    if(status == Status.MAINTENANCE && Thread.currentThread() != maintenanceLease) {
      throw new IllegalStateException("State is " + status + ", yet you don't own it!");
    } else if(status == Status.UNINITIALIZED) {
      throw new IllegalStateException("State is " + status);
    }
  }

  void checkMaintenance() {
    final Status status = currentStatus();
    if(status == Status.MAINTENANCE && Thread.currentThread() != maintenanceLease) {
      throw new IllegalStateException("State is " + status + ", yet you don't own it!");
    } else if (status != Status.MAINTENANCE) {
      throw new IllegalStateException("State is " + status);
    }
  }

  Transition init() {
    logger.trace("Initializing");
    InternalStatus.Transition st;
    for (InternalStatus.Transition cs; !currentState.compareAndSet(cs = currentState.get(), st = cs.get().init()););
    return new Transition(st, null, "Initialize");
  }

  Transition close() {
    logger.trace("Closing");
    InternalStatus.Transition st;
    if(maintenanceLease != null && Thread.currentThread() != maintenanceLease) {
      throw new IllegalStateException("You don't own this MAINTENANCE lease");
    }
    for (InternalStatus.Transition cs; !currentState.compareAndSet(cs = currentState.get(), st = cs.get().close()););
    return new Transition(st, null, "Close");
  }

  Transition maintenance() {
    logger.trace("Entering Maintenance");
    InternalStatus.Transition st;
    for (InternalStatus.Transition cs; !currentState.compareAndSet(cs = currentState.get(), st = cs.get().maintenance()););
    return new Transition(st, Thread.currentThread(), "Enter Maintenance");
  }

  Transition exitMaintenance() {
    checkMaintenance();
    logger.trace("Exiting Maintenance");
    InternalStatus.Transition st;
    for (InternalStatus.Transition cs; !currentState.compareAndSet(cs = currentState.get(), st = cs.get().close()););
    return new Transition(st, Thread.currentThread(), "Exit Maintenance");
  }

  void addHook(LifeCycled hook) {
    validateHookRegistration();
    hooks.add(hook);
  }

  void removeHook(LifeCycled hook) {
    validateHookRegistration();
    hooks.remove(hook);
  }

  private void validateHookRegistration() {
    if(currentStatus() != Status.UNINITIALIZED) {
      throw new IllegalStateException("Can't modify hooks when not in " + Status.UNINITIALIZED);
    }
  }

  void registerListener(StateChangeListener listener) {
    if(!listeners.contains(listener)) {
      listeners.add(listener);
    }
  }

  void deregisterListener(StateChangeListener listener) {
    listeners.remove(listener);
  }

  private void runInitHooks() throws Exception {
    Deque initiated = new ArrayDeque<>();
    for (LifeCycled hook : hooks) {
      try {
        hook.init();
        initiated.add(hook);
      } catch (Exception initException) {
        while (!initiated.isEmpty()) {
          try {
            initiated.pop().close();
          } catch (Exception closeException) {
            logger.error("Failed to close() while shutting down because of .init() having thrown", closeException);
          }
        }
        throw initException;
      }
    }
  }

  private void runCloseHooks() throws Exception {
    Deque initiated = new ArrayDeque<>();
    for (LifeCycled hook : hooks) {
      initiated.addFirst(hook);
    }
    Exception firstFailure = null;
    while (!initiated.isEmpty()) {
      try {
        initiated.pop().close();
      } catch (Exception closeException) {
        if (firstFailure == null) {
          firstFailure = closeException;
        } else {
          logger.error("A LifeCyclable has thrown already while closing down", closeException);
        }
      }
    }
    if (firstFailure != null) {
      throw firstFailure;
    }
  }

  private void fireTransitionEvent(Status previousStatus, Status newStatus) {
    for (StateChangeListener listener : listeners) {
      listener.stateTransition(previousStatus, newStatus);
    }
  }

  final class Transition {

    private final InternalStatus.Transition st;
    private final Thread thread;
    private final String action;

    public Transition(final InternalStatus.Transition st, final Thread thread, final String action) {
      this.st = st;
      this.thread = thread;
      this.action = action;
    }

    public void succeeded() {
      try {
        switch(st.to()) {
          case AVAILABLE:
            runInitHooks();
            break;
          case UNINITIALIZED:
            maintenanceLease = null;
            runCloseHooks();
            break;
          case MAINTENANCE:
            maintenanceLease = thread;
            break;
          default:
            throw new IllegalArgumentException("Didn't expect that enum value: " + st.to());
        }
        st.succeeded();
      } catch (Exception e) {
        st.failed();
        throw new StateTransitionException(e);
      }

      try {
        fireTransitionEvent(st.from().toPublicStatus(), st.to().toPublicStatus());
      } finally {
        maintenanceLease = thread;
        logger.debug("{} successful.", action);
      }
    }

    public StateTransitionException failed(Throwable t) {
      if (st.done()) {
        if (t != null) {
          throw new AssertionError("Throwable cannot be thrown if Transition is done.", t);
        }
        return null;
      }
      st.failed();
      if (t == null) {
        return null;
      }
      logger.error("{} failed.", action);
      if(t instanceof StateTransitionException) {
        return (StateTransitionException) t;
      }
      return new StateTransitionException(t);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy