![]() |
Home · Overviews · Examples |
Java provides a number of different features which makes it easier to deploy applications, such as the Web Start technology, Java Archives (.jar files) and, of course, the virtual machine that enables you to compile your application to bytecode once and run on all architectures.
The apparent challenge when deploying Qt Jambi applications is that Qt Jambi makes use of Qt's C++ libraries which means that platform dependent code must be distributed in addition to the platform independent bytecode. Qt Jambi provides a solution - all you have to do is to include the native libraries in a JAR bundle.
To include resources, the programmer must follow a particular syntax since Qt Jambi provides its own resource system. But note that this system makes it easy to access any kind of resource (e.g., pixmaps, translation files and xml data files), no matter whether they are located, directly on the disk or in a JAR bundle.
We will first take a look at which building blocks make up a Qt Jambi application. Then we will look at three common deployment scenarios, pure Qt Jambi applications, Qt Jambi applications making use of custom generated code and finally C++ applications making use of Qt Jambi. Where applicable, we'll also take a look at how one can bundle the application into a webstart application and/or a single executable .jar file.
The details covered in this section are primarly informational and a lot is handled automatically by Qt Jambi. When deploying a Java application making use of Qt Jambi and no other native libraries, all of this can be ignored. See the "Deploying a Pure Qt Jambi Application" section. Since 4.4, Qt Jambi provide two ways of loading libraries. The first and recommended one is via .jar file using a deployment specification. A deployment specification contains a list of all libraries to be loaded, directories they reside in and how Qt Jambi should treat them, be it a runtime library, Qt library, JNI library or a plugin. The benefit of this approach is that Qt Jambi handles custom plugin paths and loading of runtime libraries, etc and the user only has to relate to a .jar file as with any other Java application. A platform specific .jar file for your platform is available in the Qt Jambi binary package. The other alternative for loading native libraries is by relying on the traditional method for loading native libraries into Java, which is to make sure that libraries are available in a directory specified in -Djava.library.path and simply load them. In this case QT_PLUGIN_PATH and dependency on runtime libraries has to be handled by the user. Using this method it would theoretically be possible to use pre-installed versions of Qt on a given system, such as a Linux distribution, but we strongly discourage this, as the libraries installed on the system may be binary incompatible with the libraries shipped with Qt Jambi. Mixing binary incompatible libraries may lead to load errors and sporadic crashes. Using the .jar approach ensures that only the precise libraries you want to load, will be loaded, and is significantly safer . To create a .jar file that runs on multiple platforms is only possible by creating an executable .jar file which uses jar .jar files in the current directory. For instance, deploying application.jar for windows 32 and linux32 would require the following files: We highly recommend you use the ant tasks to build the platform archive, as this will automate most of the work for you. Below we will walk through the steps of creating a custom platform archive containing Qt Core and Qt Gui and the jpeg plugin in addition to customlibrary. Rather than using System.loadLibrary() to load custom libraries you should use com.trolltech.qt.Utilities.loadJambiLibrary() which supports loading native libraries via platform archives. The build.xml file in the Qt Jambi source package can also be used as a source of information on how to build the platform archives. The next step is to trigger the initialize task. This should be done early in the ant script as other tasks depends on it. Then we go on to specify the Qt libraries that should be included. In this case we choose to inly include Qt Core and Qt Gui. Then we include the Qt Jambi JNI libraries. We always need to include the qtjambi library in addition to the JNI libraries for the Qt modules we are using, in this case Qt Core and Qt Gui, and finally our own custom library. Then we move on to packaing the files into a .jar file and clearing the temporary directory.Class Files
The Qt Jambi class files are located in the library classed qtjambi.jar which is available in the root of the binary package and also in the root of a source package after the ant script has completed. The qtjambi.jar file also contains some resources that may be needed by some classes so it should be used as-is.Native Libraries
In a prebuilt Qt Jambi package there are a number of different native libraries, located in certain subdirectories of the root directory. In the library directory, which is bin under Windows and lib under Linux and Mac OS X, one can find Qt libraries, Qt Jambi libraries and runtime libraries. The Qt C++ plugins can be found in the subdirectories of the JAMBIDIR/plugins directory in the prebuilt package.Qt Jambi libraries
Qt Jambi libraries are the libraries that implement the native part of Java functions and are usually named according to the package they implement, such as com_trolltech_qt_gui for the native implementation of the com.trolltech.qt.gui package. Qt Jambi libraries are explicitly loaded by Qt Jambi using called to System.load() or similar. These packages are named as following: Windows None .dll qtjambi.dll Linux lib .so libqtjambi.so Mac OS X lib .jnilib libqtjambi.jnilib Qt Libraries
While the Qt Jambi libraries implement the native part of the Java functions, they merly translate the Java function calls into C++ function calls and calls into the Qt libraries. This means that the Qt Jambi libraries link against the Qt libraries and are dependent of them. For a process to be able to load a given library, it must be able to locate and load all its dependencies. Qt Jambi explicitly loads the dependent Qt libraries of a Qt Jambi library directly before trying to load a Qt library. This makes sure that the process has the library loaded and dependecies are resolved. Runtime Libraries
The Qt libraries also have dependencies on runtime libraries. For instance, if Qt Jambi was compiled with GCC 3.3, it would depend on the libstdc++.so.5, which is not always present on newer linux distributions. To be able to run on newer linux distributions this runtime library needs to be available and loaded/available prior to loading the Qt libraries. Qt Jambi calls these libraries "system libraries" and will load them based on a platform specific deployment specification. The specific runtimes depend on the C++ compiler used to build Qt and the operating system. MSVC 6.0 msvcr60.dll, msvcp60.dll MSVC 2002 msvcr70.dll, msvcp70.dll MSVC 2003 msvcr71.dll, msvcp71.dll MinGW mingwm10.dll GCC 3.3 and older libstdc++.so.5 GCC 3.4 and newer libstdc++.so.6 GCC on Mac OS X no dependencies MSVC 2005 and 2008 Microsoft Visual Studio 2005 and 2008 introduces manifest files as a way of describing dependencies between dll's. This restricts the way binaries can be deployed. Manifest based runtime libraries will in this case need to be available for Qt Jambi to load. These files are not available by default on Windows Vista, Windows XP nor Windows 2000. This is solved by either: Windows depends.exe, GUI tool part of MSVC Windows dumpbin /dependents, command line tool part of MSVC Linux ldd, command line tool, part of GCC Mac OS X otool -L, standard tool Plugins
Finally there are the Qt C++ plugins, which are loaded by Qt at runtime to support some features, such as loading / saving of jpeg images. By default, Qt will locate the plugin libraries where the C++ library was built, which is convenient for Qt C++ developers but less relevant for Qt Jambi developers or for deployment. It is possible to extend the default search location by adding directories to the environment variable QT_PLUGIN_PATH or by making a call to QApplication.addLibraryPath() in the Java code. Both will add searchpaths for Qt to look for plugins.Deployment Scenarios
In this section we'll look more closely at the three primary deployment scenarios that we picture for Qt Jambi. The first and easiest one is to deploy a pure Qt Jambi Application, not relying on any other native libraries than the Qt Jambi ones. Then we look at the scenario where a user has used the Qt Jambi generator to map his own libraries and deploys an application of native libraries from both Qt Jambi his own generated project. Finally we'll look at how it is possible to deploy a C++ application that makes use of Qt Jambi, like our own tool Qt Designer does. Deploying a Pure Qt Jambi Application
This method of deployment should be used when the application either makes only use of pure Java code in combination with the Qt Jambi libraries. In this scenario the user should make use of the binary package for the target platform. The final application should then consist of the following three pieces:
The only thing needed to deploy this application is to make sure the three .jar files are available in the applications CLASSPATH and start it. To make an application that can launch on multiple platforms, use the binaries from the binary packages of Qt Jambi for the target platforms that you would like to support and make sure the right platform .jar file is available in the classpath when you launch the application.Creating a Webstart Application
To create a webstart application based on Qt Jambi, specify the application .jar file along with the qtjambi-$VERSION.jar file as normal resources.<resources>
<j2se version="1.5+"/>
<jar href="application.jar"/>
<jar href="qtjambi-4.4.0_01.jar"/>
</resources>
Then, for each target platform to support, add a compatible platform bundle. For instance to add support for Windows 32-bit:<resources os="Windows" arch="x86">
<jar href="qtjambi-win32-msvc2005-4.4.0_01.jar"/>
</resources>
Creating an Executable Java Archive
To create an executable .jar file for one platform is straightforward, simply unpack the contents of the three .jar files and repack them together as one .jar file with the appropriate manifest specifying Main-Class, etc. APPDIR\application.jar
APPDIR\qtjambi-4.4.0_01.jar
APPDIR\qtjambi-win32-msvc2005-4.4.0_01.jar
APPDIR\qtjambi-linux32-gcc3.3-4.4.0_01.jar
Where application.jar has the Class-Path attribute in its Manifest set to include both the platform specific .jar files,Class-Path: qtjambi-4.4.0_01.jar qtjambi-win32-msvc2005-4.4.0_01.jar qtjambi-linux32-gcc3.3-4.4.0_01.jar
As long as the four files are distributed together, the application will run on both windows and linux using the command: > java -jar application.jar
Creating an Application Bundle using Mac OS X JarBundler
Mac OS X provides a powerful and simple to use tool that lets the user build Mac OS X Application Bundles that can be launched using Finder. This tool is called Jar Bundler and is by defualt located in /Developer/Applications/Java Tools/Jar Bundler. Using the tool, one can specify the three classes in the classpath and specify class with the main method used for launching.Deploying a Qt Jambi Application making use of generated code
This method of deployment should be used for deploying applications that contain native libraries generated using the Qt Jambi Generator. This approach should also apply when making use of your own native libraries, but some details may vary from project to project.Build Your Own Binaries
When building your own libraries, it is evident that all the libraries in your project, both Qt, Qt Jambi and your own generated libraries are compiled with the same compiler based on the same configuration of Qt. Do not use the prebuilt binary package for this kind of distribution. Compiling every part of with the same configuration avoids any binary compatibility issues. How to build Qt and Qt Jambi is documented in the Building Qt Jambi from a Source Package document.Create a Platform Archive
By default, the Qt Jambi ant build script will generate a platform archive containing all the Qt and Qt Jambi libraries. This is done using some Qt Jambi specific ant tasks, namely the com.trolltech.tools.ant.PlatformJarTask, responsible for packaging the libraries, and the com.trolltech.tools.ant.InitializeTask, responsible for setting up custom variables, etc required by the packaging process. <taskdef name="qtjambi-platform-jar"
classpath="ant-qtjambi.jar"
classname="com.trolltech.tools.ant.PlatformJarTask"/>
<taskdef name="qtjambi-initialize"
classpath="ant-qtjambi.jar"
classname="com.trolltech.tools.ant.InitializeTask"/>
These steps define the Qt Jambi ant tasks in the ant-qtjambi.jar archive as tasks for our ant script. The ant-qtjambi.jar file is built as part of the default build in the source package and is available once the source package is built. <qtjambi-initialize verbose="true" />
Then at a later point, we go on to building the platform archive. This is done in three separate steps, first we define platform archive, but specifying which libraries should go into it and what kind of libraries they are. These libraries along with a generated deployment specification are copied to a temporary output directory. Then we bundle these libraries into our custom platform archive file, and finally we remove the temporary directory.<target name="qtjambi-customplatform.jar"
description="Creating .jar file with native libs...">
<qtjambi-platform-jar cacheKey="MyCompany.Application.${DSTAMP}.${TSTAMP}"
outdir="platform-output">
The qtjambi-platform-jar section above starts the specification of the platform archive. One important piece here is the cacheKey, which is used to uniqly identify this set of libraries in the cache. If this key is not unique this platform archive may conflict with a different platform archive and the resulting application will most likely fail to run. <!-- Qt Libraries... -->
<library name="QtCore"
type="qt"
rootPath="${qtjambi.qtdir}" />
<library name="QtGui"
type="qt"
rootPath="${qtjambi.qtdir}" />
We specify the name of the library, excluding the platform specific suffix and extensions. The type of these libraries is qt, which means that we will assume the naming convention listed for Qt libraries above. Because plugins can have RPATH or similar set to load Qt Libraries from the bin or lib subdirectories, it is important that this directory structure is maintained in the final platform archive as well. We therefore only specify the rootPath, which describes the base on the directory structure and let the platform task figure out the correct subdirectory for the library. On windows for instance, the above results in files the following files inside the .jar file: bin/QtCore4.dll
bin/QtGui4.dll
Then we move on to specify the jpeg plugin. <!-- Qt Plugins... -->
<library name="qjpeg"
type="plugin"
rootPath="${qtjambi.qtdir}"
subdir="plugins/imageformats"
load="never"/>
<plugin path="plugins" />
In this case we override the operating system specific library subdirectory with the precise path to the plugin, since plugins don't follow the default library location. We also specify that the type of the library is a plugin which means that the library follows the naming convention for plugins. The we specify that the library should not be loaded explicitly. This is merly a safty measure, which will give us a runtime warning later if we try to explicitly load it ourselves. Finally we specify that the plugins subdirectory contains Qt C++ plugins, which tells Qt that the this part of the platform archive should be used by Qt to load C++ plugins. <!-- Qt Jambi Libraries -->
<library name="qtjambi" type="qtjambi" rootPath="${env.JAMBIDIR}" />
<library name="com_trolltech_qt_core" type="qtjambi" rootPath="${env.JAMBIDIR}" />
<library name="com_trolltech_qt_gui" type="qtjambi" rootPath="${env.JAMBIDIR}" />
<library name="customlibrary" type="qtjambi" rootPath="." subdir="." />
</qtjambi-platform-jar>
The rootPath of the jambi libraries are referring to the environment variable JAMBIDIR which we assume is set in this case. The type of the libraries are qtjambi which means that they follow normal JNI naming rules for this platform, as specified in the table above. In addition we include customlibrary from the current directory. That is the final part of the qtjambi-platform-jar sub-task and we close it. <jar destfile="mycustomjar-${qtjambi.osname}-${qtjambi.compiler}-${qtjambi.version}.jar">
<fileset dir="platform-output" />
</jar>
<delete dir="platform-output"/>
</target>
Using these steps in an ant build script, building a custom platform archive is pretty straight forward.Creating Webstart and Executable Archives
Once the previous step is completed you have your own platform archive and can perform the steps outlined under section Deploying a Pure Qt Jambi Application above and replace the qtjambi platform archive with our own archives.Deploying a Native Application using Qt Jambi
This section describes the less used scenario where a C++ application instantiates a virtual machine and starts making use of Qt Jambi. This is the case with for instance our own Qt Designer. The main problem in this case is that Qt will already be loaded into the process as part of the C++ application, prior to Qt Jambi starting. If loading is done via a platform archive, Qt Jambi will try to load the same libraries again, and the process will fail. The platform archive approach can for this reason \b not be used for using Qt Jambi in native applications.Building the Sources
As with the section Deploying a Qt Jambi Application making use of generated code, it is important that Qt, Qt Jambi and the application is built using the same compiler and configuration.Configuring the Library Path
For Qt Jambi to be able to load libraries, they need to be available through -Djava.library.path. This is for instance done by setting the environment variables: Windows PATH Linux LD_LIBRARY_PATH Mac OS X DYLD_LIBRARY_PATH Setting the Plugin Path
One also need to set up the plugin path, either by specifying the environment variable QT_PLUGIN_PATH or by making explicit calls to QApplication.addLibraryPath(), if the application needs to find the Qt C++ Plugins.Troubleshooting
The most common problem when deploying is that the native libraries are not found. With the archive based deployment techniques this should be significantly simpler that in previous version of Qt Jambi, but in the event something failing, there are some standard topics to investigate to see where it fails. Including Resources
Qt Jambi provides a complete file system abstraction that allows a uniform syntax (based on the Java classpath) for accessing resources, whether they are located directly on the disk or in a JAR bundle. While the standard Java API only supports accessing resources in an undocumented subset of its file I/O operations (which does not include the java.io.File class), Qt Jambi allows resources to be used wherever a file name is expected. Resources are identified by a classpath: prefix.
Note that it is also possible to load resources as raw data: QFile file = new QFile("classpath:images/fileopen.png");
file.open(QIODevice.OpenModeFlag.ReadOnly);
QByteArray rawData = file.readAll();
To include resources in your distribution, all you have to do is to ensure that the application follows the Qt Jambi syntax for accessing resources and make the resources available by adding them to your JAR bundle.
Copyright © 2008 Trolltech
Trademarks