Coverage Report - com.jcabi.log.VerboseThreads
 
Classes in this File Line Coverage Branch Coverage Complexity
VerboseThreads
0%
0/22
0%
0/34
1.4
VerboseThreads$Group
0%
0/4
N/A
1.4
VerboseThreads$Wrap
0%
0/12
N/A
1.4
 
 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.ThreadFactory;
 33  
 import java.util.concurrent.atomic.AtomicInteger;
 34  
 import lombok.EqualsAndHashCode;
 35  
 import lombok.ToString;
 36  
 
 37  
 /**
 38  
  * Convenient {@link ThreadFactory}, that logs all uncaught exceptions.
 39  
  *
 40  
  * <p>The factory should be used together
 41  
  * with executor services from {@code java.util.concurrent} package. Without
 42  
  * these "verbose" threads your runnable tasks will not report anything to
 43  
  * console once they die because of a runtime exception, for example:
 44  
  *
 45  
  * <pre> Executors.newScheduledThreadPool(2).scheduleAtFixedRate(
 46  
  *   new Runnable() {
 47  
  *     &#64;Override
 48  
  *     public void run() {
 49  
  *       // some sensitive operation that may throw
 50  
  *       // a runtime exception
 51  
  *     },
 52  
  *     1L, 1L, TimeUnit.SECONDS
 53  
  *   }
 54  
  * );</pre>
 55  
  *
 56  
  * <p>The exception in this example will never be caught by nobody. It will
 57  
  * just terminate current execution of the {@link Runnable} task. Moreover,
 58  
  * it won't reach any {@link Thread.UncaughtExceptionHandler},
 59  
  * because this
 60  
  * is how {@link java.util.concurrent.ScheduledExecutorService}
 61  
  * is behaving. This is how we solve
 62  
  * the problem with {@link VerboseThreads}:
 63  
  *
 64  
  * <pre> ThreadFactory factory = new VerboseThreads();
 65  
  * Executors.newScheduledThreadPool(2, factory).scheduleAtFixedRate(
 66  
  *   new Runnable() {
 67  
  *     &#64;Override
 68  
  *     public void run() {
 69  
  *       // the same sensitive operation that may throw
 70  
  *       // a runtime exception
 71  
  *     },
 72  
  *     1L, 1L, TimeUnit.SECONDS
 73  
  *   }
 74  
  * );</pre>
 75  
  *
 76  
  * <p>Now, every runtime exception that is not caught inside your
 77  
  * {@link Runnable} will be reported to log (using {@link Logger}).
 78  
  *
 79  
  * <p>This class is thread-safe.
 80  
  *
 81  
  * @author Yegor Bugayenko (yegor@teamed.io)
 82  
  * @version $Id: ab2ee168b7585550ffd05251b381a8999de83786 $
 83  
  * @since 0.1.2
 84  
  * @see VerboseRunnable
 85  
  */
 86  0
 @ToString
 87  0
 @EqualsAndHashCode(of = { "group", "prefix", "number", "daemon", "priority" })
 88  
 @SuppressWarnings("PMD.DoNotUseThreads")
 89  
 public final class VerboseThreads implements ThreadFactory {
 90  
 
 91  
     /**
 92  
      * Thread group.
 93  
      */
 94  
     private final transient ThreadGroup group;
 95  
 
 96  
     /**
 97  
      * Prefix to use.
 98  
      */
 99  
     private final transient String prefix;
 100  
 
 101  
     /**
 102  
      * Number of the next thread to create.
 103  
      */
 104  0
     private final transient AtomicInteger number = new AtomicInteger(1);
 105  
 
 106  
     /**
 107  
      * Create threads as daemons?
 108  
      */
 109  
     private final transient boolean daemon;
 110  
 
 111  
     /**
 112  
      * Default thread priority.
 113  
      */
 114  
     private final transient int priority;
 115  
 
 116  
     /**
 117  
      * Default constructor ({@code "verbose"} as a prefix, threads are daemons,
 118  
      * default thread priority is {@code 1}).
 119  
      */
 120  
     public VerboseThreads() {
 121  0
         this("verbose", true, 1);
 122  0
     }
 123  
 
 124  
     /**
 125  
      * Detailed constructor, with a prefix of thread names (threads are daemons,
 126  
      * default thread priority is {@code 1}).
 127  
      * @param pfx Prefix for thread names
 128  
      */
 129  
     public VerboseThreads(final String pfx) {
 130  0
         this(pfx, true, 1);
 131  0
     }
 132  
 
 133  
     /**
 134  
      * Detailed constructor, with a prefix of thread names (threads are daemons,
 135  
      * default thread priority is {@code 1}).
 136  
      * @param type Prefix will be build from this type name
 137  
      */
 138  
     public VerboseThreads(final Object type) {
 139  0
         this(type.getClass().getSimpleName(), true, 1);
 140  0
     }
 141  
 
 142  
     /**
 143  
      * Detailed constructor, with a prefix of thread names (threads are daemons,
 144  
      * default thread priority is {@code 1}).
 145  
      * @param type Prefix will be build from this type name
 146  
      */
 147  
     public VerboseThreads(final Class<?> type) {
 148  0
         this(type.getSimpleName(), true, 1);
 149  0
     }
 150  
 
 151  
     /**
 152  
      * Detailed constructor.
 153  
      * @param pfx Prefix for thread names
 154  
      * @param dmn Threads should be daemons?
 155  
      * @param prt Default priority for all threads
 156  
      */
 157  
     public VerboseThreads(final String pfx, final boolean dmn,
 158  0
         final int prt) {
 159  0
         this.prefix = pfx;
 160  0
         this.daemon = dmn;
 161  0
         this.priority = prt;
 162  0
         this.group = new VerboseThreads.Group(pfx);
 163  0
     }
 164  
 
 165  
     @Override
 166  
     public Thread newThread(final Runnable runnable) {
 167  0
         final Thread thread = new Thread(
 168  
             this.group,
 169  
             new VerboseThreads.Wrap(runnable)
 170  
         );
 171  0
         thread.setName(
 172  
             String.format(
 173  
                 "%s-%d",
 174  
                 this.prefix,
 175  
                 this.number.getAndIncrement()
 176  
             )
 177  
         );
 178  0
         thread.setDaemon(this.daemon);
 179  0
         thread.setPriority(this.priority);
 180  0
         return thread;
 181  
     }
 182  
 
 183  
     /**
 184  
      * Group to use.
 185  
      */
 186  
     private static final class Group extends ThreadGroup {
 187  
         /**
 188  
          * Ctor.
 189  
          * @param name Name of it
 190  
          */
 191  
         Group(final String name) {
 192  0
             super(name);
 193  0
         }
 194  
         @Override
 195  
         public void uncaughtException(final Thread thread,
 196  
             final Throwable throwable) {
 197  0
             Logger.warn(this, "%[exception]s", throwable);
 198  0
         }
 199  
     }
 200  
 
 201  
     /**
 202  
      * Runnable decorator.
 203  
      */
 204  
     private static final class Wrap implements Runnable {
 205  
         /**
 206  
          * Origin runnable.
 207  
          */
 208  
         private final transient Runnable origin;
 209  
         /**
 210  
          * Ctor.
 211  
          * @param runnable Origin runnable
 212  
          */
 213  0
         Wrap(final Runnable runnable) {
 214  0
             this.origin = runnable;
 215  0
         }
 216  
         @Override
 217  
         @SuppressWarnings("PMD.AvoidCatchingGenericException")
 218  
         public void run() {
 219  
             try {
 220  0
                 this.origin.run();
 221  
                 // @checkstyle IllegalCatch (1 line)
 222  0
             } catch (final RuntimeException ex) {
 223  0
                 Logger.warn(
 224  
                     this,
 225  
                     "%s: %[exception]s",
 226  
                     Thread.currentThread().getName(),
 227  
                     ex
 228  
                 );
 229  0
                 throw ex;
 230  
                 // @checkstyle IllegalCatch (1 line)
 231  0
             } catch (final Error error) {
 232  0
                 Logger.error(
 233  
                     this,
 234  
                     "%s (error): %[exception]s",
 235  
                     Thread.currentThread().getName(),
 236  
                     error
 237  
                 );
 238  0
                 throw error;
 239  0
             }
 240  0
         }
 241  
     }
 242  
 
 243  
 }