grundic / teamcity-browser-notify

Teamcity browser notifications plugin.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Please update to support TeamCity 2017.2 EAP

agross opened this issue · comments

When installing the plugin on a TeamCity 2017.2 EAP instance it fails to load:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainAutowireCandidateResolver' defined in URL [jar:file:/teamcity/webapps/ROOT/WEB-INF/lib/common-impl.jar!/META-INF/teamcity-global-spring-candidate-resolver.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [jetbrains.buildServer.spring.candidates.TeamCityAutowireCandidateResolver[]]: Error loading class [com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler] for bean with name 'com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler#0' defined in Byte array resource [plugin: teamcity-browser-notify#teamcity-browser-notify-1.0.1.jar!/META-INF/build-server-plugin-teamcity-browser-notify.xml]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/atmosphere/handler/AbstractReflectorAtmosphereHandler; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler] for bean with name 'com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler#0' defined in Byte array resource [plugin: teamcity-browser-notify#teamcity-browser-notify-1.0.1.jar!/META-INF/build-server-plugin-teamcity-browser-notify.xml]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/atmosphere/handler/AbstractReflectorAtmosphereHandler
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:179)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:678)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:520)
at jetbrains.buildServer.plugins.spring.SpringPluginLoader.pluginClassesLoaded(SpringPluginLoader.java:102)
at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at jetbrains.buildServer.util.EventDispatcher$3.run(EventDispatcher.java:126)
at jetbrains.buildServer.util.NamedThreadFactory.executeWithNewThreadName(NamedThreadFactory.java:71)
at jetbrains.buildServer.util.EventDispatcher.dispatch(EventDispatcher.java:120)
at jetbrains.buildServer.util.EventDispatcher$2.invoke(EventDispatcher.java:70)
at com.sun.proxy.$Proxy22.pluginClassesLoaded(Unknown Source)
at jetbrains.buildServer.plugins.PluginManagerImpl$2.visitPlugin(PluginManagerImpl.java:140)
at jetbrains.buildServer.plugins.PluginsCollection$6.run(PluginsCollection.java:257)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler] for bean with name 'com.github.grundic.browser.notificator.websocket.BrowserNotificationHandler#0' defined in Byte array resource [plugin: teamcity-browser-notify#teamcity-browser-notify-1.0.1.jar!/META-INF/build-server-plugin-teamcity-browser-notify.xml]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/atmosphere/handler/AbstractReflectorAtmosphereHandler
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:597)
at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1445)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:445)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:415)
at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:220)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1177)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1041)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
... 28 more
Caused by: java.lang.NoClassDefFoundError: org/atmosphere/handler/AbstractReflectorAtmosphereHandler
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at jetbrains.buildServer.plugins.classLoaders.TeamCityClassLoader.doLoadClass(TeamCityClassLoader.java:73)
at jetbrains.buildServer.plugins.classLoaders.TeamCityClassLoader.loadClass(TeamCityClassLoader.java:40)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:250)
at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:394)
at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1397)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1344)
... 39 more
Caused by: java.lang.ClassNotFoundException: Class 'org.atmosphere.handler.AbstractReflectorAtmosphereHandler' was not found
at jetbrains.buildServer.plugins.classLoaders.TeamCityClassLoader.doLoadClass(TeamCityClassLoader.java:85)
at jetbrains.buildServer.plugins.classLoaders.TeamCityClassLoader.loadClass(TeamCityClassLoader.java:40)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 55 more

+1 Yes please. I just upgraded to 2017.2 released version and found this error.

Yes, I'm also on 2017.2. Plugin page indicates that it failed to initialize a spring context.

Sorry everyone! Not working with something makes its very difficult to find a time to fix that -_-
Will fix it soon.

Just putting another vote on this, we finally upgraded to 2017.2.2 last week and this plugin doesn't work :(

Any chance you can work on it :)

Okay, spent two days configuring environment: unfortunately development environment for TC no very friendly, so I usually spent a lot of time for configuring everything.

So, the thing is that my plugin is using Atmospere library, which before was bundled with Teamcity and it was their framework for doing websockets. Since 2017.2, they dropped support of this library.

So now the plugin should be rewritten using JSR-356 Java implementation of websockets. All examples which I could find are annotation based (example). But Teamcity doesn't support this (here is open bug for ~3 years.). I wish there would be a documentation/example section in official Teamcity docs about using websocket in Teamcity plugins, but there is nothing.

So: I've tried, spent some time, but failed. I'm not Spring guru and I don't use Teamcity anymore (though I still like it) and I can't find any examples, which could help.
I will spent too much time fixing this issue, which I prefer to spent differently.
Sorry.

Probably @JeanRev could help to fix this, as he made something similar for his other plugin.

Or maybe ask support from official Teamcity guys: @pavelsher, @yaegor, @dtretyakov.

Will keep this open.

Thanks @grundic for your thorough response. Have you tried converting to JSR-356 in a similar way to JeanRev's code?
If not, that might be a good place for me to start looking at it. If I make some progress, I'll report back here.

Hi there :-)

Teamcity plugins can't indeed use anotation-based declaration of endpoints, because the jars files are added to the classpath after the initial scanning made by the JSR-356 implementation.

You can work around this by declaring the endpoint at runtime:

  1. Fetch the websocket ServerContainer instance from the ServletContext.
  2. Use a custom Configurator capable of instanciating, or providing an old instance according to the scope you need (leveraging the Spring loading system or not), from your endpoint class.
  3. Deploy the endpoint using the addEndpoint method of the WS server container.

See: https://github.com/JeanRev/TeamcityDockerCloudPlugin/blob/4f54dda72ba7cefa276c02c707b3ebfb6ff21a71/server/src/main/java/run/var/teamcity/cloud/docker/web/SpringWSEndpointOrchestrator.java

The addEndpoint method takes two arguments: the implementation class and the servlet path you want to bind it to. The path you use should start with "/app/you-plugin-id". As always, don't forget to secure the endpoint adequately, using the TeamCity authorization API.

On the server side, you will need to rewrite the whole message handling code using the JSR-356 API. The Atmosphere Javascript bindings are also not available anymore. If you were using them, you will need to rewrite your code to use standard web-sockets instead.

You'll probably need a bit of trial and error until you get everything right. Don't hesitate to tell me if you're stuck on something, maybe I can help.

On the bright side, JSR-356 is now available on a all modern application server, and is not likely to disappear. So, you should be good to go for a long time.

Hey guys!
Thank you very much for support and inspiration :)
Yeah, I saw Jean's code, but can't sort it out at first.
I'm interested how the development team is doing that? Probably the have something similar?

Now, after such a details explanation it is much cleaner. I'll try to make another attempt.
Thank you 👍

Hi,

Actually we (TeamCity team) don't recommend to rely on the libraries bundled with TeamCity, instead, you should use standalone classloader and include the necessary libs in the plugin (see details here

I suppose that the easiest way to fix the plugin is to add the Atmosphere library into the plugin.

Thanks @dmitry-treskunov.
I did manage to get the plugin to compile and load in teamcity a few weeks ago by including atmosphere in the gradle file.

However, I still must be doing something else wrong because the events didn't appear to be firing back to the browser.

@netwolfuk probably you also need to include atmoshpere.js on the client-side.

@netwolfuk, yep I did the same and added atmoshpere.js in my original attempt, but it didn't worked: trying to establish websocket connection failed with 501 Not Implemented error.

I'll try the approach, suggested by @JeanRev.

Taking the opportunity, I would like to ask Teamcity team (@dmitry-treskunov): do you have some recommendations about incorporating websockets support to the Teamcity plugins? How do you do it internally and can third-party developers reuse it? Would be very good it there would be a tutorial somewhere here, which would guide people how to add support of websockets to their plugins.

Thank you.

Hi *,

It seems I defeated this issue, at least on my local machine everything works in Docker container (checked on 2017.2.3 version).
You can download new version of plugin here.

I appreciate your patience and would like to thank everyone, who helped me with this issue :)

Hi,

just installed and confirmed it works on 2018.1 EAP (build 57605). Many thanks, @grundic!

Thanks @grundic. It is now fixed for me too on 2017.2.3 (build 51047).
I notice that your plugin is called "TeamCity Browser Notify". Have you tried updating it on plugins.jetbrains.com yet?
When I tried one of mine it complained that the name contained "teamcity or plugin".
I presume it is reading that value from the XML file inside the plugin zip. Did you have that issue?

@netwolfuk , yep got the same issue... :(