如何将嵌入式PostgreSQL Server Java组件用作单独的服务?

我正在尝试为在Tomcat(7.x)中运行的RESTful(Services)基于Java的应用程序创建一个全面的集成测试套件,并依赖于Postgresql(9.x)实例。 此外,我希望能够通过使用maven failsafe插件,将该套件作为一个独立的进程运行,如果可能的话,仅使用maven 3.x。 这样,测试可以在3个主要平台(Mac OSX,Linux和Windows)上运行。

根据我所学到的,我认为实现这一目标的关键是做这些步骤(按此顺序):

  1. 在maven生命周期的预集成测试阶段以某种方式启动postgresql DB(和架构设置)的“嵌入式”版本,让postgres-db-process在后台运行
  2. 启动我的Java容器加载我的服务应用程序:我正在使用Jetty 9.1.5插件
  3. 在集成测试阶段从Failsafe插件运行我的(基于JUnit)测试
  4. 在maven的集成后测试阶段关闭我的Jetty容器
  5. 最后,有一些机制在生命周期的后集成测试阶段关闭先前启动的(后台)postgres-db-process(杀死/清理此过程)

在我当前的实现中成功完成了步骤1 – 3。 在第2步中,它使用了exec-maven-plugin,它调用了一个使用postgresql嵌入式Java组件的Java类。 摘自POM.xml:

 org.codehaus.mojo exec-maven-plugin 1.2.1   Run Postgres DB start/schema setup pre-integration-test  java     com.some.package.test.utils.DbSetup  setup    

这里是使用postgresql-embedded启动postgresql实例的DBSetup类的摘录:

 try { DownloadConfigBuilder downloadConfigBuilder = new DownloadConfigBuilder(); downloadConfigBuilder.defaultsForCommand(Command.Postgres); downloadConfigBuilder.proxyFactory(new HttpProxyFactory(PROXY_ADDRESS, DEFAULT_PROXY_PORT)); IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder() .defaults(Command.Postgres) .artifactStore(new ArtifactStoreBuilder() .defaults(Command.Postgres) .download(downloadConfigBuilder)).build(); PostgresStarter runtime = PostgresStarter.getInstance(runtimeConfig); final PostgresConfig config = new PostgresConfig(Version.V9_2_4, new AbstractPostgresConfig.Net( "localhost", 5432 ), new AbstractPostgresConfig.Storage(dbName), new AbstractPostgresConfig.Timeout(), new AbstractPostgresConfig.Credentials(username, password)); config.getAdditionalInitDbParams().addAll(Arrays.asList( "-E", "UTF-8", "--locale=en_US.UTF-8", "--lc-collate=en_US.UTF-8", "--lc-ctype=en_US.UTF-8" )); exec = runtime.prepare(config); process = exec.start(); System.out.println("embedded Postgres started"); Thread.sleep(1200L); } catch (IOException e) { System.out.println("Something Went Wrong initializing embedded Postgres: " + e); e.printStackTrace(); } catch (InterruptedException e) { System.out.println("Something Went Wrong Pausing this thread " + e); e.printStackTrace(); } 

请注意,我没有调用process.stop(); 在启动Postgres实例的方法中。 所以,我目前无法关闭数据库进程。 一旦我退出这个类DbSetup ,对该现有进程的所有引用都将丢失。 Postgres流程永远不会关闭。 实际上,似乎jetty容器也不会关闭,整个maven作业都挂起了,所以我必须手动杀死它(摘自我的maven控制台输出):

 [INFO] --- exec-maven-plugin:1.2.1:java (Run Postgres DB start/schema setup) @ BLAHBLAH --- ***START of Postgres DB Setup Process *** Extract /Users/myUserName/.embedpostgresql/postgresql-9.2.4-1-osx-binaries.zip START ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................Extract /Users/myUserName/.embedpostgresql/postgresql-9.2.4-1-osx-binaries.zip DONE INFO:20161021 12:58:00: de.flapdoodle.embed.process.runtime.Executable de.flapdoodle.embed.process.runtime.Executable start AbstractPostgresConfig{storage=Storage{dbDir=/var/folders/8g/69wh31fn7nx3q81phwfdpld00000gn/T/postgresql-embed-66cfc41f-0e16-439f-a24b-6e5b6dbc683d/db-content-3bc4b9cc-dd21-43a7-9058-285767f5c53d, dbName='BLAH', isTmpDir=true}, network=Net{host='localhost', port=5432}, timeout=Timeout{startupTimeout=15000}, credentials=Credentials{BLAH, BLAH}, args=[], additionalInitDbParams=[-E, UTF-8, --locale=en_US.UTF-8, --lc-collate=en_US.UTF-8, --lc-ctype=en_US.UTF-8]} INFO:20161021 12:58:01: de.flapdoodle.embed.process.runtime.Executable de.flapdoodle.embed.process.runtime.Executable start AbstractPostgresConfig{storage=Storage{dbDir=/var/folders/8g/69wh31fn7nx3q81phwfdpld00000gn/T/postgresql-embed-66cfc41f-0e16-439f-a24b-6e5b6dbc683d/db-content-3bc4b9cc-dd21-43a7-9058-285767f5c53d, dbName='BLAH', isTmpDir=true}, network=Net{host='localhost', port=5432}, timeout=Timeout{startupTimeout=15000}, credentials=Credentials{BLAH, BLAH}, args=[BLAH], additionalInitDbParams=[]} INFO:20161021 12:58:04: de.flapdoodle.embed.process.runtime.Executable de.flapdoodle.embed.process.runtime.Executable start AbstractPostgresConfig{storage=Storage{dbDir=/var/folders/8g/69wh31fn7nx3q81phwfdpld00000gn/T/postgresql-embed-66cfc41f-0e16-439f-a24b-6e5b6dbc683d/db-content-3bc4b9cc-dd21-43a7-9058-285767f5c53d, dbName='BLAH', isTmpDir=true}, network=Net{host='localhost', port=5432}, timeout=Timeout{startupTimeout=15000}, credentials=Credentials{BLAH, BLAH}, args=[], additionalInitDbParams=[-E, UTF-8, --locale=en_US.UTF-8, --lc-collate=en_US.UTF-8, --lc-ctype=en_US.UTF-8]} embedded Postgres started ***END of Postgres DB Setup Process *** 

 [INFO] --- jetty-maven-plugin:9.1.2.v20140210:run-war (start-jetty) @ BLAH --- [INFO] Configuring Jetty for project: BlahProject [INFO] Context path = / [INFO] Tmp directory = some/path/to/my/webapp/target/tmp [INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml [INFO] Web overrides = none [INFO] jetty-9.1.2.v20140210 [INFO] Scanned 1 container path jars, 133 WEB-INF/lib jars, 1 WEB-INF/classes dirs in 1887ms for context oejmpJettyWebAppContext@444942b0{/,file:/some/path/to/my/webapp/,STARTING}{/some/path/to/my/Application-2.3.33+46be96b464dc5b57b2e2e04ce31718a01360e5fb.war} [INFO] Initializing Spring root WebApplicationContext INFO:20161021 12:58:27: org.springframework.web.context.ContextLoader org.springframework.web.context.ContextLoader Root WebApplicationContext: initialization started INFO:20161021 12:58:27: org.springframework.web.context.support.XmlWebApplicationContext org.springframework.context.support.AbstractApplicationContext Refreshing Root WebApplicationContext: startup date [Fri Oct 21 12:58:27 EDT 2016]; root of context hierarchy INFO:20161021 12:58:27: org.springframework.beans.factory.xml.XmlBeanDefinitionReader org.springframework.beans.factory.xml.XmlBeanDefinitionReader Loading XML bean definitions from class path resource [spring/app-config.xml] INFO:20161021 12:58:27: org.springframework.beans.factory.xml.XmlBeanDefinitionReader org.springframework.beans.factory.xml.XmlBeanDefinitionReader Loading XML bean definitions from class path resource [spring/db-config.xml] INFO:20161021 12:58:28: org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.beans.factory.support.DefaultListableBeanFactory Overriding bean definition for bean 'endpointLTERepository': replacing [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] with [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] [INFO] Started ServerConnector@3a8f9130{HTTP/1.1}{0.0.0.0:8080} [INFO] Started Jetty Server 

/ Maven控制台输出结束

我认识到postgresql嵌入式组件旨在启动和关闭同一unit testing中的Postgres数据库实例。 我试图找到一种方法来使用它,它比原来的用例更进一步。 从本质上讲,我想要一个可以从maven插件启动的某种postgresql嵌入式服务 ,然后可以用来关闭postgres进程。

有关如何创建/构建此类服务和/或插件的任何建议?

这里的核心问题是能够在插件的两个不同目标之间共享一些状态:一个start进程的start目标,然后是一个可以杀死它的stop目标。 一个好方法是使用所有mojos实现的ContextEnabled接口。 它提供了一个getPluginContext()方法,它返回一个(原始)映射,您可以在其中存储要在mojos之间共享的对象。

使用这种方法,您可以将您创建的内容存储在插件的start目标中,并将其恢复到stop目标中。 这是一个简单的示例,用于显示此操作,其中一个简单的String值在mojos之间共享。

设置Maven插件项目 。 这基本上归结为具有以下POM的项目,该POM是Maven插件的标准POM,使用Java 8和注释进行配置:

  4.0.0 sample.plugin test-maven-plugin 1.0.0 maven-plugin  UTF-8 1.8 1.8     maven-plugin-plugin 3.5      org.apache.maven maven-plugin-api 3.3.9    org.apache.maven.plugin-tools maven-plugin-annotations 3.4 provided    

请注意maven-plugin类型的包装,它向Maven声明这是一个插件项目。 在这个新项目中,考虑以下StartMojo

 @Mojo(name = "start", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST) public class StartMojo extends AbstractMojo { @SuppressWarnings("unchecked") @Override public void execute() throws MojoExecutionException { getPluginContext().put("myService", new MyService("foo")); } } 

这是声明一个新的start mojo ,它默认绑定到pre-integration-test阶段。 它检索插件上下文并在其中放入一个新对象。 在上面,它是一个名为MyService的简单自定义POJO,它在其构造函数中获取一个值。 此对象映射到"myService"的键,该键用作查找。

然后,我们可以:

 @Mojo(name = "stop", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST) public class StopMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException { MyService service = (MyService) getPluginContext().get("myService"); getLog().info(service.getValue()); } } 

这是声明一个新的stop mojo,它默认绑定到post-integration-test阶段。 它检索插件上下文,在"myService"键下提取对象,最后获取其值并记录它。

将Maven插件(使用mvn clean install )打包并安装到本地存储库后,可以在示例项目中使用它

  sample.plugin test-maven-plugin   sample  start stop     

如果在该示例项目上运行mvn clean verify ,那么在post-integration-test阶段,您最终会在日志中打印"foo" 。 这表明该值由start mojo正确设置,然后由stop mojo正确检索。

当然,您可以在此映射中存储复杂对象,而不仅仅是String (可以有更简单的解决方案)。 值得注意的是,它可能是您要停止的process实例的主机。 您可以摆脱exec-maven-plugin ,创建一个新的Maven插件,其中包含您已经拥有的代码,用于在start目标中设置嵌入式数据库,将流程实例存储在此目标的插件上下文中,最后停止此操作稍后在另一个stop mojo中通过从插件上下文中检索它来处理它。

后续行动:根据@Tunaki的建议,我确实创建了自己的maven插件,它使用postgresql-embedded的一个分支来运行9.2 postgreSQL DB的进程内下载/安装/初始化。

不得不解决一些问题,但这种做法确实对我有用。

这是一个小的maven插件 ,可以用作postgresql-embedded的maven包装器。