Package org.sonatype.nexus.capability.internal

Source Code of org.sonatype.nexus.capability.internal.DefaultCapabilityReference

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.capability.internal;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.sonatype.nexus.capability.Capability;
import org.sonatype.nexus.capability.CapabilityContext;
import org.sonatype.nexus.capability.CapabilityDescriptor;
import org.sonatype.nexus.capability.CapabilityEvent;
import org.sonatype.nexus.capability.CapabilityEvent.CallbackFailure;
import org.sonatype.nexus.capability.CapabilityEvent.CallbackFailureCleared;
import org.sonatype.nexus.capability.CapabilityIdentity;
import org.sonatype.nexus.capability.CapabilityReference;
import org.sonatype.nexus.capability.CapabilityRegistry;
import org.sonatype.nexus.capability.CapabilityType;
import org.sonatype.sisu.goodies.common.ComponentSupport;
import org.sonatype.sisu.goodies.eventbus.EventBus;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newHashMap;
import static java.util.Collections.unmodifiableMap;

/**
* Default {@link CapabilityReference} implementation.
*
* @since capabilities 2.0
*/
public class DefaultCapabilityReference
    extends ComponentSupport
    implements CapabilityReference, CapabilityContext
{

  private static final Map<String, String> EMPTY_MAP = Collections.emptyMap();

  private final CapabilityIdentity id;

  private final CapabilityType type;

  private final CapabilityDescriptor descriptor;

  private final Capability capability;

  private String notes;

  private final CapabilityRegistry capabilityRegistry;

  private final EventBus eventBus;

  private final ActivationConditionHandler activationHandler;

  private final ValidityConditionHandler validityHandler;

  private final ReentrantReadWriteLock stateLock;

  private Map<String, String> capabilityProperties;

  private State state;

  private Exception failure;

  private String failingAction;

  DefaultCapabilityReference(final CapabilityRegistry capabilityRegistry,
                             final EventBus eventBus,
                             final ActivationConditionHandlerFactory activationListenerFactory,
                             final ValidityConditionHandlerFactory validityConditionHandlerFactory,
                             final CapabilityIdentity id,
                             final CapabilityType type,
                             final CapabilityDescriptor descriptor,
                             final Capability capability)
  {
    this.capabilityRegistry = checkNotNull(capabilityRegistry);
    this.eventBus = checkNotNull(eventBus);

    this.id = checkNotNull(id);
    this.type = checkNotNull(type);
    this.descriptor = checkNotNull(descriptor);
    this.capability = checkNotNull(capability);
    capabilityProperties = EMPTY_MAP;

    state = new NewState();
    stateLock = new ReentrantReadWriteLock();
    activationHandler = checkNotNull(activationListenerFactory).create(this);
    validityHandler = checkNotNull(validityConditionHandlerFactory).create(this);

    capability.init(this);
  }

  public Capability capability() {
    return capability;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T extends Capability> T capabilityAs(final Class<T> type) {
    return (T) capability;
  }

  @Override
  public CapabilityIdentity id() {
    return id;
  }

  @Override
  public CapabilityType type() {
    return type;
  }

  @Override
  public CapabilityDescriptor descriptor() {
    return descriptor;
  }

  @Override
  public String notes() {
    return notes;
  }

  public void setNotes(final String notes) {
    this.notes = notes;
  }

  @Override
  public boolean isEnabled() {
    try {
      stateLock.readLock().lock();
      return state.isEnabled();
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  /**
   * Enables the referenced capability.
   */
  public void enable() {
    try {
      stateLock.writeLock().lock();
      state.enable();
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  /**
   * Disables the referenced capability.
   */
  public void disable() {
    try {
      stateLock.writeLock().lock();
      state.disable();
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  @Override
  public boolean isActive() {
    try {
      stateLock.readLock().lock();
      return state.isActive();
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  @Override
  public boolean hasFailure() {
    return failure() != null;
  }

  @Override
  public Exception failure() {
    try {
      stateLock.readLock().lock();
      return failure;
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  @Override
  public String failingAction() {
    try {
      stateLock.readLock().lock();
      return failingAction;
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  /**
   * Activates the referenced capability.
   */
  public void activate() {
    try {
      stateLock.writeLock().lock();
      state.activate();
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  /**
   * Passivate the referenced capability.
   */
  public void passivate() {
    try {
      stateLock.writeLock().lock();
      state.passivate();
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  @Override
  public CapabilityContext context() {
    return this;
  }

  /**
   * Callback when a new capability is created.
   *
   * @param properties capability configuration
   */
  public void create(final Map<String, String> properties) {
    try {
      stateLock.writeLock().lock();
      state.create(properties);
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  /**
   * Callback when a capability configuration is loaded from persisted store (configuration file).
   *
   * @param properties capability configuration
   */
  public void load(final Map<String, String> properties) {
    try {
      stateLock.writeLock().lock();
      state.load(properties);
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  /**
   * Callback when a capability configuration is updated.
   *
   * @param properties         capability configuration
   * @param previousProperties previous capability configuration
   */
  public void update(final Map<String, String> properties, final Map<String, String> previousProperties) {
    if (!sameProperties(previousProperties, properties)) {
      try {
        stateLock.writeLock().lock();
        state.update(properties, previousProperties);
      }
      finally {
        stateLock.writeLock().unlock();
      }
    }
  }

  /**
   * Callback when a capability configuration is removed.
   */
  public void remove() {
    try {
      stateLock.writeLock().lock();
      state.remove();
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  @Override
  public Map<String, String> properties() {
    try {
      stateLock.readLock().lock();
      return capabilityProperties;
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  @Override
  public String stateDescription() {
    try {
      stateLock.readLock().lock();
      return state.stateDescription();
    }
    finally {
      stateLock.readLock().unlock();
    }
  }

  @Override
  public String toString() {
    return String.format("capability %s (enabled=%s, active=%s)", capability, isEnabled(), isActive());
  }

  // @TestAccessible //
  static boolean sameProperties(final Map<String, String> p1, final Map<String, String> p2) {
    if (p1 == null) {
      return p2 == null;
    }
    else if (p2 == null) {
      return false;
    }
    return p1.size() == p2.size() && p1.equals(p2);
  }

  private void resetFailure() {
    try {
      stateLock.writeLock().lock();
      if (failure != null) {
        failure = null;
        failingAction = null;
        eventBus.post(new CallbackFailureCleared(capabilityRegistry, this));
      }
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  private void setFailure(final String action, final Exception e) {
    try {
      stateLock.writeLock().lock();
      failure = checkNotNull(e);
      failingAction = checkNotNull(action);
      log.error("Could not {} capability {} ({})", action.toLowerCase(), capability, id, e);
      eventBus.post(new CallbackFailure(capabilityRegistry, this, action, e));
    }
    finally {
      stateLock.writeLock().unlock();
    }
  }

  private class State
  {

    State() {
      log.debug("Capability {} ({}) state changed to {}", capability, id, this);
    }

    public boolean isEnabled() {
      return false;
    }

    public void enable() {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'enable' operation");
    }

    public void disable() {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'disable' operation");
    }

    public boolean isActive() {
      return false;
    }

    public void activate() {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'activate' operation");
    }

    public void passivate() {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'passivate' operation");
    }

    public void create(final Map<String, String> properties) {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'create' operation");
    }

    public void load(final Map<String, String> properties) {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'load' operation");
    }

    public void update(final Map<String, String> properties, final Map<String, String> previousProperties) {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'update' operation");
    }

    public void remove() {
      throw new IllegalStateException("State '" + toString() + "' does not permit 'remove' operation");
    }

    public String stateDescription() {
      return "Undefined";
    }

    @Override
    public String toString() {
      return getClass().getSimpleName();
    }

  }

  private class NewState
      extends State
  {

    @Override
    public void create(final Map<String, String> properties) {
      try {
        capabilityProperties = properties == null ? EMPTY_MAP : unmodifiableMap(newHashMap(properties));
        capability.onCreate();
        resetFailure();
      }
      catch (Exception e) {
        setFailure("Create", e);
      }
      finally {
        validityHandler.bind();
        state = new DisabledState();
      }
    }

    @Override
    public void load(final Map<String, String> properties) {
      try {
        capabilityProperties = properties == null ? EMPTY_MAP : unmodifiableMap(newHashMap(properties));
        capability.onLoad();
        resetFailure();
      }
      catch (Exception e) {
        setFailure("Load", e);
      }
      finally {
        validityHandler.bind();
        state = new DisabledState();
      }
    }

    @Override
    public String stateDescription() {
      return "New";
    }

    @Override
    public String toString() {
      return "NEW";
    }

  }

  private class DisabledState
      extends State
  {

    @Override
    public void enable() {
      log.debug("Enabling capability {} ({})", capability, id);
      state = new EnabledState();
      activationHandler.bind();
    }

    @Override
    public void disable() {
      // do nothing (not yet enabled)
    }

    @Override
    public void passivate() {
      // do nothing (not yet activated)
    }

    @Override
    public void update(final Map<String, String> properties, final Map<String, String> previousProperties) {
      try {
        eventBus.post(
            new CapabilityEvent.BeforeUpdate(
                capabilityRegistry, DefaultCapabilityReference.this, properties, previousProperties
            )
        );
        capabilityProperties = properties == null ? EMPTY_MAP : unmodifiableMap(newHashMap(properties));
        capability.onUpdate();
        resetFailure();
      }
      catch (Exception e) {
        setFailure("Update", e);
      }
      finally {
        eventBus.post(
            new CapabilityEvent.AfterUpdate(
                capabilityRegistry, DefaultCapabilityReference.this, properties, previousProperties
            )
        );
      }
    }

    @Override
    public void remove() {
      try {
        DefaultCapabilityReference.this.disable();
        validityHandler.release();
        capability.onRemove();
        resetFailure();
      }
      catch (Exception e) {
        setFailure("Remove", e);
      }
      finally {
        state = new RemovedState();
        eventBus.post(
            new CapabilityEvent.AfterRemove(capabilityRegistry, DefaultCapabilityReference.this)
        );
      }
    }

    @Override
    public String stateDescription() {
      return "Disabled";
    }

    @Override
    public String toString() {
      return "DISABLED";
    }

  }

  private class EnabledState
      extends DisabledState
  {

    @Override
    public boolean isEnabled() {
      return true;
    }

    @Override
    public void enable() {
      // do nothing (already enabled)
    }

    @Override
    public void disable() {
      log.debug("Disabling capability {} ({})", capability, id);
      activationHandler.release();
      DefaultCapabilityReference.this.passivate();
      state = new DisabledState();
    }

    @Override
    public void activate() {
      if (activationHandler.isConditionSatisfied()) {
        log.debug("Activating capability {} ({})", capability, id);
        try {
          capability.onActivate();
          resetFailure();
          log.debug("Activated capability {} ({})", capability, id);
          state = new ActiveState();
          eventBus.post(
              new CapabilityEvent.AfterActivated(capabilityRegistry, DefaultCapabilityReference.this)
          );
        }
        catch (Exception e) {
          setFailure("Activate", e);
        }
      }
      else {
        log.debug("Capability {} ({}) is not yet activatable", capability, id);
      }
    }

    @Override
    public void passivate() {
      // do nothing (not yet activated)
    }

    @Override
    public String stateDescription() {
      return activationHandler.isConditionSatisfied() ? "Enabled" : activationHandler.explainWhyNotSatisfied();
    }

    @Override
    public String toString() {
      return "ENABLED";
    }

  }

  private class ActiveState
      extends EnabledState
  {

    @Override
    public boolean isActive() {
      return true;
    }

    @Override
    public void activate() {
      // do nothing (already active)
    }

    @Override
    public void passivate() {
      log.debug("Passivating capability {} ({})", capability, id);
      try {
        state = new EnabledState();
        eventBus.post(
            new CapabilityEvent.BeforePassivated(capabilityRegistry, DefaultCapabilityReference.this)
        );
        capability.onPassivate();
        log.debug("Passivated capability {} ({})", capability, id);
      }
      catch (Exception e) {
        setFailure("Passivate", e);
      }
    }

    @Override
    public String stateDescription() {
      return "Active";
    }

    @Override
    public String toString() {
      return "ACTIVE";
    }

  }

  public class RemovedState
      extends State
  {

    @Override
    public String stateDescription() {
      return "Removed";
    }

    @Override
    public String toString() {
      return "REMOVED";
    }

  }

}
TOP

Related Classes of org.sonatype.nexus.capability.internal.DefaultCapabilityReference

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.