summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2011-05-17 21:28:20 +0200
committerJonas Smedegaard <dr@jones.dk>2011-05-17 21:28:20 +0200
commitce38b9acaafe15fd2ba8e94d72f009f62b2d05da (patch)
treeac18aaa38cb5ab29a9ccfadd632a5038a409fa60
Imported Upstream version 0.53
-rw-r--r--CHANGES176
-rw-r--r--Java.pm1191
-rw-r--r--Java.pod1162
-rw-r--r--Java/Array.pm634
-rw-r--r--Java/Callback.pm223
-rw-r--r--Java/Callback.pod406
-rw-r--r--Java/Class.pm511
-rw-r--r--Java/Handle.pm269
-rw-r--r--Java/JNI.pm41
-rw-r--r--Java/JNI.xs311
-rw-r--r--Java/JVM.pm449
-rw-r--r--Java/Makefile.PL357
-rw-r--r--Java/Object.pm547
-rw-r--r--Java/PerlInterpreter/Makefile.PL27
-rw-r--r--Java/PerlInterpreter/PerlInterpreter.pm17
-rw-r--r--Java/PerlInterpreter/PerlInterpreter.pod115
-rw-r--r--Java/PerlInterpreter/PerlInterpreter.xs95
-rw-r--r--Java/PerlInterpreter/t/01_init.t10
-rw-r--r--Java/PerlInterpreter/t/02_perl_interpreter.t146
-rw-r--r--Java/PerlInterpreter/t/Tests.pl8
-rw-r--r--Java/PerlNatives/Makefile.PL16
-rw-r--r--Java/PerlNatives/PerlNatives.pm7
-rw-r--r--Java/PerlNatives/PerlNatives.pod142
-rw-r--r--Java/PerlNatives/PerlNatives.xs222
-rw-r--r--Java/PerlNatives/t/01_init.t10
-rw-r--r--Java/PerlNatives/t/02_perl_natives.t118
-rw-r--r--Java/Portable.pm267
-rw-r--r--Java/Protocol.pm612
-rw-r--r--Java/Server.pm130
-rw-r--r--Java/jvm.def4
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaArray.java100
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCallback.java158
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java141
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCastException.java7
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaClass.java554
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaException.java7
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaHandle.java130
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java15
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java257
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlException.java25
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java107
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java231
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlObject.java73
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaProtocol.java879
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaServer.java337
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaServerThread.java69
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaThrown.java14
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java45
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java192
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUtils.java175
-rwxr-xr-xJava/typemap23
-rw-r--r--MANIFEST81
-rw-r--r--META.yml16
-rw-r--r--Makefile.PL299
-rw-r--r--README90
-rw-r--r--README.JNI177
-rw-r--r--TODO14
-rw-r--r--t/01_init.t51
-rwxr-xr-xt/02_primitives.t252
-rwxr-xr-xt/02_primitives_1_4.t72
-rwxr-xr-xt/03_objects.t157
-rwxr-xr-xt/04_members.t144
-rwxr-xr-xt/05_arrays.t265
-rwxr-xr-xt/06_static.t84
-rw-r--r--t/07_polymorph.t161
-rw-r--r--t/08_study.t117
-rwxr-xr-xt/09_usages.t70
-rwxr-xr-xt/10_1_shared_alone.t35
-rwxr-xr-xt/10_5_shared_fork.t72
-rw-r--r--t/10_6_shared_sim.t69
-rwxr-xr-xt/11_exceptions.t160
-rwxr-xr-xt/12_1_callbacks.t422
-rwxr-xr-xt/13_handles.t95
-rwxr-xr-xt/14_encoding.t70
-rwxr-xr-xt/15_native_doubles.t44
-rw-r--r--t/99_end.t19
-rw-r--r--t/no_const.classbin0 -> 231 bytes
-rwxr-xr-xt/no_const.java7
-rwxr-xr-xt/shared.java11
-rw-r--r--t/types.classbin0 -> 489 bytes
-rwxr-xr-xt/types.java20
81 files changed, 14836 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..be9d7e9
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,176 @@
+Revision history for Perl extension Inline::Java
+------------------------------------------------
+0.53 Sun Jan 9 08:15:06 EST 2011
+ - Removed PerlNatives extension from the build because it is unmaintained and broken.
+ It can still be enabled manually using the BUILD_PERL_NATIVES build configuration
+ option.
+ - Improved automatic casting.
+ - Fix with get_source_dir() using the DEBUGGER option. Thanks to Paul Frantz for the fix.
+ - Other minor bug and doc fixes.
+
+0.52 Sun Dec 17 20:46:51 EST 2006
+ - Fixed JNI on cygwin (many thanks to Eric Rybski for the patch)
+ - Improved installation. 'make java' is now performed automatically.
+ - Fixed problems with disappearing exceptions by localizing $@.
+ - Other minor bug fixes
+
+0.51 Tue May 23 20:40:07 EDT 2006
+ - Several major speed optimizations.
+ - Introduction of support for I/O mapping between Perl and Java (Inline::Java::Handle)
+ - Applied patches by Andrew Bruno and Tim Bunce for MAC OSX
+ - JNI fix for system property passing
+ (thanks to Brian Gugliemetti and Jason Stelzer)
+ - Added NATIVE_DOUBLES configuration option to avoid loss of precision
+ when passing double values between Perl and Java
+ - New interface for processing callbacks from java to perl.
+ - Added support for java.lang.CharSequence as a primitive type. Any
+ Perl scalar passed as a java.lang.CharSequence will instantiate
+ a java.lang.String on the Java side
+ - Added BUILD_JNI, BUILD_PERL_NATIVES, BUILD_PERL_INTERPRETER and JVM_LIB_TYPE build
+ configuration options to help with automated builds
+ - Added BIND configuration option. See docs for details.
+ Note: The Java JVM server noe listens on localhost by default (instead of 0.0.0.0).
+ - Other minor bug fixes
+
+0.50 Mon Jan 31 20:14:43 EST 2005
+ - Added HOST configuration option to access JVM server remotely
+ - Fixed bug with paths in Cygwin
+ - Fixed bug with regexp that was badly interpreted under the debugger
+ - Extended InlineJavaClassLoader from current thread ClassLoader to enable it to work
+ properly under Tomcat
+ - Include patch by Ihab Awad that allows running the JVM server inside Tomcat
+ - Fixed bug with longs over 32 bits in length and loosened boundary checking in numeric values
+ since it was not portable
+ - Fixed bug (found by Dean Thayer) with socket not closed properly in Java
+ - Other minor bug fixes
+
+0.49
+ - Added PerlInterpreter: require/eval Perl code directly from Java
+ - Reworked type casting: changes are NOT backwards compatible :(
+ - Callbacks can now be called in list context using "@function_name"
+ - Renamed PerlNatives stuff
+ - Added external command line tool to start/stop a SHARED_JVM server
+ - Applied JNI memory leak patch by Jeff Janes
+ - Removed exports from Inline::Java::Portable
+ - Split and updated documentation
+
+0.47 Sat Feb 14 10:00:00 EST 2004
+ - Fixed bugs in portability code and added HPUX, AIX and Solaris specifics
+ - Tweaked CLASSPATH directory order
+ - Changed port numbers for SHARED_JVM tests in order to avoid clashes
+ with running installations
+ - Made PerlNatives optional
+
+0.46 Web Feb 04 20:00:00 EST 2004
+ - Fixed Natives.xs to work with ExtUtils::ParseXS
+
+0.45 Fri Jan 30 20:00:00 EST 2004
+ - Fixed Makefile.PL arguments that were getting lost
+ - Fixed deprecated require call
+ - Fixed support for symbolic links in J2SDK directory
+ - Basic support for J2SDK 1.5
+ - Added new experimental feature: PerlNatives
+
+0.44 Sun Nov 23 15:47:06 EST 2003
+ - Callbacks from multiple threads are now supported
+ - Refactored (again...) studying/.jdat/cache stuff
+ - Added PRIVATE mode for use with SHARED_JVM
+ - Added DEBUGGER mode that launches jdb
+ - Fixed memory leak in JNI code (patch submitted by Dave Blob)
+
+0.43 Tue Oct 14 13:18:25 EDT 2003
+ - Restored $VERSION in each .pm file
+ - Inline::Java now formerly requires Perl 5.6
+
+0.42 Fri Sep 5 13:18:25 EDT 2003
+ - Fixed more CLASSPATH issues. CLASSPATH now works like this:
+ * CLASSPATH environment variable is global
+ * CLASSPATH configuration option is local to the user class loader
+ - Added method cache to increase performance and decrease reflection
+ API calls
+ - Altered and documented the study_classes() function
+ - Added EXTRA_JAVA_ARGS and EXTRA_JAVAC_ARGS config options
+
+0.41 Mon Jul 14 13:18:25 EDT 2003
+ - Fixed CLASSPATH bug
+ - Possibly (!) fixed test suite problems under heavy load
+
+0.40 Fri Apr 11 11:00:00 EST 2003
+ - Patch by Doug MacEachern to allow running under servlet engines.
+ - Patch by John Kinsley for handling empty arrays.
+ - Custom ClassLoader allows for better isolation of user code and
+ dynamic 'CLASSPATH' (multiple sections/clients are now totally
+ independant).
+ - Core Java code is now compiled separately (at install time) instead
+ of with the user code (for each script). This dramatically improves
+ build time.
+ - Default port number changed to 0 (next available port number).
+ This allows many multiple users to run concurrently without
+ clashes.
+ Note: The default port on systems where this feature is not
+ available has remained 7890.
+ Note: The default port for SHARED_JVM mode is now 7891.
+ - SHARED_JVM mode now calls release_JVM() automatically.
+ - UTF8 support
+ - User code can now be a public class and (optionally) inside a package.
+ - Callback classes have changed.
+ Note: These classes are now in the org.perl.inline.java package.
+ Note: PerlException has been renamed InlineJavaPerlException.
+ Note: If you presently use callbacks, your code may now no longer compile.
+ You will need to apply the changes listed above to fix it.
+ - study_classes now returns the package in which the studied classes
+ have been put.
+ - Many bug fixes and documentation corrections.
+
+0.33 Mon Jun 17 13:50:14 EDT 2002
+ - Improved and more flexible debug output
+ - Auto loading of thread libraries under Solaris
+ - Basic support for J2SDK 1.4
+ - Other minor bug fixes
+
+0.32 Sat Apr 6 11:45:06 EST 2002
+ - Restored compatibility with perl 5.005_03
+ - Other minor bug fixes
+
+0.31 Mon Feb 4 15:45:06 EDT 2002
+ - Exception handling (Perl can 'catch' Java exceptions)
+ - Callbacks to Perl from Java
+ - More complete test suite
+ - Improved documentation and related examples
+ - Improved installation script and directions
+ - Other minor bug fixes
+
+0.30 Mon Sep 17 15:45:06 EDT 2001
+ - JVM server is now multi-threaded
+ - Added 'SHARED_JVM' option for mod_perl support
+ - Beefed up test suite
+
+0.23 Thu Aug 30 08:41:11 EDT 2001
+ - Added support for multiple Inline sections using a special notation
+ in the CLASSPATH.
+ - Added the 'fix' Makefile.PL option au automatically fix the Makefile
+ for Win95/98/Me.
+
+0.22 Fri Jun 1 13:31:35 EDT 2001
+ - Fixed up Inline::Java to work with Inline 0.41
+ - Added support for java.lang.Number as a primitive numeric type. Any
+ Perl scalar passed as a java.lang.Number will instantiate
+ a java.lang.Double on the Java side.
+
+0.21 Tue May 8 11:32:28 EDT 2001
+ - Added 'studying' of external Java classes
+ - Added support for default no-arg constructors for public classes
+ - Caching for class information
+ - Added error message stating that Inline::Java doesn't currently
+ support multiple sections
+
+0.20 Sat Apr 14 23:00:00 EDT 2001
+ - Added optional JNI extension.
+ - Added support for arrays.
+ - Added support for public member variables.
+ - Added support for public static member variables.
+ - Added type casting.
+
+0.01 Thu Feb 15 14:01:25 EST 2001
+ - Created Inline::Java.
+
diff --git a/Java.pm b/Java.pm
new file mode 100644
index 0000000..27d5472
--- /dev/null
+++ b/Java.pm
@@ -0,0 +1,1191 @@
+package Inline::Java ;
+@Inline::Java::ISA = qw(Inline Exporter) ;
+
+# Export the cast function if wanted
+@EXPORT_OK = qw(cast coerce study_classes caught jar j2sdk) ;
+
+
+use strict ;
+require 5.006 ;
+
+$Inline::Java::VERSION = '0.53' ;
+
+
+# DEBUG is set via the DEBUG config
+if (! defined($Inline::Java::DEBUG)){
+ $Inline::Java::DEBUG = 0 ;
+}
+
+# Set DEBUG stream
+*DEBUG_STREAM = *STDERR ;
+
+require Inline ;
+use Carp ;
+use Config ;
+use File::Copy ;
+use File::Spec ;
+use Cwd ;
+use Data::Dumper ;
+
+use Inline::Java::Portable ;
+use Inline::Java::Class ;
+use Inline::Java::Object ;
+use Inline::Java::Array ;
+use Inline::Java::Handle ;
+use Inline::Java::Protocol ;
+use Inline::Java::Callback ;
+# Must be last.
+use Inline::Java::JVM ;
+# Our default J2SK
+require Inline::Java->find_default_j2sdk() ;
+
+
+# This is set when the script is over.
+my $DONE = 0 ;
+
+# This is set when at least one JVM is loaded.
+my $JVM = undef ;
+
+# This list will store the $o objects...
+my @INLINES = () ;
+
+my $report_version = "V2" ;
+
+# This stuff is to control the termination of the Java Interpreter
+sub done {
+ my $signal = shift ;
+
+ # To preserve the passed exit code...
+ my $ec = $? ;
+
+ $DONE = 1 ;
+
+ if (! $signal){
+ Inline::Java::debug(1, "killed by natural death.") ;
+ }
+ else{
+ Inline::Java::debug(1, "killed by signal SIG$signal.") ;
+ }
+
+ shutdown_JVM() ;
+ Inline::Java::debug(1, "exiting with $ec") ;
+ CORE::exit($ec) ;
+ exit($ec) ;
+}
+
+
+END {
+ if ($DONE < 1){
+ done() ;
+ }
+}
+
+
+# To export the cast function and others.
+sub import {
+ my $class = shift ;
+
+ foreach my $a (@_){
+ if ($a eq 'jar'){
+ print Inline::Java::Portable::get_server_jar() ;
+ exit() ;
+ }
+ elsif ($a eq 'j2sdk'){
+ print Inline::Java->find_default_j2sdk() . " says '" .
+ Inline::Java::get_default_j2sdk() . "'\n" ;
+ exit() ;
+ }
+ elsif ($a eq 'so_dirs'){
+ print Inline::Java::Portable::portable('SO_LIB_PATH_VAR') . "=" .
+ join(Inline::Java::Portable::portable('ENV_VAR_PATH_SEP'),
+ Inline::Java::get_default_j2sdk_so_dirs()) ;
+ exit() ;
+ }
+ }
+ $class->export_to_level(1, $class, @_) ;
+}
+
+
+
+######################## Inline interface ########################
+
+
+
+# Register this module as an Inline language support module
+sub register {
+ return {
+ language => 'Java',
+ aliases => ['JAVA', 'java'],
+ type => 'interpreted',
+ suffix => 'jdat',
+ } ;
+}
+
+
+# Here validate is overridden because some of the config options are needed
+# at load as well.
+sub validate {
+ my $o = shift ;
+
+ # This might not print since debug is set further down...
+ Inline::Java::debug(1, "Starting validate.") ;
+
+ my $jdk = Inline::Java::get_default_j2sdk() ;
+ my $dbg = $Inline::Java::DEBUG ;
+ my %opts = @_ ;
+ $o->set_option('DEBUG', $dbg, 'i', 1, \%opts) ;
+ $o->set_option('J2SDK', $jdk, 's', 1, \%opts) ;
+ $o->set_option('CLASSPATH', '', 's', 1, \%opts) ;
+
+ $o->set_option('BIND', 'localhost', 's', 1, \%opts) ;
+ $o->set_option('HOST', 'localhost', 's', 1, \%opts) ;
+ $o->set_option('PORT', -1, 'i', 1, \%opts) ;
+ $o->set_option('STARTUP_DELAY', 15, 'i', 1, \%opts) ;
+ $o->set_option('SHARED_JVM', 0, 'b', 1, \%opts) ;
+ $o->set_option('START_JVM', 1, 'b', 1, \%opts) ;
+ $o->set_option('JNI', 0, 'b', 1, \%opts) ;
+ $o->set_option('EMBEDDED_JNI', 0, 'b', 1, \%opts) ;
+ $o->set_option('NATIVE_DOUBLES', 0, 'b', 1, \%opts) ;
+
+ $o->set_option('WARN_METHOD_SELECT', 0, 'b', 1, \%opts) ;
+ $o->set_option('STUDY', undef, 'a', 0, \%opts) ;
+ $o->set_option('AUTOSTUDY', 0, 'b', 1, \%opts) ;
+
+ $o->set_option('EXTRA_JAVA_ARGS', '', 's', 1, \%opts) ;
+ $o->set_option('EXTRA_JAVAC_ARGS', '', 's', 1, \%opts) ;
+ $o->set_option('DEBUGGER', 0, 'b', 1, \%opts) ;
+
+ $o->set_option('PRIVATE', '', 'b', 1, \%opts) ;
+ $o->set_option('PACKAGE', '', 's', 1, \%opts) ;
+
+ my @left_overs = keys(%opts) ;
+ if (scalar(@left_overs)){
+ croak "'$left_overs[0]' is not a valid configuration option for Inline::Java" ;
+ }
+
+ # Now for the post processing
+ $Inline::Java::DEBUG = $o->get_java_config('DEBUG') ;
+
+ # Embedded JNI turns on regular JNI
+ if ($o->get_java_config('EMBEDDED_JNI')){
+ $o->set_java_config('JNI', 1) ;
+ }
+
+ if ($o->get_java_config('PORT') == -1){
+ if ($o->get_java_config('SHARED_JVM')){
+ $o->set_java_config('PORT', 7891) ;
+ }
+ else{
+ $o->set_java_config('PORT', -7890) ;
+ }
+ }
+
+ if (($o->get_java_config('JNI'))&&($o->get_java_config('SHARED_JVM'))){
+ croak("You can't use the 'SHARED_JVM' option in 'JNI' mode") ;
+ }
+ if (($o->get_java_config('JNI'))&&($o->get_java_config('DEBUGGER'))){
+ croak("You can't invoke the Java debugger ('DEBUGGER' option) in 'JNI' mode") ;
+ }
+ if ((! $o->get_java_config('SHARED_JVM'))&&(! $o->get_java_config('START_JVM'))){
+ croak("Disabling the 'START_JVM' option only makes sense in 'SHARED_JVM' mode") ;
+ }
+
+ if ($o->get_java_config('JNI')){
+ require Inline::Java::JNI ;
+ }
+
+ if ($o->get_java_config('DEBUGGER')){
+ # Here we want to tweak a few settings to help debugging...
+ Inline::Java::debug(1, "Debugger mode activated") ;
+ # Add the -g compile option
+ $o->set_java_config('EXTRA_JAVAC_ARGS', $o->get_java_config('EXTRA_JAVAC_ARGS') . " -g ") ;
+ # Add the -sourcepath runtime option
+ $o->set_java_config('EXTRA_JAVA_ARGS', $o->get_java_config('EXTRA_JAVA_ARGS') .
+ " -sourcepath " . $o->get_api('build_dir') .
+ Inline::Java::Portable::portable("ENV_VAR_PATH_SEP_CP") .
+ Inline::Java::Portable::get_source_dir()
+ ) ;
+ }
+
+ my $study = $o->get_java_config('STUDY') ;
+ if ((defined($study))&&(ref($study) ne 'ARRAY')){
+ croak "Configuration option 'STUDY' must be an array of Java class names" ;
+ }
+
+ Inline::Java::debug(1, "validate done.") ;
+}
+
+
+sub set_option {
+ my $o = shift ;
+ my $name = shift ;
+ my $default = shift ;
+ my $type = shift ;
+ my $env_or = shift ;
+ my $opts = shift ;
+ my $desc = shift ;
+
+ if (! exists($o->{ILSM}->{$name})){
+ my $val = undef ;
+ if (($env_or)&&(exists($ENV{"PERL_INLINE_JAVA_$name"}))){
+ $val = $ENV{"PERL_INLINE_JAVA_$name"} ;
+ }
+ elsif (exists($opts->{$name})){
+ $val = $opts->{$name} ;
+ }
+ else{
+ $val = $default ;
+ }
+
+ if ($type eq 'b'){
+ if (! defined($val)){
+ $val = 0 ;
+ }
+ $val = ($val ? 1 : 0) ;
+ }
+ elsif ($type eq 'i'){
+ if ((! defined($val))||($val !~ /\d/)){
+ $val = 0 ;
+ }
+ $val = int($val) ;
+ }
+
+ $o->set_java_config($name, $val) ;
+ }
+
+ delete $opts->{$name} ;
+}
+
+
+sub get_java_config {
+ my $o = shift ;
+ my $param = shift ;
+
+ return $o->{ILSM}->{$param} ;
+}
+
+
+sub set_java_config {
+ my $o = shift ;
+ my $param = shift ;
+ my $value = shift ;
+
+ return $o->{ILSM}->{$param} = $value ;
+}
+
+
+# In theory we shouldn't need to use this, but it seems
+# it's not all accessible by the API yet.
+sub get_config {
+ my $o = shift ;
+ my $param = shift ;
+
+ return $o->{CONFIG}->{$param} ;
+}
+
+
+sub get_api {
+ my $o = shift ;
+ my $param = shift ;
+
+ # Allows us to force a specific package...
+ if (($param eq 'pkg')&&($o->get_config('PACKAGE'))){
+ return $o->get_config('PACKAGE') ;
+ }
+
+ return $o->{API}->{$param} ;
+}
+
+
+# Parse and compile Java code
+sub build {
+ my $o = shift ;
+
+ if ($o->get_java_config('built')){
+ return ;
+ }
+
+ Inline::Java::debug(1, "Starting build.") ;
+
+ # Grab and untaint the current directory
+ my $cwd = Cwd::cwd() ;
+ if ($o->get_config('UNTAINT')){
+ ($cwd) = $cwd =~ /(.*)/ ;
+ }
+
+ # We must grab this before we change to the build dir because
+ # it could be relative...
+ my $server_jar = Inline::Java::Portable::get_server_jar() ;
+
+ # We need to add all the previous install dirs to the classpath because
+ # they can access each other.
+ my @prev_install_dirs = () ;
+ foreach my $in (@INLINES){
+ push @prev_install_dirs, File::Spec->catdir($in->get_api('install_lib'),
+ 'auto', $in->get_api('modpname')) ;
+ }
+
+ my $cp = $ENV{CLASSPATH} || '' ;
+ $ENV{CLASSPATH} = Inline::Java::Portable::make_classpath($server_jar, @prev_install_dirs, $o->get_java_config('CLASSPATH')) ;
+ Inline::Java::debug(2, "classpath: $ENV{CLASSPATH}") ;
+
+ # Create the build dir and go there
+ my $build_dir = $o->get_api('build_dir') ;
+ $o->mkpath($build_dir) ;
+ chdir $build_dir ;
+
+ my $code = $o->get_api('code') ;
+ my $pcode = $code ;
+ my $study_only = ($code =~ /^(STUDY|SERVER)$/) ;
+ my $source = ($study_only ? '' : $o->get_api('modfname') . ".java") ;
+
+ # Parse code to check for public class
+ $pcode =~ s/\\\"//g ;
+ $pcode =~ s/\"(.*?)\"//g ;
+ $pcode =~ s/\/\*(.*?)\*\///gs ;
+ $pcode =~ s/\/\/(.*)$//gm ;
+ if ($pcode =~ /public\s+(abstract\s+)?class\s+(\w+)/){
+ $source = "$2.java" ;
+ }
+
+ my $install_dir = File::Spec->catdir($o->get_api('install_lib'),
+ 'auto', $o->get_api('modpname')) ;
+ $o->mkpath($install_dir) ;
+
+ if ($source){
+ # Dump the source code...
+ open(Inline::Java::JAVA, ">$source") or
+ croak "Can't open $source: $!" ;
+ print Inline::Java::JAVA $code ;
+ close(Inline::Java::JAVA) ;
+
+ # ... and compile it.
+ my $javac = File::Spec->catfile($o->get_java_config('J2SDK'),
+ Inline::Java::Portable::portable("J2SDK_BIN"),
+ "javac" . Inline::Java::Portable::portable("EXE_EXTENSION")) ;
+ my $redir = Inline::Java::Portable::portable("IO_REDIR") ;
+
+ my $args = "-deprecation " . $o->get_java_config('EXTRA_JAVAC_ARGS') ;
+ my $pinstall_dir = Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $install_dir) ;
+ my $cmd = Inline::Java::Portable::portable("SUB_FIX_CMD_QUOTES",
+ "\"$javac\" $args -d \"$pinstall_dir\" $source > cmd.out $redir") ;
+ if ($o->get_config('UNTAINT')){
+ ($cmd) = $cmd =~ /(.*)/ ;
+ }
+ Inline::Java::debug(2, "$cmd") ;
+ my $res = system($cmd) ;
+ my $msg = $o->get_compile_error_msg() ;
+ if ($res){
+ croak $o->compile_error_msg($cmd, $msg) ;
+ } ;
+ if ($msg){
+ warn("\n$msg\n") ;
+ }
+
+ # When we run the commands, we quote them because in WIN32 you need it if
+ # the programs are in directories which contain spaces. Unfortunately, in
+ # WIN9x, when you quote a command, it masks it's exit value, and 0 is always
+ # returned. Therefore a command failure is not detected.
+ # We need to take care of checking whether there are actually files
+ # to be copied, and if not will exit the script.
+ if (Inline::Java::Portable::portable('COMMAND_COM')){
+ my @fl = Inline::Java::Portable::find_classes_in_dir($install_dir) ;
+ if (! scalar(@fl)){
+ croak "No class files produced. Previous command failed under command.com?" ;
+ }
+ foreach my $f (@fl){
+ if (! (-s $f->{file})){
+ croak "File $f->{file} has size zero. Previous command failed under command.com?" ;
+ }
+ }
+ }
+ }
+
+ $ENV{CLASSPATH} = $cp ;
+ Inline::Java::debug(2, "classpath: $ENV{CLASSPATH}") ;
+
+ # Touch the .jdat file.
+ my $jdat = File::Spec->catfile($install_dir, $o->get_api('modfname') . '.' . $o->get_api('suffix')) ;
+ if (! open(Inline::Java::TOUCH, ">$jdat")){
+ croak "Can't create file $jdat" ;
+ }
+ close(Inline::Java::TOUCH) ;
+
+ # Go back and clean up
+ chdir $cwd ;
+ if (($o->get_api('cleanup'))&&(! $o->get_java_config('DEBUGGER'))){
+ $o->rmpath('', $build_dir) ;
+ }
+
+ $o->set_java_config('built', 1) ;
+ Inline::Java::debug(1, "build done.") ;
+}
+
+
+sub get_compile_error_msg {
+ my $o = shift ;
+
+ my $msg = '' ;
+ if (open(Inline::Java::CMD, "<cmd.out")){
+ $msg = join("", <Inline::Java::CMD>) ;
+ close(Inline::Java::CMD) ;
+ }
+
+ return $msg ;
+}
+
+
+sub compile_error_msg {
+ my $o = shift ;
+ my $cmd = shift ;
+ my $error = shift ;
+
+ my $build_dir = $o->get_api('build_dir') ;
+
+ my $lang = $o->get_api('language') ;
+ return <<MSG
+
+A problem was encountered while attempting to compile and install your Inline
+$lang code. The command that failed was:
+ $cmd
+
+The build directory was:
+$build_dir
+
+The error message was:
+$error
+
+To debug the problem, cd to the build directory, and inspect the output files.
+
+MSG
+;
+}
+
+
+# Load and Run the Java Code.
+sub load {
+ my $o = shift ;
+
+ if ($o->get_java_config('loaded')){
+ return ;
+ }
+
+ Inline::Java::debug(1, "Starting load.") ;
+
+ my $install_dir = File::Spec->catdir($o->get_api('install_lib'),
+ 'auto', $o->get_api('modpname')) ;
+
+ # If the JVM is not running, we need to start it here.
+ my $cp = $ENV{CLASSPATH} || '' ;
+ if (! $JVM){
+ $ENV{CLASSPATH} = Inline::Java::Portable::make_classpath(
+ Inline::Java::Portable::get_server_jar()) ;
+ Inline::Java::debug(2, "classpath: $ENV{CLASSPATH}") ;
+ $JVM = new Inline::Java::JVM($o) ;
+ $ENV{CLASSPATH} = $cp ;
+ Inline::Java::debug(2, "classpath: $ENV{CLASSPATH}") ;
+
+ my $pc = new Inline::Java::Protocol(undef, $o) ;
+ $pc->AddClassPath(Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", Inline::Java::Portable::get_user_jar())) ;
+
+ my $st = $pc->ServerType() ;
+ if ((($st eq "shared")&&(! $o->get_java_config('SHARED_JVM')))||
+ (($st eq "private")&&($o->get_java_config('SHARED_JVM')))){
+ croak "JVM type mismatch on port " . $JVM->{port} ;
+ }
+ }
+
+ $ENV{CLASSPATH} = '' ;
+ my @cp = Inline::Java::Portable::make_classpath($install_dir, $o->get_java_config('CLASSPATH')) ;
+ $ENV{CLASSPATH} = $cp ;
+
+ my $pc = new Inline::Java::Protocol(undef, $o) ;
+ $pc->AddClassPath(@cp) ;
+
+ # Add our Inline object to the list.
+ push @INLINES, $o ;
+ $o->set_java_config('id', scalar(@INLINES) - 1) ;
+ Inline::Java::debug(3, "Inline::Java object id is " . $o->get_java_config('id')) ;
+
+ $o->study_module() ;
+ if ((defined($o->get_java_config('STUDY')))&&(scalar($o->get_java_config('STUDY')))){
+ $o->_study($o->get_java_config('STUDY')) ;
+ }
+
+ $o->set_java_config('loaded', 1) ;
+ Inline::Java::debug(1, "load done.") ;
+}
+
+
+# This function 'studies' the classes generated by the inlined code.
+sub study_module {
+ my $o = shift ;
+
+ my $install_dir = File::Spec->catdir($o->get_api('install_lib'),
+ 'auto', $o->get_api('modpname')) ;
+ my $cache = $o->get_api('modfname') . '.' . $o->get_api('suffix') ;
+
+ my $lines = [] ;
+ if (! $o->get_java_config('built')){
+ # Since we didn't build the module, this means that
+ # it was up to date. We can therefore use the data
+ # from the cache.
+ Inline::Java::debug(1, "using jdat cache") ;
+ my $p = File::Spec->catfile($install_dir, $cache) ;
+ my $size = (-s $p) || 0 ;
+ if ($size > 0){
+ if (open(Inline::Java::CACHE, "<$p")){
+ while (<Inline::Java::CACHE>){
+ push @{$lines}, $_ ;
+ }
+ close(Inline::Java::CACHE) ;
+ }
+ else{
+ croak "Can't open $p for reading: $!" ;
+ }
+ }
+ }
+ else{
+ # First thing to do is get the list of classes that comprise the module.
+
+ # We need the classes that are in the directory or under...
+ my @classes = () ;
+ my $cwd = Cwd::cwd() ;
+ if ($o->get_config('UNTAINT')){
+ ($cwd) = $cwd =~ /(.*)/ ;
+ }
+
+ # We chdir to the install dir, that makes it easier to figure out
+ # the packages for the classes.
+ chdir($install_dir) ;
+ my @fl = Inline::Java::Portable::find_classes_in_dir('.') ;
+ chdir $cwd ;
+ foreach my $f (@fl){
+ push @classes, $f->{class} ;
+ }
+
+ # Now we ask Java the info about those classes...
+ $lines = $o->report(@classes) ;
+
+ # and we update the cache with these results.
+ Inline::Java::debug(1, "updating jdat cache") ;
+ my $p = File::Spec->catfile($install_dir, $cache) ;
+ if (open(Inline::Java::CACHE, ">$p")){
+ foreach my $l (@{$lines}){
+ print Inline::Java::CACHE "$l\n" ;
+ }
+ close(Inline::Java::CACHE) ;
+ }
+ else{
+ croak "Can't open $p file for writing" ;
+ }
+ }
+
+ # Now we read up the symbols and bind them to Perl.
+ $o->bind_jdat($o->load_jdat($lines)) ;
+}
+
+
+# This function 'studies' the specified classes and binds them to
+# Perl.
+sub _study {
+ my $o = shift ;
+ my $classes = shift ;
+
+ my @new_classes = () ;
+ foreach my $class (@{$classes}){
+ $class = Inline::Java::Class::ValidateClass($class) ;
+ if (! Inline::Java::known_to_perl($o->get_api('pkg'), $class)){
+ push @new_classes, $class ;
+ }
+ }
+ if (! scalar(@new_classes)){
+ return ;
+ }
+
+ my $lines = $o->report(@new_classes) ;
+ # Now we read up the symbols and bind them to Perl.
+ $o->bind_jdat($o->load_jdat($lines)) ;
+}
+
+
+sub report {
+ my $o = shift ;
+ my @classes = @_ ;
+
+ my @lines = () ;
+ if (scalar(@classes)){
+ my $pc = new Inline::Java::Protocol(undef, $o) ;
+ my $resp = $pc->Report(join(" ", @classes)) ;
+ @lines = split("\n", $resp) ;
+ }
+
+ return \@lines ;
+}
+
+
+# Load the jdat code information file.
+sub load_jdat {
+ my $o = shift ;
+ my $lines = shift ;
+
+ Inline::Java::debug_obj($lines) ;
+
+ # We need an array here since the same object can have many
+ # study sessions.
+ if (! defined($o->{ILSM}->{data})){
+ $o->{ILSM}->{data} = [] ;
+ }
+ my $d = {} ;
+ my $data_idx = scalar(@{$o->{ILSM}->{data}}) ;
+ push @{$o->{ILSM}->{data}}, $d ;
+
+ # The original regexp didn't match anymore under the debugger...
+ # Very strange indeed...
+ # my $re = '[\w.\$\[;]+' ;
+ my $re = '.+' ;
+
+ my $idx = 0 ;
+ my $current_class = undef ;
+ if (scalar(@{$lines})){
+ my $vline = shift @{$lines} ;
+ chomp($vline) ;
+ if ($vline ne $report_version){
+ croak("Report version mismatch ($vline != $report_version). Delete your '_Inline' and try again.") ;
+ }
+ }
+ foreach my $line (@{$lines}){
+ chomp($line) ;
+ if ($line =~ /^class ($re) ($re)$/){
+ # We found a class definition
+ my $java_class = $1 ;
+ my $parent_java_class = $2 ;
+ $current_class = Inline::Java::java2perl($o->get_api('pkg'), $java_class) ;
+ $d->{classes}->{$current_class} = {} ;
+ $d->{classes}->{$current_class}->{java_class} = $java_class ;
+ if ($parent_java_class ne "null"){
+ $d->{classes}->{$current_class}->{parent_java_class} = $parent_java_class ;
+ }
+ $d->{classes}->{$current_class}->{constructors} = {} ;
+ $d->{classes}->{$current_class}->{methods} = {} ;
+ $d->{classes}->{$current_class}->{fields} = {} ;
+ }
+ elsif ($line =~ /^constructor \((.*)\)$/){
+ my $signature = $1 ;
+
+ $d->{classes}->{$current_class}->{constructors}->{$signature} =
+ {
+ SIGNATURE => [split(", ", $signature)],
+ STATIC => 1,
+ IDX => $idx,
+ } ;
+ }
+ elsif ($line =~ /^method (\w+) ($re) (\w+)\((.*)\)$/){
+ my $static = $1 ;
+ my $declared_in = $2 ;
+ my $method = $3 ;
+ my $signature = $4 ;
+
+ if (! defined($d->{classes}->{$current_class}->{methods}->{$method})){
+ $d->{classes}->{$current_class}->{methods}->{$method} = {} ;
+ }
+
+ $d->{classes}->{$current_class}->{methods}->{$method}->{$signature} =
+ {
+ SIGNATURE => [split(", ", $signature)],
+ STATIC => ($static eq "static" ? 1 : 0),
+ IDX => $idx,
+ } ;
+ }
+ elsif ($line =~ /^field (\w+) ($re) (\w+) ($re)$/){
+ my $static = $1 ;
+ my $declared_in = $2 ;
+ my $field = $3 ;
+ my $type = $4 ;
+
+ if (! defined($d->{classes}->{$current_class}->{fields}->{$field})){
+ $d->{classes}->{$current_class}->{fields}->{$field} = {} ;
+ }
+
+ $d->{classes}->{$current_class}->{fields}->{$field}->{$type} =
+ {
+ TYPE => $type,
+ STATIC => ($static eq "static" ? 1 : 0),
+ IDX => $idx,
+ } ;
+ }
+ $idx++ ;
+ }
+
+ Inline::Java::debug_obj($d) ;
+
+ return ($d, $data_idx) ;
+}
+
+
+# Binds the classes and the methods to Perl
+sub bind_jdat {
+ my $o = shift ;
+ my $d = shift ;
+ my $idx = shift ;
+
+ if (! defined($d->{classes})){
+ return ;
+ }
+
+ my $inline_idx = $o->get_java_config('id') ;
+
+ my %classes = %{$d->{classes}} ;
+ foreach my $class (sort keys %classes) {
+ my $class_name = $class ;
+ $class_name =~ s/^(.*)::// ;
+
+ my $java_class = $d->{classes}->{$class}->{java_class} ;
+ # This parent stuff is needed for PerlNatives (so that you can call PerlNatives methods
+ # from Perl...)
+ my $parent_java_class = $d->{classes}->{$class}->{parent_java_class} ;
+ my $parent_module = '' ;
+ my $parent_module_declare = '' ;
+ if (defined($parent_java_class)){
+ $parent_module = java2perl($o->get_api('pkg'), $parent_java_class) ;
+ $parent_module_declare = "\$$parent_module" . "::EXISTS_AS_PARENT = 1 ;" ;
+ $parent_module .= ' ' ;
+ }
+ if (Inline::Java::known_to_perl($o->get_api('pkg'), $java_class)){
+ next ;
+ }
+
+ my $colon = ":" ;
+ my $dash = "-" ;
+ my $ijo = 'Inline::Java::Object' ;
+
+ my $code = <<CODE;
+package $class ;
+use vars qw(\@ISA \$INLINE \$EXISTS \$JAVA_CLASS \$DUMMY_OBJECT) ;
+
+$parent_module_declare
+\@ISA = qw($parent_module$ijo) ;
+\$INLINE = \$INLINES[$inline_idx] ;
+\$EXISTS = 1 ;
+\$JAVA_CLASS = '$java_class' ;
+\$DUMMY_OBJECT = $class$dash>__new(
+ \$JAVA_CLASS, \$INLINE, 0) ;
+
+use Carp ;
+
+CODE
+
+ while (my ($field, $types) = each %{$d->{classes}->{$class}->{fields}}){
+ while (my ($type, $sign) = each %{$types}){
+ if ($sign->{STATIC}){
+ $code .= <<CODE;
+tie \$$class$colon:$field, "Inline::Java::Object::StaticMember",
+ \$DUMMY_OBJECT,
+ '$field' ;
+CODE
+ # We have at least one static version of this field,
+ # that's enough.
+ # Don't forget to reset the 'each' static pointer
+ keys %{$types} ;
+ last ;
+ }
+ }
+ }
+
+
+ if (scalar(keys %{$d->{classes}->{$class}->{constructors}})){
+ $code .= <<CODE;
+
+sub new {
+ my \$class = shift ;
+ my \@args = \@_ ;
+
+ my \$o = \$INLINE ;
+ my \$d = \$o->{ILSM}->{data}->[$idx] ;
+ my \$signatures = \$d->{classes}->{'$class'}->{constructors} ;
+ my (\$proto, \$new_args, \$static) = \$class->__validate_prototype('new', [\@args], \$signatures, \$o) ;
+
+ my \$ret = undef ;
+ eval {
+ \$ret = \$class->__new(\$JAVA_CLASS, \$o, -1, \$proto, \$new_args) ;
+ } ;
+ croak \$@ if \$@ ;
+
+ return \$ret ;
+}
+
+
+sub $class_name {
+ return new(\@_) ;
+}
+
+CODE
+ }
+
+ while (my ($method, $sign) = each %{$d->{classes}->{$class}->{methods}}){
+ $code .= $o->bind_method($idx, $class, $method) ;
+ }
+
+ Inline::Java::debug_obj(\$code) ;
+
+ # open (Inline::Java::CODE, ">>code") and print CODE $code and close(CODE) ;
+
+ # Here it seems that for the eval below to resolve the @INLINES
+ # list properly, it must be used in this function...
+ my $dummy = scalar(@INLINES) ;
+
+ eval $code ;
+
+ croak $@ if $@ ;
+ }
+}
+
+
+sub bind_method {
+ my $o = shift ;
+ my $idx = shift ;
+ my $class = shift ;
+ my $method = shift ;
+ my $static = shift ;
+
+ my $code = <<CODE;
+
+sub $method {
+ my \$this = shift ;
+ my \@args = \@_ ;
+
+ my \$o = \$INLINE ;
+ my \$d = \$o->{ILSM}->{data}->[$idx] ;
+ my \$signatures = \$d->{classes}->{'$class'}->{methods}->{'$method'} ;
+ my (\$proto, \$new_args, \$static) = \$this->__validate_prototype('$method', [\@args], \$signatures, \$o) ;
+
+ if ((\$static)&&(! ref(\$this))){
+ \$this = \$DUMMY_OBJECT ;
+ }
+
+ my \$ret = undef ;
+ eval {
+ \$ret = \$this->__get_private()->{proto}->CallJavaMethod('$method', \$proto, \$new_args) ;
+ } ;
+ croak \$@ if \$@ ;
+
+ return \$ret ;
+}
+
+CODE
+
+ return $code ;
+}
+
+
+sub get_fields {
+ my $o = shift ;
+ my $class = shift ;
+
+ my $fields = {} ;
+ my $data_list = $o->{ILSM}->{data} ;
+
+ foreach my $d (@{$data_list}){
+ if (exists($d->{classes}->{$class})){
+ while (my ($field, $value) = each %{$d->{classes}->{$class}->{fields}}){
+ # Here $value is a hash that contains all the different
+ # types available for the field $field
+ $fields->{$field} = $value ;
+ }
+ }
+ }
+
+ return $fields ;
+}
+
+
+# Return a small report about the Java code.
+sub info {
+ my $o = shift ;
+
+ if (! (($o->{INLINE}->{object_ready})||($o->get_java_config('built')))){
+ $o->build() ;
+ }
+
+ if (! $o->get_java_config('loaded')){
+ $o->load() ;
+ }
+
+ my $info = '' ;
+ my $data_list = $o->{ILSM}->{data} ;
+
+ foreach my $d (@{$data_list}){
+ if (! defined($d->{classes})){
+ next ;
+ }
+
+ my %classes = %{$d->{classes}} ;
+
+ $info .= "The following Java classes have been bound to Perl:\n" ;
+ foreach my $class (sort keys %classes) {
+ $info .= "\n class $class:\n" ;
+
+ $info .= " public methods:\n" ;
+ while (my ($k, $v) = each %{$d->{classes}->{$class}->{constructors}}){
+ my $name = $class ;
+ $name =~ s/^(.*)::// ;
+ $info .= " $name($k)\n" ;
+ }
+
+ while (my ($k, $v) = each %{$d->{classes}->{$class}->{methods}}){
+ while (my ($k2, $v2) = each %{$d->{classes}->{$class}->{methods}->{$k}}){
+ my $static = ($v2->{STATIC} ? "static " : "") ;
+ $info .= " $static$k($k2)\n" ;
+ }
+ }
+
+ $info .= " public member variables:\n" ;
+ while (my ($k, $v) = each %{$d->{classes}->{$class}->{fields}}){
+ while (my ($k2, $v2) = each %{$d->{classes}->{$class}->{fields}->{$k}}){
+ my $static = ($v2->{STATIC} ? "static " : "") ;
+ my $type = $v2->{TYPE} ;
+
+ $info .= " $static$type $k\n" ;
+ }
+ }
+ }
+ }
+
+ return $info ;
+}
+
+
+
+######################## General Functions ########################
+
+
+sub __get_JVM {
+ return $JVM ;
+}
+
+
+# For testing purposes only...
+sub __clear_JVM {
+ $JVM = undef ;
+}
+
+
+sub shutdown_JVM {
+ if ($JVM){
+ $JVM->shutdown() ;
+ $JVM = undef ;
+ }
+}
+
+
+sub reconnect_JVM {
+ if ($JVM){
+ $JVM->reconnect() ;
+ }
+}
+
+
+sub capture_JVM {
+ if ($JVM){
+ $JVM->capture() ;
+ }
+}
+
+
+sub i_am_JVM_owner {
+ if ($JVM){
+ return $JVM->am_owner() ;
+ }
+}
+
+
+sub release_JVM {
+ if ($JVM){
+ $JVM->release() ;
+ }
+}
+
+
+sub get_DEBUG {
+ return $Inline::Java::DEBUG ;
+}
+
+
+sub get_DONE {
+ return $DONE ;
+}
+
+
+sub set_DONE {
+ $DONE = 1 ;
+}
+
+
+sub __get_INLINES {
+ return \@INLINES ;
+}
+
+
+sub java2perl {
+ my $pkg = shift ;
+ my $jclass = shift ;
+
+ $jclass =~ s/[.\$]/::/g ;
+
+ if ((defined($pkg))&&($pkg)){
+ $jclass = $pkg . "::" . $jclass ;
+ }
+
+ return $jclass ;
+}
+
+
+sub known_to_perl {
+ my $pkg = shift ;
+ my $jclass = shift ;
+
+ my $perl_class = java2perl($pkg, $jclass) ;
+
+ no strict 'refs' ;
+ if (defined(${$perl_class . "::" . "EXISTS"})){
+ Inline::Java::debug(3, "perl knows about '$jclass' ('$perl_class')") ;
+ return 1 ;
+ }
+ else{
+ Inline::Java::debug(3, "perl doesn't know about '$jclass' ('$perl_class')") ;
+ }
+
+ return 0 ;
+}
+
+
+sub debug {
+ my $level = shift ;
+
+ if (($Inline::Java::DEBUG)&&($Inline::Java::DEBUG >= $level)){
+ my $x = " " x $level ;
+ my $str = join("\n$x", @_) ;
+ while (chomp($str)) {}
+ print DEBUG_STREAM sprintf("[perl][%s]$x%s\n", $level, $str) ;
+ }
+}
+
+
+sub debug_obj {
+ my $obj = shift ;
+ my $force = shift || 0 ;
+
+ if (($Inline::Java::DEBUG >= 5)||($force)){
+ debug(5, "Dump:\n" . Dumper($obj)) ;
+ if (UNIVERSAL::isa($obj, "Inline::Java::Object")){
+ # Print the guts as well...
+ debug(5, "Private Dump:" . Dumper($obj->__get_private())) ;
+ }
+ }
+}
+
+
+sub dump_obj {
+ my $obj = shift ;
+
+ return debug_obj($obj, 1) ;
+}
+
+
+######################## Public Functions ########################
+
+
+# If we are dealing with a Java object, we simply ask for a new "reference"
+# with the requested class.
+sub cast {
+ my $type = shift ;
+ my $val = shift ;
+
+ if (! UNIVERSAL::isa($val, "Inline::Java::Object")){
+ croak("Type casting can only be used on Java objects. Use 'coerce' instead.") ;
+ }
+
+ return $val->__cast($type) ;
+}
+
+
+# coerce is used to force a specific prototype to be used.
+sub coerce {
+ my $type = shift ;
+ my $val = shift ;
+ my $array_type = shift ;
+
+ if (UNIVERSAL::isa($val, "Inline::Java::Object")){
+ croak("Type coercing can't be used on Java objects. Use 'cast' instead.") ;
+ }
+
+ my $o = undef ;
+ eval {
+ $o = new Inline::Java::Class::Coerce($type, $val, $array_type) ;
+ } ;
+ croak $@ if $@ ;
+
+ return $o ;
+}
+
+
+sub study_classes {
+ my $classes = shift ;
+ my $package = shift || caller() ;
+
+ my $o = undef ;
+ my %pkgs = () ;
+ foreach (@INLINES){
+ my $i = $_ ;
+ my $pkg = $i->get_api('pkg') || 'main' ;
+ $pkgs{$pkg} = 1 ;
+ if ($pkg eq $package){
+ $o = $i ;
+ last ;
+ }
+ }
+
+ if (defined($o)){
+ $o->_study($classes) ;
+ }
+ else {
+ my $msg = "Can't place studied classes under package '$package' since Inline::Java was not used there. Valid packages are:\n" ;
+ foreach my $pkg (keys %pkgs){
+ $msg .= " $pkg\n" ;
+ }
+ croak($msg) ;
+ }
+}
+
+
+sub caught {
+ my $class = shift ;
+
+ my $e = $@ ;
+
+ $class = Inline::Java::Class::ValidateClass($class) ;
+
+ my $ret = 0 ;
+ if (($e)&&(UNIVERSAL::isa($e, "Inline::Java::Object"))){
+ my ($msg, $score) = $e->__isa($class) ;
+ if ($msg){
+ $ret = 0 ;
+ }
+ else{
+ $ret = 1 ;
+ }
+ }
+ $@ = $e ;
+
+ return $ret ;
+}
+
+
+sub find_default_j2sdk {
+ my $class = shift ;
+
+ return File::Spec->catfile('Inline', 'Java', 'default_j2sdk.pl') ;
+}
+
+
+1 ;
diff --git a/Java.pod b/Java.pod
new file mode 100644
index 0000000..453a868
--- /dev/null
+++ b/Java.pod
@@ -0,0 +1,1162 @@
+=head1 NAME
+
+Inline::Java - Write Perl classes in Java.
+
+=head1 SYNOPSIS
+
+=for comment
+
+ use Inline Java => <<'END_OF_JAVA_CODE' ;
+ class Pod_alu {
+ public Pod_alu(){
+ }
+
+ public int add(int i, int j){
+ return i + j ;
+ }
+
+ public int subtract(int i, int j){
+ return i - j ;
+ }
+ }
+ END_OF_JAVA_CODE
+
+ my $alu = new Pod_alu() ;
+ print($alu->add(9, 16) . "\n") ; # prints 25
+ print($alu->subtract(9, 16) . "\n") ; # prints -7
+
+=for comment
+
+
+=head1 DESCRIPTION
+
+The C<Inline::Java> module allows you to put Java source code
+directly "inline" in a Perl script or module. A Java compiler
+is launched and the Java code is compiled. Then Perl asks the
+Java classes what public methods have been defined. These classes
+and methods are available to the Perl program as if they had been
+written in Perl.
+
+The process of interrogating the Java classes for public methods
+occurs the first time you run your Java code. The namespace is
+cached, and subsequent calls use the cached version.
+ Z<>
+
+
+=head1 USING THE Inline::Java MODULE
+
+C<Inline::Java> is driven by fundamentally the same idea as other
+C<Inline> language modules, like C<Inline::C> or C<Inline::CPP>.
+Because Java is both compiled and interpreted, the method of getting
+your code is different, but overall, using C<Inline::Java> is very similar
+to any other C<Inline> language module.
+
+This section will explain the different ways to C<use> Inline::Java.
+For more details on C<Inline>, see 'perldoc Inline'.
+
+B<Basic Usage>
+
+The most basic form for using C<Inline::Java> is:
+
+ use Inline Java => 'Java source code' ;
+
+Of course, you can use Perl's "here document" style of quoting to make
+the code slightly easier to read:
+
+ use Inline Java => <<'END';
+
+ Java source code goes here.
+
+ END
+
+The source code can also be specified as a filename, a subroutine
+reference (sub routine should return source code), or an array
+reference (array contains lines of source code). This information
+is detailed in 'perldoc Inline'.
+
+In order for C<Inline::Java> to function properly, it needs to know
+where to find a Java 2 SDK on your machine. This is done using one
+of the following techniques:
+
+=over 4
+
+=item 1
+
+Set the J2SDK configuration option to the correct directory
+
+=item 2
+
+Set the PERL_INLINE_JAVA_J2SDK environment variable to the
+correct directory
+
+=back
+
+
+If none of these are specified, C<Inline::Java> will use the Java
+2 SDK that was specified a install time (see below).
+
+
+=head1 DEFAULT JAVA 2 SDK
+
+When C<Inline::Java> was installed, the path to the Java 2 SDK that was
+used was stored in a file called default_j2sdk.pl that resides with
+the C<Inline::Java> module. You can find this file by using the following
+command:
+
+ % perl -MInline::Java=j2sdk
+
+If you wish to permanently change the default Java 2 SDK that is used
+by C<Inline::Java>, edit this file and change the value found there.
+If you wish use a different Java 2 SDK temporarily, see the J2SDK
+configuration option described below.
+
+Additionally, you can use the following command to get the list of directories
+that you should put in you shared library path when using the JNI extension:
+
+ % perl -MInline::Java=so_dirs
+
+
+=head1 CONFIGURATION OPTIONS
+
+There are a number of configuration options that dictate the
+behavior of C<Inline::Java>:
+
+=over 4
+
+=item J2SDK
+
+Specifies the path to your Java 2 SDK.
+
+ Ex: J2SDK => '/my/java/2/sdk/path'
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item PORT
+
+Specifies the port number for the server. Default is -1 (next
+available port number), default for SHARED_JVM mode is 7891.
+
+ Ex: PORT => 4567
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item HOST
+
+Specifies the host on which the JVM server is running. This option
+really only makes sense in SHARED_JVM mode when START_JVM is disabled.
+
+ Ex: HOST => 'jvm.server.com'
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item BIND
+
+Specifies the IP address on which the JVM server will be listening. By
+default the JVM server listens for connections on 'localhost' only.
+
+ Ex: BIND => '192.168.1.1'
+ Ex: BIND => '0.0.0.0'
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item STARTUP_DELAY
+
+Specifies the maximum number of seconds that the Perl script
+will try to connect to the Java server. In other this is the
+delay that Perl gives to the Java server to start. Default
+is 15 seconds.
+
+ Ex: STARTUP_DELAY => 20
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item CLASSPATH
+
+Adds the specified CLASSPATH. This CLASSPATH will only be available
+through the user classloader. To set the CLASSPATH globally (which is
+most probably what you want to do anyways), use the CLASSPATH
+environment variable.
+
+ Ex: CLASSPATH => '/my/other/java/classses'
+
+=item JNI
+
+Toggles the execution mode. The default is to use the client/server
+mode. To use the JNI extension (you must have built it at install
+time though. See README and README.JNI for more information), set
+JNI to 1.
+
+ Ex: JNI => 1
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item EXTRA_JAVA_ARGS, EXTRA_JAVAC_ARGS
+
+Specify extra command line parameters to be passed to, respectively,
+the JVM and the Java compiler. Use with caution as some options may
+alter normal C<Inline::Java> behavior.
+
+ Ex: EXTRA_JAVA_ARGS => '-Xmx96m'
+
+Note: EXTRA_JAVA_ARGS only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item EMBEDDED_JNI
+
+Same as JNI, except C<Inline::Java> expects the JVM to already be
+loaded and to have loaded the Perl interpreter that is running the
+script. This is an advanced feature that should only be need in
+very specific circumstances.
+
+ Ex: EMBEDDED_JNI => 1
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM. Also, the EMBEDDED_JNI option automatically
+sets the JNI option.
+
+=item SHARED_JVM
+
+This mode enables mutiple processes to share the same JVM. It was
+created mainly in order to be able to use C<Inline::Java> under
+mod_perl.
+
+ Ex: SHARED_JVM => 1
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item START_JVM
+
+When used with SHARED_JVM, tells C<Inline::Java> that the JVM should
+already be running and that it should not attempt to start a new
+one. This option is useful in combination with command line interface
+described in the BUGS AND DEFICIENCIES section. Default is 1.
+
+ Ex: START_JVM => 0
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item PRIVATE
+
+In SHARED_JVM mode, makes every connection to the JVM use a different
+classloader so that each connection is isolated from the others.
+
+ Ex: PRIVATE => 1
+
+Note: This configuration option only has an effect on the first
+'use Inline Java' call inside a Perl script, since all other calls
+make use of the same JVM.
+
+=item DEBUG
+
+Enables debugging info. Debugging now uses levels (1 through 5)
+that (loosely) follow these definitions:
+
+ 1 = Major program steps
+ 2 = Object creation/destruction
+ 3 = Method/member accesses + packet dumps
+ 4 = Everything else
+ 5 = Data structure dumps
+
+ Ex: DEBUG => 2
+
+=item DEBUGGER
+
+Starts jdb, (the Java debugger) instead of the regular Java JVM.
+This option will also cause the Java code to be compiled using the
+'-g' switch for extra debugging information. EXTRA_JAVA_ARGS can
+be used use to pass extra options to the debugger.
+
+ Ex: DEBUGGER => 1
+
+=item WARN_METHOD_SELECT
+
+Throws a warning when C<Inline::Java> has to 'choose' between
+different method signatures. The warning states the possible
+choices and the signature chosen.
+
+ Ex: WARN_METHOD_SELECT => 1
+
+=item STUDY
+
+Takes an array of Java classes that you wish to have
+C<Inline::Java> learn about so that you can use them inside Perl.
+
+ Ex: STUDY => ['java.lang.HashMap', 'my.class']
+
+=item AUTOSTUDY
+
+Makes C<Inline::Java> automatically study unknown classes it
+encounters them.
+
+ Ex: AUTOSTUDY => 1
+
+=item PACKAGE
+
+Forces C<Inline::Java> to bind the Java code under the specified
+package instead of under the current (caller) package.
+
+ Ex: PACKAGE => 'main'
+
+=item NATIVE_DOUBLES
+
+Normally, C<Inline::Java> stringifies floating point numbers when passing
+them between Perl and Java. In certain cases, this can lead to loss of
+precision. When NATIVE_DOUBLES is set, C<Inline::Java> will send the actual
+double bytes in order to preserve precision.
+Note: This applies only to doubles, not floats.
+Note: This option may not be portable and may not work properly on some
+platforms.
+
+ Ex: NATIVE_DOUBLES => 1
+
+=back
+
+
+=head1 ENVIRONMENT VARIABLES
+
+Every configuration option listed above, with the exception of STUDY,
+can be specified using an environment variable named using the
+following convention:
+
+ PERL_INLINE_JAVA_<option name>
+
+For example, your can specified the JNI option usng the
+PERL_INLINE_JAVA_JNI environment variable.
+
+Note that environment variables take precedence over options specified
+in the script itself.
+
+Under Win32, you can also use set the PERL_INLINE_JAVA_COMMAND_COM
+environment variable to a true value to indicate that you are using
+the command.com shell. However, C<Inline::Java> should normally be
+able to determine this on its own.
+
+
+=head1 CLASSES AND OBJECTS
+
+Because Java is object oriented, any interface between Perl and Java
+needs to support Java classes adequately.
+
+Example:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ class Pod_1 {
+ String data = "data" ;
+ static String sdata = "static data" ;
+
+ public Pod_1(){
+ }
+
+ public String get_data(){
+ return data ;
+ }
+
+ public static String get_static_data(){
+ return sdata ;
+ }
+
+ public void set_data(String d){
+ data = d ;
+ }
+
+ private void priv(){
+ }
+ }
+ END
+
+ my $obj = new Pod_1 ;
+ print($obj->get_data() . "\n") ; # prints data
+ $obj->set_data("new data") ;
+ print($obj->get_data() . "\n") ; # prints new data
+
+=for comment
+
+C<Inline::Java> created a new namespace called C<main::Pod_1> and
+created the following functions:
+
+ sub main::Pod_::new { ... }
+ sub main::Pod_::Pod_1 { ... }
+ sub main::Pod_::get_data { ... }
+ sub main::Pod_::get_sdata { ... }
+ sub main::Pod_::set_data { ... }
+ sub main::Pod_::DESTROY { ... }
+
+Note that only the public methods are exported to Perl.
+
+Inner classes are also supported, you simply need to supply a reference
+to an outer class object as the first parameter of the constructor:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ class Pod_2 {
+ public Pod_2(){
+ }
+
+ public class Pod_2_Inner {
+ public String name = "Pod_2_Inner" ;
+
+ public Pod_2_Inner(){
+ }
+ }
+ }
+ END
+
+ my $obj = new Pod_2() ;
+ my $obj2 = new Pod_2::Pod_2_Inner($obj) ;
+ print($obj2->{name} . "\n") ; # prints Pod_2_Inner
+
+=for comment
+
+=head1 METHODS
+
+In the previous example we have seen how to call a method. You can also
+call static methods in the following manner:
+
+ print Pod_1->get_sdata() . "\n" ; # prints static data
+ # or
+ my $obj = new Pod_1() ;
+ print $obj->get_sdata() . "\n" ; # prints static data
+
+You can pass any kind of Perl scalar or any Java object to a method. It
+will be automatically converted to the correct type:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ class Pod_3_arg {
+ public Pod_3_arg(){
+ }
+ }
+ class Pod_3 {
+ public int n ;
+
+ public Pod_3(int i, String j, Pod_3_arg k) {
+ n = i ;
+ }
+ }
+ END
+
+ my $obj = new Pod_3_arg() ;
+ my $obj2 = new Pod_3(5, "toto", $obj) ;
+ print($obj2->{n} . "\n") ; # prints 5
+
+=for comment
+
+will work fine. These objects can be of any type, even if these types
+are not known to C<Inline::Java>. This is also true for return types:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+
+ class Pod_4 {
+ public Pod_4(){
+ }
+
+ public HashMap get_hash(){
+ HashMap h = new HashMap() ;
+ h.put("key", "value") ;
+
+ return h ;
+ }
+
+ public String do_stuff_to_hash(HashMap h){
+ return (String)h.get("key") ;
+ }
+ }
+ END
+
+ my $obj = new Pod_4() ;
+ my $h = $obj->get_hash() ;
+ print($obj->do_stuff_to_hash($h) . "\n") ; # prints value
+
+=for comment
+
+Objects of types unknown to Perl can exist in the Perl space, you just
+can't call any of their methods. See the STUDYING section for more
+information on how to tell C<Inline::Java> to learn about these classes.
+ Z<>
+
+
+=head1 MEMBER VARIABLES
+
+You can also access all public member variables (static or not) from Perl.
+As with method arguments, the types of these variables does not need to
+be known to Perl:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+
+ class Pod_5 {
+ public int i ;
+ public static HashMap hm ;
+
+ public Pod_5(){
+ }
+ }
+ END
+
+ my $obj = new Pod_5() ;
+ $obj->{i} = 2 ;
+ print($obj->{i} . "\n") ; # prints 2
+ my $hm1 = $obj->{hm} ; # instance way
+ my $hm2 = $Pod_4::hm ; # static way
+
+=for comment
+
+Note: Watch out for typos when accessing members in the static fashion,
+'use strict' will not catch them since they have a package name...
+ Z<>
+
+
+=head1 ARRAYS
+
+You can also send, receive and modify arrays. This is done simply by
+using Perl lists:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+
+ class Pod_6 {
+ public int i[] = {5, 6, 7} ;
+
+ public Pod_6(){
+ }
+
+ public String [] f(String a[]){
+ return a ;
+ }
+
+ public String [][] f(String a[][]){
+ return a ;
+ }
+ }
+ END
+
+ my $obj = new Pod_6() ;
+ my $i_2 = $obj->{i}->[2] ; # 7
+ print($i_2 . "\n") ; # prints 7
+
+ my $a1 = $obj->f(["a", "b", "c"]) ; # String []
+ my $a2 = $obj->f([
+ ["00", "01"],
+ ["10", "11"],
+ ]) ; # String [][]
+ print($a2->[1]->[0] . "\n") ; # prints 10
+
+=for comment
+
+
+=head1 EXCEPTIONS
+
+You can now (as of 0.31) catch exceptions as objects when they are thrown
+from Java. To do this you use the regular Perl exception tools: eval and
+$@. A helper function named 'caught' is provided to help determine the
+type of the exception. Here is a example of a typical use:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+
+ class Pod_9 {
+ public Pod_9(boolean t) throws Exception {
+ if (t){
+ throw new Exception("ouch!") ;
+ }
+ }
+ }
+ END
+
+ use Inline::Java qw(caught) ;
+
+ eval {
+ my $obj = new Pod_9(1) ;
+ } ;
+ if ($@){
+ if (caught("java.lang.Exception")){
+ my $msg = $@->getMessage() ;
+ print($msg . "\n") ; # prints ouch!
+ }
+ else{
+ # It wasn't a Java exception after all...
+ die $@ ;
+ }
+ }
+
+=for comment
+
+What's important to understand is that $@ actually contains a reference
+to the Throwable object that was thrown by Java. The getMessage() function
+is really a method of the java.lang.Exception class. So if Java is throwing
+a custom exception you have in your code, you will have access to that
+exception object's public methods just like any other Java object in
+C<Inline::Java>.
+Note: C<Inline::Java> uses eval under the hood, so it recommended that you
+store any exception in a temporary variable before processing it, especially
+f you will be calling other C<Inline::Java> functions. It is also probably
+a good idea to undef $@ once you have treated a Java exception, or else
+the object still has a reference until $@ is reset by the next eval.
+ Z<>
+
+
+=head1 FILEHANDLES
+
+Java filehandles (java.io.Reader, java.io.Writer, java.io.InputStream or
+java.io.OutputStream objects) can be wrapped the C<Inline::Java::Handle>
+class to allow reading or writing from Perl. Here's an example:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.io.* ;
+
+ class Pod_91 {
+ public static Reader getReader(String file) throws FileNotFoundException {
+ return new FileReader(file) ;
+ }
+ }
+ END
+
+ my $o = Pod_91->getReader('data.txt') ;
+ my $h = new Inline::Java::Handle($o) ;
+ while (<$h>){
+ chomp($_) ;
+ print($_ . "\n") ; # prints data
+ }
+
+
+=for comment
+
+What's important to understand is that $@ actually contains a reference
+to the Throwable object that was thrown by Java. The getMessage() function
+is really a method of the java.lang.Exception class. So if Java is throwing
+a custom exception you have in your code, you will have access to that
+exception object's public methods just like any other Java object in
+C<Inline::Java>. It is also probably a good idea to undef $@ once you have
+treated a Java exception, or else the object still has a reference until
+$@ is reset by the next eval.
+ Z<>
+
+
+=head1 CALLBACKS
+
+See L<Inline::Java::Callbacks> for more information on making callbacks.
+
+
+=head1 STUDYING
+
+As of version 0.21, C<Inline::Java> can learn about other Java classes
+and use them just like the Java code you write inside your Perl script.
+In fact you are not even required to write Java code inside your Perl
+script anymore. Here's how to use the 'studying' function:
+
+=for comment
+
+ use Inline (
+ Java => 'STUDY',
+ STUDY => ['java.util.HashMap'],
+ ) ;
+
+ my $hm = new java::util::HashMap() ;
+ $hm->put("key", "value") ;
+ my $val = $hm->get("key") ;
+ print($val . "\n") ; # prints value
+
+=for comment
+
+If you do not wish to put any Java code inside you Perl script, you must
+use the string 'STUDY' as your code. This will skip the build section.
+
+You can also use the AUTOSTUDY option to tell C<Inline::Java> that you wish
+to study all classes that it comes across:
+
+=for comment
+
+ use Inline Java => <<'END', AUTOSTUDY => 1 ;
+ import java.util.* ;
+
+ class Pod_10 {
+ public Pod_10(){
+ }
+
+ public HashMap get_hm(){
+ HashMap hm = new HashMap() ;
+ return hm ;
+ }
+ }
+ END
+
+ my $obj = new Pod_10() ;
+ my $hm = $obj->get_hm() ;
+ $hm->put("key", "value") ;
+ my $val = $hm->get("key") ;
+ print($val . "\n") ; # prints value
+
+=for comment
+
+In this case C<Inline::Java> intercepts the return value of the get_hm()
+method, sees that it's of a type that it doesn't know about
+(java.lang.HashMap), and immediately studies the class. After that call
+the java::lang::HashMap class is available to use through Perl.
+
+In some cases you may not know which classes to study until runtime. In
+these cases you can use the study_classes() function:
+
+=for comment
+
+ use Inline (
+ Java => 'STUDY',
+ STUDY => [],
+ ) ;
+ use Inline::Java qw(study_classes) ;
+
+ study_classes(['java.util.HashMap'], undef) ;
+ my $hm = new java::util::HashMap() ;
+ $hm->put("key", "value") ;
+ my $val = $hm->get("key") ;
+ print($val . "\n") ; # prints value
+
+=for comment
+
+The study_classes() function takes 2 arguments, a reference to an array of
+class names (like the STUDY configuration option) and the name of the
+package in which to bind those classes. If the name of the package is
+undefined, the classes will be bound to the current (caller) package.
+
+Note: You can only specify the names of packages in which you have
+previously "used" C<Inline::Java>.
+ Z<>
+
+
+=head1 TYPE CASTING
+
+Sometimes you need to manipulate a Java object using a specific
+subtype. That's when type casting is necessary. Here's an
+example of this:
+
+=for comment
+
+ use Inline (
+ Java => 'STUDY',
+ STUDY => ['java.util.HashMap'],
+ AUTOSTUDY => 1,
+ ) ;
+ use Inline::Java qw(cast) ;
+
+ my $hm = new java::util::HashMap() ;
+ $hm->put('key', 'value') ;
+
+ my $entries = $hm->entrySet()->toArray() ;
+ foreach my $e (@{$entries}){
+ # print($e->getKey() . "\n") ; # No!
+ print(cast('java.util.Map$Entry', $e)->getKey() . "\n") ; # prints key
+ }
+
+=for comment
+
+In this case, C<Inline::Java> knows that $e is of type java.util.HashMap$Entry.
+The problem is that this type is not public, and therefore we can't access
+the object through that type. We must cast it to a java.util.Map$Entry, which
+is a public interface and will allow us to access the getKey() method.
+
+You can also use type casting to force the selection of a specific method
+signature for methods that have multiple signatures. See examples similar
+to this in the "TYPE COERCING" section below.
+
+
+=head1 TYPE COERCING
+
+Type coercing is the equivalent of casting for primitives types
+and arrays. It is used to force the selection if a specific method
+signature when C<Inline::Java> has multiple choices. The coerce
+function returns a special object that can only be used when calling
+Java methods or assigning Java members. Here is an example:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ class Pod_101 {
+ public Pod_101(){
+ }
+
+ public String f(int i){
+ return "int" ;
+ }
+
+ public String f(char c){
+ return "char" ;
+ }
+ }
+ END
+
+ my $obj = new Pod_101() ;
+ print($obj->f('5') . "\n") ; # prints int
+
+=for comment
+
+In this case, C<Inline::Java> will call f(int i), because '5' is an integer.
+But '5' is a valid char as well. So to force the call of f(char c), do the
+following:
+
+ use Inline::Java qw(coerce) ;
+ $obj->f(coerce('char', '5')) ;
+ # or
+ $obj->f(Inline::Java::coerce('char', '5')) ;
+
+The coerce function forces the selection of the matching signature. Note that
+the coerce must match the argument type exactly. Coercing to a class that
+extends the argument type will not work.
+
+Another case where type coercing is needed is when one wants to pass an array
+as a java.lang.Object:
+
+ use Inline Java => <<'END';
+ class Pod_8 {
+ public Object o ;
+ int a[] = {1, 2, 3} ;
+
+ public Pod_8() {
+ }
+ }
+ END
+
+ my $obj = new Pod_8() ;
+ $obj->{o} = [1, 2, 3] ; # No!
+
+The reason why this will not work is simple. When C<Inline::Java> sees an
+array, it checks the Java type you are trying to match it against to validate
+the construction of your Perl list. But in this case, it can't validate
+the array because you're assigning it to an Object. You must use the 3
+parameter version of the coerce function to do this:
+
+ $obj->{o} = Inline::Java::coerce(
+ "java.lang.Object",
+ [1, 2, 3],
+ "[Ljava.lang.String;") ;
+
+This tells C<Inline::Java> to validate your Perl list as a String [], and
+then coerce it as an Object.
+
+Here is how to construct the array type representations:
+
+ [<type> -> 1 dimensional <type> array
+ [[<type> -> 2 dimensional <type> array
+ ...
+
+ where <type> is one of:
+ B byte S short I int J long
+ F float D double C char Z boolean
+
+ L<class>; array of <class> objects
+
+This is described in more detail in most Java books that talk about
+reflection.
+
+But you only need to do this if you have a Perl list. If you already have a
+Java array reference obtained from elsewhere, you don't even need to coerce:
+
+ $obj->{o} = $obj->{a} ;
+
+
+=head1 JNI vs CLIENT/SERVER MODES
+
+Starting in version 0.20, it is possible to use the JNI (Java Native
+Interface) extension. This enables C<Inline::Java> to load the Java virtual
+machine as a shared object instead of running it as a stand-alone server.
+This brings an improvement in performance.
+
+If you have built the JNI extension, you must enable it explicitely by doing
+one of the following:
+
+=over 4
+
+=item 1
+
+Set the JNI configuration option to 1
+
+=item 2
+
+Set the PERL_INLINE_JAVA_JNI environment variable to 1
+
+=back
+
+
+Note: C<Inline::Java> only creates one virtual machine instance. Therefore
+you can't use JNI for some sections and client/server for others. The first
+section determines the execution mode.
+
+See README.JNI for more information about the JNI extension.
+ Z<>
+
+
+=head1 SHARED_JVM
+
+Starting with version 0.30, the C<Inline::Java> JVM can now be shared between
+multiple processes. The first process to start creates the JVM but does not
+shut it down on exit. All other processes can then connect as needed to the JVM.
+If any of these other processes where created by forking the parent process,
+the Inline::Java->reconnect_JVM() function must be called in the child to get
+a fresh connection to the JVM. Ex:
+
+=for comment
+
+ use Inline (
+ Java => <<'END',
+ class Pod_11 {
+ public static int i = 0 ;
+ public Pod_11(){
+ i++ ;
+ }
+ }
+ END
+ SHARED_JVM => 1,
+ ) ;
+
+ my $nb = 5 ;
+ for (my $i = 0 ; $i < $nb ; $i++){
+ if (! fork()){
+ Inline::Java::reconnect_JVM() ;
+ my $f = new Pod_11() ;
+ exit ;
+ }
+ }
+ sleep(5) ;
+
+ my $f = new Pod_11() ;
+ print($f->{i} . "\n") ; # prints 6
+
+=for comment
+
+Once this code was run, each of the 6 processes will have created a different
+instance of the 't' class. Data can be shared between the processes by using
+static members in the Java code.
+
+Note: The Java System.out stream is closed in SHARED_JVM mode.
+ Z<>
+
+
+=head1 USING Inline::Java IN A CGI
+
+If you want to use C<Inline::Java> in a CGI script, do the following:
+
+=for comment
+
+ use CGI ;
+ use Inline (
+ Java => <<'END',
+ class Pod_counter {
+ public static int cnt = 0 ;
+ public Pod_counter(){
+ cnt++ ;
+ }
+ }
+ END
+ SHARED_JVM => 1,
+ DIRECTORY => '/somewhere/your/web/server/can/write',
+ ) ;
+
+ my $c = new Pod_counter() ;
+ my $q = new CGI() ;
+ print
+ $q->start_html() .
+ "This page has been accessed " . $c->{cnt} . " times." .
+ $q->end_html() ;
+
+=for comment
+
+In this scenario, the first CGI to execute will start the JVM, but does
+not shut it down on exit. Subsequent CGI, since they have the SHARED_JVM
+option enabled, will try to connect to the already existing JVM before
+trying to start a new one. Therefore if the JVM happens to crash or is
+killed, the next CGI that runs will start a new one. The JVM will be
+killed when Apache is shut down.
+
+See the BUGS AND DEFICIENCIES section if you have problems starting
+the SHARED_JVM server in a CGI.
+ Z<>
+
+
+=head1 USING Inline::Java UNDER MOD_PERL
+
+Here is an example of how to use C<Inline::Java> under mod_perl:
+
+ use Apache2::Const qw(:common) ;
+ use Inline (
+ Java => <<'END',
+ class Pod_counter {
+ public static int cnt = 0 ;
+ public Pod_counter(){
+ cnt++ ;
+ }
+ }
+ END
+ SHARED_JVM => 1,
+ DIRECTORY => '/somewhere/your/web/server/can/write',
+ ) ;
+
+ my $c = new Pod_counter() ;
+
+ sub handler {
+ my $r = shift ;
+
+ my $q = new CGI ;
+ print
+ $q->start_html() .
+ "This page has been accessed " . $c->{cnt} . " times." .
+ $q->end_html() ;
+
+ return OK ;
+ }
+
+See USING Inline::Java IN A CGI for more details.
+
+If you are using L<ModPerl::Registry>, make sure to use the C<PACKAGE>
+configuration option to specifiy the package in which C<Inline::Java>
+should bind the Java code, since L<ModPerl::Registry> will place your
+code in a package with a unpredictable name.
+
+See the BUGS AND DEFICIENCIES section if you have problems starting
+the SHARED_JVM server under MOD_PERL.
+
+=head2 Preloading and PerlChildInitHandler
+
+If you are loading C<Inline::Java> during your server startup (common practice to
+increase shared memory and reduce run time) and you are using C<SHARED_JVM>, then
+your Apache processes will all share the same socktd connection to that JVM.
+This will result in garbled communication and strange errors (like "Can't receive packet from JVM", "Broken pipe", etc).
+
+To fix this you need to tell Apache that after each child process has forked they
+each need to create their own connections to the JVM. This is done during the
+C<ChildInit> stage.
+
+For Apache 1.3.x this could look like:
+
+ # in httpd.conf
+ PerlChildInitHandler MyProject::JavaReconnect
+
+And C<MyProject::JavaReconnect> could be as simple as this:
+
+ package MyProject::JavaReconnect;
+ sub handler($$) { Inline::Java::reconnect_JVM() }
+ 1;
+
+
+=head1 BUGS AND DEFICIENCIES
+
+When reporting a bug, please do the following:
+
+ - Put "use Inline REPORTBUG;" at the top of your code, or
+ use the command line option "perl -MInline=REPORTBUG ...".
+ - Run your code.
+ - Follow the printed instructions.
+
+Here are some things to watch out for:
+
+=over 4
+
+=item 1
+
+You shouldn't name any of your classes 'B', 'S', 'I', 'J', 'F', 'D',
+'C', 'Z' or 'L'. These classes seem to be used internally by Java to
+represent the primitive types.
+
+=item 2
+
+If you upgrade C<Inline::Java> from a previous version, be sure to delete
+your _Inline directory so that C<Inline::Java>'s own Java classes get
+rebuilt to match the Perl code.
+
+=item 3
+
+Under certain environments, i.e. CGI or mod_perl, the JVM cannot start
+properly because of the way these environments set up STDIN and STDOUT.
+In these cases, you may wish to control the JVM (in shared mode) manually
+using the following commands:
+
+ % perl -MInline::Java::Server=status
+ % perl -MInline::Java::Server=start
+ % perl -MInline::Java::Server=stop
+ % perl -MInline::Java::Server=restart
+
+You can specify C<Inline::Java> options by setting the proper
+environment variables, and you can also set the _Inline directory by using
+the PERL_INLINE_JAVA_DIRECTORY environment variable.
+
+In addition, you may also wish to set the START_JVM option to 0 in your scripts
+to prevent them from trying to start their own JVM if they can't find one,
+thereby causing problems.
+
+=item 4
+
+Because of problems with modules C<Inline::Java> depends on, the usage of paths
+containing spaces is not fully supported on all platforms. This applies to the
+installation directory as well as the path for J2SDK and CLASSPATH elements.
+
+=item 5
+
+Even though it in run through a profiler regularly, C<Inline::Java> is relatively
+slow compared to native Perl or Java.
+
+
+=back
+
+
+=head1 SEE ALSO
+
+L<Inline::Java::Callback>, L<Inline::Java::PerlNatives>,
+L<Inline::Java::PerlInterpreter>.
+
+For information about using C<Inline>, see L<Inline>.
+
+For information about other Inline languages, see L<Inline-Support>.
+
+C<Inline::Java>'s mailing list is <inline@perl.org>.
+To subscribe, send an email to <inline-subscribe@perl.org>
+ Z<>
+
+
+=head1 AUTHOR
+
+Patrick LeBoutillier <patl@cpan.org> is the author of Inline::Java.
+
+Brian Ingerson <ingy@cpan.org> is the author of Inline.
+ Z<>
+
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2005, Patrick LeBoutillier.
+
+All Rights Reserved. This module is free software. It may be used,
+redistributed and/or modified under the terms of the Perl Artistic
+License. See http://www.perl.com/perl/misc/Artistic.html for more
+details.
+
+=cut
diff --git a/Java/Array.pm b/Java/Array.pm
new file mode 100644
index 0000000..b98a7d6
--- /dev/null
+++ b/Java/Array.pm
@@ -0,0 +1,634 @@
+package Inline::Java::Array ;
+@Inline::Java::Array::ISA = qw(Inline::Java::Array::Tie) ;
+
+use strict ;
+use Carp ;
+
+$Inline::Java::Array::VERSION = '0.53' ;
+
+# Here we store as keys the knots and as values our blessed objects
+my $OBJECTS = {} ;
+
+
+sub new {
+ my $class = shift ;
+ my $object = shift ;
+
+ my @this = () ;
+ my $knot = tie @this, $class ;
+ my $this = bless (\@this, $class) ;
+
+ $OBJECTS->{$knot} = $object ;
+
+ Inline::Java::debug(5, "this = '$this'") ;
+ Inline::Java::debug(5, "knot = '$knot'") ;
+
+ return $this ;
+}
+
+
+sub __get_object {
+ my $this = shift ;
+
+ my $knot = tied @{$this} || $this ;
+
+ my $ref = $OBJECTS->{$knot} ;
+ if (! defined($ref)){
+ croak "Unknown Java array reference '$knot'" ;
+ }
+
+ return $ref ;
+}
+
+
+sub __isa {
+ my $this = shift ;
+ my $proto = shift ;
+
+ return $this->__get_object()->__isa($proto) ;
+}
+
+
+sub length {
+ my $this = shift ;
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = undef ;
+ eval {
+ # Check the cached value
+ $ret = $obj->__get_private()->{array_length} ;
+ if (! defined($ret)){
+ $ret = $obj->__get_private()->{proto}->CallJavaMethod('getLength', [], []) ;
+ $obj->__get_private()->{array_length} = $ret ;
+ }
+ else{
+ Inline::Java::debug(4, "using cached array length $ret") ;
+ }
+ } ;
+ croak $@ if $@ ;
+
+ return $ret ;
+}
+
+
+sub __get_element {
+ my $this = shift ;
+ my $idx = shift ;
+
+ my $max = $this->length() - 1 ;
+ if ($idx > $max){
+ croak("Java array index out of bounds ($idx > $max)")
+ }
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = undef ;
+ eval {
+ $ret = $obj->__get_private()->{proto}->GetJavaMember($idx, ['java.lang.Object'], [undef]) ;
+ } ;
+ croak $@ if $@ ;
+
+ return $ret ;
+}
+
+
+sub __set_element {
+ my $this = shift ;
+ my $idx = shift ;
+ my $s = shift ;
+
+ my $max = $this->length() - 1 ;
+ if ($idx > $max){
+ croak("Java array index out of bounds ($idx > $max)")
+ }
+
+ my $obj = $this->__get_object() ;
+
+ # Now we need to find out if what we are trying to set matches
+ # the array.
+ my $java_class = $obj->__get_private()->{java_class} ;
+ my $elem_class = $java_class ;
+ my $an = Inline::Java::Array::Normalizer->new($obj->__get_private()->{inline}, $java_class) ;
+ if ($an->{req_nb_dim} > 1){
+ $elem_class =~ s/^\[// ;
+ }
+ else{
+ $elem_class = $an->{req_element_class} ;
+ }
+
+ my $ret = undef ;
+ eval {
+ my ($new_args, $score) = Inline::Java::Class::CastArguments([$s], [$elem_class], $obj->__get_private()->{inline}) ;
+ $ret = $obj->__get_private()->{proto}->SetJavaMember($idx, [$elem_class], $new_args) ;
+ } ;
+ croak $@ if $@ ;
+
+ return $ret ;
+}
+
+
+sub AUTOLOAD {
+ my $this = shift ;
+ my @args = @_ ;
+
+ use vars qw($AUTOLOAD) ;
+ my $func_name = $AUTOLOAD ;
+ # Strip package from $func_name, Java will take of finding the correct
+ # method.
+ $func_name =~ s/^(.*)::// ;
+
+ croak "Can't call method '$func_name' on Java arrays" ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+ my $knot = tied @{$this} ;
+ if (! $knot){
+ Inline::Java::debug(4, "destroying Inline::Java::Array::Tie") ;
+
+ $OBJECTS->{$this} = undef ;
+ }
+ else{
+ Inline::Java::debug(4, "destroying Inline::Java::Array") ;
+ }
+}
+
+
+
+
+######################## Array methods ########################
+package Inline::Java::Array::Tie ;
+@Inline::Java::Array::Tie::ISA = qw(Tie::StdArray) ;
+
+
+use Tie::Array ;
+use Carp ;
+
+
+sub TIEARRAY {
+ my $class = shift ;
+
+ return $class->SUPER::TIEARRAY(@_) ;
+}
+
+
+sub FETCHSIZE {
+ my $this = shift ;
+
+ my $array = $this->length() ;
+}
+
+
+sub STORE {
+ my $this = shift ;
+ my $idx = shift ;
+ my $s = shift ;
+
+ return $this->__set_element($idx, $s) ;
+}
+
+
+sub FETCH {
+ my $this = shift ;
+ my $idx = shift ;
+
+ return $this->__get_element($idx) ;
+}
+
+
+sub EXISTS {
+ my $this = shift ;
+ my $idx = shift ;
+
+ return $this->SUPER::EXISTS($idx) ;
+}
+
+
+sub STORESIZE {
+ my $this = shift ;
+ my $size = shift ;
+
+ croak "Operation STORESIZE not supported on Java array" ;
+}
+
+
+sub CLEAR {
+ my $this = shift ;
+
+ croak "Operation CLEAR not supported on Java array" ;
+}
+
+
+sub POP {
+ my $this = shift ;
+
+ croak "Operation POP not supported on Java array" ;
+}
+
+
+sub PUSH {
+ my $this = shift ;
+ my @list = @_ ;
+
+ croak "Operation PUSH not supported on Java array" ;
+}
+
+
+sub SHIFT {
+ my $this = shift ;
+
+ croak "Operation SHIFT not supported on Java array" ;
+}
+
+
+sub UNSHIFT {
+ my $this = shift ;
+ my @list = @_ ;
+
+ croak "Operation UNSHIFT not supported on Java array" ;
+}
+
+
+sub DELETE {
+ my $this = shift ;
+ my $idx = shift ;
+
+ croak "Operation DELETE not supported on Java array" ;
+}
+
+
+sub EXTEND {
+ my $this = shift ;
+ my $count = shift ;
+
+ croak "Operation EXTEND not supported on Java array" ;
+}
+
+
+sub SPLICE {
+ my $this = shift ;
+ my $offset = shift ;
+ my $length = shift ;
+ my @LIST = @_ ;
+
+ croak "Operation SPLICE not supported on Java array" ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+}
+
+
+
+######################## Inline::Java::Array::Normalizer ########################
+package Inline::Java::Array::Normalizer ;
+
+
+use Carp ;
+
+
+sub new {
+ my $class = shift ;
+ my $inline = shift ;
+ my $java_class = shift ;
+ my $ref = shift ;
+
+ if (! Inline::Java::Class::ClassIsArray($java_class)){
+ croak "Can't create Java array of non-array class '$java_class'" ;
+ }
+
+ my $this = {} ;
+ $this->{class} = $class ;
+ $this->{java_class} = $java_class ;
+ $this->{map} = {} ;
+ $this->{ref} = $ref ;
+ $this->{array} = [] ;
+ $this->{score} = 0 ;
+ $this->{inline} = $inline ;
+
+ bless ($this, $class) ;
+
+ # The first thing we want to do is figure out what kind of array we want,
+ # and how many dimensions it should have.
+ $this->AnalyzeArrayClass() ;
+
+ if ($ref){
+ $this->InitFromArray($this->{array}) ;
+ }
+
+ return $this ;
+}
+
+
+sub InitFromArray {
+ my $this = shift ;
+ my $array = shift ;
+ my $level = shift ;
+
+ my $ref = $this->{ref} ;
+
+ $this->ValidateArray($ref, $array) ;
+
+ if (! $level){
+ Inline::Java::debug_obj($this) ;
+ }
+}
+
+
+sub InitFromFlat {
+ my $this = shift ;
+ my $inline = shift ;
+ my $dims = shift ;
+ my $list = shift ;
+ my $level = shift ;
+
+ my $nb_list = scalar(@{$list}) ;
+ my $parts = $dims->[0] ;
+
+ my $req_nb_elem = 1 ;
+ foreach my $d (@{$dims}){
+ $req_nb_elem *= $d ;
+ }
+ if ($req_nb_elem != $nb_list){
+ my $ds = "[" . join("][", @{$dims}) . "]" ;
+ croak "Corrupted array: $ds should contain $req_nb_elem elements, has $nb_list" ;
+ }
+
+ for (my $i = 0 ; $i < $parts ; $i++){
+ my $elem = undef ;
+ if (scalar(@{$dims}) == 1){
+ # We are at the bottom of the list.
+ $elem = $list->[$i] ;
+ }
+ else{
+ my $nb_elems = $nb_list / $parts ;
+ my @sub = splice(@{$list}, 0, $nb_elems) ;
+
+ my $java_class = $this->{java_class} ;
+ $java_class =~ s/^\[// ;
+
+ my @dims = @{$dims} ;
+ shift @dims ;
+ my $obj = Inline::Java::Array::Normalizer->new($inline, $java_class) ;
+ $obj->InitFromFlat($inline, \@dims, \@sub, $level + 1) ;
+ $elem = $obj->{array} ;
+ }
+ my $nb = scalar(@{$this->{array}}) ;
+ $this->{array}->[$nb] = $elem ;
+ }
+
+ if (! $level){
+ # Inline::Java::debug_obj($this) ;
+ }
+}
+
+
+# Checks if the contents of the Array match the ones prescribed
+# by the Java prototype.
+sub AnalyzeArrayClass {
+ my $this = shift ;
+
+ my $java_class = $this->{java_class} ;
+
+ my ($depth_str, $type, $class) = Inline::Java::Class::ValidateClassSplit($java_class) ;
+ $depth_str =~ /^(\[+)/ ;
+ my $depth = length($depth_str) ;
+
+ my %map = (
+ B => 'byte',
+ S => 'short',
+ I => 'int',
+ J => 'long',
+ F => 'float',
+ D => 'double',
+ C => 'char',
+ Z => 'boolean',
+ L => $class,
+ ) ;
+
+ my $pclass = $map{$type} ;
+ if (! $pclass){
+ croak "Can't determine array type for '$java_class'" ;
+ }
+
+ $this->{req_element_class} = $pclass ;
+ $this->{req_nb_dim} = $depth ;
+
+ return ;
+}
+
+
+# This method makes sure that we have a valid array that
+# can be used in a Java function. It will return an array
+# That contains either all scalars or all object references
+# at the lowest level.
+sub ValidateArray {
+ my $this = shift ;
+ my $ref = shift ;
+ my $array = shift ;
+ my $level = shift || 0 ;
+
+ if (! ((defined($ref))&&(UNIVERSAL::isa($ref, "ARRAY")))){
+ # We must start with an array of some kind...
+ croak "'$ref' is not an array reference" ;
+ }
+
+ my $map = $this->{map} ;
+ if (! exists($map->{$level}->{max})){
+ $map->{$level}->{max} = 0 ;
+ }
+ $this->ValidateElements($ref, $array, $level) ;
+
+ foreach my $elem (@{$ref}){
+ if ((defined($elem))&&(UNIVERSAL::isa($elem, "ARRAY"))){
+ # All the elements at this level are sub-arrays.
+ my $sarray = [] ;
+ $this->ValidateArray($elem, $sarray, $level + 1) ;
+ push @{$array}, $sarray ;
+ }
+ }
+
+ $this->FillArray($array, $level) ;
+
+ if (! $level){
+ my @levels = (sort {$a <=> $b} keys %{$map}) ;
+ my $nbl = scalar(@levels) ;
+
+ my $last = $levels[$nbl - 1] ;
+ my @dims = () ;
+ my $max_cells = 1 ;
+ foreach my $l (@levels){
+ push @dims, $map->{$l}->{max} ;
+ $max_cells *= $map->{$l}->{max} ;
+ }
+ my $nb_cells = ($map->{$last}->{count} || 0) ;
+ Inline::Java::debug(4, "array is [" . join("][", @dims) . "]") ;
+ Inline::Java::debug(4, "array has $nb_cells declared cells") ;
+ Inline::Java::debug(4, "array should have $max_cells declared cells") ;
+ $this->{dim} = \@dims ;
+ $this->{nb_dim} = scalar(@dims) ;
+
+ if ($this->{nb_dim} != $this->{req_nb_dim}){
+ croak "Java array should have $this->{req_nb_dim} instead of " .
+ "$this->{nb_dim} dimensions" ;
+ }
+
+ Inline::Java::debug_obj($this) ;
+ }
+}
+
+
+# Makes sure that all the elements are of the same type.
+sub ValidateElements {
+ my $this = shift ;
+ my $ref = shift ;
+ my $array = shift ;
+ my $level = shift ;
+
+ my $map = $this->{map} ;
+ my $cnt = scalar(@{$ref}) ;
+ my $max = $map->{$level}->{max} ;
+ if ($cnt > $max){
+ $map->{$level}->{max} = $cnt ;
+ }
+
+ for (my $i = 0 ; $i < $cnt ; $i++){
+ my $elem = $ref->[$i] ;
+ if (defined($elem)){
+ if (UNIVERSAL::isa($elem, "ARRAY")){
+ $this->CheckMap("SUB_ARRAY", $level) ;
+ }
+ elsif (
+ (UNIVERSAL::isa($elem, "Inline::Java::Object"))||
+ (! ref($elem))){
+ $this->CheckMap("BASE_ELEMENT", $level) ;
+ my @ret = $this->CastArrayArgument($elem) ;
+ $array->[$i] = $ret[0] ;
+ $this->{score} += $ret[1] ;
+ }
+ else{
+ croak "A Java array can only contain scalars, Java objects or array references" ;
+ }
+ }
+ $map->{$level}->{count}++ ;
+ }
+}
+
+sub CheckMap {
+ my $this = shift ;
+ my $type = shift ;
+ my $level = shift ;
+
+ my $map = $this->{map} ;
+
+ if (! exists($map->{$level}->{type})){
+ $map->{$level}->{type} = $type ;
+ }
+ elsif ($map->{$level}->{type} ne $type){
+ croak "Java array contains mixed types in dimension $level ($type != $map->{$level}->{type})" ;
+ }
+}
+
+
+sub CastArrayArgument {
+ my $this = shift ;
+ my $arg = shift ;
+
+ my $element_class = $this->{req_element_class} ;
+
+ my ($new_arg, $score) = Inline::Java::Class::CastArgument($arg, $element_class, $this->{inline}) ;
+
+ return ($new_arg, $score) ;
+}
+
+
+# Makes sure that all the dimensions of the array have the same number of elements
+sub FillArray {
+ my $this = shift ;
+ my $array = shift ;
+ my $level = shift ;
+
+ my $map = $this->{map} ;
+
+ my $max = $map->{$level}->{max} ;
+ my $nb = scalar(@{$array}) ;
+
+ my $type = $map->{$level}->{type} ;
+ # Type can be undefined when array is zero length.
+ if ((defined($type))&&($type eq "SUB_ARRAY")){
+ foreach my $elem (@{$array}){
+ if (! defined($elem)){
+ $elem = [] ;
+ $this->FillArray($elem, $level + 1) ;
+ }
+ }
+ }
+
+ if ($nb < $max){
+ # We must stuff...
+ for (my $i = $nb ; $i < $max ; $i++){
+ if ((defined($type))&&($type eq "SUB_ARRAY")){
+ my $elem = [] ;
+ $this->FillArray($elem, $level + 1) ;
+ push @{$array}, $elem ;
+ }
+ else{
+ push @{$array}, undef ;
+ }
+ }
+ }
+}
+
+
+sub FlattenArray {
+ my $this = shift ;
+ my $level = shift ;
+
+ my $list = $this->MakeElementList($this->{array}) ;
+
+ my $dim = $this->{dim} ;
+ my $req_nb_elem = 1 ;
+ foreach my $d (@{$dim}){
+ $req_nb_elem *= $d ;
+ }
+
+ my $nb_elem = scalar(@{$list}) ;
+ if ($req_nb_elem != $nb_elem){
+ my $ds = "[" . join("][", @{$dim}) . "]" ;
+ croak "Corrupted array: $ds should contain $req_nb_elem elements, has $nb_elem" ;
+ }
+
+ my $ret = [$dim, $list] ;
+
+ Inline::Java::debug_obj($ret) ;
+
+ return $ret ;
+}
+
+
+sub MakeElementList {
+ my $this = shift ;
+ my $array = shift ;
+
+ my @ret = () ;
+ foreach my $elem (@{$array}){
+ if ((defined($elem))&&(UNIVERSAL::isa($elem, "ARRAY"))){
+ my $sret = $this->MakeElementList($elem) ;
+ push @ret, @{$sret} ;
+ }
+ else{
+ # All elements here are base level elements.
+ push @ret, @{$array} ;
+ last ;
+ }
+ }
+
+ return \@ret ;
+}
+
+
+1 ;
diff --git a/Java/Callback.pm b/Java/Callback.pm
new file mode 100644
index 0000000..de3bd1d
--- /dev/null
+++ b/Java/Callback.pm
@@ -0,0 +1,223 @@
+package Inline::Java::Callback ;
+
+use strict ;
+use Carp ;
+
+$Inline::Java::Callback::VERSION = '0.53' ;
+
+$Inline::Java::Callback::OBJECT_HOOK = undef ;
+
+
+my %OBJECTS = () ;
+my $next_id = 1 ;
+
+
+sub InterceptCallback {
+ my $inline = shift ;
+ my $resp = shift ;
+
+ # With JNI we need to store the object somewhere since we
+ # can't drag it along all the way through Java land...
+ if (! defined($inline)){
+ $inline = $Inline::Java::JNI::INLINE_HOOK ;
+ }
+
+ if ($resp =~ s/^callback ([^ ]+) (\@?[\w:]+) ([^ ]+)//){
+ my $via = $1 ;
+ my $function = $2 ;
+ my $cast_return = $3 ;
+ my @args = split(' ', $resp) ;
+
+ # "Relative" namespace...
+ if ($via =~ /^::/){
+ $via = $inline->get_api('pkg') . $via ;
+ }
+ if ($function =~ /^::/){
+ $function = $inline->get_api('pkg') . $function ;
+ }
+
+ return Inline::Java::Callback::ProcessCallback($inline, $via, $function, $cast_return, @args) ;
+ }
+
+ croak "Malformed callback request from server: $resp" ;
+}
+
+
+sub ProcessCallback {
+ my $inline = shift ;
+ my $via = shift ;
+ my $function = shift ;
+ my $cast_return = shift ;
+ my @sargs = @_ ;
+
+ my $list_ctx = 0 ;
+ if ($function =~ s/^\@//){
+ $list_ctx = 1 ;
+ }
+
+ my $pc = new Inline::Java::Protocol(undef, $inline) ;
+ my $thrown = 'false' ;
+
+ my $ret = undef ;
+ my @ret = () ;
+ eval {
+ my @args = map {
+ my $a = $pc->DeserializeObject(0, $_) ;
+ $a ;
+ } @sargs ;
+
+ no strict 'refs' ;
+ if ($via =~ /^(\d+)$/){
+ # Call via object
+ my $id = $1 ;
+ Inline::Java::debug(2, "processing callback $id" . "->" . "$function(" .
+ join(", ", @args) . ")") ;
+ my $obj = Inline::Java::Callback::GetObject($id) ;
+ if ($list_ctx){
+ @ret = $obj->$function(@args) ;
+ }
+ else{
+ $ret = $obj->$function(@args) ;
+ }
+ }
+ elsif ($via ne 'null'){
+ # Call via package
+ Inline::Java::debug(2, "processing callback $via" . "->" . "$function(" .
+ join(", ", @args) . ")") ;
+ if ($list_ctx){
+ @ret = $via->$function(@args) ;
+ }
+ else{
+ $ret = $via->$function(@args) ;
+ }
+ }
+ else {
+ # Straight call
+ Inline::Java::debug(2, "processing callback $function(" .
+ join(", ", @args) . ")") ;
+ if ($function !~ /::/){
+ $function = 'main' . '::' . $function ;
+ }
+ if ($list_ctx){
+ @ret = $function->(@args) ;
+ }
+ else{
+ $ret = $function->(@args) ;
+ }
+ }
+
+ if ($list_ctx){
+ $ret = \@ret ;
+ }
+ } ;
+ if ($@){
+ $ret = $@ ;
+ $thrown = 'true' ;
+
+ if ((ref($ret))&&(! UNIVERSAL::isa($ret, "Inline::Java::Object"))){
+ croak "Can't propagate non-Inline::Java reference exception ($ret) to Java" ;
+ }
+ }
+
+ ($ret) = Inline::Java::Class::CastArgument($ret, $cast_return, $inline) ;
+
+ # Here we must keep a reference to $ret or else it gets deleted
+ # before the id is returned to Java...
+ my $ref = $ret ;
+
+ ($ret) = $pc->ValidateArgs([$ret], 1) ;
+
+ return ("callback $thrown $ret", $ref) ;
+}
+
+
+sub GetObject {
+ my $id = shift ;
+
+ my $obj = $OBJECTS{$id} ;
+ if (! defined($obj)){
+ croak("Can't find object $id") ;
+ }
+
+ return $obj ;
+}
+
+
+sub PutObject {
+ my $obj = shift ;
+
+ my $id = $next_id ;
+ $next_id++ ;
+
+ $OBJECTS{$id} = $obj ;
+
+ return $id ;
+}
+
+
+sub DeleteObject {
+ my $id = shift ;
+ my $quiet = shift || 0 ;
+
+ my $obj = delete $OBJECTS{$id} ;
+ if ((! $quiet)&&(! defined($obj))){
+ croak("Can't find object $id") ;
+ }
+}
+
+
+sub ObjectCount {
+ return scalar(keys %OBJECTS) ;
+}
+
+
+sub __GetObjects {
+ return \%OBJECTS ;
+}
+
+
+
+########## Utility methods used by Java to access Perl objects #################
+
+
+sub java_eval {
+ my $code = shift ;
+
+ Inline::Java::debug(3, "evaling Perl code: $code") ;
+ my $ret = eval $code ;
+ if ($@){
+ die($@) ;
+ }
+
+ return $ret ;
+}
+
+
+sub java_require {
+ my $module = shift ;
+ my $is_file = shift ;
+
+ if (! defined($is_file)){
+ if (-e $module){
+ $module = "\"$module\"" ;
+ }
+ }
+
+ if ($is_file){
+ $module = "\"$module\"" ;
+ }
+
+ Inline::Java::debug(3, "requiring Perl module/file: $module") ;
+ return java_eval("require $module ;") ;
+}
+
+
+sub java_finalize {
+ my $id = shift ;
+ my $gc = shift ;
+
+ Inline::Java::Callback::DeleteObject($id, $gc) ;
+}
+
+
+1 ;
diff --git a/Java/Callback.pod b/Java/Callback.pod
new file mode 100644
index 0000000..2106c57
--- /dev/null
+++ b/Java/Callback.pod
@@ -0,0 +1,406 @@
+=head1 NAME
+
+Inline::Java::Callback - Callback into Perl from Java.
+
+=head1 SYNOPSIS
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import org.perl.inline.java.* ;
+
+ class Pod_caller extends InlineJavaPerlCaller {
+ public Pod_caller() throws InlineJavaException {
+ }
+
+ public String perl()
+ throws InlineJavaException, InlineJavaPerlException {
+
+ return (String)CallPerlSub("main::perl",
+ new Object [] {}) ;
+ }
+ }
+ END
+
+ my $pc = new Pod_caller() ;
+ print($pc->perl() . "\n") ; # prints perl
+
+ sub perl {
+ return "perl" ;
+ }
+
+=for comment
+
+
+=head1 DESCRIPTION
+
+C<Inline::Java::Callback> allows you to call Perl functions from Java. To
+do this you need to create an C<org.perl.inline.java.InlinePerlCaller>
+object. Here is a example of a typical use:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+ import org.perl.inline.java.* ;
+
+ class Pod_regexp extends InlineJavaPerlCaller {
+ public Pod_regexp() throws InlineJavaException {
+ }
+
+ public boolean match(String target, String pattern)
+ throws InlineJavaException {
+ try {
+ String m = (String)CallPerlSub("main::regexp",
+ new Object [] {target, pattern}) ;
+
+ if (m.equals("1")){
+ return true ;
+ }
+ }
+ catch (InlineJavaPerlException pe){
+ // $@ is in pe.GetObject()
+ }
+
+ return false ;
+ }
+ }
+ END
+
+ my $re = new Pod_regexp() ;
+ my $match = $re->match("Inline::Java", "^Inline") ;
+ print($match . "\n") ; # prints 1
+
+ sub regexp {
+ my $target = shift ;
+ my $pattern = shift ;
+
+ return ($target =~ /$pattern/) ;
+ }
+
+=for comment
+
+
+=head1 CALLBACK API
+
+Here are the various methods that one can use to call into
+Perl:
+
+=over 4
+
+=item public Object CallPerlSub(String sub,
+Object args[], Class cast)
+throws InlineJavaException, InlineJavaPerlException
+
+Calls the specified subroutine with the supplied arguments and tries
+to create an object of type 'cast' with the result.
+
+ /* Example */
+ Integer sum = (Integer)CallPerlSub("main::add", new Object [] {new Integer(5), new Integer(3)}, Integer.class) ;
+
+=item public Object CallPerlStaticMethod(String pkg, String method,
+Object args[], Class cast)
+throws InlineJavaException, InlineJavaPerlException
+
+Calls the specified static package method (using the $pkg->$method()
+notation) with the supplied arguments and tries to create an object
+of type 'cast' with the result.
+
+ /* Example */
+ Integer sum = (Integer)CallPerlStaticMethod("main", "add", new Object [] {new Integer(5), new Integer(3)}, Integer.class) ;
+
+=item public Object eval(String code, Class cast)
+throws InlineJavaPerlException, InlineJavaException
+
+Evaluates the given Perl code and tries to create an object
+of type 'cast' with the result.
+
+ /* Example */
+ Integer sum = (Integer)eval("5 + 3", Integer.class) ;
+
+=item public Object require(String module_or_file)
+throws InlineJavaPerlException, InlineJavaException
+
+Requires the specified module/file by using a heuristic (currently,
+checks whether or not the file exists) and calling Perl's C<require>
+function using the appropriate construct.
+
+ /* Example */
+ require("Someting")
+
+=item public Object require_file(String file)
+throws InlineJavaPerlException, InlineJavaException
+
+Requires the specified file.
+
+ /* Example */
+ require_file("./my_stuff.pl") ;
+
+=item public Object require_module(String module)
+throws InlineJavaPerlException, InlineJavaException
+
+Requires the specified module.
+
+ /* Example */
+ require_module("Data::Dumper") ;
+
+=back
+
+Note: For all CallPerl* and eval methods, the 'cast' parameter is optional
+and defaults to 'String.class'.
+
+These methods can throw 2 types of exceptions: C<InlineJavaException> and
+C<InlineJavaPerlException> (both of these belong to the C<org.perl.inline.java>
+package). The former designates an internal C<Inline::Java> error and the
+latter indicates that the Perl callback threw an exception (die() or croak()).
+The value of $@ (this can be a scalar or any valid "Inline::Java" object) can
+be retreived using the GetObject() method of the C<InlineJavaPerlException>
+object (if you are certain that $@ was a Perl scalar, you can use the
+GetString() method).
+ Z<>
+
+
+=head1 CALLBACK CONTEXT
+
+By default, callback are executed in scalar context. However if you want to
+call certain functions in list context, you must insert "@" in front of the
+function name. The result will then be passed on to Java as an Array:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import org.perl.inline.java.* ;
+
+ class Pod_Context {
+ static private String dummy[] = {} ;
+
+ static public String [] get_list()
+ throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaPerlCaller pc = new InlineJavaPerlCaller() ;
+ return (String [])pc.CallPerlSub("@main::list",
+ null, dummy.getClass()) ;
+ }
+ }
+ END
+
+ sub list {
+ return ('a', 'b', 'c') ;
+ }
+
+ print(Pod_Context->get_list()->[1] . "\n") ; # prints b
+
+=for comment
+
+Note: When calling a Perl function that returns a list or array, you will
+need to pass the Class object for the expected array type (in this case
+String []). Since these Class objects are difficult to access for array
+types, the easiest way to do this is to create a dummy array of the desired
+type and call the getClass() method on that object (as seen above).
+ Z<>
+
+
+=head1 CALLBACK LOOPS
+
+It is now possible to use callbacks from different Java threads. One of the
+big advantages of this is that you can now handle, for example, SWING events
+in Perl. Here's an example:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+ import org.perl.inline.java.* ;
+ import javax.swing.* ;
+ import java.awt.event.* ;
+
+ class Pod_Button extends InlineJavaPerlCaller
+ implements ActionListener {
+ JFrame frame = null ;
+
+ public Pod_Button() throws InlineJavaException {
+ frame = new JFrame("Pod_Button") ;
+ frame.setSize(100,100) ;
+ JButton button = new JButton("Click Me!") ;
+ frame.getContentPane().add(button) ;
+ button.addActionListener(this) ;
+ frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE) ;
+ frame.show() ;
+ }
+
+ public void actionPerformed(ActionEvent e){
+ try {
+ CallPerlSub("main::button_pressed", new Object [] {}) ;
+ }
+ catch (InlineJavaPerlException pe){
+ // $@ is in pe.GetObject()
+ }
+ catch (InlineJavaException pe) {
+ pe.printStackTrace() ;
+ }
+ }
+
+ public void close(){
+ frame.dispose() ;
+ frame.hide() ;
+ frame = null ;
+ }
+
+ public void quit(){
+ System.exit(0) ;
+ }
+ }
+ END
+
+ my $b = new Pod_Button() ;
+ $b->StartCallbackLoop() ;
+ $b->close() ;
+
+ # Maybe do some other stuff
+
+ exit() ; # in client-server mode, optional
+ $b->quit() ; # in JNI mode
+
+ sub button_pressed {
+ print('click!' . "\n") ; # prints click!
+ $b->StopCallbackLoop() ;
+ }
+
+=for comment
+
+The StartCallbackLoop method can be called on any
+C<org.perl.inline.java.InlineJavaPerlCaller> object and will block the
+current thread and allow the reception of callbacks through
+any InlineJavaPerlCaller that has been created by the same (current) thread.
+The only way to interrupt such a StartCallbackLoop method is to call the
+StopCallbackLoop method on any C<org.perl.inline.java.InlineJavaPerlCaller>
+object that has been created by that same thread.
+
+Also, only threads that communicate with Perl through C<Inline::Java> are allowed
+to create C<org.perl.inline.java.InlineJavaPerlCaller> objects and invoke their
+StartCallbackLoop / StopCallbackLoop methods.
+ Z<>
+
+
+=head1 SELECT-STYLE CALLBACK LOOPS
+
+The disadvantage with the type of callback loop presented in the previous
+section is that the main portion of the Perl program is completely blocked
+while waiting for callbacks. In version 0.51 a new API for callback loops
+was introduced, allowing for callbacks to be processed much in the same
+fashion one uses select(2) to read data from a filehandle. Here's an
+example:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+ import org.perl.inline.java.* ;
+ import javax.swing.* ;
+ import java.awt.event.* ;
+
+ class Pod_Button extends InlineJavaPerlCaller
+ implements ActionListener {
+ JFrame frame = null ;
+
+ public Pod_Button() throws InlineJavaException {
+ frame = new JFrame("Pod_Button") ;
+ frame.setSize(100,100) ;
+ JButton button = new JButton("Click Me!") ;
+ frame.getContentPane().add(button) ;
+ button.addActionListener(this) ;
+ frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE) ;
+ frame.show() ;
+ }
+
+ public void actionPerformed(ActionEvent e){
+ try {
+ CallPerlSub("main::button_pressed", new Object [] {}) ;
+ }
+ catch (InlineJavaPerlException pe){
+ // $@ is in pe.GetObject()
+ }
+ catch (InlineJavaException pe) {
+ pe.printStackTrace() ;
+ }
+ }
+
+ public void close(){
+ frame.dispose() ;
+ frame.hide() ;
+ frame = null ;
+ }
+
+ public void quit(){
+ System.exit(0) ;
+ }
+ }
+ END
+
+ my $b = new Pod_Button() ;
+ $b->OpenCallbackStream() ;
+ while ((my $rc = $b->WaitForCallback(5)) > -1){
+ if ($rc > 0){
+ # A callback is pending, we must process it.
+ $b->ProcessNextCallback() ;
+ }
+ else {
+ # A timeout has occured after, in this case, 5 secs.
+ print "5 seconds have passed, still waiting for callback...\n" ;
+ # Maybe do some other stuff
+ }
+ }
+ $b->close() ;
+
+ # Maybe do some other stuff
+
+ exit() ; # in client-server mode, optional
+ $b->quit() ; # in JNI mode
+
+ sub button_pressed {
+ print('click!' . "\n") ; # prints click!
+ $b->CloseCallbackStream() ;
+ }
+
+=for comment
+
+The StartCallbackStream method can be called on any InlineJavaPerlCaller object
+to initialize a channel to receive callbacks. The WaitForCallback method can
+then be called with a float timeout value (-1 means wait forever, 0 means return
+immediately). The WaitForCallback method can return:
+
+ rc > 0, indicating that rc callbacks are waiting to be processed
+ rc == 0, indicating that a timeout has occured and no callbacks are waiting
+ rc == -1, indicating that the callback stream has been closed
+
+The callback stream can be closed by calling CloseCallbackStream, which works
+similarly to the StopCallbackLoop method used in the previous section.
+
+Also, the restrictions regarding thread communication stated in the previous
+section are valid in this case as well.
+ Z<>
+
+
+=head1 SEE ALSO
+
+L<Inline::Java>, L<Inline::Java::PerlNatives>, L<Inline::Java::PerlInterpreter>.
+ Z<>
+
+
+=head1 AUTHOR
+
+Patrick LeBoutillier <patl@cpan.org> is the author of Inline::Java.
+
+Brian Ingerson <ingy@cpan.org> is the author of Inline.
+ Z<>
+
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2004, Patrick LeBoutillier.
+
+All Rights Reserved. This module is free software. It may be used,
+redistributed and/or modified under the terms of the Perl Artistic
+License. See http://www.perl.com/perl/misc/Artistic.html for more
+details.
+
+=cut
diff --git a/Java/Class.pm b/Java/Class.pm
new file mode 100644
index 0000000..0558885
--- /dev/null
+++ b/Java/Class.pm
@@ -0,0 +1,511 @@
+package Inline::Java::Class ;
+
+use strict ;
+use Carp ;
+
+$Inline::Java::Class::VERSION = '0.53' ;
+
+$Inline::Java::Class::MAX_SCORE = 10 ;
+
+# There is no use supporting exponent notation for integer types since
+# Jave does not support it without casting.
+my $INT_RE = '^[+-]?\d+$' ;
+my $FLOAT_RE = '^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$' ;
+
+my $RANGE = {
+ 'java.lang.Byte' => {
+ REGEXP => $INT_RE,
+ MAX => 127,
+ MIN => -128,
+ },
+ 'java.lang.Short' => {
+ REGEXP => $INT_RE,
+ MAX => 32767,
+ MIN => -32768,
+ },
+ 'java.lang.Integer' => {
+ REGEXP => $INT_RE,
+ MAX => 2147483647,
+ MIN => -2147483648,
+ },
+ 'java.lang.Float' => {
+ REGEXP => $FLOAT_RE,
+ MAX => 3.4028235e38,
+ MIN => -3.4028235e38,
+ # POS_MIN => 1.4e-45,
+ # NEG_MAX => -1.4e-45,
+ },
+ 'java.lang.Long' => {
+ REGEXP => $INT_RE,
+ # MAX => 9223372036854775807,
+ # MIN => -9223372036854775808,
+ },
+ 'java.lang.Double' => {
+ REGEXP => $FLOAT_RE,
+ # MAX => 1.79e308,
+ # MIN => -1.79e308,
+ # POS_MIN => 4.9e-324,
+ # NEG_MAX => -4.9e-324,
+ },
+} ;
+$RANGE->{byte} = $RANGE->{'java.lang.Byte'} ;
+$RANGE->{short} = $RANGE->{'java.lang.Short'} ;
+$RANGE->{'int'} = $RANGE->{'java.lang.Integer'} ;
+$RANGE->{long} = $RANGE->{'java.lang.Long'} ;
+$RANGE->{float} = $RANGE->{'java.lang.Float'} ;
+$RANGE->{double} = $RANGE->{'java.lang.Double'} ;
+
+# java.lang.Number support. We allow the widest range
+# i.e. Double
+$RANGE->{'java.lang.Number'} = $RANGE->{'java.lang.Double'} ;
+
+
+my %numeric_classes = map {($_ => 1)} qw(
+ java.lang.Byte
+ java.lang.Short
+ java.lang.Integer
+ java.lang.Long
+ java.lang.Float
+ java.lang.Double
+ java.lang.Number
+ byte
+ short
+ int
+ long
+ float
+ double
+) ;
+
+
+my %double_classes = map {($_ => 1)} qw(
+ java.lang.Double
+ double
+) ;
+
+my %string_classes = map {($_ => 1)} qw(
+ java.lang.String
+ java.lang.StringBuffer
+ java.lang.CharSequence
+) ;
+
+my %char_classes = map {($_ => 1)} qw(
+ java.lang.Character
+ char
+) ;
+
+my %bool_classes = map {($_ => 1)} qw(
+ java.lang.Boolean
+ boolean
+) ;
+
+
+# This method makes sure that the class we are asking for
+# has the correct form for a Java class.
+sub ValidateClass {
+ my $class = shift ;
+
+ my $ret = ValidateClassSplit($class) ;
+
+ return $ret ;
+}
+
+
+my $class_name_regexp = '([\w$]+)(((\.([\w$]+))+)?)' ;
+my $class_regexp1 = qr/^($class_name_regexp)()()()$/o ;
+my $class_regexp2 = qr/^(\[+)([BCDFIJSZ])()()$/o ;
+my $class_regexp3 = qr/^(\[+)([L])($class_name_regexp)(;)$/o ;
+sub ValidateClassSplit {
+ my $class = shift ;
+
+ if (($class =~ $class_regexp1)||
+ ($class =~ $class_regexp2)||
+ ($class =~ $class_regexp3)){
+ return (wantarray ? ($1, $2, $3, $4) : $class) ;
+ }
+
+ croak "Invalid Java class name $class" ;
+}
+
+
+sub CastArguments {
+ my $args = shift ;
+ my $proto = shift ;
+ my $inline = shift ;
+
+ Inline::Java::debug_obj($args) ;
+ Inline::Java::debug_obj($proto) ;
+
+ my $nb_args = scalar(@{$args}) ;
+ if ($nb_args != scalar(@{$proto})){
+ croak "Wrong number of arguments" ;
+ }
+
+ my $ret = [] ;
+ my $score = 0 ;
+ for (my $i = 0 ; $i < $nb_args ; $i++){
+ my $arg = $args->[$i] ;
+ my $pro = $proto->[$i] ;
+ my @r = CastArgument($arg, $pro, $inline) ;
+ $ret->[$i] = $r[0] ;
+
+ $score += $r[1] ;
+ }
+
+ return ($ret, $score) ;
+}
+
+
+sub CastArgument {
+ my $arg = shift ;
+ my $proto = shift ;
+ my $inline = shift ;
+
+ ValidateClass($proto) ;
+
+ my $arg_ori = $arg ;
+ my $proto_ori = $proto ;
+
+ my $array_score = 0 ;
+
+ my @ret = eval {
+ my $array_type = undef ;
+ if ((defined($arg))&&(UNIVERSAL::isa($arg, "Inline::Java::Class::Coerce"))){
+ my $v = $arg->__get_value() ;
+ $proto = $arg->__get_type() ;
+ $array_type = $arg->__get_array_type() ;
+ $arg = $v ;
+ }
+
+ if ((ClassIsReference($proto))&&
+ (defined($arg))&&
+ (! UNIVERSAL::isa($arg, "Inline::Java::Object"))){
+ # Here we allow scalars to be passed in place of java.lang.Object
+ # They will wrapped on the Java side.
+ if (UNIVERSAL::isa($arg, "ARRAY")){
+ if (! UNIVERSAL::isa($arg, "Inline::Java::Array")){
+ my $an = Inline::Java::Array::Normalizer->new($inline, $array_type || $proto, $arg) ;
+ $array_score = $an->{score} ;
+ my $flat = $an->FlattenArray() ;
+
+ # We need to create the array on the Java side, and then grab
+ # the returned object.
+ my $obj = Inline::Java::Object->__new($array_type || $proto, $inline, -1, $flat->[0], $flat->[1]) ;
+ $arg = new Inline::Java::Array($obj) ;
+ }
+ else{
+ Inline::Java::debug(4, "argument is already an Inline::Java array") ;
+ }
+ }
+ else{
+ if (ref($arg)){
+ # We got some other type of ref...
+ if ($arg !~ /^(.*?)=/){
+ # We do not have a blessed reference, so ...
+ croak "Can't convert $arg to object $proto" ;
+ }
+ }
+ else {
+ # Here we got a scalar
+ # Here we allow scalars to be passed in place of java.lang.Object
+ # They will wrapped on the Java side.
+ if ($proto ne "java.lang.Object"){
+ croak "Can't convert $arg to object $proto" ;
+ }
+ }
+ }
+ }
+ if ((ClassIsPrimitive($proto))&&(ref($arg))){
+ croak "Can't convert $arg to primitive $proto" ;
+ }
+
+ if (ClassIsNumeric($proto)){
+ if (! defined($arg)){
+ # undef gets lowest score since it can be passed
+ # as anything
+ return (0, 1) ;
+ }
+ my $re = $RANGE->{$proto}->{REGEXP} ;
+ my $min = $RANGE->{$proto}->{MIN} ;
+ my $max = $RANGE->{$proto}->{MAX} ;
+ Inline::Java::debug(4,
+ "min = " . ($min || '') . ", " .
+ "max = " . ($max || '') . ", " .
+ "val = $arg") ;
+ if ($arg =~ /$re/){
+ if (((! defined($min))||($arg >= $min))&&
+ ((! defined($max))||($arg <= $max))){
+ # number is a pretty precise match, but it's still
+ # guessing amongst the numeric types
+ my $points = 5.5 ;
+ if (($inline->get_java_config('NATIVE_DOUBLES'))&&(ClassIsDouble($proto))){
+ # We want to send the actual double bytes to Java
+ my $bytes = pack("d", $arg) ;
+ $arg = bless(\$bytes, 'Inline::Java::double') ;
+ return ($arg, $points) ;
+ }
+ else {
+ return ($arg, $points) ;
+ }
+ }
+ croak "$arg out of range for type $proto" ;
+ }
+ croak "Can't convert $arg to $proto" ;
+ }
+ elsif (ClassIsChar($proto)){
+ if (! defined($arg)){
+ # undef gets lowest score since it can be passed
+ # as anything
+ return ("\0", 1) ;
+ }
+ if (length($arg) == 1){
+ # char is a pretty precise match
+ return ($arg, 5) ;
+ }
+ croak "Can't convert $arg to $proto" ;
+ }
+ elsif (ClassIsBool($proto)){
+ if (! defined($arg)){
+ # undef gets lowest score since it can be passed
+ # as anything
+ return (0, 1) ;
+ }
+ elsif (! $arg){
+ # bool gets lowest score since anything is a bool
+ return (0, 1) ;
+ }
+ else{
+ # bool gets lowest score since anything is a bool
+ return (1, 1) ;
+ }
+ }
+ elsif (ClassIsString($proto)){
+ if (! defined($arg)){
+ # undef gets lowest score since it can be passed
+ # as anything
+ return (undef, 1) ;
+ }
+ # string get almost lowest score since anything can match it
+ # except objects
+ if ($proto eq "java.lang.StringBuffer"){
+ # in case we have both protos, we want to give String
+ # the advantage
+ return ($arg, 1.75) ;
+ }
+ return ($arg, 2) ;
+ }
+ else{
+ if (! defined($arg)){
+ # undef gets lowest score since it can be passed
+ # as anything
+ return ($arg, 1) ;
+ }
+
+ # Here the prototype calls for an object of type $proto
+ # We must ask Java if our object extends $proto
+ if (ref($arg)){
+ if ((UNIVERSAL::isa($arg, "Inline::Java::Object"))||(UNIVERSAL::isa($arg, "Inline::Java::Array"))){
+ my ($msg, $score) = $arg->__isa($proto) ;
+ if ($msg){
+ croak $msg ;
+ }
+ Inline::Java::debug(3, "$arg is a $proto") ;
+
+ # a matching object, pretty good match, except if proto
+ # is java.lang.Object
+ if ($proto eq "java.lang.Object"){
+ return ($arg, 1) ;
+ }
+
+ # Here we deduce points the more our argument is "far"
+ # from the prototype.
+ if (! UNIVERSAL::isa($arg, "Inline::Java::Array")){
+ return ($arg, 7 - ($score * 0.01)) ;
+ }
+ else{
+ # We need to keep the array score somewhere...
+ return ($arg, $array_score) ;
+ }
+ }
+ else {
+ # We want to send a Perl object to the Java side.
+ my $ijp = new Inline::Java::Protocol(undef, $inline) ;
+ my $score = $ijp->__ISA('org.perl.inline.java.InlineJavaPerlObject', $proto) ;
+ if ($score == -1){
+ croak "$proto is not a kind of org.perl.inline.java.InlineJavaPerlObject" ;
+ }
+
+ Inline::Java::debug(3, "$arg is a $proto") ;
+
+ # a matching object, pretty good match, except if proto
+ # is java.lang.Object
+ if ($proto eq "java.lang.Object"){
+ return ($arg, 1) ;
+ }
+ else{
+ return ($arg, 7 - ($score * 0.01)) ;
+ }
+ }
+ }
+
+ # Here we are passing a scalar as an object, this is pretty
+ # vague as well
+ return ($arg, 1) ;
+ }
+ } ;
+ die("$@\n") if $@ ;
+
+ if ((defined($arg_ori))&&(UNIVERSAL::isa($arg_ori, "Inline::Java::Class::Coerce"))){
+ # It seems we had casted the variable to a specific type
+ if ($arg_ori->__matches($proto_ori)){
+ Inline::Java::debug(3, "type coerce match!") ;
+ $ret[1] = $Inline::Java::Class::MAX_SCORE ;
+ }
+ else{
+ # We have coerced to something that doesn't exactly match
+ # any of the available types.
+ # For now we don't allow this.
+ croak "Coerce ($proto) doesn't exactly match prototype ($proto_ori)" ;
+ }
+ }
+
+ return @ret ;
+}
+
+
+sub IsMaxArgumentsScore {
+ my $args = shift ;
+ my $score = shift ;
+
+ if ((scalar(@{$args}) * 10) == $score){
+ return 1 ;
+ }
+
+ return 0 ;
+}
+
+
+sub ClassIsNumeric {
+ my $class = shift ;
+
+ return $numeric_classes{$class} ;
+}
+
+
+sub ClassIsDouble {
+ my $class = shift ;
+
+ return $double_classes{$class} ;
+}
+
+
+sub ClassIsString {
+ my $class = shift ;
+
+ return $string_classes{$class} ;
+}
+
+
+sub ClassIsChar {
+ my $class = shift ;
+
+ return $char_classes{$class} ;
+}
+
+
+sub ClassIsBool {
+ my $class = shift ;
+
+ return $bool_classes{$class} ;
+}
+
+
+sub ClassIsPrimitive {
+ my $class = shift ;
+
+ if ((ClassIsNumeric($class))||(ClassIsString($class))||(ClassIsChar($class))||(ClassIsBool($class))){
+ return 1 ;
+ }
+
+ return 0 ;
+}
+
+
+sub ClassIsReference {
+ my $class = shift ;
+
+ if (ClassIsPrimitive($class)){
+ return 0 ;
+ }
+
+ return 1 ;
+}
+
+
+sub ClassIsArray {
+ my $class = shift ;
+
+ if ((ClassIsReference($class))&&($class =~ /^(\[+)(.*)$/)){
+ return 1 ;
+ }
+
+ return 0 ;
+}
+
+
+
+######################## Inline::Java::Class::Coerce ########################
+package Inline::Java::Class::Coerce ;
+
+
+use Carp ;
+
+sub new {
+ my $class = shift ;
+ my $type = shift ;
+ my $value = shift ;
+ my $array_type = shift ;
+
+ if (UNIVERSAL::isa($value, "Inline::Java::Class::Coerce")){
+ # This allows chaining
+ $value = $value->get_value() ;
+ }
+
+ my $this = {} ;
+ $this->{cast} = Inline::Java::Class::ValidateClass($type) ;
+ $this->{value} = $value ;
+ $this->{array_type} = $array_type ;
+
+ bless($this, $class) ;
+ return $this ;
+}
+
+
+sub __get_value {
+ my $this = shift ;
+
+ return $this->{value} ;
+}
+
+
+sub __get_type {
+ my $this = shift ;
+
+ return $this->{cast} ;
+}
+
+sub __get_array_type {
+ my $this = shift ;
+
+ return $this->{array_type} ;
+}
+
+
+sub __matches {
+ my $this = shift ;
+ my $proto = shift ;
+
+ return ($proto eq $this->{cast}) ;
+}
+
+
+1 ;
diff --git a/Java/Handle.pm b/Java/Handle.pm
new file mode 100644
index 0000000..4bea9bc
--- /dev/null
+++ b/Java/Handle.pm
@@ -0,0 +1,269 @@
+package Inline::Java::Handle ;
+@Inline::Java::Handle::ISA = qw(Inline::Java::Handle::Tie) ;
+
+use strict ;
+use Symbol ;
+use Carp ;
+
+$Inline::Java::Handle::VERSION = '0.53' ;
+
+
+# Here we store as keys the knots and as values our blessed objects
+my $OBJECTS = {} ;
+
+
+sub new {
+ my $class = shift ;
+ my $object = shift ;
+
+ my $fh = gensym() ;
+ my $knot = tie *{$fh}, $class ;
+ my $this = bless ($fh, $class) ;
+
+ $OBJECTS->{$knot} = $object ;
+
+ Inline::Java::debug(5, "this = '$this'") ;
+ Inline::Java::debug(5, "knot = '$knot'") ;
+
+ return $this ;
+}
+
+
+sub __get_object {
+ my $this = shift ;
+
+ my $knot = tied $this || $this ;
+
+ my $ref = $OBJECTS->{$knot} ;
+ if (! defined($ref)){
+ croak "Unknown Java handle reference '$knot'" ;
+ }
+
+ return $ref ;
+}
+
+
+sub __isa {
+ my $this = shift ;
+ my $proto = shift ;
+
+ return $this->__get_object()->__isa($proto) ;
+}
+
+
+sub __read {
+ my $this = shift ;
+ my ($buf, $len, $offset) = @_ ;
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = undef ;
+ eval {
+ my $str = $obj->__get_private()->{proto}->ReadFromJavaHandle($len) ;
+ $len = length($str) ;
+ if ($len > 0){
+ substr($buf, $offset, $len) = $str ;
+ $_[0] = $buf ;
+ $ret = $len ;
+ }
+ } ;
+ croak $@ if $@ ;
+
+ return $ret ;
+}
+
+
+sub __readline {
+ my $this = shift ;
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = undef ;
+ eval {
+ $ret = $obj->__get_private()->{proto}->ReadLineFromJavaHandle() ;
+ } ;
+ croak $@ if $@ ;
+
+ return $ret ;
+}
+
+
+sub __write {
+ my $this = shift ;
+ my $buf = shift ;
+ my $len = shift ;
+ my $offset = shift ;
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = -1 ;
+ eval {
+ my $len = $obj->__get_private()->{proto}->WriteToJavaHandle(substr($buf, $offset, $len)) ;
+ $ret = $len ;
+ } ;
+ croak $@ if $@ ;
+}
+
+
+sub __eof {
+ my $this = shift ;
+}
+
+
+sub __close {
+ my $this = shift ;
+
+ my $obj = $this->__get_object() ;
+
+ my $ret = undef ;
+ {
+ local $@ ;
+ eval {
+ $ret = $obj->__get_private()->{proto}->CloseJavaHandle() ;
+ $obj->__get_private()->{closed} = 1 ;
+ } ;
+ croak $@ if $@ ;
+ }
+
+ return $ret ;
+}
+
+
+
+sub AUTOLOAD {
+ my $this = shift ;
+ my @args = @_ ;
+
+ use vars qw($AUTOLOAD) ;
+ my $func_name = $AUTOLOAD ;
+ # Strip package from $func_name, Java will take of finding the correct
+ # method.
+ $func_name =~ s/^(.*)::// ;
+
+ croak "Can't call method '$func_name' on Java handles" ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+
+ my $knot = tied *{$this} ;
+ if (! $knot){
+ Inline::Java::debug(4, "destroying Inline::Java::Handle::Tie") ;
+
+ my $obj = $this->__get_object() ;
+ if (! $obj->__get_private()->{closed}){
+ $this->__close() ;
+ }
+
+ $OBJECTS->{$this} = undef ;
+ }
+ else {
+ Inline::Java::debug(4, "destroying Inline::Java::Handle") ;
+ }
+}
+
+
+
+######################## Handle methods ########################
+package Inline::Java::Handle::Tie ;
+@Inline::Java::Handle::Tie::ISA = qw(Tie::StdHandle) ;
+
+
+use Tie::Handle ;
+use Carp ;
+
+
+sub TIEHANDLE {
+ my $class = shift ;
+ my $jclass = shift ;
+
+ return $class->SUPER::TIEHANDLE(@_) ;
+}
+
+
+sub READ {
+ my $this = shift ;
+ my ($buf, $len, $offset) = @_ ;
+
+ my $ret = $this->__read($buf, $len, $offset) ;
+ $_[0] = $buf ;
+
+ return $ret ;
+}
+
+
+sub READLINE {
+ my $this = shift ;
+
+ return $this->__readline() ;
+}
+
+
+sub WRITE {
+ my $this = shift ;
+ my $buf = shift ;
+ my $len = shift ;
+ my $offset = shift ;
+
+ return $this->__write($buf, $len, $offset) ;
+}
+
+
+sub BINMODE {
+ my $this = shift ;
+
+ croak "Operation BINMODE not supported on Java handle" ;
+}
+
+
+sub OPEN {
+ my $this = shift ;
+
+ croak "Operation OPEN not supported on Java handle" ;
+}
+
+
+sub TELL {
+ my $this = shift ;
+
+ croak "Operation TELL not supported on Java handle" ;
+}
+
+
+sub FILENO {
+ my $this = shift ;
+
+ croak "Operation FILENO not supported on Java handle" ;
+}
+
+
+sub SEEK {
+ my $this = shift ;
+
+ croak "Operation SEEK not supported on Java handle" ;
+}
+
+
+sub EOF {
+ my $this = shift ;
+
+ return $this->__eof() ;
+}
+
+
+sub CLOSE {
+ my $this = shift ;
+
+ return $this->__close() ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+}
+
+
+1 ;
+
diff --git a/Java/JNI.pm b/Java/JNI.pm
new file mode 100644
index 0000000..9384401
--- /dev/null
+++ b/Java/JNI.pm
@@ -0,0 +1,41 @@
+package Inline::Java::JNI ;
+@Inline::Java::JNI::ISA = qw(DynaLoader) ;
+
+
+use strict ;
+
+$Inline::Java::JNI::VERSION = '0.53' ;
+
+use DynaLoader ;
+use Carp ;
+use File::Basename ;
+
+
+if ($^O eq 'solaris'){
+ load_lib('-lthread') ;
+}
+
+
+sub load_lib {
+ my $l = shift ;
+ my $lib = (DynaLoader::dl_findfile($l))[0] ;
+
+ if ((! $lib)||(! defined(DynaLoader::dl_load_file($lib, 0x01)))){
+ carp("Couldn't find or load $l.") ;
+ }
+}
+
+
+# A place to attach the Inline object that is currently in Java land
+$Inline::Java::JNI::INLINE_HOOK = undef ;
+
+
+eval {
+ Inline::Java::JNI->bootstrap($Inline::Java::JNI::VERSION) ;
+} ;
+if ($@){
+ croak "Can't load JNI module. Did you build it at install time?\nError: $@" ;
+}
+
+
+1 ;
diff --git a/Java/JNI.xs b/Java/JNI.xs
new file mode 100644
index 0000000..cc359d7
--- /dev/null
+++ b/Java/JNI.xs
@@ -0,0 +1,311 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifdef __CYGWIN__
+ #include "w32api/basetyps.h"
+#endif
+
+/* Include the JNI header file */
+#include "jni.h"
+
+
+/* JNI structure */
+typedef struct {
+ JavaVM *jvm ;
+ jclass ijs_class ;
+ jobject ijs ;
+ jmethodID jni_main_mid ;
+ jmethodID process_command_mid ;
+ jint debug ;
+ int embedded ;
+ int native_doubles ;
+ int destroyed ;
+} InlineJavaJNIVM ;
+
+
+void shutdown_JVM(InlineJavaJNIVM *this){
+ if ((! this->embedded)&&(! this->destroyed)){
+ (*(this->jvm))->DestroyJavaVM(this->jvm) ;
+ this->destroyed = 1 ;
+ }
+}
+
+
+JNIEnv *get_env(InlineJavaJNIVM *this){
+ JNIEnv *env ;
+
+ (*(this->jvm))->AttachCurrentThread(this->jvm, ((void **)&env), NULL) ;
+
+ return env ;
+}
+
+
+/*
+ This is only used to trap exceptions from Perl.
+*/
+void check_exception_from_perl(JNIEnv *env, char *msg){
+ if ((*(env))->ExceptionCheck(env)){
+ (*(env))->ExceptionDescribe(env) ;
+ (*(env))->ExceptionClear(env) ;
+ croak(msg) ;
+ }
+}
+
+
+void throw_ije(JNIEnv *env, char *msg){
+ jclass ije ;
+
+ ije = (*(env))->FindClass(env, "org/perl/inline/java/InlineJavaException") ;
+ if ((*(env))->ExceptionCheck(env)){
+ (*(env))->ExceptionDescribe(env) ;
+ (*(env))->ExceptionClear(env) ;
+ (*(env))->FatalError(env, "Can't find class InlineJavaException: exiting...") ;
+ }
+ (*(env))->ThrowNew(env, ije, msg) ;
+}
+
+
+jstring JNICALL jni_callback(JNIEnv *env, jobject obj, jstring cmd){
+ dSP ;
+ jstring resp ;
+ char *c = (char *)((*(env))->GetStringUTFChars(env, cmd, NULL)) ;
+ char *r = NULL ;
+ int count = 0 ;
+ SV *hook = NULL ;
+ char msg[128] ;
+
+ ENTER ;
+ SAVETMPS ;
+
+ PUSHMARK(SP) ;
+ XPUSHs(&PL_sv_undef) ;
+ XPUSHs(sv_2mortal(newSVpv(c, 0))) ;
+ PUTBACK ;
+
+ (*(env))->ReleaseStringUTFChars(env, cmd, c) ;
+ count = perl_call_pv("Inline::Java::Callback::InterceptCallback",
+ G_ARRAY|G_EVAL) ;
+
+ SPAGAIN ;
+
+ /* Check the eval */
+ if (SvTRUE(ERRSV)){
+ STRLEN n_a ;
+ throw_ije(env, SvPV(ERRSV, n_a)) ;
+ }
+ else{
+ if (count != 2){
+ sprintf(msg, "Invalid return value from Inline::Java::Callback::InterceptCallback: %d",
+ count) ;
+ throw_ije(env, msg) ;
+ }
+ }
+
+ /*
+ The first thing to pop is a reference to the returned object,
+ which we must keep around long enough so that it is not deleted
+ before control gets back to Java. This is because this object
+ may be returned be the callback, and when it gets back to Java
+ it will already be deleted.
+ */
+ hook = perl_get_sv("Inline::Java::Callback::OBJECT_HOOK", FALSE) ;
+ sv_setsv(hook, POPs) ;
+
+ r = (char *)POPp ;
+ resp = (*(env))->NewStringUTF(env, r) ;
+
+ PUTBACK ;
+ FREETMPS ;
+ LEAVE ;
+
+ return resp ;
+}
+
+
+
+/*****************************************************************************/
+
+
+
+MODULE = Inline::Java::JNI PACKAGE = Inline::Java::JNI
+
+
+PROTOTYPES: DISABLE
+
+
+InlineJavaJNIVM *
+new(CLASS, classpath, args, embedded, debug, native_doubles)
+ char * CLASS
+ char * classpath
+ SV * args
+ int embedded
+ int debug
+ int native_doubles
+
+ PREINIT:
+ JavaVMInitArgs vm_args ;
+ JavaVMOption *options ;
+ JNIEnv *env ;
+ JNINativeMethod nm ;
+ jint res ;
+ char *cp ;
+ int args_len ;
+ int i ;
+ SV ** val = NULL ;
+ STRLEN n_a ;
+
+ CODE:
+ args = SvRV(args) ;
+ RETVAL = (InlineJavaJNIVM *)safemalloc(sizeof(InlineJavaJNIVM)) ;
+ if (RETVAL == NULL){
+ croak("Can't create InlineJavaJNIVM") ;
+ }
+ RETVAL->ijs = NULL ;
+ RETVAL->debug = debug ;
+ RETVAL->embedded = embedded ;
+ RETVAL->native_doubles = native_doubles ;
+ RETVAL->destroyed = 0 ;
+
+ /* Figure out the length of the args array */
+ args_len = av_len((AV *)args) + 1 ;
+ vm_args.version = JNI_VERSION_1_2 ;
+
+ options = (JavaVMOption *)malloc((2 + args_len) * sizeof(JavaVMOption)) ;
+ vm_args.options = options ;
+ vm_args.nOptions = 0 ;
+ vm_args.ignoreUnrecognized = JNI_FALSE ;
+
+ options[vm_args.nOptions++].optionString =
+ ((RETVAL->debug > 5) ? "-verbose" : "-verbose:") ;
+ cp = (char *)malloc((strlen(classpath) + 32) * sizeof(char)) ;
+ sprintf(cp, "-Djava.class.path=%s", classpath) ;
+ options[vm_args.nOptions++].optionString = cp ;
+
+ for (i = 0 ; i < args_len ; i++){
+ val = av_fetch((AV *)args, i, 0) ;
+ if (val != NULL){
+ options[vm_args.nOptions++].optionString = SvPV(*val, n_a) ;
+ }
+ }
+
+ /* Embedded patch and idea by Doug MacEachern */
+ if (RETVAL->embedded) {
+ /* We are already inside a JVM */
+ jint n = 0 ;
+
+ res = JNI_GetCreatedJavaVMs(&(RETVAL->jvm), 1, &n) ;
+ if (n <= 0) {
+ /* res == 0 even if no JVMs are alive */
+ res = -1;
+ }
+ if (res < 0) {
+ croak("Can't find any created Java JVMs") ;
+ }
+
+ env = get_env(RETVAL) ;
+ }
+ else {
+ /* Create the Java VM */
+ res = JNI_CreateJavaVM(&(RETVAL->jvm), (void **)&(env), &vm_args) ;
+ if (res < 0) {
+ croak("Can't create Java JVM using JNI") ;
+ }
+ }
+
+ free(options) ;
+ free(cp) ;
+
+
+ /* Load the classes that we will use */
+ RETVAL->ijs_class = (*(env))->FindClass(env, "org/perl/inline/java/InlineJavaServer") ;
+ check_exception_from_perl(env, "Can't find class InlineJavaServer") ;
+ RETVAL->ijs_class = (*(env))->NewGlobalRef(env, RETVAL->ijs_class) ;
+
+ /* Get the method ids that are needed later */
+ RETVAL->jni_main_mid = (*(env))->GetStaticMethodID(env, RETVAL->ijs_class, "jni_main",
+ "(IZ)Lorg/perl/inline/java/InlineJavaServer;") ;
+ check_exception_from_perl(env, "Can't find method jni_main in class InlineJavaServer") ;
+ RETVAL->process_command_mid = (*(env))->GetMethodID(env, RETVAL->ijs_class, "ProcessCommand",
+ "(Ljava/lang/String;)Ljava/lang/String;") ;
+ check_exception_from_perl(env, "Can't find method ProcessCommand in class InlineJavaServer") ;
+
+ /* Register the callback function */
+ nm.name = "jni_callback" ;
+ nm.signature = "(Ljava/lang/String;)Ljava/lang/String;" ;
+ nm.fnPtr = jni_callback ;
+ (*(env))->RegisterNatives(env, RETVAL->ijs_class, &nm, 1) ;
+ check_exception_from_perl(env, "Can't register method jni_callback in class InlineJavaServer") ;
+
+ OUTPUT:
+ RETVAL
+
+
+
+void
+shutdown(this)
+ InlineJavaJNIVM * this
+
+ CODE:
+ shutdown_JVM(this) ;
+
+
+
+void
+DESTROY(this)
+ InlineJavaJNIVM * this
+
+ CODE:
+ shutdown_JVM(this) ;
+ safefree(this) ;
+
+
+
+void
+create_ijs(this)
+ InlineJavaJNIVM * this
+
+ PREINIT:
+ JNIEnv *env ;
+
+ CODE:
+ env = get_env(this) ;
+ this->ijs = (*(env))->CallStaticObjectMethod(env, this->ijs_class, this->jni_main_mid, this->debug, this->native_doubles) ;
+ check_exception_from_perl(env, "Can't call jni_main in class InlineJavaServer") ;
+ this->ijs = (*(env))->NewGlobalRef(env, this->ijs) ;
+
+
+
+char *
+process_command(this, data)
+ InlineJavaJNIVM * this
+ char * data
+
+ PREINIT:
+ JNIEnv *env ;
+ jstring cmd ;
+ jstring resp ;
+ SV *hook = NULL ;
+
+ CODE:
+ env = get_env(this) ;
+ cmd = (*(env))->NewStringUTF(env, data) ;
+ check_exception_from_perl(env, "Can't create java.lang.String") ;
+
+ resp = (*(env))->CallObjectMethod(env, this->ijs, this->process_command_mid, cmd) ;
+ /* Thanks Dave Blob for spotting this. This is necessary since this code never really returns to Java
+ It simply calls into Java and comes back. */
+ (*(env))->DeleteLocalRef(env, cmd);
+ check_exception_from_perl(env, "Can't call ProcessCommand in class InlineJavaServer") ;
+
+ hook = perl_get_sv("Inline::Java::Callback::OBJECT_HOOK", FALSE) ;
+ sv_setsv(hook, &PL_sv_undef) ;
+
+ RETVAL = (char *)((*(env))->GetStringUTFChars(env, resp, NULL)) ;
+
+ OUTPUT:
+ RETVAL
+
+ CLEANUP:
+ (*(env))->DeleteLocalRef(env, resp) ;
+ (*(env))->ReleaseStringUTFChars(env, resp, RETVAL) ;
diff --git a/Java/JVM.pm b/Java/JVM.pm
new file mode 100644
index 0000000..3797cd5
--- /dev/null
+++ b/Java/JVM.pm
@@ -0,0 +1,449 @@
+package Inline::Java::JVM ;
+
+
+use strict ;
+use Carp ;
+use IO::File ;
+use IPC::Open3 ;
+use IO::Socket ;
+use Text::ParseWords ;
+use Inline::Java::Portable ;
+
+$Inline::Java::JVM::VERSION = '0.53' ;
+
+my %SIGS = () ;
+
+my @SIG_LIST = ('HUP', 'INT', 'PIPE', 'TERM') ;
+
+sub new {
+ my $class = shift ;
+ my $o = shift ;
+
+ my $this = {} ;
+ bless($this, $class) ;
+
+ foreach my $sig (@SIG_LIST){
+ local $SIG{__WARN__} = sub {} ;
+ if (exists($SIG{$sig})){
+ $SIGS{$sig} = $SIG{$sig} ;
+ }
+ }
+
+ $this->{socket} = undef ;
+ $this->{JNI} = undef ;
+ $this->{embedded} = $o->get_java_config('EMBEDDED_JNI') ;
+ $this->{owner} = 1 ;
+ $this->{destroyed} = 0 ;
+ $this->{private} = $o->get_java_config('PRIVATE') ;
+ $this->{debugger} = $o->get_java_config('DEBUGGER') ;
+
+ if ($this->{embedded}){
+ Inline::Java::debug(1, "using embedded JVM...") ;
+ }
+ else{
+ Inline::Java::debug(1, "starting JVM...") ;
+ }
+
+ my $args = $o->get_java_config('EXTRA_JAVA_ARGS') ;
+ if ($o->get_java_config('JNI')){
+ Inline::Java::debug(1, "JNI mode") ;
+
+ # Split args and remove quotes
+ my @args = map {s/(['"])(.*)\1/$2/ ; $_}
+ parse_line('\s+', 1, $args) ;
+ my $jni = new Inline::Java::JNI(
+ $ENV{CLASSPATH} || '',
+ \@args,
+ $this->{embedded},
+ Inline::Java::get_DEBUG(),
+ $o->get_java_config('NATIVE_DOUBLES'),
+ ) ;
+ $jni->create_ijs() ;
+
+ $this->{JNI} = $jni ;
+ }
+ else {
+ Inline::Java::debug(1, "client/server mode") ;
+
+ my $debug = Inline::Java::get_DEBUG() ;
+
+ $this->{shared} = $o->get_java_config('SHARED_JVM') ;
+ $this->{start_jvm} = $o->get_java_config('START_JVM') ;
+ $this->{port} = $o->get_java_config('PORT') ;
+ $this->{host} = $o->get_java_config('HOST') ;
+
+ # Used to limit the bind of the JVM server
+ $this->{'bind'} = $o->get_java_config('BIND') ;
+
+ # Grab the next free port number and release it.
+ if ((! $this->{shared})&&($this->{port} < 0)){
+ if (Inline::Java::Portable::portable("GOT_NEXT_FREE_PORT")){
+ my $sock = IO::Socket::INET->new(
+ Listen => 0, Proto => 'tcp',
+ LocalAddr => 'localhost', LocalPort => 0) ;
+ if ($sock){
+ $this->{port} = $sock->sockport() ;
+ Inline::Java::debug(2, "next available port number is $this->{port}") ;
+ close($sock) ;
+ }
+ else{
+ # Revert to the default.
+ $this->{port} = - $this->{port} ;
+ carp(
+ "Could not get next available port number, using port " .
+ "$this->{port} instead. Use the PORT configuration " .
+ "option to suppress this warning.\n Error: $!\n") ;
+ }
+ }
+ else{
+ # Revert to the default.
+ # Try this maybe: 9000 + $$ ?
+ $this->{port} = - $this->{port} ;
+ }
+ }
+
+ # Check if JVM is already running
+ if ($this->{shared}){
+ eval {
+ $this->reconnect() ;
+ } ;
+ if (! $@){
+ Inline::Java::debug(1, "connected to already running JVM!") ;
+ return $this ;
+ }
+
+ if (! $this->{start_jvm}){
+ croak("Can't find running JVM and START_JVM = 0") ;
+ }
+ }
+
+ my $java = File::Spec->catfile($o->get_java_config('J2SDK'),
+ Inline::Java::Portable::portable("J2SDK_BIN"),
+ ($this->{debugger} ? "jdb" : "java") .
+ Inline::Java::Portable::portable("EXE_EXTENSION")) ;
+
+ my $shared = ($this->{shared} ? "true" : "false") ;
+ my $priv = ($this->{private} ? "true" : "false") ;
+ my $native_doubles = ($o->get_java_config('NATIVE_DOUBLES') ? "true" : "false") ;
+ my $cmd = Inline::Java::Portable::portable("SUB_FIX_CMD_QUOTES", "\"$java\" $args org.perl.inline.java.InlineJavaServer $debug $this->{bind} $this->{port} $shared $priv $native_doubles") ;
+ Inline::Java::debug(2, $cmd) ;
+ if ($o->get_config('UNTAINT')){
+ ($cmd) = $cmd =~ /(.*)/ ;
+ }
+
+ my $pid = 0 ;
+ eval {
+ $pid = $this->launch($o, $cmd) ;
+ } ;
+ croak "Can't exec JVM: $@" if $@ ;
+
+ if ($this->{shared}){
+ # As of 0.40, we release by default.
+ $this->release() ;
+ }
+ else{
+ $this->capture() ;
+ }
+
+ $this->{pid} = $pid ;
+ $this->{socket} = setup_socket(
+ $this->{host},
+ $this->{port},
+ # Give the user an extra hour's time set breakpoints and the like...
+ ($this->{debugger} ? 3600 : 0) + int($o->get_java_config('STARTUP_DELAY')),
+ 0
+ ) ;
+ }
+
+ return $this ;
+}
+
+
+sub launch {
+ my $this = shift ;
+ my $o = shift ;
+ my $cmd = shift ;
+
+ local $SIG{__WARN__} = sub {} ;
+
+ my $dn = Inline::Java::Portable::portable("DEV_NULL") ;
+ my $in = ($this->{debugger} ? ">&STDIN" : new IO::File("<$dn")) ;
+ if (! defined($in)){
+ croak "Can't open $dn for reading" ;
+ }
+
+ my $out = ">&STDOUT" ;
+ if ($this->{shared}){
+ $out = new IO::File(">$dn") ;
+ if (! defined($out)){
+ croak "Can't open $dn for writing" ;
+ }
+ }
+
+ my $err = ">&STDERR" ;
+
+ my $pid = open3($in, $out, $err, $cmd) ;
+
+ if (! $this->{debugger}){
+ close($in) ;
+ }
+ if ($this->{shared}){
+ close($out) ;
+ }
+
+ return $pid ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+ $this->shutdown() ;
+}
+
+
+sub shutdown {
+ my $this = shift ;
+
+ if ($this->{embedded}){
+ Inline::Java::debug(1, "embedded JVM, skipping shutdown.") ;
+ return ;
+ }
+
+ if (! $this->{destroyed}){
+ if ($this->am_owner()){
+ Inline::Java::debug(1, "JVM owner exiting...") ;
+
+ if ($this->{socket}){
+ # This asks the Java server to stop and die.
+ my $sock = $this->{socket} ;
+ if ($sock->peername()){
+ Inline::Java::debug(1, "Sending 'die' message to JVM...") ;
+ print $sock "die\n" ;
+ }
+ else{
+ carp "Lost connection with Java virtual machine" ;
+ }
+ close($sock) ;
+
+ if ($this->{pid}){
+ # Here we go ahead and send the signals anyway to be very
+ # sure it's dead...
+ # Always be polite first, and then insist.
+ if (Inline::Java::Portable::portable('GOT_SAFE_SIGNALS')){
+ Inline::Java::debug(1, "Sending 15 signal to JVM...") ;
+ kill(15, $this->{pid}) ;
+ Inline::Java::debug(1, "Sending 9 signal to JVM...") ;
+ kill(9, $this->{pid}) ;
+ }
+
+ # Reap the child...
+ waitpid($this->{pid}, 0) ;
+ }
+ }
+ if ($this->{JNI}){
+ $this->{JNI}->shutdown() ;
+ }
+ }
+ else{
+ # We are not the JVM owner, so we simply politely disconnect
+ if ($this->{socket}){
+ Inline::Java::debug(1, "JVM non-owner exiting...") ;
+ close($this->{socket}) ;
+ $this->{socket} = undef ;
+ }
+
+ # This should never happen in JNI mode
+ }
+
+ $this->{destroyed} = 1 ;
+ }
+}
+
+
+# This cannot be a member function because it can be used
+# elsewhere to connect to the JVM.
+sub setup_socket {
+ my $host = shift ;
+ my $port = shift ;
+ my $timeout = shift ;
+ my $one_shot = shift ;
+
+ my $socket = undef ;
+
+ my $last_words = "timeout\n" ;
+ my $got_alarm = Inline::Java::Portable::portable("GOT_ALARM") ;
+
+ eval {
+ local $SIG{ALRM} = sub { die($last_words) ; } ;
+
+ if ($got_alarm){
+ alarm($timeout) ;
+ }
+
+ # ignore expected "connection refused" warnings
+ # Thanks binkley!
+ local $SIG{__WARN__} = sub {
+ warn($@) unless ($@ =~ /Connection refused/i) ;
+ } ;
+
+ while (1){
+ $socket = new IO::Socket::INET(
+ PeerAddr => $host,
+ PeerPort => $port,
+ Proto => 'tcp') ;
+ if (($socket)||($one_shot)){
+ last ;
+ }
+ select(undef, undef, undef, 0.1) ;
+ }
+
+ if ($got_alarm){
+ alarm(0) ;
+ }
+ } ;
+ if ($@){
+ if ($@ eq $last_words){
+ croak "JVM taking more than $timeout seconds to start, or died before Perl could connect. Increase config STARTUP_DELAY if necessary." ;
+ }
+ else{
+ if ($got_alarm){
+ alarm(0) ;
+ }
+ croak $@ ;
+ }
+ }
+
+ if (! $socket){
+ croak "Can't connect to JVM at ($host:$port): $!" ;
+ }
+
+ $socket->autoflush(1) ;
+
+ return $socket ;
+}
+
+
+sub reconnect {
+ my $this = shift ;
+
+ if (($this->{JNI})||(! $this->{shared})){
+ return ;
+ }
+
+ if ($this->{socket}){
+ # Close the previous socket
+ close($this->{socket}) ;
+ $this->{socket} = undef ;
+ }
+
+ my $socket = setup_socket(
+ $this->{host},
+ $this->{port},
+ 0,
+ 1
+ ) ;
+ $this->{socket} = $socket ;
+
+ # Now that we have reconnected, we release the JVM
+ $this->release() ;
+}
+
+
+sub capture {
+ my $this = shift ;
+
+ if (($this->{JNI})||(! $this->{shared})){
+ return ;
+ }
+
+ foreach my $sig (@SIG_LIST){
+ if (exists($SIG{$sig})){
+ $SIG{$sig} = \&Inline::Java::done ;
+ }
+ }
+
+ $this->{owner} = 1 ;
+}
+
+
+sub am_owner {
+ my $this = shift ;
+
+ return $this->{owner} ;
+}
+
+
+sub release {
+ my $this = shift ;
+
+ if (($this->{JNI})||(! $this->{shared})){
+ return ;
+ }
+
+ foreach my $sig (@SIG_LIST){
+ local $SIG{__WARN__} = sub {} ;
+ if (exists($SIG{$sig})){
+ $SIG{$sig} = $SIGS{$sig} ;
+ }
+ }
+
+ $this->{owner} = 0 ;
+}
+
+
+sub process_command {
+ my $this = shift ;
+ my $inline = shift ;
+ my $data = shift ;
+
+ my $resp = undef ;
+
+ # Patch by Simon Cozens for perl -wle 'use Our::Module; do_stuff()'
+ local $/ = "\n" ;
+ local $\ = "" ;
+ # End Patch
+
+ while (1){
+ Inline::Java::debug(3, "packet sent is $data") ;
+
+ if ($this->{socket}){
+
+ my $sock = $this->{socket} ;
+ print $sock $data . "\n" or
+ croak "Can't send packet to JVM: $!" ;
+
+ $resp = <$sock> ;
+ if (! $resp){
+ croak "Can't receive packet from JVM: $!" ;
+ }
+
+ # Release the reference since the object has been sent back
+ # to Java.
+ $Inline::Java::Callback::OBJECT_HOOK = undef ;
+ }
+ if ($this->{JNI}){
+ $Inline::Java::JNI::INLINE_HOOK = $inline ;
+ $resp = $this->{JNI}->process_command($data) ;
+ }
+ chomp($resp) ;
+
+ Inline::Java::debug(3, "packet recv is $resp") ;
+
+ # We got an answer from the server. Is it a callback?
+ if ($resp =~ /^callback/o){
+ ($data, $Inline::Java::Callback::OBJECT_HOOK) = Inline::Java::Callback::InterceptCallback($inline, $resp) ;
+ next ;
+ }
+ else{
+ last ;
+ }
+ }
+
+ return $resp ;
+}
+
+
+
+1 ;
+
diff --git a/Java/Makefile.PL b/Java/Makefile.PL
new file mode 100644
index 0000000..2349f48
--- /dev/null
+++ b/Java/Makefile.PL
@@ -0,0 +1,357 @@
+use ExtUtils::MakeMaker ;
+use File::Find ;
+
+use strict ;
+use File::Spec ;
+
+require "Portable.pm" ;
+# The file we just produced in the parent Makefile.PL
+require "default_j2sdk.pl" ;
+
+my $build_jni = $main::build_jni ;
+my $build_perl_natives = $main::build_perl_natives ;
+my $build_perl_interpreter = $main::build_perl_interpreter ;
+my $jvm_lib_type = $main::jvm_lib_type ;
+
+my $jvm_lib = Inline::Java::Portable::portable('JVM_LIB') ;
+my $jvm_so = Inline::Java::Portable::portable('JVM_SO') ;
+
+my %so_dirs = () ;
+
+my @files = (
+ 'jni.h',
+ 'jni_md.h',
+ $jvm_lib,
+) ;
+if ($jvm_so ne $jvm_lib){
+ push @files, $jvm_so ;
+}
+push @files, 'jvm.cfg' ;
+
+my $files = {
+ 'jni.h' => {
+ discard => qr/include-old/,
+ },
+ 'jni_md.h' => {
+ discard => qr/include-old/,
+ },
+ $jvm_lib => {
+ },
+ 'jvm.cfg' => {
+ },
+ $jvm_so => {
+ },
+} ;
+
+foreach my $f (@files){
+ $files->{$f}->{selected} = undef ;
+ $files->{$f}->{choices} = [] ;
+ $files->{$f}->{default_choice} = 1 ;
+}
+
+
+my $build_jni_by_dflt = Inline::Java::Portable::portable("BUILD_JNI_BY_DFLT") ;
+if (! defined($build_jni)){
+ print
+ "\nInline::Java can use a JNI extension that allows the Java Virtual Machine\n" .
+ "(JVM) to be dynamically linked with Perl instead of running as a separate\n" .
+ "process. The use of this extension is optional, and building it still\n" .
+ "allows Inline::Java to run the JVM in the default (separate process)\n" .
+ "fashion.\n" .
+ "Note: You need a C compiler to build the extension.\n" ;
+# "Note: You must build the extension if you wish to use PerlNatives or\n" .
+# " PerlInterpreter.\n" ;
+ if (AskYN("Do you wish to build the JNI extension?",
+ ($build_jni_by_dflt ? 'y' : 'n'))){
+ $build_jni = 1 ;
+ }
+}
+if ($build_jni){
+ print "\nBuilding JNI extension.\n\n" ;
+
+ my $jdk_dir = Inline::Java::get_default_j2sdk() ;
+
+ my $symlink = Inline::Java::Portable::portable("GOT_SYMLINK") ;
+ find(
+ {
+ wanted => \&search,
+ ($symlink ? (follow_fast => 1, follow_skip => 2) : ()),
+ },
+ $jdk_dir) ;
+
+ my $type = FindDefaultVMType() ;
+ if (defined($type)){
+ my $cnt = 1 ;
+ foreach my $c (@{$files->{$jvm_so}->{choices}}){
+ if ($c =~ /$type/){
+ $files->{$jvm_so}->{default_choice} = $cnt ;
+ }
+ $cnt++ ;
+ }
+ }
+
+ # We no longer need jvm.cfg from now on...
+ pop @files ;
+
+ my $done = 0 ;
+ foreach my $f (@files){
+ my $cnt = scalar(@{$files->{$f}->{choices}}) ;
+ if ($cnt == 0){
+ print "Can't locate file '$f' anywhere under '$jdk_dir'\n" ;
+ $done = 1 ;
+ last ;
+ }
+ elsif ($cnt == 1){
+ $files->{$f}->{selected} = $files->{$f}->{choices}->[0] ;
+ }
+ else {
+ my $choose = 1 ;
+ if (($f eq $jvm_lib)&&(defined($jvm_lib_type))){
+ my @matches = grep {/$jvm_lib_type/} @{$files->{$f}->{choices}} ;
+ if (! scalar(@matches)){
+ print "WARNING: No $f type matching '$jvm_lib_type' found.\n\n" ;
+ }
+ elsif (scalar(@matches) == 1){
+ print "Automatically selecting '$matches[0]' for $f type.\n\n" ;
+ $files->{$f}->{selected} = $matches[0] ;
+ $choose = 0 ;
+ }
+ }
+ Choose($f) if $choose ;
+ }
+ }
+ if (! $done){
+ # We have all the required files selected.
+
+ CleanSoDirs() ;
+
+ # Cygwin: create gcc-compatible library wrapper for jvm.dll
+ if ($^O eq 'cygwin') {
+ my $dll = File::Spec->catfile($jdk_dir, 'lib', 'libjvm.dll.a') ;
+ print "Creating '$dll' for cygwin.\n\n" ;
+ system("/usr/bin/dlltool --input-def jvm.def --kill-at --dllname jvm.dll --output-lib '$dll'")
+ and print "Error attempting to create '$jdk_dir/lib/libjvm.dll.a'\n" ;
+ }
+
+ print "Building with:\n" ;
+ map { print " " . File::Spec->catfile($files->{$_}->{selected}, $_) . "\n" ;} @files ;
+
+ $done = 0 ;
+ if (! $done){
+ print
+ "\nNote: In order for Inline::Java to use the JNI extension, you will need to\n" .
+ "use the JNI configuration option or set the PERL_INLINE_JAVA_JNI environment\n" .
+ "variable to a true value. You will also need to add the following directories\n" .
+ "to your " . Inline::Java::Portable::portable('SO_LIB_PATH_VAR') . " environment variable:\n" ;
+ @main::SO_DIRS = keys %so_dirs ;
+ map {print " $_\n"; } @main::SO_DIRS ;
+ print "See README.JNI for more information.\n\n" ;
+
+ @main::I = map { Inline::Java::Portable::portable('SUB_FIX_MAKE_QUOTES', "-I$_") }
+ ($files->{'jni.h'}->{selected}, $files->{'jni_md.h'}->{selected}) ;
+ @main::L = map { Inline::Java::Portable::portable('SUB_FIX_MAKE_QUOTES', "-L$_") }
+ ($files->{$jvm_lib}->{selected}) ;
+
+ my $DIR = [] ;
+ if (! defined($build_perl_interpreter)){
+ print <<TXT;
+The PerlInterpreter extension allows Inline::Java to be loaded directly from
+Java using an embedded Perl interpreter. It is still EXPERIMENTAL and
+may not build or work properly on all platforms. See documentation for
+more details.
+TXT
+ if (AskYN("Do you wish to build the PerlInterpreter extension?", 'n')){
+ $build_perl_interpreter = 1 ;
+ }
+ print "\n" ;
+ }
+ if ($build_perl_interpreter){
+ push @{$DIR}, 'PerlInterpreter' ;
+ }
+
+ if (! defined($build_perl_natives)){
+ print <<TXT;
+The PerlNatives extension allows for callbacks to be defined as native
+Java methods. It is still EXPERIMENTAL and may not build or work properly
+on all platforms. See documentation for more details.
+Note: PerlNatives requires J2SDK 1.4 or greater.
+TXT
+ if (AskYN("Do you wish to build the PerlNatives extension?", 'n')){
+ $build_perl_natives = 1 ;
+ }
+ print "\n" ;
+ }
+ if ($build_perl_natives){
+ push @{$DIR}, 'PerlNatives' ;
+ }
+
+
+ WriteMakefile(
+ NAME => 'Inline::Java::JNI',
+ VERSION_FROM => 'JNI.pm',
+ DIR => $DIR,
+ PMLIBDIRS => [File::Spec->catdir('sources', 'org', 'perl', 'inline', 'java')],
+ INC => join(' ', @main::I),
+ LIBS => [join(' ', @main::L) . " -ljvm"],
+ dynamic_lib => {
+ OTHERLDFLAGS => Inline::Java::Portable::portable('OTHERLDFLAGS')
+ },
+ # CCFLAGS => '-D_REENTRANT',
+ ) ;
+ }
+ }
+}
+else{
+ print "\n" ;
+ WriteMakefile(
+ NAME => 'Inline::Java::JNI',
+ VERSION_FROM => 'JNI.pm',
+ DIR => [],
+ PMLIBDIRS => [File::Spec->catdir('sources', 'org', 'perl', 'inline', 'java')],
+ XS => {},
+ C => []
+ ) ;
+}
+
+
+#################################################
+
+
+sub search {
+ my $file = $_ ;
+
+ my $ext = Inline::Java::Portable::portable('SO_EXT') ;
+ if ($File::Find::dir =~ /jre/){
+ if ($file =~ /\.$ext$/){
+ my $dir = File::Spec->canonpath($File::Find::dir) ;
+ $so_dirs{$dir} = 1 ;
+ }
+ }
+
+ foreach my $f (@files){
+ if ($file eq $f){
+ my $re = $files->{$f}->{discard} ;
+ if ((! $re)||($File::Find::dir !~ /$re/)){
+ push @{$files->{$f}->{choices}}, File::Spec->canonpath($File::Find::dir) ;
+ }
+ last ;
+ }
+ }
+}
+
+
+sub CleanSoDirs {
+ foreach my $d (keys %so_dirs){
+ if (-e File::Spec->catfile($d, $jvm_so)){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /plugin/){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /motif/){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /javaws/){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /headless/){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /xawt/){
+ delete $so_dirs{$d} ;
+ }
+ elsif ($d =~ /_threads/){
+ if ($d !~ /native_threads/){
+ delete $so_dirs{$d} ;
+ }
+ }
+ }
+ $so_dirs{$files->{$jvm_so}->{selected}} = 1 ;
+}
+
+
+sub FindDefaultVMType {
+ my $type = undef ;
+ my $choices = $files->{'jvm.cfg'}->{choices} ;
+ if (scalar(@{$choices})){
+ my $cfg = File::Spec->catfile($choices->[0], 'jvm.cfg') ;
+ if (open(CFG, "<$cfg")){
+ while (<CFG>){
+ my $line = $_ ;
+ chomp($line) ;
+ $line =~ s/^\s+// ;
+ $line =~ s/\s+$// ;
+
+ if (! $line){
+ next ;
+ }
+ elsif ($line =~ /^#/){
+ next ;
+ }
+ else{
+ ($type) = split(/\s+/, $line) ;
+ $type =~ s/^-// ;
+ last ;
+ }
+ }
+ close(CFG) ;
+ }
+ }
+
+ return $type ;
+}
+
+
+sub Choose {
+ my $f = shift ;
+
+ my $o = $files->{$f} ;
+ my $cnt = 0 ;
+ my $def = undef ;
+ foreach my $f (@{$o->{choices}}){
+ $cnt++ ;
+ print "$cnt) $f\n" ;
+ }
+ my $idx = AskSub("Please select from the above list which '$f' to use:",
+ $o->{default_choice}, sub {(($_[0] >= 1)&&($_[0] <= $cnt))}) ;
+
+ $o->{selected} = $o->{choices}->[int($idx) - 1] ;
+ print "\n" ;
+}
+
+
+# Gets string from stdin
+sub Ask {
+ my $ques = shift ;
+ my $def = shift ;
+
+ return AskSub($ques, $def, undef) ;
+}
+
+
+# Gets yes/no from stdin
+sub AskYN {
+ my $ques = shift ;
+ my $def = shift ;
+
+ my $ans = AskSub($ques, $def, sub {((! $_[0])||($_[0] =~ /^(y|n)$/i))}) ;
+
+ return (($ans =~ /^y$/i) ? 1 : 0) ;
+}
+
+
+sub AskSub {
+ my $ques = shift ;
+ my $def = shift ;
+ my $sub = shift ;
+
+ while (1){
+ my $ans = prompt($ques, $def) ;
+ if (! $sub){
+ return $ans ;
+ }
+ elsif ($sub->($ans)){
+ return $ans ;
+ }
+ }
+}
diff --git a/Java/Object.pm b/Java/Object.pm
new file mode 100644
index 0000000..2f56856
--- /dev/null
+++ b/Java/Object.pm
@@ -0,0 +1,547 @@
+package Inline::Java::Object ;
+@Inline::Java::Object::ISA = qw(Inline::Java::Object::Tie) ;
+
+use strict ;
+use Inline::Java::Protocol ;
+use Carp ;
+
+$Inline::Java::Object::VERSION = '0.53' ;
+
+# Here we store as keys the knots and as values our blessed private objects
+my $PRIVATES = {} ;
+
+
+# Bogus constructor. We fall here if no public constructor is defined
+# in the Java class.
+sub new {
+ my $class = shift ;
+
+ croak "No public constructor defined for class $class" ;
+}
+
+
+# Constructor. Here we create a new object that will be linked
+# to a real Java object.
+sub __new {
+ my $class = shift ;
+ my $java_class = shift ;
+ my $inline = shift ;
+ my $objid = shift ;
+ my $proto = shift ;
+ my $args = shift ;
+
+ my %this = () ;
+
+ my $knot = tie %this, $class ;
+ my $this = bless(\%this, $class) ;
+
+ my $pkg = $inline->get_api('pkg') ;
+ if ($class ne "Inline::Java::Object"){
+ $class = Inline::Java::java2perl($pkg, $java_class) ;
+ }
+
+ my $priv = Inline::Java::Object::Private->new($class, $java_class, $inline) ;
+ $PRIVATES->{$knot} = $priv ;
+
+ if ($objid <= -1){
+ my $obj = undef ;
+ eval {
+ $obj = $this->__get_private()->{proto}->CreateJavaObject($java_class, $proto, $args) ;
+ } ;
+ croak $@ if $@ ;
+
+ if (! defined($this->__get_private()->{id})){
+ # Use created a java::lang::String or something...
+ return $obj ;
+ }
+ }
+ else{
+ $this->__get_private()->{id} = $objid ;
+ Inline::Java::debug(2, "creating object in java ($class):") ;
+ }
+
+ Inline::Java::debug_obj($this) ;
+
+ return $this ;
+}
+
+
+sub __get_private {
+ my $this = shift ;
+
+ my $knot = tied(%{$this}) || $this ;
+
+ my $priv = $PRIVATES->{$knot} ;
+ if (! defined($priv)){
+ croak "Unknown Java object reference $knot" ;
+ }
+
+ return $priv ;
+}
+
+
+# Checks to make sure all the arguments can be "cast" to prototype
+# types.
+sub __validate_prototype {
+ my $this = shift ;
+ my $method = shift ;
+ my $args = shift ;
+ my $protos = shift ;
+ my $inline = shift ;
+
+ my @matched = () ;
+
+ my @proto_values = values %{$protos} ;
+ my @errors = () ;
+ foreach my $s (@proto_values){
+ my $proto = $s->{SIGNATURE} ;
+ my $stat = $s->{STATIC} ;
+ my $idx = $s->{IDX} ;
+ my $new_args = undef ;
+ my $score = undef ;
+
+ my $sig = Inline::Java::Protocol->CreateSignature($proto) ;
+ Inline::Java::debug(3, "matching arguments to $method$sig") ;
+
+ eval {
+ ($new_args, $score) = Inline::Java::Class::CastArguments($args, $proto, $inline) ;
+ } ;
+ if ($@){
+ if (scalar(@proto_values) == 1){
+ # Here we have only 1 prototype, so we return the error.
+ croak $@ ;
+ }
+ push @errors, $@ ;
+ Inline::Java::debug(3, "error trying to fit args to prototype: $@") ;
+ next ;
+ }
+
+ # We passed!
+ Inline::Java::debug(3, "match successful: score is $score") ;
+ my $h = {
+ PROTO => $proto,
+ NEW_ARGS => $new_args,
+ NB_ARGS => scalar(@{$new_args}),
+ SCORE => $score,
+ STATIC => $stat,
+ IDX => $idx,
+ } ;
+
+ # Tiny optimization: abort if type coerce was used and matched for
+ # every parameter
+ if (Inline::Java::Class::IsMaxArgumentsScore($new_args, $score)){
+ Inline::Java::debug(3, "perfect match found, aborting search") ;
+ @matched = () ;
+ push @matched, $h ;
+ last ;
+ }
+ else{
+ push @matched, $h ;
+ }
+ }
+
+ my $nb_matched = scalar(@matched) ;
+ if (! $nb_matched){
+ my $name = (ref($this) ? $this->__get_private()->{class} : $this) ;
+ my $sa = Inline::Java::Protocol->CreateSignature($args) ;
+ my $msg = "In method $method of class $name: Can't find any signature that matches " .
+ "the arguments passed $sa.\nAvailable signatures are:\n" ;
+ my $i = 0 ;
+ foreach my $s (@proto_values){
+ my $proto = $s->{SIGNATURE} ;
+ my $static = ($s->{STATIC} ? "static " : "") ;
+
+ my $sig = Inline::Java::Protocol->CreateSignature($proto) ;
+ $msg .= "\t$static$method$sig\n" ;
+ $msg .= "\t\terror was: $errors[$i]" ;
+ $i++ ;
+ }
+ chomp $msg ;
+ croak $msg ;
+ }
+
+ my $chosen = undef ;
+ foreach my $h (@matched){
+ my $idx = ($chosen ? $chosen->{IDX} : 0) ;
+ my $max = ($chosen ? $chosen->{SCORE} : 0) ;
+
+ my $s = $h->{SCORE} ;
+ my $i = $h->{IDX} ;
+ if ($s > $max){
+ $chosen = $h ;
+ }
+ elsif ($s == $max){
+ # Here if the scores are equal we take the last one since
+ # we start with inherited methods and move to class mothods
+ if ($i > $idx){
+ $chosen = $h ;
+ }
+ }
+ }
+
+ if ((! $chosen->{STATIC})&&(! ref($this))){
+ # We are trying to call an instance method without an object
+ # reference
+ croak "Method $method of class $this must be called from an object reference" ;
+ }
+
+ # Here we will be polite and warn the user if we had to choose a
+ # method by ourselves.
+ if ($inline->get_java_config('WARN_METHOD_SELECT')){
+ if (($nb_matched > 1)&&
+ ($chosen->{SCORE} < ($chosen->{NB_ARGS} * 10))){
+ my $msg = "Based on the arguments passed, I had to choose between " .
+ "the following method signatures:\n" ;
+ foreach my $m (@matched){
+ my $s = Inline::Java::Protocol->CreateSignature($m->{PROTO}) ;
+ my $c = ($m eq $chosen ? "*" : " ") ;
+ $msg .= " $c $method$s\n" ;
+ }
+ $msg .= "I chose the one indicated by a star (*). To force " .
+ "the use of another signature or to disable this warning, use " .
+ "the casting functionality described in the documentation." ;
+ carp $msg ;
+ }
+ }
+
+ return (
+ $chosen->{PROTO},
+ $chosen->{NEW_ARGS},
+ $chosen->{STATIC},
+ ) ;
+}
+
+
+sub __isa {
+ my $this = shift ;
+ my $proto = shift ;
+
+ my $ret = undef ;
+ eval {
+ $ret = $this->__get_private()->{proto}->ISA($proto) ;
+ } ;
+ if ($@){
+ return ($@, 0) ;
+ }
+
+ if ($ret == -1){
+ my $c = $this->__get_private()->{java_class} ;
+ return ("$c is not a kind of $proto", 0) ;
+ }
+
+ return ('', $ret) ;
+}
+
+
+sub __cast {
+ my $this = shift ;
+ my $class = shift ;
+
+ my $ret = $this->__get_private()->{proto}->Cast($class) ;
+
+ return $ret ;
+}
+
+
+sub __get_member {
+ my $this = shift ;
+ my $key = shift ;
+
+ if ($this->__get_private()->{class} eq "Inline::Java::Object"){
+ croak "Can't get member '$key' for an object that is not bound to Perl" ;
+ }
+
+ Inline::Java::debug(3, "fetching member variable '$key'") ;
+
+ my $inline = $this->__get_private()->{inline} ;
+ my $fields = $inline->get_fields($this->__get_private()->{class}) ;
+
+ my $types = $fields->{$key} ;
+ if ($types){
+ # We take the last one, which is more specific. Eventually
+ # we should use a scoring method just like for the methods
+ my $sign = undef ;
+ foreach my $s (values %{$types}){
+ if ((! defined($sign))||($s->{IDX} > $sign->{IDX})){
+ $sign = $s ;
+ }
+ }
+
+ my $proto = $sign->{TYPE} ;
+
+ my $ret = $this->__get_private()->{proto}->GetJavaMember($key, [$proto], [undef]) ;
+ Inline::Java::debug(3, "returning member (" . ($ret || '') . ")") ;
+
+ return $ret ;
+ }
+ else{
+ my $name = $this->__get_private()->{class} ;
+ croak "No public member variable '$key' defined for class '$name'" ;
+ }
+}
+
+
+sub __set_member {
+ my $this = shift ;
+ my $key = shift ;
+ my $value = shift ;
+
+ if ($this->__get_private()->{class} eq "Inline::Java::Object"){
+ croak "Can't set member '$key' for an object that is not bound to Perl" ;
+ }
+
+ my $inline = $this->__get_private()->{inline} ;
+ my $fields = $inline->get_fields($this->__get_private()->{class}) ;
+
+ my $types = $fields->{$key} ;
+ if ($types){
+ # We take the last one, which is more specific. Eventually
+ # we should use a scoring method just like for the methods
+ my $sign = undef ;
+ foreach my $s (values %{$types}){
+ if ((! defined($sign))||($s->{IDX} > $sign->{IDX})){
+ $sign = $s ;
+ }
+ }
+
+ my $proto = $sign->{TYPE} ;
+ my $new_args = undef ;
+ my $score = undef ;
+
+ ($new_args, $score) = Inline::Java::Class::CastArguments([$value], [$proto], $this->__get_private()->{inline}) ;
+ $this->__get_private()->{proto}->SetJavaMember($key, [$proto], $new_args) ;
+ }
+ else{
+ my $name = $this->__get_private()->{class} ;
+ croak "No public member variable '$key' defined for class '$name'" ;
+ }
+}
+
+
+sub AUTOLOAD {
+ my $this = shift ;
+ my @args = @_ ;
+
+ use vars qw($AUTOLOAD) ;
+ my $func_name = $AUTOLOAD ;
+ # Strip package from $func_name, Java will take of finding the correct
+ # method.
+ $func_name =~ s/^(.*)::// ;
+
+ Inline::Java::debug(5, "$func_name") ;
+
+ my $name = (ref($this) ? $this->__get_private()->{class} : $this) ;
+ if ($name eq "Inline::Java::Object"){
+ croak "Can't call method '$func_name' on an object ($name) that is not bound to Perl" ;
+ }
+
+ croak "No public method '$func_name' defined for class '$name'" ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+ my $knot = tied %{$this} ;
+ if (! $knot){
+ Inline::Java::debug(4, "destroying Inline::Java::Object::Tie") ;
+
+ if (! Inline::Java::get_DONE()){
+
+ my $class = $this->__get_private()->{class} ;
+ Inline::Java::debug(2, "destroying object in java ($class):") ;
+
+ {
+ local $@ ;
+ eval {
+ $this->__get_private()->{proto}->DeleteJavaObject($this) ;
+ } ;
+ if ($@){
+ # We croaked here. Was there already a pending $@?
+ my $name = $this->__get_private()->{class} ;
+ croak "In method DESTROY of class $name: $@" ;
+ }
+ }
+
+ # Here we have a circular reference so we need to break it
+ # so that the memory is collected.
+ my $priv = $this->__get_private() ;
+ my $proto = $priv->{proto} ;
+ $priv->{proto} = undef ;
+ $proto->{obj_priv} = undef ;
+ $PRIVATES->{$this} = undef ;
+ }
+ else{
+ Inline::Java::debug(4, "script marked as DONE, object destruction not propagated to Java") ;
+ }
+ }
+ else{
+ Inline::Java::debug(4, "destroying Inline::Java::Object") ;
+ }
+}
+
+
+
+######################## Hash Methods ########################
+package Inline::Java::Object::Tie ;
+@Inline::Java::Object::Tie::ISA = qw(Tie::StdHash) ;
+
+
+use Tie::Hash ;
+use Carp ;
+
+
+sub TIEHASH {
+ my $class = shift ;
+
+ return $class->SUPER::TIEHASH(@_) ;
+}
+
+
+sub STORE {
+ my $this = shift ;
+ my $key = shift ;
+ my $value = shift ;
+
+ return $this->__set_member($key, $value) ;
+}
+
+
+sub FETCH {
+ my $this = shift ;
+ my $key = shift ;
+
+ return $this->__get_member($key) ;
+}
+
+
+sub FIRSTKEY {
+ my $this = shift ;
+
+ return $this->SUPER::FIRSTKEY() ;
+}
+
+
+sub NEXTKEY {
+ my $this = shift ;
+
+ return $this->SUPER::NEXTKEY() ;
+}
+
+
+sub EXISTS {
+ my $this = shift ;
+ my $key = shift ;
+
+ my $inline = $this->__get_private()->{inline} ;
+ my $fields = $inline->get_fields($this->__get_private()->{class}) ;
+
+ if ($fields->{$key}){
+ return 1 ;
+ }
+
+ return 0 ;
+}
+
+
+sub DELETE {
+ my $this = shift ;
+ my $key = shift ;
+
+ croak "Operation DELETE not supported on Java object" ;
+}
+
+
+sub CLEAR {
+ my $this = shift ;
+
+ croak "Operation CLEAR not supported on Java object" ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+}
+
+
+
+
+######################## Static Member Methods ########################
+package Inline::Java::Object::StaticMember ;
+@Inline::Java::Object::StaticMember::ISA = qw(Tie::StdScalar) ;
+
+
+use Tie::Scalar ;
+use Carp ;
+
+my $DUMMIES = {} ;
+
+
+sub TIESCALAR {
+ my $class = shift ;
+ my $dummy = shift ;
+ my $name = shift ;
+
+ my $this = $class->SUPER::TIESCALAR(@_) ;
+
+ $DUMMIES->{$this} = [$dummy, $name] ;
+
+ return $this ;
+}
+
+
+sub STORE {
+ my $this = shift ;
+ my $value = shift ;
+
+ my ($obj, $key) = @{$DUMMIES->{$this}} ;
+
+ return $obj->__set_member($key, $value) ;
+}
+
+
+sub FETCH {
+ my $this = shift ;
+
+ my ($obj, $key) = @{$DUMMIES->{$this}} ;
+
+ return $obj->__get_member($key) ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+}
+
+
+
+######################## Private Object ########################
+package Inline::Java::Object::Private ;
+
+sub new {
+ my $class = shift ;
+ my $obj_class = shift ;
+ my $java_class = shift ;
+ my $inline = shift ;
+
+ my $this = {} ;
+ $this->{class} = $obj_class ;
+ $this->{java_class} = $java_class ;
+ $this->{inline} = $inline ;
+ $this->{proto} = new Inline::Java::Protocol($this, $inline) ;
+
+ bless($this, $class) ;
+
+ return $this ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+ Inline::Java::debug(4, "destroying Inline::Java::Object::Private") ;
+}
+
+
+
+1 ;
diff --git a/Java/PerlInterpreter/Makefile.PL b/Java/PerlInterpreter/Makefile.PL
new file mode 100644
index 0000000..8d867cd
--- /dev/null
+++ b/Java/PerlInterpreter/Makefile.PL
@@ -0,0 +1,27 @@
+use ExtUtils::MakeMaker ;
+use ExtUtils::Embed ;
+use Config ;
+
+use strict ;
+require "../Portable.pm" ;
+
+
+my $ccopts = ccopts() ;
+chomp($ccopts) ;
+my $ldopts = ldopts() ;
+chomp($ldopts) ;
+
+my $pre = Inline::Java::Portable::portable("PRE_WHOLE_ARCHIVE") ;
+my $post = Inline::Java::Portable::portable("POST_WHOLE_ARCHIVE") ;
+my $dupenv = Inline::Java::Portable::portable("PERL_PARSE_DUP_ENV") ;
+
+WriteMakefile(
+ NAME => 'Inline::Java::PerlInterpreter',
+ VERSION_FROM => 'PerlInterpreter.pm',
+ CCFLAGS => "$ccopts $dupenv",
+ LDDLFLAGS => "$pre $ldopts $post $Config{lddlflags}",
+ INC => join(' ', @main::I),
+ dynamic_lib => Inline::Java::Portable::portable("dynamic_lib"),
+ # CCFLAGS => '-D_REENTRANT',
+) ;
+
diff --git a/Java/PerlInterpreter/PerlInterpreter.pm b/Java/PerlInterpreter/PerlInterpreter.pm
new file mode 100644
index 0000000..842a09c
--- /dev/null
+++ b/Java/PerlInterpreter/PerlInterpreter.pm
@@ -0,0 +1,17 @@
+package Inline::Java::PerlInterpreter ;
+
+use strict ;
+use Inline::Java ;
+
+$Inline::Java::PerlInterpreter::VERSION = '0.52' ;
+
+
+use Inline (
+ Java => 'STUDY',
+ EMBEDDED_JNI => 1,
+ STUDY => [],
+ NAME => 'Inline::Java::PerlInterpreter',
+) ;
+
+
+1 ;
diff --git a/Java/PerlInterpreter/PerlInterpreter.pod b/Java/PerlInterpreter/PerlInterpreter.pod
new file mode 100644
index 0000000..436117c
--- /dev/null
+++ b/Java/PerlInterpreter/PerlInterpreter.pod
@@ -0,0 +1,115 @@
+=head1 NAME
+
+Inline::Java::PerlInterpreter - Call Perl directly from Java using Inline::Java.
+
+=head1 SYNOPSIS
+
+=for comment
+
+ import org.perl.inline.java.* ;
+
+ class HelpMePerl {
+ static private InlineJavaPerlInterpreter pi = null ;
+
+ public HelpMePerl() throws InlineJavaException {
+ }
+
+ static private boolean matches(String target, String pattern)
+ throws InlineJavaPerlException, InlineJavaException {
+ Boolean b = (Boolean)pi.eval("'" + target + "' =~ /" + pattern + "/", Boolean.class) ;
+ return b.booleanValue() ;
+ }
+
+ public static void main(String args[])
+ throws InlineJavaPerlException, InlineJavaException {
+ pi = InlineJavaPerlInterpreter.create() ;
+
+ String target = "aaabbbccc" ;
+ String pattern = "ab+" ;
+ boolean ret = matches(target, pattern) ;
+
+ System.out.println(
+ target + (ret ? " matches " : " doesn't match ") + pattern) ;
+
+ pi.destroy() ;
+ }
+ }
+
+=for comment
+
+
+=head1 DESCRIPTION
+
+WARNING: C<Inline::Java::PerlInterpreter> is still experimental.
+
+The C<org.perl.inline.java.InlineJavaPerlInterpreter> Java class allows
+you to load a Perl interpreter directly from Java. You can then perform
+regular callbacks to call into Perl.
+ Z<>
+
+
+=head1 USING THE org.perl.inline.java.InlineJavaPerlInterpreter CLASS
+
+B<Installation>
+
+Before using C<org.perl.inline.java.InlineJavaPerlInterpreter>, you must
+have installed C<Inline::Java> as well as the JNI extension. Additionally,
+the PerlInterpreter extension must also have been installed.
+
+B<Finding the jar>
+
+To be able to use the C<org.perl.inline.java.InlineJavaPerlInterpreter>
+class, you must use the jar file provided by C<Inline::Java>. You can
+easily locate this jar file using the following command:
+
+ % perl -MInline::Java=jar
+
+You must then add this jar file to your CLASSPATH as you would any jar
+file.
+
+B<Basic Usage>
+
+C<org.perl.inline.java.InlineJavaPerlInterpreter> itself extends
+C<org.perl.inline.java.InlineJavaPerlCaller>. See L<Inline::Java::Callback>
+for information on the callback API.
+
+Besides that API, C<org.perl.inline.java.InlineJavaPerlInterpreter> provides
+only 2 other public methods:
+
+=over 4
+
+=item public InlineJavaPerlInterpreter create()
+throws InlineJavaPerlException, InlineJavaException
+
+Creates a new org.perl.inline.java.InlineJavaPerlInterpreter object.
+This class in a singleton.
+
+=item public void destroy()
+
+Destroys the Perl interpreter.
+
+=back
+
+
+=head1 SEE ALSO
+
+L<Inline::Java>, L<Inline::Java::Callback>, L<Inline::Java::PerlNatives>.
+ Z<>
+
+
+=head1 AUTHOR
+
+Patrick LeBoutillier <patl@cpan.org> is the author of Inline::Java.
+ Z<>
+
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2004, Patrick LeBoutillier.
+
+All Rights Reserved. This module is free software. It may be used,
+redistributed and/or modified under the terms of the Perl Artistic
+License. See http://www.perl.com/perl/misc/Artistic.html for more
+details.
+
+=cut
diff --git a/Java/PerlInterpreter/PerlInterpreter.xs b/Java/PerlInterpreter/PerlInterpreter.xs
new file mode 100644
index 0000000..d8d5ac9
--- /dev/null
+++ b/Java/PerlInterpreter/PerlInterpreter.xs
@@ -0,0 +1,95 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifdef __CYGWIN__
+ #include "w32api/basetyps.h"
+#endif
+
+/* Include the JNI header file */
+#include "jni.h"
+
+/* The PerlInterpreter handle */
+PerlInterpreter *interp = NULL ;
+
+
+/* XS initialisation stuff */
+void boot_DynaLoader(pTHX_ CV* cv) ;
+
+
+static void xs_init(pTHX){
+ char *file = __FILE__ ;
+ dXSUB_SYS ;
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file) ;
+}
+
+
+
+void throw_ijp(JNIEnv *env, char *msg){
+ jclass ije ;
+
+ ije = (*(env))->FindClass(env, "org/perl/inline/java/InlineJavaPerlException") ;
+ if ((*(env))->ExceptionCheck(env)){
+ (*(env))->ExceptionDescribe(env) ;
+ (*(env))->ExceptionClear(env) ;
+ (*(env))->FatalError(env, "Can't find class InlineJavaPerlException: exiting...") ;
+ }
+ (*(env))->ThrowNew(env, ije, msg) ;
+}
+
+
+JNIEXPORT void JNICALL Java_org_perl_inline_java_InlineJavaPerlInterpreter_construct(JNIEnv *env, jclass cls){
+ char *args[] = {"inline-java", "-e1"} ;
+ char **envdup = NULL ;
+
+#ifdef PERL_PARSE_ENV_DUP
+ int envl = 0 ;
+ int i = 0 ;
+ /* This will leak, but it's a one shot... */
+ for (i = 0 ; environ[i] != NULL ; i++){
+ envl++ ;
+ }
+ envdup = (char **)calloc(envl + 1, sizeof(char *)) ;
+ for (i = 0 ; i < envl ; i++){
+ envdup[i] = strdup(environ[i]) ;
+ }
+#endif
+
+ interp = perl_alloc() ;
+ perl_construct(interp) ;
+ perl_parse(interp, xs_init, 2, args, envdup) ;
+ perl_run(interp) ;
+}
+
+
+JNIEXPORT void JNICALL Java_org_perl_inline_java_InlineJavaPerlInterpreter_destruct(JNIEnv *env, jclass cls){
+ if (interp != NULL){
+ perl_destruct(interp) ;
+ perl_free(interp) ;
+ interp = NULL ;
+ }
+}
+
+
+JNIEXPORT void JNICALL Java_org_perl_inline_java_InlineJavaPerlInterpreter_evalNoReturn(JNIEnv *env, jclass cls, jstring code){
+ SV *sv = NULL ;
+ char *pcode = NULL ;
+
+ pcode = (char *)((*(env))->GetStringUTFChars(env, code, NULL)) ;
+ sv = sv_2mortal(newSVpv(pcode, 0)) ;
+ /* sv = eval_pv(pcode, FALSE) ; */
+ eval_sv(sv, G_EVAL|G_KEEPERR) ;
+ (*(env))->ReleaseStringUTFChars(env, code, pcode) ;
+ if (SvTRUE(ERRSV)){
+ STRLEN n_a ;
+ throw_ijp(env, SvPV(ERRSV, n_a)) ;
+ }
+}
+
+
+
+MODULE = Inline::Java::PerlInterpreter PACKAGE = Inline::Java::PerlInterpreter
+
+PROTOTYPES: DISABLE
+
+
diff --git a/Java/PerlInterpreter/t/01_init.t b/Java/PerlInterpreter/t/01_init.t
new file mode 100644
index 0000000..d889213
--- /dev/null
+++ b/Java/PerlInterpreter/t/01_init.t
@@ -0,0 +1,10 @@
+use strict ;
+use Test ;
+
+BEGIN {
+ plan(tests => 1) ;
+ mkdir('./_Inline_test', 0777) unless -e './_Inline_test' ;
+}
+
+
+ok(1) ;
diff --git a/Java/PerlInterpreter/t/02_perl_interpreter.t b/Java/PerlInterpreter/t/02_perl_interpreter.t
new file mode 100644
index 0000000..dc04efa
--- /dev/null
+++ b/Java/PerlInterpreter/t/02_perl_interpreter.t
@@ -0,0 +1,146 @@
+use strict ;
+
+use Test ;
+use File::Spec ;
+use Config ;
+
+BEGIN {
+ if ($^O eq 'cygwin'){
+ # Stand-alone Java interpreter cannot load Cygwin DLL directly
+ plan(tests => 0) ;
+ exit ;
+ }
+
+ plan(tests => 12) ;
+}
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'DATA',
+ NAME => 'Tests'
+) ;
+use Inline::Java::Portable ;
+ok(1) ;
+
+
+my $inline = $org::perl::inline::java::InlineJavaPerlInterpreterTests::INLINE ;
+$inline = $org::perl::inline::java::InlineJavaPerlInterpreterTests::INLINE ; # stupid warning...
+
+my $install_dir = File::Spec->catdir($inline->get_api('install_lib'),
+ 'auto', $inline->get_api('modpname')) ;
+
+require Inline::Java->find_default_j2sdk() ;
+my $server_jar = Inline::Java::Portable::get_server_jar() ;
+
+run_java($install_dir, $server_jar) ;
+
+
+#################################################
+
+
+sub run_java {
+ my @cps = @_ ;
+
+ $ENV{CLASSPATH} = Inline::Java::Portable::make_classpath(@cps) ;
+ Inline::Java::debug(1, "CLASSPATH is $ENV{CLASSPATH}\n") ;
+
+ my $java = File::Spec->catfile(
+ Inline::Java::get_default_j2sdk(),
+ Inline::Java::Portable::portable("J2SDK_BIN"),
+ 'java' . Inline::Java::Portable::portable("EXE_EXTENSION")) ;
+
+ my $debug = $ENV{PERL_INLINE_JAVA_DEBUG} || 0 ;
+ my $cmd = Inline::Java::Portable::portable("SUB_FIX_CMD_QUOTES", "\"$java\" " .
+ "org.perl.inline.java.InlineJavaPerlInterpreterTests $debug") ;
+ Inline::Java::debug(1, "Command is $cmd\n") ;
+ open(CMD, "$cmd|") or die("Can't execute $cmd: $!") ;
+ while (<CMD>){
+ print $_ ;
+ }
+}
+
+
+__END__
+
+__Java__
+package org.perl.inline.java ;
+
+class InlineJavaPerlInterpreterTests implements Runnable {
+ private static int cnt = 2 ;
+ private static InlineJavaPerlInterpreter pi = null ;
+ private static int nb_callbacks_to_run = 5 ;
+ private static int nb_callbacks_run = 0 ;
+
+ private InlineJavaPerlInterpreterTests() throws InlineJavaException, InlineJavaPerlException {
+ }
+
+ private synchronized static void ok(Object o1, Object o2){
+ if (o1.equals(o2)){
+ String comment = " # " + o1 + " == " + o2 ;
+ System.out.println("ok " + cnt + comment) ;
+ }
+ else {
+ String comment = " # " + o1 + " != " + o2 ;
+ System.out.println("nok " + cnt + comment) ;
+ }
+ cnt++ ;
+ }
+
+
+ public void run(){
+ try {
+ String name = (String)pi.CallPerlSub("whats_your_name", null, String.class) ;
+ ok(name, "perl") ;
+ nb_callbacks_run++ ;
+
+ if (nb_callbacks_run == nb_callbacks_to_run){
+ pi.StopCallbackLoop() ;
+ }
+ }
+ catch (Exception e){
+ e.printStackTrace() ;
+ System.exit(1) ;
+ }
+ }
+
+
+ public static void main(String args[]){
+ try {
+ int debug = 0 ;
+ if (args.length > 0){
+ debug = Integer.parseInt(args[0]) ;
+ InlineJavaUtils.set_debug(debug) ;
+ }
+
+ InlineJavaPerlInterpreter.init("test") ;
+ pi = InlineJavaPerlInterpreter.create() ;
+
+ pi.require("t/Tests.pl") ;
+ ok("1", "1") ;
+ pi.require("Carp") ;
+ ok("1", "1") ;
+ Integer sum = (Integer)pi.eval("34 + 56", Integer.class) ;
+ ok(sum, new Integer(90)) ;
+ String name = (String)pi.CallPerlSub("whats_your_name", null, String.class) ;
+ ok(name, "perl") ;
+
+ for (int i = 1 ; i <= nb_callbacks_to_run ; i++){
+ Thread t = new Thread(new InlineJavaPerlInterpreterTests()) ;
+ t.start() ;
+ }
+
+ pi.StartCallbackLoop();
+
+ pi.destroy() ;
+ ok("1", "1") ;
+ }
+ catch (Exception e){
+ e.printStackTrace() ;
+ System.exit(1) ;
+ }
+ ok("1", "1") ;
+ }
+}
diff --git a/Java/PerlInterpreter/t/Tests.pl b/Java/PerlInterpreter/t/Tests.pl
new file mode 100644
index 0000000..6254ab7
--- /dev/null
+++ b/Java/PerlInterpreter/t/Tests.pl
@@ -0,0 +1,8 @@
+package main ;
+use strict ;
+
+sub whats_your_name {
+ return "perl" ;
+}
+
+1 ;
diff --git a/Java/PerlNatives/Makefile.PL b/Java/PerlNatives/Makefile.PL
new file mode 100644
index 0000000..b5f94dc
--- /dev/null
+++ b/Java/PerlNatives/Makefile.PL
@@ -0,0 +1,16 @@
+use ExtUtils::MakeMaker ;
+
+use strict ;
+require "../Portable.pm" ;
+
+
+WriteMakefile(
+ NAME => 'Inline::Java::PerlNatives',
+ VERSION_FROM => 'PerlNatives.pm',
+ INC => join(' ', @main::I),
+ LIBS => [join(' ', @main::L) . " -ljvm"],
+ dynamic_lib => Inline::Java::Portable::portable("dynamic_lib"),
+ # CCFLAGS => '-D_REENTRANT',
+ clean => {FILES => "_Inline_test"},
+) ;
+
diff --git a/Java/PerlNatives/PerlNatives.pm b/Java/PerlNatives/PerlNatives.pm
new file mode 100644
index 0000000..dd587ff
--- /dev/null
+++ b/Java/PerlNatives/PerlNatives.pm
@@ -0,0 +1,7 @@
+package Inline::Java::PerlNatives ;
+
+use strict ;
+
+$Inline::Java::PerlNatives::VERSION = '0.52' ;
+
+1 ;
diff --git a/Java/PerlNatives/PerlNatives.pod b/Java/PerlNatives/PerlNatives.pod
new file mode 100644
index 0000000..f479d28
--- /dev/null
+++ b/Java/PerlNatives/PerlNatives.pod
@@ -0,0 +1,142 @@
+=head1 NAME
+
+Inline::Java::PerlNatives - Map Java native methods to Perl functions.
+
+=head1 SYNOPSIS
+
+=for comment
+
+
+ use Inline Java => <<'END' ;
+ import org.perl.inline.java.* ;
+
+ class Pod_PN extends InlineJavaPerlNatives {
+ public Pod_PN() throws InlineJavaException {
+ }
+
+ native public String hello() ;
+ }
+ END
+
+ package Pod_PN ;
+ sub hello {
+ return "hi!" ;
+ }
+
+ package main ;
+ my $b = new Pod_PN() ;
+ print($b->hello() . "\n") ; # prints hi!
+
+=for comment
+
+
+=head1 DESCRIPTION
+
+WARNING: C<Inline::Java::PerlNatives> is still experimental.
+
+C<Inline::Java::PerlNatives> allows you to define your callbacks as native
+Java methods that are automatically linked to Perl subroutines. You implement
+the Perl subroutine directly in the package in which C<Inline::Java> binds
+your class. You can do this by making your Java code extend the
+C<org.perl.inline.java.InlineJavaPerlNatives> class.
+
+Note: PerlNatives requires J2SDK version >= 1.4
+ Z<>
+
+
+=head1 USING THE org.perl.inline.java.InlineJavaPerlNatives CLASS
+
+Let's revisit an example from the L<Inline::Java::Callback> documentation:
+
+=for comment
+
+ use Inline Java => <<'END' ;
+ import java.util.* ;
+ import org.perl.inline.java.* ;
+ import javax.swing.* ;
+ import java.awt.event.* ;
+
+ class Pod_Button_PN extends InlineJavaPerlNatives
+ implements ActionListener {
+ public Pod_Button_PN() throws InlineJavaException {
+ JFrame frame = new JFrame("Pod_Button") ;
+ frame.setSize(100,100) ;
+ JButton button = new JButton("Click Me!") ;
+ frame.getContentPane().add(button) ;
+ button.addActionListener(this) ;
+ frame.show() ;
+ }
+
+ public void actionPerformed(ActionEvent e){
+ button_pressed() ;
+ }
+
+ native public void button_pressed() ;
+ }
+ END
+
+ package Pod_Button_PN ;
+ sub button_pressed {
+ print('click!' . "\n") ; # prints click!
+ $main::b->StopCallbackLoop() ;
+ }
+
+ package main ;
+ $main::b = new Pod_Button_PN() ;
+ $main::b->StartCallbackLoop() ;
+
+=for comment
+
+Extending InlineJavaPerlNatives tells C<Inline::Java> that all native methods
+declared in that class should be linked to Perl subroutines implemented in the
+approriate package. You can then call these methods from Java just like regular
+methods. You can even call them from Perl if they are public.
+ Z<>
+
+
+=head1 BUGS AND DEFICIENCIES
+
+C<Inline::Java::PerlNatives> has a few limits that one must be aware of:
+
+=over 4
+
+=item 1
+
+You cannot declare 2 native methods with the same name in a class (even if they
+have different signatures).
+
+=item 2
+
+Native methods can have arguments of any type, but they must return either void
+or an Object (use wrappers like Integer and Double to return primitive types).
+
+=item 3
+
+Even if you do not declare them, InlineJavaException and InlineJavaPerlException
+exceptions (as well as others) may be thrown from within the native methods
+
+=back
+
+
+=head1 SEE ALSO
+
+L<Inline::Java>, L<Inline::Java::Callback>, L<Inline::Java::PerlInterpreter>.
+ Z<>
+
+
+=head1 AUTHOR
+
+Patrick LeBoutillier <patl@cpan.org> is the author of Inline::Java.
+ Z<>
+
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001-2004, Patrick LeBoutillier.
+
+All Rights Reserved. This module is free software. It may be used,
+redistributed and/or modified under the terms of the Perl Artistic
+License. See http://www.perl.com/perl/misc/Artistic.html for more
+details.
+
+=cut
diff --git a/Java/PerlNatives/PerlNatives.xs b/Java/PerlNatives/PerlNatives.xs
new file mode 100644
index 0000000..c66a5be
--- /dev/null
+++ b/Java/PerlNatives/PerlNatives.xs
@@ -0,0 +1,222 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+/*
+#include "stdlib.h"
+#include "string.h"
+#include "stdio.h"
+#include "stdarg.h"
+*/
+
+#ifdef __CYGWIN__
+ #include "w32api/basetyps.h"
+#endif
+
+/* Include the JNI header file */
+#include "jni.h"
+
+
+void throw_ije(JNIEnv *env, char *msg){
+ jclass ije ;
+
+ ije = (*(env))->FindClass(env, "org/perl/inline/java/InlineJavaException") ;
+ if ((*(env))->ExceptionCheck(env)){
+ (*(env))->ExceptionDescribe(env) ;
+ (*(env))->ExceptionClear(env) ;
+ (*(env))->FatalError(env, "Can't find class InlineJavaException: exiting...") ;
+ }
+ (*(env))->ThrowNew(env, ije, msg) ;
+}
+
+
+/*
+ Here we simply check if an exception is pending an re-throw it
+*/
+int check_exception_from_java(JNIEnv *env){
+ jthrowable exc ;
+ int ret = 0 ;
+
+ exc = (*(env))->ExceptionOccurred(env) ;
+ if (exc != NULL){
+ /* (*(env))->ExceptionDescribe(env) ; */
+ (*(env))->ExceptionClear(env) ;
+ if ((*(env))->Throw(env, exc)){
+ (*(env))->FatalError(env, "Throw of InlineJava*Exception failed: exiting...") ;
+ }
+ ret = 1 ;
+ }
+
+ return ret ;
+}
+
+
+jobject create_primitive_object(JNIEnv *env, char f, char *cls_name, jvalue val){
+ jclass arg_cls ;
+ jmethodID mid ;
+ jobject ret = NULL ;
+ char sign[64] ;
+
+ arg_cls = (*(env))->FindClass(env, cls_name) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+ sprintf(sign, "(%c)V", f) ;
+ mid = (*(env))->GetMethodID(env, arg_cls, "<init>", sign) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+ ret = (*(env))->NewObjectA(env, arg_cls, mid, &val) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ return ret ;
+}
+
+
+jobject extract_va_arg(JNIEnv *env, va_list *list, char f){
+ jobject ret = NULL ;
+ jvalue val ;
+
+ /*
+ A bit of voodoo going on for J and F, but the rest I think is pretty
+ kosher (on a 32 bit machine at least...)
+ */
+ switch(f){
+ case 'B':
+ val.b = (jbyte)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Byte", val) ;
+ break ;
+ case 'S':
+ val.s = (jshort)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Short", val) ;
+ break ;
+ case 'I':
+ val.i = (jint)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Integer", val) ;
+ break ;
+ case 'J':
+ val.d = (jdouble)va_arg(*list, double) ;
+ ret = create_primitive_object(env, f, "java/lang/Long", val) ;
+ break ;
+ case 'F':
+ /* Seems float is not properly promoted to double... */
+ val.i = (jint)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Float", val) ;
+ break ;
+ case 'D':
+ val.d = (jdouble)va_arg(*list, double) ;
+ ret = create_primitive_object(env, f, "java/lang/Double", val) ;
+ break ;
+ case 'Z':
+ val.z = (jboolean)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Boolean", val) ;
+ break ;
+ case 'C':
+ val.c = (jchar)va_arg(*list, int) ;
+ ret = create_primitive_object(env, f, "java/lang/Character", val) ;
+ break ;
+ }
+
+ return ret ;
+}
+
+
+/*
+ This is the generic native function that callback java to call the proper
+ perl method.
+*/
+jobject JNICALL generic_perl_native(JNIEnv *env, jobject obj, ...){
+ va_list list ;
+ jclass cls ;
+ jmethodID mid ;
+ jstring jfmt ;
+ char *fmt ;
+ int fmt_len ;
+ jclass obj_cls ;
+ jobjectArray obj_array ;
+ jobject arg ;
+ int i ;
+ jobject ret = NULL ;
+
+ cls = (*(env))->GetObjectClass(env, obj) ;
+ mid = (*(env))->GetMethodID(env, cls, "LookupMethod", "()Ljava/lang/String;") ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ /* Call obj.LookupMethod to get the format string */
+ jfmt = (*(env))->CallObjectMethod(env, obj, mid) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ fmt = (char *)((*(env))->GetStringUTFChars(env, jfmt, NULL)) ;
+ fmt_len = strlen(fmt) ;
+
+ obj_cls = (*(env))->FindClass(env, "java/lang/Object") ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ obj_array = (*(env))->NewObjectArray(env, fmt_len, obj_cls, NULL) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ (*(env))->SetObjectArrayElement(env, obj_array, 0, obj) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+ va_start(list, obj) ;
+ for (i = 1 ; i < fmt_len ; i++){
+ if (fmt[i] != 'L'){
+ arg = extract_va_arg(env, &list, fmt[i]) ;
+ if (arg == NULL){
+ return NULL ;
+ }
+ }
+ else{
+ arg = (jobject)va_arg(list, jobject) ;
+ }
+ (*(env))->SetObjectArrayElement(env, obj_array, i, arg) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+ }
+ va_end(list) ;
+
+ /* Call obj.InvokePerlMethod and grab the returned object and return it */
+ mid = (*(env))->GetMethodID(env, cls, "InvokePerlMethod", "([Ljava/lang/Object;)Ljava/lang/Object;") ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ ret = (*(env))->CallObjectMethod(env, obj, mid, obj_array) ;
+ if (check_exception_from_java(env)){
+ return NULL ;
+ }
+
+ return ret ;
+}
+
+
+/*
+ This function is used to register the specified native method and associate it with our magic
+ method that trap and redirects all the Perl native calls.
+*/
+JNIEXPORT void JNICALL Java_org_perl_inline_java_InlineJavaPerlNatives_RegisterMethod(JNIEnv *env, jobject obj, jclass cls, jstring name, jstring signature){
+ JNINativeMethod nm ;
+
+ /* Register the function */
+ nm.name = (char *)((*(env))->GetStringUTFChars(env, name, NULL)) ;
+ nm.signature = (char *)((*(env))->GetStringUTFChars(env, signature, NULL)) ;
+ nm.fnPtr = generic_perl_native ;
+
+ (*(env))->RegisterNatives(env, cls, &nm, 1) ;
+ (*(env))->ReleaseStringUTFChars(env, name, nm.name) ;
+ (*(env))->ReleaseStringUTFChars(env, signature, nm.signature) ;
+ if (check_exception_from_java(env)){
+ return ;
+ }
+}
diff --git a/Java/PerlNatives/t/01_init.t b/Java/PerlNatives/t/01_init.t
new file mode 100644
index 0000000..d889213
--- /dev/null
+++ b/Java/PerlNatives/t/01_init.t
@@ -0,0 +1,10 @@
+use strict ;
+use Test ;
+
+BEGIN {
+ plan(tests => 1) ;
+ mkdir('./_Inline_test', 0777) unless -e './_Inline_test' ;
+}
+
+
+ok(1) ;
diff --git a/Java/PerlNatives/t/02_perl_natives.t b/Java/PerlNatives/t/02_perl_natives.t
new file mode 100644
index 0000000..7b6932c
--- /dev/null
+++ b/Java/PerlNatives/t/02_perl_natives.t
@@ -0,0 +1,118 @@
+use strict ;
+use Test ;
+
+
+BEGIN {
+ plan(tests => 5) ;
+}
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline::Java qw(caught) ;
+
+use Inline (
+ Java => 'DATA',
+) ;
+
+
+eval {
+ t121->init() ;
+ my $t = new t121() ;
+ ok($t->types_stub(1, 2, 3, 4, 5, 6, 1, 2, "1000"), 1024) ;
+ ok($t->array_stub([34, 56], ["toto", "789"]), 789 + 34) ;
+
+ my $t2 = new t1212() ;
+ ok($t2->types_stub(1, 2, 3, 4, 5, 6, 1, 2, "1000"), 1024) ;
+
+ ok($t->callback_stub(), "toto") ;
+ ok($t->__get_private()->{proto}->ObjectCount(), 2) ;
+} ;
+if ($@){
+ if (caught("java.lang.Throwable")){
+ $@->printStackTrace() ;
+ die("Caught Java Exception") ;
+ }
+ else{
+ die $@ ;
+ }
+}
+
+
+##################################
+
+package t121 ;
+sub types {
+ my $this = shift ;
+
+ my $sum = 0 ;
+ map {$sum += $_} @_ ;
+ return $sum ;
+}
+
+
+sub array {
+ my $this = shift ;
+ my $i = shift ;
+ my $str = shift ;
+
+ return $i->[0] + $str->[1] ;
+}
+
+
+sub callback {
+ my $this = shift ;
+
+ return $this->get_name() ;
+}
+
+
+package main ;
+__DATA__
+
+__Java__
+
+
+import java.io.* ;
+import org.perl.inline.java.* ;
+
+class t121 extends InlineJavaPerlNatives {
+ static public boolean got14(){
+ return System.getProperty("java.version").startsWith("1.4") ;
+ }
+
+ public t121() throws InlineJavaException {
+ }
+
+ static public void init() throws InlineJavaException {
+ init("test") ;
+ }
+
+ public String types_stub(byte b, short s, int i, long j, float f, double d,
+ boolean x, char c, String str){
+ return types(b, s, i, j, f, d, x, c, str) ;
+ }
+ public native String types(byte b, short s, int i, long j, float f, double d,
+ boolean x, char c, String str) ;
+
+ public String array_stub(int i[], String str[]){
+ return array(i, str) ;
+ }
+ private native String array(int i[], String str[]) ;
+
+ public String callback_stub(){
+ return callback() ;
+ }
+ public native String callback() ;
+
+ public String get_name(){
+ return "toto" ;
+ }
+} ;
+
+
+class t1212 extends t121 {
+ public t1212() throws InlineJavaException {
+ }
+} ;
diff --git a/Java/Portable.pm b/Java/Portable.pm
new file mode 100644
index 0000000..c3f0a96
--- /dev/null
+++ b/Java/Portable.pm
@@ -0,0 +1,267 @@
+package Inline::Java::Portable ;
+@Inline::Java::Portable::ISA = qw(Exporter) ;
+
+
+use strict ;
+use Exporter ;
+use Carp ;
+use Config ;
+use File::Find ;
+use File::Spec ;
+
+$Inline::Java::Portable::VERSION = '0.53' ;
+
+# Here is some code to figure out if we are running on command.com
+# shell under Windows.
+my $COMMAND_COM =
+ (
+ ($^O eq 'MSWin32')&&
+ (
+ ($ENV{PERL_INLINE_JAVA_COMMAND_COM})||
+ (
+ (defined($ENV{COMSPEC}))&&
+ ($ENV{COMSPEC} =~ /(command|4dos)\.com/i)
+ )||
+ (`ver` =~ /win(dows )?((9[58])|(m[ei]))/i)
+ )
+ ) || 0 ;
+
+
+sub debug {
+ if (Inline::Java->can("debug")){
+ return Inline::Java::debug(@_) ;
+ }
+}
+
+
+# Cleans the CLASSPATH environment variable and adds
+# the paths specified.
+sub make_classpath {
+ my @paths = @_ ;
+
+ my @list = () ;
+ if (defined($ENV{CLASSPATH})){
+ push @list, $ENV{CLASSPATH} ;
+ }
+ push @list, @paths ;
+
+ my $sep = Inline::Java::Portable::portable("ENV_VAR_PATH_SEP_CP") ;
+ my @cp = split(/$sep+/, join($sep, @list)) ;
+
+ # Clean up paths
+ foreach my $p (@cp){
+ $p =~ s/^\s+// ;
+ $p =~ s/\s+$// ;
+ }
+
+ # Remove duplicates, remove invalids but preserve order
+ my @fcp = () ;
+ my %cp = map {$_ => 1} @cp ;
+ foreach my $p (@cp){
+ if (($p)&&(-e $p)){
+ if ($cp{$p}){
+ my $fp = File::Spec->rel2abs($p) ;
+ push @fcp, Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $fp) ;
+ delete $cp{$p} ;
+ }
+ }
+ else{
+ Inline::Java::debug(2, "classpath candidate '$p' scraped") ;
+ }
+ }
+
+ my $cp = join($sep, @fcp) ;
+
+ return (wantarray ? @fcp : $cp) ;
+}
+
+
+sub get_jar_dir {
+ my $path = $INC{"Inline/Java.pm"} ;
+ my ($v, $d, $f) = File::Spec->splitpath($path) ;
+
+ # This undef for the file should be ok.
+ my $dir = File::Spec->catpath($v, $d, 'Java', undef) ;
+
+ return File::Spec->rel2abs($dir) ;
+}
+
+
+sub get_server_jar {
+ return File::Spec->catfile(get_jar_dir(), 'InlineJavaServer.jar') ;
+}
+
+
+sub get_user_jar {
+ return File::Spec->catfile(get_jar_dir(), 'InlineJavaUser.jar') ;
+}
+
+
+sub get_source_dir {
+ return File::Spec->catdir(get_jar_dir(), 'sources') ;
+}
+
+
+# This maybe could be made more stable
+sub find_classes_in_dir {
+ my $dir = shift ;
+
+ my @ret = () ;
+ find(sub {
+ my $f = $_ ;
+ if ($f =~ /\.class$/){
+ my $file = $File::Find::name ;
+ my $fdir = $File::Find::dir ;
+ my @dirs = File::Spec->splitdir($fdir) ;
+ # Remove '.'
+ shift @dirs ;
+ # Add an empty dir to get the last '.' (for '.class')
+ if ((! scalar(@dirs))||($dirs[-1] ne '')){
+ push @dirs, '' ;
+ }
+ my $pkg = (scalar(@dirs) ? join('.', @dirs) : '') ;
+ my $class = "$pkg$f" ;
+ $class =~ s/\.class$// ;
+ push @ret, {file => $file, class => $class} ;
+ }
+ }, $dir) ;
+
+ return @ret ;
+}
+
+
+
+my $map = {
+ _DEFAULT_ => {
+ EXE_EXTENSION => $Config{exe_ext},
+ GOT_ALARM => $Config{d_alarm} || 0,
+ GOT_FORK => $Config{d_fork} || 0,
+ GOT_NEXT_FREE_PORT => 1,
+ GOT_SYMLINK => 1,
+ GOT_SAFE_SIGNALS => 1,
+ ENV_VAR_PATH_SEP => $Config{path_sep},
+ SO_EXT => $Config{dlext},
+ PREFIX => $Config{prefix},
+ LIBPERL => $Config{libperl},
+ DETACH_OK => 1,
+ SO_LIB_PATH_VAR => $Config{ldlibpthname},
+ ENV_VAR_PATH_SEP_CP => ':',
+ IO_REDIR => '2>&1',
+ MAKE => 'make',
+ DEV_NULL => '/dev/null',
+ COMMAND_COM => 0,
+ SUB_FIX_JAVA_PATH => undef,
+ SUB_FIX_CMD_QUOTES => undef,
+ SUB_FIX_MAKE_QUOTES => undef,
+ JVM_LIB => "libjvm.$Config{dlext}",
+ JVM_SO => "libjvm.$Config{dlext}",
+ PRE_WHOLE_ARCHIVE => '-Wl,--whole-archive',
+ POST_WHOLE_ARCHIVE => '-Wl,--no-whole-archive',
+ PERL_PARSE_DUP_ENV => '-DPERL_PARSE_DUP_ENV',
+ BUILD_JNI_BY_DFLT => 1,
+ J2SDK_BIN => 'bin',
+ DEFAULT_J2SDK_DIR => undef,
+ OTHERLDFLAGS => '',
+ dynamic_lib => {}
+ },
+ MSWin32 => {
+ ENV_VAR_PATH_SEP_CP => ';',
+ # 2>&1 doesn't work under command.com
+ IO_REDIR => ($COMMAND_COM ? '' : undef),
+ MAKE => 'nmake',
+ DEV_NULL => 'nul',
+ COMMAND_COM => $COMMAND_COM,
+ SUB_FIX_MAKE_QUOTES => sub {
+ my $arg = shift ;
+ $arg = qq{"$arg"} ;
+ return $arg ;
+ },
+ SO_LIB_PATH_VAR => 'PATH',
+ DETACH_OK => 0,
+ JVM_LIB => 'jvm.lib',
+ JVM_SO => 'jvm.dll',
+ GOT_NEXT_FREE_PORT => 0,
+ GOT_SYMLINK => 0,
+ GOT_SAFE_SIGNALS => 0,
+ PRE_WHOLE_ARCHIVE => '',
+ POST_WHOLE_ARCHIVE => '',
+ },
+ cygwin => {
+ ENV_VAR_PATH_SEP_CP => ';',
+ SUB_FIX_JAVA_PATH => sub {
+ my $arg = shift ;
+ if (defined($arg)&&($arg)){
+ $arg = `cygpath -w \"$arg\"` ;
+ chomp($arg) ;
+ }
+ return $arg ;
+ },
+ JVM_LIB => 'jvm.lib',
+ JVM_SO => 'jvm.dll',
+ BUILD_JNI_BY_DFLT => 1,
+ dynamic_lib => { OTHERLDFLAGS => '-Wl,-add-stdcall-alias' },
+ },
+ hpux => {
+ GOT_NEXT_FREE_PORT => 0,
+ },
+ solaris => {
+ GOT_NEXT_FREE_PORT => 0,
+ PRE_WHOLE_ARCHIVE => '-Wl,-zallextract',
+ POST_WHOLE_ARCHIVE => '-Wl,-zdefaultextract',
+ },
+ aix => {
+ JVM_LIB => "libjvm$Config{lib_ext}",
+ JVM_SO => "libjvm$Config{lib_ext}",
+ },
+ darwin => {
+ # Suggested by Ken Williams, mailing list 2004/07/07
+ SO_EXT => $Config{so},
+ # Andrew Bruno
+ JVM_LIB => "libjvm.dylib",
+ JVM_SO => "libjvm.dylib",
+ PRE_WHOLE_ARCHIVE => '-Wl',
+ POST_WHOLE_ARCHIVE => '-Wl',
+ GOT_SYMLINK => 1,
+ J2SDK_BIN => 'Commands',
+ DEFAULT_J2SDK_DIR => '/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK',
+ # Tim Bunce:
+ OTHERLDFLAGS => '-framework JavaVM',
+ },
+} ;
+
+sub portable {
+ my $key = shift ;
+ my $arg = shift ;
+
+ if (! exists($map->{_DEFAULT_}->{$key})){
+ croak "Portability issue $key not defined!" ;
+ }
+
+ my $val = undef ;
+ if ((defined($map->{$^O}))&&(defined($map->{$^O}->{$key}))){
+ $val = $map->{$^O}->{$key} ;
+ }
+ else {
+ $val = $map->{_DEFAULT_}->{$key} ;
+ }
+
+ if ($key =~ /^SUB_/){
+ my $sub = $val ;
+ if (defined($sub)){
+ $arg = $sub->($arg) ;
+ Inline::Java::Portable::debug(4, "portable: $key => $arg for $^O is '$arg'") ;
+ return $arg ;
+ }
+ else {
+ Inline::Java::Portable::debug(4, "portable: $key => $arg for $^O is default '$arg'") ;
+ return $arg ;
+ }
+ }
+ else {
+ Inline::Java::Portable::debug(4, "portable: $key for $^O is '$val'") ;
+ return $val ;
+ }
+}
+
+
+1 ;
diff --git a/Java/Protocol.pm b/Java/Protocol.pm
new file mode 100644
index 0000000..56e715c
--- /dev/null
+++ b/Java/Protocol.pm
@@ -0,0 +1,612 @@
+package Inline::Java::Protocol ;
+
+use strict ;
+use Inline::Java::Object ;
+use Inline::Java::Array ;
+use Carp ;
+use MIME::Base64 ;
+BEGIN {
+ eval "require Encode" ;
+}
+
+
+$Inline::Java::Protocol::VERSION = '0.53' ;
+
+my %CLASSPATH_ENTRIES = () ;
+
+
+sub new {
+ my $class = shift ;
+ my $obj = shift ;
+ my $inline = shift ;
+
+ my $this = {} ;
+ $this->{obj_priv} = $obj || {} ;
+ $this->{inline} = $inline ;
+
+ bless($this, $class) ;
+ return $this ;
+}
+
+
+sub AddClassPath {
+ my $this = shift ;
+ my @paths = @_ ;
+
+ @paths = map {
+ my $e = $_ ;
+ if ($CLASSPATH_ENTRIES{$e}){
+ () ;
+ }
+ else{
+ Inline::Java::debug(2, "adding to classpath: '$e'") ;
+ $CLASSPATH_ENTRIES{$e} = 1 ;
+ $e ;
+ }
+ } @paths ;
+
+ my $data = "add_classpath " . join(" ", map {encode($_)} @paths) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub ServerType {
+ my $this = shift ;
+
+ Inline::Java::debug(3, "getting server type") ;
+
+ my $data = "server_type" ;
+
+ return $this->Send($data) ;
+}
+
+
+# Known issue: $classes must contain at least one class name.
+sub Report {
+ my $this = shift ;
+ my $classes = shift ;
+
+ Inline::Java::debug(3, "reporting on $classes") ;
+
+ my $data = join(" ",
+ "report",
+ $this->ValidateArgs([$classes]),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub ISA {
+ my $this = shift ;
+ my $proto = shift ;
+
+ my $class = $this->{obj_priv}->{java_class} ;
+
+ return $this->__ISA($proto, $class) ;
+}
+
+
+sub __ISA {
+ my $this = shift ;
+ my $proto = shift ;
+ my $class = shift ;
+
+ Inline::Java::debug(3, "checking if $class is a $proto") ;
+
+ my $data = join(" ",
+ "isa",
+ Inline::Java::Class::ValidateClass($class),
+ Inline::Java::Class::ValidateClass($proto),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub ObjectCount {
+ my $this = shift ;
+
+ Inline::Java::debug(3, "getting object count") ;
+
+ my $data = join(" ",
+ "obj_cnt",
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+# Called to create a Java object
+sub CreateJavaObject {
+ my $this = shift ;
+ my $class = shift ;
+ my $proto = shift ;
+ my $args = shift ;
+
+ Inline::Java::debug(3, "creating object new $class" . $this->CreateSignature($args)) ;
+
+ my $data = join(" ",
+ "create_object",
+ Inline::Java::Class::ValidateClass($class),
+ $this->CreateSignature($proto, ","),
+ $this->ValidateArgs($args),
+ ) ;
+
+ return $this->Send($data, 1) ;
+}
+
+
+# Calls a Java method.
+sub CallJavaMethod {
+ my $this = shift ;
+ my $method = shift ;
+ my $proto = shift ;
+ my $args = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+ Inline::Java::debug(3, "calling object($id).$method" . $this->CreateSignature($args)) ;
+
+ my $data = join(" ",
+ "call_method",
+ $id,
+ Inline::Java::Class::ValidateClass($class),
+ $this->ValidateMethod($method),
+ $this->CreateSignature($proto, ","),
+ $this->ValidateArgs($args),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+# Casts a Java object.
+sub Cast {
+ my $this = shift ;
+ my $class = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ Inline::Java::debug(3, "creating a new reference to object($id) with type $class") ;
+
+ my $data = join(" ",
+ "cast",
+ $id,
+ Inline::Java::Class::ValidateClass($class),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+# Sets a member variable.
+sub SetJavaMember {
+ my $this = shift ;
+ my $member = shift ;
+ my $proto = shift ;
+ my $arg = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+ Inline::Java::debug(3, "setting object($id)->{$member} = " . ($arg->[0] || '')) ;
+ my $data = join(" ",
+ "set_member",
+ $id,
+ Inline::Java::Class::ValidateClass($class),
+ $this->ValidateMember($member),
+ Inline::Java::Class::ValidateClass($proto->[0]),
+ $this->ValidateArgs($arg),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+# Gets a member variable.
+sub GetJavaMember {
+ my $this = shift ;
+ my $member = shift ;
+ my $proto = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+ Inline::Java::debug(3, "getting object($id)->{$member}") ;
+
+ my $data = join(" ",
+ "get_member",
+ $id,
+ Inline::Java::Class::ValidateClass($class),
+ $this->ValidateMember($member),
+ Inline::Java::Class::ValidateClass($proto->[0]),
+ "undef:",
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub ReadFromJavaHandle {
+ my $this = shift ;
+ my $len = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+ Inline::Java::debug(3, "reading from handle object($id)") ;
+
+ my $data = join(" ",
+ "read",
+ $id,
+ $len,
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub MakeJavaHandleBuffered {
+ my $this = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ Inline::Java::debug(3, "making handle object($id) buffered") ;
+
+ my $data = join(" ",
+ "make_buffered",
+ $id,
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub ReadLineFromJavaHandle {
+ my $this = shift ;
+
+ my $id = undef ;
+ if (! defined($this->{obj_priv}->{buffered})){
+ my $nid = $this->MakeJavaHandleBuffered() ;
+ $this->{obj_priv}->{buffered} = Inline::Java::Object->__new('<buffer>', $this->{inline}, $nid) ;
+ }
+ $id = $this->{obj_priv}->{buffered}->__get_private()->{id} ;
+ Inline::Java::debug(3, "reading line from handle object($id)") ;
+
+ my $data = join(" ",
+ "readline",
+ $id,
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub WriteToJavaHandle {
+ my $this = shift ;
+ my $str = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ Inline::Java::debug(3, "writing to handle object($id)") ;
+
+ my $data = join(" ",
+ "write",
+ $id,
+ $this->ValidateArgs([$str]),
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+sub CloseJavaHandle {
+ my $this = shift ;
+
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+ Inline::Java::debug(3, "closing handle object($id)") ;
+
+ my $data = join(" ",
+ "close",
+ $id,
+ ) ;
+
+ return $this->Send($data) ;
+}
+
+
+# Deletes a Java object
+sub DeleteJavaObject {
+ my $this = shift ;
+ my $obj = shift ;
+
+ if (defined($this->{obj_priv}->{id})){
+ my $id = $this->{obj_priv}->{id} ;
+ my $class = $this->{obj_priv}->{java_class} ;
+
+ Inline::Java::debug(3, "deleting object $obj $id ($class)") ;
+
+ my $data = join(" ",
+ "delete_object",
+ $id,
+ ) ;
+
+ $this->Send($data) ;
+ }
+}
+
+
+# This method makes sure that the method we are asking for
+# has the correct form for a Java method.
+sub ValidateMethod {
+ my $this = shift ;
+ my $method = shift ;
+
+ if ($method !~ /^(\w+)$/){
+ croak "Invalid Java method name $method" ;
+ }
+
+ return $method ;
+}
+
+
+# This method makes sure that the member we are asking for
+# has the correct form for a Java member.
+sub ValidateMember {
+ my $this = shift ;
+ my $member = shift ;
+
+ if ($member !~ /^(\w+)$/){
+ croak "Invalid Java member name $member" ;
+ }
+
+ return $member ;
+}
+
+
+# Validates the arguments to be used in a method call.
+sub ValidateArgs {
+ my $this = shift ;
+ my $args = shift ;
+ my $callback = shift ;
+
+ my @ret = () ;
+ foreach my $arg (@{$args}){
+ if (! defined($arg)){
+ push @ret, "undef:" ;
+ }
+ elsif (ref($arg)){
+ if ((UNIVERSAL::isa($arg, "Inline::Java::Object"))||(UNIVERSAL::isa($arg, "Inline::Java::Array"))){
+ my $obj = $arg ;
+ if (UNIVERSAL::isa($arg, "Inline::Java::Array")){
+ $obj = $arg->__get_object() ;
+ }
+ my $class = $obj->__get_private()->{java_class} ;
+ my $id = $obj->__get_private()->{id} ;
+ push @ret, "java_object:$class:$id" ;
+ }
+ elsif (UNIVERSAL::isa($arg, 'Inline::Java::double')){
+ push @ret, "double:" . encode(${$arg}) ;
+ }
+ elsif ($arg =~ /^(.*?)=/){
+ my $id = Inline::Java::Callback::PutObject($arg) ;
+ # Bug. The delimiter is :, so we need to escape the package separator (::)
+ my $pkg = $1 ;
+ $pkg =~ s/:/\//g ;
+ push @ret, "perl_object:$pkg:$id" ;
+ }
+ else {
+ if (! $callback){
+ croak "A Java method or member can only have Java objects, Java arrays, Perl objects or scalars as arguments" ;
+ }
+ else{
+ croak "A Java callback function can only return Java objects, Java arrays, Perl objects or scalars" ;
+ }
+ }
+ }
+ else {
+ push @ret, "scalar:" . encode($arg) ;
+ }
+ }
+
+ return @ret ;
+}
+
+
+sub CreateSignature {
+ my $this = shift ;
+ my $proto = shift ;
+ my $del = shift || ", " ;
+
+ no warnings ;
+ my $sig = join($del, @{$proto}) ;
+ return "($sig)" ;
+}
+
+
+# This actually sends the request to the Java program. It also takes
+# care of registering the returned object (if any)
+sub Send {
+ my $this = shift ;
+ my $data = shift ;
+ my $const = shift ;
+
+ my $resp = Inline::Java::__get_JVM()->process_command($this->{inline}, $data) ;
+ if ($resp =~ /^error scalar:([\w+\/=+]*)$/){
+ my $msg = decode($1) ;
+ Inline::Java::debug(3, "packet recv error: $msg") ;
+ croak $msg ;
+ }
+ elsif ($resp =~ s/^ok //){
+ return $this->DeserializeObject($const, $resp) ;
+ }
+
+ croak "Malformed response from server: $resp" ;
+}
+
+
+sub DeserializeObject {
+ my $this = shift ;
+ my $const = shift ;
+ my $resp = shift ;
+
+ if ($resp =~ /^scalar:([\w+\/=+]*)$/){
+ return decode($1) ;
+ }
+ elsif ($resp =~ /^double:(.*)$/){
+ my $bytes = decode($1) ;
+ my $d = unpack('d', $bytes) ;
+ return $d ;
+ }
+ elsif ($resp =~ /^undef:$/){
+ return undef ;
+ }
+ elsif ($resp =~ /^java_(object|array|handle):([01]):(\d+):(.*)$/){
+ # Create the Perl object wrapper and return it.
+ my $type = $1 ;
+ my $thrown = $2 ;
+ my $id = $3 ;
+ my $class = $4 ;
+
+ if ($thrown){
+ # If we receive a thrown object, we jump out of 'constructor
+ # mode' and process the returned object.
+ $const = 0 ;
+ }
+
+ if ($const){
+ $this->{obj_priv}->{java_class} = $class ;
+ $this->{obj_priv}->{id} = $id ;
+
+ return undef ;
+ }
+ else {
+ my $pkg = $this->{inline}->get_api('pkg') ;
+
+ my $obj = undef ;
+ my $elem_class = $class ;
+
+ Inline::Java::debug(3, "checking if stub is array...") ;
+ if ($type eq 'array'){
+ my @d = Inline::Java::Class::ValidateClassSplit($class) ;
+ $elem_class = $d[2] ;
+ }
+
+ my $perl_class = "Inline::Java::Object" ;
+ if ($elem_class){
+ # We have a real class or an array of real classes
+ $perl_class = Inline::Java::java2perl($pkg, $elem_class) ;
+ if (Inline::Java::Class::ClassIsReference($elem_class)){
+ if (! Inline::Java::known_to_perl($pkg, $elem_class)){
+ if (($thrown)||($this->{inline}->get_java_config('AUTOSTUDY'))){
+ Inline::Java::debug(2, "autostudying $elem_class...") ;
+ $this->{inline}->_study([$elem_class]) ;
+ }
+ else{
+ # Object is not known to Perl, it lives as a
+ # Inline::Java::Object
+ $perl_class = "Inline::Java::Object" ;
+ }
+ }
+ }
+ }
+ else{
+ # We should only get here if an array of primitives types
+ # was returned, and there is nothing to do since
+ # the block below will handle it.
+ }
+
+ if ($type eq 'array'){
+ Inline::Java::debug(3, "creating array object...") ;
+ $obj = Inline::Java::Object->__new($class, $this->{inline}, $id) ;
+ $obj = new Inline::Java::Array($obj) ;
+ Inline::Java::debug(3, "array object created...") ;
+ }
+ # To be finished at a later time...
+ # elsif ($type eq 'handle'){
+ # Inline::Java::debug(3, "creating handle object...") ;
+ # $obj = Inline::Java::Object->__new($class, $this->{inline}, $id) ;
+ # $obj = new Inline::Java::Handle($obj) ;
+ # Inline::Java::debug(3, "handle object created...") ;
+ # }
+ else{
+ $obj = $perl_class->__new($class, $this->{inline}, $id) ;
+ }
+
+ if ($thrown){
+ Inline::Java::debug(3, "throwing stub...") ;
+ my ($msg, $score) = $obj->__isa('org.perl.inline.java.InlineJavaPerlException') ;
+ if ($msg){
+ die $obj ;
+ }
+ else{
+ die $obj->GetObject() ;
+ }
+ }
+ else{
+ Inline::Java::debug(3, "returning stub...") ;
+ return $obj ;
+ }
+ }
+ }
+ elsif ($resp =~ /^perl_object:(\d+):(.*)$/){
+ my $id = $1 ;
+ my $pkg = $2 ;
+
+ return Inline::Java::Callback::GetObject($id) ;
+ }
+ else{
+ croak "Malformed response from server: $resp" ;
+ }
+}
+
+
+sub encode {
+ my $s = shift ;
+
+ # Get UTF-8 byte representation of the data.
+ my $bytes = undef ;
+ if ($INC{'Encode.pm'}){
+ $bytes = Encode::encode_utf8($s) ;
+ }
+ else {
+ $bytes = $s ;
+ $bytes =~ s/([\x80-\xFF])/chr(0xC0|ord($1)>>6).chr(0x80|ord($1)&0x3F)/eg ;
+ }
+
+ # Base-64 encode it.
+ my $base64 = encode_base64($bytes, '') ;
+
+ return $base64 ;
+}
+
+
+sub decode {
+ my $s = shift ;
+
+ # Decode the Base-64 data into bytes (UTF-8 representation of the data).
+ my $bytes = decode_base64($s) ;
+
+ # Take the UTF-8 encoding and convert it back to logical characters.
+ my $string = undef ;
+ if ($INC{'Encode.pm'}){
+ $string = Encode::decode_utf8($bytes) ;
+ }
+ else {
+ $string = $bytes ;
+ $string =~ s/([\xC2\xC3])([\x80-\xBF])/chr(ord($1)<<6&0xC0|ord($2)&0x3F)/eg ;
+ }
+
+ if (utf8->can('downgrade')){
+ utf8::downgrade($string, 1) ;
+ }
+
+ return $string ;
+}
+
+
+sub DESTROY {
+ my $this = shift ;
+
+ Inline::Java::debug(4, "destroying Inline::Java::Protocol") ;
+}
+
+
+1 ;
diff --git a/Java/Server.pm b/Java/Server.pm
new file mode 100644
index 0000000..59ad7a2
--- /dev/null
+++ b/Java/Server.pm
@@ -0,0 +1,130 @@
+package Inline::Java::Server ;
+@Inline::Java::Server::ISA = qw(Exporter) ;
+
+# Export the cast function if wanted
+@EXPORT_OK = qw(start stop status) ;
+
+
+use strict ;
+use Exporter ;
+use Carp ;
+require Inline ;
+require Inline::Java ;
+use File::Spec ;
+
+
+$Inline::Java::Server::VERSION = '0.53' ;
+
+
+# Create a dummy Inline::Java object in order to
+# get the default options.
+my $IJ = bless({}, "Inline::Java") ;
+$IJ->validate(
+ SHARED_JVM => 1
+) ;
+
+
+
+sub import {
+ my $class = shift ;
+ my $a = shift ;
+
+ my @actions = () ;
+ if ($a eq 'restart'){
+ push @actions, 'stop', 'sleep', 'start' ;
+ }
+ else{
+ push @actions, $a ;
+ }
+
+ my $host = $IJ->get_java_config("HOST") ;
+ my $bind = $IJ->get_java_config("BIND") ;
+ my $port = $IJ->get_java_config("PORT") ;
+ foreach $a (@actions){
+ if ($a eq 'sleep'){
+ sleep(5) ;
+ next ;
+ }
+
+ my $status = Inline::Java::Server::status() ;
+
+ if ($a eq 'start'){
+ if ($status){
+ print "SHARED_JVM server on port $bind:$port is already running\n" ;
+ }
+ else{
+ Inline::Java::Server::start() ;
+ my $pid = Inline::Java::__get_JVM()->{pid} ;
+ print "SHARED_JVM server on port $bind:$port started with pid $pid\n" ;
+ }
+ }
+ elsif ($a eq 'stop'){
+ if (! $status){
+ print "SHARED_JVM server on port $host:$port is not running\n" ;
+ }
+ else {
+ Inline::Java::Server::stop() ;
+ print "SHARED_JVM server on port $host:$port stopped\n" ;
+ }
+ }
+ elsif ($a eq 'status'){
+ if ($status){
+ print "SHARED_JVM on port $host:$port is running\n" ;
+ }
+ else {
+ print "SHARED_JVM on port $host:$port is not running\n" ;
+ }
+ }
+ else{
+ croak("Usage: perl -MInline::Java::Server=(start|stop|restart|status)\n") ;
+ }
+ }
+
+ exit() ;
+}
+
+
+
+sub status {
+ my $socket = undef ;
+
+ eval {
+ $socket = Inline::Java::JVM::setup_socket(
+ $IJ->get_java_config("HOST"),
+ $IJ->get_java_config("PORT"),
+ 0,
+ 1
+ ) ;
+ } ;
+ if ($@){
+ return 0 ;
+ }
+ else {
+ close($socket) ;
+ return 1 ;
+ }
+}
+
+
+sub start {
+ my $dir = $ENV{PERL_INLINE_JAVA_DIRECTORY} ;
+
+ Inline->bind(
+ Java => 'STUDY',
+ SHARED_JVM => 1,
+ ($dir ? (DIRECTORY => $dir) : ()),
+ ) ;
+}
+
+
+sub stop {
+ # This will connect us to the running JVM
+ Inline::Java::Server::start() ;
+ Inline::Java::capture_JVM() ;
+ Inline::Java::shutdown_JVM() ;
+}
+
+
+
+1 ;
+
diff --git a/Java/jvm.def b/Java/jvm.def
new file mode 100644
index 0000000..bdab35d
--- /dev/null
+++ b/Java/jvm.def
@@ -0,0 +1,4 @@
+EXPORTS
+JNI_CreateJavaVM@12
+JNI_GetDefaultJavaVMInitArgs@4
+JNI_GetCreatedJavaVMs@12
diff --git a/Java/sources/org/perl/inline/java/InlineJavaArray.java b/Java/sources/org/perl/inline/java/InlineJavaArray.java
new file mode 100644
index 0000000..c397ab4
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaArray.java
@@ -0,0 +1,100 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.lang.reflect.Array ;
+
+
+class InlineJavaArray {
+ private InlineJavaClass ijc ;
+
+
+ InlineJavaArray(InlineJavaClass _ijc){
+ ijc = _ijc ;
+ }
+
+
+ Object CreateArray(Class c, StringTokenizer st) throws InlineJavaException {
+ StringBuffer sb = new StringBuffer(st.nextToken()) ;
+ sb.replace(0, 1, "") ;
+ sb.replace(sb.length() - 1, sb.length(), "") ;
+
+ StringTokenizer st2 = new StringTokenizer(sb.toString(), ",") ;
+ ArrayList al = new ArrayList() ;
+ while (st2.hasMoreTokens()){
+ al.add(al.size(), st2.nextToken()) ;
+ }
+
+ int size = al.size() ;
+ int dims[] = new int[size] ;
+ for (int i = 0 ; i < size ; i++){
+ dims[i] = Integer.parseInt((String)al.get(i)) ;
+ InlineJavaUtils.debug(4, "array dimension: " + (String)al.get(i)) ;
+ }
+
+ Object array = null ;
+ try {
+ array = Array.newInstance(c, dims) ;
+
+ ArrayList args = new ArrayList() ;
+ while (st.hasMoreTokens()){
+ args.add(args.size(), st.nextToken()) ;
+ }
+
+ // Now we need to fill it. Since we have an arbitrary number
+ // of dimensions, we can do this recursively.
+
+ PopulateArray(array, c, dims, args) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments to array constructor for class " + c.getName() + " are incompatible: " + e.getMessage()) ;
+ }
+
+ return array ;
+ }
+
+
+ void PopulateArray (Object array, Class elem, int dims[], ArrayList args) throws InlineJavaException {
+ if (dims.length > 1){
+ int nb_args = args.size() ;
+ int nb_sub_dims = dims[0] ;
+ int nb_args_per_sub_dim = nb_args / nb_sub_dims ;
+
+ int sub_dims[] = new int[dims.length - 1] ;
+ for (int i = 1 ; i < dims.length ; i++){
+ sub_dims[i - 1] = dims[i] ;
+ }
+
+ for (int i = 0 ; i < nb_sub_dims ; i++){
+ // We want the args from i*nb_args_per_sub_dim ->
+ ArrayList sub_args = new ArrayList() ;
+ for (int j = (i * nb_args_per_sub_dim) ; j < ((i + 1) * nb_args_per_sub_dim) ; j++){
+ sub_args.add(sub_args.size(), (String)args.get(j)) ;
+ }
+ PopulateArray(((Object [])array)[i], elem, sub_dims, sub_args) ;
+ }
+ }
+ else{
+ String msg = "In creation of array of " + elem.getName() + ": " ;
+ try {
+ for (int i = 0 ; i < dims[0] ; i++){
+ String arg = (String)args.get(i) ;
+
+ Object o = ijc.CastArgument(elem, arg) ;
+ Array.set(array, i, o) ;
+ if (o != null){
+ InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o.toString()) ;
+ }
+ else{
+ InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o) ;
+ }
+ }
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallback.java b/Java/sources/org/perl/inline/java/InlineJavaCallback.java
new file mode 100644
index 0000000..da23afe
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCallback.java
@@ -0,0 +1,158 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Callback to Perl...
+*/
+class InlineJavaCallback {
+ private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private String pkg = null ;
+ private InlineJavaPerlObject obj = null ;
+ private String method = null ;
+ private Object args[] = null ;
+ private Class cast = null ;
+ private Object response = null ;
+ private boolean response_set = false ;
+
+
+ InlineJavaCallback(String _pkg, String _method, Object _args[], Class _cast) {
+ this(null, _pkg, _method, _args, _cast) ;
+ }
+
+
+ InlineJavaCallback(InlineJavaPerlObject _obj, String _method, Object _args[], Class _cast) {
+ this(_obj, null, _method, _args, _cast) ;
+ if (obj == null){
+ throw new NullPointerException() ;
+ }
+ }
+
+
+ private InlineJavaCallback(InlineJavaPerlObject _obj, String _pkg, String _method, Object _args[], Class _cast) {
+ obj = _obj ;
+ pkg = _pkg ;
+ method = _method ;
+ args = _args ;
+ cast = _cast ;
+
+ if (method == null){
+ throw new NullPointerException() ;
+ }
+ if (cast == null){
+ cast = java.lang.Object.class ;
+ }
+ }
+
+
+ private String GetCommand(InlineJavaProtocol ijp) throws InlineJavaException {
+ String via = null ;
+ if (obj != null){
+ via = "" + obj.GetId() ;
+ }
+ else if (pkg != null){
+ via = pkg ;
+ }
+ StringBuffer cmdb = new StringBuffer("callback " + via + " " + method + " " + cast.getName()) ;
+ if (args != null){
+ for (int i = 0 ; i < args.length ; i++){
+ cmdb.append(" " + ijp.SerializeObject(args[i], null)) ;
+ }
+ }
+ return cmdb.toString() ;
+ }
+
+
+ void ClearResponse(){
+ response = null ;
+ response_set = false ;
+ }
+
+
+ Object GetResponse(){
+ return response ;
+ }
+
+
+ synchronized Object WaitForResponse(Thread t){
+ while (! response_set){
+ try {
+ InlineJavaUtils.debug(3, "waiting for callback response in " + t.getName() + "...") ;
+ wait() ;
+ }
+ catch (InterruptedException ie){
+ // Do nothing, return and wait() some more...
+ }
+ }
+ InlineJavaUtils.debug(3, "got callback response") ;
+ Object resp = response ;
+ response = null ;
+ response_set = false ;
+ return resp ;
+ }
+
+
+ synchronized void NotifyOfResponse(Thread t){
+ InlineJavaUtils.debug(3, "notifying that callback has completed in " + t.getName()) ;
+ notify() ;
+ }
+
+
+ synchronized void Process() throws InlineJavaException, InlineJavaPerlException {
+ Object ret = null ;
+ try {
+ InlineJavaProtocol ijp = new InlineJavaProtocol(ijs, null) ;
+ String cmd = GetCommand(ijp) ;
+ InlineJavaUtils.debug(2, "callback command: " + cmd) ;
+
+ Thread t = Thread.currentThread() ;
+ String resp = null ;
+ while (true) {
+ InlineJavaUtils.debug(3, "packet sent (callback) is " + cmd) ;
+ if (! ijs.IsJNI()){
+ // Client-server mode.
+ InlineJavaServerThread ijt = (InlineJavaServerThread)t ;
+ ijt.GetWriter().write(cmd + "\n") ;
+ ijt.GetWriter().flush() ;
+
+ resp = ijt.GetReader().readLine() ;
+ }
+ else{
+ // JNI mode
+ resp = ijs.jni_callback(cmd) ;
+ }
+ InlineJavaUtils.debug(3, "packet recv (callback) is " + resp) ;
+
+ StringTokenizer st = new StringTokenizer(resp, " ") ;
+ String c = st.nextToken() ;
+ if (c.equals("callback")){
+ boolean thrown = new Boolean(st.nextToken()).booleanValue() ;
+ String arg = st.nextToken() ;
+ InlineJavaClass ijc = new InlineJavaClass(ijs, ijp) ;
+ ret = ijc.CastArgument(cast, arg) ;
+
+ if (thrown){
+ throw new InlineJavaPerlException(ret) ;
+ }
+
+ break ;
+ }
+ else{
+ // Pass it on through the regular channel...
+ InlineJavaUtils.debug(3, "packet is not callback response: " + resp) ;
+ cmd = ijs.ProcessCommand(resp, false) ;
+
+ continue ;
+ }
+ }
+ }
+ catch (IOException e){
+ throw new InlineJavaException("IO error: " + e.getMessage()) ;
+ }
+
+ response = ret ;
+ response_set = true ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java
new file mode 100644
index 0000000..a1d7ab1
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java
@@ -0,0 +1,141 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Queue for callbacks to Perl...
+*/
+class InlineJavaCallbackQueue {
+ // private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private ArrayList queue = new ArrayList() ;
+ private boolean wait_interrupted = false ;
+ private boolean stream_opened = false ;
+
+
+ InlineJavaCallbackQueue(){
+ }
+
+
+ synchronized void EnqueueCallback(InlineJavaCallback ijc){
+ queue.add(ijc) ;
+ notify() ;
+ }
+
+
+ synchronized private InlineJavaCallback DequeueCallback(){
+ if (GetSize() > 0){
+ return (InlineJavaCallback)queue.remove(0) ;
+ }
+ return null ;
+ }
+
+
+ synchronized int WaitForCallback(double timeout){
+ long secs = (long)Math.floor(timeout) ;
+ double rest = timeout - ((double)secs) ;
+ long millis = (long)Math.floor(rest * 1000.0) ;
+ rest = (rest * 1000.0) - ((double)millis) ;
+ int nanos = (int)Math.floor(rest * 1000000.0) ;
+
+ return WaitForCallback((secs * 1000) + millis, nanos) ;
+ }
+
+
+ /*
+ Blocks up to the specified time for the next callback to arrive.
+ Returns -1 if the wait was interrupted voluntarily, 0 on timeout or
+ > 0 if a callback has arrived before the timeout expired.
+ */
+ synchronized int WaitForCallback(long millis, int nanos){
+ wait_interrupted = false ;
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "waiting for callback request (" + millis + " millis, " +
+ nanos + " nanos) in " + t.getName() + "...") ;
+
+ if (! stream_opened){
+ return -1 ;
+ }
+
+ while ((stream_opened)&&(! wait_interrupted)&&(IsEmpty())){
+ try {
+ wait(millis, nanos) ;
+ // If we reach this code, it means the either we timed out
+ // or that we were notify()ed.
+ // In the former case, we must break out and return 0.
+ // In the latter case, either the queue will not be empty or
+ // wait_interrupted will be set. We must therefore also break out.
+ break ;
+ }
+ catch (InterruptedException ie){
+ // Do nothing, return and wait() some more...
+ }
+ }
+ InlineJavaUtils.debug(3, "waiting for callback request finished " + t.getName() + "...") ;
+
+ if (wait_interrupted){
+ return -1 ;
+ }
+ else {
+ return GetSize() ;
+ }
+ }
+
+
+ /*
+ Waits indefinetely for the next callback to arrive and executes it.
+ Return true on success of false if the wait was interrupted voluntarily.
+ */
+ synchronized boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException {
+ int rc = WaitForCallback(0, 0) ;
+ if (rc == -1){
+ // Wait was interrupted
+ return false ;
+ }
+
+ // DequeueCallback can't return null because we explicetely
+ // waited until a callback was there.
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "processing callback request in " + t.getName() + "...") ;
+ InlineJavaCallback ijc = DequeueCallback() ;
+ ijc.Process() ;
+ ijc.NotifyOfResponse(t) ;
+
+ return true ;
+ }
+
+
+ private boolean IsEmpty(){
+ return (GetSize() == 0) ;
+ }
+
+
+ void OpenCallbackStream(){
+ stream_opened = true ;
+ }
+
+
+ synchronized void CloseCallbackStream(){
+ stream_opened = false ;
+ InterruptWaitForCallback() ;
+ }
+
+
+ boolean IsStreamOpen(){
+ return stream_opened ;
+ }
+
+
+ int GetSize(){
+ return queue.size() ;
+ }
+
+
+ synchronized private void InterruptWaitForCallback(){
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "interrupting wait for callback request in " + t.getName() + "...") ;
+ wait_interrupted = true ;
+ notify() ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCastException.java b/Java/sources/org/perl/inline/java/InlineJavaCastException.java
new file mode 100644
index 0000000..b799689
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCastException.java
@@ -0,0 +1,7 @@
+package org.perl.inline.java ;
+
+class InlineJavaCastException extends InlineJavaException {
+ InlineJavaCastException(String m){
+ super(m) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaClass.java b/Java/sources/org/perl/inline/java/InlineJavaClass.java
new file mode 100644
index 0000000..bd57769
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaClass.java
@@ -0,0 +1,554 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.lang.reflect.* ;
+
+
+class InlineJavaClass {
+ private InlineJavaServer ijs ;
+ private InlineJavaProtocol ijp ;
+
+ static private HashMap class2jni_code = new HashMap() ;
+ static {
+ class2jni_code.put(byte.class, "B") ;
+ class2jni_code.put(short.class, "S") ;
+ class2jni_code.put(int.class, "I") ;
+ class2jni_code.put(long.class, "J") ;
+ class2jni_code.put(float.class, "F") ;
+ class2jni_code.put(double.class, "D") ;
+ class2jni_code.put(boolean.class, "Z") ;
+ class2jni_code.put(char.class, "C") ;
+ class2jni_code.put(void.class, "V") ;
+ } ;
+
+ static private HashMap class2wrapper = new HashMap() ;
+ static {
+ class2wrapper.put(byte.class, java.lang.Byte.class) ;
+ class2wrapper.put(short.class, java.lang.Short.class) ;
+ class2wrapper.put(int.class, java.lang.Integer.class) ;
+ class2wrapper.put(long.class, java.lang.Long.class) ;
+ class2wrapper.put(float.class, java.lang.Float.class) ;
+ class2wrapper.put(double.class, java.lang.Double.class) ;
+ class2wrapper.put(boolean.class, java.lang.Boolean.class) ;
+ class2wrapper.put(char.class, java.lang.Character.class) ;
+ class2wrapper.put(void.class, java.lang.Void.class) ;
+ } ;
+
+ static private HashMap name2class = new HashMap() ;
+ static {
+ name2class.put("byte", byte.class) ;
+ name2class.put("short", short.class) ;
+ name2class.put("int", int.class) ;
+ name2class.put("long", long.class) ;
+ name2class.put("float", float.class) ;
+ name2class.put("double", double.class) ;
+ name2class.put("boolean", boolean.class) ;
+ name2class.put("char", char.class) ;
+ name2class.put("void", void.class) ;
+ name2class.put("B", byte.class) ;
+ name2class.put("S", short.class) ;
+ name2class.put("I", int.class) ;
+ name2class.put("J", long.class) ;
+ name2class.put("F", float.class) ;
+ name2class.put("D", double.class) ;
+ name2class.put("Z", boolean.class) ;
+ name2class.put("C", char.class) ;
+ name2class.put("V", void.class) ;
+ } ;
+
+
+ InlineJavaClass(InlineJavaServer _ijs, InlineJavaProtocol _ijp){
+ ijs = _ijs ;
+ ijp = _ijp ;
+ }
+
+
+ /*
+ Makes sure a class exists
+ */
+ static Class ValidateClass(String name) throws InlineJavaException {
+ Class pc = FindType(name) ;
+ if (pc != null){
+ return pc ;
+ }
+
+ try {
+ Class c = Class.forName(name, true, InlineJavaServer.GetInstance().GetUserClassLoader()) ;
+ return c ;
+ }
+ catch (ClassNotFoundException e){
+ throw new InlineJavaException("Class " + name + " not found") ;
+ }
+ }
+
+
+ /*
+ Remove L...; from a class name if it has been extracted from an Array class name.
+ */
+ static String CleanClassName(String name){
+ if (name != null){
+ int l = name.length() ;
+ if ((l > 2)&&(name.charAt(0) == 'L')&&(name.charAt(l - 1) == ';')){
+ name = name.substring(1, l - 1) ;
+ }
+ }
+ return name ;
+ }
+
+
+ static private Class ValidateClassQuiet(String name){
+ try {
+ return ValidateClass(name) ;
+ }
+ catch (InlineJavaException ije){
+ return null ;
+ }
+ }
+
+ /*
+ This is the monster method that determines how to cast arguments
+ */
+ Object [] CastArguments(Class [] params, ArrayList args) throws InlineJavaException {
+ Object ret[] = new Object [params.length] ;
+
+ for (int i = 0 ; i < params.length ; i++){
+ // Here the args are all strings or objects (or undef)
+ // we need to match them to the prototype.
+ Class p = params[i] ;
+ InlineJavaUtils.debug(4, "arg " + String.valueOf(i) + " of signature is " + p.getName()) ;
+
+ ret[i] = CastArgument(p, (String)args.get(i)) ;
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ This is the monster method that determines how to cast arguments
+ */
+ Object CastArgument(Class p, String argument) throws InlineJavaException {
+ Object ret = null ;
+
+ ArrayList tokens = new ArrayList() ;
+ StringTokenizer st = new StringTokenizer(argument, ":") ;
+ for (int j = 0 ; st.hasMoreTokens() ; j++){
+ tokens.add(j, st.nextToken()) ;
+ }
+ if (tokens.size() == 1){
+ tokens.add(1, "") ;
+ }
+ String type = (String)tokens.get(0) ;
+
+ // We need to separate the primitive types from the
+ // reference types.
+ boolean num = ClassIsNumeric(p) ;
+ if ((num)||(ClassIsString(p))){
+ Class ap = p ;
+ if (ap == java.lang.Number.class){
+ InlineJavaUtils.debug(4, "specializing java.lang.Number to java.lang.Double") ;
+ ap = java.lang.Double.class ;
+ }
+ else if (ap.getName().equals("java.lang.CharSequence")){
+ InlineJavaUtils.debug(4, "specializing java.lang.CharSequence to java.lang.String") ;
+ ap = java.lang.String.class ;
+ }
+
+ if (type.equals("undef")){
+ if (num){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " 0") ;
+ ret = ijp.CreateObject(ap, new Object [] {"0"}, new Class [] {String.class}) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ ret = null ;
+ InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " " + ret) ;
+ InlineJavaUtils.debug(4, " result is " + ret) ;
+ }
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to " + ap.getName()) ;
+ try {
+ ret = ijp.CreateObject(ap, new Object [] {arg}, new Class [] {String.class}) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ catch (NumberFormatException e){
+ throw new InlineJavaCastException("Can't convert " + arg + " to " + ap.getName()) ;
+ }
+ }
+ else if (type.equals("double")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ // We have native double bytes in arg.
+ long l = 0 ;
+ char c[] = arg.toCharArray() ;
+ for (int i = 0 ; i < 8 ; i++){
+ l += (((long)c[i]) << (8 * i)) ;
+ }
+ double d = Double.longBitsToDouble(l) ;
+ ret = new Double(d) ;
+ }
+ else {
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else if (ClassIsBool(p)){
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to bool false") ;
+ ret = new Boolean("false") ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to bool") ;
+ if ((arg.equals(""))||(arg.equals("0"))){
+ arg = "false" ;
+ }
+ else{
+ arg = "true" ;
+ }
+ ret = new Boolean(arg) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else if (ClassIsChar(p)){
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to char '\0'") ;
+ ret = new Character('\0') ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar -> forcing to char") ;
+ char c = '\0' ;
+ if (arg.length() == 1){
+ c = arg.toCharArray()[0] ;
+ }
+ else if (arg.length() > 1){
+ throw new InlineJavaCastException("Can't convert " + arg + " to " + p.getName()) ;
+ }
+ ret = new Character(c) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else {
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+ // We know that what we expect here is a real object
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to null") ;
+ ret = null ;
+ }
+ else if (type.equals("scalar")){
+ // Here if we need a java.lang.Object.class, it's probably
+ // because we can store anything, so we use a String object.
+ if (p == java.lang.Object.class){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ ret = arg ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert primitive type to " + p.getName()) ;
+ }
+ }
+ else if (type.equals("java_object")){
+ // We need an object and we got an object...
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+
+ String c_name = (String)tokens.get(1) ;
+ String objid = (String)tokens.get(2) ;
+
+ Class c = ValidateClass(c_name) ;
+
+ if (DoesExtend(c, p) > -1){
+ InlineJavaUtils.debug(4, " " + c.getName() + " is a kind of " + p.getName()) ;
+ // get the object from the hash table
+ int id = Integer.parseInt(objid) ;
+ Object o = ijs.GetObject(id) ;
+ ret = o ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't cast a " + c.getName() + " to a " + p.getName()) ;
+ }
+ }
+ else{
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+
+ String pkg = (String)tokens.get(1) ;
+ pkg = pkg.replace('/', ':') ;
+ String objid = (String)tokens.get(2) ;
+
+
+ if (DoesExtend(p, org.perl.inline.java.InlineJavaPerlObject.class) > -1){
+ InlineJavaUtils.debug(4, " Perl object is a kind of " + p.getName()) ;
+ int id = Integer.parseInt(objid) ;
+ ret = new InlineJavaPerlObject(pkg, id) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't cast a Perl object to a " + p.getName()) ;
+ }
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Returns the number of levels that separate a from b
+ */
+ static int DoesExtend(Class a, Class b){
+ return DoesExtend(a, b, 0) ;
+ }
+
+
+ static int DoesExtend(Class a, Class b, int level){
+ InlineJavaUtils.debug(4, "checking if " + a.getName() + " extends " + b.getName()) ;
+
+ if (a == b){
+ return level ;
+ }
+
+ Class parent = a.getSuperclass() ;
+ if (parent != null){
+ InlineJavaUtils.debug(4, " parent is " + parent.getName()) ;
+ int ret = DoesExtend(parent, b, level + 1) ;
+ if (ret != -1){
+ return ret ;
+ }
+ }
+
+ // Maybe b is an interface a implements it?
+ Class inter[] = a.getInterfaces() ;
+ for (int i = 0 ; i < inter.length ; i++){
+ InlineJavaUtils.debug(4, " interface is " + inter[i].getName()) ;
+ int ret = DoesExtend(inter[i], b, level + 1) ;
+ if (ret != -1){
+ return ret ;
+ }
+ }
+
+ return -1 ;
+ }
+
+
+ /*
+ Finds the wrapper class for the passed primitive type.
+ */
+ static Class FindWrapper(Class p){
+ Class w = (Class)class2wrapper.get(p) ;
+ if (w == null){
+ w = p ;
+ }
+
+ return w ;
+ }
+
+
+ /*
+ Finds the primitive type class for the passed primitive type name.
+ */
+ static Class FindType (String name){
+ return (Class)name2class.get(name) ;
+ }
+
+
+ static String FindJNICode(Class p){
+ if (! Object.class.isAssignableFrom(p)){
+ return (String)class2jni_code.get(p) ;
+ }
+ else {
+ String name = p.getName().replace('.', '/') ;
+ if (p.isArray()){
+ return name ;
+ }
+ else{
+ return "L" + name + ";" ;
+ }
+ }
+ }
+
+
+ static boolean ClassIsPrimitive(Class p){
+ String name = p.getName() ;
+
+ if ((ClassIsNumeric(p))||(ClassIsString(p))||(ClassIsChar(p))||(ClassIsBool(p))){
+ InlineJavaUtils.debug(4, "class " + name + " is primitive") ;
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ /*
+ Determines if class is of numerical type.
+ */
+ static private HashMap numeric_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Byte.class,
+ java.lang.Short.class,
+ java.lang.Integer.class,
+ java.lang.Long.class,
+ java.lang.Float.class,
+ java.lang.Double.class,
+ java.lang.Number.class,
+ byte.class,
+ short.class,
+ int.class,
+ long.class,
+ float.class,
+ double.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ numeric_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsNumeric (Class p){
+ return (numeric_classes.get(p) != null) ;
+ }
+
+
+ static private HashMap double_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Double.class,
+ double.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ double_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsDouble (Class p){
+ return (double_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is String or StringBuffer
+ */
+ static private HashMap string_classes = new HashMap() ;
+ static {
+ Class csq = ValidateClassQuiet("java.lang.CharSequence") ;
+ Class [] list = {
+ java.lang.String.class,
+ java.lang.StringBuffer.class,
+ csq
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ string_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsString (Class p){
+ return (string_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is Char
+ */
+ static private HashMap char_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Character.class,
+ char.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ char_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsChar (Class p){
+ return (char_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is Bool
+ */
+ static private HashMap bool_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Boolean.class,
+ boolean.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ bool_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsBool (Class p){
+ return (bool_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Determines if a class is not of a primitive type or of a
+ wrapper class.
+ */
+ static boolean ClassIsReference (Class p){
+ String name = p.getName() ;
+
+ if (ClassIsPrimitive(p)){
+ return false ;
+ }
+
+ InlineJavaUtils.debug(4, "class " + name + " is reference") ;
+
+ return true ;
+ }
+
+
+ static boolean ClassIsArray (Class p){
+ String name = p.getName() ;
+
+ if ((ClassIsReference(p))&&(name.startsWith("["))){
+ InlineJavaUtils.debug(4, "class " + name + " is array") ;
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsPublic (Class p){
+ int pub = p.getModifiers() & Modifier.PUBLIC ;
+ if (pub != 0){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsHandle (Class p){
+ if ((ClassIsReadHandle(p))||(ClassIsWriteHandle(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsReadHandle (Class p){
+ if ((java.io.Reader.class.isAssignableFrom(p))||
+ (java.io.InputStream.class.isAssignableFrom(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsWriteHandle (Class p){
+ if ((java.io.Writer.class.isAssignableFrom(p))||
+ (java.io.OutputStream.class.isAssignableFrom(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaException.java b/Java/sources/org/perl/inline/java/InlineJavaException.java
new file mode 100644
index 0000000..7e2f3cd
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaException.java
@@ -0,0 +1,7 @@
+package org.perl.inline.java ;
+
+public class InlineJavaException extends Exception {
+ public InlineJavaException(String s) {
+ super(s) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaHandle.java b/Java/sources/org/perl/inline/java/InlineJavaHandle.java
new file mode 100644
index 0000000..681cf18
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaHandle.java
@@ -0,0 +1,130 @@
+package org.perl.inline.java ;
+
+
+import java.util.* ;
+import java.io.* ;
+
+
+public class InlineJavaHandle {
+ private static final String charset = "ISO-8859-1" ;
+
+
+ static String read(Object o, int len) throws InlineJavaException, IOException {
+ String ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.Reader){
+ char buf[] = new char[len] ;
+ int rc = ((java.io.Reader)o).read(buf) ;
+ if (rc != -1){
+ ret = new String(buf) ;
+ }
+ }
+ else {
+ byte buf[] = new byte[len] ;
+ int rc = ((java.io.InputStream)o).read(buf) ;
+ if (rc != -1){
+ ret = new String(buf, charset) ;
+ }
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't read from non-readhandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static String readLine(Object o) throws InlineJavaException, IOException {
+ String ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.BufferedReader){
+ ret = ((java.io.BufferedReader)o).readLine() ;
+ }
+ else {
+ throw new InlineJavaException("Can't read line from non-buffered Reader or InputStream") ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't read line from non-readhandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static Object makeBuffered(Object o) throws InlineJavaException, IOException {
+ Object ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.BufferedReader){
+ ret = (java.io.BufferedReader)o ;
+ }
+ else if (o instanceof java.io.Reader){
+ ret = new BufferedReader((java.io.Reader)o) ;
+ }
+ else {
+ ret = new BufferedReader(new InputStreamReader((java.io.InputStream)o, charset)) ;
+ }
+ }
+ else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.BufferedWriter){
+ ret = (java.io.BufferedWriter)o ;
+ }
+ else if (o instanceof java.io.Writer){
+ ret = new BufferedWriter((java.io.Writer)o) ;
+ }
+ else {
+ ret = new BufferedWriter(new OutputStreamWriter((java.io.OutputStream)o, charset)) ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't make non-handle object buffered (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static int write(Object o, String str) throws InlineJavaException, IOException {
+ int ret = -1 ;
+ if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.Writer){
+ ((java.io.Writer)o).write(str) ;
+ ret = str.length() ;
+ }
+ else {
+ byte b[] = str.getBytes(charset) ;
+ ((java.io.OutputStream)o).write(b) ;
+ ret = b.length ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't write to non-writehandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static void close(Object o) throws InlineJavaException, IOException {
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.Reader){
+ ((java.io.Reader)o).close() ;
+ }
+ else {
+ ((java.io.InputStream)o).close() ;
+ }
+ }
+ else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.Writer){
+ ((java.io.Writer)o).close() ;
+ }
+ else {
+ ((java.io.OutputStream)o).close() ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't close non-handle object (" + o.getClass().getName() + ")") ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java
new file mode 100644
index 0000000..768bb1a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java
@@ -0,0 +1,15 @@
+package org.perl.inline.java ;
+
+class InlineJavaInvocationTargetException extends InlineJavaException {
+ private Throwable t ;
+
+
+ InlineJavaInvocationTargetException(String m, Throwable _t){
+ super(m) ;
+ t = _t ;
+ }
+
+ Throwable GetThrowable(){
+ return t ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java
new file mode 100644
index 0000000..c389358
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java
@@ -0,0 +1,257 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Callback to Perl...
+*/
+public class InlineJavaPerlCaller {
+ private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private Thread creator = null ;
+ static private Map thread_callback_queues = Collections.synchronizedMap(new HashMap()) ;
+ static private ResourceBundle resources = null ;
+ static private boolean inited = false ;
+
+
+ /*
+ Only thread that communicate with Perl are allowed to create PerlCallers because
+ this is where we get the thread that needs to be notified when the callbacks come in.
+ */
+ public InlineJavaPerlCaller() throws InlineJavaException {
+ init() ;
+ Thread t = Thread.currentThread() ;
+ if (ijs.IsThreadPerlContact(t)){
+ creator = t ;
+ }
+ else{
+ throw new InlineJavaException("InlineJavaPerlCaller objects can only be created by threads that communicate directly with Perl") ;
+ }
+ }
+
+
+ synchronized static protected void init() throws InlineJavaException {
+ if (! inited){
+ try {
+ resources = ResourceBundle.getBundle("InlineJava") ;
+
+ inited = true ;
+ }
+ catch (MissingResourceException mre){
+ throw new InlineJavaException("Error loading InlineJava.properties: " + mre.getMessage()) ;
+ }
+ }
+ }
+
+
+ static protected ResourceBundle GetBundle(){
+ return resources ;
+ }
+
+ /* Old interface */
+ /**
+ * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[])}
+ */
+ public Object CallPerl(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerl(pkg, method, args, null) ;
+ }
+
+
+ /* Old interface */
+ /**
+ * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[],Class)}
+ */
+ public Object CallPerl(String pkg, String method, Object args[], String cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ (String)null, pkg + "::" + method, args,
+ (cast == null ? null : InlineJavaClass.ValidateClass(cast))) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlSub(String sub, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlSub(sub, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlSub(String sub, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ (String)null, sub, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlMethod(obj, method, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ obj, method, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlStaticMethod(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlStaticMethod(pkg, method, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlStaticMethod(String pkg, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ pkg, method, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ public Object eval(String code) throws InlineJavaPerlException, InlineJavaException {
+ return eval(code, null) ;
+ }
+
+
+ public Object eval(String code, Class cast) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_eval", new Object [] {code}, cast) ;
+ }
+
+
+ public Object require(String module_or_file) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module_or_file}) ;
+ }
+
+
+ public Object require_file(String file) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {file, new Boolean("true")}) ;
+ }
+
+
+ public Object require_module(String module) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module, new Boolean("false")}) ;
+ }
+
+
+ private Object CallPerl(InlineJavaCallback ijc) throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (t == creator){
+ ijc.Process() ;
+ return ijc.GetResponse() ;
+ }
+ else {
+ // Enqueue the callback into the creator thread's queue and notify it
+ // that there is some work for him.
+ ijc.ClearResponse() ;
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ InlineJavaUtils.debug(3, "enqueing callback for processing for " + creator.getName() + " in " + t.getName() + "...") ;
+ q.EnqueueCallback(ijc) ;
+ InlineJavaUtils.debug(3, "notifying that a callback request is available for " + creator.getName() + " in " + t.getName()) ;
+
+ // Now we must wait until the callback is processed and get back the result...
+ return ijc.WaitForResponse(t) ;
+ }
+ }
+
+
+ public void OpenCallbackStream() throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.OpenCallbackStream() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ q.OpenCallbackStream() ;
+ }
+
+
+ /*
+ Blocks until either a callback arrives, timeout seconds has passed or the call is
+ interrupted by Interrupt?
+ */
+ public int WaitForCallback(double timeout) throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.WaitForCallback() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ if (timeout == 0.0){
+ // no wait
+ return q.GetSize() ;
+ }
+ else if (timeout == -1.0){
+ timeout = 0.0 ;
+ }
+
+ return q.WaitForCallback(timeout) ;
+ }
+
+
+ public boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.ProcessNextCallback() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ return q.ProcessNextCallback() ;
+ }
+
+
+ public void CloseCallbackStream() throws InlineJavaException {
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ q.CloseCallbackStream() ;
+ }
+
+
+ public void StartCallbackLoop() throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.StartCallbackLoop() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ InlineJavaUtils.debug(3, "starting callback loop for " + creator.getName() + " in " + t.getName()) ;
+ q.OpenCallbackStream() ;
+ while (q.IsStreamOpen()){
+ q.ProcessNextCallback() ;
+ }
+ }
+
+
+ public void StopCallbackLoop() throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ InlineJavaUtils.debug(3, "stopping callback loop for " + creator.getName() + " in " + t.getName()) ;
+ q.CloseCallbackStream() ;
+ }
+
+
+ /*
+ Here the prototype accepts Threads because the JNI thread
+ calls this method also.
+ */
+ static synchronized void AddThread(Thread t){
+ thread_callback_queues.put(t, new InlineJavaCallbackQueue()) ;
+ }
+
+
+ static synchronized void RemoveThread(InlineJavaServerThread t){
+ thread_callback_queues.remove(t) ;
+ }
+
+
+ static private InlineJavaCallbackQueue GetQueue(Thread t) throws InlineJavaException {
+ InlineJavaCallbackQueue q = (InlineJavaCallbackQueue)thread_callback_queues.get(t) ;
+
+ if (q == null){
+ throw new InlineJavaException("Can't find thread " + t.getName() + "!") ;
+ }
+ return q ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlException.java b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java
new file mode 100644
index 0000000..38fd631
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java
@@ -0,0 +1,25 @@
+package org.perl.inline.java ;
+
+
+public class InlineJavaPerlException extends Exception {
+ private Object obj ;
+
+
+ public InlineJavaPerlException(Object o){
+ super(o.toString()) ;
+ obj = o ;
+ }
+
+ public InlineJavaPerlException(String s){
+ super(s) ;
+ obj = s ;
+ }
+
+ public Object GetObject(){
+ return obj ;
+ }
+
+ public String GetString(){
+ return (String)obj ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java
new file mode 100644
index 0000000..6a24ce2
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java
@@ -0,0 +1,107 @@
+package org.perl.inline.java ;
+
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ InlineJavaPerlInterpreter
+
+ This singleton class creates a PerlInterpreter object. To this object is bound
+ an instance of InlineJavaServer that will allow communication with Perl.
+
+ All communication with Perl must be done via InlineJavaPerlCaller in order to insure
+ thread synchronization. Therefore all Perl actions will be implemented via functions
+ in Inline::Java::PerlInterperter so that they can be called via InlineJavaPerlCaller
+*/
+public class InlineJavaPerlInterpreter extends InlineJavaPerlCaller {
+ static private boolean inited = false ;
+ static InlineJavaPerlInterpreter instance = null ;
+ static boolean test = false ;
+ static String libperl_so = "" ;
+
+
+ protected InlineJavaPerlInterpreter() throws InlineJavaPerlException, InlineJavaException {
+ init() ;
+
+ InlineJavaUtils.debug(2, "constructing perl interpreter") ;
+ construct() ;
+ InlineJavaUtils.debug(2, "perl interpreter constructed") ;
+
+ if (! libperl_so.equals("")){
+ evalNoReturn("require DynaLoader ;") ;
+ evalNoReturn("DynaLoader::dl_load_file(\"" + libperl_so + "\", 0x01) ;") ;
+ }
+ if (test){
+ evalNoReturn("use blib ;") ;
+ }
+ evalNoReturn("use Inline::Java::PerlInterpreter ;") ;
+ }
+
+
+ synchronized static public InlineJavaPerlInterpreter create() throws InlineJavaPerlException, InlineJavaException {
+ if (instance == null){
+ // Here we create a temporary InlineJavaServer instance in order to be able to instanciate
+ // ourselves. When we create InlineJavaPerlInterpreter, the instance will be overriden.
+ InlineJavaUtils.debug(2, "creating temporary JNI InlineJavaServer") ;
+ InlineJavaServer.jni_main(InlineJavaUtils.get_debug(), false) ;
+ InlineJavaUtils.debug(2, "temporary JNI InlineJavaServer created") ;
+ InlineJavaUtils.debug(2, "creating InlineJavaPerlInterpreter") ;
+ instance = new InlineJavaPerlInterpreter() ;
+ InlineJavaUtils.debug(2, "InlineJavaPerlInterpreter created") ;
+ }
+ return instance ;
+ }
+
+
+ synchronized static protected void init() throws InlineJavaException {
+ init("install") ;
+ }
+
+
+ synchronized static protected void init(String mode) throws InlineJavaException {
+ InlineJavaPerlCaller.init() ;
+ if (! inited){
+ test = (mode.equals("test") ? true : false) ;
+ try {
+ String perlinterpreter_so = GetBundle().getString("inline_java_perlinterpreter_so_" + mode) ;
+ File f = new File(perlinterpreter_so) ;
+ if (! f.exists()){
+ throw new InlineJavaException("Can't initialize PerlInterpreter " +
+ "functionnality: PerlInterpreter extension (" + perlinterpreter_so +
+ ") can't be found") ;
+ }
+
+ // Load the PerlInterpreter shared object
+ InlineJavaUtils.debug(2, "loading shared library " + perlinterpreter_so) ;
+ System.load(perlinterpreter_so) ;
+ InlineJavaUtils.debug(2, "shared library " + perlinterpreter_so + " loaded") ;
+
+ libperl_so = GetBundle().getString("inline_java_libperl_so") ;
+
+ inited = true ;
+ }
+ catch (MissingResourceException mre){
+ throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ;
+ }
+ }
+ }
+
+
+ synchronized static private native void construct() ;
+
+
+ synchronized static private native void evalNoReturn(String code) throws InlineJavaPerlException ;
+
+
+ synchronized static private native void destruct() ;
+
+
+ synchronized static public void destroy() {
+ if (instance != null){
+ destruct() ;
+ instance = null ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java
new file mode 100644
index 0000000..5682e6a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java
@@ -0,0 +1,231 @@
+package org.perl.inline.java ;
+
+import java.lang.reflect.* ;
+import java.util.* ;
+import java.io.* ;
+
+
+public class InlineJavaPerlNatives extends InlineJavaPerlCaller {
+ static private boolean inited = false ;
+ static private Map registered_classes = Collections.synchronizedMap(new HashMap()) ;
+ static private Map registered_methods = Collections.synchronizedMap(new HashMap()) ;
+
+
+ protected InlineJavaPerlNatives() throws InlineJavaException {
+ init() ;
+ RegisterPerlNatives(this.getClass()) ;
+ }
+
+
+ static protected void init() throws InlineJavaException {
+ init("install") ;
+ }
+
+
+ synchronized static protected void init(String mode) throws InlineJavaException {
+ InlineJavaPerlCaller.init() ;
+ if (! inited){
+ try {
+ String perlnatives_so = GetBundle().getString("inline_java_perlnatives_so_" + mode) ;
+ File f = new File(perlnatives_so) ;
+ if (! f.exists()){
+ throw new InlineJavaException("Can't initialize PerlNatives " +
+ "functionnality: PerlNatives extension (" + perlnatives_so +
+ ") can't be found") ;
+ }
+
+ try {
+ Class ste_class = Class.forName("java.lang.StackTraceElement") ;
+ }
+ catch (ClassNotFoundException cnfe){
+ throw new InlineJavaException("Can't initialize PerlNatives " +
+ "functionnality: Java 1.4 or higher required (current is " +
+ System.getProperty("java.version") + ").") ;
+ }
+
+ // Load the Natives shared object
+ InlineJavaUtils.debug(2, "loading shared library " + perlnatives_so) ;
+ System.load(perlnatives_so) ;
+
+ inited = true ;
+ }
+ catch (MissingResourceException mre){
+ throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ;
+ }
+ }
+ }
+
+
+ // This method actually does the real work of registering the methods.
+ synchronized private void RegisterPerlNatives(Class c) throws InlineJavaException {
+ if (registered_classes.get(c) == null){
+ InlineJavaUtils.debug(3, "registering natives for class " + c.getName()) ;
+
+ Constructor constructors[] = c.getDeclaredConstructors() ;
+ Method methods[] = c.getDeclaredMethods() ;
+
+ registered_classes.put(c, c) ;
+ for (int i = 0 ; i < constructors.length ; i++){
+ Constructor x = constructors[i] ;
+ if (Modifier.isNative(x.getModifiers())){
+ RegisterMethod(c, "new", x.getParameterTypes(), c) ;
+ }
+ }
+
+ for (int i = 0 ; i < methods.length ; i++){
+ Method x = methods[i] ;
+ if (Modifier.isNative(x.getModifiers())){
+ RegisterMethod(c, x.getName(), x.getParameterTypes(), x.getReturnType()) ;
+ }
+ }
+ }
+ }
+
+
+ private void RegisterMethod(Class c, String mname, Class params[], Class rt) throws InlineJavaException {
+ String cname = c.getName() ;
+ InlineJavaUtils.debug(3, "registering native method " + mname + " for class " + cname) ;
+
+ // Check return type
+ if ((! Object.class.isAssignableFrom(rt))&&(rt != void.class)){
+ throw new InlineJavaException("Perl native method " + mname + " of class " + cname + " can only have Object or void return types (not " + rt.getName() + ")") ;
+ }
+
+ // fmt starts with the return type, which for now is Object only (or void).
+ StringBuffer fmt = new StringBuffer("L") ;
+ StringBuffer sign = new StringBuffer("(") ;
+ for (int i = 0 ; i < params.length ; i++){
+ String code = InlineJavaClass.FindJNICode(params[i]) ;
+ sign.append(code) ;
+ char ch = code.charAt(0) ;
+ char f = ch ;
+ if (f == '['){
+ // Arrays are Objects...
+ f = 'L' ;
+ }
+ fmt.append(new String(new char [] {f})) ;
+ }
+ sign.append(")") ;
+
+ sign.append(InlineJavaClass.FindJNICode(rt)) ;
+ InlineJavaUtils.debug(3, "signature is " + sign) ;
+ InlineJavaUtils.debug(3, "format is " + fmt) ;
+
+ // For now, no method overloading so no signature necessary
+ String meth = cname + "." + mname ;
+ String prev = (String)registered_methods.get(meth) ;
+ if (prev != null){
+ throw new InlineJavaException("There already is a native method '" + mname + "' registered for class '" + cname + "'") ;
+ }
+ registered_methods.put(meth, fmt.toString()) ;
+
+ // call the native method to hook it up
+ RegisterMethod(c, mname, sign.toString()) ;
+ }
+
+
+ // This native method will call RegisterNative to hook up the magic
+ // method implementation for the method.
+ native private void RegisterMethod(Class c, String name, String signature) throws InlineJavaException ;
+
+
+ // This method will be called from the native side. We need to figure
+ // out who this method is and then look in up in the
+ // registered method list and return the format.
+ private String LookupMethod() throws InlineJavaException {
+ InlineJavaUtils.debug(3, "entering LookupMethod") ;
+
+ String caller[] = GetNativeCaller() ;
+ String meth = caller[0] + "." + caller[1] ;
+
+ String fmt = (String)registered_methods.get(meth) ;
+ if (fmt == null){
+ throw new InlineJavaException("Native method " + meth + " is not registered") ;
+ }
+
+ InlineJavaUtils.debug(3, "exiting LookupMethod") ;
+
+ return fmt ;
+ }
+
+
+ private Object InvokePerlMethod(Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaUtils.debug(3, "entering InvokePerlMethod") ;
+
+ String caller[] = GetNativeCaller() ;
+ String pkg = caller[0] ;
+ String method = caller[1] ;
+
+ // Transform the Java class name into the Perl package name
+ StringTokenizer st = new StringTokenizer(pkg, ".") ;
+ StringBuffer perl_sub = new StringBuffer() ;
+ // Starting with "::" means that the package is relative to the caller package
+ while (st.hasMoreTokens()){
+ perl_sub.append("::" + st.nextToken()) ;
+ }
+ perl_sub.append("::" + method) ;
+
+ for (int i = 0 ; i < args.length ; i++){
+ InlineJavaUtils.debug(3, "InvokePerlMethod argument " + i + " = " + args[i]) ;
+ }
+
+ Object ret = CallPerlSub(perl_sub.toString(), args) ;
+
+ InlineJavaUtils.debug(3, "exiting InvokePerlMethod") ;
+
+ return ret ;
+ }
+
+
+ // This method must absolutely be called by a method DIRECTLY called
+ // by generic_perl_native
+ private String[] GetNativeCaller() throws InlineJavaException {
+ InlineJavaUtils.debug(3, "entering GetNativeCaller") ;
+
+ Class ste_class = null ;
+ try {
+ ste_class = Class.forName("java.lang.StackTraceElement") ;
+ }
+ catch (ClassNotFoundException cnfe){
+ throw new InlineJavaException("Can't load class java.lang.StackTraceElement") ;
+ }
+
+ Throwable exec_point = new Throwable() ;
+ try {
+ Method m = exec_point.getClass().getMethod("getStackTrace", new Class [] {}) ;
+ Object stack = m.invoke(exec_point, new Object [] {}) ;
+ if (Array.getLength(stack) <= 2){
+ throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (call stack too short)") ;
+ }
+
+ Object ste = Array.get(stack, 2) ;
+ m = ste.getClass().getMethod("isNativeMethod", new Class [] {}) ;
+ Boolean is_nm = (Boolean)m.invoke(ste, new Object [] {}) ;
+ if (! is_nm.booleanValue()){
+ throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (caller is not native)") ;
+ }
+
+ m = ste.getClass().getMethod("getClassName", new Class [] {}) ;
+ String cname = (String)m.invoke(ste, new Object [] {}) ;
+ m = ste.getClass().getMethod("getMethodName", new Class [] {}) ;
+ String mname = (String)m.invoke(ste, new Object [] {}) ;
+
+ InlineJavaUtils.debug(3, "exiting GetNativeCaller") ;
+
+ return new String [] {cname, mname} ;
+ }
+ catch (NoSuchMethodException nsme){
+ throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " +
+ nsme.getMessage()) ;
+ }
+ catch (IllegalAccessException iae){
+ throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " +
+ iae.getMessage()) ;
+ }
+ catch (InvocationTargetException ite){
+ // None of the methods invoked throw exceptions, so...
+ throw new InlineJavaException("Exception caught while manipulating java.lang.StackTraceElement classes: " +
+ ite.getTargetException()) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java
new file mode 100644
index 0000000..b57caab
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java
@@ -0,0 +1,73 @@
+package org.perl.inline.java ;
+
+
+/*
+ InlineJavaPerlObject
+*/
+public class InlineJavaPerlObject extends InlineJavaPerlCaller {
+ private int id = 0 ;
+ private String pkg = null ;
+
+
+ /*
+ Creates a Perl Object by calling
+ pkg->new(args) ;
+ */
+ public InlineJavaPerlObject(String _pkg, Object args[]) throws InlineJavaPerlException, InlineJavaException {
+ pkg = _pkg ;
+ InlineJavaPerlObject stub = (InlineJavaPerlObject)CallPerlStaticMethod(pkg, "new", args, getClass()) ;
+ id = stub.GetId() ;
+ stub.id = 0 ;
+ }
+
+
+ /*
+ This is just a stub for already existing objects
+ */
+ InlineJavaPerlObject(String _pkg, int _id) throws InlineJavaException {
+ pkg = _pkg ;
+ id = _id ;
+ }
+
+
+ int GetId(){
+ return id ;
+ }
+
+
+ public String GetPkg(){
+ return pkg ;
+ }
+
+
+ public Object InvokeMethod(String name, Object args[]) throws InlineJavaPerlException, InlineJavaException {
+ return InvokeMethod(name, args, null) ;
+ }
+
+
+ public Object InvokeMethod(String name, Object args[], Class cast) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlMethod(this, name, args, cast) ;
+ }
+
+
+ public void Dispose() throws InlineJavaPerlException, InlineJavaException {
+ Dispose(false) ;
+ }
+
+
+ protected void Dispose(boolean gc) throws InlineJavaPerlException, InlineJavaException {
+ if (id != 0){
+ CallPerlSub("Inline::Java::Callback::java_finalize", new Object [] {new Integer(id), new Boolean(gc)}) ;
+ }
+ }
+
+
+ protected void finalize() throws Throwable {
+ try {
+ Dispose(true) ;
+ }
+ finally {
+ super.finalize() ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaProtocol.java b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java
new file mode 100644
index 0000000..e84c214
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java
@@ -0,0 +1,879 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+import java.lang.reflect.* ;
+
+
+/*
+ This is where most of the work of Inline Java is done. Here determine
+ the request type and then we proceed to serve it.
+*/
+class InlineJavaProtocol {
+ private InlineJavaServer ijs ;
+ private InlineJavaClass ijc ;
+ private InlineJavaArray ija ;
+ private String cmd ;
+ private String response = null ;
+
+ private final String encoding = "UTF-8" ;
+
+ static private Map member_cache = Collections.synchronizedMap(new HashMap()) ;
+ static private final String report_version = "V2" ;
+
+ InlineJavaProtocol(InlineJavaServer _ijs, String _cmd) {
+ ijs = _ijs ;
+ ijc = new InlineJavaClass(ijs, this) ;
+ ija = new InlineJavaArray(ijc) ;
+
+ cmd = _cmd ;
+ }
+
+
+ /*
+ Starts the analysis of the command line
+ */
+ void Do() throws InlineJavaException {
+ StringTokenizer st = new StringTokenizer(cmd, " ") ;
+ String c = st.nextToken() ;
+
+ if (c.equals("call_method")){
+ CallJavaMethod(st) ;
+ }
+ else if (c.equals("set_member")){
+ SetJavaMember(st) ;
+ }
+ else if (c.equals("get_member")){
+ GetJavaMember(st) ;
+ }
+ else if (c.equals("add_classpath")){
+ AddClassPath(st) ;
+ }
+ else if (c.equals("server_type")){
+ ServerType(st) ;
+ }
+ else if (c.equals("report")){
+ Report(st) ;
+ }
+ else if (c.equals("isa")){
+ IsA(st) ;
+ }
+ else if (c.equals("create_object")){
+ CreateJavaObject(st) ;
+ }
+ else if (c.equals("delete_object")){
+ DeleteJavaObject(st) ;
+ }
+ else if (c.equals("obj_cnt")){
+ ObjectCount(st) ;
+ }
+ else if (c.equals("cast")){
+ Cast(st) ;
+ }
+ else if (c.equals("read")){
+ Read(st) ;
+ }
+ else if (c.equals("make_buffered")){
+ MakeBuffered(st) ;
+ }
+ else if (c.equals("readline")){
+ ReadLine(st) ;
+ }
+ else if (c.equals("write")){
+ Write(st) ;
+ }
+ else if (c.equals("close")){
+ Close(st) ;
+ }
+ else if (c.equals("die")){
+ InlineJavaUtils.debug(1, "received a request to die...") ;
+ ijs.Shutdown() ;
+ }
+ else {
+ throw new InlineJavaException("Unknown command " + c) ;
+ }
+ }
+
+ /*
+ Returns a report on the Java classes, listing all public methods
+ and members
+ */
+ void Report(StringTokenizer st) throws InlineJavaException {
+ StringBuffer pw = new StringBuffer(report_version + "\n") ;
+
+ StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":") ;
+ st2.nextToken() ;
+
+ StringTokenizer st3 = new StringTokenizer(Decode(st2.nextToken()), " ") ;
+
+ ArrayList class_list = new ArrayList() ;
+ while (st3.hasMoreTokens()){
+ String c = st3.nextToken() ;
+ class_list.add(class_list.size(), c) ;
+ }
+
+ for (int i = 0 ; i < class_list.size() ; i++){
+ String name = (String)class_list.get(i) ;
+ Class c = ijc.ValidateClass(name) ;
+
+ InlineJavaUtils.debug(3, "reporting for " + c) ;
+
+ Class parent = c.getSuperclass() ;
+ String pname = (parent == null ? "null" : parent.getName()) ;
+ pw.append("class " + c.getName() + " " + pname + "\n") ;
+ Constructor constructors[] = c.getConstructors() ;
+ Method methods[] = c.getMethods() ;
+ Field fields[] = c.getFields() ;
+
+ boolean pub = ijc.ClassIsPublic(c) ;
+ if (pub){
+ // If the class is public and has no constructors,
+ // we provide a default no-arg constructors.
+ if (c.getDeclaredConstructors().length == 0){
+ String noarg_sign = InlineJavaUtils.CreateSignature(new Class [] {}) ;
+ pw.append("constructor " + noarg_sign + "\n") ;
+ }
+ }
+
+ boolean pn = InlineJavaPerlNatives.class.isAssignableFrom(c) ;
+ for (int j = 0 ; j < constructors.length ; j++){
+ Constructor x = constructors[j] ;
+ if ((pn)&&(Modifier.isNative(x.getModifiers()))){
+ continue ;
+ }
+ Class params[] = x.getParameterTypes() ;
+ String sign = InlineJavaUtils.CreateSignature(params) ;
+ Class decl = x.getDeclaringClass() ;
+ pw.append("constructor " + sign + "\n") ;
+ }
+
+ for (int j = 0 ; j < methods.length ; j++){
+ Method x = methods[j] ;
+ if ((pn)&&(Modifier.isNative(x.getModifiers()))){
+ continue ;
+ }
+ String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ;
+ String sign = InlineJavaUtils.CreateSignature(x.getParameterTypes()) ;
+ Class decl = x.getDeclaringClass() ;
+ pw.append("method" + stat + decl.getName() + " " + x.getName() + sign + "\n") ;
+ }
+
+ for (int j = 0 ; j < fields.length ; j++){
+ Field x = fields[(InlineJavaUtils.ReverseMembers() ? (fields.length - 1 - j) : j)] ;
+ String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ;
+ Class decl = x.getDeclaringClass() ;
+ Class type = x.getType() ;
+ pw.append("field" + stat + decl.getName() + " " + x.getName() + " " + type.getName() + "\n") ;
+ }
+ }
+
+ SetResponse(pw.toString()) ;
+ }
+
+
+ void AddClassPath(StringTokenizer st) throws InlineJavaException {
+ while (st.hasMoreTokens()){
+ String path = Decode(st.nextToken()) ;
+ InlineJavaServer.GetInstance().GetUserClassLoader().AddClassPath(path) ;
+ }
+ SetResponse(null) ;
+ }
+
+
+ void ServerType(StringTokenizer st) throws InlineJavaException {
+ SetResponse(ijs.GetType()) ;
+ }
+
+
+ void IsA(StringTokenizer st) throws InlineJavaException {
+ String class_name = st.nextToken() ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ String is_it_a = st.nextToken() ;
+ Class d = ijc.ValidateClass(is_it_a) ;
+
+ SetResponse(new Integer(ijc.DoesExtend(c, d))) ;
+ }
+
+
+ void ObjectCount(StringTokenizer st) throws InlineJavaException {
+ SetResponse(new Integer(ijs.ObjectCount())) ;
+ }
+
+
+ /*
+ Creates a Java Object with the specified arguments.
+ */
+ void CreateJavaObject(StringTokenizer st) throws InlineJavaException {
+ String class_name = st.nextToken() ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ if (! ijc.ClassIsArray(c)){
+ ArrayList f = ValidateMethod(true, c, class_name, st) ;
+ Object p[] = (Object [])f.get(1) ;
+ Class clist[] = (Class [])f.get(2) ;
+
+ try {
+ Object o = CreateObject(c, p, clist) ;
+ SetResponse(o) ;
+ }
+ catch (InlineJavaInvocationTargetException ite){
+ Throwable t = ite.GetThrowable() ;
+ if (t instanceof InlineJavaException){
+ InlineJavaException ije = (InlineJavaException)t ;
+ throw ije ;
+ }
+ else{
+ SetResponse(new InlineJavaThrown(t)) ;
+ }
+ }
+ }
+ else{
+ // Here we send the type of array we want, but CreateArray
+ // exception the element type.
+ StringBuffer sb = new StringBuffer(class_name) ;
+ // Remove the ['s
+ while (sb.toString().startsWith("[")){
+ sb.replace(0, 1, "") ;
+ }
+ // remove the L and the ;
+ if (sb.toString().startsWith("L")){
+ sb.replace(0, 1, "") ;
+ sb.replace(sb.length() - 1, sb.length(), "") ;
+ }
+
+ Class ec = ijc.ValidateClass(sb.toString()) ;
+
+ InlineJavaUtils.debug(4, "array elements: " + ec.getName()) ;
+ Object o = ija.CreateArray(ec, st) ;
+ SetResponse(o) ;
+ }
+ }
+
+
+ /*
+ Calls a Java method
+ */
+ void CallJavaMethod(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String method = st.nextToken() ;
+
+ if ((ijc.ClassIsArray(c))&&(method.equals("getLength"))){
+ int length = Array.getLength(o) ;
+ SetResponse(new Integer(length)) ;
+ }
+ else{
+ ArrayList f = ValidateMethod(false, c, method, st) ;
+ Method m = (Method)f.get(0) ;
+ String name = m.getName() ;
+ Object p[] = (Object [])f.get(1) ;
+
+ try {
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().invoke(m, o, p) ;
+ SetResponse(ret, AutoCast(ret, m.getReturnType())) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to invoke method " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments for method " + name + " in class " + class_name + " are incompatible: " + e.getMessage()) ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ String type = t.getClass().getName() ;
+ String msg = t.getMessage() ;
+ InlineJavaUtils.debug(1, "method " + name + " in class " + class_name + " threw exception " + type + ": " + msg) ;
+ if (t instanceof InlineJavaException){
+ InlineJavaException ije = (InlineJavaException)t ;
+ throw ije ;
+ }
+ else{
+ SetResponse(new InlineJavaThrown(t)) ;
+ }
+ }
+ }
+ }
+
+
+ /*
+ */
+ Class AutoCast(Object o, Class want){
+ if (o == null){
+ return null ;
+ }
+ else {
+ Class got = o.getClass() ;
+ if (got.equals(want)){
+ return null ;
+ }
+ else {
+ boolean _public = (got.getModifiers() & Modifier.PUBLIC) != 0 ;
+ if ((_public)||(got.getPackage() == null)){
+ return null ;
+ }
+ else {
+ InlineJavaUtils.debug(3, "AutoCast: " + got.getName() + " -> " + want.getName()) ;
+ return want ;
+ }
+ }
+ }
+ }
+
+
+ /*
+ Returns a new reference to the current object, using the provided subtype
+ */
+ void Cast(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = ijs.GetObject(id) ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ SetResponse(o, c) ;
+ }
+
+
+ /*
+ */
+ void Read(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+ int len = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.read(o, len) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void MakeBuffered(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.makeBuffered(o) ;
+ if (ret != o){
+ int buf_id = ijs.PutObject(ret) ;
+ ret = new Integer(buf_id) ;
+ }
+ else {
+ ret = new Integer(id) ;
+ }
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void ReadLine(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.readLine(o) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void Write(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+ Object arg = ijc.CastArgument(Object.class, st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ int len = InlineJavaHandle.write(o, arg.toString()) ;
+ ret = new Integer(len) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void Close(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ InlineJavaHandle.close(o) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ /*
+ Sets a Java member variable
+ */
+ void SetJavaMember(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String member = st.nextToken() ;
+
+ if (ijc.ClassIsArray(c)){
+ int idx = Integer.parseInt(member) ;
+ Class type = ijc.ValidateClass(st.nextToken()) ;
+ String arg = st.nextToken() ;
+
+ String msg = "For array of type " + c.getName() + ", element " + member + ": " ;
+ try {
+ Object elem = ijc.CastArgument(type, arg) ;
+ InlineJavaServer.GetInstance().GetUserClassLoader().array_set(o, idx, elem) ;
+ SetResponse(null) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+ else{
+ ArrayList fl = ValidateMember(c, member, st) ;
+ Field f = (Field)fl.get(0) ;
+ String name = f.getName() ;
+ Object p = (Object)fl.get(1) ;
+
+ try {
+ InlineJavaServer.GetInstance().GetUserClassLoader().set(f, o, p) ;
+ SetResponse(null) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ;
+ }
+ }
+ }
+
+
+ /*
+ Gets a Java member variable
+ */
+ void GetJavaMember(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String member = st.nextToken() ;
+
+ if (ijc.ClassIsArray(c)){
+ int idx = Integer.parseInt(member) ;
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().array_get(o, idx) ;
+ Class eclass = ijc.ValidateClass(ijc.CleanClassName(class_name.substring(1))) ;
+ SetResponse(ret, AutoCast(ret, eclass)) ;
+ }
+ else{
+ ArrayList fl = ValidateMember(c, member, st) ;
+
+ Field f = (Field)fl.get(0) ;
+ String name = f.getName() ;
+ try {
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().get(f, o) ;
+ SetResponse(ret, AutoCast(ret, f.getType())) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ;
+ }
+ }
+ }
+
+
+ /*
+ Deletes a Java object
+ */
+ void DeleteJavaObject(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.DeleteObject(id) ;
+
+ SetResponse(null) ;
+ }
+
+
+ /*
+ Creates a Java Object with the specified arguments.
+ */
+ Object CreateObject(Class p, Object args[], Class proto[]) throws InlineJavaException {
+ p = ijc.FindWrapper(p) ;
+
+ String name = p.getName() ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaServer.GetInstance().GetUserClassLoader().create(p, args, proto) ;
+ }
+ catch (NoSuchMethodException e){
+ throw new InlineJavaException("Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " not found: " + e.getMessage()) ;
+ }
+ catch (InstantiationException e){
+ throw new InlineJavaException("You are not allowed to instantiate object of class " + name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to instantiate object of class " + name + " using the constructor with signature " + InlineJavaUtils.CreateSignature(proto) + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments to constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " are incompatible: " + e.getMessage()) ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ String type = t.getClass().getName() ;
+ String msg = t.getMessage() ;
+ throw new InlineJavaInvocationTargetException(
+ "Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " threw exception " + type + ": " + msg,
+ t) ;
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Makes sure a method exists
+ */
+ ArrayList ValidateMethod(boolean constructor, Class c, String name, StringTokenizer st) throws InlineJavaException {
+ ArrayList ret = new ArrayList() ;
+
+ // Extract signature
+ String signature = st.nextToken() ;
+
+ // Extract the arguments
+ ArrayList args = new ArrayList() ;
+ while (st.hasMoreTokens()){
+ args.add(args.size(), st.nextToken()) ;
+ }
+
+ String key = c.getName() + "." + name + signature ;
+ ArrayList ml = new ArrayList() ;
+ Class params[] = null ;
+
+ Member cached = (Member)member_cache.get(key) ;
+ if (cached != null){
+ InlineJavaUtils.debug(3, "method was cached") ;
+ ml.add(ml.size(), cached) ;
+ }
+ else{
+ Member ma[] = (constructor ? (Member [])c.getConstructors() : (Member [])c.getMethods()) ;
+ for (int i = 0 ; i < ma.length ; i++){
+ Member m = ma[i] ;
+
+ if (m.getName().equals(name)){
+ InlineJavaUtils.debug(3, "found a " + name + (constructor ? " constructor" : " method")) ;
+
+ if (constructor){
+ params = ((Constructor)m).getParameterTypes() ;
+ }
+ else{
+ params = ((Method)m).getParameterTypes() ;
+ }
+
+ // Now we check if the signatures match
+ String sign = InlineJavaUtils.CreateSignature(params, ",") ;
+ InlineJavaUtils.debug(3, sign + " = " + signature + "?") ;
+
+ if (signature.equals(sign)){
+ InlineJavaUtils.debug(3, "has matching signature " + sign) ;
+ ml.add(ml.size(), m) ;
+ member_cache.put(key, m) ;
+ break ;
+ }
+ }
+ }
+ }
+
+ // Now we got a list of matching methods (actually 0 or 1).
+ // We have to figure out which one we will call.
+ if (ml.size() == 0){
+ // Nothing matched. Maybe we got a default constructor
+ if ((constructor)&&(signature.equals("()"))){
+ ret.add(0, null) ;
+ ret.add(1, new Object [] {}) ;
+ ret.add(2, new Class [] {}) ;
+ }
+ else{
+ throw new InlineJavaException(
+ (constructor ? "Constructor " : "Method ") +
+ name + " for class " + c.getName() + " with signature " +
+ signature + " not found") ;
+ }
+ }
+ else if (ml.size() == 1){
+ // Now we need to force the arguments received to match
+ // the methods signature.
+ Member m = (Member)ml.get(0) ;
+ if (constructor){
+ params = ((Constructor)m).getParameterTypes() ;
+ }
+ else{
+ params = ((Method)m).getParameterTypes() ;
+ }
+
+ String msg = "In method " + name + " of class " + c.getName() + ": " ;
+ try {
+ ret.add(0, m) ;
+ ret.add(1, ijc.CastArguments(params, args)) ;
+ ret.add(2, params) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Makes sure a member exists
+ */
+ ArrayList ValidateMember(Class c, String name, StringTokenizer st) throws InlineJavaException {
+ ArrayList ret = new ArrayList() ;
+
+ // Extract member type
+ String type = st.nextToken() ;
+
+ // Extract the argument
+ String arg = st.nextToken() ;
+
+ String key = type + " " + c.getName() + "." + name ;
+ ArrayList fl = new ArrayList() ;
+ Class param = null ;
+
+ Member cached = (Member)member_cache.get(key) ;
+ if (cached != null){
+ InlineJavaUtils.debug(3, "member was cached") ;
+ fl.add(fl.size(), cached) ;
+ }
+ else {
+ Field fa[] = c.getFields() ;
+ for (int i = 0 ; i < fa.length ; i++){
+ Field f = fa[(InlineJavaUtils.ReverseMembers() ? (fa.length - 1 - i) : i)] ;
+
+ if (f.getName().equals(name)){
+ InlineJavaUtils.debug(3, "found a " + name + " member") ;
+
+ param = f.getType() ;
+ String t = param.getName() ;
+ if (type.equals(t)){
+ InlineJavaUtils.debug(3, "has matching type " + t) ;
+ fl.add(fl.size(), f) ;
+ }
+ }
+ }
+ }
+
+ // Now we got a list of matching members.
+ // We have to figure out which one we will call.
+ if (fl.size() == 0){
+ throw new InlineJavaException(
+ "Member " + name + " of type " + type + " for class " + c.getName() +
+ " not found") ;
+ }
+ else {
+ // Now we need to force the arguments received to match
+ // the methods signature.
+
+ // If we have more that one, we use the last one, which is the most
+ // specialized
+ Field f = (Field)fl.get(fl.size() - 1) ;
+ member_cache.put(key, f) ;
+ param = f.getType() ;
+
+ String msg = "For member " + name + " of class " + c.getName() + ": " ;
+ try {
+ ret.add(0, f) ;
+ ret.add(1, ijc.CastArgument(param, arg)) ;
+ ret.add(2, param) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ This sets the response that will be returned to the Perl
+ script
+ */
+ void SetResponse(Object o) throws InlineJavaException {
+ SetResponse(o, null) ;
+ }
+
+
+ void SetResponse(Object o, Class p) throws InlineJavaException {
+ response = "ok " + SerializeObject(o, p) ;
+ }
+
+
+ String SerializeObject(Object o, Class p) throws InlineJavaException {
+ Class c = (o == null ? null : o.getClass()) ;
+
+ if ((c != null)&&(p != null)){
+ if (ijc.DoesExtend(c, p) < 0){
+ throw new InlineJavaException("Can't cast a " + c.getName() + " to a " + p.getName()) ;
+ }
+ else{
+ c = p ;
+ }
+ }
+
+ if (o == null){
+ return "undef:" ;
+ }
+ else if ((ijc.ClassIsNumeric(c))||(ijc.ClassIsChar(c))||(ijc.ClassIsString(c))){
+ if ((ijs.GetNativeDoubles())&&(ijc.ClassIsDouble(c))){
+ Double d = (Double)o ;
+ long l = Double.doubleToLongBits(d.doubleValue()) ;
+ char ca[] = new char[8] ;
+ for (int i = 0 ; i < 8 ; i++){
+ ca[i] = (char)((l >> (8 * i)) & 0xFF) ;
+ }
+ return "double:" + Encode(new String(ca)) ;
+ }
+ else {
+ return "scalar:" + Encode(o.toString()) ;
+ }
+ }
+ else if (ijc.ClassIsBool(c)){
+ String b = o.toString() ;
+ return "scalar:" + Encode((b.equals("true") ? "1" : "0")) ;
+ }
+ else {
+ if (! (o instanceof org.perl.inline.java.InlineJavaPerlObject)){
+ // Here we need to register the object in order to send
+ // it back to the Perl script.
+ boolean thrown = false ;
+ String type = "object" ;
+ if (o instanceof InlineJavaThrown){
+ thrown = true ;
+ o = ((InlineJavaThrown)o).GetThrowable() ;
+ c = o.getClass() ;
+ }
+ else if (ijc.ClassIsArray(c)){
+ type = "array" ;
+ }
+ else if (ijc.ClassIsHandle(c)){
+ type = "handle" ;
+ }
+ int id = ijs.PutObject(o) ;
+
+ return "java_" + type + ":" + (thrown ? "1" : "0") + ":" + String.valueOf(id) +
+ ":" + c.getName() ;
+ }
+ else {
+ return "perl_object:" + ((InlineJavaPerlObject)o).GetId() +
+ ":" + ((InlineJavaPerlObject)o).GetPkg() ;
+ }
+ }
+ }
+
+
+ byte[] DecodeToByteArray(String s){
+ return InlineJavaUtils.DecodeBase64(s.toCharArray()) ;
+ }
+
+
+ String Decode(String s) throws InlineJavaException {
+ try {
+ if (encoding != null){
+ return new String(DecodeToByteArray(s), encoding) ;
+ }
+ else {
+ return new String(DecodeToByteArray(s)) ;
+ }
+ }
+ catch (UnsupportedEncodingException e){
+ throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ;
+ }
+ }
+
+
+ String EncodeFromByteArray(byte bytes[]){
+ return new String(InlineJavaUtils.EncodeBase64(bytes)) ;
+ }
+
+
+ String Encode(String s) throws InlineJavaException {
+ try {
+ if (encoding != null){
+ return EncodeFromByteArray(s.getBytes(encoding)) ;
+ }
+ else {
+ return EncodeFromByteArray(s.getBytes()) ;
+ }
+ }
+ catch (UnsupportedEncodingException e){
+ throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ;
+ }
+ }
+
+
+ String GetResponse(){
+ return response ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaServer.java b/Java/sources/org/perl/inline/java/InlineJavaServer.java
new file mode 100644
index 0000000..1c62ef9
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaServer.java
@@ -0,0 +1,337 @@
+package org.perl.inline.java ;
+
+import java.net.* ;
+import java.io.* ;
+import java.util.* ;
+
+
+/*
+ This is the server that will answer all the requests for and on Java
+ objects.
+*/
+public class InlineJavaServer {
+ private static InlineJavaServer instance = null ;
+ private String host = null ;
+ private int port = 0 ;
+ private boolean shared_jvm = false ;
+ private boolean priv = false ;
+ private boolean native_doubles = false ;
+
+ private boolean finished = false ;
+ private ServerSocket server_socket = null ;
+ private InlineJavaUserClassLoader ijucl = null ;
+ private HashMap thread_objects = new HashMap() ;
+ private int objid = 1 ;
+ private boolean jni = false ;
+ private Thread creator = null ;
+ private int thread_count = 0 ;
+
+
+ // This constructor is used in JNI mode
+ private InlineJavaServer(int debug, boolean _native_doubles){
+ init(debug, _native_doubles) ;
+
+ jni = true ;
+ AddThread(creator) ;
+ }
+
+
+ // This constructor is used in server mode
+ // Normally one would then call RunMainLoop()
+ /* Note: Consider http://groups.google.com/group/perl.inline/tree/browse_frm/thread/aa7f5ce236f6d576/3db48a308a8175fb?rnum=1&hl=en&q=Congratulations+with+Inline%3A%3AJava+0.51&_done=%2Fgroup%2Fperl.inline%2Fbrowse_frm%2Fthread%2Faa7f5ce236f6d576%2Fd2de9cf38429c09c%3Flnk%3Dst%26q%3DCongratulations+with+Inline%3A%3AJava+0.51%26rnum%3D1%26hl%3Den%26#doc_3db48a308a8175fb before changing this prototype */
+ public InlineJavaServer(int debug, String _host, int _port, boolean _shared_jvm, boolean _priv, boolean _native_doubles){
+ init(debug, _native_doubles) ;
+
+ jni = false ;
+ host = _host ;
+ port = _port ;
+ shared_jvm = _shared_jvm ;
+ priv = _priv ;
+
+ try {
+ if ((host == null)||(host.equals(""))||(host.equals("ANY"))){
+ server_socket = new ServerSocket(port) ;
+ }
+ else {
+ server_socket = new ServerSocket(port, 0, InetAddress.getByName(host)) ;
+ }
+ }
+ catch (IOException e){
+ InlineJavaUtils.Fatal("Can't open server socket on port " + String.valueOf(port) +
+ ": " + e.getMessage()) ;
+ }
+ }
+
+
+ public void RunMainLoop(){
+ while (! finished){
+ try {
+ String name = "IJST-#" + thread_count++ ;
+ InlineJavaServerThread ijt = new InlineJavaServerThread(name, this, server_socket.accept(),
+ (priv ? new InlineJavaUserClassLoader() : ijucl)) ;
+ ijt.start() ;
+ if (! shared_jvm){
+ try {
+ ijt.join() ;
+ }
+ catch (InterruptedException e){
+ }
+ break ;
+ }
+ }
+ catch (IOException e){
+ if (! finished){
+ System.err.println("Main Loop IO Error: " + e.getMessage()) ;
+ System.err.flush() ;
+ }
+ }
+ }
+ }
+
+
+ private synchronized void init(int debug, boolean _native_doubles){
+ instance = this ;
+ creator = Thread.currentThread() ;
+ InlineJavaUtils.set_debug(debug) ;
+ native_doubles = _native_doubles ;
+
+ ijucl = new InlineJavaUserClassLoader() ;
+ }
+
+
+ static InlineJavaServer GetInstance(){
+ if (instance == null){
+ InlineJavaUtils.Fatal("No instance of InlineJavaServer has been created!") ;
+ }
+
+ return instance ;
+ }
+
+
+ InlineJavaUserClassLoader GetUserClassLoader(){
+ Thread t = Thread.currentThread() ;
+ if (t instanceof InlineJavaServerThread){
+ return ((InlineJavaServerThread)t).GetUserClassLoader() ;
+ }
+ else {
+ return ijucl ;
+ }
+ }
+
+
+ String GetType(){
+ return (shared_jvm ? "shared" : "private") ;
+ }
+
+
+ boolean GetNativeDoubles(){
+ return native_doubles ;
+ }
+
+
+ boolean IsJNI(){
+ return jni ;
+ }
+
+
+ /*
+ Since this function is also called from the JNI XS extension,
+ it's best if it doesn't throw any exceptions.
+ */
+ String ProcessCommand(String cmd) {
+ return ProcessCommand(cmd, true) ;
+ }
+
+
+ String ProcessCommand(String cmd, boolean addlf) {
+ InlineJavaUtils.debug(3, "packet recv is " + cmd) ;
+
+ String resp = null ;
+ if (cmd != null){
+ InlineJavaProtocol ijp = new InlineJavaProtocol(this, cmd) ;
+ try {
+ ijp.Do() ;
+ InlineJavaUtils.debug(3, "packet sent is " + ijp.GetResponse()) ;
+ resp = ijp.GetResponse() ;
+ }
+ catch (InlineJavaException e){
+ // Encode the error in default encoding since we don't want any
+ // Exceptions thrown here...
+ String err = "error scalar:" + ijp.EncodeFromByteArray(e.getMessage().getBytes()) ;
+ InlineJavaUtils.debug(3, "packet sent is " + err) ;
+ resp = err ;
+ }
+ }
+ else{
+ if (! shared_jvm){
+ // Probably connection dropped...
+ InlineJavaUtils.debug(1, "lost connection with client in single client mode. Exiting.") ;
+ System.exit(1) ;
+ }
+ else{
+ InlineJavaUtils.debug(1, "lost connection with client in shared JVM mode.") ;
+ return null ;
+ }
+ }
+
+ if (addlf){
+ resp = resp + "\n" ;
+ }
+
+ return resp ;
+ }
+
+
+ /*
+ This method really has no business here, but for historical reasons
+ it will remain here.
+ */
+ native String jni_callback(String cmd) ;
+
+
+ boolean IsThreadPerlContact(Thread t){
+ if (((jni)&&(t == creator))||
+ ((! jni)&&(t instanceof InlineJavaServerThread))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ synchronized Object GetObject(int id) throws InlineJavaException {
+ Object o = null ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ o = h.get(new Integer(id)) ;
+ if (o == null){
+ throw new InlineJavaException("Can't find object " + id + " for thread " +Thread.currentThread().getName()) ;
+ }
+ }
+
+ return o ;
+ }
+
+
+ synchronized int PutObject(Object o) throws InlineJavaException {
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ int id = objid ;
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ h.put(new Integer(objid), o) ;
+ objid++ ;
+ }
+
+ return id ;
+ }
+
+
+ synchronized Object DeleteObject(int id) throws InlineJavaException {
+ Object o = null ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ o = h.remove(new Integer(id)) ;
+ if (o == null){
+ throw new InlineJavaException("Can't find object " + id + " for thread " + Thread.currentThread().getName()) ;
+ }
+ }
+
+ return o ;
+ }
+
+
+ synchronized int ObjectCount() throws InlineJavaException {
+ int i = -1 ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ i = h.values().size() ;
+ }
+
+ return i ;
+ }
+
+
+ public synchronized void StopMainLoop(){
+ if (! jni){
+ try {
+ finished = true ;
+ server_socket.close() ;
+ }
+ catch (IOException e){
+ System.err.println("Shutdown IO Error: " + e.getMessage()) ;
+ System.err.flush() ;
+ }
+ }
+ }
+
+
+ synchronized void Shutdown(){
+ StopMainLoop() ;
+ System.exit(0) ;
+ }
+
+
+ /*
+ Here the prototype accepts Threads because the JNI thread
+ calls this method also.
+ */
+ synchronized void AddThread(Thread t){
+ thread_objects.put(t, new HashMap()) ;
+ InlineJavaPerlCaller.AddThread(t) ;
+ }
+
+
+ synchronized void RemoveThread(InlineJavaServerThread t){
+ thread_objects.remove(t) ;
+ InlineJavaPerlCaller.RemoveThread(t) ;
+ }
+
+
+
+ /*
+ Startup
+ */
+ public static void main(String[] argv){
+ int debug = Integer.parseInt(argv[0]) ;
+ String host = argv[1] ;
+ int port = Integer.parseInt(argv[2]) ;
+ boolean shared_jvm = new Boolean(argv[3]).booleanValue() ;
+ boolean priv = new Boolean(argv[4]).booleanValue() ;
+ boolean native_doubles = new Boolean(argv[5]).booleanValue() ;
+
+ InlineJavaServer ijs = new InlineJavaServer(debug, host, port, shared_jvm, priv, native_doubles) ;
+ ijs.RunMainLoop() ;
+ System.exit(0) ;
+ }
+
+
+ /*
+ With PerlInterpreter this is called twice, but we don't want to create
+ a new object the second time.
+ */
+ public static InlineJavaServer jni_main(int debug, boolean native_doubles){
+ if (instance != null){
+ InlineJavaUtils.set_debug(debug) ;
+ InlineJavaUtils.debug(1, "recycling InlineJavaServer created by PerlInterpreter") ;
+ return instance ;
+ }
+ else {
+ return new InlineJavaServer(debug, native_doubles) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaServerThread.java b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java
new file mode 100644
index 0000000..e39e4e9
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java
@@ -0,0 +1,69 @@
+package org.perl.inline.java ;
+
+import java.io.* ;
+import java.net.* ;
+import java.util.* ;
+
+
+class InlineJavaServerThread extends Thread {
+ private InlineJavaServer ijs ;
+ private Socket client ;
+ private BufferedReader br ;
+ private BufferedWriter bw ;
+ private InlineJavaUserClassLoader ijucl ;
+
+
+ InlineJavaServerThread(String name, InlineJavaServer _ijs, Socket _client, InlineJavaUserClassLoader _ijucl) throws IOException {
+ super(name) ;
+ client = _client ;
+ ijs = _ijs ;
+ ijucl = _ijucl ;
+
+ InputStreamReader ir = new InputStreamReader(client.getInputStream()) ;
+ OutputStreamWriter or = new OutputStreamWriter(client.getOutputStream()) ;
+ br = new BufferedReader(ir) ;
+ bw = new BufferedWriter(or) ;
+ }
+
+
+ BufferedReader GetReader(){
+ return br ;
+ }
+
+
+ BufferedWriter GetWriter(){
+ return bw ;
+ }
+
+
+ InlineJavaUserClassLoader GetUserClassLoader(){
+ return ijucl ;
+ }
+
+
+ public void run(){
+ try {
+ ijs.AddThread(this) ;
+
+ while (true){
+ String cmd = br.readLine() ;
+
+ String resp = ijs.ProcessCommand(cmd) ;
+ if (resp != null){
+ bw.write(resp) ;
+ bw.flush() ;
+ }
+ else {
+ client.close() ;
+ break ;
+ }
+ }
+ }
+ catch (IOException e){
+ System.err.println("IO error: " + e.getMessage()) ;
+ }
+ finally {
+ ijs.RemoveThread(this) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaThrown.java b/Java/sources/org/perl/inline/java/InlineJavaThrown.java
new file mode 100644
index 0000000..38bee49
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaThrown.java
@@ -0,0 +1,14 @@
+package org.perl.inline.java ;
+
+
+class InlineJavaThrown {
+ Throwable t ;
+
+ InlineJavaThrown(Throwable _t){
+ t = _t ;
+ }
+
+ Throwable GetThrowable(){
+ return t ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java
new file mode 100644
index 0000000..0f2d97a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java
@@ -0,0 +1,45 @@
+import java.util.* ;
+import java.lang.reflect.* ;
+
+
+public class InlineJavaUserClassLink {
+ public InlineJavaUserClassLink(){
+ }
+
+
+ public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ return m.invoke(o, p) ;
+ }
+
+
+ public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException {
+ return f.get(o) ;
+ }
+
+
+ public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException {
+ f.set(o, p) ;
+ }
+
+
+ public Object array_get(Object o, Integer idx){
+ return Array.get(o, idx.intValue()) ;
+ }
+
+
+ public void array_set(Object o, Integer idx, Object elem) throws IllegalArgumentException {
+ Array.set(o, idx.intValue(), elem) ;
+ }
+
+
+ public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ // This will allow usage of the default no-arg constructor
+ if (proto.length == 0){
+ return p.newInstance() ;
+ }
+ else{
+ Constructor con = (Constructor)p.getConstructor(proto) ;
+ return con.newInstance(args) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java
new file mode 100644
index 0000000..0105872
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java
@@ -0,0 +1,192 @@
+package org.perl.inline.java ;
+
+import java.net.* ;
+import java.util.* ;
+import java.io.* ;
+import java.lang.reflect.* ;
+
+
+/*
+ This is the ClassLoader that loads the users code. It is also
+ used to pass reflection calls to the InlineJavaUserClassLink
+ so that it will execute them.
+*/
+class InlineJavaUserClassLoader extends URLClassLoader {
+ private HashMap urls = new HashMap() ;
+
+ private Object link = null ;
+ private Method invoke = null ;
+ private Method get = null ;
+ private Method set = null ;
+ private Method array_get = null ;
+ private Method array_set = null ;
+ private Method create = null ;
+
+
+ public InlineJavaUserClassLoader(){
+ // Added Thread.currentThread().getContextClassLoader() so that the code works
+ // in Tomcat and possibly other embedded environments asa well.
+ super(new URL [] {}, Thread.currentThread().getContextClassLoader()) ;
+ }
+
+
+ public void AddClassPath(String path) throws InlineJavaException {
+ try {
+ File p = new File(path) ;
+ URL u = p.toURI().toURL() ;
+ if (urls.get(u) == null){
+ urls.put(u, "1") ;
+ addURL(u) ;
+ InlineJavaUtils.debug(2, "added " + u + " to classpath") ;
+ }
+ }
+ catch (MalformedURLException e){
+ throw new InlineJavaException("Can't add invalid classpath entry '" + path + "'") ;
+ }
+ }
+
+
+ synchronized private void check_link() throws InlineJavaException {
+ if (link == null){
+ try {
+ InlineJavaUtils.debug(1, "loading InlineJavaUserClassLink via InlineJavaUserClassLoader") ;
+ Class c = Class.forName("InlineJavaUserClassLink", true, this) ;
+ link = c.newInstance() ;
+
+ invoke = find_method(c, "invoke") ;
+ get = find_method(c, "get") ;
+ set = find_method(c, "set") ;
+ array_get = find_method(c, "array_get") ;
+ array_set = find_method(c, "array_set") ;
+ create = find_method(c, "create") ;
+ }
+ catch (Exception e){
+ throw new InlineJavaException("InlineJavaUserClassLoader can't load InlineJavaUserClassLink: invalid classpath setup (" +
+ e.getClass().getName() + ": " + e.getMessage() + ")") ;
+ }
+ }
+ }
+
+
+ private Method find_method(Class c, String name) throws InlineJavaException {
+ Method ml[] = c.getMethods() ;
+ for (int i = 0 ; i < ml.length ; i++){
+ if (ml[i].getName().equals(name)){
+ return ml[i] ;
+ }
+ }
+
+ throw new InlineJavaException("Can't find method '" + name +
+ "' in class InlineJavaUserClassLink") ;
+ }
+
+
+ private Object invoke_via_link(Method m, Object p[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ try {
+ return m.invoke(link, p) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalAccessException") ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalArgumentException") ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ if (t instanceof NoSuchMethodException){
+ throw (NoSuchMethodException)t ;
+ }
+ else if (t instanceof InstantiationException){
+ throw (InstantiationException)t ;
+ }
+ else if (t instanceof IllegalAccessException){
+ throw (IllegalAccessException)t ;
+ }
+ if (t instanceof IllegalAccessException){
+ throw (IllegalAccessException)t ;
+ }
+ else if (t instanceof IllegalArgumentException){
+ throw (IllegalArgumentException)t ;
+ }
+ else if (t instanceof InvocationTargetException){
+ throw (InvocationTargetException)t ;
+ }
+ // Not sure if this is really necessary, but...
+ else if (t instanceof RuntimeException){
+ RuntimeException re = (RuntimeException)t ;
+ throw re ;
+ }
+ else{
+ // In theory this case is impossible.
+ throw new InlineJavaException("Unexpected exception of type '" +
+ t.getClass().getName() + "': " + t.getMessage()) ;
+ }
+ }
+ }
+
+
+ public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(invoke, new Object [] {m, o, p}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ return null ;
+ }
+
+
+ public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(get, new Object [] {f, o}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ return null ;
+ }
+
+
+ public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ invoke_via_link(set, new Object [] {f, o, p}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ }
+
+
+ public Object array_get(Object o, int idx) throws InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(array_get, new Object [] {o, new Integer(idx)}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (IllegalAccessException iae){/* Impossible */}
+ catch (IllegalArgumentException iae){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ return null ;
+ }
+
+
+ public void array_set(Object o, int idx, Object elem) throws IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ invoke_via_link(array_set, new Object [] {o, new Integer(idx), elem}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (IllegalAccessException iae){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ }
+
+
+ public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ check_link() ;
+ return invoke_via_link(create, new Object [] {p, args, proto}) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUtils.java b/Java/sources/org/perl/inline/java/InlineJavaUtils.java
new file mode 100644
index 0000000..65426dd
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUtils.java
@@ -0,0 +1,175 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+
+
+/*
+ Creates a string representing a method signature
+*/
+class InlineJavaUtils {
+ private static int debug = 0 ;
+
+
+ public synchronized static void set_debug(int d){
+ debug = d ;
+ }
+
+
+ public static int get_debug(){
+ return debug ;
+ }
+
+
+ static String CreateSignature(Class param[]){
+ return CreateSignature(param, ", ") ;
+ }
+
+
+ static String CreateSignature(Class param[], String del){
+ StringBuffer ret = new StringBuffer() ;
+ for (int i = 0 ; i < param.length ; i++){
+ if (i > 0){
+ ret.append(del) ;
+ }
+ ret.append(param[i].getName()) ;
+ }
+
+ return "(" + ret.toString() + ")" ;
+ }
+
+
+ synchronized static void debug(int level, String s) {
+ if ((debug > 0)&&(debug >= level)){
+ StringBuffer sb = new StringBuffer() ;
+ for (int i = 0 ; i < level ; i++){
+ sb.append(" ") ;
+ }
+ System.err.println("[java][" + level + "]" + sb.toString() + s) ;
+ System.err.flush() ;
+ }
+ }
+
+
+ static void Fatal(String msg){
+ System.err.println(msg) ;
+ System.err.flush() ;
+ System.exit(1) ;
+ }
+
+
+ static boolean ReverseMembers() {
+ String v = System.getProperty("java.version") ;
+ boolean no_rev = ((v.startsWith("1.2"))||(v.startsWith("1.3"))) ;
+
+ return (! no_rev) ;
+ }
+
+
+
+ /*
+ Base64 stuff. This section conatins code by Christian d'Heureuse that is
+ licended under the LGPL. Used by permission:
+
+ From: Christian d'Heureuse <chdh@inventec.ch>
+ To: Patrick LeBoutillier <patrick.leboutillier@gmail.com>
+ Date: Aug 11, 2005 4:45 AM
+ Subject: Re: Base64Coder
+
+ > I was wondering if you can grant me permission to include your
+ > code in my project.
+
+ Yes, I grant you permission to include the Base64Coder class in your
+ project.
+
+ *
+ * A Base64 Encoder/Decoder.
+ *
+ * This class is used to encode and decode data in Base64 format
+ * as described in RFC 1521.
+ *
+ * <p>
+ * Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland.<br>
+ * License: This is "Open Source" software and released under the <a href="http://www.gnu.org/licenses/lgpl.html" target="_top">GNU/LGPL</a> license.
+ * It is provided "as is" without warranty of any kind. Please contact the author for other licensing arrangements.<br>
+ * Home page: <a href="http://www.source-code.biz" target="_top">www.source-code.biz</a><br>
+ *
+ * <p>
+ * Version history:<br>
+ * 2003-07-22 Christian d'Heureuse (chdh): Module created.<br>
+ * 2005-08-11 chdh: Lincense changed from GPL to LGPL.
+ *
+ */
+
+ // Mapping table from 6-bit nibbles to Base64 characters.
+ private static char[] map1 = new char[64];
+ static {
+ int i=0;
+ for (char c='A'; c<='Z'; c++) map1[i++] = c;
+ for (char c='a'; c<='z'; c++) map1[i++] = c;
+ for (char c='0'; c<='9'; c++) map1[i++] = c;
+ map1[i++] = '+'; map1[i++] = '/';
+ }
+
+ // Mapping table from Base64 characters to 6-bit nibbles.
+ private static byte[] map2 = new byte[128];
+ static {
+ for (int i=0; i<map2.length; i++) map2[i] = -1;
+ for (int i=0; i<64; i++) map2[map1[i]] = (byte)i;
+ }
+
+
+ public static char[] EncodeBase64(byte[] in){
+ int iLen = in.length;
+ int oDataLen = (iLen*4+2)/3; // output length without padding
+ int oLen = ((iLen+2)/3)*4; // output length including padding
+ char[] out = new char[oLen];
+ int ip = 0;
+ int op = 0;
+ while (ip < iLen) {
+ int i0 = in[ip++] & 0xff;
+ int i1 = ip < iLen ? in[ip++] & 0xff : 0;
+ int i2 = ip < iLen ? in[ip++] & 0xff : 0;
+ int o0 = i0 >>> 2;
+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
+ int o3 = i2 & 0x3F;
+ out[op++] = map1[o0];
+ out[op++] = map1[o1];
+ out[op] = op < oDataLen ? map1[o2] : '='; op++;
+ out[op] = op < oDataLen ? map1[o3] : '='; op++;
+ }
+ return out;
+ }
+
+
+ public static byte[] DecodeBase64(char[] in){
+ int iLen = in.length;
+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
+ while (iLen > 0 && in[iLen-1] == '=') iLen--;
+ int oLen = (iLen*3) / 4;
+ byte[] out = new byte[oLen];
+ int ip = 0;
+ int op = 0;
+ while (ip < iLen) {
+ int i0 = in[ip++];
+ int i1 = in[ip++];
+ int i2 = ip < iLen ? in[ip++] : 'A';
+ int i3 = ip < iLen ? in[ip++] : 'A';
+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int b0 = map2[i0];
+ int b1 = map2[i1];
+ int b2 = map2[i2];
+ int b3 = map2[i3];
+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int o0 = ( b0 <<2) | (b1>>>4);
+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
+ int o2 = ((b2 & 3)<<6) | b3;
+ out[op++] = (byte)o0;
+ if (op<oLen) out[op++] = (byte)o1;
+ if (op<oLen) out[op++] = (byte)o2;
+ }
+ return out;
+ }
+}
diff --git a/Java/typemap b/Java/typemap
new file mode 100755
index 0000000..b470113
--- /dev/null
+++ b/Java/typemap
@@ -0,0 +1,23 @@
+TYPEMAP
+InlineJavaJNIVM * T_PTROBJ_IJVM
+
+
+OUTPUT
+
+T_PTROBJ_IJVM
+ sv_setref_pv($arg, \"Inline::Java::JNI\", (void *)$var) ;
+
+
+INPUT
+
+T_PTROBJ_IJVM
+ if (sv_derived_from($arg, \"Inline::Java::JNI\")) {
+ $var = ($type)SvIV((SV*)SvRV($arg)) ;
+ }
+ else{
+ if (SvOK($arg)){
+ croak(\"$var is not of type Inline::Java::JNI\") ;
+ }
+ }
+
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..bd7d8ca
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,81 @@
+CHANGES
+MANIFEST
+README
+README.JNI
+TODO
+META.yml
+Makefile.PL
+Java.pm
+Java.pod
+Java/Object.pm
+Java/Protocol.pm
+Java/Class.pm
+Java/Callback.pm
+Java/Callback.pod
+Java/Portable.pm
+Java/Array.pm
+Java/Handle.pm
+Java/Makefile.PL
+Java/JVM.pm
+Java/Server.pm
+Java/JNI.pm
+Java/JNI.xs
+Java/typemap
+Java/jvm.def
+Java/sources/org/perl/inline/java/InlineJavaArray.java
+Java/sources/org/perl/inline/java/InlineJavaCastException.java
+Java/sources/org/perl/inline/java/InlineJavaClass.java
+Java/sources/org/perl/inline/java/InlineJavaException.java
+Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java
+Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java
+Java/sources/org/perl/inline/java/InlineJavaPerlException.java
+Java/sources/org/perl/inline/java/InlineJavaProtocol.java
+Java/sources/org/perl/inline/java/InlineJavaServer.java
+Java/sources/org/perl/inline/java/InlineJavaServerThread.java
+Java/sources/org/perl/inline/java/InlineJavaThrown.java
+Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java
+Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java
+Java/sources/org/perl/inline/java/InlineJavaUtils.java
+Java/sources/org/perl/inline/java/InlineJavaCallback.java
+Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java
+Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java
+Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java
+Java/sources/org/perl/inline/java/InlineJavaPerlObject.java
+Java/sources/org/perl/inline/java/InlineJavaHandle.java
+Java/PerlNatives/Makefile.PL
+Java/PerlNatives/PerlNatives.pm
+Java/PerlNatives/PerlNatives.xs
+Java/PerlNatives/PerlNatives.pod
+Java/PerlNatives/t/01_init.t
+Java/PerlNatives/t/02_perl_natives.t
+Java/PerlInterpreter/Makefile.PL
+Java/PerlInterpreter/PerlInterpreter.pm
+Java/PerlInterpreter/PerlInterpreter.xs
+Java/PerlInterpreter/PerlInterpreter.pod
+Java/PerlInterpreter/t/01_init.t
+Java/PerlInterpreter/t/02_perl_interpreter.t
+Java/PerlInterpreter/t/Tests.pl
+t/01_init.t
+t/02_primitives.t
+t/02_primitives_1_4.t
+t/03_objects.t
+t/04_members.t
+t/05_arrays.t
+t/06_static.t
+t/07_polymorph.t
+t/08_study.t
+t/09_usages.t
+t/10_1_shared_alone.t
+t/10_5_shared_fork.t
+t/10_6_shared_sim.t
+t/11_exceptions.t
+t/12_1_callbacks.t
+t/13_handles.t
+t/14_encoding.t
+t/15_native_doubles.t
+t/99_end.t
+t/types.java
+t/types.class
+t/no_const.java
+t/no_const.class
+t/shared.java
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..c36ff7d
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,16 @@
+--- #YAML:1.0
+name: Inline-Java
+version: 0.53
+abstract: Write Perl classes in Java.
+license: ~
+author:
+ - Patrick LeBoutillier <patl@cpan.org>
+generated_by: ExtUtils::MakeMaker version 6.42
+distribution_type: module
+requires:
+ Inline: 0.44
+ MIME::Base64: 0
+ Test: 1.13
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.3.html
+ version: 1.3
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..6ed46c5
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,299 @@
+use ExtUtils::MakeMaker ;
+
+use strict ;
+use File::Spec ;
+use Cwd ;
+use Config ;
+
+require "Java/Portable.pm" ;
+
+print "\nWelcome to the Inline::Java installation procedure.\n\n" ;
+
+# Hide PerlNatives by default...
+$main::build_perl_natives = 0 ;
+
+# Grab the J2SDK argument
+my $jdk_dir = '' ;
+for (my $i = 0 ; $i < scalar(@ARGV) ; $i++){
+ my $remove = 0 ;
+ if ($ARGV[$i] =~ /^J2SDK=(.+)$/){
+ $jdk_dir = $1 ;
+ $remove = 1 ;
+ }
+ elsif ($ARGV[$i] =~ /^BUILD_JNI=(.+)$/){
+ $main::build_jni = $1 ;
+ $remove = 1 ;
+ }
+ elsif ($ARGV[$i] =~ /^BUILD_PERL_NATIVES=(.+)$/){
+ $main::build_perl_natives = $1 ;
+ $remove = 1 ;
+ }
+ elsif ($ARGV[$i] =~ /^BUILD_PERL_INTERPRETER=(.+)$/){
+ $main::build_perl_interpreter = $1 ;
+ $remove = 1 ;
+ }
+ elsif ($ARGV[$i] =~ /^JVM_LIB_TYPE=(.+)$/){
+ $main::jvm_lib_type = $1 ;
+ $remove = 1 ;
+ }
+ if ($remove){
+ splice(@ARGV, $i, 1) ;
+ $i-- ;
+ }
+}
+
+if (! $jdk_dir){
+ my $try = $ENV{PERL_INLINE_JAVA_J2SDK} || $ENV{JAVA_HOME}
+ || Inline::Java::Portable::portable('DEFAULT_J2SDK_DIR') ;
+ print "Using $try as J2SDK directory.\n\n" if $try ;
+ $jdk_dir = $try ;
+}
+
+if (! $jdk_dir){
+ my $def_pl = File::Spec->catfile('Java', 'default_j2sdk.pl') ;
+ if (-e $def_pl){
+ require File::Spec->catfile('Java', 'default_j2sdk.pl') ;
+ $jdk_dir = Inline::Java::get_default_j2sdk() ;
+ }
+ else {
+ print <<NO_J2SDK;
+A Java 2 SDK is required to install and use Inline::Java. Please
+specify your Java 2 SDK installation directory using the J2SDK
+option to Makefile.PL as such:
+
+ perl Makefile.PL J2SDK=/path/to/your/j2sdk/installation
+
+You can set the JAVA_HOME environment variable to specify your
+Java 2 SDK installation directory. For example, if you are using
+the CPAN installer you can do:
+
+ JAVA_HOME=/path/to/your/j2sdk/installation cpan Inline::Java
+NO_J2SDK
+ exit(1) ;
+ }
+}
+elsif (! -d $jdk_dir){
+ print <<BAD_J2SDK;
+Java 2 SDK installation directory '$jdk_dir' does not exist.
+BAD_J2SDK
+ exit(1) ;
+}
+my $perl_jdk_dir = $jdk_dir ;
+$perl_jdk_dir =~ s/'/\'/g ;
+
+# Check directory
+my $jdk_bin = Inline::Java::Portable::portable("J2SDK_BIN") ;
+my $ext = Inline::Java::Portable::portable('EXE_EXTENSION') ;
+foreach my $f ('javac', 'jar', 'java'){
+ if (! -f File::Spec->catfile($jdk_dir, $jdk_bin, $f . $ext)){
+ my $bf = File::Spec->catfile($jdk_bin, $f . $ext) ;
+ print "Can't locate file '$bf' anywhere under '$jdk_dir'\n" ;
+ }
+}
+
+
+# Now we have the J2SDK directory and it exists.
+# We will create the default_j2sdk.pl file that
+# will contain that value for future use.
+my $def_jdk = File::Spec->catfile('Java', 'default_j2sdk.pl') ;
+open(J2SDK, ">$def_jdk") or
+ die("Can't open '$def_jdk' for writing: $!") ;
+print J2SDK <<J2SDK_PL;
+# This file is created by the Makefile.PL for Inline::Java
+# You can modify it if you wish
+use strict ;
+
+# The default J2SDK to use for Inline::Java. You can change
+# it if this value becomes invalid.
+sub Inline::Java::get_default_j2sdk {
+ return '$perl_jdk_dir' ;
+}
+1 ;
+
+
+J2SDK_PL
+close(J2SDK) ;
+
+print <<SAVE_J2SDK;
+Default J2SDK for Inline::Java will be '$jdk_dir'.
+See module documentation for information on how to use a different J2SDK
+or change this default value.
+
+SAVE_J2SDK
+
+
+# We will now add the building of our Java files to the Makefile.
+my $javac = File::Spec->catfile($jdk_dir, $jdk_bin, 'javac' . $ext) ;
+my $jar = File::Spec->catfile($jdk_dir, $jdk_bin, 'jar' . $ext) ;
+my $src_dir = File::Spec->catdir('Java', 'sources', 'org', 'perl', 'inline', 'java') ;
+my $src = File::Spec->catfile($src_dir, '*.java') ;
+my $obj_dir = File::Spec->catdir('Java', 'classes') ;
+my $server_arch = File::Spec->catfile('Java', 'InlineJavaServer.jar') ;
+my $user_arch = File::Spec->catfile('Java', 'InlineJavaUser.jar') ;
+
+# Create the object diretory because later we need to put the properties
+# file inside it.
+if (! -e $obj_dir){
+ mkdir($obj_dir) or
+ die("Can't create object directory '$obj_dir': $!") ;
+}
+
+sub MY::top_targets {
+ my $this = shift ;
+
+ my $make = <<MAKE ;
+# Added by Inline::Java installation
+pure_all :: java
+MAKE
+
+ return $make . $this->MM::top_targets() ;
+}
+
+
+my $INSTALLSITEARCH = '' ;
+my $INST_ARCHLIB = '' ;
+sub MY::postamble {
+ my $this = shift ;
+
+ my $java_src = join(' ', glob($src), File::Spec->catfile($obj_dir, 'InlineJava.properties')) ;
+
+ my $make = <<MAKE ;
+# Added by Inline::Java installation
+JAVA_SRC=$java_src
+
+java.ts: \$(JAVA_SRC)
+ \@\$(MKPATH) $obj_dir
+ "$javac" -deprecation -g -d $obj_dir $src
+ "$jar" cf $server_arch -C $obj_dir org -C $obj_dir InlineJava.properties
+ "$jar" cf $user_arch -C $obj_dir InlineJavaUserClassLink.class
+ \@\$(TOUCH) java.ts
+
+java :: java.ts
+MAKE
+
+ # Used for PerlNatives
+ $INSTALLSITEARCH = expand_macros($this, 'INSTALLSITEARCH') ;
+ $INST_ARCHLIB = expand_macros($this, 'INST_ARCHLIB') ;
+
+ return $make ;
+}
+
+
+sub expand_macros {
+ my $mm = shift ;
+ my $var = shift ;
+
+ my $val = $mm->{$var} ;
+ while ($val =~ s/\$\((.*?)\)/$mm->{$1}/){}
+ $val =~ s/\\/\\\\/g ;
+
+ return $val ;
+}
+
+
+# Write the Makefile
+my $natives_test = File::Spec->catdir('Java', 'Natives', '_Inline_test') ;
+my $perlinterp_test = File::Spec->catdir('Java', 'PerlInterpreter', '_Inline_test') ;
+WriteMakefile(
+ NAME => 'Inline::Java',
+ VERSION_FROM => 'Java.pm',
+ DIR => ['Java'],
+ PREREQ_PM => {
+ Inline => 0.44,
+ Test => 1.13,
+ 'MIME::Base64' => 0,
+ },
+ PM => {
+ 'Java.pm' => File::Spec->catfile('$(INST_LIBDIR)', 'Java.pm'),
+ 'Java.pod'=> File::Spec->catfile('$(INST_LIBDIR)', 'Java.pod'),
+ $server_arch => File::Spec->catfile('$(INST_LIBDIR)', $server_arch),
+ $user_arch => File::Spec->catfile('$(INST_LIBDIR)', $user_arch),
+ },
+ clean => {FILES => "$def_jdk _Inline_test $natives_test $perlinterp_test $obj_dir $server_arch $user_arch java.ts"},
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'Java.pod', # retrieve abstract from module
+ AUTHOR => 'Patrick LeBoutillier <patl@cpan.org>') : ()),
+) ;
+
+
+# Add the so_dirs to the default_j2sdk.pl file.
+open(J2SDK, ">>$def_jdk") or
+ die("Can't open '$def_jdk' for appending: $!") ;
+print J2SDK <<J2SDK_PL;
+sub Inline::Java::get_default_j2sdk_so_dirs {
+ return (
+J2SDK_PL
+foreach my $d (@main::SO_DIRS){
+ $d =~ s/'/\'/g ;
+ print J2SDK "\t\t'$d',\n" ;
+}
+print J2SDK <<J2SDK_PL;
+ ) ;
+}
+
+
+1 ;
+J2SDK_PL
+close(J2SDK) ;
+
+
+
+# Create the properties that will be included in the jar.
+my @perlnatives_so_parts = ("auto", "Inline", "Java", "PerlNatives",
+ "PerlNatives." . Inline::Java::Portable::portable('SO_EXT')) ;
+my $install_perlnatives_so = File::Spec->catfile($INSTALLSITEARCH, @perlnatives_so_parts) ;
+$install_perlnatives_so = Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $install_perlnatives_so) ;
+$install_perlnatives_so =~ s/\\/\\\\/g ;
+my $test_perlnatives_so = File::Spec->rel2abs(File::Spec->catfile($INST_ARCHLIB, @perlnatives_so_parts)) ;
+$test_perlnatives_so = Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $test_perlnatives_so) ;
+$test_perlnatives_so =~ s/\\/\\\\/g ;
+
+my @perlinterpreter_so_parts = ("auto", "Inline", "Java", "PerlInterpreter",
+ "PerlInterpreter." . Inline::Java::Portable::portable('SO_EXT')) ;
+my $install_perlinterpreter_so = File::Spec->catfile($INSTALLSITEARCH, @perlinterpreter_so_parts) ;
+$install_perlinterpreter_so = Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $install_perlinterpreter_so) ;
+$install_perlinterpreter_so =~ s/\\/\\\\/g ;
+my $test_perlinterpreter_so = File::Spec->rel2abs(File::Spec->catfile($INST_ARCHLIB, @perlinterpreter_so_parts)) ;
+$test_perlinterpreter_so = Inline::Java::Portable::portable("SUB_FIX_JAVA_PATH", $test_perlinterpreter_so) ;
+$test_perlinterpreter_so =~ s/\\/\\\\/g ;
+
+my $libperl = $Config{libperl} ;
+my $dlext = $Config{dlext} ;
+my $libperl_so = '' ;
+if ($libperl =~ /\.$dlext$/){
+ $libperl_so = File::Spec->catfile($Config{installarchlib}, 'CORE', $libperl) ;
+}
+
+my $prop = File::Spec->catfile($obj_dir, 'InlineJava.properties') ;
+open(PROP, ">$prop") or
+ die("Can't open '$prop' for writing: $!") ;
+print PROP <<PROP;
+# This file is created by the Makefile.PL for Inline::Java
+inline_java_perlnatives_so_install = $install_perlnatives_so
+inline_java_perlnatives_so_test = $test_perlnatives_so
+inline_java_perlinterpreter_so_install = $install_perlinterpreter_so
+inline_java_perlinterpreter_so_test = $test_perlinterpreter_so
+inline_java_libperl_so = $libperl_so
+PROP
+close(PROP) ;
+
+# Clean up the Makefile for Win95/98/Me
+if (Inline::Java::Portable::portable('COMMAND_COM')){
+ print "\nFixing Makefile for Win95/98/Me...\n" ;
+ open(MAKEFILE, "<Makefile") or die "Can't open Makefile for reading" ;
+ my @lines = <MAKEFILE> ;
+ close(MAKEFILE) ;
+ open(MAKEFILE, ">Makefile") or die "Can't open Makefile for writing" ;
+ foreach my $line (@lines){
+ if ($line !~ /^\s*((\@\[)|(\]))\s*$/){
+ print MAKEFILE $line ;
+ }
+ }
+ close(MAKEFILE) ;
+}
+
+my $make = Inline::Java::Portable::portable('MAKE') ;
+print "\nYou can continue the installation with the following commands:\n" ;
+print " % $make\n" ;
+print " % $make test\n" ;
+print " % $make install\n" ;
diff --git a/README b/README
new file mode 100644
index 0000000..9f8ac01
--- /dev/null
+++ b/README
@@ -0,0 +1,90 @@
+INTRODUCTION:
+
+Inline::Java - Write Perl classes in Java.
+
+Inline::Java lets you write Perl classes in Java.
+
+Example:
+
+ use Inline Java => <<'END';
+ class JAxH {
+ public JAxH(String x){
+ System.out.println("Just Another " + x + " Hacker") ;
+ }
+ }
+ END
+
+ new JAxH('Inline') ;
+
+When run, this complete program prints:
+
+ Just Another Inline Hacker
+
+
+-------------------------------------------------------------------------------
+JNI (JAVA NATIVE INTERFACE) EXTENSION:
+
+Inline::Java now provides a JNI extension that allows you to load the Java
+virtual machine as shared object instead of running it as a separate process.
+
+See README.JNI for more information on building the JNI extension.
+
+
+-------------------------------------------------------------------------------
+INSTALLATION:
+
+- This module requires Inline version 0.44 or higher to be installed.
+- It also requires a version of the Java SDK 1.2 or higher to be
+ installed. You can get the latest Java SDK from Sun Microsystems
+ at http://java.sun.com. Follow the provided instructions in order
+ to install the Java SDK properly.
+
+To install Inline::Java do this:
+
+ % perl Makefile.PL J2SDK=/your/java/dir (see Note 1)
+ % make (see Note 2)
+ % make test (see Note 3, 4)
+ % make install
+
+You have to 'make install' before you can run it successfully.
+
+Note 1: Under Win95/98/Me, you may need to do 'cd ..' to get back to the
+your original directory after the command has completed. Also, you may set
+either the JAVA_HOME or the PERL_INLINE_JAVA_J2SDK environment variable to
+/your/java/dir instead of using the J2SDK Makefile.PL parameter.
+
+Note 2: Use nmake on Win32.
+
+Note 3: If you have built the JNI extension and want the test suite to use
+it, you will need to set the PERL_INLINE_JAVA_JNI environment variable to 1
+BEFORE running 'make test'.
+
+Note 4: When testing Inline::Java, it's always a good idea to run 'make test'
+twice. The first time you test the building and loading of a module, the
+second time you test loading of an already built module.
+
+
+-------------------------------------------------------------------------------
+FEATURES FOR THIS VERSION:
+
+Inline::Java version 0.52 is a minor upgrade that includes:
+ - Fixed JNI on cygwin (many thanks to Eric Rybski for the patch)
+ - Improved installation. 'make java' is now performed automatically.
+ - Fixed problems with disappearing exceptions by localizing $@.
+ - Other minor bug fixes.
+
+See CHANGES for a full change list.
+
+-------------------------------------------------------------------------------
+INFORMATION:
+
+= For more information on Inline::Java, see 'perldoc Inline::Java'.
+= For information about Inline, see 'perldoc Inline'.
+= For information on using Java, visit http://java.sun.org.
+
+Inline::Java's mailing list is inline@perl.org.
+To subscribe, send an email to inline-subscribe@perl.org.
+
+Please send questions and comments to Patrick LeBoutillier <patl@cpan.org>.
+
+Copyright (c) 2001-2005, Patrick LeBoutillier. All Rights Reserved.
diff --git a/README.JNI b/README.JNI
new file mode 100644
index 0000000..7bc6ad1
--- /dev/null
+++ b/README.JNI
@@ -0,0 +1,177 @@
+JNI (JAVA NATIVE INTERFACE) EXTENSION
+-------------------------------------
+Inline::Java now provides a JNI extension that allows you to load the Java
+virtual machine as shared object instead of running it as a separate process.
+
+
+PLATFORM AVAILABILITY
+---------------------
+The JNI extension is available on all supported platforms.
+
+The extension builds properly on all platform, but problems can occur when
+running it or tweaking maybe necessary on certain platforms. All help
+is welcome if anyone out there is a JNI expert.
+
+The reason why JNI is a bit complex under Linux/Solaris is because of
+threads. The Java Virtual Machine (libjvm.so) shared object uses native
+threads when embedded inside another program and that host program (in
+this case Perl) must link with the same threads library for everything
+to work properly. Starting with Perl 5.8, this works fine. With previous
+versions, you may get around rebuilding Perl by setting LD_PRELOAD.
+The only problems encountered where that when setting LD_PRELOAD before
+running the test suite, the LD_PRELOAD affects make as well and on Solaris
+some crashes were seen. Read more on this in the Solaris section below.
+
+Note: Make sure the directories listed at the end of the installation
+procedure are included in your LD_LIBRARY_PATH (PATH on Win32) environment
+variable. This JNI extension will not load properly the the Java shared
+objects cannot be located at runtime.
+
+
+OVERVIEW
+--------
+ +----------+------------+------------+
+ | JDK1.3.1 | J2SDK1.4.2 | J2SDK1.5.0 |
++-------------+----------+------------+------------+
+| Win32 | ok | ok | ok |
++-------------+----------+------------+------------+
+| cygwin | ok* | ok* | ok* |
++-------------+----------+------------+------------+
+| Linux RH7.3 | ok* | ok* | ? |
++-------------+----------+------------+------------+
+| Linux RH9.0 | ok* | ok* | ok |
++-------------+----------+------------+------------+
+| Soloris 2.8 | ok* | ? | ? |
++-------------+----------+------------+------------+
+* : Read below
+
+
+WIN32
+-----
+Java 2 SDK 1.3.1:
+ The JNI extension runs without problems.
+
+Java 2 SDK 1.4.2:
+ The JNI extension runs without problems.
+
+
+LINUX
+-----
+Java 2 SDK 1.3.1:
+ The JNI extension runs without problems with this Java 2 SDK, provided
+that you use Perl >= 5.8.0 or do one of the following:
+
+1- Rebuild perl and add the libpthread library in front of all other
+ libraries (see the 'BUILDING PERL' section below). You should also
+ use your system's malloc (not perl's).
+ This is the recommended solution.
+
+2- Add the proper version of libpthread.so to your LD_PRELOAD environment
+ variable. On my test system this was /lib/i686/libpthread.so.0.
+ This solution seems viable but thread related crashes/hang-ups have
+ been reported on some systems. If make crashes while running the test
+ suite, try running it like this:
+ % for i in `ls t/*.t` ; do perl -Mblib $i; done
+
+Also, make sure you use do not use 'classic' VM. This one should only
+be used with 'green threads', which don't seem to work with JNI. Use either
+the 'client' or the 'server' VMs.
+
+Java 2 SDK 1.4.2:
+ The same as Java 2 SDK 1.3.1 applies.
+
+
+SOLARIS
+-------
+Java 2 SDK 1.3.1:
+ The JNI extension runs without problems with this Java 2 SDK, provided
+that you use Perl >= 5.8.0 or do one of the following:
+
+1- Rebuild perl and add the libthread library in front of all other
+ libraries (see the 'BUILDING PERL' section below). You should also
+ use gcc and your system's malloc (not perl's).
+ This is the recommended solution.
+
+2- Add the proper version of libthread.so to your LD_PRELOAD environment
+ variable. On my test system this was /lib/libthread.so.
+ This solution seems viable but thread related crashes/hang-ups have
+ been reported on some systems. If make crashes while running the test
+ suite, try running it like this:
+ % for i in `ls t/*.t` ; do perl -Mblib $i; done
+
+
+CYGWIN
+------
+Java 2 SDK 1.3.1:
+ The JNI extension runs without problems.
+
+ PerlInterpreter:
+ Compiles but stand-alone (non-JNI initiated) Java interpreter locks
+ up when attempting to load the DLL. This is a known issue with
+ initialization of the cygwin1.dll from a non-cygwin DLL or EXE, which
+ has been "broken" since at least Cygwin 1.5.13.
+ A possible work around might be to build the PerlInterpreter against
+ a native Win32 Perl distribution with '-mno-cygwin' GCC compile flag,
+ although this means you would be using native Perl instead of Cygwin
+ Perl when using PerlInterpreter.
+
+Java 2 SDK 1.4.2:
+ The JNI extension runs without problems.
+
+ PerlInterpreter:
+ The same as Java 2 SDK 1.3.1 applies.
+
+ PerlNatives:
+ Compiles and runs fine in a JNI-initiated interpreter, but stand-alone
+ (non-JNI initiated) Java interpreter locks up when attempting to load
+ the DLL. This is a known issue with initialization of the
+ cygwin1.dll from a non-cygwin DLL or EXE, which has been "broken"
+ since at least Cygwin 1.5.13.
+
+Java 2 SDK 1.5.0:
+ The same as Java 2 SDK 1.4.2 applies.
+
+
+BUILDING PERL
+-------------
+Here's how to rebuild Perl (version < 5.8.0) to get the JNI extension to
+work properly:
+
+- Use all the defaults or whatever makes sense, but no threaded Perl
+ and no interpreter threads, i.e.:
+
+ Build a threading Perl? [n] n
+ Build Perl for multiplicity? [n] n
+
+- When asked what libraries to use, type -lthread (Solaris) or -lpthread
+ (Linux) and tack on whatever the default is at the end, i.e.:
+
+ What libraries to use? -> [-lnsl -lndbm -lgdbm -ldb -ldl -lm -lc -lcrypt
+ -lutil] -lpthread -lnsl -lndbm -lgdbm -ldb -ldl -lm -lc -lcrypt -lutil
+ (under Linux it's -lpthread, but for Solaris it's -lthread)
+
+- Make sure that you do not use perl's own malloc, i.e.:
+
+ Do you wish to attempt to use the malloc that comes with perl5? -> [n] n
+
+
+RUNNING Inline::Java WITH THE JNI EXTENSION
+-------------------------------------------
+To run Inline::Java with the JNI extension, do one of the following:
+
+ - set the JNI configuration option to 1
+ - set the PERL_INLINE_JAVA_JNI environment variable to 1
+
+To run the test suite (make test) with the JNI extension you must use the
+PERL_INLINE_JAVA_JNI environment variable
+
+
+USING THE 'SHARED_JVM' MODE
+---------------------------
+Inline::Java 0.30 introduced a 'SHARED_JVM' mode that allows many clients
+to connect to the same Inline::Java Java server. The 'SHARED_JVM' mode is
+meant to be used with forking processes such as Apache with mod_perl. The
+'SHARED_JVM' mode does NOT work along with the JNI mode. In fact the author
+was not able to successfully fork the Java Virtual Machine under any
+circumstances.
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..997685c
--- /dev/null
+++ b/TODO
@@ -0,0 +1,14 @@
+CODE:
+- Localize $@ where required
+- Finish PerlInterpreter test suite
+- Finish PerlHandle stuff and document
+
+DOCUMENTATION:
+- Document new Callback Interface
+- Document InlineJavaPerlObject when finished
+
+TEST:
+- Alpha
+- Cygwin
+
+
diff --git a/t/01_init.t b/t/01_init.t
new file mode 100644
index 0000000..83b0a60
--- /dev/null
+++ b/t/01_init.t
@@ -0,0 +1,51 @@
+use strict ;
+use Test ;
+
+BEGIN {
+ $main::cp = $ENV{CLASSPATH} || "<empty>" ;
+ plan(tests => 1) ;
+ mkdir('./_Inline_test', 0777) unless -e './_Inline_test' ;
+}
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'DATA'
+) ;
+
+my $ij = $types1::INLINE ;
+$ij = $types1::INLINE ; # Stupid warning...
+my $jdk = $ij->get_java_config("J2SDK") ;
+my $ver = types1->version() ;
+
+print STDERR "\nPerl version is $]\n" ;
+print STDERR "Inline version is $Inline::VERSION\n" ;
+print STDERR "Inline::Java version is $Inline::Java::VERSION\n" ;
+
+print STDERR "J2SDK version is $ver, from $jdk\n" ;
+print STDERR "CLASSPATH is $main::cp\n" ;
+
+if ($ENV{PERL_INLINE_JAVA_EMBEDDED_JNI}){
+ print STDERR "Using JNI extension (embedded).\n" ;
+}
+elsif ($ENV{PERL_INLINE_JAVA_JNI}){
+ print STDERR "Using JNI extension.\n" ;
+}
+
+ok(1) ;
+
+
+
+__END__
+
+__Java__
+
+class types1 {
+ static public String version(){
+ return System.getProperty("java.version") ;
+ }
+}
+
+
+
diff --git a/t/02_primitives.t b/t/02_primitives.t
new file mode 100755
index 0000000..245557d
--- /dev/null
+++ b/t/02_primitives.t
@@ -0,0 +1,252 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'DATA'
+) ;
+
+
+BEGIN {
+ plan(tests => 102) ;
+}
+
+
+my $t = new types2() ;
+
+{
+ my $max = undef ;
+ my $min = undef ;
+
+ $max = 127 ;
+ $min = -128 ;
+ ok($t->_byte(undef) == 1) ;
+ ok($t->_byte(0) == 1) ;
+ ok($t->_byte($max - 1) == $max) ;
+ ok($t->_byte("$min") == $min + 1) ;
+ eval {$t->_byte($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_byte($min - 1)} ; ok($@, qr/out of range/) ;
+ ok($t->_Byte(undef) == 0) ;
+ ok($t->_Byte(0) == 0) ;
+ ok($t->_Byte($max) == $max) ;
+ ok($t->_Byte("$min") == $min) ;
+ eval {$t->_Byte($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_Byte($min - 1)} ; ok($@, qr/out of range/) ;
+
+ $max = 32767 ;
+ $min = -32768 ;
+ ok($t->_short(undef) == 1) ;
+ ok($t->_short(0) == 1) ;
+ ok($t->_short($max - 1) == $max) ;
+ ok($t->_short("$min") == $min + 1) ;
+ eval {$t->_short($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_short($min - 1)} ; ok($@, qr/out of range/) ;
+ ok($t->_Short(undef) == 0) ;
+ ok($t->_Short(0) == 0) ;
+ ok($t->_Short($max) == $max) ;
+ ok($t->_Short("$min") == $min) ;
+ eval {$t->_Short($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_Short($min - 1)} ; ok($@, qr/out of range/) ;
+
+ $max = 2147483647 ;
+ $min = -2147483648 ;
+ ok($t->_int(undef) == 1) ;
+ ok($t->_int(0) == 1) ;
+ ok($t->_int($max - 1) == $max) ;
+ ok($t->_int("$min") == $min + 1) ;
+ eval {$t->_int($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_int($min - 1)} ; ok($@, qr/out of range/) ;
+ ok($t->_Integer(undef) == 0) ;
+ ok($t->_Integer(0) == 0) ;
+ ok($t->_Integer($max) == $max) ;
+ ok($t->_Integer("$min") == $min) ;
+ eval {$t->_Integer($max + 1)} ; ok($@, qr/out of range/) ;
+ eval {$t->_Integer($min - 1)} ; ok($@, qr/out of range/) ;
+
+ $max = 3.4028235e38 ;
+ $min = -3.4028235e38 ;
+ ok($t->_float(undef) == 1) ;
+ ok($t->_float(0) == 1) ;
+ ok($t->_float($max / 2)) ;
+ ok($t->_float($min / 2)) ;
+ ok($t->_float($max - 1)) ;
+ ok($t->_float("$min")) ;
+ eval {$t->_float($max + $max)} ; ok($@, qr/out of range/) ;
+ eval {$t->_float($min + $min)} ; ok($@, qr/out of range/) ;
+ ok($t->_Float(undef) == 0) ;
+ ok($t->_Float(0) == 0) ;
+ ok($t->_Float($max / 2)) ;
+ ok($t->_Float($min / 2)) ;
+ # Equality tests for such large floating point number are not always reliable
+ ok($t->_Float($max)) ;
+ ok($t->_Float("$min")) ;
+ eval {$t->_Float($max + $max)} ; ok($@, qr/out of range/) ;
+ eval {$t->_Float($min + $min)} ; ok($@, qr/out of range/) ;
+
+ #
+ # Boundary testing for long, double are not predictable enough
+ # to be reliable.
+ #
+ my $val = 123456 ;
+ ok($t->_long(undef) == 1) ;
+ ok($t->_long(0) == 1) ;
+ ok($t->_long($val - 1) == $val) ;
+ ok($t->_long("-$val") == -$val + 1) ;
+ ok($t->_Long(undef) == 0) ;
+ ok($t->_Long(0) == 0) ;
+ ok($t->_Long($val) == $val) ;
+ ok($t->_Long("-$val") == -$val) ;
+
+ $val = 123456.789 ;
+ ok($t->_double(undef) == 1) ;
+ ok($t->_double(0) == 1) ;
+ ok($t->_double($val - 1) == $val) ;
+ ok($t->_double("-$val") == -$val + 1) ;
+ ok($t->_Double(undef) == 0) ;
+ ok($t->_Double(0) == 0) ;
+ ok($t->_Double($val) == $val) ;
+ ok($t->_Double("-$val") == -$val) ;
+
+
+ # Number is forced to Double
+ ok($t->_Number(undef) == 0) ;
+ ok($t->_Number(0) == 0) ;
+ ok($t->_Number($val) == $val) ;
+ ok($t->_Number("-$val") == -$val) ;
+
+ ok(! $t->_boolean(undef)) ;
+ ok(! $t->_boolean(0)) ;
+ ok(! $t->_boolean("")) ;
+ ok($t->_boolean("true")) ;
+ ok($t->_boolean(1)) ;
+ ok(! $t->_Boolean(undef)) ;
+ ok(! $t->_Boolean(0)) ;
+ ok(! $t->_Boolean("")) ;
+ ok($t->_Boolean("true")) ;
+ ok($t->_Boolean(1)) ;
+
+ ok($t->_char(undef), "\0") ;
+ ok($t->_char(0), "0") ;
+ ok($t->_char("1"), '1') ;
+ eval {$t->_char("10")} ; ok($@, qr/Can't convert/) ; #'
+ ok($t->_Character(undef), "\0") ;
+ ok($t->_Character(0), "0") ;
+ ok($t->_Character("1"), '1') ;
+ eval {$t->_Character("10")} ; ok($@, qr/Can't convert/) ; #'
+
+ ok($t->_String(undef), undef) ;
+ ok($t->_String(0), "0") ;
+ ok($t->_String("string"), 'string') ;
+
+ my $str = "\r\n&&&\r\n\ntre gfd gf$$ b F D&a;t% R f &p;vf\r\r" ;
+ ok($t->_String($str), $str) ;
+
+ ok($t->_StringBuffer(undef), undef) ;
+ ok($t->_StringBuffer(0), "0") ;
+ ok($t->_StringBuffer("stringbuffer"), 'stringbuffer') ;
+
+ # Test if scalars can pass as java.lang.Object.
+ # They should be converted to strings.
+ ok($t->_Object(undef), undef) ;
+ ok($t->_Object(0), "0") ;
+ ok($t->_Object(666) == 666) ;
+ ok($t->_Object("object"), 'object') ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+
+
+__END__
+
+__Java__
+
+class types2 {
+ public types2(){
+ }
+
+ public byte _byte(byte b){
+ return (byte)(b + (byte)1) ;
+ }
+
+ public Byte _Byte(Byte b){
+ return b ;
+ }
+
+ public short _short(short s){
+ return (short)(s + (short)1) ;
+ }
+
+ public Short _Short(Short s){
+ return s ;
+ }
+
+ public int _int(int i){
+ return i + 1 ;
+ }
+
+ public Integer _Integer(Integer i){
+ return i ;
+ }
+
+ public long _long(long l){
+ return l + 1 ;
+ }
+
+ public Long _Long(Long l){
+ return l ;
+ }
+
+ public float _float(float f){
+ return f + 1 ;
+ }
+
+ public Float _Float(Float f){
+ return f ;
+ }
+
+ public double _double(double d){
+ return d + 1 ;
+ }
+
+ public Double _Double(Double d){
+ return d ;
+ }
+
+ public Number _Number(Number n){
+ return n ;
+ }
+
+ public boolean _boolean(boolean b){
+ return b ;
+ }
+
+ public Boolean _Boolean(Boolean b){
+ return b ;
+ }
+
+ public char _char(char c){
+ return c ;
+ }
+
+ public Character _Character(Character c){
+ return c ;
+ }
+
+ public String _String(String s){
+ return s ;
+ }
+
+ public StringBuffer _StringBuffer(StringBuffer sb){
+ return sb ;
+ }
+
+ public Object _Object(Object o){
+ return o ;
+ }
+}
+
+
diff --git a/t/02_primitives_1_4.t b/t/02_primitives_1_4.t
new file mode 100755
index 0000000..4c81f3f
--- /dev/null
+++ b/t/02_primitives_1_4.t
@@ -0,0 +1,72 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+
+package t02_14 ;
+use Inline(
+ Java => qq |
+ class t02_14 {
+ public static boolean got14(){
+ try {
+ Class c = Class.forName("java.lang.CharSequence") ;
+ }
+ catch (ClassNotFoundException cnfe){
+ return false ;
+ }
+ return true ;
+ }
+ }
+ |,
+ NAME => 't02_14',
+) ;
+
+
+package main ;
+BEGIN {
+ my $got14 = t02_14::t02_14->got14() ;
+ if (! $got14){
+ plan(tests => 0) ;
+ exit(0) ;
+ }
+
+ plan(tests => 4) ;
+}
+
+
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+
+
+my $t = new types2_1() ;
+
+{
+ ok($t->_CharSequence(undef), undef) ;
+ ok($t->_CharSequence(0), "0") ;
+ ok($t->_CharSequence("charsequence"), "charsequence") ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+
+
+__END__
+
+__Java__
+
+class types2_1 {
+ public types2_1(){
+ }
+
+ public CharSequence _CharSequence(CharSequence c){
+ return c ;
+ }
+}
+
+
diff --git a/t/03_objects.t b/t/03_objects.t
new file mode 100755
index 0000000..d081ecd
--- /dev/null
+++ b/t/03_objects.t
@@ -0,0 +1,157 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+
+BEGIN {
+ plan(tests => 16) ;
+}
+
+
+# Create some objects
+my $t = new types3() ;
+
+{
+ my $obj1 = new obj13() ;
+ eval {my $obj2 = new obj23()} ; ok($@, qr/No public constructor/) ;
+ my $obj11 = new obj113() ;
+
+ ok($t->_obj1(undef), undef) ;
+ ok($t->_obj1($obj1)->get_data(), "obj1") ;
+ ok($t->_obj11($obj11)->get_data(), "obj11") ;
+ ok($t->_obj1($obj11)->get_data(), "obj11") ;
+ eval {$t->_int($obj1)} ; ok($@, qr/Can't convert (.*) to primitive int/) ;
+ eval {$t->_obj11($obj1)} ; ok($@, qr/is not a kind of/) ;
+
+ # Inner class
+ my $in = new obj13::inner_obj13($obj1) ;
+ ok($in->{data}, "inner") ;
+
+ # Receive an unbound object and send it back
+ my $unb = $t->get_unbound() ;
+ ok($t->send_unbound($unb), "al_elem") ;
+
+ # Unexisting method
+ eval {$t->toto()} ; ok($@, qr/No public method/) ;
+
+ # Method on unbound object
+ eval {$unb->toto()} ; ok($@, qr/Can't call method/) ;
+
+ # Incompatible prototype, 1 signature
+ eval {$t->_obj1(5)} ; ok($@, qr/Can't convert/) ;
+
+ # Incompatible prototype, >1 signature
+ eval {$t->__obj1(5)} ; ok($@, qr/Can't find any signature/) ;
+
+ # Return a scalar hidden in an object.
+ ok($t->_olong(), 12345) ;
+
+ # Pass a non-Java object, a hash ref.
+ my $d = {} ;
+ eval {$t->_Object($d)} ; ok($@, qr/Can't convert/) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+__END__
+
+__Java__
+
+import java.util.* ;
+
+
+class obj13 {
+ String data = "obj1" ;
+
+ public obj13() {
+ }
+
+ public String get_data(){
+ return data ;
+ }
+
+ public class inner_obj13 {
+ public String data = "inner" ;
+
+ public inner_obj13(){
+ }
+ }
+}
+
+class obj113 extends obj13 {
+ String data = "obj11" ;
+
+ public obj113() {
+ }
+
+ public String get_data(){
+ return data ;
+ }
+}
+
+
+class obj23 {
+ String data = "obj2" ;
+
+ obj23() {
+ }
+
+ public String get_data(){
+ return data ;
+ }
+}
+
+
+class types3 {
+ public types3(){
+ }
+
+ public int _int(int i){
+ return i + 1 ;
+ }
+
+ public Object _Object(Object o){
+ return o ;
+ }
+
+ public obj13 _obj1(obj13 o){
+ return o ;
+ }
+
+
+ public obj13 __obj1(obj13 o, int i){
+ return o ;
+ }
+
+
+ public obj13 __obj1(obj13 o){
+ return o ;
+ }
+
+
+ public obj113 _obj11(obj113 o){
+ return o ;
+ }
+
+ public ArrayList get_unbound(){
+ ArrayList al = new ArrayList() ;
+ al.add(0, "al_elem") ;
+
+ return al ;
+ }
+
+ public String send_unbound(ArrayList al){
+ return (String)al.get(0) ;
+ }
+
+ public Object _olong(){
+ return new Long("12345") ;
+ }
+}
diff --git a/t/04_members.t b/t/04_members.t
new file mode 100755
index 0000000..d4fa845
--- /dev/null
+++ b/t/04_members.t
@@ -0,0 +1,144 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+
+BEGIN {
+ plan(tests => 28) ;
+}
+
+
+my $t = new types4() ;
+
+{
+ $t->{_byte} = 123 ;
+ ok($t->{_byte} == 123) ;
+ $t->{_Byte} = 123 ;
+ ok($t->{_Byte} == 123) ;
+
+ $t->{_short} = 123 ;
+ ok($t->{_short} == 123) ;
+ $t->{_Short} = 123 ;
+ ok($t->{_Short} == 123) ;
+
+ $t->{_int} = 123 ;
+ ok($t->{_int} == 123) ;
+ $t->{_Integer} = 123 ;
+ ok($t->{_Integer} == 123) ;
+
+ $t->{_long} = 123 ;
+ ok($t->{_long} == 123) ;
+ $t->{_Long} = 123 ;
+ ok($t->{_Long} == 123) ;
+
+ $t->{_float} = 123.456 ;
+ ok($t->{_float} == 123.456) ;
+ $t->{_Float} = 123.456 ;
+ ok($t->{_Float} == 123.456) ;
+
+ $t->{_double} = 123.456 ;
+ ok($t->{_double} == 123.456) ;
+ $t->{_Double} = 123.456 ;
+ ok($t->{_Double} == 123.456) ;
+
+ $t->{_boolean} = 1 ;
+ ok($t->{_boolean}) ;
+ $t->{_Boolean} = 1 ;
+ ok($t->{_Boolean}) ;
+
+ $t->{_char} = "a" ;
+ ok($t->{_char}, "a") ;
+ $t->{_Character} = "a" ;
+ ok($t->{_Character}, "a") ;
+
+ $t->{_String} = "string" ;
+ ok($t->{_String}, "string") ;
+ $t->{_StringBuffer} = "stringbuffer" ;
+ ok($t->{_StringBuffer}, "stringbuffer") ;
+
+ my $obj1 = new obj14() ;
+ $t->{_Object} = $obj1 ;
+ ok($t->{_Object}->get_data(), "obj1") ;
+ $t->{_Object} = "object" ;
+ ok($t->{_Object}, "object") ;
+
+ $t->{_Object} = undef ;
+ ok($t->{_Object}, undef) ;
+ $t->{_int} = undef ;
+ ok($t->{_int} == 0) ;
+
+ # Receive an unbound object and try to call a member
+ my $unb = $t->get_unbound() ;
+ eval {$unb->{toto} = 1} ; ok($@, qr/Can't set member/) ;
+ eval {my $a = $unb->{toto}} ; ok($@, qr/Can't get member/) ;
+
+ # Unexisting member
+ eval {$t->{toto} = 1} ; ok($@, qr/No public member/) ;
+ eval {my $a = $t->{toto}} ; ok($@, qr/No public member/) ;
+
+ # Incompatible type
+ eval {$t->{_long} = $obj1} ; ok($@, qr/Can't convert/) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+__END__
+
+__Java__
+
+import java.util.* ;
+
+class obj14 {
+ String data = "obj1" ;
+
+ public obj14() {
+ }
+
+ public String get_data(){
+ return data ;
+ }
+}
+
+
+class types4 {
+ public byte _byte ;
+ public Byte _Byte ;
+ public short _short ;
+ public Short _Short ;
+ public int _int ;
+ public Integer _Integer ;
+ public long _long ;
+ public Long _Long ;
+ public float _float ;
+ public Float _Float ;
+ public double _double ;
+ public Double _Double ;
+ public boolean _boolean ;
+ public Boolean _Boolean ;
+ public char _char ;
+ public Character _Character ;
+ public String _String ;
+ public StringBuffer _StringBuffer ;
+ public Object _Object ;
+
+ public types4(){
+ }
+
+ public ArrayList get_unbound(){
+ ArrayList al = new ArrayList() ;
+ al.add(0, "al_elem") ;
+
+ return al ;
+ }
+
+ public String send_unbound(ArrayList al){
+ return (String)al.get(0) ;
+ }
+}
+
diff --git a/t/05_arrays.t b/t/05_arrays.t
new file mode 100755
index 0000000..c3aacf0
--- /dev/null
+++ b/t/05_arrays.t
@@ -0,0 +1,265 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA'
+) ;
+
+BEGIN {
+ plan(tests => 55) ;
+}
+
+
+my $t = new types5() ;
+
+{
+ ok(++($t->_byte([12, 34, 56])->[0]) == 124) ;
+ ok(eq_array($t->_Byte([12, 34, 56]), [12, 34, 56])) ;
+ ok(++($t->_short([12, 34, 56])->[0]) == 124) ;
+ ok(eq_array($t->_Short([12, 34, 56]), [12, 34, 56])) ;
+ ok(++($t->_int([12, 34, 56])->[0]) == 124) ;
+ ok(eq_array($t->_Integer([12, 34, 56]), [12, 34, 56])) ;
+ ok(++($t->_long([12, 34, 56])->[0]) == 124) ;
+ ok(eq_array($t->_Long([12, 34, 56]), [12, 34, 56])) ;
+ ok(++($t->_float([12.34, 5.6, 7])->[0]) == 124.456) ;
+ ok(eq_array($t->_Float([12.34, 5.6, 7]), [12.34, 5.6, 7])) ;
+ ok(++($t->_double([12.34, 5.6, 7])->[0]) == 124.456) ;
+ ok(eq_array($t->_Double([12.34, 5.6, 7]), [12.34, 5.6, 7])) ;
+ ok($t->_boolean([1, 0, "tree"])->[0]) ;
+ ok($t->_Boolean([1, 0])->[0]) ;
+ ok(! $t->_Boolean([1, 0])->[1]) ;
+ ok($t->_char(['a', 'b', 'c'])->[0], "A") ;
+ ok(eq_array($t->_Character(['a', 'b', 'c']), ['a', 'b', 'c'], 1)) ;
+ my $a = $t->_String(["bla", "ble", "bli"]) ;
+ ok($a->[0], "STRING") ;
+ $a->[1] = "wazoo" ;
+ ok($a->[1], "wazoo") ;
+ ok($t->_StringBuffer(["bla", "ble", "bli"])->[0], "STRINGBUFFER") ;
+
+ ok($t->_Object(undef), undef) ;
+ $a = $t->_Object([1, "two", $t]) ;
+ ok($a->[0], "1") ;
+ ok($a->[1], "two") ;
+ ok(UNIVERSAL::isa($a->[2], "main::types5")) ;
+ ok($a->[2]->{data}->[1], "a") ;
+ $a->[2]->{data} = ["1", "2"] ;
+ ok($a->[2]->{data}->[1], 2) ;
+
+ $a->[0]++ ;
+ ok($a->[0], "2") ;
+
+ $a->[1] = "three" ;
+ ok($a->[1], "three") ;
+
+ $a->[2] = "string" ;
+ ok($a->[2], "string") ;
+
+ $a->[0] = $t ;
+ ok(UNIVERSAL::isa($a->[0], "main::types5")) ;
+
+ # Try some multidimensional arrays.
+ $a = $t->_StringString([
+ ["00", "01"],
+ ["10", "11"]
+ ]) ;
+
+ # Try some incomplete multidimensional arrays.
+ $a = $t->_StringString([
+ [undef, "01", "02"],
+ [undef, "11"],
+ undef,
+ ]) ;
+ ok($a->[1]->[0], undef) ;
+
+
+ my $b = $a->[1] ;
+ ok($t->_String($b)->[0], "STRING") ;
+
+ # Arrays of other arrays
+ $a = $t->_StringString([
+ $a->[0],
+ ]) ;
+ ok($a->[0]->[2], "02") ;
+
+ # This is one of the things that won't work.
+ # Try passing an array as an Object.
+ eval {$t->_o(["a", "b", "c"])} ; ok($@, qr/Can't create Java array/) ;
+ ok($t->_o(Inline::Java::coerce(
+ "java.lang.Object",
+ ["a", "b", "c"],
+ "[Ljava.lang.String;"))->[0], "a") ;
+ $t->{o} = Inline::Java::coerce(
+ "java.lang.Object",
+ ["a", "b", "c"],
+ "[Ljava.lang.String;") ;
+ ok($t->{o}->[0], "a") ;
+ $t->{o} = $t->{i} ;
+ ok($t->{o}->[0], "1") ;
+
+ # Mixed types
+ eval {$t->_int(["3", "3456", "cat"])} ; ok($@, qr/Can't convert/) ;
+ ok($t->_Object(["3", "3456", "cat"])->[2], 'cat') ;
+
+ # Badly constructed array
+ eval {$t->_int(["3", [], "cat"])} ; ok($@, qr/Java array contains mixed types/) ;
+ eval {$t->_StringString([["3"], "string"])} ; ok($@, qr/Java array contains mixed types/) ;
+
+ # Invalid operations on arrays.
+ eval {@{$b} = ()} ; ok($@, qr/Operation CLEAR/) ;
+ eval {pop @{$b}} ; ok($@, qr/Operation POP/) ;
+ eval {shift @{$b}} ; ok($@, qr/Operation SHIFT/) ;
+ eval {splice(@{$b}, 0, 1)} ; ok($@, qr/Operation SPLICE/) ;
+ eval {$b->[10] = 5} ; ok($@, qr/out of bounds/) ;
+
+ # Cool stuff on arrays
+ $a = $t->_byte([12, 34, 56]) ;
+ ok(scalar(@{$a}), 3) ;
+ foreach my $e (@{$a}){
+ ok($e =~ /^(123|34|56)$/) ;
+ }
+
+ # Zero length arrays
+ $a = $t->_Byte([]) ;
+ ok(scalar(@$a), 0) ;
+ $a = $t->_StringString([[], []]) ;
+ ok(scalar(@{$a}), 2) ;
+ ok(scalar(@{$a->[0]}), 0) ;
+ ok(scalar(@{$a->[1]}), 0) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+sub eq_array {
+ my $a1 = shift ;
+ my $a2 = shift ;
+ my $eq = shift || 0 ;
+
+ if (scalar(@{$a1}) != scalar(@{$a2})){
+ return 0 ;
+ }
+
+ my $ok = 1 ;
+ for (0..$#{$a1}){
+ if ($eq){
+ $ok = ($a1->[$_] eq $a2->[$_]) ;
+ }
+ else{
+ $ok = ($a1->[$_] == $a2->[$_]) ;
+ }
+ last unless $ok ;
+ }
+
+ return $ok ;
+}
+
+
+__END__
+
+__Java__
+
+
+class types5 {
+ public Object o ;
+ public int i[] = {1, 2, 3} ;
+ public String data[] = {"d", "a", "t", "a"} ;
+ public types5(){
+ }
+
+ public byte[] _byte(byte b[]){
+ b[0] = (byte)123 ;
+ return b ;
+ }
+
+ public Byte[] _Byte(Byte b[]){
+ return b ;
+ }
+
+ public short[] _short(short s[]){
+ s[0] = (short)123 ;
+ return s ;
+ }
+
+ public Short[] _Short(Short s[]){
+ return s ;
+ }
+
+ public int[] _int(int i[]){
+ i[0] = 123 ;
+ return i ;
+ }
+
+ public Integer[] _Integer(Integer i[]){
+ return i ;
+ }
+
+ public long[] _long(long l[]){
+ l[0] = 123 ;
+ return l ;
+ }
+
+ public Long[] _Long(Long l[]){
+ return l ;
+ }
+
+ public float[] _float(float f[]){
+ f[0] = (float)123.456 ;
+ return f ;
+ }
+
+ public Float[] _Float(Float f[]){
+ return f ;
+ }
+
+ public double[] _double(double d[]){
+ d[0] = 123.456 ;
+ return d ;
+ }
+
+ public Double[] _Double(Double d[]){
+ return d ;
+ }
+
+ public boolean[] _boolean(boolean b[]){
+ b[0] = true ;
+ return b ;
+ }
+
+ public Boolean[] _Boolean(Boolean b[]){
+ return b ;
+ }
+
+ public char[] _char(char c[]){
+ c[0] = 'A' ;
+ return c ;
+ }
+
+ public Character[] _Character(Character c[]){
+ return c ;
+ }
+
+ public String[] _String(String s[]){
+ s[0] = "STRING" ;
+ return s ;
+ }
+
+ public String[][] _StringString(String s[][]){
+ return s ;
+ }
+
+ public StringBuffer[] _StringBuffer(StringBuffer sb[]){
+ sb[0] = new StringBuffer("STRINGBUFFER") ;
+ return sb ;
+ }
+
+ public Object[] _Object(Object o[]){
+ return o ;
+ }
+
+ public Object _o(Object o){
+ return o ;
+ }
+}
diff --git a/t/06_static.t b/t/06_static.t
new file mode 100755
index 0000000..660d361
--- /dev/null
+++ b/t/06_static.t
@@ -0,0 +1,84 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+
+BEGIN {
+ plan(tests => 10) ;
+}
+
+
+# Methods
+ok(p06::types6->get("key"), undef) ;
+my $t = new p06::types6("key", "value") ;
+
+{
+ ok($t->get("key"), "value") ;
+
+ # Members
+ ok($p06::types6::i == 5) ;
+ $p06::types6::i = 7 ;
+ ok($t->{i} == 7) ;
+
+ my $t2 = new p06::types6("key2", "value2") ;
+ my $hm = $p06::types6::hm ;
+ $p06::types6::hm = undef ;
+ ok(p06::types6->get($hm, "key2"), "value2") ;
+
+ $p06::types6::hm = $hm ;
+ ok($t2->get("key2"), "value2") ;
+
+ # Calling an instance method without an object reference
+ eval {p06::types6->set()} ; ok($@, qr/must be called from an object reference/) ;
+
+ # Put in back like before...
+ $p06::types6::i = 5 ;
+ ok($p06::types6::i == 5) ;
+ my $tt = new p06::types6("key", undef) ;
+ ok($tt->get("key"), undef) ;
+}
+
+# Since $types::hm was returned to the Perl space, it was registered in the object
+# HashMap.
+ok($t->__get_private()->{proto}->ObjectCount(), 2) ;
+
+
+__END__
+
+__Java__
+
+
+// package test
+package p06 ;
+
+
+import java.util.* ;
+
+
+public class types6 {
+ public static int i = 5 ;
+ public static HashMap hm = new HashMap() ;
+
+ public types6(String k, String v){
+ hm.put(k, v) ;
+ }
+
+ public static String get(String k){
+ return (String)hm.get(k) ;
+ }
+
+ public static String get(HashMap h, String k){
+ return (String)h.get(k) ;
+ }
+
+ public String set(){
+ return "set" ;
+ }
+}
+
diff --git a/t/07_polymorph.t b/t/07_polymorph.t
new file mode 100644
index 0000000..ed9d0e9
--- /dev/null
+++ b/t/07_polymorph.t
@@ -0,0 +1,161 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+ STUDY => ['java.util.HashMap', 'java.lang.String'],
+ AUTOSTUDY => 1,
+) ;
+
+use Inline::Java qw(cast coerce) ;
+
+
+BEGIN {
+ plan(tests => 24) ;
+}
+
+
+my $t = new types7() ;
+
+{
+ my $t1 = new t17() ;
+
+ ok($t->func(5), "int") ;
+ ok($t->func(coerce("char", 5)), "char") ;
+ ok($t->func(55), "int") ;
+ ok($t->func("str"), "string") ;
+ ok($t->func(coerce("java.lang.StringBuffer", "str")), "stringbuffer") ;
+
+ ok($t->f($t->{hm}), "hashmap") ;
+ ok($t->f(cast("java.lang.Object", $t->{hm})), "object") ;
+
+ ok($t->f(["a", "b", "c"]), "string[]") ;
+
+ ok($t->f(["12.34", "45.67"]), "double[]") ;
+ ok($t->f(coerce("java.lang.Object", ['a'], "[Ljava.lang.String;")), "object") ;
+
+ eval {$t->func($t1)} ; ok($@, qr/Can't find any signature/) ;
+ eval {$t->func(cast("int", $t1))} ; ok($@, qr/Can't cast (.*) to a int/) ;
+
+ my $t2 = new t27() ;
+ ok($t2->f($t2), "t1") ;
+ ok($t1->f($t2), "t1") ;
+ ok($t2->f($t1), "t2") ;
+ ok($t2->f(cast("t17", $t2)), "t2") ;
+
+ ok($t2->f($t1), "t2") ;
+
+ # Here we should always get the more specific stuff
+ ok($t2->{i}, 7) ;
+ ok($t2->{j}, 3.1416) ;
+
+ # So this should fail
+ eval {$t2->{j} = "string"} ; ok($@, qr/Can't convert/) ;
+
+ # Interfaces
+ my $al = $t1->get_al() ;
+ ok(0, $t1->count($al)) ;
+
+ my $hm = new java::util::HashMap() ;
+ $hm->put('key', 'value') ;
+ my $a = $hm->entrySet()->toArray() ;
+ foreach my $e (@{$a}){
+ ok(cast('java.util.Map$Entry', $e)->getKey(), 'key') ;
+ }
+
+ my $str = new java::lang::String('test') ;
+ ok($str, 'test') ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+__END__
+
+__Java__
+
+
+import java.util.* ;
+
+class t17 {
+ public int i = 5 ;
+ public String j = "toto" ;
+
+ public t17(){
+ }
+
+ public String f(t27 o){
+ return "t1" ;
+ }
+
+ public void n(){
+ }
+
+ public ArrayList get_al(){
+ return new ArrayList() ;
+ }
+
+ public int count(Collection c){
+ return c.size() ;
+ }
+}
+
+
+class t27 extends t17 {
+ public int i = 7 ;
+ public double j = 3.1416 ;
+
+ public t27(){
+ }
+
+ public String f(t17 o){
+ return "t2" ;
+ }
+
+ public void n(){
+ }
+}
+
+
+class types7 {
+ public HashMap hm = new HashMap() ;
+
+ public types7(){
+ }
+
+ public String func(String o){
+ return "string" ;
+ }
+
+ public String func(StringBuffer o){
+ return "stringbuffer" ;
+ }
+
+ public String func(int o){
+ return "int" ;
+ }
+
+ public String func(char o){
+ return "char" ;
+ }
+
+ public String f(HashMap o){
+ return "hashmap" ;
+ }
+
+ public String f(Object o){
+ return "object" ;
+ }
+
+ public String f(String o[]){
+ return "string[]" ;
+ }
+
+ public String f(double o[]){
+ return "double[]" ;
+ }
+}
+
diff --git a/t/08_study.t b/t/08_study.t
new file mode 100644
index 0000000..5ff1fc7
--- /dev/null
+++ b/t/08_study.t
@@ -0,0 +1,117 @@
+package study ;
+
+use strict ;
+use Test ;
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+# There once was a bug with importing code twice.
+use Inline(
+ Java => 'STUDY',
+ AUTOSTUDY => 1,
+ STUDY => ['t.types'],
+ CLASSPATH => '.',
+) ;
+use Inline(
+ Java => 'STUDY',
+ AUTOSTUDY => 1,
+ STUDY => ['t.types'],
+ CLASSPATH => '.',
+) ;
+
+
+package toto ;
+
+use Inline(
+ Java => 'STUDY',
+ AUTOSTUDY => 1,
+ STUDY => ['t.types'],
+ CLASSPATH => '.',
+) ;
+use Inline(
+ Java => 'STUDY',
+ AUTOSTUDY => 1,
+ STUDY => ['t.types'],
+ CLASSPATH => '.',
+ PACKAGE => 'main',
+) ;
+package study ;
+
+use Inline::Java qw(study_classes) ;
+
+
+
+BEGIN {
+ plan(tests => 11) ;
+}
+
+study_classes([
+ 't.no_const'
+]) ;
+
+my $t = new study::t::types() ;
+
+{
+ ok($t->func(), "study") ;
+ ok($t->hm()->get("key"), "value") ;
+
+ my $nc = new study::t::no_const() ;
+ ok($nc->{i}, 5) ;
+
+ my $a = new study::study::a8() ;
+ ok($a->{i}, 50) ;
+ ok($a->truth()) ;
+ ok($a->sa()->[1], 'titi') ;
+ ok($a->sb()->[0]->get('toto'), 'titi') ;
+ ok($a->sb()->[1]->get('error'), undef) ;
+
+ my $toto_t = new toto::t::types() ;
+ ok(1) ;
+ my $main_t = new t::types() ;
+ ok(1) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+__DATA__
+
+__Java__
+
+// Use a public class
+package study ;
+
+import java.util.* ;
+
+public class a8 {
+ public int i = 50 ;
+
+ public a8(){
+ }
+
+ public boolean truth(){
+ return true ;
+ }
+
+ public String [] sa(){
+ String a[] = {"toto", "titi"} ;
+ return a ;
+ }
+
+ public HashMap [] sb(){
+ HashMap h1 = new HashMap() ;
+ HashMap h2 = new HashMap() ;
+ h1.put("toto", "titi") ;
+ h2.put("tata", "tete") ;
+
+ HashMap a[] = {h1, h2} ;
+ return a ;
+ }
+}
+
diff --git a/t/09_usages.t b/t/09_usages.t
new file mode 100755
index 0000000..07cfa35
--- /dev/null
+++ b/t/09_usages.t
@@ -0,0 +1,70 @@
+use strict ;
+use Test ;
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+
+BEGIN {
+ plan(tests => 6) ;
+}
+
+
+
+package t09::p1 ;
+use Inline(
+ Java => qq |
+ class t09p1 {
+ public static String name = "p1" ;
+
+ public t09p1(){
+ }
+
+ public static String get_prop(int n){
+ return System.getProperty("prop" + n) ;
+ }
+ }
+ |,
+ NAME => 't09::p1',
+ EXTRA_JAVA_ARGS => '-Dprop1="c:\program files" -Dprop3=42',
+) ;
+
+
+package t09::p2 ;
+use Inline(
+ Java => qq |
+ class t09p2 {
+ public static String name = "p2" ;
+ }
+ |,
+ NAME => 't09::p2',
+) ;
+
+
+
+package t09::p3 ;
+Inline->bind(
+ Java => qq |
+ class t09p3 {
+ public static String name = "p3" ;
+
+ }
+ |,
+ NAME => 't09::p3',
+) ;
+
+
+package main ;
+
+my $t = new t09::p1::t09p1() ;
+
+{
+ ok($t->{name}, "p1") ;
+ ok($t->get_prop(1), 'c:\program files') ;
+ ok($t->get_prop(3), 42) ;
+ ok($t09::p2::t09p2::name . $t09::p3::t09p3::name, "p2p3") ;
+ ok($t09::p2::t09p2::name . $t09::p3::t09p3::name, "p2p3") ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
diff --git a/t/10_1_shared_alone.t b/t/10_1_shared_alone.t
new file mode 100755
index 0000000..49b5124
--- /dev/null
+++ b/t/10_1_shared_alone.t
@@ -0,0 +1,35 @@
+use strict ;
+use Test ;
+
+
+BEGIN {
+ if ($ENV{PERL_INLINE_JAVA_JNI}){
+ plan(tests => 0) ;
+ exit ;
+ }
+ else{
+ plan(tests => 4) ;
+ }
+}
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 't/shared.java',
+ SHARED_JVM => 1,
+ PORT => 17891
+) ;
+
+
+my $t = new t10() ;
+
+{
+ ok($t->{i}, 5) ;
+ ok(! Inline::Java::i_am_JVM_owner()) ;
+ Inline::Java::capture_JVM() ;
+ ok(Inline::Java::i_am_JVM_owner()) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
diff --git a/t/10_5_shared_fork.t b/t/10_5_shared_fork.t
new file mode 100755
index 0000000..049f52f
--- /dev/null
+++ b/t/10_5_shared_fork.t
@@ -0,0 +1,72 @@
+package t10 ;
+
+use strict ;
+use Test ;
+
+
+BEGIN {
+ # Leave previous server enough time to die...
+ sleep(1) ;
+ require Inline::Java::Portable ;
+ if ($ENV{PERL_INLINE_JAVA_JNI}){
+ plan(tests => 0) ;
+ exit ;
+ }
+ elsif (! Inline::Java::Portable::portable('GOT_FORK')){
+ plan(tests => 0) ;
+ exit ;
+ }
+ else{
+ $t10::nb = 5 ;
+ plan(tests => $t10::nb + 3) ;
+ }
+}
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 't/shared.java',
+ SHARED_JVM => 1,
+ PORT => 17891,
+ NAME => 't10',
+) ;
+
+
+$t10::t10::i = 0 ;
+
+my $nb = $t10::nb ;
+my $sum = (($nb) * ($nb + 1)) / 2 ;
+for (my $i = 0 ; $i < $nb ; $i++){
+ if (! fork()){
+ do_child($i) ;
+ }
+}
+
+
+# Wait for kids to finish
+for (my $i = 0 ; $i < $nb ; $i++){
+ wait() ;
+ ok(1) ;
+}
+
+ok($t10::t10::i, $sum) ;
+
+# Bring down the JVM
+ok(! Inline::Java::i_am_JVM_owner()) ;
+Inline::Java::capture_JVM() ;
+ok(Inline::Java::i_am_JVM_owner()) ;
+
+
+sub do_child {
+ my $i = shift ;
+
+ Inline::Java::reconnect_JVM() ;
+
+ my $t = new t10::t10() ;
+ for (my $j = 0 ; $j <= $i ; $j++){
+ $t->incr() ;
+ }
+ exit ;
+}
diff --git a/t/10_6_shared_sim.t b/t/10_6_shared_sim.t
new file mode 100644
index 0000000..128b865
--- /dev/null
+++ b/t/10_6_shared_sim.t
@@ -0,0 +1,69 @@
+package t10 ;
+
+use strict ;
+use Test ;
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+
+BEGIN {
+ # Leave previous server enough time to die...
+ sleep(1) ;
+ if ($ENV{PERL_INLINE_JAVA_JNI}){
+ plan(tests => 0) ;
+ exit ;
+ }
+ else{
+ plan(tests => 7) ;
+ }
+}
+
+
+
+Inline->bind(
+ Java => 't/shared.java',
+ SHARED_JVM => 1,
+ PORT => 17891,
+ NAME => 't10',
+) ;
+{
+ my $t = new t10::t10() ;
+ ok($t->{i}++, 5) ;
+ ok(! Inline::Java::i_am_JVM_owner()) ;
+}
+my $JVM1 = Inline::Java::__get_JVM() ;
+$JVM1->{destroyed} = 1 ;
+Inline::Java::__clear_JVM() ;
+
+
+Inline->bind(
+ Java => 't/shared.java',
+ SHARED_JVM => 1,
+ PORT => 17891,
+ NAME => 't10',
+) ;
+{
+ my $t = new t10::t10() ;
+ ok($t->{i}++, 6) ;
+ ok(! Inline::Java::i_am_JVM_owner()) ;
+}
+my $JVM2 = Inline::Java::__get_JVM() ;
+$JVM2->{destroyed} = 1 ;
+Inline::Java::__clear_JVM() ;
+
+
+Inline->bind(
+ Java => 't/shared.java',
+ SHARED_JVM => 1,
+ PORT => 17891,
+ NAME => 't10',
+) ;
+{
+ my $t = new t10::t10() ;
+ ok($t->{i}, 7) ;
+ ok(! Inline::Java::i_am_JVM_owner()) ;
+ Inline::Java::capture_JVM() ;
+ ok(Inline::Java::i_am_JVM_owner()) ;
+}
diff --git a/t/11_exceptions.t b/t/11_exceptions.t
new file mode 100755
index 0000000..eb1f750
--- /dev/null
+++ b/t/11_exceptions.t
@@ -0,0 +1,160 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+use Inline::Java qw(caught) ;
+
+
+BEGIN {
+ # Leave previous server enough time to die...
+ sleep(1) ;
+ plan(tests => 8) ;
+}
+
+
+my $t = new t9(0) ;
+
+{
+ my $msg = '' ;
+ eval {
+ $t->f() ;
+ } ;
+ if ($@){
+ if (caught("java.io.IOException")){
+ $msg = $@->getMessage() . "io" ;
+ }
+ elsif (caught("java.lang.Exception")){
+ $msg = $@->getMessage() ;
+ }
+ else {
+ die $@ ;
+ }
+ } ;
+ ok($msg, "from fio") ;
+
+ $msg = '' ;
+ eval {
+ $t->f() ;
+ } ;
+ if ($@){
+ if (caught("java.lang.Throwable")){
+ $msg = $@->getMessage() ;
+ }
+ elsif (caught("java.io.IOException")){
+ $msg = $@->getMessage() . "io" ;
+ }
+ else {
+ die $@ ;
+ }
+ }
+ ok($msg, "from f") ;
+
+
+ $msg = '' ;
+ eval {
+ die("not e\n") ;
+ } ;
+ if ($@){
+ if (caught("java.lang.Exception")){
+ $msg = $@->getMessage() ;
+ }
+ else {
+ $msg = $@ ;
+ }
+ }
+ ok($msg, "not e\n") ;
+
+
+ my $e = $t->f2() ;
+ ok($e->getMessage(), "from f2") ;
+
+
+ $msg = '' ;
+ eval {
+ my $t2 = new t9(1) ;
+ } ;
+ if ($@){
+ if (caught("java.lang.Exception")){
+ $msg = $@->getMessage() ;
+ }
+ else{
+ die $@ ;
+ }
+ }
+ ok($msg, "from const") ;
+
+ # Undeclared exception, java.lang.NullPointerException
+ $msg = '' ;
+ eval {
+ my $t2 = new t9(0) ;
+ $t2->len(undef) ;
+ } ;
+ if ($@){
+ if (caught("java.lang.NullPointerException")){
+ $msg = "null" ;
+ }
+ else {
+ die $@ ;
+ }
+ }
+ ok($msg, "null") ;
+
+ # Undeclared exception, java.lang.NullPointerException
+ $msg = '' ;
+ eval {
+ my $t2 = new t9(0) ;
+ $t2->len(undef) ;
+ } ;
+ if ($@){
+ if (caught("java.lang.IOException")){
+ $msg = "io" ;
+ }
+ elsif (caught("java.lang.Exception")){
+ $msg = "null" ;
+ }
+ else{
+ die $@ ;
+ }
+ }
+ ok($msg, "null") ;
+
+ # Make sure the last exception is not lying around...
+ $@ = undef ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+__END__
+
+__Java__
+
+
+import java.io.* ;
+
+class t9 {
+ public t9(boolean t) throws Exception {
+ if (t){
+ throw new Exception("from const") ;
+ }
+ }
+
+ public String f() throws IOException {
+ throw new IOException("from f") ;
+ }
+
+ public IOException f2() {
+ return new IOException("from f2") ;
+ }
+
+ public int len(String s) {
+ return s.length() ;
+ }
+}
+
diff --git a/t/12_1_callbacks.t b/t/12_1_callbacks.t
new file mode 100755
index 0000000..e479506
--- /dev/null
+++ b/t/12_1_callbacks.t
@@ -0,0 +1,422 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline (
+ Java => 'DATA',
+ STUDY => ['org.perl.inline.java.InlineJavaPerlCaller'],
+ STARTUP_DELAY => 20,
+) ;
+
+use Inline::Java qw(caught) ;
+
+
+BEGIN {
+ my $cnt = 37 ;
+ plan(tests => $cnt) ;
+}
+
+my $mtc_cnt = 0 ;
+my $mtc_mode = 0 ;
+my $t = new t15() ;
+
+{
+ eval {
+ ok($t->add(5, 6), 11) ;
+ ok($t->add_via_perl(5, 6), 11) ;
+ my $a = $t->incr_via_perl([7, 6, 5]) ;
+ ok($a->[1], 7) ;
+ $a = $t->incr_via_perl_ctx($a) ;
+ ok($a->[1], 8) ;
+ ok($t->mul(5, 6), 30) ;
+ ok($t->mul_via_perl(5, 6), 30) ;
+ ok($t->silly_mul(3, 2), 6) ;
+ ok($t->silly_mul_via_perl(3, 2), 6) ;
+
+ ok(add_via_java(3, 4), 7) ;
+
+ ok($t->add_via_perl_via_java(3, 4), 7) ;
+ ok($t->silly_mul_via_perl_via_java(10, 9), 90) ;
+
+ ok(t15->add_via_perl_via_java_t($t, 6, 9), 15) ;
+
+ ok($t->cat_via_perl("Inline", "Java"), "InlineJava") ;
+
+ ok($t->perl_static(), 'main->static') ;
+
+ ok(twister(20, 0, 0), "return perl twister") ;
+ ok($t->twister(20, 0, 0), "return java twister") ;
+
+ eval {twister(20, 0, 1)} ; ok($@, qr/^throw perl twister/) ;
+
+ my $msg = '' ;
+ eval {$t->twister(20, 0, 1)} ;
+ if ($@) {
+ if (caught('t15$OwnException')){
+ $msg = $@->getMessage() ;
+ }
+ else{
+ die $@ ;
+ }
+ }
+ ok($msg, "throw java twister") ;
+
+ eval {$t->bug()} ; ok($@, qr/^bug/) ;
+
+ ok($t->perlt()->add(5, 6), 11) ;
+
+ eval {$t->perldummy()} ; ok($@, qr/Can't propagate non-/) ; #'
+
+ $t->mtc_callbacks(20) ;
+ $t->StartCallbackLoop() ;
+ ok($mtc_cnt, 20) ;
+
+ $mtc_cnt = -30 ;
+ $t->mtc_callbacks2(50) ;
+ $t->StartCallbackLoop() ;
+ ok($mtc_cnt, 20) ;
+
+ $mtc_cnt = 0 ;
+ $mtc_mode = 1 ;
+ $t->mtc_callbacks2(20) ;
+ $t->StartCallbackLoop() ;
+ ok($mtc_cnt, 20) ;
+
+ $mtc_cnt = 0 ;
+ $mtc_mode = 2 ;
+ $t->mtc_callbacks2(20) ;
+ $t->OpenCallbackStream() ;
+ while (($mtc_cnt < 20)&&($t->WaitForCallback(-1) > 0)){
+ $t->ProcessNextCallback() ;
+ }
+ ok($mtc_cnt, 20) ;
+
+ $mtc_cnt = 0 ;
+ $mtc_mode = 2 ;
+ $t->mtc_callbacks2(10) ;
+ while ($t->WaitForCallback(3.1416) > 0){
+ ok($t->WaitForCallback(0) >= 1) ;
+ $t->ProcessNextCallback() ;
+ }
+ ok($mtc_cnt, 10) ;
+
+ # Unfortunately we can't test this because the Thread.run method doesn't allow us
+ # to throw any exceptions...
+ # $t->mtc_callbacks_error() ;
+ } ;
+ if ($@){
+ if (caught("java.lang.Throwable")){
+ $@->printStackTrace() ;
+ die("Caught Java Exception") ;
+ }
+ else{
+ die $@ ;
+ }
+ }
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+sub add {
+ my $i = shift ;
+ my $j = shift ;
+
+ return $i + $j ;
+}
+
+
+sub incr {
+ my $ija = shift ;
+
+ for (my $i = 0 ; $i < $ija->length() ; $i++){
+ $ija->[$i]++ ;
+ }
+
+ return wantarray ? @{$ija} : $ija ;
+}
+
+
+sub mul {
+ my $i = shift ;
+ my $j = shift ;
+
+ return $i * $j ;
+}
+
+
+sub cat {
+ my $i = shift ;
+ my $j = shift ;
+
+ return $i . $j ;
+}
+
+
+sub add_via_java {
+ my $i = shift ;
+ my $j = shift ;
+
+ return $t->add($i, $j) ;
+}
+
+
+sub add_via_java_t {
+ my $_t = shift ;
+ my $i = shift ;
+ my $j = shift ;
+
+ return $_t->add($i, $j) ;
+}
+
+
+sub twister {
+ my $max = shift ;
+ my $cnt = shift ;
+ my $explode = shift ;
+
+ if ($cnt == $max){
+ if ($explode){
+ die("throw perl twister") ;
+ }
+ else{
+ return "return perl twister" ;
+ }
+ }
+ else{
+ return $t->twister($max, $cnt+1, $explode) ;
+ }
+}
+
+
+sub t {
+ return $t ;
+}
+
+
+sub dummy {
+ die(bless({}, "Inline::Java::dummy")) ;
+}
+
+
+
+sub mt_callback {
+ my $pc = shift ;
+ $mtc_cnt++ ;
+ if ($mtc_cnt >= 20){
+ if ($mtc_mode == 0){
+ $pc->StopCallbackLoop() ;
+ }
+ elsif ($mtc_mode == 1){
+ my $o = new org::perl::inline::java::InlineJavaPerlCaller() ;
+ $o->StopCallbackLoop() ;
+ }
+ }
+}
+
+
+sub static_method {
+ my $class = shift ;
+
+ return 'main->static' ;
+}
+
+
+__END__
+
+__Java__
+
+
+import java.io.* ;
+import org.perl.inline.java.* ;
+
+class t15 extends InlineJavaPerlCaller {
+ class OwnException extends Exception {
+ OwnException(String msg){
+ super(msg) ;
+ }
+ }
+
+ class OwnThread extends Thread {
+ InlineJavaPerlCaller pc = null ;
+ boolean error = false ;
+
+ OwnThread(InlineJavaPerlCaller _pc, int nb, boolean err){
+ super("CALLBACK-TEST-THREAD-#" + nb) ;
+ pc = _pc ;
+ error = err ;
+ }
+
+ public void run(){
+ try {
+ if (! error){
+ pc.CallPerlSub("main::mt_callback", new Object [] {pc}) ;
+ }
+ else {
+ new InlineJavaPerlCaller() ;
+ }
+ }
+ catch (InlineJavaException ie){
+ ie.printStackTrace() ;
+ }
+ catch (InlineJavaPerlException ipe){
+ ipe.printStackTrace() ;
+ }
+ }
+ }
+
+ public t15() throws InlineJavaException {
+ }
+
+ public int add(int a, int b){
+ return a + b ;
+ }
+
+ public int mul(int a, int b){
+ return a * b ;
+ }
+
+ public int silly_mul(int a, int b){
+ int ret = 0 ;
+ for (int i = 0 ; i < b ; i++){
+ ret = add(ret, a) ;
+ }
+ return a * b ;
+ }
+
+ public int silly_mul_via_perl(int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ int ret = 0 ;
+ for (int i = 0 ; i < b ; i++){
+ ret = add_via_perl(ret, a) ;
+ }
+ return ret ;
+ }
+
+ public int add_via_perl(int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ String val = (String)CallPerlSub("main::add",
+ new Object [] {new Integer(a), new Integer(b)}) ;
+
+ return new Integer(val).intValue() ;
+ }
+
+ public int [] incr_via_perl(int a[]) throws InlineJavaException, InlineJavaPerlException {
+ int [] r = (int [])CallPerlSub("main::incr",
+ new Object [] {a}, a.getClass()) ;
+
+ return r ;
+ }
+
+ public int [] incr_via_perl_ctx(int a[]) throws InlineJavaException, InlineJavaPerlException {
+ int [] r = (int [])CallPerlSub("@main::incr",
+ new Object [] {a}, a.getClass()) ;
+
+ return r ;
+ }
+
+ public void death_via_perl() throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaPerlCaller c = new InlineJavaPerlCaller() ;
+ c.CallPerlSub("main::death", null) ;
+ }
+
+ public void except() throws InlineJavaException, InlineJavaPerlException {
+ throw new InlineJavaPerlException("test") ;
+ }
+
+ public int mul_via_perl(int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ String val = (String)CallPerlSub("main::mul",
+ new Object [] {new Integer(a), new Integer(b)}) ;
+
+ return new Integer(val).intValue() ;
+ }
+
+ public int add_via_perl_via_java(int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ String val = (String)CallPerlSub("main::add_via_java",
+ new Object [] {new Integer(a), new Integer(b)}) ;
+
+ return new Integer(val).intValue() ;
+ }
+
+ static public int add_via_perl_via_java_t(t15 t, int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaPerlCaller c = new InlineJavaPerlCaller() ;
+ String val = (String)c.CallPerlSub("main::add_via_java_t",
+ new Object [] {t, new Integer(a), new Integer(b)}) ;
+
+ return new Integer(val).intValue() ;
+ }
+
+
+ public int silly_mul_via_perl_via_java(int a, int b) throws InlineJavaException, InlineJavaPerlException {
+ int ret = 0 ;
+ for (int i = 0 ; i < b ; i++){
+ String val = (String)CallPerlSub("add_via_java",
+ new Object [] {new Integer(ret), new Integer(a)}) ;
+ ret = new Integer(val).intValue() ;
+ }
+ return ret ;
+ }
+
+
+ public String cat_via_perl(String a, String b) throws InlineJavaException, InlineJavaPerlException {
+ String val = (String)CallPerlSub("cat",
+ new Object [] {a, b}) ;
+
+ return val ;
+ }
+
+ public String twister(int max, int cnt, int explode) throws InlineJavaException, InlineJavaPerlException, OwnException {
+ if (cnt == max){
+ if (explode > 0){
+ throw new OwnException("throw java twister") ;
+ }
+ else{
+ return "return java twister" ;
+ }
+ }
+ else{
+ return (String)CallPerlSub("twister",
+ new Object [] {new Integer(max), new Integer(cnt+1), new Integer(explode)}) ;
+ }
+ }
+
+
+ public void bug() throws InlineJavaException {
+ throw new InlineJavaException("bug") ;
+ }
+
+
+ public Object perlt() throws InlineJavaException, InlineJavaPerlException, OwnException {
+ return CallPerlSub("t", null) ;
+ }
+
+
+ public Object perl_static() throws InlineJavaException, InlineJavaPerlException, OwnException {
+ return CallPerlStaticMethod("main", "static_method", null) ;
+ }
+
+
+ public Object perldummy() throws InlineJavaException, InlineJavaPerlException, OwnException {
+ return CallPerlSub("dummy", null) ;
+ }
+
+ public void mtc_callbacks(int n){
+ for (int i = 0 ; i < n ; i++){
+ OwnThread t = new OwnThread(this, i, false) ;
+ t.start() ;
+ }
+ }
+
+ public void mtc_callbacks2(int n) throws InlineJavaException, InlineJavaPerlException {
+ for (int i = 0 ; i < n ; i++){
+ InlineJavaPerlCaller pc = new InlineJavaPerlCaller() ;
+ OwnThread t = new OwnThread(pc, i, false) ;
+ t.start() ;
+ }
+ }
+
+ public void mtc_callbacks_error(){
+ OwnThread t = new OwnThread(this, 0, true) ;
+ t.start() ;
+ }
+}
diff --git a/t/13_handles.t b/t/13_handles.t
new file mode 100755
index 0000000..9abec99
--- /dev/null
+++ b/t/13_handles.t
@@ -0,0 +1,95 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test';
+
+use Inline(
+ Java => 'DATA',
+) ;
+
+use Inline::Java qw(caught) ;
+
+
+BEGIN {
+ # Leave previous server enough time to die...
+ sleep(1) ;
+ plan(tests => 12) ;
+}
+
+
+my $t = new t13() ;
+
+{
+ my $f = File::Spec->catfile("t", "t13.txt") ;
+ my $o = t13->getWriter($f) ;
+ my $h = new Inline::Java::Handle($o) ;
+ for (my $i = 1 ; $i <= 10 ; $i++){
+ print $h "$i\n" ;
+ }
+ close($h) ;
+ ok(1) ;
+
+ $o = t13->getReader($f) ;
+ $h = new Inline::Java::Handle($o) ;
+ for (my $i = 1 ; $i <= 10 ; $i++){
+ my $l = <$h> ;
+ ok($l, $i) ;
+ }
+ ok(! defined(<$h>)) ;
+}
+
+
+# It seems that filehandle destruction leaks on certain version
+# of Perl. We will change this test to a warning.
+if ($t->__get_private()->{proto}->ObjectCount() != 1){
+ warn "\nWARNING: Your Perl version ($]) seems to leak tied filehandles. Using\n" .
+ "Inline::Java::Handle objects will result in memory leaks both in Perl\n" .
+ "and in Java\n" ;
+}
+
+
+__END__
+
+__Java__
+
+
+import java.io.* ;
+
+class t13 {
+ public t13(){
+ }
+
+ public static Reader getReader(String file) throws FileNotFoundException {
+ return new FileReader(file) ;
+ }
+
+ public static Reader getBufferedReader(String file) throws FileNotFoundException {
+ return new BufferedReader(new FileReader(file)) ;
+ }
+
+ public static InputStream getInputStream(String file) throws FileNotFoundException {
+ return new FileInputStream(file) ;
+ }
+
+ public static InputStream getBufferedInputStream(String file) throws FileNotFoundException {
+ return new BufferedInputStream(new FileInputStream(file)) ;
+ }
+
+ public static Writer getWriter(String file) throws IOException {
+ return new FileWriter(file) ;
+ }
+
+ public static Writer getBufferedWriter(String file) throws IOException {
+ return new BufferedWriter(new FileWriter(file)) ;
+ }
+
+ public static OutputStream getOutputStream(String file) throws FileNotFoundException {
+ return new FileOutputStream(file) ;
+ }
+
+ public static OutputStream getBufferedOutputStream(String file) throws FileNotFoundException {
+ return new BufferedOutputStream(new FileOutputStream(file)) ;
+ }
+}
+
diff --git a/t/14_encoding.t b/t/14_encoding.t
new file mode 100755
index 0000000..5228978
--- /dev/null
+++ b/t/14_encoding.t
@@ -0,0 +1,70 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'DATA'
+) ;
+
+
+BEGIN {
+ plan(tests => 9) ;
+}
+
+
+my $t = new t14() ;
+
+{
+ ok($t->_String("A"), "A") ;
+ ok($t->_String("\x{41}"), "A") ;
+ ok($t->_String("A"), "\x{41}") ;
+
+ # This is E9 (233), which is e acute. Although the byte
+ # E9 is invalid in UTF-8, the character 233 is valid and
+ # all should work out.
+ ok($t->_String("\x{E9}"), "\x{E9}") ;
+ my $a = $t->toCharArray("\x{E9}") ;
+ ok(ord($a->[0]) == 233) ;
+
+ # Send a unicode escape sequence.
+ ok($t->_String("\x{263A}"), "\x{263A}") ;
+
+ # Generate some binary data
+ my $bin = '' ;
+ for (my $i = 0; $i < 256 ; $i++) {
+ my $c = chr($i) ;
+ $bin .= $c ;
+ }
+ ok($t->_String($bin), $bin) ;
+
+ # Mix it up
+ ok($t->_String("$bin\x{E9}\x{263A}"), "$bin\x{E9}\x{263A}") ;
+
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+
+
+__END__
+
+__Java__
+
+class t14 {
+ public t14(){
+ }
+
+ public String _String(String s){
+ return s ;
+ }
+
+
+ public char [] toCharArray(String s){
+ return s.toCharArray() ;
+ }
+}
+
+
diff --git a/t/15_native_doubles.t b/t/15_native_doubles.t
new file mode 100755
index 0000000..0c57454
--- /dev/null
+++ b/t/15_native_doubles.t
@@ -0,0 +1,44 @@
+use strict ;
+use Test ;
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'DATA',
+ NATIVE_DOUBLES => 2,
+) ;
+
+
+BEGIN {
+ plan(tests => 3) ;
+}
+
+
+my $t = new t15() ;
+
+{
+ # Here it is hard to test for accuracy, but either it works or it doesn't...
+ ok($t->_Double(0.056200000000000028) > 0.056) ;
+ ok($t->_Double(0.056200000000000028) < 0.057) ;
+}
+
+ok($t->__get_private()->{proto}->ObjectCount(), 1) ;
+
+
+
+
+__END__
+
+__Java__
+
+class t15 {
+ public t15(){
+ }
+
+ public Double _Double(Double d){
+ return d ;
+ }
+}
+
+
diff --git a/t/99_end.t b/t/99_end.t
new file mode 100644
index 0000000..c081a42
--- /dev/null
+++ b/t/99_end.t
@@ -0,0 +1,19 @@
+use strict ;
+use Test ;
+
+
+BEGIN {
+ plan(tests => 1) ;
+}
+
+
+use Inline Config =>
+ DIRECTORY => './_Inline_test' ;
+
+use Inline (
+ Java => 'STUDY',
+) ;
+
+Inline::Java::capture_JVM() ;
+ok(Inline::Java::i_am_JVM_owner()) ;
+
diff --git a/t/no_const.class b/t/no_const.class
new file mode 100644
index 0000000..98ea5ef
--- /dev/null
+++ b/t/no_const.class
Binary files differ
diff --git a/t/no_const.java b/t/no_const.java
new file mode 100755
index 0000000..08b8c18
--- /dev/null
+++ b/t/no_const.java
@@ -0,0 +1,7 @@
+package t ;
+
+
+public class no_const {
+ public int i = 5 ;
+}
+
diff --git a/t/shared.java b/t/shared.java
new file mode 100755
index 0000000..2409b40
--- /dev/null
+++ b/t/shared.java
@@ -0,0 +1,11 @@
+class t10 {
+ static public int i = 5 ;
+
+ public t10(){
+ }
+
+ static synchronized public void incr(){
+ i++ ;
+ }
+}
+
diff --git a/t/types.class b/t/types.class
new file mode 100644
index 0000000..918c408
--- /dev/null
+++ b/t/types.class
Binary files differ
diff --git a/t/types.java b/t/types.java
new file mode 100755
index 0000000..b13e0b1
--- /dev/null
+++ b/t/types.java
@@ -0,0 +1,20 @@
+package t ;
+
+import java.util.* ;
+
+
+public class types {
+ public types(){
+ }
+
+ public String func(){
+ return "study" ;
+ }
+
+ public HashMap hm(){
+ HashMap hm = new HashMap() ;
+ hm.put("key", "value") ;
+
+ return hm ;
+ }
+}