how to deploy a webapp and create its resources

21,242

Solution 1

finally i found a solution:

  1. database create/upgrade: in ServletContextListener.contextInitialized i use compile-time-generated ddl script to create database if not exists, or use liquibase to upgrade database if exists. no more persistence.xml usage for database generation.

  2. authentication realm deploy: i don't deploy or create any container-specific realm. in ServletContextListener.contextInitialized i register a custom JASPIC implementation, which is itself a JAAS login module wrapper. thanks to @ArjanTijms for this aricle and this answer

Solution 2

You can configure application scoped resources for the jdbc-connection-pool and jdbc-resource inside the glassfish-resources.xml and when you deploy your WAR file they will be created. When you undeploy your WAR they will go away. That solves the problem of manually adding them with asadmin. What I usually do it set it up manually using the GUI, then copy and paste the <jdbc-connection-pool> and <jdbc-resource> elements from the domain.xml into glassfish-resources.xml then I change the jndi-name to be application scoped, for example:

<jdbc-resource pool-name="MyAppPool" jndi-name="java:app/jdbc/my-app-pool"></jdbc-resource>

Then I make sure glassfish-resources.xml is packaged into the appropriate place in the WAR file, namely in the WEB-INF folder.

From what I have read in the Oracle documentation for Glassfish 4 it does not appear that you can package the auth-realm configuration with the application like you can for the JDBC stuff. I have filed an enhancement request for this glassfish auth-realm packaging enhancement It only appears that you can package the association to the realm in various deployment descriptors, see the "How to set a realm for an Application or Module" section of this guide for details.

HACK Alert

Actually I just thought of something which is a bit of a hack but might work around this issue. You could create a simple web APP which includes your real applications WAR file in it (in a resource directory). This wrapper app would include a REST client (like Jersey client) which would make REST calls to the Glassfish administration REST API to see if the auth-realm is there and configured. If it is it would use the REST API to deploy the embedded WAR file. If it isn't it would use the REST API to create the auth-realm and then it would deploy the WAR.

I'm not quite clear on the issues you are having with schema generation but that's all supported via the persistence.xml and works fine. If you want even more functionality in the way of automatic migration scripting then I would look at integrating a package like FlyAway

Here's an example of a glassfish-resources.xml file with app scoped resources which works for me under Glassfish 3.1.2. Some of the attributes you have I don't and I also don't use a JDBC JNDI naming style for the pool-name on the jdbc-resource.

<?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-connection-pool validation-table-name="TABLEVALIDATION" allow-non-component-callers="true"
                          statement-cache-size="200" associate-with-thread="true" statement-timeout-in-seconds="300"
                          non-transactional-connections="true" connection-leak-reclaim="true"
                          lazy-connection-association="true" connection-creation-retry-attempts="12"
                          lazy-connection-enlistment="true" validate-atmost-once-period-in-seconds="120"
                          statement-leak-timeout-in-seconds="360"
                          datasource-classname="oracle.jdbc.pool.OracleDataSource" res-type="javax.sql.DataSource"
                          connection-leak-timeout-in-seconds="420" statement-leak-reclaim="true"
                          name="UnitTestPool" is-connection-validation-required="true">
        <property name="DataSourceName" value="OracleDataSource"></property>
        <property name="ImplicitCachingEnabled" value="false"></property>
        <property name="NetworkProtocol" value="tcp"></property>
        <property name="DatabaseName" value="unittestdb"></property>
        <property name="LoginTimeout" value="0"></property>
        <property name="Password" value="tester"></property>
        <property name="URL" value="jdbc:oracle:thin:@testbed:1521:xe"></property>
        <property name="User" value="testertester"></property>
        <property name="PortNumber" value="1521"></property>
        <property name="ExplicitCachingEnabled" value="false"></property>
        <property name="dynamic-reconfiguration-wait-timeout-in-seconds" value="960"></property>
        <property name="MaxStatements" value="0"></property>
    </jdbc-connection-pool>
    <jdbc-resource pool-name="UnitTestPool" jndi-name="java:app/jdbc/unittestdb-pool"></jdbc-resource>
</resources>
Share:
21,242
Michele Mariotti
Author by

Michele Mariotti

currently coding SKYNET (in java), to get my personal Terminator army.

Updated on July 06, 2020

Comments

  • Michele Mariotti
    Michele Mariotti almost 4 years

    before deploy "myapp.war" on glassfish 4 i have to

    1. create jdbc connection pool (/WEB-INF/glassfish-resources.xml -> jdbc-connection-pool does not work by itself... fine from asadmin)
    2. create jdbc resource for pool (/WEB-INF/glassfish-resources.xml -> jdbc-resource same as above)
    3. create auth-realm (nothing auto, using asadmin for now)
    4. create schema (peristence.xml -> property javax.persistence.schema-generation.create-database-schemas, but is bogus)
    5. create tables (persistence.xml -> `create-tables', not perfect but at least it works)

    now i'm doing:

    1. upload "myapp.war", "glassfish-resources.xml" on /tmp/install
    2. asadmin add-resources ...
    3. asadmin create-auth-realm ...
    4. asadmin deploy ...
    5. asadmin disable myapp ...
    6. nano /.../glassfish/applications/myapp/WEB-INF/classes/META-INF/persistence.xml
    7. comment a few lines, ctrl+o, enter, ctrl+x, enter
    8. asadmin enable myapp ...
    9. rm -Rf /tmp/install

    and without other suggestion i'm planning to:

    1. upload "myapp.war", "deploy.sh" on /tmp/install
    2. chmod +x deploy.sh
    3. ./deploy.sh

    and the script will take care of everything. but i'd like to upload only a war file via glassfish http console and obtain the same result.

    is there a way to have a class or script called before contextInitialized?

    how would you deploy this thing?


    for completeness here are some additional info:

    /myapp/WEB-INF/classes/META-INF/persistence.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
        <persistence-unit name="myapp" transaction-type="JTA">
            <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
            <jta-data-source>jdbc/myapp</jta-data-source>
            <exclude-unlisted-classes>false</exclude-unlisted-classes>
            <shared-cache-mode>NONE</shared-cache-mode>
            <properties>
                <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
                <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/myapp"/>
                <property name="javax.persistence.jdbc.user" value="root"/>
                <property name="javax.persistence.jdbc.password" value="password"/>
                <property name="javax.persistence.schema-generation.database.action" value="create"/>
                <property name="javax.persistence.schema-generation.create-database-schemas" value="false"/>
                <property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>
                <property name="javax.persistence.schema-generation.scripts.create-target" value="C:/tmp/myapp_create.ddl"/>
                <property name="javax.persistence.schema-generation.scripts.drop-target" value="C:/tmp/myapp_drop.ddl"/>
                <property name="eclipselink.deploy-on-startup" value="true"/>
                <property name="eclipselink.target-database" value="MySQL"/>
    <!--            <property name="eclipselink.ddl-generation" value="create-tables"/> -->
    <!--            <property name="eclipselink.ddl-generation.output-mode" value="database"/> -->
    <!--            <property name="eclipselink.create-ddl-jdbc-file-name" value="myapp.ddl"/> -->
    <!--            <property name="eclipselink.logging.level" value="FINE" /> -->
    <!--            <property name="eclipselink.logging.level.sql" value="FINE"/> -->
    <!--            <property name="eclipselink.logging.parameters" value="true"/> -->
    <!--            <property name="eclipselink.logging.logger" value="org.eclipse.persistence.logging.DefaultSessionLog"/> -->
            </properties>
        </persistence-unit>
    </persistence>
    

    /myapp/WEB-INF/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-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="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" 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="jdbc/myapp_pool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
            <property name="serverName" value="localhost"/>
            <property name="portNumber" value="3306"/>
            <property name="databaseName" value="myapp"/>
            <property name="User" value="root"/>
            <property name="Password" value="password"/>
            <property name="URL" value="jdbc:mysql://localhost:3306/myapp"/>
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        </jdbc-connection-pool>
        <jdbc-resource enabled="true" jndi-name="jdbc/myapp" object-type="user" pool-name="jdbc/myapp_pool"/>
    </resources>
    

    while glassfish understands /myapp/.../persistence.xml (sometimes executing also CREATE SCHEMA myapp, sometimes not, apparently random - but it's fine),

    i definitely can't make glassfish read /myapp/WEB-INF/glassfish-resources.xml. it ignores this file.

    UPDATE glassfish reads the file but prefixes jndi names with java:app/ breaking other references. being aware of this thing i rewrite references with prefix and now it is working fine. lastly, i noted that if glassfish-resources.xml is inside META-INF (instead of WEB-INF) glassfish reads the file and it is also present in http ui under "applications > myapp > descriptors"

  • Michele Mariotti
    Michele Mariotti over 10 years
    my big problem is that glassfish ignores /WEB-INF/glassfish-resources.xml packed into myapp.war. no errors or warnings, simply ignores it. however see updated answer
  • Michele Mariotti
    Michele Mariotti over 10 years
    and no, auth-realm is not in glassfish.org/dtds/glassfish-resources_1_5.dtd
  • NBW
    NBW over 10 years
    @MicheleMariotti re: schema generation, your glassfish-resources.xml DTD is from GF 3.1, are you using that or Glassfish 4? JPA schema generation is part of JavaEE-7 JPA 2.1 so you would need to use Glassfish 4 to use them. Since they aren't in the JPA 2.0/EE6 spec which is in GF 3.1.x you have to use vendor specific properties such as the eclipselink ones you have commmented out.
  • NBW
    NBW over 10 years
    @MicheleMariotti I updated my answer with a sample of my glassfish-resources.xml file. How are you determining that yours is not being honored? Are you seeing exceptions? If you are trying to verify by looking at the admin GUI JDBC resources/connection pools section you won't see it there since app scoped resources do not show up there.
  • Michele Mariotti
    Michele Mariotti over 10 years
    i've updated persistence.xml to 2.1 schema: no behaviour change. but i managed to make glassfish read glassfish-resources.xml prefixing jndi names with java:app/
  • Michele Mariotti
    Michele Mariotti over 10 years
    however this discussion doesn't answer my questions: is it possible to deploy a war and create all necessary objects only uploading a war (or ear) file using http interface? if not, is there a way to execute code on deploy before contextInitialized?
  • NBW
    NBW over 10 years
    @MicheleMariotti What version of Glassfish are you using? Updating the persistence.xml schema will only make a difference if you are using Glassfish 4 per my previous comments about ee7/JPA 2.1
  • NBW
    NBW over 10 years
    @MicheleMariotti I've updated my answer with info on the auth-realm. Basically it looks like you can get everything on deployment except for that. Trying to provision it via code is not going to work since the auth-realm is bound on deployment which happens prior to the context initialized lifecycle event which is the earliest you can hook into things.
  • NBW
    NBW over 10 years
    @MicheleMariotti I edited my answer one more time with an idea for a work around to the auth-realm configuration issue.
  • Michele Mariotti
    Michele Mariotti over 10 years
    +1 for your time, +1 for mentioning FlyWay (but i've chosen liquibase instead), +1 for make me understand local resources. however i finally found the solution!