I'm working on a Groovy script in the SAP CPI environment which means the script runs in a somewhat canned JVM.
I uploaded the jjwt-api-0.11.2
, jjwt-impl-0.11.2
and jjwt-jackson-0.11.2
jars, in order to build a JWT.
The first run just after deployment the script runs just fine, but every run afterwards I get the following error:
com.sap.it.rt.adapter.http.api.exception.HttpResponseException: An internal server error occured: java.lang.ClassCastException: io.jsonwebtoken.impl.DefaultJwtBuilder (loaded by com.sap.gateway.ip.core.customdev.classloader.MergeClassLoaders@1625616) cannot be cast to io.jsonwebtoken.JwtBuilder (loaded by com.sap.gateway.ip.core.customdev.classloader.MergeClassLoaders@1624982) (found matching interface io.jsonwebtoken.JwtBuilder loaded by com.sap.gateway.ip.core.customdev.classloader.MergeClassLoaders@1625616, but needed loader com.sap.gateway.ip.core.customdev.classloader.MergeClassLoaders@1624982)@ line 70 in script1.groovy.
Line 70 reads:
JwtBuilder jwtBuilder = Jwts.builder().setClaims(claims).setIssuedAt(nowDate).setExpiration(expDate).setHeader((Map<String, Object>) header);
The error persists even if I change it to:
DefaultJwtBuilder jwtBuilder = Jwts.builder().setClaims(claims).setIssuedAt(nowDate).setExpiration(expDate).setHeader((Map<String, Object>) header);
When I re-deploy, it works again the first time and then goes back to the error every subsequent call.
As a debugging aid I dumped System.getenv()
but the data it returns is the same in both succesful and erroneous runs:
{HC_GLOBAL_HOST=br1.hana.ondemand.com, HC_COMPONENT=web, HC_REGION=BR_1, HC_APPLICATION=someid, HC_HOST_INT=int.br1.hana.ondemand.com, replicate_ebs_volumes=false, replicate_ebs_with_delete=false, HC_PROCESS_ID=f6576be76bd88828e305602dbbca9d13890c, ENABLE_CAM=false, HC_LANDSCAPE=production, DISABLE_ANTIVIRUS=true, HC_APPLICATION_URL=https://someid-n9e08ec00.int.br1.hana.ondemand.com, HC_LOCAL_HTTP_PORT=9001, HC_HOST_SCP=sapcp.br1.hana.ondemand.com, HC_HOST_SVC=svc.br1.hana.ondemand.com, HC_HOST_CERT=cert.br1.hana.ondemand.com, HC_LANDSCAPE_TYPE=single_AZ, HC_ACCOUNT=someotherid, HC_OP_HTTP_PROXY_HOST=localhost, LDAP_SEARCH_BASE=dc=spa3,dc=od,dc=sap,dc=biz, HC_HOST=br1.hana.ondemand.com, HC_OP_HTTP_PROXY_PORT=20003, HOME=/usr/sap/ljs/home, HC_AVAILABILITY_ZONE=NLB1FSA02}
The rest of the relevant code is:
import com.sap.gateway.ip.core.customdev.util.Message;
import com.sap.it.api.ITApiFactory;
import com.sap.it.api.keystore.KeystoreService;
import java.security.cert.Certificate;
import java.security.Key;
import java.util.HashMap;
import java.io.File;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
import java.util.Scanner;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultHeader;
def Message processData(Message message) {
def messageLog = messageLogFactory.getMessageLog(message);
messageLog.addAttachmentAsString("dump_env", System.getenv().toString(), "text/plain");
Map<String, Object> header = new HashMap<String, Object>();
header.put("typ", "JWT");
header.put("alg", "RS256");
// Claims claims = Jwts.claims();
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("scope", "https://www.googleapis.com/auth/devstorage.full_control");
claims.put("aud", tokenUrl);
claims.put("iss", "mysecret@thing.iam.gserviceaccount.com");
/* Create JWT json with JwtBuilder */
DefaultJwtBuilder jwtBuilder = Jwts.builder().setClaims(claims).setIssuedAt(nowDate).setExpiration(expDate)
.setHeader((Map<String, Object>) header);
// ^^^^^^^^----> Crashing line
// ...jwt signing and whatnot
/* Set body and URL encode the grant_type string. */
body = "grant_type=" + URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer") + "&assertion=" + assertion
message.setHeader("Content-Type", "application/x-www-form-urlencoded")
message.setBody(body);
return message;
Formerly I was using the Headers and Claims classes but those were exhibiting this same issue (they worked fine the first time but afterwards threw a classcasting error which didn't go away by changing the variable class)
What are other tools, system variables or resources I could use to debug this strange behaviour?
I suspect it's a classloading error, but I'm learning java as I work on this and I still haven't got to grips with the JVM and the Java world idiosyncrasy.