Closed Bug 299263 Opened 19 years ago Closed 2 years ago

Implement Java component loader

Categories

(Core Graveyard :: Java to XPCOM Bridge, enhancement)

enhancement
Not set
normal

Tracking

(Not tracked)

RESOLVED WONTFIX

People

(Reporter: jhpedemonte, Unassigned)

References

Details

Attachments

(1 file, 3 obsolete files)

CC'ing some folks who might be interested.  Patch coming up.
Attached patch work in progress (obsolete) — Splinter Review
The biggest concern when working on this was doing it in such a way that the
JVM would only get loaded  (for the most part) when the component was actually
created.  I had several ideas, but in the end I decided to define the module
and components in a simple XML file.  This would be easiest for a component
developer.

You can take a look at the XML file in the patch
(extensions/java/xpcom/tests/loader/contents.xml).  This just covers the
required elements.  The full 'spec' would look something like this:

<module>
  <name> Name of Module </name>
  <ctor> (optional) Module constructor method </ctor>
  <dtor> (optional) Module destructor method </dtor>
  <component>
    <description> Descriptive name for component </description>
    <cid> Class ID ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") </cid>
    <contractid> Contract ID (i.e. "@foo.org/bar;1") </contractid>
    <ctor> Component contructor method (format: <package>.<class>#<method>,
i.e. "org.foo.bar.Bar#barCtor") </ctor>
    <regproc> (optional) Component registration method </regproc>
    <unregproc> (optional) Unregistration method </unregproc>
    <factorydtor> (optional) Factory destructor method </factorydtor>
    <ifacesproc> (optional) GetInterfaces method </ifacesproc>
    <langhelpproc> (optional) Language helper method </langhelpproc>
    <classinfo> (optional) ???? </classinfo>
    <flags> (optional) Class info flags </flags>
  </component>
  <component>
    ...
  </component>
  ...
</module>

I then load the XML file in |nsGenericJavaModule::LoadComponentManifest()|,
using some DOM document APIs.  I essentially enumerate through all the children
and copy in the information.  Not sure if I am doing this correctly, though.

What do you guys think of this approach?

Some other notes:
I made |nsGenericJavaFactory| inherit from |nsGenericFactory|, so I wouldn't
need to implement all the methods, many of which are the same between the two. 
To that end, I had to make the destructor and member variable for
|nsGenericFactory| be "protected" instead of "private".  Is that an issue?

The test in this patch works fine on Linux, but only if I have already set
LD_LIBRARY_PATH to "$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/client". 
This is needed to that it will find 'libjvm.so' and some of the libraries it
links to.  I tried setting up the path from within the code, so that the user
would only need to set JAVA_HOME, but I couldn't get it to work.  I think I
read somewhere where bsmedberg mentioned that on Linux and Mac OS X you can't
change the library path after the process has already started.	Is there any
way around this limitation?
Another note: As a result of inheriting from |nsGenericFactory|, I also had to
link to 'xpcomglue_s'.  If I don't, then when loading the javaloader, I get an
unresolved symbol: 'nsGenericFactory::QueryInterface(nsID const&, void**)'.
Attached patch load JVM on registration (obsolete) — Splinter Review
bsmedberg asked why I try to delay loading of JVM, since component will only
get registered when it is newly installed/upgraded, so it shouldn't be much of
a hit.	Well, here is a patch that does that.  I still needed a common entry
point (similar to |NSGetModule| C method), so I thought of making each Java
component have a class called |XPCOMModule|, which has a |getModule| method. 
Then in the code, I search the jar file for that class in order to get its
fully qualified name, and load it up.
It might be interesting to find out how blackconnect exposed NSGetModule.  Maybe
it has a different way that would be useful to mimic?
Blackconnect installed two files for a Java XPCOM component: a *.jar.info
information file, and a *.jar.comp file that contained the actual JAR file with
Java code.  The *.jar.info file (i.e.
http://lxr.mozilla.org/mozilla/source/java/xpcom/java/test/bcJavaSample.jar.info)
is just a simple text file that has some info about the XPCOM component.  So in
the end, it's very similar to my "context.xml" file from the first patch in this
bug.  I just didn't want to have two separate files in the 'components'
directory, so I put it inside the JAR file.  So basically, Blackconnect doesn't
start the JVM until someone tries to create an instance of the component.
Instead of XPCOMModule.getModule, you could also make it so that FooBar.jar
would be required to implement the following:

  public class FooBar {
    public static nsIModule NSGetModule(nsIComponentManager compMgr,
                                        nsIFile location) {
      ...
    }
  }

I suppose you could then search for "*/FooBar.class".  To me this is somewhat
nice because simple Java components could have only a single class (without
having to call the class XPCOMModule), and it reminds me of the way the "main"
function works for java programs.
But how would I know to search for "FooBar.class"?  Where would I get that text?
 That was the main reason I decided to make it a known class name such as
"XPCOMModule.class".  The best way I can think of is to make the name of the
component jar file specify the name of the main class;  therefore, "FooBar.jar"
component would contain "FooBar.class".
> ... therefore, "FooBar.jar" component would contain "FooBar.class".

Yup, that was exactly what I was suggesting.  I should have clarified that.
* Created a custom class loader that extends |URLClassLoader|, which loads all of the Java components.  Looks for a class called "<JAR name>Module", and then calls the |NSGetModule| method on it.
* This patch still uses the old way of doing loaders, since I am looking to release a FF 1.5/2 extension.  I'll update to the new loader code once this code is working well on all 3 major platforms.
* Added some code in rules.mk for automating the Java compilation process.
* Since this loader is an extension, I had to add a hack to go back over some components directories in order to register any Java components there.  See |nsJavaComponentLoader::Observe|.
* On Windows and Linux, this code depends on the "JAVA_HOME" environment variable to find the installed JVM.  On Windows, I assume the best way to find the JVM is to look in the registry.  Not sure what to do on Linux.  There isn't really a set location that JVMs are installed, and I don't think I can rely on "JAVA_HOME" always being set.  Any ideas (for both platforms)?
Attachment #187831 - Attachment is obsolete: true
Attachment #188433 - Attachment is obsolete: true
This also brings up the issue of dynamically generating Java interfaces.  Since any extension can provide their own interface(s), JavaXPCOM won't know about them, since they are not in the MozillaInterfaces.jar file.  So I need a way to generate them on the fly.  That way, it would be possible (for example) to only ship the core frozen interfaces in a jar file, and every other interface would be generated on the fly.
Why not generate all the interfaces dynamically, to save the download footprint of the MozillaInterfaces.jar?  That's my plan with XPCOM.NET, such as it is.  (It also makes it less likely that the Java interface gets out of sync with the XPT when we update.)
Yeah, I could generate all of the interfaces.  Now I just need to find out what the best way to do that is.
Does Java have a varargs calling function? You could theoretically represent any XPCOM interface function call like so

interface nsISupports
{
  callMethod(string Name, /*arguments*/...);
}

Which could be used to represent arbitrary or even unknown interfaces... and the type conversion would be performed at runtime using nsIInterfaceInfo.
I'm kinda assuming that it's hard or impossible to dynamically generate Java types, because I couldn't find any mention of it on the web. Perhaps I'm not looking hard enough.
varargs style arguments were added in Java 1.5, but I don't think Java is flexible enough to do what you are suggesting.  I'll see what I can find.
As far as I can tell, Rhino's ClassFileWriter class doesn't create interfaces.  I would have to add some code in order for it to handle interfaces.

Several people on the java.sun.com forums have mentioned using com.sun.tools.javac.Main.compile() to actually take a source file and compile it on the fly, but this method is only available in the tools.jar package, and that is only provided in the JDK, not the JRE.  So this isn't really an option.
In bug 279649, biesi suggested http://cglib.sourceforge.net/ for dynamically creating Java proxies for XPCOM objects.  I took a look at this project last night, and it can dynamically create both interfaces and classes.  However, it does more than we need.

One of the libraries it depends on, though, is ASM (http://asm.objectweb.org), which can be used to create interfaces and classes.  Plus, it's small, fast, and easy to use.  But it has a different license (http://asm.objectweb.org/license.html), which basically says 'do as you like, but let others know you are using this code'.  Not sure what Mozilla's policy is on using libraries with other licenses.
You can email gerv to make sure, but all kinds of MIT/BSD licenses are compatible with the MPL tri-license.
(In reply to comment #17)
> In bug 279649, biesi suggested http://cglib.sourceforge.net/ for dynamically
> creating Java proxies for XPCOM objects.  I took a look at this project last
> night, and it can dynamically create both interfaces and classes.  However, it
> does more than we need.
> 
> One of the libraries it depends on, though, is ASM (http://asm.objectweb.org),
> which can be used to create interfaces and classes.  Plus, it's small, fast,
> and easy to use.  But it has a different license
> (http://asm.objectweb.org/license.html), which basically says 'do as you like,
> but let others know you are using this code'.  Not sure what Mozilla's policy
> is on using libraries with other licenses.
> 

Another very interesting code that might be usefull is http://www.janino.net/
and its license could be suitable one. (http://www.janino.net/licensing.html)
* This patch uses the asm library to dynamically generate Mozilla interfaces on the fly.  In order to do so, I had to create a chain of class loaders (see diagram at top of JavaComponentLoader.java).  That means that the bulk of MozillaInterfaces.jar doesn't need to ship with the Java Component Loader extension (only Mozilla.java and associated classes are necessary).  Also, the component loader can now make use of interfaces that weren't available at build time, such as custom interfaces that are shipped with extensions.
* This patch depends on the patches from bug 328901 and bug 333618.
Attachment #211148 - Attachment is obsolete: true
Depends on: 328901, 333618
Actually, this latest patch isn't ideal.  The Java component loader library makes use of some internal JavaXPCOM methods and globals (InitializeJavaGlobals(), gJavaKeywords, shortClass, etc), as part of the implementation of the InterfaceInfoManager and related classes..  This is OK for now, since the Java component loader library is linked against the JavaXPCOM library.  However, in the future, when FIrefox will be XULRunner based, the only way for this patch to work would be to expose those internal JavaXPCOM symbols, which isn't what we want.

Perhaps the best solution would be to move InterfaceInfoManager.java and related classes to JavaXPCOM.  They could be made part of the MozillaGlue.jar library (see bug 328901).  Or we could create a separate library called MozillaUtils.jar.  That way, MozillaGlue.jar would contain classes and interfaces associated with embedding and initialization, whereas MozillaUtils.jar would contain helper classes and classes that don't belong in MozillaGlue.jar.  How's that sound?
Blocks: 350886
No longer blocks: 350886
Any status update on this?
I've started to update the code to account for all the changes that have taken place in JavaXPCOM.  Unfortunately, I can only work on this as time permits, so I can't say when it will be done.  But I want to get it out as soon as possible.
Is this bug/upcoming feature the reason that new interfaces cannot be implemented in Java?

http://groups.google.com/group/mozilla.dev.tech.java/browse_thread/thread/1fc57ba670c887a0#
Assignee: jhpedemonte → nobody
JavaXPCOM is effectively dead. Should this bug be closed?
Product: Core → Core Graveyard

JavaXPCOM was removed in bug 648593.

Status: NEW → RESOLVED
Closed: 2 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: