content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.apache.ambari.annotations;
  
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Inherited;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  import java.util.concurrent.locks.ReadWriteLock;
  
  import org.apache.ambari.server.configuration.Configuration;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.inject.persist.Transactional;
  
  /**
   * The {@link TransactionalLock} annotation is used to provide advice around a
   * joinpoint which will invoke a lock. The lock is invoked before the method
   * begins and is released after it has executed.
   * <p/>
   * This is mainly used in combination with {@link Transactional} methods to
   * provide locking around the entire transaction in order to prevent other
   * methods from performing work before a transaction has completed.
   */
  @Inherited
  @Retention(RetentionPolicy.RUNTIME)
  @Target({ ElementType.METHOD })
  public @interface TransactionalLock {
  
    /**
     * The logic unit of work being locked.
     *
     * @return
     */
    LockArea lockArea();
  
    /**
     * @return
     */
    LockType lockType();
  
    /**
     * The area that the lock is being applied to. There is exactly 1
     * {@link ReadWriteLock} for every area defined.
     */
    enum LockArea {
      /**
       * Joinpoint lock around work performed on caching the host role command
       * status in a given stage and request.
       */
      HRC_STATUS_CACHE(Configuration.SERVER_HRC_STATUS_SUMMARY_CACHE_ENABLED.getKey());
  
      /**
       * Logger.
       */
      private final static Logger LOG = LoggerFactory.getLogger(LockArea.class);
  
      /**
       * The property which governs whether the lock area is enabled or disabled.
       * Because of the inherent nature of deadlocks with interceptors that lock,
       * it's wise to be able to disable a lock area dynamically.
       */
      private String m_configurationProperty;
  
      /**
       * {@code true} if the lock area is enabled and should be lockable.
       */
      private Boolean m_enabled = null;
  
      /**
       * Constructor.
       *
       * @param configurationProperty
       */
      LockArea(String configurationProperty) {
        m_configurationProperty = configurationProperty;
      }
  
      /**
       * Gets whether this {@link LockArea} is enabled.
       *
       * @param configuration
       *          the configuration to read from (not {@code null}).
       * @return {@code true} if enabled, {@code false} otherwise.
       */
      public boolean isEnabled(Configuration configuration) {
        if (null != m_enabled) {
          return m_enabled.booleanValue();
        }
  
        // start with TRUE
        m_enabled = Boolean.TRUE;
        String property = configuration.getProperty(m_configurationProperty);
  
        if (null != property) {
          try {
            m_enabled = Boolean.valueOf(property);
          } catch (Exception exception) {
            LOG.error("Unable to determine if the lock area {} is enabled, defaulting to TRUE",
                m_configurationProperty, exception);
          }
        }
  
        LOG.info("LockArea {} is {}", name(), m_enabled ? "enabled" : "disabled");
        return m_enabled.booleanValue();
      }
  
      /**
       * Used for testing to clean the internal state of enabled. This should not
       * be used directly.
       */
      void clearEnabled() {
        m_enabled = null;
      }
    }
  
    /**
     * The type of lock which should be acquired.
     */
    enum LockType {
      /**
       * Read Lock.
       */
      READ,
  
      /**
       * Write lock.
       */
      WRITE;
    }
  }
  