#430 Bundle Java classes in pod

tompalmer Wed 14 Jan 2009

Is there a standard way to include jars/dlls in pods? Something based on simple conventions (supporting multiple platforms in the same pod) would be just great.

For DLLs, I'm referring to .NET DLLs above, but it would also be good to have a place to put native code (for different OSs/architectures) in each pod, too. Some of the native libraries would be used by both .NET and Java. Others would be specific to each of the high level platforms.

Thoughts?

brian Wed 14 Jan 2009

The pod files are already used to store the Java classfiles, and that is pretty easy to control with classloaders. .NET isn't quite so clean - you have to store it to disk if you want to use pdb files to get line numbers which I personally think is design flaw. But still we could unpack them to disk.

Although this is really more a deployment issue, how do you package stuff up for installation - which kinds of leads to Fan's version of RubyGems/Maven.

A very simple solution might be to put an "unpack" directory into a pod which got extracted on startup to the file system.

tompalmer Fri 23 Jan 2009

What I mean, is say I want to include a separate 3rd-party library as part of my pod, and it would be automatically added to the classpath (or to the native library path for OS-level libraries).

qualidafial Thu 7 Oct 2010

Ping. Is there a way to do this? In F4 I can add e.g. the Mockito jar to the Java classpath which works fine when running tests from within Eclipse. However there is no equivalent that I can see in build::BuildPod, so cannot build from the command line.

Only alternative seems to be to include third-party sources in javaDirs inside the project and recompile from source.

yachris Thu 7 Oct 2010

Maybe I'm misunderstanding, but I put my Mockito jar in $fanhome/lib/java/ext/ and I can compile and run from the command line...

qualidafial Thu 7 Oct 2010

@yachris: I'm looking for a solution where I can bundle the third party code with the pod itself, as an alternative to putting it in $FAN_HOME.

brian Thu 7 Oct 2010

We already bundled Java classfiles in pods today, so much of that infrastructure is already in place. However, the FanClassLoader won't look in arbitrary pods for any old class - it only looks for classes in a given pod by naming convention ("fan.{pod}").

Then on the compiler side, I don't think it looks in pods for Java classes (assuming you need that too).

In order to make it all work, it would probably be something along the lines of:

  • maybe use some pod meta-data key to define which Java packages it contains
  • add something into BuildPod that would include a jar into the pod zip and insert the appropriate meta-data
  • enhance FanClassLoader to use that meta-data key in resolving classes
  • enhance compilerJava::ClassPath to do it too

The problem is that I do not want the runtime having to open up every pod every single time you run the VM. So the trick is how to do things fast, that is what makes this such a difficult feature.

qualidafial Thu 7 Oct 2010

I'm approaching this from the perspective of jars that the pod depends on--not exporting the Jar's API from the pod.

so in theory, the class loader would only go looking up jars (dll's) when one of the fantom types inside the pod requests a java class.

I hope that made sense.. :)

qualidafial Fri 8 Oct 2010

Just to clarify, the reason I want this is so that pods can be distributed inside a single .pod file, with all native dependencies included.

I could see the fwt pod being bundled this way. If the runtime wants to unpack those libraries for performance purposes the first time they're loaded, great. But it doesn't affect the distribution method.

brian Fri 8 Oct 2010

Creating a pod internal dependency is actually a much simpler problem. Well at least from the java bytecode side (I don't fully understand how the native DLL stuff works, but I don't think that is commonly a JVM issue).

During the Env redesign one of the things I did (to make the OSGi runtime work) was to give each pod its own classloader. So theoretically we should be able to stick something into the pod's meta and then just tweak FanClassLoader.findClass to check for those packages.

Would you be interested in helping to figure out how that works? If so maybe you can try hacking it on your side which works for your use case. Then you can email me directly and we can get it into the next build.

kaushik Mon 22 Nov 2010

Will be very useful once this is done. Can we track this as a ticket?

brian Fri 26 Nov 2010

Promoted to ticket #430 and assigned to brian

Will be very useful once this is done. Can we track this as a ticket?

Yes this seems valuable enough that it should be targeted for 1.0. Think the only real thing that needs to happen is add a bit of meta-data into the pod that the FanClassLoader can use to decide what pod to look in for a class. Although there are tons of complications and boundary conditions with regard to dependencies that I doubt we should fully tackle. For example to start with, I think we should keep this a pod internal issue.

kaushik Sat 27 Nov 2010

Thanks, internal to pod should be fine for this. Alongside, could we also allow better configuration of $FAN_CP(removing -d "$FAN_CP" in fanlaunch)? With that I can write a simple shell script to set the classpath before calling "fan".

brian Sun 28 Nov 2010

Alongside, could we also allow better configuration of $FAN_CP

Not sure I follow this request. Right now the code is:

# Set FAN_CP classpath if not already set
if [ -z "$FAN_CP" -o ! -d "$FAN_CP" ] ; then
  # always put sys.jar in classpath
  FAN_CP="$FAN_HOME/lib/java/sys.jar"
fi

Do you want me to email a patch with how you would like it work?

kaushik Sun 28 Nov 2010

Sent you a mail. Thanks.

brian Thu 2 Dec 2010

As part of this discussion, I added support to pass $CLASSPATH through on the command line in the bash scripts - changeset

brian Wed 5 Jan 2011

Renamed from Including jars/dlls in pods to Bundle Java classes in pod

brian Wed 5 Jan 2011

Ticket resolved in 1.0.57

Okay made two tweaks: one to the runtime and one to the build tool.

First I enhanced the Java runtime so that the Fantom class loader will check its own pod for any Java class first. This will let you easily bundle non-Java classes into your pods and they will be visible to the native code in your pod (but they will not have visibility outside of the pod).

As part of this change I also discovered some legacy behavior regarding how Java native compilation worked. Previously it was generating lib/java/foo.jar and adding classfiles to the lib/fan/foo.pod file too. The runtime hasn't actually used lib/java in a long time. The only use for the lib/java files was support in javac to compile against dependencies. But since pods are effectively treated as jars by the runtime, I changed build scripts to have javac compiler use pods directly for the dependencies. This allows us to skip generating the lib/java files altogether.

I didn't create any build support to add a jar into the pod zip, however that should be easy to add to your own build scripts.

I also beefed up docs regaridng class loading, here is new section in Java FFI:

Deployment

During runtime on the JVM, the Fantom uses a classloader per pod. Classes are resolved as follows:

  1. Classfile is contained in the pod zip:
    1. Fantom type foo::Bar maps to precompiled classfile fan/foo/Bar.class"
    2. Java type acme.foo.Bar maps to "acme/foo/Bar.class"
  2. Fantom type foo::Bar is loaded as follows:
    1. first resolve pod file "foo.pod" via Env
    2. emit classfile from "fcode/Bar.fcode"
  3. Search extension jars in "{fan.home}/lib/java/ext"
  4. Search extension jars in "{fan.home}/lib/java/ext/{platform}" (see Env)
  5. System classloader which typically includes "{java.home}/lib/ext"

You can bundle Java code in a pod zip file and it will be visible to classes within your pod. However other pods will only have visibility to your Fantom classes in that pod (not other Java classes).

qualidafial Thu 6 Jan 2011

Per point 1A above, does this mean that a Fantom class is compiled to fcode, and precompiled to a Java .class? Because that would mean a Fantom .pod file could be used as a valid .jar, and be included in a plain old Java runtime classpath.

brian Thu 6 Jan 2011

It is always been true that pods with Java natives have both fcode and precompiled classsfiles. And since pods are zip files they can be treated as just jars. But things don't work just off Java runtime classpath unless you using JarDistEnv. Although one could build a variation of JarDistEnv that worked with multiple jars, although it would be harder to implement things like Pod.list and some of the reflection stuff.

jhughes Thu 5 Jan 2017

I didn't see it mentioned specifically anywhere but the question was asked about bundling jar files into a pod but I don't see the syntax to make that happen. Since i'm developing in F4 IDE, I would be interested in how this is accomplished within the confines of that enviornment. If it's not possible from within F4, then what are the extra steps to build a pod with all it's dependencies bundled (jars,pods,etc)?

brian Thu 5 Jan 2017

The best thing to do is just map a Java jar into a Fantom pod and then treat it as a first class module. Its very easy, just create a pod with a build script and the jar you want to "wrap" in the same directory, and then in your build file make the jar one of your res:

resDirs = [`your-java.jar`]

Once the pod is compiled it is essentially both a jar and a Fantom pod, but has the Fantom meta to treat it as a first class module.

jhughes Thu 12 Jan 2017

Since i'm developing within F4, as I assume most people are, prefering an IDE over command line, I have never had to build a buildScript. Looking at the docs, I don't really see how this is used or any examples of it being used so i'm still not sure how I would accomplish this.

How does one use this method to compile a pod with a jar?

SlimerDude Fri 13 Jan 2017

By buildscript, Brian is talking about the build.fan file - which every project has, even those in F4!

You can find out more about build.fan in docTools::Build (fantom.org) and Inside a Fantom Build Script (alienfactory.co.uk)

So by setting the (overloaded) resDirs field to include .jar files, those jars are merged with the resulting .pod file.

resDirs = [
  `lib/wot.jar`,
  `lib/ever.jar`
]

Note that the location of the jar files should be relative to the build.fan file.

Depending on what you're trying to accomplish, it may be easier for you to just copy the .jar files from one Fantom runtime environment to another.

Maru-chan Tue 7 Feb 2017

Do you have samples of codes using Fantom?

SlimerDude Tue 7 Feb 2017

Hi Maru-chan,

Do you mean an example build script that converts a java .jar file into a Fantom .pod?

If so then try this simple example that creates a commons-lang pod...

Create a build.fan file:

using build

class Build : BuildPod {
    new make() {
        podName = "commonsLang"
        summary = "Apache commons-lang"
        version = Version("2.6")

        depends = [,]

        srcDirs = [,]
        resDirs = [`commons-lang-2.6.jar`]
    }
}

Assuming you have commons-lang-2.6.jar in the same directory, then run it as Fantom script:

C:\> fan build.fan

compile [commonsLang]
  Compile [commonsLang]
    FindSourceFiles [0 files]
    WritePod [file:/C:/Apps/fantom-1.0.69/lib/fan/commonsLang.pod]
BUILD SUCCESS [68ms]!

Maru-chan Fri 10 Feb 2017

Thank you! i would also like to ask about Literals used by fantom...it is stated there that: "The three types Bool, Int, and Float are value-types. These types are not necessarily passed as object references, but rather passed by value on the call stack. When value types are coerced to/from reference types like Obj and Num, the compiler will generate boxing/unboxing operations."

What do you mean boxing/unboxing operations in that statement?

Ethan Reynolds Sun 17 Sep 2023

I am trying to include more than 1 jar as resources in a build (as shown in the Fri 13 Jan 2017 post above).

When I try to build this pod using F4_1.1.6 and Fantom_1.0.78, I get the following error:

Cannot write resource file '/META-INF/MANIFEST.MF': sys::IOErr: java.util.zip.ZipException: duplicate entry: META-INF/MANIFEST.MF

How do I get around this? or can I?

matthew Mon 18 Sep 2023

I've hit that issue before and the workaround I used was to remove the /META-INF/MANIFEST-MF file from all the jars. Usually the information in that file is not important so it can be safely removed.

Login or Signup to reply.