com.igormaznitsa.meta.common.utils.Deferrers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcp Show documentation
Show all versions of jcp Show documentation
Powerful multi-pass preprocessor to process directives situated in C-styled commentaries
/*
* 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;
}
}