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