Mariusz Sieroń 6 年 前
コミット
d7546883fa

+ 174 - 0
assira/src/main/java/net/ranides/assira/trace/ExceptionUtils.java

@@ -0,0 +1,174 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import net.ranides.assira.collection.iterators.ForwardSpliterator;
+import net.ranides.assira.events.EventListener;
+import net.ranides.assira.events.Events;
+import net.ranides.assira.functional.CheckedRunnable;
+
+/**
+ *
+ * @author ranides
+ */
+public final class ExceptionUtils {
+
+    private ExceptionUtils() { }
+    
+    public static String toString(Throwable cause) {
+        StringWriter out = new StringWriter();
+        cause.printStackTrace(new PrintWriter(out));
+        return out.toString();
+    }
+	
+	public static Stream<Throwable> getCauseStream(Throwable cause) {
+		return StreamSupport.stream(new CauseSpliterator(cause), false);
+	}
+	
+	public static List<Throwable> getCauseList(Throwable cause) {
+		return getCauseStream(cause).collect(Collectors.toList());
+    }
+
+    public static String getStackTrace(Throwable cause) {
+        StringWriter result = new StringWriter();
+        PrintWriter writer = new PrintWriter(result);
+        cause.printStackTrace(writer);
+        writer.flush();
+        return result.toString();
+    }
+
+    public static RuntimeException rethrow(Throwable cause) {
+        new ExceptionErasure<RuntimeException>().rethrow(cause);
+        
+        // not reachable, wyjątek bez wrappowania leci linię wyżej
+        throw new RuntimeException(cause);  // NOPMD
+    }
+    
+    private static class ExceptionErasure<T extends Throwable> { // NOPMD
+        
+        @SuppressWarnings("unchecked")
+        private void rethrow(Throwable exception) throws T {
+            throw (T) exception;
+        }
+
+    }
+	
+	private static final class CauseSpliterator extends ForwardSpliterator<Throwable> {
+		
+		private Throwable current;
+		
+		public CauseSpliterator(Throwable current) {
+			this.current = current;
+		}
+
+		@Override
+		public boolean tryAdvance(Consumer<? super Throwable> action) {
+            if(null == current) {
+                return false;
+            }
+            action.accept(current);
+            current = current.getCause();
+            return true;
+		}
+
+	}
+    
+    public static <E extends Exception> GroupExceptions<E> group(Supplier<E> supplier) {
+        return new GroupExceptions<>(supplier);
+    }
+    
+    public static <E extends Exception> ConsumeExceptions<E> consume(Consumer<E> consumer) {
+        return new ConsumeExceptions<>(consumer);
+    }
+    
+    public static <E extends Exception> ObserveExceptions observe(EventListener<? super Events.Failure> listener) {
+        return new ObserveExceptions(listener);
+    }
+    
+    public static class ObserveExceptions {
+        
+        private final EventListener<? super Events.Failure> listener;
+        
+        ObserveExceptions(EventListener<? super Events.Failure> listener) {
+            this.listener = listener;
+        }
+        
+        public <T extends Exception> ObserveExceptions run(CheckedRunnable<T> action) {
+            try {
+                action.run();
+            } catch(Exception cause) {
+                listener.handleEvent(Events.failure(cause));
+            }
+            return this;
+        }
+        
+    }
+    
+    public static class ConsumeExceptions<E extends Exception> {
+        
+        private final Consumer<E> consumer;
+        
+        ConsumeExceptions(Consumer<E> consumer) {
+            this.consumer = consumer;
+        }
+
+        @SuppressWarnings("unchecked")
+        public ConsumeExceptions<E> run(CheckedRunnable<E> action) {
+            try {
+                action.run();
+            } catch(Exception cause) {
+                consumer.accept((E)cause);
+            }
+            return this;
+        }
+        
+    }
+    
+    public static class GroupExceptions<E extends Exception> {
+    
+        private final Supplier<E> supplier;
+        
+        private E exception;
+        
+        GroupExceptions(Supplier<E> supplier) {
+            this.supplier = supplier;
+        }
+        
+        public <T extends Exception> GroupExceptions<E> run(CheckedRunnable<T> action) {
+            try {
+                action.run();
+            } catch(Exception cause) {
+                if(exception == null) {
+                    exception = supplier.get();
+                }
+                exception.addSuppressed(cause);
+            }
+            return this;
+        }
+        
+        public Optional<E> cause() {
+            return Optional.ofNullable(exception);
+        }
+        
+        public void rethrow() throws E {
+            if(null != exception) {
+                throw exception;
+            }
+        }
+        
+    }
+    
+}

+ 109 - 0
assira/src/main/java/net/ranides/assira/trace/LoggerLevel.java

@@ -0,0 +1,109 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.util.function.IntPredicate;
+import org.slf4j.Logger;
+import static org.slf4j.spi.LocationAwareLogger.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public enum LoggerLevel implements IntPredicate {
+    
+    TRACE(TRACE_INT) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return logger.isTraceEnabled();
+        }
+    },
+    DEBUG(DEBUG_INT) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return logger.isDebugEnabled();
+        }
+    },
+    INFO(INFO_INT) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return logger.isInfoEnabled();
+        }
+    },
+    WARN(WARN_INT) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return logger.isWarnEnabled();
+        }
+    },
+    ERROR(ERROR_INT) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return logger.isErrorEnabled();
+        }
+    },
+    UNKNOWN(Integer.MAX_VALUE) {
+        @Override
+        public boolean isEnabled(Logger logger) {
+            return true;
+        }
+    };
+    
+    private final int level;
+    
+    LoggerLevel(int level) {
+        this.level = level;
+    }
+    
+    public int level() {
+        return level;
+    }
+    
+    public abstract boolean isEnabled(Logger logger);
+    
+    @Override // NOPMD
+    public boolean test(int level) {
+        return level >= this.level;
+    }
+
+    public static LoggerLevel valueOf(Logger logger) {
+        if(logger.isTraceEnabled()) {
+            return TRACE;
+        }
+        if(logger.isDebugEnabled()) {
+            return DEBUG;
+        }
+        if(logger.isInfoEnabled()) {
+            return INFO;
+        }
+        if(logger.isWarnEnabled()) {
+            return WARN;
+        }
+        if(logger.isErrorEnabled()) {
+            return ERROR;
+        }
+        return UNKNOWN;
+    }
+    
+    public static LoggerLevel valueOf(int level) {
+        switch(level) {
+            case TRACE_INT:
+                return TRACE;
+            case DEBUG_INT:
+                return DEBUG;
+            case INFO_INT:
+                return INFO;
+            case WARN_INT:
+                return WARN;
+            case ERROR_INT:
+                return ERROR;
+            default:
+                return UNKNOWN;
+        }
+    }
+    
+}

+ 50 - 0
assira/src/main/java/net/ranides/assira/trace/LoggerUtils.java

@@ -0,0 +1,50 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class LoggerUtils {
+    
+    private LoggerUtils() {
+        /* utility class */
+    }
+    
+    /**
+     * Returns logger for the "current" class, so you don't have to hardcode class name.
+     * @return 
+     */
+    public static org.slf4j.Logger getLogger() {
+        return LoggerFactory.getLogger(TraceUtils.getCallerName());
+    }
+    
+    public static void setThreadLogger(org.slf4j.Logger logger) {
+        Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> {
+            logger.error("Abnormal termination of thread: " + thread.getName(), exception);
+        });
+    }
+
+    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);
+        global.setLevel(level);
+        return global;
+    }
+
+    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) {
+                logger.removeHandler(handler);
+            }
+        }
+    }
+    
+}

+ 93 - 0
assira/src/main/java/net/ranides/assira/trace/NSTException.java

@@ -0,0 +1,93 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import net.ranides.assira.text.ResolveFormatUtils;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class NSTException extends RuntimeException {
+
+    private static final String EMPTY_TEXT = "";
+    
+    private static final Object[] EMPTY_ARGS = new Object[0];
+    
+    private static final AtomicInteger COUNTER = new AtomicInteger(0);
+    
+    private static final class Li { // NOPMD - lazy init idiom
+        
+        public static final String VMNAME = ManagementFactory.getRuntimeMXBean().getName();
+        
+    }
+    
+    private final String uid;
+    private final String pattern;
+    private final Object[] arguments;
+    
+    public NSTException() {
+        this(EMPTY_TEXT, EMPTY_ARGS);
+    }
+
+    public NSTException(String message) {
+        this(message, EMPTY_ARGS);
+    }
+    
+    public NSTException(String message, Object... arguments) {
+        this.uid = initUID();
+        this.pattern = message;
+        this.arguments = arguments;
+    }
+    
+    public NSTException(Throwable cause) {
+        this(cause, EMPTY_TEXT, EMPTY_ARGS);
+    }
+
+    public NSTException(Throwable cause, String message) {
+        this(cause, message, EMPTY_ARGS);
+    }
+    
+    public NSTException(Throwable cause, String message, Object... arguments) {
+        super(cause);
+        this.uid = initUID();
+        this.pattern = message;
+        this.arguments = arguments;
+    }
+    
+    public String uid() {
+        return uid;
+    }
+    
+    @Override
+    public Throwable fillInStackTrace() {
+        return this;
+    }
+    
+    @Override
+    public String getMessage() {
+        return ResolveFormatUtils.vformat(pattern, arguments);
+    }
+
+    @Override
+    public String getLocalizedMessage() {
+        return getMessage();
+    }
+
+    @Override
+    public String toString() {
+        String s = getClass().getName();
+        String m = getLocalizedMessage();
+        return s + "(" + uid + "): " + m;
+    }
+    
+    private static String initUID() {
+        return Li.VMNAME + "@" + COUNTER.incrementAndGet();
+    }
+}

+ 77 - 0
assira/src/main/java/net/ranides/assira/trace/ThreadUtils.java

@@ -0,0 +1,77 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.lang.management.LockInfo;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.function.Consumer;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class ThreadUtils {
+	
+	private final ThreadInfo info;
+
+	private ThreadUtils(long cid) {
+        info = Li.THREAD_INFO.getThreadInfo(new long[]{ cid }, true, true)[0];
+	}
+    
+	public static ThreadUtils get() {
+		return get(Thread.currentThread());
+	}
+	
+	public static ThreadUtils get(Thread thread) {
+		return new ThreadUtils(thread.getId());
+	}
+	
+	public static void dump(boolean condition, Consumer<String> out) {
+		if(condition) {
+			ThreadUtils info = ThreadUtils.get();
+			if(info.locks() > 0) {
+				info.dump(out);
+			}
+		}
+	}
+	
+	public void dump(Consumer<String> out) {
+		out.accept(String.format("THREAD: %s 0x%08X (monitors=%d, locks=%d)", 
+            info.getThreadName(), 
+            info.getThreadId(),
+			info.getLockedMonitors().length,
+			info.getLockedSynchronizers().length
+        ));
+        for(MonitorInfo item : info.getLockedMonitors()) {
+            out.accept(String.format(" - MONITOR : %s 0x%08X @ %s",
+                item.getClassName(),
+                item.getIdentityHashCode(),
+                item.getLockedStackFrame()
+            ));
+        }
+        for(LockInfo item : info.getLockedSynchronizers()) {
+            out.accept(String.format(" - LOCK : %s 0x%08X", 
+                item.getClassName(), 
+                item.getIdentityHashCode()
+            ));
+        }
+	}
+
+    public int locks() {
+        return info.getLockedMonitors().length + info.getLockedSynchronizers().length;
+    }
+
+    private static final class Li { // NOPMD lazy init idiom 
+        
+        public static final ThreadMXBean THREAD_INFO = ManagementFactory.getThreadMXBean();
+		
+    }
+    
+}

+ 122 - 0
assira/src/main/java/net/ranides/assira/trace/TraceUtils.java

@@ -0,0 +1,122 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.trace;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public final class TraceUtils {
+
+    interface IStackWalker {
+
+        List<StackTraceElement> getFrames();
+
+        List<Class<?>> getTypes();
+
+        List<String> getNames();
+
+        Optional<StackTraceElement> getFrame(Predicate<StackTraceElement> filter);
+
+        Optional<Class<?>> getType(Predicate<Class<?>> filter);
+
+        Optional<String> getName(Predicate<String> filter);
+
+        StackTraceElement getFrame(int index);
+
+        Class<?> getType(int index);
+
+        String getName(int index);
+
+    }
+
+    private static final IStackWalker WALKER = init(
+        "net.ranides.assira.trace.FStackWalker11",
+        "net.ranides.assira.trace.FStackWalker8",
+        "net.ranides.assira.trace.FStackWalker6"
+    );
+
+    private static IStackWalker init(String... names) {
+        for(String name : names) {
+            try {
+                return (IStackWalker)Class.forName(name).newInstance();
+            } catch (ReflectiveOperationException ex) {
+                // continue
+            }
+        }
+        throw new UnsatisfiedLinkError("There is no IStackWalker implementation. Add dependency: assira.jdk8 or assira.jdk11");
+    }
+
+    private TraceUtils() {
+        /* utility class */
+    }
+
+    public static List<StackTraceElement> getFrames() {
+        return WALKER.getFrames();
+    }
+
+    public static List<Class<?>> getTypes() {
+        return WALKER.getTypes();
+    }
+
+    public static List<String> getNames() {
+        return WALKER.getNames();
+    }
+
+    public static Optional<StackTraceElement> getFrame(Predicate<StackTraceElement> filter) {
+        return WALKER.getFrame(filter);
+    }
+
+    public static Optional<Class<?>> getType(Predicate<Class<?>> filter) {
+        return WALKER.getType(filter);
+    }
+
+    public static Optional<String> getName(Predicate<String> filter) {
+        return WALKER.getName(filter);
+    }
+
+    public static StackTraceElement getFrame(int index) {
+        return WALKER.getFrame(index);
+    }
+
+    public static Class<?> getType(int index) {
+        return WALKER.getType(index);
+    }
+
+    public static String getName(int index) {
+        return WALKER.getName(index);
+    }
+
+    public static StackTraceElement getCalleeFrame() {
+        return getFrame(1);
+    }
+
+    public static Class<?> getCalleeType() {
+        return getType(1);
+    }
+
+    public static String getCalleeName() {
+        return getName(1);
+    }
+
+    public static StackTraceElement getCallerFrame() {
+        return getFrame(2);
+    }
+
+    public static Class<?> getCallerType() {
+        return getType(2);
+    }
+
+    public static String getCallerName() {
+        return getName(2);
+    }
+
+}