#!/usr/bin/perl package JMX::Jmx4Perl::Agent::Jolokia::ArtifactHandler; =head1 NAME JMX::Jmx4Perl::Agent::ArtifactHandler - Handler for extracting and manipulating Jolokia artifacts =head1 DESCRIPTION This module is responsible for mangaging a singe JAR or WAR Archive. It requires L for proper operation. I.e. this module can =over =item * Extract jolokia-access.xml and web.xml from WAR/JAR archives =item * Check for the esistance of jolokia-access.xml =item * Update web.xml for WAR files =back =cut use Data::Dumper; use strict; use vars qw($HAS_ARCHIVE_ZIP $GLOBAL_ERROR); BEGIN { $HAS_ARCHIVE_ZIP = eval "require Archive::Zip; Archive::Zip->import(qw(:ERROR_CODES)); 1"; if ($HAS_ARCHIVE_ZIP) { Archive::Zip::setErrorHandler( sub { $GLOBAL_ERROR = join " ",@_; chomp $GLOBAL_ERROR; } ); } } =head1 METHODS =over 4 =item $handler = JMX::Jmx4Perl::Agent::Jolokia::ArtifactHandler->new(...) Create a new handler with the following options: file => $file : Path to archive to handle logger => $logger : Logger to use meta => $meta : Jolokia-Meta handler to extract the type of an archive =cut sub new { my $class = shift; my %args = @_; my $file = $args{file}; my $self = { file => $file, logger => $args{logger}, meta => $args{meta}}; bless $self,(ref($class) || $class); $self->_fatal("No Archive::Zip found. Please install it for handling Jolokia archives.") unless $HAS_ARCHIVE_ZIP; $self->_fatal("No file given") unless $file; $self->_fatal("No such file $file") unless -e $file; return $self; } =item $info = $handler->info() Extract information about an archive. Return value is a has with the following keys: "version" Agent's version "type" Agent type (war, osgi, osgi-bundle, mule, jdk6) "artifactId" Maven artifact id "groupId" Maven group Id =cut sub info { my $self = shift; my $file = $self->{file}; my $jar = $self->_read_archive(); my @props = $jar->membersMatching('META-INF/maven/org.jolokia/.*?/pom.properties'); $self->_fatal("Cannot extract pom.properties from $file") unless @props; for my $prop (@props) { my ($content,$status) = $prop->contents; $self->_fatal("Cannot extract pom.properties: ",$GLOBAL_ERROR) unless $status eq AZ_OK(); my $ret = {}; for my $l (split /\n/,$content) { next if $l =~ /^\s*#/; my ($k,$v) = split /=/,$l,2; $ret->{$k} = $v; } $self->_fatal("$file is not a Jolokia archive") unless $ret->{groupId} eq "org.jolokia" ; my $type; if ($self->{meta}->initialized()) { $type = $self->{meta}->extract_type($ret->{artifactId}); } else { $type = $self->_detect_type_by_heuristic($ret->{artifactId}); } if ($type) { $ret->{type} = $type; return $ret; } } return {}; } =item $handler->add_policy($policy) Add or update the policy given as string to this archive. Dependening on whether it is a WAR or another agent, it is put into the proper place For "war" agents, this is F, for all others it is F =cut sub add_policy { my $self = shift; my $policy = shift; my $file = $self->{file}; $self->_fatal("No such file $policy") unless -e $policy; my $jar = $self->_read_archive(); my $path = $self->_policy_path; my $existing = $jar->removeMember($path); my $res = $jar->addFile($policy,$path); $self->_fatal("Cannot add $policy to $file as ",$path,": ",$GLOBAL_ERROR) unless $res; my $status = $jar->overwrite(); $self->_fatal("Cannot write $file: ",$GLOBAL_ERROR) unless $status eq AZ_OK(); $self->_info($existing ? "Replacing existing policy " : "Adding policy ","[em]",$path,"[/em]",$existing ? " in " : " to ","[em]",$file,"[/em]"); } =item $handler->remove_policy() Remove a policy file (no-op, when no policy is present) =cut sub remove_policy { my $self = shift; my $file = $self->{file}; my $jar = $self->_read_archive(); my $path = $self->_policy_path; my $existing = $jar->removeMember($path); if ($existing) { my $status = $jar->overwrite(); $self->_fatal("Cannot write $file: ",$GLOBAL_ERROR) unless $status eq AZ_OK(); $self->_info("Removing policy","[em]",$path,"[/em]"," in ","[em]",$file,"[/em]"); } else { $self->_info("No policy found, leaving ","[em]",$file,"[/em]"," untouched."); } } =item $handler->has_policy() Returns true (i.e. the path to the policy file) if a policy file is contained, C otherwise. =cut sub has_policy { my $self = shift; my $jar = $self->_read_archive(); my $path = $self->_policy_path; return $jar->memberNamed($path) ? $path : undef; } =item $handler->get_policy() Get the policy file as string or C if no policy is contained. =cut sub get_policy { my $self = shift; my $jar = $self->_read_archive(); my $path = $self->_policy_path; return $jar->contents($path); } =item $handler->extract_webxml() Extract F from WAR agents, for other types, a fatal error is raised. Return value is a string containing the web.xml. =cut sub extract_webxml { my $self = shift; my $type = $self->type; $self->_fatal("web.xml can only be read from 'war' archives (not '",$type,"')") unless $type eq "war"; my $jar = $self->_read_archive(); return $jar->contents("WEB-INF/web.xml"); } =item $handler->update_webxml($webxml) Update F in WAR agents, for other types, a fatal error is raised. Return value is a string containing the web.xml. C<$webxml> is the descriptor as a string. =cut sub update_webxml { my $self = shift; my $webxml = shift; my $type = $self->type; $self->_fatal("web.xml can only be updated in 'war' archives (not '",$type,"')") unless $type eq "war"; my $jar = $self->_read_archive(); $jar->removeMember("WEB-INF/web.xml"); my $res = $jar->addString($webxml,"WEB-INF/web.xml"); $self->_fatal("Cannot update WEB-INF/web.xml: ",$GLOBAL_ERROR) unless $res; my $status = $jar->overwrite(); $self->_fatal("Cannot write ",$self->{file},": ",$GLOBAL_ERROR) unless $status eq AZ_OK(); $self->_info("Updated ","[em]","web.xml","[/em]"," for ",$self->{file}); } =item $handler->type() Return the agent's type, which is one of "war", "osgi", "osgi-bundle", "mule" or "jdk6" =cut sub type { my $self = shift; my $info = $self->info; return $info->{type}; } =back =cut # ======================================================================== sub _detect_type_by_heuristic { my $self = shift; my $artifact_id = shift; return { "jolokia-osgi" => "osgi", "jolokia-mule" => "mule", "jolokia-osgi-bundle" => "osgi-bundle", "jolokia-jvm-jdk6" => "jdk6", "jolokia-jvm" => "jvm", "jolokia-war" => "war" }->{$artifact_id}; } sub _read_archive { my $self = shift; my $file = $self->{file}; my $jar = new Archive::Zip(); my $status = $jar->read($file); $self->_fatal("Cannot read content of $file: ",$GLOBAL_ERROR) unless $status eq AZ_OK(); return $jar; } sub _policy_path { my $self = shift; return ($self->type eq "war" ? "WEB-INF/classes/" : "") . "jolokia-access.xml"; } sub _fatal { my $self = shift; $self->{logger}->error(@_); die "\n"; } sub _info { my $self = shift; $self->{logger}->info(@_); } =head1 LICENSE This file is part of jmx4perl. Jmx4perl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by The Free Software Foundation, either version 2 of the License, or (at your option) any later version. jmx4perl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with jmx4perl. If not, see . A commercial license is available as well. Please contact roland@cpan.org for further details. =head1 AUTHOR roland@cpan.org =cut 1;