No DataSourceFactory service implementation found in class path. Probably missing dependency to avaje-datasource?

197 views
Skip to first unread message

Net Wolf

unread,
Oct 25, 2016, 6:59:49 PM10/25/16
to Ebean ORM
This is probably a quirk of TeamCity, Spring and classpaths, but I thought I'd try here first in case there was a suggestion on how to work around this.
Is there a way to tell eBean about DataSource classes, in a similar way to how models are specified?
Or is there other suggestions on how to make the server aware of the provided jar?

Long story.

I am developing a plugin for the TeamCity (TC) build server. TC has an API which is based on Spring. The plugin will be installed on user's servers, and I don't know in advance where the H2 file be will stored, so I am building the DataSourceConfig from a combination of Spring injected plugin location, and ebean.properties using the following code...

                ServerConfig config = new ServerConfig();
                config.setName("db");
                config.loadFromProperties();

                DataSourceConfig dsConfig = config.getDataSourceConfig();
                dsConfig.setUrl(
                        "jdbc:h2:file:" + myDataDir.getAbsolutePath() + File.separator + "tcDebRepositoryDB;DB_CLOSE_ON_EXIT=FALSE");

                config.setDataSourceConfig(dsConfig);


The EbeanServer is instantiated by Spring's XML configuration (which is started by Teamcity's plugin manager somehow).

My junit test runs fine in junit's classpath, but loading the plugin inside TC gives the following stack trace.

Caused by: java.lang.IllegalStateException: No DataSourceFactory service implementation found in class path. Probably missing dependency to avaje-datasource?
    at com.avaje.ebeaninternal.server.core.DefaultContainer.getDataSourceFromConfig(DefaultContainer.java:288)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.setDataSource(DefaultContainer.java:252)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:97)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:42)
    at com.avaje.ebean.EbeanServerFactory.createInternal(EbeanServerFactory.java:108)
    at com.avaje.ebean.EbeanServerFactory.create(EbeanServerFactory.java:67)
    at debrepo.teamcity.ebean.server.EbeanServerProvider.createEbeanServerInstance(EbeanServerProvider.java:56)
    at debrepo.teamcity.ebean.server.EbeanServerProvider.<init>(EbeanServerProvider.java:19)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)


So I explicitly added avaje-datasource to the maven POM.

        <dependency>
            <groupId>org.avaje</groupId>
            <artifactId>avaje-datasource</artifactId>
            <version>1.1.4</version>
        </dependency>


But, that has not made any difference. Full stacktrace follows.

ERROR - gins.spring.SpringPluginLoader - Failed to initialize spring context for plugin tcdebrepo
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ebeanServerProvider' defined in Byte array resource [plugin: tcdebrepo#tcdebrepo-server-1.0-SNAPSHOT.jar!/META-INF/build-server-plugin-tcdebrepo.xml]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [debrepo.teamcity.ebean.server.EbeanServerProvider]: Constructor threw exception; nested exception is java.lang.IllegalStateException: No DataSourceFactory service implementation found in class path. Probably missing dependency to avaje-datasource?
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:275)
    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:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at jetbrains.buildServer.plugins.spring.SpringPluginLoader.pluginClassesLoaded(SpringPluginLoader.java:95)
    at sun.reflect.GeneratedMethodAccessor59.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at jetbrains.buildServer.util.EventDispatcher.dispatch(EventDispatcher.java:121)
    at jetbrains.buildServer.util.EventDispatcher$2.invoke(EventDispatcher.java:68)
    at com.sun.proxy.$Proxy16.pluginClassesLoaded(Unknown Source)
    at jetbrains.buildServer.plugins.PluginManagerImpl$2.visitPlugin(PluginManagerImpl.java:140)
    at jetbrains.buildServer.plugins.PluginsCollection.foreachLoadedPlugins(PluginsCollection.java:222)
    at jetbrains.buildServer.plugins.PluginManagerImpl.firePluginClassesLoaded(PluginManagerImpl.java:138)
    at jetbrains.buildServer.plugins.PluginManagerImpl.loadPlugins(PluginManagerImpl.java:84)
    at jetbrains.buildServer.web.plugins.PluginManagerConfigurator.initializePlugins(PluginManagerConfigurator.java:9)
    at jetbrains.buildServer.web.impl.BuildServerConfigurator.loadConfiguration(BuildServerConfigurator.java:68)
    at java.util.concurrent.CopyOnWriteArrayList.forEach(CopyOnWriteArrayList.java:890)
    at jetbrains.buildServer.serverSide.impl.BuildServerLifecycleProcessor.doStartup(BuildServerLifecycleProcessor.java:30)
    at jetbrains.buildServer.maintenance.TeamCityDispatcherServlet$WebApplicationCreatorAndDestroyer.createApplication(TeamCityDispatcherServlet.java:25)
    at jetbrains.buildServer.maintenance.StartupProcessor.doApplicationStarting(StartupProcessor.java:141)
    at jetbrains.buildServer.maintenance.StartupProcessor.processConcreteStage(StartupProcessor.java:556)
    at jetbrains.buildServer.maintenance.StartupProcessor.processConcreteStageSafe(StartupProcessor.java:744)
    at jetbrains.buildServer.maintenance.StartupProcessor.processTeamCityLifecycle(StartupProcessor.java:811)
    at jetbrains.buildServer.maintenance.StartupProcessor.access$000(StartupProcessor.java:15)
    at jetbrains.buildServer.maintenance.StartupProcessor$1.run(StartupProcessor.java:2)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [debrepo.teamcity.ebean.server.EbeanServerProvider]: Constructor threw exception; nested exception is java.lang.IllegalStateException: No DataSourceFactory service implementation found in class path. Probably missing dependency to avaje-datasource?
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122)
    at jetbrains.buildServer.spring.InstantiationStrategySelector$1.instantiate(InstantiationStrategySelector.java:77)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:267)
    ... 34 more
Caused by: java.lang.IllegalStateException: No DataSourceFactory service implementation found in class path. Probably missing dependency to avaje-datasource?
    at com.avaje.ebeaninternal.server.core.DefaultContainer.getDataSourceFromConfig(DefaultContainer.java:288)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.setDataSource(DefaultContainer.java:252)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:97)
    at com.avaje.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:42)
    at com.avaje.ebean.EbeanServerFactory.createInternal(EbeanServerFactory.java:108)
    at com.avaje.ebean.EbeanServerFactory.create(EbeanServerFactory.java:67)
    at debrepo.teamcity.ebean.server.EbeanServerProvider.createEbeanServerInstance(EbeanServerProvider.java:56)
    at debrepo.teamcity.ebean.server.EbeanServerProvider.<init>(EbeanServerProvider.java:19)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
    ... 37 more


Rob Bygrave

unread,
Oct 25, 2016, 7:29:07 PM10/25/16
to ebean@googlegroups
So by default "service loading" uses the Thread.currentThread().getContextClassLoader().

So the theory with this exception is that the "context class loader" provided by TC to its plugins ... is not including the avaje-datasource dependency.


Options:

- Explicitly set the classLoader for Ebean to use via serverConfig.setClassLoader(...) // but you probably can't do that as a plugin ??

- Explicitly set a DataSource via serverConfig.setDataSource(...) // that is, create the DataSource and set it when ServerConfig is being created bypassing the need to find the DataSourceFactory ... i.e. use DataSourceFactory programmatically to create a DataSource and serverConfig.setDataSource(...) .

- Look into TC PluginManagerImpl to review the ClassLoader setup (look for a call to
Thread.currentThread().setContextClassLoader() ... and see if  avaje-datasource is included in the plugin classpath ).  Maybe this is some explicit way to add avaje-datasource to the plugin classpath / classloader.


Cheers, Rob.


--

---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Net Wolf

unread,
Oct 25, 2016, 8:52:59 PM10/25/16
to Ebean ORM
Thanks for your quick reply Rob.

I thought option two was the simplest, so I manually instantiated a ConnectionPool

                DataSourcePool pool = new ConnectionPool("db", dsConfig);
               
                config.setDataSource(pool);
                config.setDataSourceConfig(dsConfig);

Which then failed with:

Caused by: java.lang.RuntimeException: DataSource user is null?
    at org.avaje.datasource.pool.ConnectionPool.<init>(ConnectionPool.java:204)
    at debrepo.teamcity.ebean.server.EbeanServerProvider.createEbeanServerInstance(EbeanServerProvider.java:66)


So a bit more digging revealed that it's not finding the ebean.properties file at all which points to a massive classpath blind spot. I'll have to try to resolve using one of your other suggestions.
Option three will be tricky (closed source).

I'll let you know how I get on.

Net Wolf

unread,
Nov 10, 2016, 4:29:36 PM11/10/16
to eb...@googlegroups.com
I finally got around to raising a help request with TeamCity, and they have a nice tidy solution which allows me to inject their special classloader, and then call `doUnderContextClassloader`.
I need to tidy the code up and do better exception handling, but it's working now and I can read and save entities in the DB.

        /**
         * EbeanServerProvider
         * @param serverPaths The directory to store the H2 files in.
         * @param teamCityClassLoader The classloader to use to create the instance.
         */


       
public EbeanServerProvider(ServerPaths serverPaths, TeamCityClassLoader teamCityClassLoader) {
               
Loggers.SERVER.info("EbeanServerProvider :: Getting EBeanServer via TeamCity classpath method.");
               
try {
                       
this.myEbeanServer = Util.doUnderContextClassLoader(teamCityClassLoader,
                                       
new EbeanServerProviderInstantiationFunction(serverPaths.getPluginDataDirectory())
                                       
);
               
} catch (Exception e) {
                       
Loggers.SERVER.error("EbeanServerProvider :: Could not create eBean instance!");
                       
Loggers.SERVER.debug(e);
               
}
       
}
       
       
public class EbeanServerProviderInstantiationFunction implements FuncThrow<EbeanServer, Exception> {
               
final File path;
               
public EbeanServerProviderInstantiationFunction(File dataDir) {
                       
this.path = dataDir;
               
}

               
@Override
               
public EbeanServer apply() throws Exception {
                       
return createEbeanServerInstance(this.path);
               
}
       
}



Rob Bygrave

unread,
Nov 11, 2016, 4:17:55 PM11/11/16
to ebean@googlegroups
Well done and thanks for the feedback.  I'm sure other TC users will appreciate that.

Cheers, Rob.

Reply all
Reply to author
Forward
0 new messages