Using exploded deployments and CLI attachments

In WildFly there used to be two worlds : one for developers with exploded deployments using a scanner and one for production where artifacts (wars/ears/jars) were deployed.
Now those two worlds have collided and you can have exploded deployments without a scanner.
And since this is using the management API you get remote access to the content for "free" and of course it works in domain mode.
While this new feature is really usefull for tools developers (JBoss Developer Studio will use this in a tech preview, and NetBeans should use it also), YOU can also take advantage of it.

Creating exploded deployments

There are two ways to create an exploded deployment:

  • Create an empty deployment and add content to it.

  • Deploy an artifact and explode it.

Creating an exploded deployment from scratch

Let’s create a from_scratch.war deployment:

[standalone@localhost:9990 /] /deployment=from_scratch.war:add(content=[{empty=true}])
[standalone@localhost:9990 /] /deployment=from_scratch.war:add-content(content=[
          {input-stream-index=/home/ehsavoie/NetBeansProjects/SimpleWebapp/target/SimpleWebapp/index.html,target-path=index.html},
          {input-stream-index=/home/ehsavoie/NetBeansProjects/SimpleWebapp/target/SimpleWebapp/WEB-INF/web.xml, target-path=WEB-INF/web.xml},
          {input-stream-index=/home/ehsavoie/NetBeansProjects/SimpleWebapp/target/SimpleWebapp/WEB-INF/classes/org/wildfly/sample/simplewebapp/SimpleServlet.class, target-path=WEB-INF/classes/org/wildfly/sample/simplewebapp/SimpleServlet.class}])
[standalone@localhost:9990 /] /deployment=from_scratch.war:deploy

Here we have created an empty deployment called from_scratch.war to which we have added 3 files: - a simple HTML page index.html. - A class file for a servlet. - A web.xml descriptor.

Then we have enabled the deployment and thus could access the index.html and the servlet.
If you take a look at the $JBOSS_HOME/standalone/content/dc/9567f71b186466b21fff825d60f5fbc84ae6b1/content/ you will see the exploded war content.

Important

Since this content is managed, don’t touch it directly by copying file to it manually, always use the Management API (through the jboss-cli or the web console).

Note

Note that the add-content can be used to replace files in a deployment but if the deployment is running this might not show until you redeploy it.
You can also delete files in a deployment using remove-content but if the deployment is running this might not show until you redeploy it.

Exploding a deployment

Let’s install and explode a to_be_exploded.war deployment:

[standalone@localhost:9990 /] /deployment=to_be_exploded.war:add(content=[{input-stream-index=/home/ehsavoie/NetBeansProjects/SimpleWebapp/target/SimpleWebapp.war}],enabled=false)
[standalone@localhost:9990 /] /deployment=to_be_exploded.war:explode
[standalone@localhost:9990 /] /deployment=to_be_exploded.war:deploy

This can be achieved via the web console using the explode operation in the menu: image::exploded_deployments/explode.png[explode]

If you take a look at the $JBOSS_HOME/standalone/content//cf/9918bad9d875ffb20da8747b5dcd15bdab16e0/content/ you will see the exploded war content (it differs from the first example hash has the war file contains META-INF files).

Important

Since this content is managed, don’t touch it directly by copying file to it manually, always use the Management API (through the jboss-cli or the web console).

Note

Note also that you can only explode an unexploded deployment or an archive file if the deployment is not running (aka is disabled).

Reading content from a deployment

Now you can read content from a deployment, so you can directly see what’s in your deployment. This operation can be use to browse the content of a deployment and read or download its files (even from inside an archive file).

Using the JBoss CLI

Using the browse-content operation you can have the list of files in the deployment :

[standalone@localhost:9990 /] /deployment=to_be_exploded.war:browse-content
{
    "outcome" => "success",
    "result" => [
        {
            "path" => "META-INF/",
            "directory" => true
        },
        {
            "path" => "META-INF/MANIFEST.MF",
            "directory" => false,
            "file-size" => 134L
        },
        {
            "path" => "WEB-INF/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/classes/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/classes/org/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/classes/org/wildfly/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/classes/org/wildfly/sample/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/classes/org/wildfly/sample/simplewebapp/",
            "directory" => true
        },
        {
            "path" => "WEB-INF/web.xml",
            "directory" => false,
            "file-size" => 916L
        },
        {
            "path" => "WEB-INF/classes/org/wildfly/sample/simplewebapp/SimpleServlet.class",
            "directory" => false,
            "file-size" => 2302L
        },
        {
            "path" => "index.html",
            "directory" => false,
            "file-size" => 234L
        },
        {
            "path" => "META-INF/maven/",
            "directory" => true
        },
        {
            "path" => "META-INF/maven/org.wildfly.sample/",
            "directory" => true
        },
        {
            "path" => "META-INF/maven/org.wildfly.sample/SimpleWebapp/",
            "directory" => true
        },
        {
            "path" => "META-INF/maven/org.wildfly.sample/SimpleWebapp/pom.xml",
            "directory" => false,
            "file-size" => 2992L
        },
        {
            "path" => "META-INF/maven/org.wildfly.sample/SimpleWebapp/pom.properties",
            "directory" => false,
            "file-size" => 125L
        }
    ]
}

You can reduce the output by filtering using the path, depth and archive parameters. For exemple

[standalone@localhost:9990 /] /deployment=to_be_exploded.war:browse-content(path=WEB-INF/, depth=1)
{
    "outcome" => "success",
    "result" => [
        {
            "path" => "web.xml",
            "directory" => false,
            "file-size" => 916L
        },
        {
            "path" => "classes/",
            "directory" => true
        }
    ]
}

So now we can display the content of the web.xml. Using the read-content operation is not sufficient enough as it will return an attachment:

[standalone@localhost:9990 /] /deployment=to_be_exploded.war:read-content(path=WEB-INF/web.xml)
{
    "outcome" => "success",
    "result" => {"uuid" => "c778c51e-a507-4a71-a21f-d6af8b230db4"},
    "response-headers" => {"attached-streams" => [{
        "uuid" => "c778c51e-a507-4a71-a21f-d6af8b230db4",
        "mime-type" => "application/xml"
    }]}
}

So we need to combine this operation with the attachment operation like this :

[standalone@localhost:9990 /] attachment display --operation=/deployment=to_be_exploded.war:read-content(path=WEB-INF/web.xml)
ATTACHMENT 582a10e0-5159-4d2b-8d07-8d39af0df8c3:
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <servlet id="SimpleServlet">
        <servlet-name>SimpleServlet</servlet-name>
        <display-name>SimpleServlet</display-name>
        <servlet-class>org.wildfly.sample.simplewebapp.SimpleServlet</servlet-class>
        <init-param>
            <param-name>message</param-name>
            <param-value>Hello World</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>SimpleServlet</servlet-name>
        <url-pattern>/SimpleServlet</url-pattern>
    </servlet-mapping>
</web-app>

And to save this content locally we can use:

[standalone@localhost:9990 /] attachment save --operation=/deployment=to_be_exploded.war:read-content(path=WEB-INF/web.xml) --file=/home/ehsavoie/tmp/web.xml
File saved to /home/ehsavoie/tmp/web.xml

Using the web console

Navigate to 'Deployments' and select the deployment you want to browse. Then open the context menu and choose Browse Content:

browse content op

This opens a new page with the contents of the deployment. For each file, there’s a link with the full path and size of the file. Click on the link to download the file:

content

Using HAL.NEXT

The next major version of the web console (HAL.next) is currently under active development and is available as technical preview https://github.com/hal/hal.next. Follow the instruction in https://github.com/hal/hal.next#running to get started. Besides general improvements like better navigation and a revisited look and feel, HAL.next comes with many improvements for dealing with deployments:

  • Add Deployments using drag & drop.

  • New content browser using a tree view and an editor with syntax highlighting.

  • Download complete deployments or single files of a deployment.

Select deployments and just click on View to display its content:

explode next

This opens a new page which allows for a really nice way to browse and read content from a deployment:

content next

References

For the official documentation regarding deployments: Official Documentation
The example basic webapp used in this article is available here