瀏覽代碼

#37 javadoc: trace

Ranides Atterwim 4 年之前
父節點
當前提交
ad06a15da6

+ 6 - 1
assira.core/src/main/java/net/ranides/assira/events/EventDispatcher.java

@@ -64,7 +64,7 @@ public class EventDispatcher implements EventRouter {
      * Creates named instant dispatcher.
      * Subclasses should use this constructor only inside protected or private constructors.
      *
-     * Correctly created dispatcher is always created by static method {@link #newInstance(String, Function)}.
+     * Correctly created dispatcher is always created by static method {@link #newInstance(String, Supplier)}.
      *
      * @param name name
      */
@@ -244,7 +244,10 @@ public class EventDispatcher implements EventRouter {
      * zupełnie innym wątku, aby zaimplementować inną strategię przetwarzania zdarzeń.
      *
      * <div class="message-note">thread-safe method</div>
+     *
      * @param event event
+     *
+     * @todo #85 and then document
      */
     protected void dispatchEvent(Event event) {
         dispatchEvent(event, false);
@@ -322,6 +325,8 @@ public class EventDispatcher implements EventRouter {
      *
      * <div class="message-note">thread-safe method</div>
      * @param event
+     *
+     * @todo #85 and then document
      */
     @Override
     public boolean signalEvent(Event event) {

+ 146 - 20
assira.core/src/main/java/net/ranides/assira/trace/ExceptionUtils.java

@@ -6,6 +6,7 @@
  */
 package net.ranides.assira.trace;
 
+import lombok.experimental.UtilityClass;
 import net.ranides.assira.collection.iterators.ForwardSpliterator;
 import net.ranides.assira.events.EventListener;
 import net.ranides.assira.events.Events;
@@ -22,27 +23,52 @@ import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 /**
+ * Utility methods for Exception objects.
  *
  * @author ranides
  */
-public final class ExceptionUtils {
+@UtilityClass
+public class ExceptionUtils {
 
-    private ExceptionUtils() { }
-    
+    /**
+     * Converts exception to String.
+     *
+     * It returns formatted stacktrace, but exact format is undefined.
+     * The point of the method is to return readable and meaningful description of exception.
+     *
+     * @param cause cause
+     * @return String
+     */
     public static String toString(Throwable cause) {
-        StringWriter out = new StringWriter();
-        cause.printStackTrace(new PrintWriter(out));
-        return out.toString();
+        return getStackTrace(cause);
     }
-	
+
+    /**
+     * Returns stream with chain of cause exceptions.
+     *
+     * @param cause cause
+     * @return stream
+     */
 	public static Stream<Throwable> getCauseStream(Throwable cause) {
 		return StreamSupport.stream(new CauseSpliterator(cause), false);
 	}
-	
+
+    /**
+     * Returns list with chain of cause exceptions.
+     *
+     * @param cause cause
+     * @return list
+     */
 	public static List<Throwable> getCauseList(Throwable cause) {
 		return getCauseStream(cause).collect(Collectors.toList());
     }
 
+    /**
+     * Returns formatted stacktrace as String
+     *
+     * @param cause cause
+     * @return string
+     */
     public static String getStackTrace(Throwable cause) {
         StringWriter result = new StringWriter();
         PrintWriter writer = new PrintWriter(result);
@@ -51,6 +77,22 @@ public final class ExceptionUtils {
         return result.toString();
     }
 
+    /**
+     * It is sneaky throw.
+     * It throws any passed exception, without declaring it, even check one.
+     *
+     * It can be used by simple call:
+     * ExceptionUtils.rethrow(new IOException(...))
+     *
+     * But preferred way is to write:
+     * throw ExceptionUtils.rethrow(new IOException(...))
+     *
+     * Second form tells the compiler, that we throw unconditionally,
+     * so flow analysis will work correctly.
+     *
+     * @param cause cause
+     * @return should be ingored
+     */
     public static RuntimeException rethrow(Throwable cause) {
         new ExceptionErasure<RuntimeException>().rethrow(cause);
         
@@ -86,19 +128,52 @@ public final class ExceptionUtils {
 		}
 
 	}
-    
+
+    /**
+     * It creates executor, which will collect all thrown exceptions as
+     * suppressed objects inside "E" created by provided supplier.
+     * Supplier will be called by executor only if some exception is thrown at least once.
+     *
+     * Returned executor allows to run any code by calling {@link GroupExceptions#run(CheckedRunnable)}
+     *
+     * @param supplier supplier
+     * @param <E> E
+     * @return executor
+     */
     public static <E extends Exception> GroupExceptions<E> group(Supplier<E> supplier) {
         return new GroupExceptions<>(supplier);
     }
-    
+
+    /**
+     * It creates executor, which will send all thrown exceptions to consumer.
+     *
+     * Returned executor allows to run any code by calling {@link ConsumeExceptions#run(CheckedRunnable)}
+     *
+     * @param consumer consumer
+     * @param <E> E
+     * @return executor
+     */
     public static <E extends Exception> ConsumeExceptions<E> consume(Consumer<E> consumer) {
         return new ConsumeExceptions<>(consumer);
     }
-    
+
+    /**
+     * It creates executor, which will send all thrown exceptions to event listener.
+     * Type of generated event is: {@link Events.Failure}.
+     *
+     * Returned executor allows to run any code by calling {@link ConsumeExceptions#run(CheckedRunnable)}
+     *
+     * @param listener listener
+     * @param <E> E
+     * @return executor
+     */
     public static <E extends Exception> ObserveExceptions observe(EventListener<? super Events.Failure> listener) {
         return new ObserveExceptions(listener);
     }
-    
+
+    /**
+     * Executor which allows execution of arbitrary code and sends events about thrown exceptions.
+     */
     public static class ObserveExceptions {
         
         private final EventListener<? super Events.Failure> listener;
@@ -106,7 +181,15 @@ public final class ExceptionUtils {
         ObserveExceptions(EventListener<? super Events.Failure> listener) {
             this.listener = listener;
         }
-        
+
+        /**
+         * Executes code and catches thrown exceptions.
+         * You can chain calls to this method.
+         *
+         * @param action action
+         * @param <T> T
+         * @return this
+         */
         public <T extends Exception> ObserveExceptions run(CheckedRunnable<T> action) {
             try {
                 action.run();
@@ -117,7 +200,12 @@ public final class ExceptionUtils {
         }
         
     }
-    
+
+    /**
+     * Executor which allows execution of arbitrary code and sends thrown exceptions to consumer.
+     *
+     * @param <E> E
+     */
     public static class ConsumeExceptions<E extends Exception> {
         
         private final Consumer<E> consumer;
@@ -126,6 +214,13 @@ public final class ExceptionUtils {
             this.consumer = consumer;
         }
 
+        /**
+         * Executes code and catches thrown exceptions.
+         * You can chain calls to this method.
+         *
+         * @param action action
+         * @return this
+         */
         @SuppressWarnings("unchecked")
         public ConsumeExceptions<E> run(CheckedRunnable<E> action) {
             try {
@@ -137,7 +232,16 @@ public final class ExceptionUtils {
         }
         
     }
-    
+
+    /**
+     * Executor which allows execution of arbitrary code and collects thrown exceptions.
+     *
+     * Exceptions are collected as "suppressed" inside parent exception created by "supplier".
+     *
+     * Parent exception is created only if at least one exception is thrown.
+     *
+     * @param <E>
+     */
     public static class GroupExceptions<E extends Exception> {
     
         private final Supplier<E> supplier;
@@ -147,7 +251,15 @@ public final class ExceptionUtils {
         GroupExceptions(Supplier<E> supplier) {
             this.supplier = supplier;
         }
-        
+
+        /**
+         * Executes code and catches thrown exceptions.
+         * You can chain calls to this method.
+         *
+         * @param action action
+         * @param <T> T
+         * @return this
+         */
         public <T extends Exception> GroupExceptions<E> run(CheckedRunnable<T> action) {
             try {
                 action.run();
@@ -159,15 +271,29 @@ public final class ExceptionUtils {
             }
             return this;
         }
-        
-        public Optional<E> cause() {
+
+        /**
+         * Returns "container exception" with cause exceptions added as "suppressed"
+         * If there was no error then it returns none
+         *
+         * @return optional
+         */
+        public Optional<E> exception() {
             return Optional.ofNullable(exception);
         }
-        
-        public void rethrow() throws E {
+
+        /**
+         * Throws "container exception" if there was any error at execution time.
+         * If there was no error then it returns "false".
+         *
+         * @throws E E
+         * @return boolean
+         */
+        public boolean rethrow() throws E {
             if(null != exception) {
                 throw exception;
             }
+            return false;
         }
         
     }

+ 52 - 16
assira.core/src/main/java/net/ranides/assira/trace/LoggerLevel.java

@@ -8,39 +8,44 @@ package net.ranides.assira.trace;
 
 import java.util.function.IntPredicate;
 import org.slf4j.Logger;
-import static org.slf4j.spi.LocationAwareLogger.*;
+import org.slf4j.spi.LocationAwareLogger;
 
 /**
+ * Enum class with levels used by SLF4J.
+ *
+ * We defined this enum, because in SLF4J you can't "just" check enabled level by calling method like "isEnabled(level)".
+ * You have to call different methods which makes writing general logging code painful, if you want to be level-independent.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
+@SuppressWarnings("MissingJavadoc")
 public enum LoggerLevel implements IntPredicate {
-    
-    TRACE(TRACE_INT) {
+
+    TRACE(LocationAwareLogger.TRACE_INT) {
         @Override
         public boolean isEnabled(Logger logger) {
             return logger.isTraceEnabled();
         }
     },
-    DEBUG(DEBUG_INT) {
+    DEBUG(LocationAwareLogger.DEBUG_INT) {
         @Override
         public boolean isEnabled(Logger logger) {
             return logger.isDebugEnabled();
         }
     },
-    INFO(INFO_INT) {
+    INFO(LocationAwareLogger.INFO_INT) {
         @Override
         public boolean isEnabled(Logger logger) {
             return logger.isInfoEnabled();
         }
     },
-    WARN(WARN_INT) {
+    WARN(LocationAwareLogger.WARN_INT) {
         @Override
         public boolean isEnabled(Logger logger) {
             return logger.isWarnEnabled();
         }
     },
-    ERROR(ERROR_INT) {
+    ERROR(LocationAwareLogger.ERROR_INT) {
         @Override
         public boolean isEnabled(Logger logger) {
             return logger.isErrorEnabled();
@@ -58,18 +63,41 @@ public enum LoggerLevel implements IntPredicate {
     LoggerLevel(int level) {
         this.level = level;
     }
-    
+
+    /**
+     * Returns level as "int". Compatible with constants from {@link LocationAwareLogger}.
+     *
+     * @return int
+     */
     public int level() {
         return level;
     }
-    
+
+    /**
+     * Returns true if provided logger is enabled for level represented by this enum.
+     *
+     * @param logger logger
+     * @return boolean
+     */
     public abstract boolean isEnabled(Logger logger);
-    
+
+    /**
+     * Returns "true" if provided level is higher than or equal to this enum.
+     *
+     * @param level level
+     * @return boolean
+     */
     @Override // NOPMD
     public boolean test(int level) {
         return level >= this.level;
     }
 
+    /**
+     * Returns most detailed level enabled in provided logger.
+     *
+     * @param logger logger
+     * @return level
+     */
     public static LoggerLevel valueOf(Logger logger) {
         if(logger.isTraceEnabled()) {
             return TRACE;
@@ -88,18 +116,26 @@ public enum LoggerLevel implements IntPredicate {
         }
         return UNKNOWN;
     }
-    
+
+    /**
+     * Returns enum for provided "int" level.
+     *
+     * Compatible with constants from {@link LocationAwareLogger}.
+     *
+     * @param level level
+     * @return level
+     */
     public static LoggerLevel valueOf(int level) {
         switch(level) {
-            case TRACE_INT:
+            case LocationAwareLogger.TRACE_INT:
                 return TRACE;
-            case DEBUG_INT:
+            case LocationAwareLogger.DEBUG_INT:
                 return DEBUG;
-            case INFO_INT:
+            case LocationAwareLogger.INFO_INT:
                 return INFO;
-            case WARN_INT:
+            case LocationAwareLogger.WARN_INT:
                 return WARN;
-            case ERROR_INT:
+            case LocationAwareLogger.ERROR_INT:
                 return ERROR;
             default:
                 return UNKNOWN;

+ 26 - 7
assira.core/src/main/java/net/ranides/assira/trace/LoggerUtils.java

@@ -6,32 +6,46 @@
  */
 package net.ranides.assira.trace;
 
+import lombok.experimental.UtilityClass;
 import org.slf4j.LoggerFactory;
 
 /**
+ * Utility methods for SLF4J and java.util.logging
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
-public final class LoggerUtils {
-    
-    private LoggerUtils() {
-        /* utility class */
-    }
-    
+@UtilityClass
+public class LoggerUtils {
+
     /**
      * Returns logger for the "current" class, so you don't have to hardcode class name.
+     * Can be invoked from static context.
+     *
      * @return Logger
      */
     public static org.slf4j.Logger getLogger() {
         return LoggerFactory.getLogger(TraceUtils.getCallerName());
     }
-    
+
+    /**
+     * Installs default uncaught exception handler for all threads.
+     * Handler will log error message using provided logger.
+     *
+     * @param logger
+     */
     public static void setThreadLogger(org.slf4j.Logger logger) {
         Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> {
             logger.error("Abnormal termination of thread: " + thread.getName(), exception);
         });
     }
 
+    /**
+     * Resets java.util configuration and changes global logging level.
+     * Returns global logger which can be used for additional configuration.
+     *
+     * @param level level
+     * @return logger
+     */
     public static java.util.logging.Logger resetJavaLogger(java.util.logging.Level level) {
         java.util.logging.LogManager.getLogManager().reset();
         java.util.logging.Logger global = java.util.logging.Logger.getLogger(java.util.logging.Logger.GLOBAL_LOGGER_NAME);
@@ -39,6 +53,11 @@ public final class LoggerUtils {
         return global;
     }
 
+    /**
+     * Removes all ConsoleHandlers from provided logger
+     *
+     * @param logger logger
+     */
     public static void removeHandlers(final java.util.logging.Logger logger) {
         for(java.util.logging.Handler handler : logger.getHandlers()) {
             if( handler instanceof java.util.logging.ConsoleHandler) {

+ 58 - 5
assira.core/src/main/java/net/ranides/assira/trace/NSTException.java

@@ -11,6 +11,15 @@ import java.util.concurrent.atomic.AtomicInteger;
 import net.ranides.assira.text.ResolveFormatUtils;
 
 /**
+ * [N]o [S]tack [T]race Runtime Exception.
+ * This exception does not store stacktrace.
+ * You can use it for security of performance reasons.
+ *
+ * Please note, that performance gain is quite big:
+ * stacktrace generation is very expensive operation when exception is thrown.
+ *
+ * Please note, that this exception has unique ID generated at construction time.
+ * It uses global, non-blocking, atomic counter.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
@@ -31,36 +40,80 @@ public class NSTException extends RuntimeException {
     private final String uid;
     private final String pattern;
     private final Object[] arguments;
-    
+
+    /**
+     * Creates new exception
+     */
     public NSTException() {
         this(EMPTY_TEXT, EMPTY_ARGS);
     }
 
+    /**
+     * Creates new exception
+     *
+     * @param message message
+     */
     public NSTException(String message) {
         this(message, EMPTY_ARGS);
     }
-    
+
+    /**
+     * Creates new exception.
+     * Message is lazily formatted using {@link ResolveFormatUtils#vformat(String, Object...)}
+     *
+     * @param message message
+     * @param arguments arguments
+     */
     public NSTException(String message, Object... arguments) {
         this.uid = initUID();
         this.pattern = message;
         this.arguments = arguments;
     }
-    
+
+    /**
+     * Creates new exception.
+     *
+     * @param cause cause
+     */
     public NSTException(Throwable cause) {
         this(cause, EMPTY_TEXT, EMPTY_ARGS);
     }
 
+    /**
+     * Creates new exception.
+     *
+     * @param cause cause
+     * @param message message
+     */
     public NSTException(Throwable cause, String message) {
         this(cause, message, EMPTY_ARGS);
     }
-    
+
+    /**
+     * Creates new exception.
+     * Mssage is lazily formatted using {@link ResolveFormatUtils#vformat(String, Object...)}
+     *
+     * @param cause cause
+     * @param message message
+     * @param arguments arguments
+     */
     public NSTException(Throwable cause, String message, Object... arguments) {
         super(cause);
         this.uid = initUID();
         this.pattern = message;
         this.arguments = arguments;
     }
-    
+
+    /**
+     * Returns UID generated at construction time.
+     * Format of generated UID is undefined and it could be any string.
+     *
+     * UID will be unique in current instance of VM.
+     *
+     * We try to generate UID unique across VMs, but it is not guaranteed.
+     *
+     * @return String
+     */
     public String uid() {
         return uid;
     }

+ 2 - 4
assira.core/src/main/java/net/ranides/assira/trace/PreparedException.java

@@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import lombok.experimental.Accessors;
 
+import lombok.experimental.UtilityClass;
 import net.ranides.assira.functional.special.AnyFunction;
 import net.ranides.assira.functional.Compositor;
 import net.ranides.assira.functional.Compositor.CompositorBuilder;
@@ -31,10 +32,7 @@ import java.util.function.Supplier;
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
-public class PreparedException {
-    private PreparedException() {
-        // this is utility class
-    }
+@UtilityClass public class PreparedException {
 
     /**
      * Prepare new unchecked exception.

+ 9 - 9
assira.core/src/main/java/net/ranides/assira/trace/PreparedMessage.java

@@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import lombok.experimental.Accessors;
 
+import lombok.experimental.UtilityClass;
 import net.ranides.assira.functional.special.AnyFunction;
 import net.ranides.assira.functional.Compositor;
 import net.ranides.assira.functional.Compositor.CompositorBuilder;
@@ -29,11 +30,14 @@ import java.util.function.Consumer;
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
-public class PreparedMessage {
-    private PreparedMessage() {
-        // this is utlity class
-    }
+@UtilityClass public class PreparedMessage {
 
+    /**
+     * Starts building new message with "current class" specified as a tag.
+     * Can be invoked from static context.
+     *
+     * @return builder
+     */
     public static MessageBuilder forThis() {
         return forClass(TraceUtils.getCallerName());
     }
@@ -122,6 +126,7 @@ public class PreparedMessage {
 
         /**
          * Configuration of arguments is delegated to functional compositor
+         * Composition should be terminated using "function" method.
          *
          * @return compositor
          */
@@ -130,11 +135,6 @@ public class PreparedMessage {
             return Compositor.compose(new Decorator());
         }
 
-        /**
-         * This decorator is used to create prepared messages which format text using additional arguments
-         *
-         * @since 2021-05-05
-         */
         private class Decorator extends Compositor.CompositorDecorator<MessageBuilder, Object, Boolean> {
             @Override
             public Function0<Boolean> before(Function0<Boolean> action) {