I created the Jenkins pipeline which calls below function... It creates a Template variable with StreamingTemplateEngine object... But it gives an error
def call() {
def name = "abc"
def binding = [
firstname: "Grace",
lastname: "Hopper",
]
def text = 'Dear <% out.print firstname %> ${lastname}'
def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)
print template.make(binding)
def response = template.make(binding)
withCredentials([string(credentialsId: 'Token', variable: 'TOKEN')]) {
println("test")
println(response)
}
}
Above code prints response successfully first time but at the end it gives below error
an exception which occurred:
in field com.cloudbees.groovy.cps.impl.BlockScopeEnv.locals
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv#3678d955
in field com.cloudbees.groovy.cps.impl.CpsClosureDef.capture
in object com.cloudbees.groovy.cps.impl.CpsClosureDef#23a3d63c
in field com.cloudbees.groovy.cps.impl.CpsClosure.def
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#6d8ad313
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#76f2b368
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#76f2b368
Caused: java.io.NotSerializableException: groovy.text.StreamingTemplateEngine$StreamingTemplate
If I remove withCredentials function then it works fine.
Jenkins Pipeline runs Groovy code in the continuation-passing style (groovy-cps). It expects that every local variable is Serializable so it can safely serialize every computation and restore it in case of, e.g., Jenkins restart.
In case of using a non-serializable object, Jenkins Pipeline offers #NonCPS annotation that can be used with a method to mark that this part of code is not serializable and shouldn't be transformed to the CPS code interpretation.
"Pipeline scripts may mark designated methods with the annotation #NonCPS. These are then compiled normally (except for sandbox security checks), and so behave much like “binary” methods from the Java Platform, Groovy runtime, or Jenkins core or plugin code. #NonCPS methods may safely use non-Serializable objects as local variables, though they should not accept nonserializable parameters or return or store nonserializable values. You may not call regular (CPS-transformed) methods, or Pipeline steps, from a #NonCPS method, so they are best used for performing some calculations before passing a summary back to the main script. Note in particular that #Overrides of methods defined in binary classes, such as Object.toString(), should in general be marked #NonCPS since it will commonly be binary code calling them."
Source: https://github.com/jenkinsci/workflow-cps-plugin#technical-design
You can extract StreamingTemplateEngine part to the separate #NonCPS method that expects a template as text, and a map of bindings. Something like this should be safe to use:
import com.cloudbees.groovy.cps.NonCPS
def call() {
def name = "abc"
def binding = [
firstname: "Grace",
lastname: "Hopper",
]
def text = 'Dear <% out.print firstname %> ${lastname}'
def response = parseTemplate(text, binding)
withCredentials([string(credentialsId: 'Token', variable: 'TOKEN')]) {
println(response)
}
}
#NonCPS
String parseTemplate(String text, Map bindings) {
new groovy.text.StreamingTemplateEngine().createTemplate(text)
.make(bindings)
.toString()
}
Related
I can wrap a Java function like this:
* def myJavaMethod =
"""
function() {
var Utils = Java.type('Utils');
// use Number type in constructor
var obj = new Utils(...);
return obj.myJavaMethod();
}
"""
But why would I? I can use Java functions straight in the test scenarios, like this:
Scenario: Test exec and error value
* def Utils = Java.type('Utils');
* def ret = Utils.exec('echo "From Java" > /home/richard//karate-test/karate-0.9.6/out.txt');
* match read('out.txt') == "From Java\n";
* match Utils.exec('exit 123') == 123
Note: exec is a static method that uses a shell to execute the command given.
At least I tested this for static methods and that works fine without the JavaScript detour.
It seems that the JavaScript wrapper only adds an extra layer of complication.
Besides that, with the 'call' syntax I can only pass one parameter (that admittedly can be an entire object or array).
But, I can pass parameters straight to a Java function (using normal syntax) and even use the result in a match.
(I assume parameters and results are implicitly converted to a JavaScript value, that could be a JSON object or array).
So far, I miss to see the advantage of explicitly wrapping Java code inside JavaScript wrappers. I assume the problem is me, that is: I am missing some important point here?
The only advantage is to reduce typing and when you have a lot of code re-use. For example, instead of:
* def foo = java.util.UUID.randomUUID() + ''
You define it once:
* def uuid = function(){ return java.util.UUID.randomUUID() + '' }
And then call it wherever, and it is super-concise:
* def json = { foo: '#(uuid())' }
I'm running in to a problem with KSQL while trying to set up an ETL pipeline using a UDF. At some point in the ETL process I need to isolate specific info from a description field (VARCHAR) in my data. A made-up example for context:
description = "species=dog.sex=male.color=blonde.age=10." (the real data is formatted in the same way)
I've written a simple UDF to isolate any information on demand. It looks like this:
package com.my.package;
/** IMPORTS **/
import io.confluent.ksql.function.udf.Udf;
import io.confluent.ksql.function.udf.UdfDescription;
/** ClASS DEFINITION **/
#UdfDescription(name = "extract_from_description",
author = "Me",
version = "0.0.1",
description = "Given a description and a request for information, isolates and returns the requested information. Pass requested tag as 'tag='".)
public class Extract_From_Description {
#Udf(description = "Given a description and a request for information, isolates and returns the requested information. Pass requested tag as 'tag='.)
public String extract_from_description(final String description, final String request) {
return description.split(request)[1].split("\\.")[0];
}
}
I can upload and register the function just fine, it's listed and described properly when I run:
ksql> list functions;
ksql> describe function EXTRACT_FROM_DESCRIPTION;
I call the function like this to create a new stream:
CREATE STREAM result AS
SELECT recordId,
OtherVariables,
EXTRACT_FROM_DESCRIPTION(description, 'species=') AS species
FROM parent_stream
EMIT CHANGES;
There I get an error I can't make sense of:
Function 'extract_from_description' does not accept parameters (STRING, STRING).
Valid alternatives are:
Apparently KSQL can't properly interpret what the input for the function is supposed to be (looks like it expects no input?) and I can't figure out why. I've read through documentation to see if I define my function in a weird way but can't find any differences between the examples and my function. I did notice there are supposed to be several ways to define the input a function takes and tried them all, but the result is always the same.
I use Maven to create the jar file for this function (JDK1.8.0_201). Can anyone help me figure out what's going on?
TL;DR: My KSQL UDF doesn't accept input of type (String, String) even though the function specifies the input should be of type (String, String)
Found the problem, answering here for anyone that might run in to the same problem.
You need to specify the parameters using #UdfParameter, like this:
import io.confluent.ksql.function.udf.UdfParameter; // add this to the list of imports
// add #UdfParameter(name) to each input variable
public String extract_from_description(#UdfParameter(value = "description") final String description, #UdfParameter(value = "request") final String request){
function body
}
To the downvoters - this is a legitimate question. Please take the time to examine it before assuming I'm mixing up my languages like some kind of programming newb!
I need to know if it's possible to import a Java object (specifically, an enum class) in a Typescript script.
I've googled but haven't found anything.
The ErrorCodeAuthority is for having custom, standardized errors thrown from our service for each known error with set messages (some parameterized, some not), http status codes, etc defined in one place.
In our javascript code we have
var JavaErrorCodeAuthority = Java.type("com.domain.ErrorCodeAuthority");
Is it possible to do the same in Typescript?
Edit based on answer below
I've declared the following:
declare module Java {
export enum ErrorCodeAuthority {
ENTITY_NOT_FOUND,
HTTP_VERB_NOT_SUPPORTED,
BAD_REQUEST,
//...
}
export function type(arg: "com.domain.ErrorCodeAuthority"): ErrorCodeAuthority;
export function type(arg: string): any;
}
var JavaErrorCodeAuthority = Java.type("com.domain.ErrorCodeAuthority");
and I'm attempting to use the new type as follows:
export class HttpFailResult extends HttpResult {
constructor(public errorCode : Java.ErrorCodeAuthority, public userParams? : UserParam[]) {
super(errorCode.httpStatus.value(), errorCode.toString());
}
}
I'm getting the following error when I try to use grunt to compile to js:
error TS2339: Property 'httpStatus' does not exist on type 'ErrorCodeAuthority'.
(For reference, the super HttpResult is an object that contains a number http code and astringbody. HttpStatus, in the Java enum, is of typeorg.springframework.http.HttpStatus`).
I tried removing the export function type(arg: "com.domain.ErrorCodeAuthority"): ErrorCodeAuthority; line but that didn't change the exception.
EDIT 2
We're running all of this inside a nashorn container if that makes a difference
Is it possible to do the same in Typescript?
Yes. With 1c, you can just write
let JavaErrorCodeAuthority = com.domain.ErrorCodeAuthority
And there will be auto-completion on each level of packages.
Yes, if you already did this in JavaScript you can use the code by creating a definition file for it and port it to TypeScript.
An example might be like this:
declare module Java {
export enum ErrorCodeAuthority {
item1,
item2
}
export function type(arg: "com.domain.ErrorCodeAuthority"): ErrorCodeAuthority;
export function type(arg: string): any;
}
var JavaErrorCodeAuthority = Java.type("com.domain.ErrorCodeAuthority");
The enum and the first type function with the "com.domain.ErrorCodeAuthority" is optional but it gives you better typeinfo when passed in that particular string. Note the declare module part doesn't generate any code and you can add it to a .ts or .d.ts file. More info about creating a definition file can be found here: https://github.com/Microsoft/TypeScript/wiki/Writing%20Definition%20Files
EDIT
after the info from the comments I hope this code below will better suite your need.
This has the downside that it isn't usable in a switch statement but in this case I think it is better to see the java enum as a module (or class was possible to). This might not be 100% correctly modelled but hopefully it gives you some extra idea's. Just a small side note, I find your case very interesting and challenging!
declare module Java {
interface ErrorCodeValue {
toString(): string;
value(): number;
}
module ErrorCodeAuthority {
var ENTITY_NOT_FOUND: IErrorCodeAuthority;
var HTTP_VERB_NOT_SUPPORTED: IErrorCodeAuthority;
var BAD_REQUEST: IErrorCodeAuthority;
}
interface IErrorCodeAuthority {
httpStatus: ErrorCodeValue;
}
export function type(arg: "com.domain.ErrorCodeAuthority"): typeof ErrorCodeAuthority;
export function type(arg: string): any;
}
export class HttpResult {
constructor(code: number, description: string) {
}
}
export class HttpFailResult extends HttpResult {
constructor(public errorCode: Java.IErrorCodeAuthority, public userParams? :any[]) {
super(errorCode.httpStatus.value(), errorCode.toString());
}
}
var JavaErrorCodeAuthority = Java.type("com.domain.ErrorCodeAuthority");
new HttpFailResult(JavaErrorCodeAuthority.BAD_REQUEST, null);
i'm trying to create a grammar that join togheter a script language with the possibility to create method.
Grammar
grammar org.example.domainmodel.Domainmodel with org.eclipse.xtext.xbase.Xbase
generate domainmodel "http://www.example.org/domainmodel/Domainmodel"
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
Model:
imports = XImportSection
methods += XMethodDeclaration*
body = XBlockScriptLanguage;
XMethodDeclaration:
"def" type=JvmTypeReference name=ValidID
'('(params+=FullJvmFormalParameter (',' params+=FullJvmFormalParameter)*)? ')'
body=XBlockExpression
;
XBlockScriptLanguage returns xbase::XExpression:
{xbase::XBlockExpression}
(expressions+=XExpressionOrVarDeclaration ';'?)*
;
At the moment i create the following JvmModelInferr, for defining the main method for scripting language.
JvmModelInferr
def dispatch void infer(Model model, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(
model.toClass("myclass")
).initializeLater [
members += model.toMethod("main", model.newTypeRef(Void::TYPE)) [
parameters += model.toParameter("args", model.newTypeRef(typeof(String)).addArrayTypeDimension)
setStatic(true)
body = model.body
]
]
}
When i tryed to use my grammar, i obtain the following error after that i wrote my method:
no viable alternative at input 'def'
The method mymethod() is undefined
The problem is related only with method declaration, without it myclass.java is created.
Moreover i obtain the "Warning 200" for a not clear grammar, why?
There are two fixes that appear necessary:
The imports section is not marked as optional. If it was intended to be optional, it should be declared as imports ?= XImportSection. Or, add necessary import statements to your JvmModelInferr example.
The dispatch keyword isn't defined in your grammar. As defined, a method should consist of def, followed by a Java type (the return type), and then the method's name (then the body, etc.). You could add `(dispatch ?= 'dispatch') if you're targeting Xtend and intend to support its multiple dispatch feature (or your own version of it).
HTH
I have a Groovy file that looks like this (currently).
main.groovy
import org.packages.mystuff.JavaClassIAmUsing;
public class MyObject {
def rate(item){
def o = evaluate(new File (new File(getClass().protectionDomain.codeSource.location.path).parent),"CommonFunctions.groovy");
println o.whoami();
}
}
i have another groovy file called
CommonFunctions.groovy
def whoami() {return 'no body';}
I'm trying to include the CommonFunctions script in to main script, BUT the location of the script are not known at build time (i.e. i can not hardcode a absolute file path in the script or absoulte path of the java process in relation to where the scripts will be stored).
All i know is that the scripts will be together or at a location relative to the calling script (say sub directory).
I've attempted to try and location the calling script location, but i get the error
No signature of method: MyObject.evaluate()
How can i referance this script, considering the main script is accessed at runtime using a GroovyClassLoader.parseClass(File) method.
I'm not really sure why you want to do it this way, I think it would be much simpler to make a class of CommonsFunctions that you could instantiate normally and use everywhere.
However, it is possible to achieve what you want; with Groovy, there are not that many limitations...
There are two problems with your suggested solution:
getClass() inside your MyObject class naturally refers to ... the MyObject class, so your attempt to find the location of the script will fail. You're on the right track, but you need to resolve the script location using the surrounding Script class.
evaluate doesn't really work the way you think it does. The result of the evaluate method is the result of the script, not an instance of the Script class. One way to remedy this, is to rewrite the methods in CommonFunction as closure properties. These properties will be available in the shell Binding object when evaluating the script.
So, with these rewrites, you end up with something like this:
main.groovy
class MyObject {
def scriptDir
def rate(item) {
def commonFunctionsScriptFile = new File(scriptDir, "CommonFunctions.groovy")
def binding = new Binding()
new GroovyShell(binding).evaluate(commonFunctionsScriptFile)
println binding.variables.whoami()
}
}
scriptFile = new File(getClass().protectionDomain.codeSource.location.path)
new MyObject(scriptDir: scriptFile.parentFile).rate(null)
Here, the script file location is resolved in the script, not in the inner class.
CommonFunctions.groovy
whoami = { 'no body' }
Here, whoami is no longer a method, but a closure property which will be added to the binding. Make sure that you don't prefix this property with def, since then it will be a local variable instead of a property added to the binding object.
Output after these rewrites is the expected: no body.