1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 package com.sun.star.lib.loader; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.UnsupportedEncodingException; 34 import java.lang.reflect.InvocationTargetException; 35 import java.lang.reflect.Method; 36 import java.net.JarURLConnection; 37 import java.net.MalformedURLException; 38 import java.net.URL; 39 import java.net.URLClassLoader; 40 import java.util.Enumeration; 41 import java.util.jar.Attributes; 42 import java.util.jar.Manifest; 43 import java.util.StringTokenizer; 44 import java.util.Vector; 45 46 /** 47 * This class can be used as a loader for application classes which use UNO. 48 * 49 * <p>The Loader class detects a UNO installation on the system and adds the 50 * UNO jar files to the search path of a customized class loader, which is used 51 * for loading the application classes.</p> 52 */ 53 public final class Loader { 54 55 private static ClassLoader m_Loader = null; 56 57 /** 58 * do not instantiate 59 */ 60 private Loader() {} 61 62 /** 63 * The main method instantiates a customized class loader with the 64 * UNO jar files added to the search path and loads the application class, 65 * which is specified in the Main-Class attribute of the 66 * com/sun/star/lib/Loader.class entry of the manifest file or 67 * as first parameter in the argument list. 68 */ 69 public static void main( String[] arguments ) throws Exception { 70 71 // get the name of the class to be loaded from the manifest 72 String className = null; 73 Class clazz = Loader.class; 74 ClassLoader loader = clazz.getClassLoader(); 75 Vector res = new Vector(); 76 try { 77 Enumeration en = loader.getResources( "META-INF/MANIFEST.MF" ); 78 while ( en.hasMoreElements() ) { 79 res.add( (URL) en.nextElement() ); 80 } 81 // the jarfile with the com/sun/star/lib/loader/Loader.class 82 // per-entry attribute is most probably the last resource in the 83 // list, therefore search backwards 84 for ( int i = res.size() - 1; i >= 0; i-- ) { 85 URL jarurl = (URL) res.elementAt( i ); 86 try { 87 JarURLConnection jarConnection = 88 (JarURLConnection) jarurl.openConnection(); 89 Manifest mf = jarConnection.getManifest(); 90 Attributes attrs = (Attributes) mf.getAttributes( 91 "com/sun/star/lib/loader/Loader.class" ); 92 if ( attrs != null ) { 93 className = attrs.getValue( "Application-Class" ); 94 if ( className != null ) 95 break; 96 } 97 } catch ( IOException e ) { 98 // if an I/O error occurs when opening a new 99 // JarURLConnection, ignore this manifest file 100 System.err.println( "com.sun.star.lib.loader.Loader::" + 101 "main: bad manifest file: " + e ); 102 } 103 } 104 } catch ( IOException e ) { 105 // if an I/O error occurs when getting the manifest resources, 106 // try to get the name of the class to be loaded from the argument 107 // list 108 System.err.println( "com.sun.star.lib.loader.Loader::" + 109 "main: cannot get manifest resources: " + e ); 110 } 111 112 // if no manifest entry was found, get the name of the class 113 // to be loaded from the argument list 114 String[] args; 115 if ( className == null ) { 116 if ( arguments.length > 0 ) { 117 className = arguments[0]; 118 args = new String[arguments.length - 1]; 119 System.arraycopy( arguments, 1, args, 0, args.length ); 120 } else { 121 throw new IllegalArgumentException( 122 "The name of the class to be loaded must be either " + 123 "specified in the Main-Class attribute of the " + 124 "com/sun/star/lib/loader/Loader.class entry " + 125 "of the manifest file or as a command line argument." ); 126 } 127 } else { 128 args = arguments; 129 } 130 131 // load the class with the customized class loader and 132 // invoke the main method 133 if ( className != null ) { 134 ClassLoader cl = getCustomLoader(); 135 Thread.currentThread().setContextClassLoader(cl); 136 Class c = cl.loadClass( className ); 137 Method m = c.getMethod( "main", new Class[] { String[].class } ); 138 m.invoke( null, new Object[] { args } ); 139 } 140 } 141 142 /** 143 * Gets the customized class loader with the UNO jar files added to the 144 * search path. 145 * 146 * @return the customized class loader 147 */ 148 public static synchronized ClassLoader getCustomLoader() { 149 150 final String CLASSESDIR = "classes"; 151 final String JUHJAR = "juh.jar"; 152 153 if ( m_Loader == null ) { 154 155 // get the urls from which to load classes and resources 156 // from the class path 157 Vector vec = new Vector(); 158 String classpath = null; 159 try { 160 classpath = System.getProperty( "java.class.path" ); 161 } catch ( SecurityException e ) { 162 // don't add the class path entries to the list of class 163 // loader URLs 164 System.err.println( "com.sun.star.lib.loader.Loader::" + 165 "getCustomLoader: cannot get system property " + 166 "java.class.path: " + e ); 167 } 168 if ( classpath != null ) { 169 addUrls(vec, classpath, File.pathSeparator); 170 } 171 172 // get the urls from which to load classes and resources 173 // from the UNO installation 174 String path = InstallationFinder.getPath(); 175 if ( path != null ) { 176 File fClassesDir = new File( path, CLASSESDIR ); 177 File fJuh = new File( fClassesDir, JUHJAR ); 178 if ( fJuh.exists() ) { 179 URL[] clurls = new URL[1]; 180 try { 181 clurls[0] = fJuh.toURL(); 182 ClassLoader cl = new CustomURLClassLoader( clurls ); 183 Class c = cl.loadClass( 184 "com.sun.star.comp.helper.UnoInfo" ); 185 Method m = c.getMethod( "getJars", (Class[]) null ); 186 URL[] jarurls = (URL[]) m.invoke( 187 null, (Object[]) null ); 188 for ( int i = 0; i < jarurls.length; i++ ) { 189 vec.add( jarurls[i] ); 190 } 191 } catch ( MalformedURLException e ) { 192 // don't add the UNO jar files to the list of class 193 // loader URLs 194 System.err.println( "com.sun.star.lib.loader.Loader::" + 195 "getCustomLoader: cannot add UNO jar files: " + e ); 196 } catch ( ClassNotFoundException e ) { 197 // don't add the UNO jar files to the list of class 198 // loader URLs 199 System.err.println( "com.sun.star.lib.loader.Loader::" + 200 "getCustomLoader: cannot add UNO jar files: " + e ); 201 } catch ( NoSuchMethodException e ) { 202 // don't add the UNO jar files to the list of class 203 // loader URLs 204 System.err.println( "com.sun.star.lib.loader.Loader::" + 205 "getCustomLoader: cannot add UNO jar files: " + e ); 206 } catch ( IllegalAccessException e ) { 207 // don't add the UNO jar files to the list of class 208 // loader URLs 209 System.err.println( "com.sun.star.lib.loader.Loader::" + 210 "getCustomLoader: cannot add UNO jar files: " + e ); 211 } catch ( InvocationTargetException e ) { 212 // don't add the UNO jar files to the list of class 213 // loader URLs 214 System.err.println( "com.sun.star.lib.loader.Loader::" + 215 "getCustomLoader: cannot add UNO jar files: " + e ); 216 } 217 } else { 218 callUnoinfo(path, vec); 219 } 220 } else { 221 System.err.println( "com.sun.star.lib.loader.Loader::" + 222 "getCustomLoader: no UNO installation found!" ); 223 } 224 225 // copy urls to array 226 URL[] urls = new URL[vec.size()]; 227 vec.toArray( urls ); 228 229 // instantiate class loader 230 m_Loader = new CustomURLClassLoader( urls ); 231 } 232 233 return m_Loader; 234 } 235 236 private static void addUrls(Vector urls, String data, String delimiter) { 237 StringTokenizer tokens = new StringTokenizer( data, delimiter ); 238 while ( tokens.hasMoreTokens() ) { 239 try { 240 urls.add( new File( tokens.nextToken() ).toURL() ); 241 } catch ( MalformedURLException e ) { 242 // don't add this class path entry to the list of class loader 243 // URLs 244 System.err.println( "com.sun.star.lib.loader.Loader::" + 245 "getCustomLoader: bad pathname: " + e ); 246 } 247 } 248 } 249 250 private static void callUnoinfo(String path, Vector urls) { 251 Process p; 252 try { 253 p = Runtime.getRuntime().exec( 254 new String[] { new File(path, "unoinfo").getPath(), "java" }); 255 } catch (IOException e) { 256 System.err.println( 257 "com.sun.star.lib.loader.Loader::getCustomLoader: exec" + 258 " unoinfo: " + e); 259 return; 260 } 261 new Drain(p.getErrorStream()).start(); 262 int code; 263 byte[] buf = new byte[1000]; 264 int n = 0; 265 try { 266 InputStream s = p.getInputStream(); 267 code = s.read(); 268 for (;;) { 269 if (n == buf.length) { 270 if (n > Integer.MAX_VALUE / 2) { 271 System.err.println( 272 "com.sun.star.lib.loader.Loader::getCustomLoader:" + 273 " too much unoinfo output"); 274 return; 275 } 276 byte[] buf2 = new byte[2 * n]; 277 for (int i = 0; i < n; ++i) { 278 buf2[i] = buf[i]; 279 } 280 buf = buf2; 281 } 282 int k = s.read(buf, n, buf.length - n); 283 if (k == -1) { 284 break; 285 } 286 n += k; 287 } 288 } catch (IOException e) { 289 System.err.println( 290 "com.sun.star.lib.loader.Loader::getCustomLoader: reading" + 291 " unoinfo output: " + e); 292 return; 293 } 294 int ev; 295 try { 296 ev = p.waitFor(); 297 } catch (InterruptedException e) { 298 Thread.currentThread().interrupt(); 299 System.err.println( 300 "com.sun.star.lib.loader.Loader::getCustomLoader: waiting for" + 301 " unoinfo: " + e); 302 return; 303 } 304 if (ev != 0) { 305 System.err.println( 306 "com.sun.star.lib.loader.Loader::getCustomLoader: unoinfo" 307 + " exit value " + n); 308 return; 309 } 310 String s; 311 if (code == '0') { 312 s = new String(buf); 313 } else if (code == '1') { 314 try { 315 s = new String(buf, "UTF-16LE"); 316 } catch (UnsupportedEncodingException e) { 317 System.err.println( 318 "com.sun.star.lib.loader.Loader::getCustomLoader:" + 319 " transforming unoinfo output: " + e); 320 return; 321 } 322 } else { 323 System.err.println( 324 "com.sun.star.lib.loader.Loader::getCustomLoader: bad unoinfo" 325 + " output"); 326 return; 327 } 328 addUrls(urls, s, "\0"); 329 } 330 331 private static final class Drain extends Thread { 332 public Drain(InputStream stream) { 333 super("unoinfo stderr drain"); 334 this.stream = stream; 335 } 336 337 public void run() { 338 try { 339 while (stream.read() != -1) {} 340 } catch (IOException e) { /* ignored */ } 341 } 342 343 private final InputStream stream; 344 } 345 346 /** 347 * A customized class loader which is used to load classes and resources 348 * from a search path of user-defined URLs. 349 */ 350 private static final class CustomURLClassLoader extends URLClassLoader { 351 352 public CustomURLClassLoader( URL[] urls ) { 353 super( urls ); 354 } 355 356 protected Class findClass( String name ) throws ClassNotFoundException { 357 // This is only called via this.loadClass -> super.loadClass -> 358 // this.findClass, after this.loadClass has already called 359 // super.findClass, so no need to call super.findClass again: 360 throw new ClassNotFoundException( name ); 361 } 362 363 protected Class loadClass( String name, boolean resolve ) 364 throws ClassNotFoundException 365 { 366 Class c = findLoadedClass( name ); 367 if ( c == null ) { 368 try { 369 c = super.findClass( name ); 370 } catch ( ClassNotFoundException e ) { 371 return super.loadClass( name, resolve ); 372 } catch ( SecurityException e ) { 373 // A SecurityException "Prohibited package name: java.lang" 374 // may occur when the user added the JVM's rt.jar to the 375 // java.class.path: 376 return super.loadClass( name, resolve ); 377 } 378 } 379 if ( resolve ) { 380 resolveClass( c ); 381 } 382 return c; 383 } 384 } 385 } 386