Page 1 of 1

Resolved: java.lang.NoSuchMethodError on Staging Server

PostPosted: Wed Jan 11, 2023 8:10 pm
by swingman
I have implemented Single-Sign-On, SSO with Azure AD in Servoy. This works perfectly in Developer, but when we deploy to staging the callback from Microsoft causes a 500 error.

Servoy 2021.12, Tomcat 9.0.58.

Code: Select all
2023-01-11 17:56   Executor,uuid:48D7B71:4, clientid: BB49CBF2-D564-4F54-BD18-74DA2CE756E2   ERROR   org.sablo.eventthread.EventDispatcher   Exception in dispatch()       
java.lang.NoSuchMethodError: 'com.fasterxml.jackson.core.util.JacksonFeatureSet com.fasterxml.jackson.core.JsonParser.getReadCapabilities()'
at com.fasterxml.jackson.databind.DeserializationContext.<init>(DeserializationContext.java:211) ~[?:?]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.<init>(DefaultDeserializationContext.java:50) ~[?:?]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl.<init>(DefaultDeserializationContext.java:391) ~[?:?]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext$Impl.createInstance(DefaultDeserializationContext.java:413) ~[?:?]
at com.fasterxml.jackson.databind.ObjectMapper.createDeserializationContext(ObjectMapper.java:4656) ~[?:?]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4631) ~[?:?]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3042) ~[?:?]
at com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor.createToken(OAuth2AccessTokenJsonExtractor.java:83) ~[?:?]
at com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor.extract(OAuth2AccessTokenJsonExtractor.java:39) ~[?:?]
at com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor.extract(OAuth2AccessTokenJsonExtractor.java:17) ~[?:?]
at com.github.scribejava.core.oauth.OAuth20Service.sendAccessTokenRequestSync(OAuth20Service.java:157) ~[?:?]
at com.github.scribejava.core.oauth.OAuth20Service.getAccessToken(OAuth20Service.java:237) ~[?:?]
at com.github.scribejava.core.oauth.OAuth20Service.getAccessToken(OAuth20Service.java:232) ~[?:?]
at com.servoy.extensions.plugins.oauth.OAuthService.setAccessToken(OAuthService.java:112) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:166) ~[js.jar:?]
at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:292) ~[js.jar:?]
at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1487) ~[js.jar:?]
at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:815) ~[js.jar:?]
at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:109) ~[js.jar:?]
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:399) ~[js.jar:?]
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3534) ~[js.jar:?]
at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:107) ~[js.jar:?]
at com.servoy.j2db.scripting.ScriptEngine.executeFunction(ScriptEngine.java:727) ~[j2db.jar:2021.12.2.3724]
at com.servoy.j2db.scripting.ScopesScope.executeGlobalFunction(ScopesScope.java:193) ~[j2db.jar:2021.12.2.3724]
at com.servoy.j2db.server.ngclient.NGFormManager.makeSolutionSettings(NGFormManager.java:263) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGFormManager$1.run(NGFormManager.java:412) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGFormManager.propertyChange(NGFormManager.java:417) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:341) ~[?:?]
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:333) ~[?:?]
at javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:92) ~[?:?]
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:266) ~[?:?]
at com.servoy.j2db.J2DBGlobals.firePropertyChange(J2DBGlobals.java:107) ~[j2db.jar:2021.12.2.3724]
at com.servoy.j2db.server.ngclient.NGClient.lambda$1(NGClient.java:716) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGClient.runWhileShowingLoadingIndicator(NGClient.java:705) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGClient.loadSolution(NGClient.java:713) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGClient.loadSolution(NGClient.java:568) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGClientWebsocketSession$1.run(NGClientWebsocketSession.java:293) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at org.sablo.eventthread.Event$1.run(Event.java:97) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.websocket.CurrentWindow.runForWindow(CurrentWindow.java:80) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.Event.execute(Event.java:87) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.EventDispatcher.addEvent(EventDispatcher.java:172) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.EventDispatcher.addEvent(EventDispatcher.java:162) ~[sablo_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.NGClientWebsocketSession.onOpen(NGClientWebsocketSession.java:277) ~[servoy_ngclient_2021.12.2.3724.jar:?]
at org.sablo.websocket.WebsocketEndpoint$1.run(WebsocketEndpoint.java:180) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.Event$1.run(Event.java:97) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.websocket.CurrentWindow.runForWindow(CurrentWindow.java:80) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.Event.execute(Event.java:87) ~[sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.EventDispatcher.dispatch(EventDispatcher.java:130) [sablo_2021.12.2.3724.jar:?]
at org.sablo.eventthread.EventDispatcher.run(EventDispatcher.java:89) [sablo_2021.12.2.3724.jar:?]
at com.servoy.j2db.server.ngclient.eventthread.NGEventDispatcher.run(NGEventDispatcher.java:57) [servoy_ngclient_2021.12.2.3724.jar:?]
at java.lang.Thread.run(Thread.java:829) [?:?]


Any idea of what we need to look for?

Re: java.lang.NoSuchMethodError on Staging Server

PostPosted: Wed Jan 11, 2023 8:29 pm
by emera
Hello Christian,

It looks like you have multiple versions of the jackson-core jar. It could be due to an older version of jasper or another plugin.
In case you use jasper, you need to get the version which is compatible with 2021.12 from https://github.com/Servoy/servoy_jasper ... /v6.17.0_1

Starting with Servoy 2022.03 we detect such situations automatically on war export, keeping the latest jars only.

Regards,
Edit

Re: java.lang.NoSuchMethodError on Staging Server

PostPosted: Wed Jan 11, 2023 10:14 pm
by swingman
Hi, thanks, I will look into this. I'm using the native Excel import and Export so I have a light version of Jasper installed.

Re: java.lang.NoSuchMethodError on Staging Server

PostPosted: Wed Jan 11, 2023 10:35 pm
by swingman
Upgrading Jasper worked, many thanks.
I owe you a drink at the next Servoy World!
Remind me.

Christian

Re: Resolved: java.lang.NoSuchMethodError on Staging Server

PostPosted: Thu Jan 12, 2023 2:00 pm
by steve1376656734
Hi Christian,

We are about to look at implementing this in our solution. Would you be willing/able to share any information on how you achieved this please?

Many thanks
Steve

Re: Resolved: java.lang.NoSuchMethodError on Staging Server

PostPosted: Thu Jan 12, 2023 5:49 pm
by swingman
Hi Steve,

the implementation is quite simple, give the oauth plugin has a preset for Azure AD.

First you need to set up an App registration on https://portal.azure.com, you can follow the 0) of this one, ignore 1) onwards.

https://github.com/heartcombo/devise/wi ... th:AzureAD

My client has also approved various scopes in the graph API, including User.read.

I have put the code below in the global scope of my login solution. I have put a button on my login screen
with action scopes.globals.authorize();

I also have a function called outlook_login in my user_authenticator solution allow outlook_users access to Servoy.
Code: Select all
/**
* @type {String}
*
* @properties={typeid:35,uuid:"3DB7DE7C-9FE3-4D87-A1B7-E869A860D79B"}
*/
var clientId = "YOUR CLIENT ID GOES HERE";
/**
* @type {String}
*
* @properties={typeid:35,uuid:"3E5B9E55-476C-47BC-9A25-D635031D4F57"}
*/
var clientSecret = "YOUR CLIENT SECRET GOES HERE";
/**
* @type {String}
*
* @properties={typeid:35,uuid:"E40BCF30-A50B-4DDB-A21C-592ACFFBF59E"}
*/
var state =  "SOME SECRET STRING, SO YOU CAN CHECK RESPONSE IS GENUINE";
/**
* @type {String}
*
* @properties={typeid:35,uuid:"EC7DFBE6-F1C9-491C-BA3F-EDFC824A04A6"}
*/
var callback = "microsoft_ad_callback";

///**
// * @type {plugins.oauth.OAuthService}
// *
// * @properties={typeid:35,uuid:"AF7A6579-70C3-4D4B-B705-E83F7325C373",variableType:-4}
// */
//var service;


/**
* @properties={typeid:24,uuid:"A3531553-F27F-4BEC-BE12-F3A5ECD282AA"}
* @AllowToRunInFind
*/
function authorize() {
   var service = plugins.oauth.getOAuthService(plugins.oauth.OAuthProviders.MICROSOFT_AD, clientId, clientSecret, 'User.read', state, callback);
   application.showURL(service.getAuthorizationURL(),"_self");
}

/**
* @param a
* @param args
*
* @properties={typeid:24,uuid:"9E2714EF-B5C6-4C26-A62A-14C3D082842B"}
*/
function microsoft_ad_callback(a,args) {
   var service = plugins.oauth.getOAuthService(plugins.oauth.OAuthProviders.MICROSOFT_AD, clientId, clientSecret, 'User.read', state, callback);
   service.setAccessToken(args.code);
   /** @type {plugins.oauth.OAuthResponseJSON} */
   var response = service.executeGetRequest("https://graph.microsoft.com/v1.0/me");
   if (response.getCode() == 200) {
      var json = response.getAsJSON();
      
      // My servoy users have usernames, so I need to figure out which user is logging
                // in by looking up the email address using a function.
      var user = find_user_by_email(json['mail']);
      if(user) {
         security.authenticate('user_authenticator', 'outlook_login', [user.username, '']);
         forms.login.error_message = "";
         return true;
      } else {
         forms.login.error_message = "User's email address not known.";
         return false;
      }
   } else {
      forms.login.error_message = 'Autentication error code ' + response.getCode();
      return false;
   }
}


Servoy's example uses a scope variable called service to keep track of the service created, but I could not get that to work. In microsoft_ad_callback, the service variable was always null, so I simply create a new one.

Hope this helps,

Re: Resolved: java.lang.NoSuchMethodError on Staging Server

PostPosted: Thu Jan 12, 2023 6:37 pm
by steve1376656734
Hi Christian,

Many thanks for the comprehensive reply. A couple of questions if you don't mind:

1. It looks from the above code like you are using a login solution and authentication solution. We have moved on to using the svySecurity module which just has a login form. I'm guessing that we just need to replace the
Code: Select all
security.authenticate('user_authenticator', 'outlook_login', [user.username, '']);

with:
Code: Select all
security.login(......)


2. How do you add the users initially? Is it done manually in the application or do you use the Azure user provisioning to automatically create users?

Thanks
Steve

Re: Resolved: java.lang.NoSuchMethodError on Staging Server

PostPosted: Fri Jan 13, 2023 9:34 am
by swingman
HI Steve,

1) I have not used svysecurity, so I don't know.

2) We read the users from Azure with Microsoft Graph, so as soon as IT support creates a new user, that user will appear on Servoy. This is done in two ways, we subscribe to user changes with a web hook so we get them more or less real time and run a full import now and then in case some of the web hook callbacks have been missed because of downtime.