You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/05/13 11:53:28 UTC
[groovy] branch master updated: GROOVY-9534: support platform
logging AST transform (closes #1237)
This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new d9c0a3b GROOVY-9534: support platform logging AST transform (closes #1237)
d9c0a3b is described below
commit d9c0a3bc0ff0de9b773187e635477e3f7846cceb
Author: Paul King <pa...@asert.com.au>
AuthorDate: Fri May 1 21:43:56 2020 +1000
GROOVY-9534: support platform logging AST transform (closes #1237)
---
build.gradle | 1 +
gradle/docs.gradle | 1 +
src/main/java/groovy/util/logging/PlatformLog.java | 113 +++++++++
.../org/codehaus/groovy/vmplugin/v9/Java9.java | 16 ++
.../vmplugin/v9/PluginDefaultGroovyMethods.java | 170 +++++++++++++
.../services/java.lang.System$LoggerFinder | 19 ++
src/test/groovy/util/logging/vm9/LoggerSpy.groovy | 54 ++++
.../groovy/util/logging/vm9/LoggerSpyFinder.groovy | 13 +
.../groovy/util/logging/vm9/PlatformLogTest.groovy | 280 +++++++++++++++++++++
9 files changed, 667 insertions(+)
diff --git a/build.gradle b/build.gradle
index ffb1753..c62c564 100644
--- a/build.gradle
+++ b/build.gradle
@@ -214,6 +214,7 @@ sourceSets {
if (!JavaVersion.current().isJava9Compatible()) {
exclude '**/v9/*'
exclude '**/vm9/*'
+ exclude '**/PlatformLog.java'
}
}
groovy {
diff --git a/gradle/docs.gradle b/gradle/docs.gradle
index a73b3e9..1f32490 100644
--- a/gradle/docs.gradle
+++ b/gradle/docs.gradle
@@ -160,6 +160,7 @@ task docGDK {
arg(value: 'org.codehaus.groovy.runtime.SocketGroovyMethods')
arg(value: 'org.codehaus.groovy.runtime.StringGroovyMethods')
arg(value: 'org.codehaus.groovy.vmplugin.v8.PluginDefaultGroovyMethods')
+ arg(value: 'org.codehaus.groovy.vmplugin.v9.PluginDefaultGroovyMethods')
// TODO don't hard-code these
arg(value: 'subprojects/groovy-dateutil/src/main/java/org/apache/groovy/dateutil/extensions/DateUtilExtensions.java')
arg(value: 'subprojects/groovy-datetime/src/main/java/org/apache/groovy/datetime/extensions/DateTimeExtensions.java')
diff --git a/src/main/java/groovy/util/logging/PlatformLog.java b/src/main/java/groovy/util/logging/PlatformLog.java
new file mode 100644
index 0000000..aec56fa
--- /dev/null
+++ b/src/main/java/groovy/util/logging/PlatformLog.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.util.logging;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+import org.codehaus.groovy.transform.LogASTTransformation;
+import org.codehaus.groovy.transform.LogASTTransformation.LoggingStrategy;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+
+/**
+ * This local transform adds a logging ability to your program using
+ * java.logging. Every method call on a unbound variable named <i>log</i>
+ * will be mapped to a call to the logger. For this a <i>log</i> field will be
+ * inserted in the class. If the field already exists the usage of this transform
+ * will cause a compilation error. The method name will be used to determine
+ * what to call on the logger.
+ * <pre>
+ * import groovy.util.logging.*
+ * import static java.lang.System.Logger.Level.INFO
+ *
+ * {@code @PlatformLog}
+ * class Foo {
+ * def method() {
+ * log.log INFO, 'Foobar'
+ * }
+ * }
+ *
+ * new Foo().method()
+ * </pre>
+ *
+ * @since 4.0.0
+ */
+@java.lang.annotation.Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE})
+@GroovyASTTransformationClass("org.codehaus.groovy.transform.LogASTTransformation")
+public @interface PlatformLog {
+ String value() default "log";
+
+ String category() default LogASTTransformation.DEFAULT_CATEGORY_NAME;
+
+ /**
+ * If specified, must match the "id" attribute in a VisibilityOptions annotation to enable a custom visibility.
+ */
+ String visibilityId() default Undefined.STRING;
+
+ Class<? extends LoggingStrategy> loggingStrategy() default JavaUtilLoggingStrategy.class;
+
+ /**
+ * This class contains the logic of how to weave a Java platform logger into the host class.
+ */
+ class JavaUtilLoggingStrategy extends LogASTTransformation.AbstractLoggingStrategyV2 {
+
+ private static final ClassNode LOGGER_CLASSNODE = ClassHelper.make(System.Logger.class);
+ private static final ClassNode LOGGER_FINDER_CLASSNODE = ClassHelper.make(System.LoggerFinder.class);
+
+ protected JavaUtilLoggingStrategy(final GroovyClassLoader loader) {
+ super(loader);
+ }
+
+ @Override
+ public FieldNode addLoggerFieldToClass(ClassNode classNode, String logFieldName, String categoryName, int fieldModifiers) {
+ Expression module = callX(classX(classNode), "getModule");
+ Expression loggerFinder = callX(classX(LOGGER_FINDER_CLASSNODE), "getLoggerFinder");
+ Expression initialValue = callX(loggerFinder, "getLogger", args(constX(getCategoryName(classNode, categoryName)), module));
+ return classNode.addField(logFieldName, fieldModifiers, LOGGER_CLASSNODE, initialValue);
+ }
+
+ @Override
+ public boolean isLoggingMethod(String methodName) {
+// return methodName.matches("error|warn|info|debug|trace");
+ return false;
+ }
+
+ @Override
+ public Expression wrapLoggingMethodCall(Expression logVariable, String methodName, Expression originalExpression) {
+ // no shortcut
+ return originalExpression;
+ }
+ }
+}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
index e635157..ebd08f3 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
@@ -46,6 +46,7 @@ import java.math.BigInteger;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -64,6 +65,16 @@ import java.util.stream.Collectors;
public class Java9 extends Java8 {
private static final Logger LOGGER = Logger.getLogger(Java9.class.getName());
+ private final Class<?>[] PLUGIN_DGM;
+
+ public Java9() {
+ super();
+ List<Class<?>> dgmClasses = new ArrayList<>();
+ Collections.addAll(dgmClasses, super.getPluginDefaultGroovyMethods());
+ dgmClasses.add(PluginDefaultGroovyMethods.class);
+ PLUGIN_DGM = dgmClasses.toArray(new Class<?>[0]);
+ }
+
@Override
public Map<String, Set<String>> getDefaultImportClasses(String[] packageNames) {
List<String> javaPns = new ArrayList<>(4);
@@ -165,6 +176,11 @@ public class Java9 extends Java8 {
}
@Override
+ public Class<?>[] getPluginDefaultGroovyMethods() {
+ return PLUGIN_DGM;
+ }
+
+ @Override
public int getVersion() {
return 9;
}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/PluginDefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/PluginDefaultGroovyMethods.java
new file mode 100644
index 0000000..b332853
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/PluginDefaultGroovyMethods.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.vmplugin.v9;
+
+import org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport;
+
+/**
+ * Defines new Groovy methods which appear on normal JDK 9
+ * classes inside the Groovy environment.
+ *
+ * @since 3.0.4
+ */
+public class PluginDefaultGroovyMethods extends DefaultGroovyMethodsSupport {
+
+ // No instances, static methods only
+ private PluginDefaultGroovyMethods() {
+ }
+
+ /**
+ * Convenience method for logging info level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void info(final System.Logger logger, final String msg) {
+ logger.log(System.Logger.Level.INFO, msg);
+ }
+
+ /**
+ * Convenience method for logging info level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void info(final System.Logger logger, final String msg, final Throwable thrown) {
+ logger.log(System.Logger.Level.INFO, msg, thrown);
+ }
+
+ /**
+ * Convenience method for logging info level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void info(final System.Logger logger, final String format, final Object... params) {
+ logger.log(System.Logger.Level.INFO, format, params);
+ }
+
+ /**
+ * Convenience method for logging trace level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void trace(final System.Logger logger, final String msg) {
+ logger.log(System.Logger.Level.TRACE, msg);
+ }
+
+ /**
+ * Convenience method for logging trace level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void trace(final System.Logger logger, final String msg, final Throwable thrown) {
+ logger.log(System.Logger.Level.TRACE, msg, thrown);
+ }
+
+ /**
+ * Convenience method for logging trace level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void trace(final System.Logger logger, final String format, final Object... params) {
+ logger.log(System.Logger.Level.TRACE, format, params);
+ }
+
+ /**
+ * Convenience method for logging warning level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void warn(final System.Logger logger, final String msg) {
+ logger.log(System.Logger.Level.WARNING, msg);
+ }
+
+ /**
+ * Convenience method for logging warning level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void warn(final System.Logger logger, final String msg, final Throwable thrown) {
+ logger.log(System.Logger.Level.WARNING, msg, thrown);
+ }
+
+ /**
+ * Convenience method for logging warning level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void warn(final System.Logger logger, final String format, final Object... params) {
+ logger.log(System.Logger.Level.WARNING, format, params);
+ }
+
+ /**
+ * Convenience method for logging error level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void error(final System.Logger logger, final String msg) {
+ logger.log(System.Logger.Level.ERROR, msg);
+ }
+
+ /**
+ * Convenience method for logging error level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void error(final System.Logger logger, final String msg, final Throwable thrown) {
+ logger.log(System.Logger.Level.ERROR, msg, thrown);
+ }
+
+ /**
+ * Convenience method for logging error level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void error(final System.Logger logger, final String format, final Object... params) {
+ logger.log(System.Logger.Level.ERROR, format, params);
+ }
+
+ /**
+ * Convenience method for logging debug level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void debug(final System.Logger logger, final String msg) {
+ logger.log(System.Logger.Level.DEBUG, msg);
+ }
+
+ /**
+ * Convenience method for logging debug level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void debug(final System.Logger logger, final String msg, final Throwable thrown) {
+ logger.log(System.Logger.Level.DEBUG, msg, thrown);
+ }
+
+ /**
+ * Convenience method for logging debug level messages with the platform logger.
+ *
+ * @param logger the Optional
+ */
+ public static void debug(final System.Logger logger, final String format, final Object... params) {
+ logger.log(System.Logger.Level.DEBUG, format, params);
+ }
+
+}
diff --git a/src/test-resources/META-INF/services/java.lang.System$LoggerFinder b/src/test-resources/META-INF/services/java.lang.System$LoggerFinder
new file mode 100644
index 0000000..86210b2
--- /dev/null
+++ b/src/test-resources/META-INF/services/java.lang.System$LoggerFinder
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+groovy.util.logging.vm9.LoggerSpyFinder
\ No newline at end of file
diff --git a/src/test/groovy/util/logging/vm9/LoggerSpy.groovy b/src/test/groovy/util/logging/vm9/LoggerSpy.groovy
new file mode 100644
index 0000000..a9a9c5c
--- /dev/null
+++ b/src/test/groovy/util/logging/vm9/LoggerSpy.groovy
@@ -0,0 +1,54 @@
+package groovy.util.logging.vm9
+
+import groovy.transform.AutoImplement
+import groovy.transform.CompileStatic
+import groovy.transform.PackageScope
+
+@PackageScope
+@AutoImplement
+@CompileStatic
+class LoggerSpy implements System.Logger {
+ String warningParameter = null
+ String infoParameter = null
+ String traceParameter = null
+ String errorParameter = null
+ String debugParameter = null
+
+ void reset() {
+ warningParameter = null
+ infoParameter = null
+ traceParameter = null
+ errorParameter = null
+ debugParameter = null
+ }
+
+ @Override
+ void log(Level lev, String s) {
+ switch (lev) {
+ case Level.WARNING:
+ if (warningParameter) throwAssertionError("Warning already called once with parameter $warningParameter")
+ warningParameter = s
+ break
+ case Level.INFO:
+ if (infoParameter) throwAssertionError("Info already called once with parameter $infoParameter")
+ infoParameter = s
+ break
+ case Level.ERROR:
+ if (errorParameter) throwAssertionError("Error already called once with parameter $errorParameter")
+ errorParameter = s
+ break
+ case Level.DEBUG:
+ if (debugParameter) throwAssertionError("Debug already called once with parameter $debugParameter")
+ debugParameter = s
+ break
+ case Level.TRACE:
+ if (traceParameter) throwAssertionError("Trace already called once with parameter $traceParameter")
+ traceParameter = s
+ break
+ }
+ }
+
+ private static void throwAssertionError(Object detailMessage) {
+ throw new AssertionError(detailMessage)
+ }
+}
diff --git a/src/test/groovy/util/logging/vm9/LoggerSpyFinder.groovy b/src/test/groovy/util/logging/vm9/LoggerSpyFinder.groovy
new file mode 100644
index 0000000..dce6f9f
--- /dev/null
+++ b/src/test/groovy/util/logging/vm9/LoggerSpyFinder.groovy
@@ -0,0 +1,13 @@
+package groovy.util.logging.vm9
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class LoggerSpyFinder extends System.LoggerFinder {
+ static System.Logger spy = null
+
+ @Override
+ System.Logger getLogger(String name, Module module) {
+ spy = new LoggerSpy()
+ }
+}
\ No newline at end of file
diff --git a/src/test/groovy/util/logging/vm9/PlatformLogTest.groovy b/src/test/groovy/util/logging/vm9/PlatformLogTest.groovy
new file mode 100644
index 0000000..b1b4aed
--- /dev/null
+++ b/src/test/groovy/util/logging/vm9/PlatformLogTest.groovy
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.util.logging.vm9
+
+import groovy.test.GroovyTestCase
+import groovy.util.logging.PlatformLog
+import org.codehaus.groovy.control.MultipleCompilationErrorsException
+
+import java.lang.reflect.Field
+import java.lang.reflect.Modifier
+
+/**
+ * Test to make sure the @Log annotation is working correctly.
+ */
+class PlatformLogTest extends GroovyTestCase {
+ void testPrivateFinalStaticLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ @groovy.util.logging.PlatformLog
+ class MyClassPrivateFinalStaticLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ Modifier.isPrivate(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testPrivateFinalStaticNamedLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ @groovy.util.logging.PlatformLog('logger')
+ class MyClassPrivateFinalStaticNamedLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'logger' &&
+ Modifier.isPrivate(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testExplicitPrivateFinalStaticLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ import static groovy.transform.options.Visibility.*
+ @groovy.transform.VisibilityOptions(value = PRIVATE)
+ @groovy.util.logging.PlatformLog
+ class MyClassExplicitPrivateFinalStaticLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ Modifier.isPrivate(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testPackagePrivateFinalStaticLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ import static groovy.transform.options.Visibility.*
+ @groovy.transform.VisibilityOptions(value = PACKAGE_PRIVATE)
+ @groovy.util.logging.PlatformLog
+ class MyClassPackagePrivateFinalStaticLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ !Modifier.isPrivate(field.getModifiers()) &&
+ !Modifier.isProtected(field.getModifiers()) &&
+ !Modifier.isPublic(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testProtectedFinalStaticLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ import static groovy.transform.options.Visibility.*
+ @groovy.transform.VisibilityOptions(value = PROTECTED)
+ @groovy.util.logging.PlatformLog
+ class MyClassProtectedFinalStaticLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ Modifier.isProtected(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testPublicFinalStaticLogFieldAppears() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ import static groovy.transform.options.Visibility.*
+ @groovy.transform.VisibilityOptions(value = PUBLIC)
+ @groovy.util.logging.PlatformLog
+ class MyClassPublicFinalStaticLogFieldAppears { }
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ Modifier.isPublic(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testClassAlreadyHasLogField() {
+ def msg = shouldFail {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ @groovy.util.logging.PlatformLog
+ class MyClassAlreadyHasLogField {
+ String log
+ }
+ ''')
+
+ assert clazz.getConstructor().newInstance()
+ }
+ assert msg.contains('cannot have log field declared')
+ }
+
+ void testClassAlreadyHasNamedLogField() {
+ def msg = shouldFail {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ @groovy.util.logging.PlatformLog('logger')
+ class MyClassAlreadyHasNamedLogField {
+ String logger
+ }
+ ''')
+ assert clazz.getConstructor().newInstance()
+ }
+ assert msg.contains('cannot have log field declared')
+ }
+
+ @PlatformLog
+ class MyClassLogFromStaticMethods {
+ static loggingMethod() {
+ log.info ('info called')
+ }
+ }
+
+ void testLogFromStaticMethods() {
+ MyClassLogFromStaticMethods.loggingMethod()
+ def finder = System.LoggerFinder.getLoggerFinder()
+ assert finder instanceof LoggerSpyFinder
+ def logSpy = finder.spy
+ assert logSpy.infoParameter == 'info called'
+ }
+
+ void testLogInfo() {
+ Class clazz = new GroovyClassLoader().parseClass('''
+ @groovy.util.logging.PlatformLog
+ class MyClassLogInfo {
+ def loggingMethod() {
+ log.error('error called')
+ log.warn ('warning called')
+ log.info ('info called')
+ log.debug('debug called')
+ log.trace('trace called')
+ }
+ }
+ ''')
+ def s = clazz.getConstructor().newInstance()
+ s.loggingMethod()
+ def finder = System.LoggerFinder.getLoggerFinder()
+ assert finder instanceof LoggerSpyFinder
+ def logSpy = finder.spy
+ assert logSpy.warningParameter == 'warning called'
+ assert logSpy.infoParameter == 'info called'
+ assert logSpy.debugParameter == 'debug called'
+ assert logSpy.traceParameter == 'trace called'
+ assert logSpy.errorParameter == 'error called'
+ logSpy.reset()
+ }
+
+ @PlatformLog('logger')
+ static class MyClassLogInfoWithName {
+ def loggingMethod() {
+ logger.error('error called')
+ logger.warn ('warning called')
+ logger.info ('info called')
+ logger.debug('debug called')
+ logger.trace('trace called')
+ }
+ }
+
+ void testLogInfoWithName() {
+ new MyClassLogInfoWithName().loggingMethod()
+ def finder = System.LoggerFinder.getLoggerFinder()
+ assert finder instanceof LoggerSpyFinder
+ def logSpy = finder.spy
+ assert logSpy.warningParameter == 'warning called'
+ assert logSpy.infoParameter == 'info called'
+ assert logSpy.debugParameter == 'debug called'
+ assert logSpy.traceParameter == 'trace called'
+ assert logSpy.errorParameter == 'error called'
+ }
+
+ void testInheritancePrivateNoShadowingIssue() {
+ def clazz = new GroovyShell().evaluate('''
+ class MyParentTestInheritance {
+ private log
+ }
+
+ @groovy.util.logging.PlatformLog
+ class MyClassTestInheritance extends MyParentTestInheritance {
+ def loggingMethod() {
+ log.info (prepareLogMessage())
+ }
+ def prepareLogMessage() {
+ 'formatted log message'
+ }
+ }
+
+ return MyClassTestInheritance
+ ''')
+
+ assert clazz.declaredFields.find { Field field ->
+ field.name == 'log' &&
+ Modifier.isPrivate(field.getModifiers()) &&
+ Modifier.isStatic(field.getModifiers()) &&
+ Modifier.isTransient(field.getModifiers()) &&
+ Modifier.isFinal(field.getModifiers())
+ }
+ }
+
+ void testInheritanceProtectedShadowing() {
+ def msg = shouldFail(MultipleCompilationErrorsException) {
+ new GroovyClassLoader().parseClass('''
+ class MyParentProtectedShadowing {
+ protected log
+ }
+
+ @groovy.util.logging.PlatformLog
+ class MyClassProtectedShadowing extends MyParentProtectedShadowing { }
+ ''')
+ }
+ assert msg.contains('cannot have log field declared because the field exists in the parent class')
+ }
+
+ void testInheritancePublicShadowing() {
+ def msg = shouldFail(MultipleCompilationErrorsException) {
+ new GroovyClassLoader().parseClass('''
+ class MyParentPublicShadowing {
+ public log
+ }
+
+ @groovy.util.logging.PlatformLog
+ class MyClassPublicShadowing extends MyParentPublicShadowing {
+ }
+ ''')
+ }
+ assert msg.contains('cannot have log field declared because the field exists in the parent class')
+ }
+}
+