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