/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ package com.sun.star.lib.loader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.StringTokenizer; import java.util.Vector; /** * This class can be used as a loader for application classes which use UNO. * *

The Loader class detects a UNO installation on the system and adds the * UNO jar files to the search path of a customized class loader, which is used * for loading the application classes.

*/ public final class Loader { private static ClassLoader m_Loader = null; /** * do not instantiate */ private Loader() {} /** * The main method instantiates a customized class loader with the * UNO jar files added to the search path and loads the application class, * which is specified in the Main-Class attribute of the * com/sun/star/lib/Loader.class entry of the manifest file or * as first parameter in the argument list. */ public static void main( String[] arguments ) throws Exception { // get the name of the class to be loaded from the manifest String className = null; Class clazz = Loader.class; ClassLoader loader = clazz.getClassLoader(); Vector res = new Vector(); try { Enumeration en = loader.getResources( "META-INF/MANIFEST.MF" ); while ( en.hasMoreElements() ) { res.add( (URL) en.nextElement() ); } // the jarfile with the com/sun/star/lib/loader/Loader.class // per-entry attribute is most probably the last resource in the // list, therefore search backwards for ( int i = res.size() - 1; i >= 0; i-- ) { URL jarurl = (URL) res.elementAt( i ); try { JarURLConnection jarConnection = (JarURLConnection) jarurl.openConnection(); Manifest mf = jarConnection.getManifest(); Attributes attrs = (Attributes) mf.getAttributes( "com/sun/star/lib/loader/Loader.class" ); if ( attrs != null ) { className = attrs.getValue( "Application-Class" ); if ( className != null ) break; } } catch ( IOException e ) { // if an I/O error occurs when opening a new // JarURLConnection, ignore this manifest file System.err.println( "com.sun.star.lib.loader.Loader::" + "main: bad manifest file: " + e ); } } } catch ( IOException e ) { // if an I/O error occurs when getting the manifest resources, // try to get the name of the class to be loaded from the argument // list System.err.println( "com.sun.star.lib.loader.Loader::" + "main: cannot get manifest resources: " + e ); } // if no manifest entry was found, get the name of the class // to be loaded from the argument list String[] args; if ( className == null ) { if ( arguments.length > 0 ) { className = arguments[0]; args = new String[arguments.length - 1]; System.arraycopy( arguments, 1, args, 0, args.length ); } else { throw new IllegalArgumentException( "The name of the class to be loaded must be either " + "specified in the Main-Class attribute of the " + "com/sun/star/lib/loader/Loader.class entry " + "of the manifest file or as a command line argument." ); } } else { args = arguments; } // load the class with the customized class loader and // invoke the main method if ( className != null ) { ClassLoader cl = getCustomLoader(); Thread.currentThread().setContextClassLoader(cl); Class c = cl.loadClass( className ); Method m = c.getMethod( "main", new Class[] { String[].class } ); m.invoke( null, new Object[] { args } ); } } /** * Gets the customized class loader with the UNO jar files added to the * search path. * * @return the customized class loader */ public static synchronized ClassLoader getCustomLoader() { final String CLASSESDIR = "classes"; final String JUHJAR = "juh.jar"; if ( m_Loader == null ) { // get the urls from which to load classes and resources // from the class path Vector vec = new Vector(); String classpath = null; try { classpath = System.getProperty( "java.class.path" ); } catch ( SecurityException e ) { // don't add the class path entries to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot get system property " + "java.class.path: " + e ); } if ( classpath != null ) { addUrls(vec, classpath, File.pathSeparator); } // get the urls from which to load classes and resources // from the UNO installation String path = InstallationFinder.getPath(); if ( path != null ) { File fClassesDir = new File( path, CLASSESDIR ); File fJuh = new File( fClassesDir, JUHJAR ); if ( fJuh.exists() ) { URL[] clurls = new URL[1]; try { clurls[0] = fJuh.toURL(); ClassLoader cl = new CustomURLClassLoader( clurls ); Class c = cl.loadClass( "com.sun.star.comp.helper.UnoInfo" ); Method m = c.getMethod( "getJars", (Class[]) null ); URL[] jarurls = (URL[]) m.invoke( null, (Object[]) null ); for ( int i = 0; i < jarurls.length; i++ ) { vec.add( jarurls[i] ); } } catch ( MalformedURLException e ) { // don't add the UNO jar files to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot add UNO jar files: " + e ); } catch ( ClassNotFoundException e ) { // don't add the UNO jar files to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot add UNO jar files: " + e ); } catch ( NoSuchMethodException e ) { // don't add the UNO jar files to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot add UNO jar files: " + e ); } catch ( IllegalAccessException e ) { // don't add the UNO jar files to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot add UNO jar files: " + e ); } catch ( InvocationTargetException e ) { // don't add the UNO jar files to the list of class // loader URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: cannot add UNO jar files: " + e ); } } else { callUnoinfo(path, vec); } } else { System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: no UNO installation found!" ); } // copy urls to array URL[] urls = new URL[vec.size()]; vec.toArray( urls ); // instantiate class loader m_Loader = new CustomURLClassLoader( urls ); } return m_Loader; } private static void addUrls(Vector urls, String data, String delimiter) { StringTokenizer tokens = new StringTokenizer( data, delimiter ); while ( tokens.hasMoreTokens() ) { try { urls.add( new File( tokens.nextToken() ).toURL() ); } catch ( MalformedURLException e ) { // don't add this class path entry to the list of class loader // URLs System.err.println( "com.sun.star.lib.loader.Loader::" + "getCustomLoader: bad pathname: " + e ); } } } private static void callUnoinfo(String path, Vector urls) { Process p; try { p = Runtime.getRuntime().exec( new String[] { new File(path, "unoinfo").getPath(), "java" }); } catch (IOException e) { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader: exec" + " unoinfo: " + e); return; } new Drain(p.getErrorStream()).start(); int code; byte[] buf = new byte[1000]; int n = 0; try { InputStream s = p.getInputStream(); code = s.read(); for (;;) { if (n == buf.length) { if (n > Integer.MAX_VALUE / 2) { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader:" + " too much unoinfo output"); return; } byte[] buf2 = new byte[2 * n]; for (int i = 0; i < n; ++i) { buf2[i] = buf[i]; } buf = buf2; } int k = s.read(buf, n, buf.length - n); if (k == -1) { break; } n += k; } } catch (IOException e) { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader: reading" + " unoinfo output: " + e); return; } int ev; try { ev = p.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader: waiting for" + " unoinfo: " + e); return; } if (ev != 0) { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader: unoinfo" + " exit value " + n); return; } String s; if (code == '0') { s = new String(buf); } else if (code == '1') { try { s = new String(buf, "UTF-16LE"); } catch (UnsupportedEncodingException e) { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader:" + " transforming unoinfo output: " + e); return; } } else { System.err.println( "com.sun.star.lib.loader.Loader::getCustomLoader: bad unoinfo" + " output"); return; } addUrls(urls, s, "\0"); } private static final class Drain extends Thread { public Drain(InputStream stream) { super("unoinfo stderr drain"); this.stream = stream; } public void run() { try { while (stream.read() != -1) {} } catch (IOException e) { /* ignored */ } } private final InputStream stream; } /** * A customized class loader which is used to load classes and resources * from a search path of user-defined URLs. */ private static final class CustomURLClassLoader extends URLClassLoader { public CustomURLClassLoader( URL[] urls ) { super( urls ); } protected Class findClass( String name ) throws ClassNotFoundException { // This is only called via this.loadClass -> super.loadClass -> // this.findClass, after this.loadClass has already called // super.findClass, so no need to call super.findClass again: throw new ClassNotFoundException( name ); } protected Class loadClass( String name, boolean resolve ) throws ClassNotFoundException { Class c = findLoadedClass( name ); if ( c == null ) { try { c = super.findClass( name ); } catch ( ClassNotFoundException e ) { return super.loadClass( name, resolve ); } catch ( SecurityException e ) { // A SecurityException "Prohibited package name: java.lang" // may occur when the user added the JVM's rt.jar to the // java.class.path: return super.loadClass( name, resolve ); } } if ( resolve ) { resolveClass( c ); } return c; } } }