Хотелось бы поделиться опытом использования payara-micro в разработке корпоративных приложений. Надеюсь, что кому-то сэкономлю время, так как такое решение пришло далеко не сразу. Если вы уже используете Payara или Glassfish в качестве промышленного сервера, или только собираетесь окунуться в мир javaee, то payara-micro и эта статья для Вас.
Вам понадобится web-приложение, которое собирается посредством maven в war-архив, и Java 8 (на более старших работу не проверял).
Сначала приведу полный текст профиля, после чего мы его разберем на кусочки и дополним недостающими деталями, которые не видны в исходном коде.
<profile>
<id>micro</id>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-payara-micro</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<stripVersion>true</stripVersion>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-micro</artifactId>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<cut/>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<arguments>
<!--http://blog.payara.fish/using-hotswapagent-to-speed-up-development-->
<argument>-XXaltjvm=dcevm</argument>
<argument>-javaagent:hotswap/hotswap-agent.jar</argument>
<argument>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009</argument>
<argument>-Duser.language=en</argument>
<argument>-Duser.region=US</argument>
<argument>-Ddb.ora.url=jdbc:p6spy:oracle:thin:@localhost:1521:XE</argument>
<argument>-Ddb.pg.url=jdbc:p6spy:postgresql://localhost:5432/postgres</argument>
<argument>-jar</argument>
<argument>${project.build.directory}/${project.build.finalName}-microbundle.jar</argument>
<!--<argument>--prebootcommandfile</argument>-->
<!--<argument>src/main/setup/payara-micro-domain-config.txt</argument>-->
<argument>--domainConfig</argument>
<argument>src/main/setup/domain.xml</argument>
<argument>--deploy</argument>
<argument>${project.build.directory}/${project.build.finalName}.war</argument>
<argument>--rootDir</argument>
<argument>${project.build.directory}/payaramicro</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>fish.payara.maven.plugins</groupId>
<artifactId>payara-micro-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>bundle</goal>
<goal>start</goal>
</goals>
</execution>
</executions>
<configuration>
<payaraVersion>4.1.2.173</payaraVersion>
<autoDeployArtifact>false</autoDeployArtifact>
<customJars>
<artifactItem>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>2.3.1</version>
</artifactItem>
<artifactItem>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</artifactItem>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.0</version>
</dependency>
</customJars>
</configuration>
</plugin>
</plugins>
</build>
<cut/>
<dependencies>
<dependency>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-micro</artifactId>
<version>4.1.2.173</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
Первое, что необходимо сделать — скачать сам архив payara-micro. Именно это и делает первый плагин (maven-dependency-plugin). Архив представляет собой полноценный сервер приложений, который может быть запущен из командной строки как простое java-приложение.
Последний плагин (payara-micro-maven-plugin) не имеет никаких особенностей и настроен на стабильно работающую версию payra-micro 4.1.2.173. Из возможностей плагина здесь используется функция запаковки web-приложения в специальный bundle, готовый к запуску. Есть и другие способы запуска payara-micro, но здесь они нам не пригодятся.
Осталось сгенерировать нужную командную строку и собрать все воедино. Для запуска из командной строки как раз и используется exec-maven-plugin. Рассмотрим подробнее параметры командной строки:
<configuration>
<executable>java</executable>
<arguments>
<!--http://blog.payara.fish/using-hotswapagent-to-speed-up-development-->
<argument>-XXaltjvm=dcevm</argument>
<argument>-javaagent:hotswap/hotswap-agent.jar</argument>
<argument>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009</argument>
<argument>-Duser.language=en</argument>
<argument>-Duser.region=US</argument>
<argument>-Ddb.ora.url=jdbc:p6spy:oracle:thin:@localhost:1521:XE</argument>
<argument>-Ddb.pg.url=jdbc:p6spy:postgresql://localhost:5432/postgres</argument>
<argument>-jar</argument>
<argument>${project.build.directory}/${project.build.finalName}-microbundle.jar</argument>
<argument>--domainConfig</argument>
<argument>src/main/setup/domain.xml</argument>
<argument>--deploy</argument>
<argument>${project.build.directory}/${project.build.finalName}.war</argument>
<argument>--rootDir</argument>
<argument>${project.build.directory}/payaramicro</argument>
</arguments>
</configuration>
Первые два аргумента (-XXaltjvm=dcevm -javaagent:hotswap/hotswap-agent.jar) необходимы для изменения исходного кода без перезагрузки приложения. Благодаря второму аргументу (-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009) мы сразу запускаем приложение в режиме отладки, что позволит через средства применения изменений кода в отладке в IDE изменять не только тело самих методов, но и добавлять новые поля и многое другое. Необходимо только подключиться отладчиком из IDE, скомпилировать измененный файл с исходным кодом и применить изменения.
Следующие два аргумента (-Duser.language=en -Duser.region=US) нужны для корректной работы jdbc-клиента с Oracle. Настройки подключения к тестовой СУБД передаются через -D аргументы. В своих приложениях я использую ссылку вида ${db.ora.url} на эти параметры в файле ресурсов, который автоматически создает их на старте. Подмена файла, который создает ресурсы с тестовыми или промышленными настройками тоже реализована через профиль в maven и его возможности фильтровать текстовые файлы.
Ключевые аргументы при запуске указывают путь (--domainConfig src/main/setup/domain.xml) до файла настроек домена, а так же путь до war-файла (--deploy ${project.build.directory}/${project.build.finalName}.war), подлежащего деплою на сервер. Настройки домена были мной получены на основании стандартного файла внутри архива. Содержимое приведу в качестве примера. Для использования в разработке указанных настроек более чем достаточно.
<!--
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved.
The contents of this file are subject to the terms of either the GNU
General Public License Version 2 only ("GPL") or the Common Development
and Distribution License("CDDL") (collectively, the "License"). You
may not use this file except in compliance with the License. You can
obtain a copy of the License at
https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
or packager/legal/LICENSE.txt. See the License for the specific
language governing permissions and limitations under the License.
When distributing the software, include this License Header Notice in each
file and include the License file at packager/legal/LICENSE.txt.
GPL Classpath Exception:
Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the GPL Version 2 section of the License
file that accompanied this code.
Modifications:
If applicable, add the following below the License Header, with the fields
enclosed by brackets [] replaced by your own identifying information:
"Portions Copyright [year] [name of copyright owner]"
Contributor(s):
If you wish your version of this file to be governed by only the CDDL or
only the GPL Version 2, indicate your decision by adding "[Contributor]
elects to include this software in this distribution under the [CDDL or GPL
Version 2] license." If you don't indicate a single choice of license, a
recipient has the option to distribute your version of this file under
either the CDDL, the GPL Version 2 or to extend the choice of license to
its licensees as provided above. However, if you add GPL Version 2 code
and therefore, elected the GPL Version 2 license, then the option applies
only if the new code is made subject to such option by the copyright
holder.
Portions Copyright [2016] [Payara Foundation and/or its affiliates]
-->
<domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" version="10.0">
<security-configurations>
<authorization-service default="true" name="authorizationService">
<security-provider name="simpleAuthorization" type="Simple" provider-name="simpleAuthorizationProvider">
<authorization-provider-config support-policy-deploy="false" name="simpleAuthorizationProviderConfig"></authorization-provider-config>
</security-provider>
</authorization-service>
</security-configurations>
<system-applications />
<applications />
<resources>
<jdbc-resource pool-name="DerbyPool" jndi-name="jdbc/__default" object-type="system-all" />
<jdbc-connection-pool is-isolation-level-guaranteed="false" name="DerbyPool" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource">
<property name="databaseName" value="${com.sun.aas.instanceRoot}/lib/databases/embedded_default" />
<property name="connectionAttributes" value=";create=true" />
</jdbc-connection-pool>
<context-service description="context service" jndi-name="concurrent/__defaultContextService" object-type="system-all"></context-service>
<managed-executor-service maximum-pool-size="200" core-pool-size="6" long-running-tasks="true" keep-alive-seconds="300" hung-after-seconds="300" task-queue-capacity="20000" jndi-name="concurrent/__defaultManagedExecutorService" object-type="system-all"></managed-executor-service>
<managed-scheduled-executor-service core-pool-size="6" long-running-tasks="true" keep-alive-seconds="300" hung-after-seconds="300" jndi-name="concurrent/__defaultManagedScheduledExecutorService" object-type="system-all"></managed-scheduled-executor-service>
<managed-thread-factory description="thread factory" jndi-name="concurrent/__defaultManagedThreadFactory" object-type="system-all"></managed-thread-factory>
</resources>
<servers>
<server name="server" config-ref="server-config">
<resource-ref ref="jdbc/__default" />
</server>
</servers>
<configs>
<config name="server-config">
<health-check-service-configuration enabled="false">
<log-notifier enabled="true"/>
<eventbus-notifier enabled="false"/>
<cpu-usage-checker unit="MINUTES" name="CPU" time="1" enabled="true" />
<machine-memory-usage-checker unit="MINUTES" name="MMEM" time="3" enabled="true" />
<heap-memory-usage-checker unit="MINUTES" name="HEAP" time="3" enabled="true" />
<hogging-threads-checker unit="MINUTES" name="HT" time="5" enabled="true" />
<garbage-collector-checker unit="MINUTES" name="GC" time="5" enabled="true" />
</health-check-service-configuration>
<http-service access-logging-enabled="false">
<access-log format="%client.name% %auth-user-name% %datetime% %request% %status% %response.length%" rotation-interval-in-minutes="15" rotation-suffix="yyyy-MM-dd"></access-log>
<virtual-server id="server" access-logging-enabled="false" access-log="" network-listeners="http-listener, https-listener"></virtual-server>
</http-service>
<iiop-service>
<orb use-thread-pool-ids="thread-pool-1"></orb>
<iiop-listener id="orb-listener-1" enabled="false" address="0.0.0.0"></iiop-listener>
</iiop-service>
<admin-service system-jmx-connector-name="system" type="das-and-server">
<jmx-connector port="8686" address="0.0.0.0" security-enabled="false" auth-realm-name="admin-realm" name="system" enabled="false"></jmx-connector>
<das-config></das-config>
</admin-service>
<connector-service class-loading-policy="global" shutdown-timeout-in-seconds="30">
</connector-service>
<!--<ejb-container steady-pool-size="0" max-pool-size="32" session-store="${com.sun.aas.instanceRoot}/session-store" pool-resize-quantity="8">-->
<!--<ejb-timer-service ejb-timer-service="Hazelcast"></ejb-timer-service>-->
<!--</ejb-container>-->
<log-service file="${com.sun.aas.instanceRoot}/logs/server.log" log-rotation-limit-in-bytes="2000000">
<module-log-levels />
</log-service>
<security-service activate-default-principal-to-role-mapping="true" jacc="simple">
<auth-realm classname="com.sun.enterprise.security.auth.realm.file.FileRealm" name="admin-realm">
<property value="${com.sun.aas.instanceRoot}/config/admin-keyfile" name="file" />
<property value="fileRealm" name="jaas-context" />
</auth-realm>
<auth-realm classname="com.sun.enterprise.security.auth.realm.file.FileRealm" name="file">
<property value="${com.sun.aas.instanceRoot}/config/keyfile" name="file" />
<property value="fileRealm" name="jaas-context" />
</auth-realm>
<auth-realm classname="com.sun.enterprise.security.auth.realm.certificate.CertificateRealm" name="certificate" />
<jacc-provider policy-configuration-factory-provider="com.sun.enterprise.security.provider.PolicyConfigurationFactoryImpl" policy-provider="com.sun.enterprise.security.provider.PolicyWrapper" name="default">
<property value="${com.sun.aas.instanceRoot}/generated/policy" name="repository" />
</jacc-provider>
<jacc-provider policy-configuration-factory-provider="com.sun.enterprise.security.jacc.provider.SimplePolicyConfigurationFactory" policy-provider="com.sun.enterprise.security.jacc.provider.SimplePolicyProvider" name="simple" />
<audit-module classname="com.sun.enterprise.security.ee.Audit" name="default">
<property value="false" name="auditOn" />
</audit-module>
<message-security-config auth-layer="SOAP">
<provider-config provider-id="XWS_ClientProvider" class-name="com.sun.xml.wss.provider.ClientSecurityAuthModule" provider-type="client">
<request-policy auth-source="content" />
<response-policy auth-source="content" />
<property value="s1as" name="encryption.key.alias" />
<property value="s1as" name="signature.key.alias" />
<property value="false" name="dynamic.username.password" />
<property value="false" name="debug" />
</provider-config>
<provider-config provider-id="ClientProvider" class-name="com.sun.xml.wss.provider.ClientSecurityAuthModule" provider-type="client">
<request-policy auth-source="content" />
<response-policy auth-source="content" />
<property value="s1as" name="encryption.key.alias" />
<property value="s1as" name="signature.key.alias" />
<property value="false" name="dynamic.username.password" />
<property value="false" name="debug" />
<property value="${com.sun.aas.instanceRoot}/config/wss-server-config-1.0.xml" name="security.config" />
</provider-config>
<provider-config provider-id="XWS_ServerProvider" class-name="com.sun.xml.wss.provider.ServerSecurityAuthModule" provider-type="server">
<request-policy auth-source="content" />
<response-policy auth-source="content" />
<property value="s1as" name="encryption.key.alias" />
<property value="s1as" name="signature.key.alias" />
<property value="false" name="debug" />
</provider-config>
<provider-config provider-id="ServerProvider" class-name="com.sun.xml.wss.provider.ServerSecurityAuthModule" provider-type="server">
<request-policy auth-source="content" />
<response-policy auth-source="content" />
<property value="s1as" name="encryption.key.alias" />
<property value="s1as" name="signature.key.alias" />
<property value="false" name="debug" />
<property value="${com.sun.aas.instanceRoot}/config/wss-server-config-1.0.xml" name="security.config" />
</provider-config>
</message-security-config>
<property value="SHA-256" name="default-digest-algorithm" />
</security-service>
<transaction-service tx-log-dir="${com.sun.aas.instanceRoot}/logs" >
</transaction-service>
<hazelcast-runtime-configuration enabled="false" multicastGroup="224.2.2.4" multicastPort="2904" generate-names="true"></hazelcast-runtime-configuration>
<phone-home-runtime-configuration></phone-home-runtime-configuration>
<request-tracing-service-configuration>
<log-notifier enabled="true"></log-notifier>
</request-tracing-service-configuration>
<notification-service-configuration enabled="true">
<log-notifier-configuration enabled="true"></log-notifier-configuration>
<eventbus-notifier-configuration enabled="false"></eventbus-notifier-configuration>
</notification-service-configuration>
<batch-runtime-configuration table-prefix="jbatch" data-source-lookup-name="jdbc/__default"></batch-runtime-configuration>
<availability-service availability-enabled="true" >
<web-container-availability availability-enabled="true" persistence-scope="modified-session" sso-failover-enabled="true" persistence-type="hazelcast"></web-container-availability>
<!--<ejb-container-availability sfsb-ha-persistence-type="hazelcast" sfsb-persistence-type="hazelcast" ></ejb-container-availability>-->
</availability-service>
<network-config>
<protocols>
<protocol name="http-listener">
<http default-virtual-server="server" xpowered-by="false" max-connections="250" comet-support-enabled="true">
<file-cache enabled="false"></file-cache>
</http>
</protocol>
<protocol security-enabled="true" name="https-listener">
<http default-virtual-server="server" xpowered-by="false" comet-support-enabled="true" max-connections="250">
<file-cache enabled="false"></file-cache>
</http>
<ssl classname="com.sun.enterprise.security.ssl.GlassfishSSLImpl" ssl3-enabled="false" cert-nickname="s1as"></ssl>
</protocol>
</protocols>
<network-listeners>
<network-listener port="8080" protocol="http-listener" transport="tcp" name="http-listener" thread-pool="http-thread-pool" enabled="true" />
<network-listener port="8443" protocol="https-listener" transport="tcp" name="https-listener" thread-pool="http-thread-pool" enabled="false" />
</network-listeners>
<transports>
<transport byte-buffer-type="HEAP" name="tcp" acceptor-threads="-1"></transport>
</transports>
</network-config>
<thread-pools>
<thread-pool name="http-thread-pool" min-thread-pool-size="10" max-thread-pool-size="200" max-queue-size="4096"></thread-pool>
<thread-pool name="thread-pool-1" min-thread-pool-size="2" max-thread-pool-size="200"/>
</thread-pools>
</config>
</configs>
<system-property name="fish.payara.classloading.delegate" value="false"/>
<property name="administrative.domain.name" value="domain1"/>
</domain>
Также приведу пример файла описания ресурсов для их автоматического создания на старте приложения.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-resource enabled="true" jndi-name="java:app/ora_con" object-type="user" pool-name="java:app/OraPool"/>
<jdbc-connection-pool
allow-non-component-callers="false"
associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
connection-validation-method="table"
driver-classname="com.p6spy.engine.spy.P6SpyDriver"
fail-all-connections="false"
idle-timeout-in-seconds="300"
is-connection-validation-required="true"
is-isolation-level-guaranteed="true"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="false"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="java:app/OraPool"
non-transactional-connections="false"
ping="false"
pool-resize-quantity="2"
pooling="true"
res-type="java.sql.Driver"
statement-cache-size="0"
statement-leak-reclaim="false"
statement-leak-timeout-in-seconds="0"
statement-timeout-in-seconds="-1"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"
validation-table-name="DUAL"
wrap-jdbc-objects="true">
<property name="URL" value="${db.ora.url}"/>
<property name="User" value="system"/>
<property name="Password" value="1"/>
<property name="property.dynamic-reconfiguration-waittimeout-in-seconds" value="60" />
</jdbc-connection-pool>
<jdbc-resource enabled="true" jndi-name="java:app/pg_con" object-type="user" pool-name="java:app/PGPool"/>
<jdbc-connection-pool
allow-non-component-callers="false"
associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
connection-validation-method="table"
driver-classname="com.p6spy.engine.spy.P6SpyDriver"
fail-all-connections="false"
idle-timeout-in-seconds="300"
is-connection-validation-required="true"
is-isolation-level-guaranteed="true"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="false"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="java:app/PGPool"
non-transactional-connections="false"
ping="false"
pool-resize-quantity="2"
pooling="true"
res-type="java.sql.Driver"
statement-cache-size="0"
statement-leak-reclaim="false"
statement-leak-timeout-in-seconds="0"
statement-timeout-in-seconds="-1"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"
validation-table-name="DUAL"
wrap-jdbc-objects="true">
<property name="URL" value="${db.pg.url}"/>
<property name="User" value="postgres"/>
<property name="Password" value="postgres"/>
<property name="property.dynamic-reconfiguration-waittimeout-in-seconds" value="60" />
</jdbc-connection-pool>
</resources>
Установить dcevm можно отсюда. Обязательно устанавливайте ее как альтернативную JVM, чтобы всегда была возможность работать на обычной по умолчанию.
Скачать последнюю версию агента для неограниченного изменения исходного кода, а также перезагрузки кода внутри фреймворков подобных hibernate можно отсюда.
Идеи по настройке перезагрузки кода для payara были подчерпнуты отсюда. Но правда только сама идея, так как реализация для payara-micro там не рассматривается.
Благодаря библиотеке p6spy свои sql-логи вы можете найти в корневой папке проекта — файл spy.log. Библиотеку p6spy при необходимости можно настроить, но это уже тема совсем другой статьи.
После размещения описанного выше профиля в вашем pom.xml и дополнительных файлов, рассмотренных выше, в соответствующих папках вы сможете запустить свое приложение следующей командой:
mvn install exec:exec -P micro
И никаких дополнительных плагинов для сервера приложений в IDE не требуется...
Автор: Сергей Лысенко