Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,8 +25,9 @@
/**
* Default implementation of StackTraceFilterer.
*
* @since 2.0
* @author Graeme Rocher
* @author Michael Yan
* @since 2.0
*/
public class DefaultStackTraceFilterer implements StackTraceFilterer {

Expand All @@ -37,16 +38,18 @@ public class DefaultStackTraceFilterer implements StackTraceFilterer {
private static final String[] DEFAULT_INTERNAL_PACKAGES = new String[] {
"org.codehaus.groovy.runtime.",
"org.codehaus.groovy.reflection.",
"org.codehaus.groovy.vmplugin.",
"org.codehaus.groovy.ast.",
"org.springframework.web.filter",
"org.springframework.boot.actuate",
"org.mortbay.",
"groovy.lang.",
"org.apache.catalina.",
"org.apache.coyote.",
"org.apache.tomcat.",
"net.sf.cglib.proxy.",
"sun.",
"jdk.internal.reflect.",
"java.lang.",
"java.lang.reflect.",
"org.springframework.boot.devtools.",
"org.springsource.loaded.",
Expand All @@ -69,28 +72,33 @@ public DefaultStackTraceFilterer(boolean shouldFilter) {
this.packagesToFilter.addAll(Arrays.asList(DEFAULT_INTERNAL_PACKAGES));
}

@Override
public void addInternalPackage(String name) {
if (name == null) {
throw new IllegalArgumentException("Package name cannot be null");
}
this.packagesToFilter.add(name);
}

@Override
public void setCutOffPackage(String cutOffPackage) {
this.cutOffPackage = cutOffPackage;
}

@Override
public Throwable filter(Throwable source, boolean recursive) {
if (recursive) {
Throwable current = source;
while (current != null) {
current = filter(current);
current = current.getCause();
}
return current;
}
return filter(source);
}

@Override
public Throwable filter(Throwable source) {
if (this.shouldFilter) {
StackTraceElement[] trace = source.getStackTrace();
Expand Down Expand Up @@ -149,8 +157,14 @@ protected boolean isApplicationClass(String className) {
return true;
}

@Override
public void setShouldFilter(boolean shouldFilter) {
this.shouldFilter = shouldFilter;
}

@Override
public boolean isShouldFilter() {
return this.shouldFilter;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2022 the original author or authors.
* Copyright 2011-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,8 +18,9 @@
/**
* Improves the output of stack traces produced by exceptions in a Grails application.
*
* @since 2.0
* @author Graeme Rocher
* @author Michaael Yan
* @since 2.0
*/
public interface StackTraceFilterer {

Expand Down Expand Up @@ -62,4 +63,9 @@ public interface StackTraceFilterer {
*/
void setShouldFilter(boolean shouldFilter);

/**
* @return boolean Whether to filter stack traces or not
*/
boolean isShouldFilter();

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2022 the original author or authors.
* Copyright 2009-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@
* Thrown when validation fails during a .save().
*
* @author Jeff Brown
* @author Michael Yan
* @since 1.2
*/
public class ValidationException extends GrailsException {
Expand Down Expand Up @@ -54,8 +55,11 @@ public static String formatErrors(Errors errors) {

public static String formatErrors(Errors errors, String msg) {
StringBuilder b = new StringBuilder();
if (msg != null && msg.length() > 0) {
b.append(msg).append(":\n");
if (msg != null && !msg.isEmpty()) {
b.append(msg);
}
if (errors.hasErrors()) {
b.append(":\n");
}
for (ObjectError error : errors.getAllErrors()) {
b.append("- ").append(error).append("\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.grails.exceptions.reporting.StackTracePrinter
*
* @author Graeme Rocher
* @author Marc Palmer
* @author Michael Yan
* @since 2.0
*/
class DefaultErrorsPrinter extends DefaultStackTracePrinter implements CodeSnippetPrinter {
Expand Down Expand Up @@ -82,7 +83,7 @@ class DefaultErrorsPrinter extends DefaultStackTracePrinter implements CodeSnipp
}
}

if (!className || !lineNumber) {
if (!className || lineNumber < 0) {
continue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ class StackTracePrinterSpec extends Specification {

then:"The formatting is correctly applied"
result != null
result.contains '| 7 | callMe . . . . . . in test.FooController'
result.contains '->> 7 | callMe in test.FooController'
}

@Requires({jvm.isJava8()})
void "Test pretty print nested stack trace"() {
given: "a controller that throws an exception"
final gcl = new GroovyClassLoader()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ class GrailsGradlePlugin extends GroovyPlugin {
task.systemProperty Metadata.APPLICATION_VERSION, project.version
task.systemProperty Metadata.APPLICATION_GRAILS_VERSION, grailsVersion
task.systemProperty Environment.KEY, defaultGrailsEnv
task.systemProperty Environment.FULL_STACKTRACE, System.getProperty(Environment.FULL_STACKTRACE) ?: ''
if (task.minHeapSize == null) {
task.minHeapSize = '768m'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 the original author or authors.
* Copyright 2022-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.Servlet;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand All @@ -43,7 +44,10 @@
import grails.config.Config;
import grails.config.Settings;
import grails.core.GrailsApplication;
import grails.util.Environment;

import org.grails.exceptions.reporting.DefaultStackTraceFilterer;
import org.grails.exceptions.reporting.StackTraceFilterer;
import org.grails.web.errors.GrailsExceptionResolver;
import org.grails.web.filters.HiddenHttpMethodFilter;
import org.grails.web.filters.OrderedHiddenHttpMethodFilter;
Expand Down Expand Up @@ -81,11 +85,30 @@ public FilterRegistrationBean<GrailsWebRequestFilter> grailsWebRequestFilter(
}

@Bean
public GrailsExceptionResolver exceptionHandler() {
@ConditionalOnMissingBean
public StackTraceFilterer stackTraceFilterer(ObjectProvider<GrailsApplication> grailsApplicationProvider) {
GrailsApplication grailsApplication = grailsApplicationProvider.getIfAvailable();
Config config = grailsApplication.getConfig();
boolean shouldFilter = !config.getProperty(Environment.FULL_STACKTRACE, Boolean.class, Boolean.FALSE);
Class<?> filtererClass = config.getProperty(Settings.SETTING_LOGGING_STACKTRACE_FILTER_CLASS,
Class.class, DefaultStackTraceFilterer.class);

StackTraceFilterer filtererBean = BeanUtils.instantiateClass(filtererClass, StackTraceFilterer.class);
filtererBean.setShouldFilter(shouldFilter);
return filtererBean;
}

@Bean
public GrailsExceptionResolver exceptionHandler(ObjectProvider<GrailsApplication> grailsApplicationProvider,
ObjectProvider<StackTraceFilterer> stackTraceFiltererObjectProvider) {
GrailsExceptionResolver exceptionResolver = new GrailsExceptionResolver();
exceptionResolver.setGrailsApplication(grailsApplicationProvider.getIfAvailable());
exceptionResolver.setStackTraceFilterer(stackTraceFiltererObjectProvider.getIfAvailable());

Properties exceptionMappings = new Properties();
exceptionMappings.put("java.lang.Exception", "/error");
exceptionResolver.setExceptionMappings(exceptionMappings);

return exceptionResolver;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
"type": "java.lang.String",
"defaultValue": "\/static\/**"
},
{
"name": "grails.full.stacktrace",
"description": "Whether the display of full stack traces is needed.",
"type": "java.lang.Boolean",
"defaultValue": false
},
{
"name": "grails.exceptionresolver.logRequestParameters",
"description": "Whether to log request parameters in the console.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand Down Expand Up @@ -157,7 +158,6 @@ public CachingGrailsConventionGroovyPageLocator groovyPageLocator(GroovyPagesPro
}

@Bean
@ConditionalOnMissingBean
public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesProperties, ObjectProvider<GrailsApplication> grailsApplication) {
Config config = grailsApplication.getIfAvailable().getConfig();
Environment env = Environment.getCurrent();
Expand All @@ -168,6 +168,7 @@ public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesPr
(developmentMode && env == Environment.DEVELOPMENT) ? 0L : groovyPagesProperties.getReload().getInterval());

CachingGroovyPageStaticResourceLocator groovyPageStaticResourceLocator = new CachingGroovyPageStaticResourceLocator();
groovyPageStaticResourceLocator.setSearchLocation(BuildSettings.BASE_DIR.getAbsolutePath());

if (enableReload) {
groovyPageStaticResourceLocator.setCacheTimeout(gspCacheTimeout);
Expand All @@ -178,7 +179,8 @@ public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesPr

@Bean
@ConditionalOnMissingBean
public ErrorsViewStackTracePrinter errorsViewStackTracePrinter(ObjectProvider<ResourceLocator> grailsResourceLocator) {
public ErrorsViewStackTracePrinter errorsViewStackTracePrinter(
@Qualifier("grailsResourceLocator") ObjectProvider<ResourceLocator> grailsResourceLocator) {
return new ErrorsViewStackTracePrinter(grailsResourceLocator.getObject());
}

Expand Down
Loading