The supplied files describe a partially developed Perl/Moo-based toolset for managing DAP application binaries stored in Artifactory and making them available on destination hosts.
At a high level, the project appears intended to:
1. Validate that a named release exists in Artifactory. 2. Read release metadata from Artifactory. 3. Update a local status.xml manifest describing supported products and JDKs. 4. Download product archives such as WebLogic, Tomcat, JBoss and JDK tarballs from Artifactory. 5. Stage or extract those archives onto destination hosts. 6. Optionally process local .dap metadata files to update status.xml.
The main implemented workflows are split between:
- artifactory-release, which is a CLI script for checking a release and updating status.xml. - lib/Dap/Artifactory/Manifest.pm, which reads remote release metadata and rewrites sections of status.xml. - lib/Dap/Artifactory.pm, which downloads and stages/extracts products and JDKs from Artifactory. - lib/Dap/Artifactory/Tx.pm, which provides the Artifactory HTTP/curl transaction layer. - lib/Dap/Artifactory/Dapfile*.pm, which seem intended to process per-package .dap JSON metadata files, although this area is incomplete.
There are several implementation bugs and inconsistencies, so the project should be regarded as incomplete or not yet production-safe.
According to artifactory-release, the first visible use case is:
artifactory-release --release <release-path> --statusxml <status.xml>
The script:
1. Checks whether a named release exists in Artifactory. 2. Logs success or failure. 3. Uses Dap::Artifactory::Manifest to update a local status.xml.
The release metadata itself is expected to live in Artifactory under a path based on:
/dist-private-local/com/db/dap/<release>
as implemented in lib/Dap/Artifactory/Manifest.pm and lib/Dap/Artifactory/Tx.pm.
The second use case, represented by lib/Dap/Artifactory.pm, is installation/staging:
1. Read a manifest. 2. For each product/JDK in the manifest: check if it is already installed or staged. If not, verify the archive exists in Artifactory. Download it. For WebLogic, move the archive into a WebLogic staging tarball directory. For Tomcat/JBoss/JDK, extract the archive into the staging application directory.
The third partial use case, represented by lib/Dap/Artifactory/Dapfile.pm, is discovery of .dap metadata files inside package directories and conversion of that metadata into status.xml entries.
artifactory-release defines package Dap::Artifactory::Release and a main block.
It accepts two command-line options through Dap::Script:
"release|r=s" => \my $release, "statusxml=s" => \my $status_xml,
The object requires:
has status_xml => ( is => 'rw', required => 1 ); has release => ( is => 'rw', required => 1 );
Its run method does:
if (not $self->tx->file_exist($self->release)) { $self->log->error("RELEASE: " . $self->release . ' NOT FOUND on Artifactory'); exit 1; } $self->log->info('RELEASE ' . $self->release . ' OK'); Dap::Artifactory::Manifest->new( release => $self->release, status_xml => $self->status_xml, )->update;
So the intended behavior is:
1. Confirm the release path exists in Artifactory. 2. Update the provided status.xml using release metadata.
From artifactory-release: --release / -r (required): Release path/name in Artifactory. --statusxml (required): Local XML file to update. Environment variables (required, indirectly): Artifactory connection details used by Dap::Artifactory::Tx.
Logs to Mojo::Log. Updated status.xml. Exits with status 1 if release is not found.
This file is intended to define the main deployment/downloader class. However, there is an important typo:
package Dap::Artifatory {
The package name is missing the c in Artifactory. This means the file path lib/Dap/Artifactory.pm does not define Dap::Artifactory; it defines Dap::Artifatory. Unless the rest of the system uses that typo, this module will not load as expected.
This module is intended to: 1. Process JBoss. 2. Process Tomcat. 3. Process WebLogic. 4. Process JDKs.
The run method in lib/Dap/Artifactory.pm does:
$self->process_jboss; $self->process_tomcat; $self->process_weblogic; $self->process_jdks;
It has a force_download flag: has force_download => ( is => 'rw', default => 0 );
The comments say: "This option means: ignore staging copy, download remote copy, remove local copy, extract remote copy"
process_weblogic, process_tomcat, and process_jboss all call _process_product.
The _process_product method: 1. Iterates over products from the manifest. 2. Filters by technology. 3. Skips if already installed or staged unless force_download is enabled. 4. Checks if the artifact exists in Artifactory. 5. Downloads it. 6. For WebLogic: moves the archive to the WebLogic tarball staging directory. 7. For Tomcat/JBoss: extracts it under the staging application directory and removes the downloaded temporary file.
There is a typo in the extract call: $p->verison should be $p->version.
process_jdks in lib/Dap/Artifactory.pm: 1. Iterates over manifest JDKs. 2. Skips JDKs where location is unknown. 3. Skips already installed/staged JDKs unless force_download is enabled. 4. Checks the JDK archive in Artifactory. 5. Downloads and extracts it. 6. Deletes the temporary archive.
Dap::Artifactory::Tx wraps Artifactory operations. It uses Mojo::UserAgent, Dap::Const, and Dap::Cluster.
It expects Artifactory credentials and base URL from environment variables: ARTIFACTORY_USER, ARTIFACTORY_TOKEN, ARTIFACTORY_URL.
The default Artifactory repository path is /dist-private-local/com/db/dap, defined via: has default_artifatory_path => ( is => 'rw', default => sub { '/dist-private-local/com/db/dap' } );
Note the attribute name is also misspelled as default_artifatory_path.
Mojo::UserAgent is configured so that every request gets credentials via userinfo and base URL.
file_exist calls Artifactory's storage API: my $url = $self->url . "/api/storage/dist-private-local/com/db/dap/$file"; $self->ua->get($url)->res->is_success;
The download method deliberately shells out to curl instead of using Mojo::UserAgent. The comment says: "For some funny reason Mojo::UserAgent does not download entire file from Artifactory therefore the need to shell out to external curl command to download the archive."
The generated command is approximately: curl -OJkLu <user>:<token> <ARTIFACTORY_URL>/<path>/<file>
The file is downloaded into $const->dap_tmp.
The extract method extracts tar archives into the staging apps directory using: /bin/tar --no-same-owner --extract --auto-compress --directory=<staging_apps> --file=<dap_tmp>/<archive>
Before extraction, it removes any existing staging directory: rm -fr <staging_apps>/<dir>
The upload method PUTs a file to Artifactory using Mojo::UserAgent.
Dap::Artifactory::Manifest is the main metadata updater. It requires: has release => ( is => 'rw', required => 1 );
It also has a status_xml path, defaulting to $const->dap_etc . '/status.xml'. The XML document is loaded lazily via XML::LibXML->load_xml.
The release URL is computed as: $self->tx->url . $self->tx->default_artifatory_path . '/' . $self->release
update calls: $self->update_products; $self->update_jdks; $self->update_jdklist; $self->save;
So it rewrites three sections of status.xml: 1. <products> 2. <jdks>/<jdk> 3. <jdks>/<jdklist>
Dap::Artifactory::Manifest expects at least three remote resources:
Fetched from <release-url>/products. Expected format: JSON array. Each product entry is converted to Dap::Artifactory::Manifest::Entry::Product.
Required product fields: upgradedue, name, technology, securitystatus, status, jdklist, version. Optional: releasedate.
Fetched from <release-url>/java/jdks. Expected format: JSON array. Each entry is converted to Dap::Artifactory::Manifest::Entry::Jdk.
Required JDK fields: location, upgradedue, securitystatus, status, name, version.
Fetched from <release-url>/java/jdklist. Expected format: plain text. Blank lines and comments (lines starting with #) are ignored. Each non-comment line is split on whitespace into list name and jdk name.
Example format: java87 java8-openjdk-401 / java87 java8-openjdk-402 / java1187 java11-openjdk-21
Each line becomes a Dap::Artifactory::Manifest::Entry::Jdklist.
The expected XML structure is approximately: <cluster><products><dap .../><tomcat .../><jboss .../></products><jdks><jdk .../><jdklist name="java87"><jdk name="java8-openjdk-401" priority="1" /></jdklist></jdks></cluster>
update_products removes the existing <products> node under <cluster> and recreates it. For each remote product, it creates an XML element named after the product technology and sets attributes: upgradedue, name, technology, securitystatus, status, jdklist, version.
update_jdks removes all existing <jdk> children under <cluster>/<jdks> and adds remote JDKs back with attributes: name, version, location, status, securitystatus, upgradedue.
update_jdklist removes all existing <jdklist> children under <cluster>/<jdks> and recreates them based on remote java/jdklist.
Dap::Artifactory::Manifest::Entry::Product represents one application server/product entry. Required fields: upgradedue, name, technology, securitystatus, status, jdklist, version. Optional: releasedate.
It uses constants from lib/Dap/Artifactory/Const.pm.
sub is_weblogic { my $self = shift; return $self->technology eq WEBLOGIC; }
Since WEBLOGIC is defined as dap, WebLogic product entries are expected to have technology: "dap".
For WebLogic: weblogic-<version>.tar.bz2. For other products: <version>.tar.gz.
There is a bug in the non-WebLogic case: : $_->version . '.tar.gz' should be : $self->version . '.tar.gz'. As written, $_ may be undefined.
For WebLogic: weblogic/weblogic-<version>.tar.bz2. For others: <technology>/<version>.tar.gz. Examples: weblogic/weblogic-14.1.1.0.240628.tar.bz2, tomcat/tomcat-9.0.71.tar.gz, jboss/jboss-eap-7.4.17.tar.gz.
Dap::Artifactory::Manifest::Entry::Jdk represents one JDK. Required fields: location, upgradedue, securitystatus, status, name, version.
Archive naming: sub archive { shift->location . '.tar.gz' }. Artifactory path: sub af_file { 'java/' . shift->archive }. So location openjdk1.17.0_10 maps to java/openjdk1.17.0_10.tar.gz.
Installation and staging paths are based on $const: absolute_install_name => $const->dap_apps . '/' . location, absolute_staging_name => $const->staging_apps . '/' . location.
Dap::Artifactory::Manifest::Entry::Jdklist is a small value object with fields: list, name, priority. It represents one mapping from a JDK list name to a JDK name, e.g. list=>'java87', name=>'java8-openjdk-401', priority=>1.
Dap::Artifactory::Const exports technology constants: sub WEBLOGIC { 'dap' }; sub TOMCAT { 'tomcat' }; sub JBOSS { 'jboss' }; sub JAVA { 'java' };
The important detail is that WebLogic is represented as technology value dap, not weblogic. This matters in Manifest/Entry/Product.pm, Artifactory.pm, and Dapfile.pm.
Dap::Artifactory::Dapfile is intended to read a .dap JSON file from a directory. process($dir) sets $self->file(path("$dir/.dap")). If the file does not exist, it logs an error.
If it exists: 1. Slurps JSON. 2. Reads the technology key. 3. Normalises it with lc(trim(...)). 4. Sets a selector: tomcat => 'tomcat', dap/WebLogic => 'dap', jboss => 'jboss'. 5. Constructs a Dap::Artifactory::Dapfile::Generic object.
Example Tomcat metadata: { "technology":"tomcat", "name":"Tomcat 9.0.71", "status":"Unsupported", "version":"tomcat-9.0.71", "jdklist":"java1187", "securitystatus":"Insecure High", "upgradedue":"2023-09-12", "releasedate":"2023-06-12" }
There is also a JDK example but it contains invalid JSON syntax: "location"="openjdk1.17.0_10" should be "location": "openjdk1.17.0_10".
Dap::Artifactory::Dapfile::Generic updates an XML status file based on .dap metadata. Required attributes: jdklist, name, releasedate, securitystatus, status, technology, upgradedue, version, selector.
It currently uses a hard-coded XML file: my $filename = '/home/nedevala/status.xml'; — this should use $const->dap_etc . '/status.xml' as suggested by the commented code.
It searches for an existing product node, updates it if found (jdklist, securitystatus, status, upgradedue), or creates a new element if not found. It then pretty-prints and writes the XML file.
This is a narrower, local-metadata-based alternative to the release-manifest approach in lib/Dap/Artifactory/Manifest.pm.
Dap::Artifactory::Dapfile::Jdk defines a data model for JDK .dap metadata. Required fields: technology, name, priority, jdklist, version, location, status, securitystatus, upgradedue, releasedate.
However, the process method is empty — JDK-specific .dap processing is not implemented yet.
Dap::Artifactory::PrettyPrint is a vendored copy of XML::LibXML::PrettyPrint. It strips insignificant whitespace, indents XML nodes, and pretty-prints documents before saving. Used by Manifest.pm and Dapfile/Generic.pm.
list.json is an example Artifactory storage API response for the WebLogic directory (dist-private-local/com/db/dap/weblogic). Each file entry includes uri, size, lastModified, folder, sha1, sha2.
This confirms the archive naming convention: weblogic-<version>.tar.bz2. For example: /weblogic-14.1.1.0.240628.tar.bz2, /weblogic-14.1.1.0.231220.tar.bz2, /weblogic-12.2.1.4.231010.tar.bz2.
Based on all files, the project can be specified as follows.
The system manages binary releases stored in Artifactory and updates destination hosts' metadata and staging areas. It supports: WebLogic (constant 'dap', XML element <dap>, archive weblogic-<version>.tar.bz2), Tomcat (constant 'tomcat', archive <version>.tar.gz), JBoss (constant 'jboss', archive <version>.tar.gz), Java/JDK (constant 'java', XML element <jdk>, archive <location>.tar.gz).
Required environment variables: ARTIFACTORY_USER, ARTIFACTORY_TOKEN, ARTIFACTORY_URL. Default repository path: /dist-private-local/com/db/dap. Storage API check path: <ARTIFACTORY_URL>/api/storage/dist-private-local/com/db/dap/<file>. Download path: <ARTIFACTORY_URL>/dist-private-local/com/db/dap/<file>.
A release should live under /dist-private-local/com/db/dap/<release>. The following files are expected: <release>/products (JSON array), <release>/java/jdks (JSON array), <release>/java/jdklist (plain text).
Given a valid release and local XML file: 1. Load status.xml. 2. Remove existing <products> under <cluster>. 3. Create a new <products> element. 4. Add one child element per product. 5. Remove existing <jdk> children under <cluster>/<jdks>. 6. Add one <jdk> element per remote JDK. 7. Remove existing <jdklist> children under <cluster>/<jdks>. 8. Add one <jdklist> per remote JDK list. 9. Pretty-print the XML. 10. Save status.xml.
For each product: if already installed or staged, skip. Otherwise check Artifactory, download into $const->dap_tmp. If WebLogic: move archive into $const->staging_wltarballs. If Tomcat/JBoss: extract into $const->staging_apps and delete temporary archive.
For each JDK: ignore entries where location is 'unknown'. If already installed or staged, skip. Otherwise check Artifactory, download, extract into $const->staging_apps, delete temporary archive.
If force_download is enabled, the installed/staged checks are bypassed.
From artifactory-release: --release / -r (release path/name in Artifactory), --statusxml (local XML file to update). Example: artifactory-release --release release-2024-08 --statusxml /opt/dap/etc/status.xml
ARTIFACTORY_USER, ARTIFACTORY_TOKEN, ARTIFACTORY_URL (from lib/Dap/Artifactory/Tx.pm).
status.xml (from Manifest.pm), <directory>/.dap (from Dapfile.pm), and paths from Dap::Const: $const->dap_tmp, $const->dap_apps, $const->dap_etc, $const->staging_apps, $const->staging_wltarballs, $const->prog_curl. Note: Dap::Const is not provided in the submitted documents but is heavily relied on.
<release>/products, <release>/java/jdks, <release>/java/jdklist. Expected artifact locations: weblogic/weblogic-<version>.tar.bz2, tomcat/<version>.tar.gz, jboss/<version>.tar.gz, java/<location>.tar.gz.
The main output of artifactory-release and Manifest.pm is an updated status.xml.
Staged software under: $const->staging_apps/<product-version>, $const->staging_apps/<jdk-location>, $const->staging_wltarballs/weblogic-<version>.tar.bz2.
All major classes use Mojo::Log: artifactory-release, Artifactory.pm, Tx.pm, Dapfile.pm, Dapfile/Generic.pm, Manifest.pm.
The file declares package Dap::Artifatory { but the expected package from the path is Dap::Artifactory. This will cause load/use issues unless all callers use the misspelled name.
Manifest.pm has has release => ( is => 'rw', required => 1 ) but Artifactory.pm constructs it as Dap::Artifactory::Manifest->new with no arguments. That will fail. Either release needs to be supplied, or the manifest class should support a local-only mode.
: $_->version . '.tar.gz' should be : $self->version . '.tar.gz'. As written, $_ may be undefined.
$self->tx->extract(dir => $p->verison, ...) should be $p->version.
The code checks file_exist('products') (resolves to /dist-private-local/com/db/dap/products) but fetches from $self->_release_url . '/products' (resolves to /dist-private-local/com/db/dap/<release>/products). The check probably should include $self->release.
In Dapfile/Generic.pm: my $filename = '/home/nedevala/status.xml' — should use $const->dap_etc . '/status.xml' as suggested by the commented code.
In Dapfile.pm and Dapfile/Jdk.pm, the sample JDK JSON contains "location"="openjdk1.17.0_10" (invalid syntax) — should be "location": "openjdk1.17.0_10".
In Tx.pm, system(@cmd) is called but the return code is not checked. A production version should verify system(@cmd) == 0 and confirm the downloaded file exists with non-zero size.
In Tx.pm, system("@cmd") (string form) is used for rm -fr. This invokes the shell. It would be safer to use the list form system(@cmd) or a Perl filesystem library.
In Manifest.pm the appendChild call is inside the for my $attr loop. Appending the same node repeatedly is semantically wrong (though it doesn't duplicate in libxml2). The append should happen after the loop.
Manifest.pm assumes <cluster> and <jdks> nodes exist. If they are missing, methods like getChildrenByTagName('jdk') will fail.
Remote files (products, java/jdks, .dap) are assumed to contain valid JSON with all required fields. Moo required attributes will catch missing fields but error reporting may not be user-friendly.
artifactory-release --release RELEASE --statusxml STATUS_XML
1. ARTIFACTORY_USER, ARTIFACTORY_TOKEN, and ARTIFACTORY_URL are set. 2. STATUS_XML exists and has a <cluster> root containing <products> and <jdks>. 3. Artifactory contains: /dist-private-local/com/db/dap/RELEASE/products, /dist-private-local/com/db/dap/RELEASE/java/jdks, /dist-private-local/com/db/dap/RELEASE/java/jdklist.
1. Validate that the release exists. 2. Load remote product list. 3. Load remote JDK list. 4. Load remote JDK-list mappings. 5. Rewrite relevant sections of STATUS_XML. 6. Pretty-print and save STATUS_XML.
A separate deployment command or object should: 1. Load the same release manifest. 2. For each product and JDK: determine expected Artifactory archive and local install/staging paths. Skip if already installed/staged unless forced. Download missing archives. Extract or stage them.
WebLogic: weblogic/weblogic-<version>.tar.bz2. Tomcat: tomcat/<version>.tar.gz. JBoss: jboss/<version>.tar.gz. JDK: java/<location>.tar.gz.
artifactory-release: CLI script. Validates release exists and updates status.xml.
lib/Dap/Artifactory.pm: Intended main downloader/stager for WebLogic, Tomcat, JBoss, and JDKs. Contains package-name typo.
lib/Dap/Artifactory/Dapfile.pm: Reads .dap JSON metadata and dispatches to a processor.
lib/Dap/Artifactory/Dapfile/Generic.pm: Updates product entries in status.xml from .dap metadata. Uses hard-coded XML path.
lib/Dap/Artifactory/Dapfile/Jdk.pm: Placeholder model for JDK .dap metadata. Processing not implemented.
lib/Dap/Artifactory/PrettyPrint.pm: Vendored XML pretty-printer used before saving XML.
lib/Dap/Artifactory/Tx.pm: Artifactory transaction layer: existence check, download, upload, extract.
lib/Dap/Artifactory/Manifest.pm: Core release manifest reader and status.xml updater.
lib/Dap/Artifactory/Manifest/Entry/Product.pm: Product metadata object and archive/path naming logic. Contains bug using $_.
lib/Dap/Artifactory/Manifest/Entry/Jdk.pm: JDK metadata object and archive/path naming logic.
lib/Dap/Artifactory/Manifest/Entry/Jdklist.pm: JDK-list mapping object.
lib/Dap/Artifactory/Const.pm: Defines technology constants.
list.json: Example Artifactory storage API response for WebLogic artifacts. Confirms archive naming and repository layout.