Inline...llback(3pm)

✨ NAME ✨

Inline::Java::Callback β€” callback into Perl from Java, bestie πŸ’–


πŸŽ€ SYNOPSIS πŸŽ€

Like, the whole vibe in one go:

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" ;
}

okay so you make a Java class, you call back into Perl, and it prints perl. that's it. that's the whole flex πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

Inline::Java::Callback is the plug that lets Java reach back into Perl and call your subs. no cap, that's literally its whole job. to make it happen you need to build an org.perl.inline.java.InlinePerlCaller object β€” that's your little passport for crossing the border into Perl-land 🫢

here's a super typical example, the kind of thing you'd actually write:

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/) ;
}

see how Java is asking Perl "hey does this regex match πŸ₯Ί" and Perl is answering? iconic behaviour. no notes.


πŸ’– CALLBACK API πŸ’–

here's every method you can use to call into Perl. collect them all, they're the main characters:

  • public Object CallPerlSub(String sub, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException β€” calls the subroutine you named, feeds it the args you gave it, and tries to build an object of type cast out of whatever comes back.
/* Example */
Integer sum = (Integer)CallPerlSub("main::add", new Object [] {Integer.valueOf(5), Integer.valueOf(3)}, Integer.class) ;
  • public Object CallPerlStaticMethod(String pkg, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException β€” calls the static package method you named (using that $pkg->$method() notation, iykyk), feeds it the args, and tries to make an object of type cast from the result.
/* Example */
Integer sum = (Integer)CallPerlStaticMethod("main", "add", new Object [] {Integer.valueOf(5), Integer.valueOf(3)}, Integer.class) ;
  • public Object eval(String code, Class cast) throws InlineJavaPerlException, InlineJavaException β€” evaluates the Perl code you handed over and tries to build an object of type cast from what it returns.
/* Example */
Integer sum = (Integer)eval("5 + 3", Integer.class) ;
  • public Object require(String module_or_file) throws InlineJavaPerlException, InlineJavaException β€” requires the module or file you gave it, using a heuristic (right now that means: it checks whether the file exists or not) and then calling Perl's require function with the appropriate construct.
/* Example */
require("Someting")
  • public Object require_file(String file) throws InlineJavaPerlException, InlineJavaException β€” requires that specific file, periodt.
/* Example */
require_file("./my_stuff.pl") ;
  • public Object require_module(String module) throws InlineJavaPerlException, InlineJavaException β€” requires that specific module. slay.
/* Example */
require_module("Data::Dumper") ;
Note: for all the CallPerl* methods and eval, that cast parameter is optional and it defaults to String.class. so if you don't feel like specifying, it'll just assume String. easy ✨

these methods can throw two kinds of exceptions: InlineJavaException and InlineJavaPerlException β€” and yes, both of those live in the org.perl.inline.java package, they're neighbours 🏑

  • InlineJavaException = something inside Inline::Java itself went wrong (an internal error, not your fault bestie).
  • InlineJavaPerlException = your Perl callback actually threw something (die() or croak()), so like… that one's on the Perl side 😭

the value of $@ (which can be a scalar, or honestly any valid Inline::Java object) is retrievable with the GetObject() method on the InlineJavaPerlException object. and if you're certain that $@ was a plain Perl scalar, you can just use the GetString() method instead. ✨


🌈 CALLBACK CONTEXT 🌈

by default, callbacks run in scalar context. but if you want to call certain functions in list context, you have to put an @ in front of the function name. then the result gets passed on to Java as an Array. the way that works is so clever honestly:

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
Note: when you call a Perl function that returns a list or an array, you have to pass the Class object for the expected array type β€” in the case above, that's String []. and because those Class objects are genuinely annoying to get ahold of for array types, the easiest hack is to make a dummy array of the type you want and call getClass() on it, exactly like the example above does. trust, it's the move 🫢

πŸ”₯ CALLBACK LOOPS πŸ”₯

okay so now you can use callbacks from different Java threads. and one of the big advantages of that is you can finally handle stuff like SWING events in Perl. here's the whole thing:

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() ;
}

so StartCallbackLoop can be called on any org.perl.inline.java.InlineJavaPerlCaller object, and what it does is block the current thread and let callbacks come in through any InlineJavaPerlCaller that was created by that same (current) thread. and the only way to interrupt a StartCallbackLoop is to call StopCallbackLoop on any org.perl.inline.java.InlineJavaPerlCaller object created by that same thread. that's the only exit, bestie, no sneaking out any other way πŸšͺ

also β€” and this one matters β€” only threads that actually talk to Perl through Inline::Java are allowed to create org.perl.inline.java.InlineJavaPerlCaller objects and invoke their StartCallbackLoop / StopCallbackLoop methods. no random threads pulling up, it's invite only πŸ’…


part 1 of 2 β€” there's more coming, don't go anywhere πŸ’œ

✨ SELECT-STYLE CALLBACK LOOPS ✨

okay so bestie, the callback loop from the last section has one glaring flaw and we need to talk about it πŸ’€ the way that your main Perl program is just... completely blocked the whole time it's waiting for callbacks? not it. not the vibe. πŸ™ˆ

so version 0.51 said "we're fixing this" and dropped a whole new API for callback loops β€” now you can process callbacks basically the same way you'd use select(2) to read data from a filehandle. iconic behavior honestly πŸ‘‘

here's the example, look at it, absorb it:

   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 occurred 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() ;
   }

so like, the StartCallbackStream method? you can call it on any InlineJavaPerlCaller object and it sets up a channel to receive callbacks. then WaitForCallback is your bestie for checking in β€” you hand it a float timeout value, and here's the tea on what those mean:

  • -1 means wait forever (romantic, honestly πŸ₯Ί)
  • 0 means return immediately (no patience, respect πŸ’…)

and WaitForCallback can hand you back one of these three:

   rc  >  0, indicating that rc callbacks are waiting to be processed
   rc ==  0, indicating that a timeout has occurred and no callbacks are waiting
   rc == -1, indicating that the callback stream has been closed

when you're done-done, you close the callback stream by calling CloseCallbackStream β€” it works basically the same way as the StopCallbackLoop method from the previous section, so if you understood that one you already know this one 🫢

🚨 also important: all those restrictions about thread communication that we talked about in the previous section? yeah those are still valid here. same rules, no exceptions, don't @ me.

🌈 SEE ALSO

  • Inline::Java
  • Inline::Java::PerlNatives
  • Inline::Java::PerlInterpreter

πŸŽ€ AUTHOR

Patrick LeBoutillier <patl@cpan.org> is the author of Inline::Java. king behavior πŸ‘‘

Brian Ingerson <ingy@cpan.org> is the author of Inline.


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.


and that's the whole page bestie!! 🌸✨ this one came from perl v5.40.0 and it was dated 2024-12-07, filed under Inline...llback(3pm) in the User Contributed Perl Documentation section πŸ’œ go forth and stream those callbacks, you absolute legend πŸ¦‹

πŸ“¦ where this page comes from