Using Groovy as a Script Engine in Java Projects: Integration, Performance Optimization, and Security
By adopting Groovy as a JSR‑223 script engine, the Vivo Internet Server team streamlined dynamic query and rule processing in their Java content platform, demonstrating integration via ScriptEngineManager, GroovyShell, and GroovyClassLoader, while optimizing performance through caching and static typing and enforcing security with SecureASTCustomizer and isolated execution.
This article, authored by the Vivo Internet Server Team, shares practical experience of adopting Groovy as a dynamic script engine in a Java-based content platform. It explains why Groovy was chosen, its basic principles, how it integrates with Java, and the security and performance considerations involved.
Why use a scripting language
Rapid business growth creates a need for flexible, multi‑dimensional queries and rule‑based workflows. Traditional approaches—hard‑coded enumerations, rule engines like Drools, or static code—either become cumbersome or lack adaptability. A dynamic script engine (e.g., Groovy via JSR‑223) offers a standardized way to execute scripts from Java and access Java resources, making it suitable for highly customizable business logic.
Technical selection
Groovy was selected because it has a gentle learning curve, rich syntactic sugar, strong Java compatibility, mature tooling, and stable performance. These factors make it developer‑friendly for Java teams.
Business transformation
The content platform needed to support arbitrary query dimensions and distribution strategies. By abstracting query conditions and dispatch rules into Groovy scripts, the team eliminated repetitive code, reduced development cycles, and avoided frequent releases.
Groovy–Java integration
Three main integration methods are demonstrated:
ScriptEngineManager (JSR‑223)
GroovyShell
GroovyClassLoader
Example code for each method is provided below.
/** * Build a MongoDB Query object with pagination */ public Query query(int page) { String source = "Groovy"; int articleType = 4; // composite index for efficiency Query query = Query.query(where("source").is(source)); query.addCriteria(where("articleType").is(articleType)); Pageable pageable = new PageRequest(page, PAGESIZE); query.with(pageable); query.fields().include("authorId"); query.fields().include("level"); return query; } /** * Filter each page of results */ public boolean filter(UpAuthor upAuthor) { return !"S".equals(upAuthor.getLevel()); // keep only level S authors } /** * Process each result item */ public void handle(UpAuthor upAuthor) { UpAuthorService upAuthorService = SpringUtil.getBean("upAuthorService"); if (upAuthorService == null) { throw new RuntimeException("upAuthorService is null"); } AnalysePlatService analysePlatService = SpringUtil.getBean("analysePlatService"); if (analysePlatService == null) { throw new RuntimeException("analysePlatService is null"); } List articleList = upAuthorService.getArticles(upAuthor); if (CollectionUtils.isEmpty(articleList)) { return; } articleList.forEach(article -> { if (article.getAnalysis() == null) { analysePlatService.analyse(article.getArticleId()); } }); } Using ScriptEngineManager : ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("groovy"); // creates a new engine instance each call Bindings binding = engine.createBindings(); binding.put("date", new Date()); // input parameter engine.eval("def getTime(){return date.getTime();}", binding); engine.eval("def sayHello(name,age){return 'Hello,I am ' + name + ',age' + age;}"); Long time = (Long) ((Invocable) engine).invokeFunction("getTime", null); String message = (String) ((Invocable) engine).invokeFunction("sayHello", "zhangsan", 12); System.out.println(time); System.out.println(message); Using GroovyShell : final String script = "Runtime.getRuntime().availableProcessors()"; Binding intBinding = new Binding(); GroovyShell shell = new GroovyShell(intBinding); Object eval = shell.evaluate(script); System.out.println(eval); Using GroovyClassLoader : GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); String helloScript = "package com.vivo.groovy.util" + "class Hello {" + "String say(String name) { System.out.println(\"hello, \" + name); return name; }" + "}"; Class helloClass = groovyClassLoader.parseClass(helloScript); GroovyObject object = (GroovyObject) helloClass.newInstance(); Object ret = object.invokeMethod("say", "vivo"); System.out.println(ret.toString()); Performance optimization High concurrency can cause repeated compilation, class‑loader synchronization, and Metaspace growth, leading to memory leaks and thread blocking. The article recommends caching compiled classes (keyed by script MD5), limiting the number of GroovyClassLoader instances, and using static typing to reduce dynamic checks. Security Active security measures include using SecureASTCustomizer to blacklist dangerous tokens, imports, and statements (e.g., while , goto , certain packages). Example configuration: final SecureASTCustomizer secure = new SecureASTCustomizer(); secure.setClosuresAllowed(true); List tokensBlacklist = new ArrayList<>(); tokensBlacklist.add(Types.KEYWORD_WHILE); tokensBlacklist.add(Types.KEYWORD_GOTO); secure.setTokensBlacklist(tokensBlacklist); secure.setIndirectImportCheckEnabled(true); List importsBlacklist = new ArrayList<>(); importsBlacklist.add("com.alibaba.fastjson.JSONObject"); secure.setImportsBlacklist(importsBlacklist); List > statementBlacklist = new ArrayList<>(); statementBlacklist.add(WhileStatement.class); secure.setStatementsBlacklist(statementBlacklist); CompilerConfiguration config = new CompilerConfiguration(); config.addCompilationCustomizers(secure); GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader(), config); Passive security involves isolating script execution in thread pools, monitoring resource usage, and terminating runaway scripts. Conclusion Groovy provides a dynamic, JVM‑compatible scripting solution that accelerates development, improves responsiveness to changing business requirements, and, when properly secured and optimized, can be safely used in backend systems.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.