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

Powerful multi-pass preprocessor to process directives situated in C-styled commentaries

There is a newer version: 7.1.2
Show 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.
   * @return the same object from arguments
   * @since 1.0
   */
  @Weight(Weight.Unit.NORMAL)
  public static Deferred defer(@Nonnull final Deferred deferred) {
    REGISTRY.get().add(assertNotNull(deferred));
    return 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  type of the object to be processed
   * @param closeable an object with close() method.
   * @return the same object from arguments.
   * @since 1.0
   */
  @Warning("Using reflection")
  @Weight(Weight.Unit.NORMAL)
  public static  T deferredClose(@Nullable final T 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);
          }
        }
      });
    }
    return closeable;
  }

  /**
   * Defer closing of an closeable object.
   *
   * @param  type of closeable object
   * @param closeable an object implements java.io.Closeable
   * @return the same closeable object from arguments
   * @since 1.0
   */
  @Weight(Weight.Unit.NORMAL)
  public static  T defer(@Nullable final T closeable) {
    if (closeable != null) {
      defer(new Deferred() {
        private static final long serialVersionUID = 2265124256013043847L;

        @Override
        public void executeDeferred() throws Exception {
          IOUtils.closeQuetly(closeable);
        }
      });
    }
    return closeable;
  }

  /**
   * Defer execution of some runnable action.
   *
   * @param runnable some runnable action to be executed in future
   * @return the same runnable object from arguments.
   * @throws AssertionError if the runnable object is null
   */
  @Weight(Weight.Unit.NORMAL)
  public static Runnable 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();
      }
    });
    return runnable;
  }

  /**
   * Defer execution of some disposable object.
   *
   * @param disposable some disposable object to be processed.
   * @return the same object from arguments
   * @throws AssertionError if the disposable object is null
   * @see Disposable
   */
  @Weight(Weight.Unit.NORMAL)
  public static Disposable 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();
      }
    });
    return disposable;
  }

  /**
   * 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 - 2024 Weber Informatics LLC | Privacy Policy