Need help implementing new commands in groovysh - groovy

I've found very little info on building new commands for Groovysh. I'd like to use it as a normal part of my dev environment, to some degree replacing cmd.exe().
I did notice that there is a "register" command in groovysh that allows you to register new commands. After finding nothing I ended up looking at the source code for the existing commands and came up with this:
import org.codehaus.groovy.tools.shell.*
class test extends CommandSupport
{
public static final String COMMAND_NAME = 'findall'
// Printed when you use the help command specifying 'find' as an argument
String help="Help"
String usage="Usage"
// Printed when you use the help command with no arguments
String description="Description"
public test(org.codehaus.groovy.tools.shell.Groovysh shell)
{
super(shell, COMMAND_NAME, 'find')
}
Object execute(List<String> args)
{
return "Commanded "+args+" "+args.size()
}
}
This does most of what I want, but I have a couple problems with it. First of all, the thing passed to "execute" is pre-parsed in an ugly way. If I try to find a string like "test strange spacing" I get ["test, strange, spacing"] I can use the quotes to rebuild what was supposed to be quoted as a single string but I can't replace the extra spaces"
The second issue is that I'd like to use tab completion. I can see that there are getCompleter and makeCompleters commands but there is no info on what a completer is... the javadocs link to a page that doesn't exist.
There are completers in the JLine library but I'm not sure they are the same thing (I tend to doubt it because JLine is inaccessible from groovysh, if you needed to use those to write scripts, you'd think they would be accessible)
If anyone knows of a blog that instructs you on how to do this kind of stuff--or has a few minimal examples laying around I'd appreciate the help.

You have deciphered groovy source pretty well. You can return jline completeres in overridden createCompleters method. You can also use completeres from org.codehaus.groovy.tools.shell.util.
import jline.console.completer.StringsCompleter
import org.codehaus.groovy.tools.shell.CommandSupport
import org.codehaus.groovy.tools.shell.Groovysh
import org.codehaus.groovy.tools.shell.util.SimpleCompletor;
public class GroovyshCmd extends CommandSupport {
public static final String COMMAND_NAME = ':mycmd'
public static final String SHORTCUT = ':my'
protected GroovyshCmd(Groovysh shell) {
super(shell, COMMAND_NAME, SHORTCUT)
}
#Override
public List<Completer> createCompleters() {
//return [new SimpleCompletor((String[])["what", "ever", "here"]), null]
return [new StringsCompleter("what", "ever", "here"), null]
}
#Override
public Object execute(List<String> args) {
println "yo"
}
}
I agree this is needlessly overcomplicated.

Related

Groovy execute code using custom annotation

I would like to execute a function (with parameters) through an annotation tag in a groovy script.
If we execute a method in our groovy script with this annotation it would print in the console (stderr) a custom message like:
warning: '<function_name>' is deprecated [[Use '<Deprecated.instead>' instead.][More info: '<Deprecated.more_info>']]
So, I have created a custom annotation like this
public #interface Deprecated {
public String instead() default null
public String more_info() default null
}
The goal is to use it like this:
def new_call() {
//new version of the method
}
#Deprecated(instead="new_call")
def call() {
//do something
}
In my example, it would output like this:
warning: 'call' is deprecated. Use 'new_call' instead.
I saw this post Groovy: How to call annotated methods, it's over 7 years old now but looks good so i'll look deeper.
I saw also Delegate.deprecated but i'm not sure if that's what i want
I'm not sure I am doing right. So if you have any advice or suggestions, I'll be happy to hear you.
Simple AOP Approach
This is very-very basic implementation with groovy out-of the box.
Deprecated Annotation
#Target([ElementType.METHOD])
#Retention(RetentionPolicy.RUNTIME)
#interface Deprecated {
String instead() default 'null'
String more_info() default 'null'
}
Class which should get this functionality
The class has to implement GroovyInterceptable - invokeMethod.
class SomeClass implements GroovyInterceptable {
#Override
def invokeMethod(String name, args) {
DeprecatedInterception.apply(this, name, args)
}
def new_call() {
println('new_call invoked')
}
#Deprecated(instead = 'new_call', more_info = '... the reason')
def depr_call() {
println('depr_call invoked')
}
}
Interception Util
import org.codehaus.groovy.reflection.CachedMethod
class DeprecatedInterception {
static apply(Object owner, String methodName, Object args) {
MetaMethod metaMethod = owner.metaClass.getMetaMethod(methodName, args)
Deprecated d = extractAnnotation(metaMethod)
if (d) {
println("warning: '$methodName' is deprecated. Use '${d.instead()}' instead. More info: '${d.more_info()}'")
}
// handle methods with var-args
metaMethod.isVargsMethod() ?
metaMethod.doMethodInvoke(owner, args) :
metaMethod.invoke(owner, args)
}
static Deprecated extractAnnotation(MetaMethod metaMethod) {
if (metaMethod instanceof CachedMethod) {
metaMethod.getCachedMethod()?.getAnnotation(Deprecated)
} else {
null
}
}
}
Simple Test
Just check that no exceptions/errors..
class TestWarnings {
#Test
void test() {
new SomeClass().with {
new_call()
depr_call()
}
}
}
Output:
new_call invoked
warning: 'depr_call' is deprecated. Use 'new_call' instead. More info: '... the reason'
depr_call invoked
Disclaimer
This should work for most cases, but has some limitations:
will not work for static methods (unless invoked on Object instance)
you have to implement GroovyInterceptable per each class, to apply
you might faced with some side-effects in some groovy syntax or features (at least I've found the issue with vararg methods invocation, but this already fixed)
So this should be tested and potentially improved before widely using for some production projects.
Other options:
Shortly, because implementation might be more complex (not sure, at least I not able to provide some example in a short time), but potentially this is more solid.
Adding AST Transformations.
Use some AOP library.

How can I run code in JUnit before Spring starts?

How can I run code in my #RunWith(SpringRunner.class) #SpringBootTest(classes = {...}) JUnit test before Spring starts?
This question has been asked several times (e.g. 1, 2) but was always "solved" by some configuration recommendation or other, never with a universal answer. Kindly don't question what I am about to do in that code but simply suggest a clean way to do it.
Tried so far and failed:
Extend SpringJUnit4ClassRunner to get a class whose constructor can run custom code before initializing Spring. Failed because super(testClass) must be called first thing and already does a whole lot of things that get in the way.
Extend Runner to get a class that delegates to SpringRunner instead of inheriting it. This class could run custom code in its constructor before actually instantiating the SpringRunner. However, this setup fails with obscure error messages like java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig. "Obscure" because my test has no web config and thus shouldn't meddle with sessions and cookies.
Adding an ApplicationContextInitializer that is triggered before Spring loads its context. These things are easy to add to the actual #SpringApplication, but hard to add in Junit. They are also quite late in the process, and a lot of Spring has already started.
One way to do it is to leave out SpringRunner and use the equivalent combination of SpringClassRule and SpringMethodRule instead. Then you can wrap the SpringClassRule and do your stuff before it kicks in:
public class SomeSpringTest {
#ClassRule
public static final TestRule TestRule = new TestRule() {
private final SpringClassRule springClassRule =
new SpringClassRule();
#Override
public Statement apply(Statement statement, Description description) {
System.out.println("Before everything Spring does");
return springClassRule.apply(statement, description);
}
};
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Test
public void test() {
// ...
}
}
(Tested with 5.1.4.RELEASE Spring verison)
I don't think you can get more "before" than that. As for other options you could also check out #BootstrapWith and #TestExecutionListeners annotations.
Complementing jannis' comment on the question, the option to create an alternative JUnit runner and let it delegate to the SpringRunner does work:
public class AlternativeSpringRunner extends Runner {
private SpringRunner springRunner;
public AlternativeSpringRunner(Class testClass) {
doSomethingBeforeSpringStarts();
springRunner = new SpringRunner(testClass);
}
private doSomethingBeforeSpringStarts() {
// whatever
}
public Description getDescription() {
return springRunner.getDescription();
}
public void run(RunNotifier notifier) {
springRunner.run(notifier);
}
}
Being based on spring-test 4.3.9.RELEASE, I had to override spring-core and spring-tx, plus javax.servlet's servlet-api with higher versions to make this work.

Black listing synchronized keyword for groovy scripts

There is an application where users can provide custom groovy scripts. They can write their own functions in those scripts. I want to restrict people from using the 'synchronized' keyword as well as some other keywords in these scripts. For example it should not be possible to write a function like below.
public synchronized void test() {
}
I am creating a CompilerConfiguration and using the SecureASTCustomizer. However adding org.codehaus.groovy.syntax.Types.KEYWORD_SYNCHRONIZED to the list of black listed tokens doesn't seem to do the job. (if I add org.codehaus.groovy.syntax.Types.PLUS it's preventing the usage of '+' within scripts.. but doesn't seem to do the job for synchronized)
Any ideas on how to achieve this ...
You can do something like this:
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.classgen.GeneratorContext
class SynchronizedRemover extends org.codehaus.groovy.control.customizers.CompilationCustomizer {
SynchronizedRemover() {
super(org.codehaus.groovy.control.CompilePhase.CONVERSION)
}
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
classNode.methods.each { mn ->
if (mn.modifiers & 0x0020) { // 0x0020 is for synchronized
source.addError(new SyntaxException("Synchronized is not allowed", mn.lineNumber, mn.columnNumber))
}
}
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new SynchronizedRemover())
def shell = new GroovyShell(config)
shell.evaluate '''
class Foo { public synchronized void foo() { println 'bar' } }
'''
The idea is to create a compilation customizer that checks generated classes and for each method, add an error if the synchronized modifier is present. For synchronized block inside methods, you can probably use the SecureASTCustomizer with a custom statement checker.

Geb: setting the reports dir at runtime

I'm writing a little test framework for a site using Geb. As part of my reporting functionality, I'd like to be able to specify at runtime where the reportsDir lives. I'm no developer so please excuse any omissions in this question.
Everything I've read so far suggests that this can only be set via the project's configuration or the build adaptor. However, Geb's Configuration class has a setReportsDir method which I can access from my browser object:
def currentConfig = pageBrowser.getConfig()
def reportLocation = "target/runtime_reports_dir"
def reportFile = new File(reportLocation)
reportFile.mkdirs()
File newTarget = new File(reportLocation)
currentConfig.setReportsDir(newTarget)
Unfortunately, although this appears to change the reportsDir in the browser's config object, the actual output still appears in the directory defined by my configuration.
Is this possible? Could I override the setupReporting method in GebReportingTest instead (I've not found anything suggesting how this might be done either)?
--- Edit 1 ---
I've tried
class MyTest extends GebReportingTest {
def pageBrowser
def setup() {
this.pageBrowser = new Browser()
this.pageBrowser.config.reportsDir = new File( 'target/runtime_reports_dir' )
}
#Test
void runTestSet() {
setup()
this.pageBrowser....
}
}
after Tim's comments but I've had no joy so far. After invoking the setup() method, the pageBrowser's config object returns the reportsDir I've defined in code. However, all instances of the 'report' command store screenshots etc in the directory defined in GebConfig.groovy.
Geb will look for GebConfig.groovy in the root of the classpath
You can set reportsDir in there
See: http://www.gebish.org/manual/0.6.2/configuration.html#reports_dir
Edit:
Have you tried:
class MyTest extends GebReportingTest {
void setUp() {
browser.config.reportsDir = new File( 'target/runtime_reports_dir' )
}
#Test
void runTestSet() {
// Do testing
}
}

Can I use something like DebuggerTypeProxyAttribute on a type that I don't own?

I've got an IClaimsPrincipal variable, and I'd like to see how many claims are in it. Navigating through the properties in the watch window is complicated, so I'd like to customize how this object is displayed.
I'm aware of the [DebuggerTypeProxy] attribute, which initially looked like it might do what I want. Unfortunately, it needs to be attached to the class, and I don't "own" the class. In this case it's a Microsoft.IdentityModel.Claims.ClaimsPrincipal.
I'd like to display the value of IClaimsPrincipal.Identities[0].Claims.Count.
Is there any way, using [DebuggerTypeProxy] or similar, to customize how the value of a type that I don't own is displayed in the watch window?
Example of DebuggerTypeProxyAttribute applied to KeyValuePair showing only the Value member:
using System.Collections.Generic;
using System.Diagnostics;
[assembly: DebuggerTypeProxy(typeof(ConsoleApp2.KeyValuePairDebuggerTypeProxy<,>), Target = typeof(KeyValuePair<,>))]
// alternative format [assembly: DebuggerTypeProxy(typeof(ConsoleApp2.KeyValuePairDebuggerTypeProxy<,>), TargetTypeName = "System.Collections.Generic.KeyValuePair`2")]
namespace ConsoleApp2
{
class KeyValuePairDebuggerTypeProxy<TKey, TValue>
{
private KeyValuePair<TKey, TValue> _keyValuePair; // beeing non-public this member is hidden
//public TKey Key => _keyValuePair.Key;
public TValue Value => _keyValuePair.Value;
public KeyValuePairDebuggerTypeProxy(KeyValuePair<TKey, TValue> keyValuePair)
{
_keyValuePair = keyValuePair;
}
}
class Program
{
static void Main(string[] args)
{
var dictionary = new Dictionary<int, string>() { [1] = "one", [2] = "two" };
Debugger.Break();
}
}
}
Tested on Visual Studio 2017
The best I've come up with so far is to call a method:
public static class DebuggerDisplays
{
public static int ClaimsPrincipal(IClaimsPrincipal claimsPrincipal)
{
return claimsPrincipal.Identities[0].Claims.Count;
}
}
...from the watch window:
DebuggerDisplays.ClaimsPrincipal(_thePrincipal),ac = 10
The ",ac" suppresses the "This expression causes side effects and will not be evaluated".
However, note that when this goes out of scope, Visual Studio will simply grey out the watch window entry, even with the ",ac". To avoid this, you'll need to ensure that everything is fully qualified, which means that you'll end up with extremely long expressions in the watch window.

Resources