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

com.igormaznitsa.meta.common.utils.Deferrers Maven / Gradle / Ivy

Go to download

Set of annotations and auxuliary classes to mark code and make measurement and analyzing in runtime. It includes shaded FindBugs jsr-305 annotations from the com.google.code.findbugs library .

The newest version!
/*
 * Copyright 2015 Igor Maznitsa.
 *
 * 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.igormaznitsa.meta.common.utils;

import com.igormaznitsa.meta.annotation.Weight;
import com.igormaznitsa.meta.common.exceptions.MetaErrorListeners;
import static com.igormaznitsa.meta.common.utils.Assertions.assertNotNull;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.meta.common.interfaces.Disposable;
import com.igormaznitsa.meta.annotation.Warning;
import com.igormaznitsa.meta.common.exceptions.UnexpectedProcessingError;
import java.io.Serializable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Auxiliary tool to defer some actions and process them in some point
 in future. it check stack depth and executes only locally (for the stack
 level) defer actions. It works through ThreadLocal so that
 * actions saved separately for every thread.
 *
 * @since 1.0
 * @see ThreadLocal
 */
@ThreadSafe
public final class Deferrers {

  private Deferrers () {
  }

  /**
   * Class wrapping executeDeferred method and stack depth for action.
   *
   * @since 1.0
   */
  @Immutable
  @Weight (Weight.Unit.VARIABLE)
  public abstract static class Deferred implements Serializable {

    private static final long serialVersionUID = -1134788854676942497L;

    private final int stackDepth;

    /**
     * The Constructor.
     * @since 1.0
     */
    @Weight(value = Weight.Unit.VARIABLE, comment = "Depends on the current call stack depth@")
    public Deferred () {
      this.stackDepth = ThreadUtils.stackDepth() - 1;
    }

    /**
     * Get the stack depth detected during object creation.
     * @return the stack depth
     * @since 1.0
     */
    public int getStackDepth () {
      return this.stackDepth;
    }

    /**
     * Execute call.
     * @throws Exception it will be thrown for error.
     * @since 1.0
     */
    public abstract void executeDeferred () throws Exception;
  }

  /**
   * Inside registry for defer actions.
   * @since 1.0
   */
  @MustNotContainNull
  private static final ThreadLocal> REGISTRY = new ThreadLocal>() {
    @Override
    protected List initialValue () {
      return new ArrayList();
    }
  };

  /**
   * Defer some action.
   *
   * @param deferred action to be defer.
   * @since 1.0
   */
  @Weight (Weight.Unit.NORMAL)
  public static void defer (@Nonnull final Deferred deferred) {
    REGISTRY.get().add(assertNotNull(deferred));
  }

  /**
   * Defer object containing public close() method. It catches all exceptions during closing and make notifications only for global error listeners.
   * It finds a public 'close' method of the object and call that through reflection.
   *
   * @param closeable an object with close() method.
   * @since 1.0
   */
  @Warning("Using reflection")
  @Weight (Weight.Unit.NORMAL)
  public static void deferredClose (@Nullable final Object closeable) {
    if (closeable != null) {
      defer(new Deferred() {
        private static final long serialVersionUID = 2265124256013043847L;
        @Override
        public void executeDeferred () throws Exception {
          try {
            closeable.getClass().getMethod("close").invoke(closeable);
          }
          catch (Exception thr) {
            MetaErrorListeners.fireError("Error during deferred closing action", thr);
          }
        }
      });
    }
  }

  /**
   * Defer closing of an closeable object.
   *
   * @param closeable an object implements java.io.Closeable
   * @since 1.0
   */
  @Weight (Weight.Unit.NORMAL)
  public static void defer (@Nullable final Closeable closeable) {
    if (closeable != null) {
      defer(new Deferred() {
        private static final long serialVersionUID = 2265124256013043847L;
        @Override
        public void executeDeferred () throws Exception {
          IOUtils.closeQuetly(closeable);
        }
      });
    }
  }

  /**
   * Defer execution of some runnable action.
   *
   * @param runnable some runnable action to be executed in future
   * @throws AssertionError if the runnable object is null
   */
  @Weight (Weight.Unit.NORMAL)
  public static void defer (@Nonnull final Runnable runnable) {
    assertNotNull(runnable);
    defer(new Deferred() {
      private static final long serialVersionUID = 2061489024868070733L;
      private final Runnable value = runnable;

      @Override
      public void executeDeferred () throws Exception {
        this.value.run();
      }
    });

  }

  /**
   * Defer execution of some disposable object.
   *
   * @param disposable some disposable object to be processed.
   * @throws AssertionError if the disposable object is null
   * @see Disposable
   */
  @Weight (Weight.Unit.NORMAL)
  public static void defer (@Nonnull final Disposable disposable) {
    assertNotNull(disposable);
    defer(new Deferred() {
      private static final long serialVersionUID = 7940162959962038010L;
      private final Disposable value = disposable;

      @Override
      public void executeDeferred () throws Exception {
        this.value.dispose();
      }
    });

  }

  /**
   * Cancel all defer actions globally.
   *
   * @since 1.0
   */
  @Weight (Weight.Unit.NORMAL)
  public static void cancelAllDeferredActionsGlobally () {
    final List list = REGISTRY.get();
    list.clear();
    REGISTRY.remove();
  }

  /**
   * Cancel all defer actions for the current stack depth.
   *
   * @since 1.0
   */
  @Weight (value = Weight.Unit.VARIABLE, comment = "Depends on the current call stack depth")
  public static void cancelDeferredActions () {
    final int stackDepth = ThreadUtils.stackDepth();

    final List list = REGISTRY.get();
    final Iterator iterator = list.iterator();

    while (iterator.hasNext()) {
      final Deferred deferred = iterator.next();
      if (deferred.getStackDepth() >= stackDepth) {
        iterator.remove();
      }
    }
    if (list.isEmpty()) {
      REGISTRY.remove();
    }
  }

  /**
   * Process all defer actions for the current stack depth level.
   *
   * @since 1.0
   */
  @Weight (value = Weight.Unit.VARIABLE,comment = "Depends on the current call stack depth")
  public static void processDeferredActions () {
    final int stackDepth = ThreadUtils.stackDepth();

    final List list = REGISTRY.get();
    final Iterator iterator = list.iterator();

    while (iterator.hasNext()) {
      final Deferred deferred = iterator.next();
      if (deferred.getStackDepth() >= stackDepth) {
        try {
          deferred.executeDeferred();
        }
        catch (Exception ex) {
          final UnexpectedProcessingError error = new UnexpectedProcessingError("Error during deferred action processing", ex);
          MetaErrorListeners.fireError(error.getMessage(), error);
        }
        finally {
          iterator.remove();
        }
      }
    }
    if (list.isEmpty()) {
      REGISTRY.remove();
    }
  }

  /**
   * Check that presented defer actions for the current thread.
   *
   * @return true if presented, false otherwise
   * @since 1.0
   */
  @Weight (Weight.Unit.NORMAL)
  public static boolean isEmpty () {
    final boolean result = REGISTRY.get().isEmpty();
    if (result) {
      REGISTRY.remove();
    }
    return result;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy