| /******************************************************************************* |
| * Copyright (c) 2010-present Sonatype, Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.sisu.inject; |
| |
| import java.util.Map; |
| import java.util.logging.Level; |
| |
| import com.google.inject.Binding; |
| import com.google.inject.Injector; |
| import com.google.inject.Key; |
| import com.google.inject.Module; |
| import com.google.inject.ProvisionException; |
| import com.google.inject.spi.Element; |
| import com.google.inject.spi.Elements; |
| |
| /** |
| * Utility methods for dealing with container logging and recovery. |
| * <p> |
| * Set <b>-Dsisu.debug</b> to send detailed tracing to the console. |
| */ |
| public final class Logs |
| { |
| // ---------------------------------------------------------------------- |
| // Static initialization |
| // ---------------------------------------------------------------------- |
| |
| static |
| { |
| String newLine; |
| boolean toConsole; |
| try |
| { |
| newLine = System.getProperty( "line.separator", "\n" ); |
| final String debug = System.getProperty( "sisu.debug", "false" ); |
| toConsole = "".equals( debug ) || "true".equalsIgnoreCase( debug ); |
| } |
| catch ( final RuntimeException e ) |
| { |
| newLine = "\n"; |
| toConsole = false; |
| } |
| NEW_LINE = newLine; |
| Sink sink; |
| try |
| { |
| sink = toConsole ? new ConsoleSink() : new SLF4JSink(); |
| } |
| catch ( final RuntimeException e ) |
| { |
| sink = new JULSink(); |
| } |
| catch ( final LinkageError e ) |
| { |
| sink = new JULSink(); |
| } |
| SINK = sink; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Constants |
| // ---------------------------------------------------------------------- |
| |
| public static final String NEW_LINE; |
| |
| private static final String SISU = "Sisu"; |
| |
| private static final Sink SINK; |
| |
| public static final boolean TRACE_ENABLED = SINK.isTraceEnabled(); |
| |
| // ---------------------------------------------------------------------- |
| // Constructors |
| // ---------------------------------------------------------------------- |
| |
| private Logs() |
| { |
| // static utility class, not allowed to create instances |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Utility methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Logs a trace message; uses "{}" format anchors. Pass {@link Throwable}s in last parameter for special handling. |
| * |
| * @param format The trace message format |
| * @param arg1 First object to format |
| * @param arg2 Second object to format |
| */ |
| public static void trace( final String format, final Object arg1, final Object arg2 ) |
| { |
| if ( TRACE_ENABLED ) |
| { |
| SINK.trace( format( format( format, arg1 ), arg2 ), arg2 instanceof Throwable ? (Throwable) arg2 : null ); |
| } |
| } |
| |
| /** |
| * Logs a warning message; uses "{}" format anchors. Pass {@link Throwable}s in last parameter for special handling. |
| * |
| * @param format The warning message format |
| * @param arg1 First object to format |
| * @param arg2 Second object to format |
| */ |
| public static void warn( final String format, final Object arg1, final Object arg2 ) |
| { |
| SINK.warn( format( format( format, arg1 ), arg2 ), arg2 instanceof Throwable ? (Throwable) arg2 : null ); |
| } |
| |
| /** |
| * Helper method for catching {@link Throwable}s; severe errors such as {@link ThreadDeath} are always re-thrown. |
| * |
| * @param problem The problem |
| */ |
| public static void catchThrowable( final Throwable problem ) |
| { |
| for ( Throwable cause = problem; cause != null; cause = cause.getCause() ) |
| { |
| if ( cause instanceof ThreadDeath || cause instanceof VirtualMachineError ) |
| { |
| throw (Error) cause; // must immediately re-throw severe errors |
| } |
| } |
| } |
| |
| /** |
| * Helper method for throwing {@link Throwable}s; checked exceptions are wrapped as {@link ProvisionException}s. |
| * |
| * @param problem The problem |
| */ |
| public static void throwUnchecked( final Throwable problem ) |
| { |
| if ( problem instanceof RuntimeException ) |
| { |
| throw (RuntimeException) problem; |
| } |
| if ( problem instanceof Error ) |
| { |
| throw (Error) problem; |
| } |
| // this cast lets us load the 'Logs' class and log messages even if Guice is not available |
| throw RuntimeException.class.cast( new ProvisionException( problem.toString(), problem ) ); |
| } |
| |
| /** |
| * Returns an identity string for the given object. |
| * |
| * @see System#identityHashCode(Object) |
| * @param object The object |
| * @return Identity string of the object. |
| */ |
| public static String identityToString( final Object object ) |
| { |
| return null == object ? null : object.getClass().getName() + '@' // |
| + Integer.toHexString( System.identityHashCode( object ) ); |
| } |
| |
| /** |
| * Returns a string representation of the given {@link Module}. |
| * |
| * @param module The module |
| * @return String representation of the module. |
| */ |
| public static String toString( final Module module ) |
| { |
| final StringBuilder buf = new StringBuilder( identityToString( module ) ); |
| buf.append( NEW_LINE ).append( NEW_LINE ); |
| buf.append( "-----[elements]----------------------------------------------------------------" ).append( NEW_LINE ); |
| int i = 0; |
| for ( final Element e : Elements.getElements( module ) ) |
| { |
| buf.append( i++ ).append( ". " ).append( e ).append( NEW_LINE ); |
| } |
| return buf.append( "-------------------------------------------------------------------------------" ).append( NEW_LINE ).toString(); |
| } |
| |
| /** |
| * Returns a string representation of the given {@link Injector}. |
| * |
| * @param injector The injector |
| * @return String representation of the injector. |
| */ |
| public static String toString( final Injector injector ) |
| { |
| final StringBuilder buf = new StringBuilder( identityToString( injector ) ); |
| if ( null != injector.getParent() ) |
| { |
| buf.append( " parent: " ).append( identityToString( injector.getParent() ) ); |
| } |
| buf.append( NEW_LINE ).append( NEW_LINE ); |
| buf.append( "-----[explicit bindings]-------------------------------------------------------" ).append( NEW_LINE ); |
| int i = 0; |
| final Map<Key<?>, Binding<?>> explicitBindings = injector.getBindings(); |
| for ( final Binding<?> b : explicitBindings.values() ) |
| { |
| buf.append( i++ ).append( ". " ).append( b ).append( NEW_LINE ); |
| } |
| buf.append( "-----[implicit bindings]-------------------------------------------------------" ).append( NEW_LINE ); |
| for ( final Binding<?> b : injector.getAllBindings().values() ) |
| { |
| if ( !explicitBindings.containsKey( b.getKey() ) ) |
| { |
| buf.append( i++ ).append( ". " ).append( b ).append( NEW_LINE ); |
| } |
| } |
| return buf.append( "-------------------------------------------------------------------------------" ).append( NEW_LINE ).toString(); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Implementation methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Replaces the first available formatting anchor with the given object. |
| * |
| * @param format The format string |
| * @param arg The object to format |
| */ |
| private static String format( final String format, final Object arg ) |
| { |
| final int len = format.length(); |
| boolean detailed = true; |
| int cursor = 0; |
| for ( char prevChar = ' ', currChar; cursor < len; prevChar = currChar, cursor++ ) |
| { |
| currChar = format.charAt( cursor ); |
| if ( prevChar == '{' && currChar == '}' ) |
| { |
| break; // replace anchor with String.valueOf |
| } |
| if ( prevChar == '<' && currChar == '>' ) |
| { |
| detailed = false; |
| break; // use Logs.identityToString instead |
| } |
| } |
| if ( cursor >= len ) |
| { |
| return format; |
| } |
| final StringBuilder buf = new StringBuilder(); |
| if ( --cursor > 0 ) |
| { |
| buf.append( format.substring( 0, cursor ) ); |
| } |
| try |
| { |
| buf.append( detailed ? arg : identityToString( arg ) ); |
| } |
| catch ( final RuntimeException e ) |
| { |
| buf.append( e ); |
| } |
| cursor += 2; |
| if ( cursor < len ) |
| { |
| buf.append( format.substring( cursor, len ) ); |
| } |
| return buf.toString(); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Implementation types |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Something that accepts formatted messages. |
| */ |
| private interface Sink |
| { |
| /** |
| * @return {@code true} if trace is enabled; otherwise {@code false} |
| */ |
| boolean isTraceEnabled(); |
| |
| /** |
| * Accepts a trace message and optional exception cause. |
| * |
| * @param message The trace message |
| * @param cause The exception cause |
| */ |
| void trace( String message, Throwable cause ); |
| |
| /** |
| * Accepts a warning message and optional exception cause. |
| * |
| * @param message The warning message |
| * @param cause The exception cause |
| */ |
| void warn( String message, Throwable cause ); |
| } |
| |
| /** |
| * {@link Sink}s messages to the system console. |
| */ |
| static final class ConsoleSink |
| implements Sink |
| { |
| // ---------------------------------------------------------------------- |
| // Constants |
| // ---------------------------------------------------------------------- |
| |
| private static final String TRACE = "TRACE: " + SISU + " - "; |
| |
| private static final String WARN = "WARN: " + SISU + " - "; |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| public boolean isTraceEnabled() |
| { |
| return true; |
| } |
| |
| public void trace( final String message, final Throwable cause ) |
| { |
| System.out.println( TRACE + message ); |
| if ( null != cause ) |
| { |
| cause.printStackTrace( System.out ); |
| } |
| } |
| |
| public void warn( final String message, final Throwable cause ) |
| { |
| System.err.println( WARN + message ); |
| if ( null != cause ) |
| { |
| cause.printStackTrace( System.err ); |
| } |
| } |
| } |
| |
| /** |
| * {@link Sink}s messages to the JDK. |
| */ |
| static final class JULSink |
| implements Sink |
| { |
| // ---------------------------------------------------------------------- |
| // Implementation fields |
| // ---------------------------------------------------------------------- |
| |
| private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger( SISU ); |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| public boolean isTraceEnabled() |
| { |
| return logger.isLoggable( Level.FINER ); |
| } |
| |
| public void trace( final String message, final Throwable cause ) |
| { |
| logger.log( Level.FINER, message, cause ); |
| } |
| |
| public void warn( final String message, final Throwable cause ) |
| { |
| logger.log( Level.WARNING, message, cause ); |
| } |
| } |
| |
| /** |
| * {@link Sink}s messages via SLF4J. |
| */ |
| static final class SLF4JSink |
| implements Sink |
| { |
| // ---------------------------------------------------------------------- |
| // Implementation fields |
| // ---------------------------------------------------------------------- |
| |
| private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( SISU ); |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| public boolean isTraceEnabled() |
| { |
| return logger.isTraceEnabled(); |
| } |
| |
| public void trace( final String message, final Throwable cause ) |
| { |
| logger.trace( message, cause ); |
| } |
| |
| public void warn( final String message, final Throwable cause ) |
| { |
| logger.warn( message, cause ); |
| } |
| } |
| } |