Trolltech Home | Qt-jambi-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 1

Qt-jambi-interest Archive, June 2007
QtJambi, Java Web Start and Mac OS (a solution)


Message 1 in thread

  Dear all,

  After some search a found a solution to start web start apps under Mac OS
X.

  First, you need to create a jar with all necessary binary. To do this i
use the attached script named "createqtjambimacjar.sh ".

  Now, we can test the created jar with the qtjambi demo jar (from the
webstart app) :

$ java -cp qtjambi.jar:qtjambi-mac-1.0.0-beta2.jar:qtjambi-launcher.jar -
Dcom.trolltech.qt.verbose-loading=true    -XstartOnFirstThread
com.trolltech.launcher.Launcher

This command will fail with the following message :

Loaded(libQtCore.4.dylib) using cached
Loaded(libQtGui.4.dylib) using cached
Loaded(libqtjambi.jnilib ) using cached
Loaded(libQtCore.4.dylib) using cached
Loaded(libQtGui.4.dylib) using cached
java.lang.UnsatisfiedLinkError:
/private/tmp/QtJambi_0.1.1/libcom_trolltech_qt_core.jnilib:
        at java.lang.ClassLoader$NativeLibrary.load (Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1647)
        at java.lang.Runtime.load0(Runtime.java:769)
        at java.lang.Runtime.load(Runtime.java:757)
        at com.trolltech.qt.Utilities.loadLibrary(Utilities.java:132)
        at com.trolltech.qt.Utilities.loadJambiLibrary(Utilities.java:80)
        at com.trolltech.qt.core.QtJambi_LibraryInitializer.<clinit>(QtJambi_LibraryInitializer.java:9)
        at com.trolltech.qt.core.QAbstractFileEngineHandler.<clinit>(
QAbstractFileEngineHandler.java:12)
        at com.trolltech.qt.QtJambi_LibraryInitializer.<clinit>(QtJambi_LibraryInitializer.java:26)
        at com.trolltech.qt.QtJambiObject.<clinit>(QtJambiObject.java:29)
Exception in thread "main" java.lang.UnsatisfiedLinkError: __qt_initLibrary
        at com.trolltech.qt.core.QtJambi_LibraryInitializer.__qt_initLibrary(Native
Method)
        at com.trolltech.qt.core.QtJambi_LibraryInitializer
.<clinit>(QtJambi_LibraryInitializer.java:10)
        at com.trolltech.qt.core.QAbstractFileEngineHandler.<clinit>(
QAbstractFileEngineHandler.java:12)
        at com.trolltech.qt.QtJambi_LibraryInitializer
.<clinit>(QtJambi_LibraryInitializer.java:26)
        at com.trolltech.qt.QtJambiObject.<clinit>(QtJambiObject.java:29)

  But if in the /private/tmp/QtJambi_0.1.1 directory, we create a symlink :

  $ ln -s libqtjambi.jnilib libqtjambi.1.jnilib

  We can run the demo without error. com.trolltech.qt.Utilities seem to be
patched but as now i don't have the time to investigate it. :-(

  If we want to start a java web start, we must run the app in another JVM
as with JSW, swing is already started. To do this, i wrote a class (
BootStrap.java attached to this mail) that call JWS internal methods to
collect all parameters of the jnlp file to create a command line to start a
new JVM. In the jnlp file the java-vm-args argument must containts at least
"-XstartOnFirstThread" to start QtJambi.

  In the main file of the app to start, a function called "bootstrap()" must
be added:

  private static void bootstrap() {

    if (!System.getProperty("os.name").toLowerCase().startsWith("mac os x"))
      return;

    try {
      Corsen.class.getClassLoader().loadClass("com.sun.jnlp.JNLPClassLoader
");
      BootStrap.bootstrap();
    } catch (ClassNotFoundException e) {
    }
  }

  As the BootStrap class is linked to classes of the javaws.jar, we can't
call it in the bootstraped JVM (an exception will be throwed as
javaws.jaris not in the classpath), that why this function must be
defined in the main
class. Then, this method must be called at the beginning of the main method
:

   public static void main(final String[] args) throws IOException {

    bootstrap();
     ... The code of the application ...
    }

   Once the little bug in com.trolltech.qt.Utilities fixed, it will be
possible to run out of the box Java Web Start apps using QtJambi with this
little magic code. :-)

  Laurent.
package fr.ens.transcriptome.test;
package fr.ens.transcriptome.test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.sun.javaws.JnlpxArgs;
import com.sun.javaws.cache.DiskCacheEntry;
import com.sun.javaws.cache.DownloadProtocol;
import com.sun.javaws.exceptions.JNLPException;
import com.sun.javaws.jnl.ApplicationDesc;
import com.sun.javaws.jnl.JARDesc;
import com.sun.javaws.jnl.JREDesc;
import com.sun.javaws.jnl.LaunchDesc;
import com.sun.javaws.jnl.ResourcesDesc;
import com.sun.jnlp.JNLPClassLoader;

/**
 * A Bootstrap class for webstart apps under Mac OS X.
 * @author Laurent Jourdren
 */
public class BootStrap {

  private static final String JAVA_PATH_MACOS = "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Commands/java";

  private List<String> classpath = new ArrayList<String>();
  private String mainClass;
  private String[] args;
  private Map<String, String> properties = new HashMap<String, String>();
  private String vmArgs;
  private long maxHeap;
  private long minHeap;

  private static void save(File f, String s) {

    try {
      Writer writer = new OutputStreamWriter(new FileOutputStream(f));
      writer.write(s);
      writer.close();
    } catch (Exception e) {

    }

  }

  public void collectInfos(final JNLPClassLoader cl) {

    final LaunchDesc ld = cl.getLaunchDesc();

    collectClasspath(ld);
    collectMainClass(ld);
    collectProperties(ld);
    collectJVM(ld.getResources().getSelectedJRE());
  }

  private void collectClasspath(final LaunchDesc ld) {

    final List<JARDesc> jarDescs = new ArrayList<JARDesc>();

    ResourcesDesc rd = ld.getResources();

    if (rd != null) {

      JARDesc[] jars = rd.getEagerOrAllJarDescs(true);

      for (int i = 0; i < jars.length; i++)
        if (jars[i].isJavaFile() || jars[i].isNativeLib())
          jarDescs.add(jars[i]);
    }

    for (JARDesc jd : jarDescs) {

      URL location = jd.getLocation();
      String version = jd.getVersion();

      try {
        DiskCacheEntry dce = DownloadProtocol.getResource(location, version,
            DownloadProtocol.JAR_DOWNLOAD, true, null);
        this.classpath.add(dce.getFile().getAbsolutePath());

      } catch (JNLPException e) {
      }
    }

  }

  private void collectMainClass(final LaunchDesc ld) {

    // If applet exit.
    if (ld.isApplet())
      System.exit(0);

    ApplicationDesc ad = ld.getApplicationDescriptor();
    this.mainClass = ad.getMainClass();
    this.args = ad.getArguments();
  }

  private void collectJVM(final JREDesc jreDesc) {

    this.maxHeap = JnlpxArgs.getMaxHeapSize();
    this.minHeap = JnlpxArgs.getInitialHeapSize();
    this.vmArgs = jreDesc.getVmArgs();
  }

  private void collectProperties(final LaunchDesc ld) {

    ResourcesDesc rd = ld.getResources();
    Properties ps = rd.getResourceProperties();
    Iterator it = ps.keySet().iterator();
    while (it.hasNext()) {
      String key = (String) it.next();
      this.properties.put(key, ps.getProperty(key));
    }
  }

  public static void bootstrap() {

    final ClassLoader cl = BootStrap.class.getClassLoader();

    if (cl == null)
      return;

    if (cl instanceof JNLPClassLoader) {

      BootStrap b = new BootStrap();
      b.collectInfos((JNLPClassLoader) cl);
      String cmd = b.createCommandLine();
      save(new File(System.getProperty("user.home") + File.separator
          + "javacmd.txt"), cmd);
      exec(cmd);
      System.exit(0);
    }
  }

  private String createCommandLine() {

    StringBuffer sb = new StringBuffer();

    sb.append(JAVA_PATH_MACOS);

    // Set classpath
    sb.append(" -cp ");
    boolean first = true;
    for (String path : this.classpath) {
      if (first)
        first = false;
      else
        sb.append(":");
      sb.append(path);
    }

    // Set Java properties
    for (String key : this.properties.keySet()) {

      String value = this.properties.get(key);

      sb.append(" -D");
      sb.append(key);
      sb.append("=");
      sb.append(value);
    }

    // Set vm args
    if (this.vmArgs != null) {
      sb.append(" ");
      sb.append(this.vmArgs);
    }

    // Set initial heap
    if (this.minHeap != -1) {
      sb.append(" -Xms");
      sb.append(this.minHeap);
    }

    // Set max heap
    if (this.maxHeap != -1) {
      sb.append(" -Xmx");
      sb.append(this.maxHeap);
    }

    // Main class
    sb.append(" ");
    sb.append(this.mainClass);

    // Args

    for (int i = 0; i < this.args.length; i++) {
      sb.append(" ");
      sb.append(this.args[i]);
    }

    return sb.toString();
  }

  private static void exec(final String cmd) {

    Runtime rt = Runtime.getRuntime();

    try {
      rt.exec(cmd);
    } catch (IOException e) {
    }
  }

  //
  // Constructor
  //

  private BootStrap() {
  }
}

Attachment:

Attachment: createqtjambimacjar.sh
Description: Bourne shell script


Message 2 in thread

  Hi all,

  I'm just thinking about it: we can change the links of the libraries with
install_name_tool.

  So, I've update createjambimacjar.sh and now it's works without creating
the symlink. No more need to patch com.trolltech.qt.Utilities !!!!

  I've only test with the launch the qtjambi demo in command line but there
is no reason that with a Java Web Start app it fails.

  Laurent.

Attachment:

Attachment: createqtjambimacjar.sh
Description: Bourne shell script


Message 3 in thread

Laurent Jourdren wrote:
> 
>   Hi all,
> 
>   I'm just thinking about it: we can change the links of the libraries 
> with install_name_tool.
> 
>   So, I've update createjambimacjar.sh and now it's works without 
> creating the symlink. No more need to patch com.trolltech.qt.Utilities !!!!
> 
>   I've only test with the launch the qtjambi demo in command line but 
> there is no reason that with a Java Web Start app it fails.

Hi Laurent,

For the final release we use a similar approach, downloading the 
packages and launch a new process with the right parameters, so we will 
have webstart for all our binary platforms ;-)

-
Gunnar