Главная » Хабрахабр » [Из песочницы] Компактный сервер с настроенной перезагрузкой кода и sql-логами одной maven командой для продуктивной разработки

[Из песочницы] Компактный сервер с настроенной перезагрузкой кода и sql-логами одной maven командой для продуктивной разработки

Надеюсь, что кому-то сэкономлю время, так как такое решение пришло далеко не сразу. Хотелось бы поделиться опытом использования payara-micro в разработке корпоративных приложений. Если вы уже используете Payara или Glassfish в качестве промышленного сервера, или только собираетесь окунуться в мир javaee, то payara-micro и эта статья для Вас.

Вам понадобится web-приложение, которое собирается посредством maven в war-архив, и Java 8 (на более старших работу не проверял).

Сначала приведу полный текст профиля, после чего мы его разберем на кусочки и дополним недостающими деталями, которые не видны в исходном коде.

maven профиль для payara-micro

<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>$</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>

Именно это и делает первый плагин (maven-dependency-plugin). Первое, что необходимо сделать — скачать сам архив payara-micro. Архив представляет собой полноценный сервер приложений, который может быть запущен из командной строки как простое java-приложение.

1. Последний плагин (payara-micro-maven-plugin) не имеет никаких особенностей и настроен на стабильно работающую версию payra-micro 4. 173. 2. Есть и другие способы запуска payara-micro, но здесь они нам не пригодятся. Из возможностей плагина здесь используется функция запаковки web-приложения в специальный bundle, готовый к запуску.

Для запуска из командной строки как раз и используется 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>

Благодаря второму аргументу (-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009) мы сразу запускаем приложение в режиме отладки, что позволит через средства применения изменений кода в отладке в IDE изменять не только тело самих методов, но и добавлять новые поля и многое другое. Первые два аргумента (-XXaltjvm=dcevm -javaagent:hotswap/hotswap-agent.jar) необходимы для изменения исходного кода без перезагрузки приложения. Необходимо только подключиться отладчиком из IDE, скомпилировать измененный файл с исходным кодом и применить изменения.

Настройки подключения к тестовой СУБД передаются через -D аргументы. Следующие два аргумента (-Duser.language=en -Duser.region=US) нужны для корректной работы jdbc-клиента с Oracle. Подмена файла, который создает ресурсы с тестовыми или промышленными настройками тоже реализована через профиль в maven и его возможности фильтровать текстовые файлы. В своих приложениях я использую ссылку вида ${db.ora.url} на эти параметры в файле ресурсов, который автоматически создает их на старте.

Настройки домена были мной получены на основании стандартного файла внутри архива. Ключевые аргументы при запуске указывают путь (--domainConfig src/main/setup/domain.xml) до файла настроек домена, а так же путь до war-файла (--deploy ${project.build.directory}/${project.build.finalName}.war), подлежащего деплою на сервер. Для использования в разработке указанных настроек более чем достаточно. Содержимое приведу в качестве примера.

domain.xml

<!-- 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>

Также приведу пример файла описания ресурсов для их автоматического создания на старте приложения.

glassfish-resources.xml

<?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>

Обязательно устанавливайте ее как альтернативную JVM, чтобы всегда была возможность работать на обычной по умолчанию. Установить dcevm можно отсюда.

Скачать последнюю версию агента для неограниченного изменения исходного кода, а также перезагрузки кода внутри фреймворков подобных hibernate можно отсюда.

Но правда только сама идея, так как реализация для payara-micro там не рассматривается. Идеи по настройке перезагрузки кода для payara были подчерпнуты отсюда.

Библиотеку p6spy при необходимости можно настроить, но это уже тема совсем другой статьи. Благодаря библиотеке p6spy свои sql-логи вы можете найти в корневой папке проекта — файл spy.log.

После размещения описанного выше профиля в вашем pom.xml и дополнительных файлов, рассмотренных выше, в соответствующих папках вы сможете запустить свое приложение следующей командой:

mvn install exec:exec -P micro

И никаких дополнительных плагинов для сервера приложений в IDE не требуется...


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] Философия Джефа Безоса: «День 1»

13 сентября Джеф Безос стартовал филантропический проект «День 1». Копнем, что же стоит за этим названием. Какова философия «Дня 1» Джеффа Безоса? Изначально этот вопрос появился на ‘Quora’: месте для получения и обмена знаниями, позволяющим людям учиться у других и ...

Very Special Event: как мы смотрели презентацию Apple и что об этом думаем

Тем не менее, мы в Авито не могли пропустить это событие. От презентации Apple, которая должна была пройти 12 сентября, ничего особенного не ждали: три новых модели iPhone и новую версию Apple Watch — об этих новинках знали заранее. Посмотреть ...