如何将嵌入式PostgreSQL Server Java组件用作单独的服务? [英] How to use the Embedded PostgreSQL Server Java component as a separate service?

查看:285
本文介绍了如何将嵌入式PostgreSQL Server Java组件用作单独的服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a comprehensive integration test suite for a RESTful (Services) Java-based application that runs in Tomcat(7.x) and depends on an Postgresql (9.x) instance. Further, I would like to be able to run this suite as a self-contained process, exclusively from maven 3.x, if possible, by using the maven failsafe plugin. That way, the tests can be run across the 3 major platforms (Mac OSX, Linux & Windows).

从我所学到的,我相信关键是实现这一目标就是做这些步骤(按此顺序):

From what I have learned, I believe that the key to making this happen is doing something like these steps (in this order):


  1. 启动postgresql DB的嵌入式版本(和模式)设置)以某种方式在 maven生命周期的预集成测试阶段,让postgres-db-process在后台运行

  2. 启动我的Java容器加载我的服务应用程序:我正在使用Jetty 9.1.5插件

  3. 在集成测试阶段从Failsafe插件运行我的(基于JUnit的)测试

  4. 在maven的集成后测试阶段关闭我的Jetty容器

  5. 最后,在post-integration-tes中有一些机制 shutdown 之前启动的(后台)postgres-db-process生命周期的t阶段(杀死/清理此过程)

  1. Start an "embedded" version of the postgresql DB (and schema set up) somehow in the pre-integration-test phase of maven lifecycle, have that postgres-db-process running in the background
  2. Start my Java container which loads my Services App: I am using the Jetty 9.1.5 plugin
  3. Run my (JUnit-based) tests from Failsafe plugin during the integration-test phase
  4. Shut down my Jetty container in maven's post-integration-test phase
  5. Finally, have some mechanism shutdown the previously started (background) postgres-db-process in the post-integration-test phase of the lifecycle (kill/clean-up this process)

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

In my current implementation completes steps 1 - 3 successfully. In step 2, it uses of the exec-maven-plugin, which calls a Java class that uses the postgresql-embedded Java component. An Excerpt from the POM.xml:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.2.1</version>
  <executions>
    <execution>
          <id>Run Postgres DB start/schema setup</id>
          <phase>pre-integration-test</phase>
            <goals>
              <goal>java</goal>
            </goals>
      </execution>
    </executions>
    <configuration>
      <mainClass>com.some.package.test.utils.DbSetup</mainClass>
      <arguments>
        <argument>setup</argument>
      </arguments>
    </configuration>
</plugin>

以下是使用postgresql-embedded启动postgresql实例的DBSetup类的摘录: / p>

And here is an excerpt of the DBSetup class that uses postgresql-embedded to start a postgresql instance:

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<PostgresExecutable, PostgresProcess> 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控制台输出):

Please note that I am not calling process.stop(); within the method that starts the Postgres instance. So, I currently have no way to shutdown the DB process. Once I have exited out of this class DbSetup, all reference to that existing process will be lost. And the Postgres process never shuts down. In fact, it seems like the jetty container will not shut down either and the entire maven job is hung-up, so I have to manually kill it (excerpts from my maven console output):

[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 o.e.j.m.p.JettyWebAppContext@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控制台输出结束

/End of Maven console output

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

I recognize that the postgresql-embedded component is designed to start-up and shut-down a Postgres DB instance all within the same Unit test. I am trying to find a way to use it that it goes further than the originally intended use case. Essentially, I would like a postgresql-embedded service of some kind that can be launched from a maven plugin and can subsequently then be used to shut down that postgres process.

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

Any suggestions on how to create/build such a service &/or plugin?

推荐答案

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

The core problem here, is to be able to share some state between two different goals of a plugin: a start goal that would launch a process, and then a stop goal that would kill it. A good way to do that is to make use of the ContextEnabled interface that all mojos implement. It provides a getPluginContext() method that returns a (raw) map, in which you can store objects to be shared among mojos.

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

With this approach, you can store something you created in the start goal of a plugin, and get it back in the stop goal. Here's a minimalistic example to show this in action, where a simple String value is shared between mojos.

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

Set up a Maven plugin project. This basically comes down to having a project with the following POM, which is the standard POM for a Maven plugin, using Java 8 and annotations for configuration:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>sample.plugin</groupId>
  <artifactId>test-maven-plugin</artifactId>
  <version>1.0.0</version>
  <packaging>maven-plugin</packaging>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-plugin-plugin</artifactId>
        <version>3.5</version>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>3.3.9</version>
    </dependency>

    <!-- dependencies to annotations -->
    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <version>3.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

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

Note the packaging of type maven-plugin which declares to Maven that this is a plugin project. In this new project, consider the following 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"));
    }

}

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

This is declaring a new start mojo which is bound by default to the pre-integration-test phase. It retrieves the plugin context and puts a new object in it. In the above, it is a simple custom POJO called MyService which takes a value in its constructor. This object is mapped to a key of "myService", which serves as look-up.

然后,我们可以有:

@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());
    }

}

这是宣布一个新的停止 mojo默认情况下绑定到 post-integration-test 阶段。它检索插件上下文,在键myService下提取对象,最后得到它的值并记录它。

This is declaring a new stop mojo which is bound by default to the post-integration-test phase. It retrieves the plugin context, extracts the object under the key "myService", and finally get its value and logs it.

将此Maven插件(包含 mvn clean install )打包并安装到本地存储库后,您可以在

After packaging and installing this Maven plugin (with mvn clean install) into your local repo, you can use it in a sample project with

<plugin>
  <groupId>sample.plugin</groupId>
  <artifactId>test-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>sample</id>
      <goals>
        <goal>start</goal>
        <goal>stop</goal>
      </goals>
    </execution>
  </executions>
</plugin>

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

If you run mvn clean verify on that sample project, you'll end up having "foo" printed in your logs, in the post-integration-test phase. This shows that the value was correctly set-up by the start mojo, and then correctly retrieved by the stop mojo.

当然,您可以在此地图中存储复杂对象,而不仅仅是字符串(可能存在更简单的解决方案)。值得注意的是,它可能是您要停止的进程实例的主机。你可以摆脱 exec-maven-plugin ,创建一个新的Maven插件,其中包含你在中设置嵌入式数据库所需的代码启动目标,将流程实例存储在此目标的插件上下文中,最后通过从另一个 stop mojo中检索它来停止此过程插件上下文。

Of course, you can store complex objects in this map, not just a String (for which there could be more simple solutions). Notably, it could be a host for your process instance that you want to stop. You can get rid of the exec-maven-plugin, create a new Maven plugin containing the code you already have to set up the embedded database in a start goal, store the process instance in the plugin context in this goal, and finally stop this process later in another stop mojo by retrieving it from the plugin context.

这篇关于如何将嵌入式PostgreSQL Server Java组件用作单独的服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆