| PXP documentation | Contained in the PXP distribution. |
PXP::Plugin - Plugin class definition (used only in the internal registry)
<?xml version="1.0"?>
<plugin id="IMC::WebApp::TestPlugin" name="Test plugin" version="0.1" provider-name='IDEALX'>
</plugin>
A plugin groups together a set of extensions and/or extension-points.
A PXP::Plugin represents such a container as it is read and its
content loaded into the system. A common interface is provided to
access the configuration descriptors and the actual implementation
that has been loaded into the system.
NOTE: a PXP::Plugin object is NOT a plugin.
A real plugin, is just a set of Perl modules loaded according to the 'plugin.xml' descriptor file.
Plugin dependencies are supported with the <require> tag.
We support only a subset of the Eclipse plugin model.
Load a perl class ($class) and instantiate a new object of this class, with optional arguments for the object initializer (@args)
Return the new instance or undef if the class could not be loaded
Basic accessors for plugin properties.
Accessor for the 'resourceDir' attribute. 'resourceDir' points to the directory containing the resources bundled with the plugin, such as config files, templates, etc.
This method is called by the PluginRegistry as it loads the plugin.
PXP::PluginRegistry, PXP::ExtensionPoint, PXP::Extension
See the article on eclipse.org describing the plugin architecture : http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
| PXP documentation | Contained in the PXP distribution. |
package PXP::Plugin;
use strict; use warnings; use PXP::ExtensionPoint; use PXP::Extension; use XML::XPath; use File::Spec; use Cwd; use Log::Log4perl qw(get_logger); my $logger = get_logger(__PACKAGE__); sub new { my $class = shift; my $self = { name => '', id => '', version => 0, provider_name => '', extensions => {}, extension_points => {}, dependencies => [] }; bless $self, $class; return $self->init(@_); } sub init { my $self = shift; my %opts = @_; # TODO : warn if no directory $self->directory($opts{'directory'}); my $filename = $opts{'filename'} || ''; if ($filename) { my $config = $self->loadLocalizedResource($filename) || die "cannot find config for plugin"; my $xp = XML::XPath->new(xml => $config); $self->loadFromXML($xp); } return $self; } sub loadFromXML { my $self = shift; my $xp = shift; # FIXME: Check all values # Set plugin params; my @nodes = $xp->findnodes('/plugin'); unless (@nodes) { $logger->error("No plugin defined in xml config file. Loading aborted."); return undef; } my $node = shift @nodes; # Define parameters $self->id($node->getAttribute('id')); $self->class($node->getAttribute('class')); $self->name($node->getAttribute('name')); $self->version($node->getAttribute('version')); $self->providerName($node->getAttribute('provider-name')); # Handle dependencies my @deps = $xp->findnodes('/plugin/require'); $self->addDependencies(map ($_->getAttribute('plugin'), @deps)); # Handle libraries my @libs = $xp->findnodes('/plugin/runtime/library'); $self->addLibraries(map ($_->getAttribute('name'), @libs)); # Save XML node $self->{'xml'} = $xp; # Add the plugin directory to Perl search path unshift @INC, $self->directory(); # Extensions and ExtensionPoints are no longer instantiated here. # This must be done by _instantiateExtensions(), after dependencies # have been loaded. } sub instantiateExtensions { my $self = shift; my $xp = $self->{'xml'}; my $node; # Create extension points foreach $node ($xp->findnodes('/plugin/extension-point')) { my $id = $node->getAttribute('id') || die "bummer"; my $class = $node->getAttribute('class') || $id; # FIXME: if nothing was specified, do not force a class load my $name = $node->getAttribute('name'); my $version = $node->getAttribute('version'); my $obj; # Instantiate it # FIXME: Add test on values my $extpt = PXP::ExtensionPoint->new($self); if (_loadClass($class)) { $obj = $class->new($self); # support for Aurelien's approach (an extension point 'IS-A' PXP::ExtensionPoint) if ($obj->isa('PXP::ExtensionPoint')) { $logger->warn("$id should not be an PXP::ExtensionPoint !"); undef $extpt; $extpt = $obj; } $extpt->object($obj); } $extpt->id($id); $extpt->class($class); $extpt->name($name); $extpt->version($version); $self->_storeExtensionPoint($extpt); } # Add extensions foreach $node ($xp->findnodes('/plugin/extension')) { # Get informations my $id = $node->getAttribute('id') || die "bummer"; my $point = $node->getAttribute('point') || die "bummer"; my $class = $node->getAttribute('class') || $id; my $name = $node->getAttribute('name'); my $version = $node->getAttribute('version'); my $obj; my $ext = PXP::Extension->new($self); $ext->id($id); $ext->class($class); $ext->name($name); $ext->version($version); $ext->point($point); $ext->node($node); $self->_storeExtension($ext); } }
sub instantiateExtension { my $self = shift; my $class = shift; my @args = shift; # Instantiate if (PXP::Plugin::_loadClass($class)) { return $class->new(@args); } else { die "Could not instanciate class $class"; return undef; } }
sub name { my $self = shift; if (@_) { $self->{name} = shift; return $self; } else { return $self->{name}; } } sub id { my $self = shift; if (@_) { $self->{id} = shift; return $self; } else { return $self->{id}; } } sub class { my $self = shift; if (@_) { $self->{class} = shift; return $self; } else { return $self->{class}; } } sub version { my $self = shift; if (@_) { $self->{version} = shift; return $self; } else { return $self->{version}; } } sub providerName { my $self = shift; if (@_) { $self->{provider_name} = shift; return $self; } else { return $self->{provider_name}; } }
sub resourceDir { my $self = shift; return $self->directory(@_); } sub xml { my $self = shift; return $self->{xml}; } sub directory { my $self = shift; if (@_) { $self->{directory} = shift; return $self; } else { return $self->{directory}; } } sub fullDirectory { my $self = shift; return File::Spec->catdir(getcwd(), $self->{directory}); } sub resource { my $self = shift; return File::Spec->catfile($self->directory(), @_); } sub fullResource { my $self = shift; return File::Spec->catfile($self->fullDirectory(), @_); } sub getDependencies { my $self = shift; return $self->{'dependencies'}; } sub addDependencies { my $self = shift; push @{$self->{'dependencies'}}, @_; return $self; } sub libraries { my $self = shift; return $self->{'libraries'}; } sub addLibraries { my $self = shift; push @{$self->{'libraries'}}, @_; return $self; } sub _loadClass { my $class = shift; eval "require $class"; if ($@) { warn "Could not load $class: " .$@; return undef; } return 1; } sub _storeExtensionPoint { my $self = shift; my $ext = shift; $self->{'extension_points'}->{$ext->id()} = $ext; return $ext; } sub getExtensionPoint { my $self = shift; my $id = shift; return $self->{'extension_points'}->{$id}; } sub getAllExtensionPoints { my $self = shift; if ($self->{'extension_points'}) { return (values %{$self->{'extension_points'}}); } } sub _storeExtension { my $self = shift; my $ext = shift; $self->{'extensions'}->{$ext->id()} = $ext; # remember extension declaration order in the plugin.xml file push @{$self->{'extensions_array'}}, $ext; return $ext; } sub getExtension { my($self) = shift; my($id) = shift; return($self->{'extensions'}->{$id}); } # Return an _ordered_ list of extensions for the plugin, so we can control extension registration order # (this is important for constructing the MainPipeline, and make sure the RequestManager gets called first) sub getAllExtensions { my $self = shift; if ($self->{'extensions_array'}) { return @{$self->{'extensions_array'}}; } } use FileHandle; sub getResourceHandle { my $self = shift; my $resourceId = shift; if (! $self->isa('PXP::Plugin')) { $self = PXP::PluginRegistry::getPlugin($self) || die "cannot find which plugin I am !"; } my $filename = File::Spec->catfile($self->resourceDir(), $resourceId); $logger->debug("resource for " . $self->id() . " should be in " . $filename); return new FileHandle $filename; } # almost the same code as above, except we try to find a localized property file # property files are named with the locale and after the original filename, # but with a .properties extension (instead of the filename original extension, like .xml) sub getPropertyFileHandle { my $self = shift; my $resourceId = shift; my $dir; if (! $self->isa('PXP::Plugin')) { $self = PXP::PluginRegistry::getPlugin($self) || die "cannot find which plugin I am !"; } $dir = $self->resourceDir(); use File::Basename; my ($filenamePrefix, $dummy, $dummy2) = fileparse($resourceId, qr/\..*/); my $filename = File::Spec->catfile($dir, $filenamePrefix); $logger->debug('looking for property file ' . $filenamePrefix . '.properties'); my $pfname = PXP::I18N::getPropertyFile($filename); return new FileHandle $pfname; } sub loadResource { my $self = shift; my $resourceId = shift; $logger->debug("loading resource $resourceId"); my $handle = $self->getResourceHandle($resourceId); local $/; return <$handle>; } # same as above, but we look for a property file # and substitute with the '%property' pattern (same as in Eclipse) sub loadLocalizedResource { my $self = shift; my $resourceId = shift; $logger->debug("loading localized resource $resourceId"); my $pfh = $self->getPropertyFileHandle($resourceId); unless ($pfh) { my $rfh = $self->getResourceHandle($resourceId); local $/; return <$rfh>; } my $rfh = $self->getResourceHandle($resourceId); return PXP::I18N::loadNLocalize($rfh, $pfh); }
1;