PerlOnJava supports three types of Perl modules:
- Pure Perl modules (.pm files) - No Java code needed
- Java-implemented modules (via XSLoader) - Perl modules that load Java implementations, replacing XS/C modules
- Built-in modules (in GlobalContext) - Internal PerlOnJava modules available at startup (e.g., UNIVERSAL)
Most CPAN module ports should use type #2 (XSLoader). Type #3 is only for internal PerlOnJava functionality.
- Pure Perl modules:
src/main/perl/lib/ - Java implementations:
src/main/java/org/perlonjava/perlmodule/
Pure Perl modules can be used directly or with minimal changes. Example from if.pm:
package if;
use strict;
sub import { shift; unshift @_, 1; goto &work }
sub unimport { shift; unshift @_, 0; goto &work }Java implementations replace Perl XS modules. They extend PerlModuleBase and are loaded via XSLoader::load().
XSLoader maps Perl module names to Java class names:
- Perl module:
DBI→ Java class:org.perlonjava.runtime.perlmodule.Dbi - Perl module:
Text::CSV→ Java class:org.perlonjava.runtime.perlmodule.Text_CSV - Perl module:
My::Module→ Java class:org.perlonjava.runtime.perlmodule.My_Module
Rules:
- Package: Always
org.perlonjava.runtime.perlmodule - Class name: Perl module name with
::replaced by_ - First letter capitalized (Java convention)
package org.perlonjava.runtime.perlmodule;
public class Dbi extends PerlModuleBase {
public Dbi() {
super("DBI", false);
}
// Called by XSLoader::load('DBI')
public static void initialize() {
Dbi dbi = new Dbi();
dbi.registerMethod("connect", null);
dbi.registerMethod("prepare", null);
// Register other methods...
}
// Implement methods
public static RuntimeList connect(RuntimeArray args, int ctx) {
// Implementation...
}
}In your Perl module, load the Java implementation:
package My::Module;
use strict;
use warnings;
our $VERSION = '1.00';
# Load Java implementation
require XSLoader;
XSLoader::load('My::Module', $VERSION);
# Pure Perl methods can call Java methods
sub helper_method {
my ($self, @args) = @_;
return $self->java_implemented_method(@args);
}
1;Users just use the module normally:
use My::Module;
my $obj = My::Module->new();
$obj->method();The XSLoader mechanism is completely transparent to end users.
In your Java class's initialize() method, register all methods:
public static void initialize() {
MyModule module = new MyModule();
module.registerMethod("method_name", null);
module.registerMethod("perl_name", "java_method_name", null);
}module.defineExport("EXPORT", "function1", "function2");
module.defineExport("EXPORT_OK", "optional_function");
module.defineExportTag("group", "function1", "function2");- First parameter: RuntimeArray containing arguments
- Second parameter: Context type (void/scalar/list)
Example:
public static RuntimeList method_name(RuntimeArray args, int ctx) {
RuntimeHash self = args.get(0).hashDeref();
String param1 = args.get(1).toString();
return new RuntimeList(new RuntimeScalar(result));
}- Return
RuntimeListcontaining results - For scalar context: return single-element list
- For list context: return multi-element list
- For void context: return empty list
There are two ways to register Java-implemented modules:
Only for internal PerlOnJava modules that need to be available immediately at startup (e.g., UNIVERSAL, CORE functions).
Register in GlobalContext.java:
// Initialize built-in Perl classes
DiamondIO.initialize(compilerOptions);
Universal.initialize();Do not use this approach for regular CPAN-style modules.
This is the standard approach for porting modules. Use XSLoader in your Perl module:
package DBI;
use strict;
use warnings;
our $VERSION = '1.643';
# Load Java implementation
require XSLoader;
XSLoader::load('DBI', $VERSION);
# Pure Perl methods
sub do {
my ($dbh, $statement, $attr, @params) = @_;
my $sth = $dbh->prepare($statement, $attr) or return undef;
$sth->execute(@params) or return undef;
my $rows = $sth->rows;
($rows == 0) ? "0E0" : $rows;
}
1;When XSLoader::load('DBI') is called:
- XSLoader looks for the Java class
org.perlonjava.runtime.perlmodule.Dbi - Calls the static
initialize()method - Registers all methods defined in the Java class
This is transparent to users - they just use DBI and it works.
The DBI module demonstrates a complete port using XSLoader:
- Perl module (
DBI.pm):
package DBI;
use strict;
use warnings;
our $VERSION = '1.643';
# Load Java implementation
require XSLoader;
XSLoader::load('DBI', $VERSION);
# Pure Perl helper method
sub do {
my ($dbh, $statement, $attr, @params) = @_;
my $sth = $dbh->prepare($statement, $attr) or return undef;
$sth->execute(@params) or return undef;
my $rows = $sth->rows;
($rows == 0) ? "0E0" : $rows;
}
1;- Java implementation (
org/perlonjava/perlmodule/Dbi.java):
public class Dbi extends PerlModuleBase {
public Dbi() {
super("DBI", false);
}
// Called by XSLoader
public static void initialize() {
Dbi dbi = new Dbi();
dbi.registerMethod("connect", null);
dbi.registerMethod("prepare", null);
dbi.registerMethod("execute", null);
// ... register other methods
}
// Implementation of connect method
public static RuntimeList connect(RuntimeArray args, int ctx) {
RuntimeHash dbh = new RuntimeHash();
String jdbcUrl = args.get(1).toString();
dbh.put("Username", new RuntimeScalar(args.get(2).toString()));
// ... JDBC connection logic
return dbh.createReference().getList();
}
}Key points:
- DBI.pm calls
XSLoader::load('DBI')to load the Java implementation - Java class is in
org.perlonjava.runtime.perlmodule.Dbi(naming convention) initialize()method registers all Java-implemented methods- Pure Perl methods (like
do()) can call Java methods (likeprepare(),execute())
- Keep pure Perl code for simple functionality
- Use Java implementation for:
- Performance-critical code
- System interactions
- Database connectivity
- Complex data structures
- Maintain Perl calling conventions
- Handle both scalar and list contexts
- Properly manage resources and error states
- Follow PerlOnJava naming conventions
- Create test files in
src/test/resources/ - Write Java tests in
src/test/java/ - Test both pure Perl and Java implementations
- Verify compatibility with original Perl module
- Perl version requirements
- Java version requirements
- PerlOnJava version compatibility matrix
- Perl exceptions map to Java RuntimeExceptions
- Standard error patterns follow Perl conventions
- Error propagation maintains stack traces
- Use die() for Perl-style exceptions
- Propagate Java exceptions with proper context
- Maintain error state in $@ variable
- Use Java for performance-critical code paths
- Pure Perl for maintainability and compatibility
- Hybrid approach for balanced solutions
- Minimize context switches
- Cache frequently used values
- Use native Java collections where appropriate
- Release resources promptly
- Monitor object lifecycles
- Follow Java garbage collection best practices
- Module loading failures
- Method registration problems
- Context handling errors
- Enable verbose logging
- Use Java debugger for implementation code
- Perl debugging for pure Perl portions
- Verify path configuration
- Check initialization sequence
- Validate export definitions
- Analyze module dependencies
- Identify XS/C components
- Document API requirements
- Unit test coverage
- Integration tests
- Performance benchmarks
- API documentation
- Migration notes
- Version compatibility
- Functionality verification
- Performance validation
- Compatibility testing