Hi, In the last fews days i investigated in my spare time how to make QT-Jambi run smoothly on OSGi (on Equinox - it might fail on other OSGI-implementations) and finally succeeded.
Attached are the modifications I made to the standard Jambi-Sources. Tom
/**************************************************************************** ** ** Copyright (C) 1992-2009 Nokia. All rights reserved. ** ** This file is part of Qt Jambi. ** ** ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at [email protected]. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ package com.trolltech.qt.internal; import java.io.*; import java.util.*; import java.util.jar.*; import java.net.*; import java.security.*; import javax.xml.parsers.*; import org.xml.sax.helpers.*; import com.trolltech.qt.Utilities; // !!NOTE!! This class can have no dependencies on Qt since // it is required for loading the libraries. class DeploymentSpecException extends RuntimeException { private static final long serialVersionUID = 1L; public DeploymentSpecException(String msg) { super(msg); } } class WrongSystemException extends DeploymentSpecException { private static final long serialVersionUID = 1L; public WrongSystemException(String msg) { super(msg); } } /** * The NativeLibraryManager class is responsible for handling native * libraries in Qt Jambi. Native libraries can be loaded either * directly from the file system using * <code>-Djava.library.path</code> or indirectly JAR-file that * contain a deployment descriptor. For normal deployment, the JAR * approach is recommended. * * Loading libraries is done by calling the methods * <code>loadQtLibrary</code> and <code>loadLibrary</code>. * * When the indirect .jar file approach is taken, the .jar file will * be opened and the native library manager will search for a file * named "qtjambi-deployment.xml". This file contains a list of native * libraries that should unpacked to a temporary directory and loaded, * either right away or at a later time. There are three types of * libraries. * * <ll> * * <li> System libraries; such as the system runtime * libraries. These libraries are usually loaded automatically by * the native library loader. * * <li> Normal libraries; such as the QtCore and * com_trolltech_qt_core libraries. These are loaded at runtime on * demand. * * <li> Plugins; such as qjpeg. These are never loaded explicitly by * the native library manager, but are unpacked into the temporary * folder so that Qt can find and load them from the file system. * * </ll> * * There are three possible deployment scenarios. The simplest and * most straightforward approach is when deploying a Pure Java * application based on Qt Jambi. In this case the prebuilt binaries * from the binary package can just be deployed as part of the * classpath and the rest will solve itself. * * When deploying a Qt Jambi application that is using native code * other than Qt Jambi, we recommend building a new .jar file with a * custom qtjambi-deployment.xml which contais the Qt Jambi libraries * and the custom native libraries. Deployment can then be done by * making sure that this new .jar file is available in the classpath. * * The final option for deployment is when users have a C++ * application which starts and makes use of Qt Jambi. In this case we * suggest that all dependent libraries are available in the file * system and via <code>-Djava.library.path<code> * * To get runtime information about how library loading works, specify * the <code>-Dcom.trolltech.qt.verbose-loading</code> system property * to the Virtual Machine. It possible to specify that the native * library manager should load debug versions of libraries as * well. This is done by specifying the system property * </code>-Dcom.trolltech.qt.debug</code> * */ public class NativeLibraryManager { public static String DEPLOY_DESCRIPTOR_NAME = "qtjambi-deployment.xml"; private static final String DEBUG_SUFFIX = "_debuglib"; private static final boolean VERBOSE_LOADING = System.getProperty("com.trolltech.qt.verbose-loading") != null; private static final int LOAD_TRUE = 1; private static final int LOAD_FALSE = 2; private static final int LOAD_NEVER = 3; private static class LibraryEntry { public String name; public int load; public DeploymentSpec spec; public boolean loaded; public boolean isOsgi; } private static class DeploymentSpec { public String key; public String jarName; public List<LibraryEntry> libraries; public List<String> pluginPaths; public void addPluginPath(String path) { if (pluginPaths == null) pluginPaths = new ArrayList<String>(); pluginPaths.add(path); reporter.report(" - plugin path='", path, "'"); } public void addLibraryEntry(LibraryEntry e) { if (libraries == null) libraries = new ArrayList<LibraryEntry>(); libraries.add(e); reporter.report(" - library: name='", e.name, "', ", (e.load == LOAD_TRUE ? "load" : (e.load == LOAD_NEVER ? "never load" : "")) ); } } private static class XMLHandler extends DefaultHandler { public DeploymentSpec spec; public void startElement(String uri, String localName, String name, org.xml.sax.Attributes attributes) { if (name.equals("cache")) { String key = attributes.getValue("key"); if (key == null) { throw new DeploymentSpecException("<cache> element missing required attribute \"key\""); } spec.key = key; reporter.report(" - cache key='", spec.key, "'"); } else if (name.equals("library")) { LibraryEntry e = new LibraryEntry(); e.name = attributes.getValue("name"); if (e.name == null) { throw new DeploymentSpecException("<library> element missing required attribute \"name\""); } String load = attributes.getValue("load"); if (load != null && load.equals("true")) e.load = LOAD_TRUE; else if (load != null && load.equals("never")) e.load = LOAD_NEVER; else e.load = LOAD_FALSE; e.spec = spec; String fileName = new File(e.name).getName(); if (e.load == LOAD_NEVER) { neverLoad.put(fileName, e); } else { // Add library name to the global map of libraries... LibraryEntry old = libraryMap.get(fileName); if (old != null) { throw new DeploymentSpecException("<library> '" + e.name + "' is duplicated. Present in both '" + spec.jarName + "' and '" + old.spec.jarName + "'."); } reporter.report(" - adding '", fileName, "' to library map"); libraryMap.put(fileName, e); } spec.addLibraryEntry(e); } else if (name.equals("plugin")) { String path = attributes.getValue("path"); if (path == null) { throw new DeploymentSpecException("<plugin> element missing required attribute \"path\""); } spec.addPluginPath(path); } else if (name.equals("qtjambi-deploy")) { String system = attributes.getValue("system"); if (system == null || system.length() == 0) { throw new DeploymentSpecException("<qtjambi-deploy> element missing required attribute 'system'"); } else if (!system.equals(OSInfo.osArchName())) { throw new WrongSystemException("trying to load: '" + system + "', expected: '" + OSInfo.osArchName() + "'"); } } } } private static class ChecksumFileFilter implements FilenameFilter { public boolean accept(File dir, String name) { return name.endsWith(".chk"); } } /** * Returns a file that is used for caching native libraries. The * path is a subdirectory of <code>java.io.tmpdir</code>, named * <code>QtJambi_{user}_{architecture}_{version}_{key}</code>. The * key is the same as the cache key used in the deployment * descriptor. The purpose of using different keys is to avoid * binary compatibility when various configurations of Qt and Qt * Jambi are used on the same system. * * When deployment descriptors are not used, this location will * not be used. * * @param key The cache key to. * @return A File representing the location directory for * temporary content. The directory is not explicitly created in * this here. */ public static File jambiTempDirBase(String key) { File tmpDir = new File(System.getProperty("java.io.tmpdir")); String user = System.getProperty("user.name"); String arch = System.getProperty("os.arch"); return new File(tmpDir, "QtJambi_" + user + "_" + arch + "_" + Utilities.VERSION_STRING + "_" + key); } /** * Returns the list of all plugin paths that are specified in the * deployment descriptors. If deployment descriptors are not used, * this list will be an empty list. * * @return The list of plugin paths */ public static List<String> pluginPaths() { List<String> paths = new ArrayList<String>(); for (DeploymentSpec spec : deploymentSpecs) { File root = jambiTempDirBase(spec.key); if (spec.pluginPaths != null) for (String path : spec.pluginPaths) paths.add(new File(root, path).getAbsolutePath()); } return paths; } /** * Loads a library with name specified in <code>library</code>. * The library name will be expanded to the JNI shared library * name for a given platform, so the name "qtjambi" will be * expanded like this: * * <ll> * <li> Windows: qtjambi.dll * <li> Linux / Unix: libqtjambi.so * <li> Mac OS X: libqtjambi.jnilib * </ll> * * When using loading libraries from the filesystem, this method * simply calls <code>System.loadLibrary</code>. * * When the system property <code>-Dcom.trolltech.qt.debug</code> * is specified, the suffix <code>_debuglib</code> will be appended * to the filename, replacing "qtjambi" above with "qtjambi_debuglib". * * @param library The name of the library.. */ public static void loadLibrary(String library) { if (Utilities.configuration == Utilities.Configuration.Debug) library += DEBUG_SUFFIX; String lib = jniLibraryName(library); loadNativeLibrary(lib); } /** * Overload which passes the default value of "4" as the version */ public static void loadQtLibrary(String library) { loadQtLibrary(library, "4"); } /** * Loads a library with name specified in <code>library</code>. * The library name will be expanded to the default shared library * name for a given platform, so the name "QtCore" and version "4" will be * expanded like this: * * <ll> * <li> Windows: QtCore4.dll * <li> Linux / Unix: libQtCore.so.4 * <li> Mac OS X: libQtCore.4.dylib * </ll> * * When using loading libraries from the filesystem, this method * simply calls <code>System.loadLibrary</code>. * * @param library The name of the library.. */ public static void loadQtLibrary(String library, String version) { String lib = qtLibraryName(library, version); loadNativeLibrary(lib); } private static void unpack() { if (unpacked) return; try { unpack_helper(); if (VERBOSE_LOADING) System.out.println(reporter.recentReports()); unpacked = true; } catch (Throwable t) { throw new RuntimeException("Failed to unpack native libraries, progress so far:\n" + reporter, t); } } private static void unpack_helper() throws Exception { ClassLoader loader = classLoader(); Enumeration<URL> specs = loader.getResources(DEPLOY_DESCRIPTOR_NAME); int count = 0; while (specs.hasMoreElements()) { URL url = specs.nextElement(); if (url.getProtocol().equals("jar")) { String eform = url.toExternalForm(); // Try to decide the name of the .jar file to have a // reference point for later.. int end = eform.length() - DEPLOY_DESCRIPTOR_NAME.length() - 2; int start = eform.lastIndexOf('/', end - 1) + 1; if (start > 0 && start < end) { String jarName = eform.substring(start, end); if (VERBOSE_LOADING) reporter.report("Loading ", jarName, " from ", eform); unpackDeploymentSpec(url, jarName); ++count; } } else if( url.getProtocol().equals("bundleresource") ) { String eform = url.toExternalForm(); // Try to decide the name of the .jar file to have a // reference point for later.. int end = eform.length() - DEPLOY_DESCRIPTOR_NAME.length() - 2; int start = eform.lastIndexOf('/', end - 1) + 1; if (start > 0 && start < end) { String jarName = eform.substring(start, end); if (VERBOSE_LOADING) reporter.report("Loading ", jarName, " from ", eform); unpackDeploymentSpecOSGi(url,jarName); ++count; } } } if (count == 0) { reporter.report("No '", DEPLOY_DESCRIPTOR_NAME, "' found in classpath, loading libraries via 'java.library.path'"); } } /** * Returns a classloader for current context... * @return The classloader */ private static ClassLoader classLoader() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = NativeLibraryManager.class.getClassLoader(); assert loader != null; } return loader; } /** * Tries to load the specified library. It will first try to load * the library using the deploy spec, and if that fails, it will * try to load the library using a standard System.loadLibrary() * call. * @param lib The full name of the library to load, such as libQtCore.so.4 */ private static void loadNativeLibrary(String lib) { try { loadLibrary_helper(lib); if (VERBOSE_LOADING) System.out.println(reporter.recentReports()); } catch (Throwable e) { throw new RuntimeException("Loading library failed, progress so far:\n" + reporter, e); } } private static void loadLibrary_helper(String lib) { unpack(); reporter.report("Loading library: '", lib, "'..."); // First of all verify that we're allowed to load this library... LibraryEntry e = neverLoad.get(lib); if (e != null) { throw new RuntimeException("Library '" + lib + "' cannot be loaded, deploy spec"); } // Try to load via deploy spec... e = libraryMap.get(lib); if (e != null) { if (e.loaded) { reporter.report(" - already loaded, skipping..."); return; } reporter.report(" - using deployment spec"); if( ! e.isOsgi ) { File libFile = new File(jambiTempDirBase(e.spec.key), e.name); Runtime.getRuntime().load(libFile.getAbsolutePath()); } else { System.loadLibrary(new File(e.name).getName()); } reporter.report(" - ok!"); e.loaded = true; // Load via System.load() using default paths.. } else { boolean loaded = false; String libPaths = System.getProperty("com.trolltech.qt.library-path-override"); if (libPaths != null && libPaths.length() > 0) { reporter.report(" - using 'com.trolltech.qt.library-path-override"); } else { reporter.report(" - using 'java.library.path'"); libPaths = System.getProperty("java.library.path"); } if (libPaths != null) { String paths[] = RetroTranslatorHelper.split(libPaths, File.pathSeparator); for (String path : paths) { File f = new File(path, lib); if (f.exists()) { Runtime.getRuntime().load(f.getAbsolutePath()); reporter.report(" - ok, path was: " + f.getAbsolutePath()); loaded = true; break; } } } if (!loaded) { throw new RuntimeException("Library '" + lib +"' was not found in 'java.library.path'=" + libPaths); } } } private static DeploymentSpec readDeploySpec(URL url, String jarName) throws Exception { reporter.report("Checking Archive '", jarName, "'"); DeploymentSpec spec = new DeploymentSpec(); spec.jarName = jarName; SAXParserFactory fact = SAXParserFactory.newInstance(); SAXParser parser = fact.newSAXParser(); XMLHandler handler = new XMLHandler(); handler.spec = spec; try { parser.parse(url.openStream(), handler); if (spec.key == null) { throw new DeploymentSpecException("Deployment Specification doesn't include required <cache key='...'/>"); } deploymentSpecs.add(spec); return spec; } catch (WrongSystemException e) { reporter.report(" - skipping because of wrong system: " + e.getMessage()); return null; } } private static void unpackDeploymentSpecOSGi(URL deploymentSpec, String jarName) throws Exception { DeploymentSpec spec = readDeploySpec(deploymentSpec, jarName); if( spec == null ) return; for (LibraryEntry e : spec.libraries) { e.isOsgi=true; } } private static void unpackDeploymentSpec(URL deploymentSpec, String jarName) throws Exception { reporter.report("Unpacking .jar file: '", jarName, "'"); DeploymentSpec spec = readDeploySpec(deploymentSpec, jarName); if (spec == null) return; File tmpDir = jambiTempDirBase(spec.key); reporter.report(" - using cache directory: '", tmpDir.getAbsolutePath(), "'"); boolean shouldCopy = false; // If the dir exists, sanity check the contents... if (tmpDir.exists()) { reporter.report(" - cache directory exists"); } else { shouldCopy = true; } // If the dir doesn't exists or it was only half completed, copy the files over... if (shouldCopy) { reporter.report(" - starting to copy content to cache directory..."); for (LibraryEntry e : spec.libraries) { reporter.report(" - copying over: '", e.name, "'..."); InputStream in = null; Enumeration<URL> resources = classLoader().getResources(e.name); while (resources.hasMoreElements()) { URL url = resources.nextElement(); String eform = url.toExternalForm(); if (eform.contains(jarName)) { in = url.openStream(); reporter.report(" - matched url: ", url.toExternalForm()); } else if (VERBOSE_LOADING) { reporter.report(" - unmatched .jar file: ", eform); } } if (in == null) { throw new FileNotFoundException("Library '" + e.name + "' specified in qtjambi-deployment.xml in '" + jarName + "' does not exist"); } File outFile = new File(tmpDir, e.name); File outFileDir = outFile.getParentFile(); if (!outFileDir.exists()) { reporter.report(" - creating directory: ", outFileDir.getAbsolutePath()); outFileDir.mkdirs(); } OutputStream out = new FileOutputStream(new File(tmpDir, e.name)); copy(in, out); } } // Load the libraries tagged for loading... Runtime rt = Runtime.getRuntime(); for (LibraryEntry e : spec.libraries) { if (e.load == LOAD_TRUE) { reporter.report(" - trying to load: ", e.name); File f = new File(tmpDir, e.name); rt.load(f.getAbsolutePath()); reporter.report(" - ok!"); } } } private static String jniLibraryName(String lib) { switch (Utilities.operatingSystem) { case Windows: return lib + ".dll"; case MacOSX: return "lib" + lib + ".jnilib"; case Linux: return "lib" + lib + ".so"; } throw new RuntimeException("Unreachable statement"); } private static String qtLibraryName(String lib, String version) { switch (Utilities.operatingSystem) { case Windows: return Utilities.configuration == Utilities.Configuration.Debug ? lib + "d" + version + ".dll" : lib + version + ".dll"; case MacOSX: return Utilities.configuration == Utilities.Configuration.Debug ? "lib" + lib + "_debug." + version + ".dylib" : "lib" + lib + "." + version + ".dylib"; case Linux: // Linux doesn't have a dedicated "debug" library since 4.2 return "lib" + lib + ".so." + version; } throw new RuntimeException("Unreachable statement"); } private static String stripLibraryName(String lib) { // Strip away "lib" prefix if (Utilities.operatingSystem != Utilities.OperatingSystem.Windows) lib = lib.substring(3); int dot = -1; switch (Utilities.operatingSystem) { case Windows: dot = lib.indexOf(".dll"); break; case Linux: dot = lib.indexOf(".so"); break; case MacOSX: dot = lib.indexOf("."); // makes a fair attemt at matching /.[0-9]*.(jni)|(dy)lib/ break; } // Strip away the library postfix... return lib.substring(0, dot); } /** * Copies the data in the inputstream into the output stream. * @param in The source. * @param out The destination. * * @throws IOException when there is a problem... */ private static void copy(InputStream in, OutputStream out) throws IOException { byte buffer[] = new byte[1024 * 64]; while (in.available() > 0) { int read = in.read(buffer); out.write(buffer, 0, read); } in.close(); out.close(); } public static boolean isUsingDeploymentSpec() { unpack(); return deploymentSpecs != null && deploymentSpecs.size() != 0; } private static Map<String, LibraryEntry> libraryMap = new HashMap<String, LibraryEntry>(); private static Map<String, LibraryEntry> neverLoad = new HashMap<String, LibraryEntry>(); private static List<DeploymentSpec> deploymentSpecs = new ArrayList<DeploymentSpec>(); private static Reporter reporter = new Reporter(); private static boolean unpacked = false; public static void main(String args[]) throws Exception { unpack(); loadQtLibrary("QtCore"); loadQtLibrary("QtGui"); loadLibrary("qtjambi"); loadLibrary("com_trolltech_qt_core"); loadLibrary("com_trolltech_qt_gui"); loadQtLibrary("QtGui"); loadQtLibrary("QtNetwork"); // loadLibrary("com_trolltech_qt_network"); for (String s : pluginPaths()) System.out.println("PluginPath: " + s); // System.out.println(reporter.toString()); } }
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.0 Created-By: 1.5.0_13-119 (Apple Inc.) Built-By: qt - September 25 2008 Bundle-ManifestVersion: 2 Bundle-Name: QT Jambi Bundle-SymbolicName: com.trolltech.qt.jambi Bundle-Version: 4.4.3 Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Export-Package: com.trolltech.extensions.signalhandler, com.trolltech.qt, com.trolltech.qt.core, com.trolltech.qt.designer, com.trolltech.qt.gui, com.trolltech.qt.network, com.trolltech.qt.opengl, com.trolltech.qt.phonon, com.trolltech.qt.sql, com.trolltech.qt.svg, com.trolltech.qt.webkit, com.trolltech.qt.xml, com.trolltech.qt.xmlpatterns Eclipse-BuddyPolicy: dependent Eclipse-ExtensibleAPI: true
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.0 Created-By: 1.5.0_13-119 (Apple Inc.) Built-By: qt - September 25 2008 Bundle-ManifestVersion: 2 Bundle-Name: QT Jambi Bundle-SymbolicName: com.trolltech.qt.jambi.osx Bundle-Version: 4.4.3 Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Fragment-Host: com.trolltech.qt.jambi Eclipse-PlatformFilter: (osgi.os=macosx) Bundle-NativeCode: lib/libQtCore.4.dylib; lib/libQtGui.4.dylib; lib/libQtNetwork.4.dylib; lib/libQtOpenGL.4.dylib; lib/libQtSql.4.dylib; lib/libQtSvg.4.dylib; lib/libQtWebKit.4.dylib; lib/libQtXml.4.dylib; lib/libQtXmlPatterns.4.dylib; lib/libcom_trolltech_qt_core.jnilib; lib/libcom_trolltech_qt_gui.jnilib; lib/libcom_trolltech_qt_network.jnilib; lib/libcom_trolltech_qt_opengl.jnilib; lib/libcom_trolltech_qt_phonon.jnilib; lib/libcom_trolltech_qt_sql.jnilib; lib/libcom_trolltech_qt_svg.jnilib; lib/libcom_trolltech_qt_webkit.jnilib; lib/libcom_trolltech_qt_xml.jnilib; lib/libcom_trolltech_qt_xmlpatterns.jnilib; lib/libphonon.4.dylib; lib/libqtjambi.jnilib; plugins/accessible/libqtaccessiblewidgets.dylib; plugins/codecs/libqcncodecs.dylib; plugins/codecs/libqjpcodecs.dylib; plugins/codecs/libqkrcodecs.dylib; plugins/codecs/libqtwcodecs.dylib; plugins/iconengines/libqsvgicon.dylib; plugins/imageformats/libqgif.dylib; plugins/imageformats/libqjpeg.dylib; plugins/imageformats/libqmng.dylib; plugins/imageformats/libqsvg.dylib; plugins/imageformats/libqtiff.dylib; plugins/phonon_backend/libphonon_qt7.dylib; plugins/sqldrivers/libqsqlite.dylib
_______________________________________________ Qt-jambi-interest mailing list [email protected] http://lists.trolltech.com/mailman/listinfo/qt-jambi-interest
