Coverage Report - com.jcabi.log.VerboseRunnable
 
Classes in this File Line Coverage Branch Coverage Complexity
VerboseRunnable
0%
0/39
0%
0/28
2.3
VerboseRunnable$1
0%
0/10
N/A
2.3
 
 1  
 /**
 2  
  * Copyright (c) 2012-2015, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.log;
 31  
 
 32  
 import java.util.concurrent.Callable;
 33  
 import java.util.concurrent.TimeUnit;
 34  
 import lombok.EqualsAndHashCode;
 35  
 import lombok.ToString;
 36  
 
 37  
 /**
 38  
  * Wrapper of {@link Runnable}, that logs all uncaught runtime exceptions.
 39  
  *
 40  
  * <p>You can use it with scheduled executor, for example:
 41  
  *
 42  
  * <pre> Executors.newScheduledThreadPool(2).scheduleAtFixedRate(
 43  
  *   new VerboseRunnable(runnable, true), 1L, 1L, TimeUnit.SECONDS
 44  
  * );</pre>
 45  
  *
 46  
  * <p>Now, every runtime exception that is not caught inside your
 47  
  * {@link Runnable} will be reported to log (using {@link Logger}).
 48  
  * Two-arguments constructor can be used when you need to instruct the class
 49  
  * about what to do with the exception: either swallow it or escalate.
 50  
  * Sometimes it's very important to swallow exceptions. Otherwise an entire
 51  
  * thread may get stuck (like in the example above).
 52  
  *
 53  
  * <p>This class is thread-safe.
 54  
  *
 55  
  * @author Yegor Bugayenko (yegor@teamed.io)
 56  
  * @version $Id: 60c96a9e004e9017e14f9fec78c37aea754c1527 $
 57  
  * @since 0.1.3
 58  
  * @see VerboseThreads
 59  
  * @link <a href="http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html">Java theory and practice: Dealing with InterruptedException</a>
 60  
  */
 61  0
 @ToString
 62  0
 @EqualsAndHashCode(of = { "origin", "rethrow", "verbose" })
 63  
 @SuppressWarnings("PMD.DoNotUseThreads")
 64  
 public final class VerboseRunnable implements Runnable {
 65  
 
 66  
     /**
 67  
      * Original runnable.
 68  
      */
 69  
     private final transient Runnable origin;
 70  
 
 71  
     /**
 72  
      * Rethrow exceptions (TRUE) or swallow them?
 73  
      */
 74  
     private final transient boolean rethrow;
 75  
 
 76  
     /**
 77  
      * Shall we report a full stacktrace?
 78  
      */
 79  
     private final transient boolean verbose;
 80  
 
 81  
     /**
 82  
      * Default constructor, doesn't swallow exceptions.
 83  
      * @param runnable Runnable to wrap
 84  
      */
 85  
     public VerboseRunnable(final Runnable runnable) {
 86  0
         this(runnable, false);
 87  0
     }
 88  
 
 89  
     /**
 90  
      * Default constructor, doesn't swallow exceptions.
 91  
      * @param callable Callable to wrap
 92  
      * @since 0.7.17
 93  
      */
 94  
     public VerboseRunnable(final Callable<?> callable) {
 95  0
         this(callable, false);
 96  0
     }
 97  
 
 98  
     /**
 99  
      * Default constructor, doesn't swallow exceptions.
 100  
      * @param callable Callable to wrap
 101  
      * @param swallow Shall we swallow exceptions
 102  
      *  ({@code TRUE}) or re-throw
 103  
      *  ({@code FALSE})? Exception swallowing means that {@link #run()}
 104  
      *  will never throw any exceptions (in any case all exceptions are logged
 105  
      *  using {@link Logger}.
 106  
      * @since 0.1.10
 107  
      */
 108  
     public VerboseRunnable(final Callable<?> callable, final boolean swallow) {
 109  0
         this(callable, swallow, true);
 110  0
     }
 111  
 
 112  
     /**
 113  
      * Default constructor.
 114  
      * @param callable Callable to wrap
 115  
      * @param swallow Shall we swallow exceptions
 116  
      *  ({@code TRUE}) or re-throw
 117  
      *  ({@code FALSE})? Exception swallowing means that {@link #run()}
 118  
      *  will never throw any exceptions (in any case all exceptions are logged
 119  
      *  using {@link Logger}.
 120  
      * @param vrbs Shall we report the entire
 121  
      *  stacktrace of the exception
 122  
      *  ({@code TRUE}) or just its message in one line ({@code FALSE})
 123  
      * @since 0.7.17
 124  
      */
 125  
     @SuppressWarnings("PMD.AvoidCatchingGenericException")
 126  
     public VerboseRunnable(final Callable<?> callable,
 127  
         final boolean swallow, final boolean vrbs) {
 128  0
         this(
 129  0
             new Runnable() {
 130  
                 @Override
 131  
                 public void run() {
 132  
                     try {
 133  0
                         callable.call();
 134  0
                     } catch (final InterruptedException ex) {
 135  0
                         Thread.currentThread().interrupt();
 136  0
                         throw new IllegalStateException(ex);
 137  
                     // @checkstyle IllegalCatch (1 line)
 138  0
                     } catch (final Exception ex) {
 139  0
                         throw new IllegalStateException(ex);
 140  0
                     }
 141  0
                 }
 142  
                 @Override
 143  
                 public String toString() {
 144  0
                     return callable.toString();
 145  
                 }
 146  
             },
 147  
             swallow,
 148  
             vrbs
 149  
         );
 150  0
     }
 151  
 
 152  
     /**
 153  
      * Default constructor, with configurable behavior for exceptions.
 154  
      * @param runnable Runnable to wrap
 155  
      * @param swallow Shall we swallow exceptions
 156  
      *  ({@code TRUE}) or re-throw
 157  
      *  ({@code FALSE})? Exception swallowing means that {@link #run()}
 158  
      *  will never throw any exceptions (in any case all exceptions are logged
 159  
      *  using {@link Logger}.
 160  
      * @since 0.1.4
 161  
      */
 162  
     public VerboseRunnable(final Runnable runnable, final boolean swallow) {
 163  0
         this(runnable, swallow, true);
 164  0
     }
 165  
 
 166  
     /**
 167  
      * Default constructor, with fully configurable behavior.
 168  
      * @param runnable Runnable to wrap
 169  
      * @param swallow Shall we swallow exceptions
 170  
      *  ({@code TRUE}) or re-throw
 171  
      *  ({@code FALSE})? Exception swallowing means that {@link #run()}
 172  
      *  will never throw any exceptions (in any case all exceptions are logged
 173  
      *  using {@link Logger}.
 174  
      * @param vrbs Shall we report the entire
 175  
      *  stacktrace of the exception
 176  
      *  ({@code TRUE}) or just its message in one line ({@code FALSE})
 177  
      * @since 0.7.17
 178  
      */
 179  
     @SuppressWarnings("PMD.BooleanInversion")
 180  
     public VerboseRunnable(final Runnable runnable,
 181  0
         final boolean swallow, final boolean vrbs) {
 182  0
         this.origin = runnable;
 183  0
         this.rethrow = !swallow;
 184  0
         this.verbose = vrbs;
 185  0
     }
 186  
 
 187  
     /**
 188  
      * {@inheritDoc}
 189  
      *
 190  
      * <p>We catch {@link RuntimeException} and {@link Error} here. All other
 191  
      * types of exceptions are "checked exceptions" and won't be thrown out
 192  
      * of {@code Runnable#run()} method.
 193  
      */
 194  
     @Override
 195  
     @SuppressWarnings("PMD.AvoidCatchingGenericException")
 196  
     public void run() {
 197  
         try {
 198  0
             this.origin.run();
 199  
         // @checkstyle IllegalCatch (1 line)
 200  0
         } catch (final RuntimeException ex) {
 201  0
             if (this.rethrow) {
 202  0
                 Logger.warn(this, "escalated exception: %s", this.tail(ex));
 203  0
                 throw ex;
 204  
             }
 205  0
             Logger.warn(this, "swallowed exception: %s", this.tail(ex));
 206  
         // @checkstyle IllegalCatch (1 line)
 207  0
         } catch (final Error error) {
 208  0
             if (this.rethrow) {
 209  0
                 Logger.error(this, "escalated error: %s", this.tail(error));
 210  0
                 throw error;
 211  
             }
 212  0
             Logger.error(this, "swallowed error: %s", this.tail(error));
 213  0
         }
 214  
         try {
 215  0
             TimeUnit.MICROSECONDS.sleep(1L);
 216  0
         } catch (final InterruptedException ex) {
 217  0
             Thread.currentThread().interrupt();
 218  0
             throw new IllegalStateException(ex);
 219  0
         }
 220  0
     }
 221  
 
 222  
     /**
 223  
      * Make a tail of the error/warning message, using the exception thrown.
 224  
      * @param throwable The exception/error caught
 225  
      * @return The message to show in logs
 226  
      */
 227  
     private String tail(final Throwable throwable) {
 228  
         final String tail;
 229  0
         if (this.verbose) {
 230  0
             tail = Logger.format("%[exception]s", throwable);
 231  
         } else {
 232  0
             tail = Logger.format(
 233  
                 "%[type]s('%s')",
 234  
                 throwable,
 235  
                 throwable.getMessage()
 236  
             );
 237  
         }
 238  0
         return tail;
 239  
     }
 240  
 
 241  
 }