PerlOnJava supports three types of Perl modules:
- Pure Perl modules (.pm files)
- Java-implemented modules (replacing XS/C modules)
- Hybrid modules (combining Perl and Java implementations)
A hybrid module typically consists of a .pm file containing Perl code and a corresponding Java class that implements performance-critical or system-level functionality. This approach lets you leverage the best of both languages - Perl's expressiveness and Java's performance.
- 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 implement the module's functionality in Java.
Example from DBI module:
public class Dbi extends PerlModuleBase {
public Dbi() {
super("DBI", false);
}
public static void initialize() {
Dbi dbi = new Dbi();
dbi.registerMethod("connect", null);
dbi.registerMethod("prepare", null);
// Register other methods...
}
}
use ModuleName;
require "ModuleName.pm";
- Extend PerlModuleBase:
public class MyModule extends PerlModuleBase {
public MyModule() {
super("My::Module");
}
}
- Register methods:
protected void initialize() {
registerMethod("method_name", null);
registerMethod("perl_name", "java_name", null);
}
- Define exports:
defineExport("EXPORT", "function1", "function2");
defineExport("EXPORT_OK", "optional_function");
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
RuntimeList
containing results - For scalar context: return single-element list
- For list context: return multi-element list
- For void context: return empty list
After implementing a module, register it in GlobalContext.java
:
// Initialize built-in Perl classes
DiamondIO.initialize(compilerOptions);
Universal.initialize();
MyNewModule.initialize(); // Add your module here
This step ensures your module is initialized during PerlOnJava startup alongside other core modules.
The initialization sequence handles:
- Method registration
- Export definitions
- Global variable setup
- Module state initialization
The DBI module demonstrates a complete port:
- Pure Perl portion (
DBI.pm
):
package DBI;
use strict;
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;
}
- Java implementation (
Dbi.java
):
public class Dbi extends PerlModuleBase {
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()));
// Implementation...
return dbh.createReference().getList();
}
}
- 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