Commit 834543eb authored by Nils Christian Ehmke's avatar Nils Christian Ehmke

Added some tests; Added some comments and JavaDoc; Added log4j2 configuration...

Added some tests; Added some comments and JavaDoc; Added log4j2 configuration files; Added exception handling for the import thread; Corrected the logger class
parent 1d5061ff
......@@ -8,23 +8,42 @@ import com.google.inject.Singleton;
import kieker.diagnosis.architecture.service.ServiceBase;
import kieker.diagnosis.architecture.service.cache.UseCache;
/**
* The {@link PatternService} is responsible for validating and compiling regular expressions.
*
* @author Nils Christian Ehmke
*/
@Singleton
public class PatternService extends ServiceBase {
/**
* Checks whether the given pattern is valid. In other words: Is the pattern compilable by {@link #compilePattern(String)}. This method is cached.
*
* @param aPattern
* The pattern to check.
*
* @return true if and only if the pattern is valid.
*/
@UseCache ( cacheName = "validPattern" )
public boolean isValidPattern( final String aPattern ) {
if ( aPattern == null ) {
return true;
}
try {
Pattern.compile( aPattern );
return true;
} catch ( final PatternSyntaxException ex ) {
} catch ( final PatternSyntaxException | NullPointerException ex ) {
getLogger( ).debug( ( ) -> String.format( "Pattern \"%s\" is invalid", aPattern ), ex );
return false;
}
}
/**
* Compiles the given pattern. This method is cached.
*
* @param aPattern
* The pattern to compile.
*
* @return The compiled pattern.
*/
@UseCache ( cacheName = "pattern" )
public Pattern compilePattern( final String aPattern ) {
return Pattern.compile( aPattern );
......
......@@ -90,19 +90,19 @@ class AggregatedMethodsViewModel extends ViewModelBase<AggregatedMethodsView> {
if ( filter.isUseRegExpr( ) ) {
final PatternService patternService = getService( PatternService.class );
if ( !patternService.isValidPattern( filter.getHost( ) ) ) {
if ( !( patternService.isValidPattern( filter.getHost( ) ) || filter.getHost( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getHost( ) ) );
}
if ( !patternService.isValidPattern( filter.getClazz( ) ) ) {
if ( !( patternService.isValidPattern( filter.getClazz( ) ) || filter.getClazz( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getClazz( ) ) );
}
if ( !patternService.isValidPattern( filter.getMethod( ) ) ) {
if ( !( patternService.isValidPattern( filter.getMethod( ) ) || filter.getMethod( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getMethod( ) ) );
}
if ( !patternService.isValidPattern( filter.getException( ) ) ) {
if ( !( patternService.isValidPattern( filter.getException( ) ) || filter.getException( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getException( ) ) );
}
}
......
......@@ -16,6 +16,7 @@ import javafx.scene.image.Image;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import kieker.diagnosis.architecture.common.ExceptionUtil;
import kieker.diagnosis.architecture.exception.BusinessException;
import kieker.diagnosis.architecture.exception.BusinessRuntimeException;
import kieker.diagnosis.architecture.service.properties.DevelopmentModeProperty;
......@@ -166,6 +167,9 @@ public class MainController extends ControllerBase<MainViewModel> {
Platform.runLater( ( ) -> {
getViewModel( ).performRefresh( );
} );
} catch ( final Exception ex ) {
// At this point we have no exception handling. We have to perform this ourselves.
ExceptionUtil.handleException( ex, getLogger( ).getName( ) );
} finally {
Platform.runLater( ( ) -> {
importerDialogView.close( );
......
......@@ -96,19 +96,19 @@ class MethodsViewModel extends ViewModelBase<MethodsView> {
if ( filter.isUseRegExpr( ) ) {
final PatternService patternService = getService( PatternService.class );
if ( !patternService.isValidPattern( filter.getHost( ) ) ) {
if ( !( patternService.isValidPattern( filter.getHost( ) ) || filter.getHost( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getHost( ) ) );
}
if ( !patternService.isValidPattern( filter.getClazz( ) ) ) {
if ( !( patternService.isValidPattern( filter.getClazz( ) ) || filter.getClazz( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getClazz( ) ) );
}
if ( !patternService.isValidPattern( filter.getMethod( ) ) ) {
if ( !( patternService.isValidPattern( filter.getMethod( ) ) || filter.getMethod( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getMethod( ) ) );
}
if ( !patternService.isValidPattern( filter.getException( ) ) ) {
if ( !( patternService.isValidPattern( filter.getException( ) ) || filter.getException( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getException( ) ) );
}
}
......
......@@ -155,19 +155,19 @@ class TracesViewModel extends ViewModelBase<TracesView> {
if ( filter.isUseRegExpr( ) ) {
final PatternService patternService = getService( PatternService.class );
if ( !patternService.isValidPattern( filter.getHost( ) ) ) {
if ( !( patternService.isValidPattern( filter.getHost( ) ) || filter.getHost( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getHost( ) ) );
}
if ( !patternService.isValidPattern( filter.getClazz( ) ) ) {
if ( !( patternService.isValidPattern( filter.getClazz( ) ) || filter.getClazz( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getClazz( ) ) );
}
if ( !patternService.isValidPattern( filter.getMethod( ) ) ) {
if ( !( patternService.isValidPattern( filter.getMethod( ) ) || filter.getMethod( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getMethod( ) ) );
}
if ( !patternService.isValidPattern( filter.getException( ) ) ) {
if ( !( patternService.isValidPattern( filter.getException( ) ) || filter.getException( ) == null ) ) {
throw new BusinessException( String.format( getLocalizedString( "errorMessageRegExpr" ), filter.getException( ) ) );
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
package kieker.diagnosis.service.aggregatedmethods;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
import kieker.diagnosis.KiekerTraceDiagnosisModule;
import kieker.diagnosis.service.data.AggregatedMethodCall;
import kieker.diagnosis.service.data.MonitoringLogService;
/**
* Test class for the {@link AggregatedMethodsService}.
*
* @author Nils Christian Ehmke
*/
public class AggregatedMethodsServiceTest {
private AggregatedMethodsService ivMethodsService;
private MonitoringLogService ivDataService;
@Before
public void setUp( ) {
final Injector injector = Guice.createInjector( new KiekerTraceDiagnosisModule( ) );
ivMethodsService = injector.getInstance( AggregatedMethodsService.class );
ivDataService = injector.getInstance( MonitoringLogService.class );
}
@Test
public void testSimpleSearch( ) {
// Prepare some data for the search
createMethodCall( "host1", "class1", "op1", "cause1" );
createMethodCall( "host1", "class2", "op1", "cause1" );
createMethodCall( "host1", "class1", "op3", "cause1" );
createMethodCall( "host1", "class1", "op3", "cause4" );
assertThat( ivMethodsService.countMethods( ), is( 4 ) );
// Now search with a filter
final AggregatedMethodsFilter methodsFilter = new AggregatedMethodsFilter( );
methodsFilter.setHost( "host1" );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 4 ) );
methodsFilter.setClazz( "class1" );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 3 ) );
methodsFilter.setMethod( "op3" );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 2 ) );
methodsFilter.setException( "cause4" );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 1 ) );
}
@Test
public void testSearchTypeFilter( ) {
// Prepare some data for the search
createMethodCall( "host1", "class1", "op1", "cause1" );
createMethodCall( "host1", "class2", "op1", null );
createMethodCall( "host1", "class1", "op3", "cause1" );
createMethodCall( "host1", "class1", "op3", "cause4" );
assertThat( ivMethodsService.countMethods( ), is( 4 ) );
// Now search with a filter
final AggregatedMethodsFilter methodsFilter = new AggregatedMethodsFilter( );
methodsFilter.setSearchType( SearchType.ALL );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 4 ) );
methodsFilter.setSearchType( SearchType.ONLY_FAILED );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 3 ) );
methodsFilter.setSearchType( SearchType.ONLY_SUCCESSFUL );
assertThat( ivMethodsService.searchMethods( methodsFilter ).size( ), is( 1 ) );
}
private void createMethodCall( final String aHost, final String aClazz, final String aMethod, final String aException ) {
final AggregatedMethodCall methodCall = new AggregatedMethodCall( );
methodCall.setHost( aHost );
methodCall.setClazz( aClazz );
methodCall.setMethod( aMethod );
methodCall.setException( aException );
ivDataService.getAggreatedMethods( ).add( methodCall );
}
}
......@@ -38,6 +38,11 @@ import kieker.common.util.registry.IRegistry;
import kieker.common.util.registry.Registry;
import kieker.diagnosis.KiekerTraceDiagnosisModule;
/**
* Test class for the {@link MonitoringLogService}.
*
* @author Nils Christian Ehmke
*/
public class MonitoringLogServiceTest {
@Rule
......
......@@ -13,6 +13,11 @@ import kieker.diagnosis.KiekerTraceDiagnosisModule;
import kieker.diagnosis.service.data.MethodCall;
import kieker.diagnosis.service.data.MonitoringLogService;
/**
* Test class for the {@link MethodsService}.
*
* @author Nils Christian Ehmke
*/
public class MethodsServiceTest {
private MethodsService ivMethodsService;
......
package kieker.diagnosis.service.pattern;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
import kieker.diagnosis.KiekerTraceDiagnosisModule;
/**
* Test class for the {@link PatternService}.
*
* @author Nils Christian Ehmke
*/
public class PatternServiceTest {
private PatternService ivPatternService;
@Before
public void setUp( ) {
final Injector injector = Guice.createInjector( new KiekerTraceDiagnosisModule( ) );
ivPatternService = injector.getInstance( PatternService.class );
}
@Test
public void testVariousValidPattern( ) {
assertThat( ivPatternService.isValidPattern( ".*" ), is( true ) );
assertThat( ivPatternService.isValidPattern( "Test" ), is( true ) );
assertThat( ivPatternService.isValidPattern( ".*Test.*" ), is( true ) );
assertThat( ivPatternService.isValidPattern( "(\\d)" ), is( true ) );
assertThat( ivPatternService.isValidPattern( "[.*]" ), is( true ) );
}
@Test
public void testVariousInvalidPattern( ) {
assertThat( ivPatternService.isValidPattern( "(" ), is( false ) );
assertThat( ivPatternService.isValidPattern( ")" ), is( false ) );
assertThat( ivPatternService.isValidPattern( "*" ), is( false ) );
assertThat( ivPatternService.isValidPattern( "[" ), is( false ) );
assertThat( ivPatternService.isValidPattern( null ), is( false ) );
}
@Test
public void testCompilePatternWithVariousPattern( ) {
assertThat( ivPatternService.compilePattern( ".*" ).matcher( "Test" ).matches( ), is( true ) );
assertThat( ivPatternService.compilePattern( "Test" ).matcher( "Test" ).matches( ), is( true ) );
assertThat( ivPatternService.compilePattern( ".*Test.*" ).matcher( "Test" ).matches( ), is( true ) );
assertThat( ivPatternService.compilePattern( "(\\\\d)" ).matcher( "Test" ).matches( ), is( false ) );
assertThat( ivPatternService.compilePattern( "\\d*" ).matcher( "123" ).matches( ), is( true ) );
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss} %-5level %logger{36} - %msg%rEx{0}%n" />
</Console>
</Appenders>
<Loggers>
<Root level="all">
<AppenderRef ref="console" />
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
package kieker.diagnosis.architecture.common;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ResourceBundle;
import org.apache.logging.log4j.LogManager;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import kieker.diagnosis.architecture.exception.BusinessRuntimeException;
/**
* A util class to handle exceptions
*
* @author Nils Christian Ehmke
*/
public class ExceptionUtil {
private static final ResourceBundle cvResourceBundle = ResourceBundle.getBundle( ExceptionUtil.class.getName( ) );
private ExceptionUtil( ) {
// Avoid instantiation
}
/**
* This method handles an exception. The exception is logged and an error dialog is shown. If the exception is a {@link BusinessRuntimeException}, the error
* is not logged and the error dialog indicates that the error is a business error.
*
* @param aThrowable
* The exception to handle.
* @param aLoggerName
* The name of the logger, the exception will be logged in.
*/
public static void handleException( final Throwable aThrowable, final String aLoggerName ) {
showExceptionDialog( aThrowable );
logException( aThrowable, aLoggerName );
}
private static void showExceptionDialog( final Throwable aThrowable ) {
// Find out whether this is a business exception or not
final boolean isBusinessException = aThrowable instanceof BusinessRuntimeException;
final Throwable exception = isBusinessException ? aThrowable.getCause( ) : aThrowable;
final AlertType alertType = isBusinessException ? AlertType.WARNING : AlertType.ERROR;
// Keep in mind that some controllers start a new thread. As this might also lead to exceptions, the whole dialog showing has to be performed in the
// JavaFX application thread.
final Runnable runnable = ( ) -> {
// Prepare the dialog
final Alert alert = new Alert( alertType );
alert.setTitle( cvResourceBundle.getString( "errorTitle" ) );
alert.setHeaderText( cvResourceBundle.getString( "errorMessage" ) );
alert.setContentText( exception.getMessage( ) );
if ( !isBusinessException ) {
// Convert the throwable into a string
final StringWriter sw = new StringWriter( );
final PrintWriter pw = new PrintWriter( sw );
exception.printStackTrace( pw );
final String exceptionText = sw.toString( );
// Prepare the remaining components
final Label label = new Label( cvResourceBundle.getString( "errorDescription" ) );
final TextArea textArea = new TextArea( exceptionText );
textArea.setEditable( false );
textArea.setWrapText( true );
textArea.setMaxWidth( Double.MAX_VALUE );
textArea.setMaxHeight( Double.MAX_VALUE );
GridPane.setVgrow( textArea, Priority.ALWAYS );
GridPane.setHgrow( textArea, Priority.ALWAYS );
final GridPane expContent = new GridPane( );
expContent.setMaxWidth( Double.MAX_VALUE );
expContent.add( label, 0, 0 );
expContent.add( textArea, 0, 1 );
alert.getDialogPane( ).setExpandableContent( expContent );
}
// Add the logo
final String iconPath = cvResourceBundle.getString( "errorIcon" );
final InputStream iconStream = ExceptionUtil.class.getClassLoader( ).getResourceAsStream( iconPath );
final Image icon = new Image( iconStream );
final Stage stage = (Stage) alert.getDialogPane( ).getScene( ).getWindow( );
stage.getIcons( ).add( icon );
alert.showAndWait( );
};
// Now decide whether we need to execute this in the JavaFX thread or whether we are already in this thread.
if ( Platform.isFxApplicationThread( ) ) {
runnable.run( );
} else {
Platform.runLater( runnable );
}
}
private static void logException( final Throwable aThrowable, final String aLoggerName ) {
if ( !( aThrowable instanceof BusinessRuntimeException ) ) {
LogManager.getLogger( aLoggerName ).error( cvResourceBundle.getString( "errorMessage" ), aThrowable );
}
}
}
......@@ -76,7 +76,7 @@ public abstract class ServiceBase {
*/
protected final Logger getLogger( ) {
if ( cvLogger == null ) {
cvLogger = LogManager.getLogger( getClass( ) );
cvLogger = LogManager.getLogger( ClassUtil.getRealClass( getClass( ) ) );
}
return cvLogger;
......
......@@ -2,6 +2,9 @@ package kieker.diagnosis.architecture.ui;
import java.util.ResourceBundle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.inject.Inject;
import com.google.inject.Injector;
......@@ -21,6 +24,8 @@ public abstract class ControllerBase<VM extends ViewModelBase<?>> {
private final ResourceBundle ivResourceBundle = ResourceBundle.getBundle( ClassUtil.getRealName( getClass( ) ) );
private static Logger cvLogger;
@Inject
private VM ivViewModel;
......@@ -53,6 +58,19 @@ public abstract class ControllerBase<VM extends ViewModelBase<?>> {
return ivInjector.getInstance( aServiceClass );
}
/**
* Returns the logger for the current class.
*
* @return The logger.
*/
protected final Logger getLogger( ) {
if ( cvLogger == null ) {
cvLogger = LogManager.getLogger( ClassUtil.getRealClass( getClass( ) ) );
}
return cvLogger;
}
/**
* Gets a controller of the given type. Use this method with care, as it can lead to incomprehensible execution flow.
*
......
package kieker.diagnosis.architecture.ui;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ResourceBundle;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.logging.log4j.LogManager;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import kieker.diagnosis.architecture.common.ClassUtil;
import kieker.diagnosis.architecture.common.ExceptionUtil;
import kieker.diagnosis.architecture.exception.BusinessRuntimeException;
/**
......@@ -30,87 +16,19 @@ import kieker.diagnosis.architecture.exception.BusinessRuntimeException;
*/
public final class ErrorHandlingInterceptor implements MethodInterceptor {
private final ResourceBundle ivResourceBundle = ResourceBundle.getBundle( getClass( ).getName( ) );
@Override
public Object invoke( final MethodInvocation aMethodInvocation ) throws Throwable {
try {
return aMethodInvocation.proceed( );
} catch ( final Throwable ex ) {
handleException( ex, aMethodInvocation.getThis( ) );
return null;
}
}
private void handleException( final Throwable aThrowable, final Object aObject ) {
showExceptionDialog( aThrowable );
logException( aThrowable, aObject );
}
private void showExceptionDialog( final Throwable aThrowable ) {
// Find out whether this is a business exception or not
final boolean isBusinessException = aThrowable instanceof BusinessRuntimeException;
final Throwable exception = isBusinessException ? aThrowable.getCause( ) : aThrowable;
final AlertType alertType = isBusinessException ? AlertType.WARNING : AlertType.ERROR;
// Keep in mind that some controllers start a new thread. As this might also lead to exceptions, the whole dialog showing has to be performed in the
// JavaFX application thread.
final Runnable runnable = ( ) -> {
// Prepare the dialog
final Alert alert = new Alert( alertType );
alert.setTitle( ivResourceBundle.getString( "errorTitle" ) );
alert.setHeaderText( ivResourceBundle.getString( "errorMessage" ) );
alert.setContentText( exception.getMessage( ) );
if ( !isBusinessException ) {
// Convert the throwable into a string
final StringWriter sw = new StringWriter( );
final PrintWriter pw = new PrintWriter( sw );
exception.printStackTrace( pw );
final String exceptionText = sw.toString( );
// Get the logger name
final Object controller = aMethodInvocation.getThis( );
final Class<?> controllerClass = ClassUtil.getRealClass( controller.getClass( ) );
// Prepare the remaining components
final Label label = new Label( ivResourceBundle.getString( "errorDescription" ) );
// Now handle the exception
ExceptionUtil.handleException( ex, controllerClass.getName( ) );
final TextArea textArea = new TextArea( exceptionText );
textArea.setEditable( false );
textArea.setWrapText( true );
textArea.setMaxWidth( Double.MAX_VALUE );
textArea.setMaxHeight( Double.MAX_VALUE );
GridPane.setVgrow( textArea, Priority.ALWAYS );
GridPane.setHgrow( textArea, Priority.ALWAYS );
final GridPane expContent = new GridPane( );
expContent.setMaxWidth( Double.MAX_VALUE );
expContent.add( label, 0, 0 );
expContent.add( textArea, 0, 1 );
alert.getDialogPane( ).setExpandableContent( expContent );
}
// Add the logo
final String iconPath = ivResourceBundle.getString( "errorIcon" );
final InputStream iconStream = getClass( ).getClassLoader( ).getResourceAsStream( iconPath );
final Image icon = new Image( iconStream );
final Stage stage = (Stage) alert.getDialogPane( ).getScene( ).getWindow( );
stage.getIcons( ).add( icon );
alert.showAndWait( );
};
// Now decide whether we need to execute this in the JavaFX thread or whether we are already in this thread.
if ( Platform.isFxApplicationThread( ) ) {
runnable.run( );
} else {
Platform.runLater( runnable );
}
}
private void logException( final