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.unoloader;
25 
26 import java.io.IOException;
27 import java.lang.reflect.InvocationTargetException;
28 import java.net.MalformedURLException;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.util.jar.Attributes;
32 import java.util.jar.Manifest;
33 import java.util.jar.JarInputStream;
34 
35 /**
36  * The root UNO class loader.
37  *
38  * <p>This class loader is able to load all published URE classes, including the
39  * classes representing the published URE UNO types.  For consistency, it is
40  * important that within each Java UNO environment there is one instance of this
41  * class loader that is the defining class loader of all published URE classes
42  * (and hence of all unpublished URE classes, too) and of all classes
43  * representing UNO types (the published URE UNO types, any unpublished URE UNO
44  * types, and any additional UNO types introduced by components; for the latter,
45  * {@link #addURL} may be necessary).</p>
46  *
47  * <p><em>This is an internal, unstable class of the Uno Runtime Environment; it
48  * should not be used by client code.</em></p>
49  *
50  * @since UDK 3.2.0
51  */
52 public final class UnoClassLoader extends URLClassLoader {
53     /**
54      * Instantiates the root UNO class loader.
55      *
56      * @param base a base URL relative to which the URE JARs
57      * (<code>java_uno.jar</code>, <code>juh.jar</code>, <code>jurt.jar</code>,
58      * <code>ridl.jar</code>) can be found; must not be <code>null</code>.
59      *
60      * @param classPath an array of URLs that form the class path of this class
61      * loader; may be <code>null</code>, which is the same as an empty array.
62      * The URLs are interpreted in the same way as the arguments of a {@link
63      * URLClassLoader}.
64      *
65      * @param parent the parent class loader for delegation.
66      *
67      * @throws MalformedURLException if the given <code>base</code> URL is
68      * malformed.
69      */
UnoClassLoader(URL base, URL[] classPath, ClassLoader parent)70     public UnoClassLoader(URL base, URL[] classPath, ClassLoader parent)
71         throws MalformedURLException
72     {
73         super(createUrls(base, classPath), parent);
74     }
75 
76     /**
77      * Obtains a class loader for a UNO JAR.
78      *
79      * @param jar the URL of a UNO JAR; must not be <code>null</code>.
80      *
81      * @param mainAttributes represents the main section of the manifest of the
82      * given JAR <code>jar</code>; <code>null</code> if the given JAR does not
83      * have a manifest.  (This redundant parameter is there for performance
84      * reasons, as typically the caller of this method already has this
85      * information available.)
86      *
87      * @return an appropriate class loader; will never be <code>null</code>.
88      *
89      * @throws MalformedURLException if the given <code>jar</code> URL or any of
90      * the UNO-Type-Path URLs specified in the given JAR are malformed.
91      */
getClassLoader(URL jar, Attributes mainAttributes)92     public ClassLoader getClassLoader(URL jar, Attributes mainAttributes)
93         throws MalformedURLException
94     {
95         String path = mainAttributes == null ?
96             null : mainAttributes.getValue("UNO-Type-Path");
97         if (path == null) {
98             path = "<>";
99         }
100         for (int i = 0; i < path.length();) {
101             while (i < path.length() && path.charAt(i) == ' ') {
102                 ++i;
103             }
104             if (i < path.length()) {
105                 String url;
106                 if (path.charAt(i) == '<') {
107                     int j = path.indexOf('>', i + 1);
108                     if (j < 0) {
109                         url = path.substring(i + 1);
110                         i = path.length();
111                     } else {
112                         url = path.substring(i + 1, j);
113                         i = j + 1;
114                     }
115                 } else {
116                     int j = path.indexOf(' ', i + 1);
117                     if (j < 0) {
118                         j = path.length();
119                     }
120                     url = path.substring(i, j);
121                     i = j;
122                 }
123                 addURL(new URL(jar, url));
124             }
125         }
126         return URLClassLoader.newInstance(new URL[] { jar }, this);
127     }
128 
129     /**
130      * Executes a UNO JAR.
131      *
132      * @param jar the URL of a UNO JAR that specifies a Main-Class; must not be
133      * <code>null</code>.
134      *
135      * @param arguments any arguments passed to the <code>main</code> method of
136      * the specified Main-Class of the given JAR <code>jar</code>; must not be
137      * <code>null</code>.
138      *
139      * @throws IOException if there are any problems processing the given JAR
140      * <code>jar</code>.
141      *
142      * @throws ClassNotFoundException if the given JAR <code>jar</code> does not
143      * specify a Main-Class, or if the specified Main-Class cannot be found.
144      *
145      * @throws NoSuchMethodException if the specified Main-Class of the given
146      * JAR <code>jar</code> does not have an appropriate <code>main</code>
147      * method.
148      *
149      * @throws InvocationTargetException if an exception occurs while executing
150      * the <code>main</code> method of the specified Main-Class of the given JAR
151      * <code>jar</code>.
152      */
execute(URL jar, String[] arguments)153     public void execute(URL jar, String[] arguments)
154         throws IOException, ClassNotFoundException, NoSuchMethodException,
155         InvocationTargetException
156     {
157         Attributes attr = getJarMainAttributes(jar);
158         String name = attr == null
159             ? null : attr.getValue(Attributes.Name.MAIN_CLASS);
160         if (name == null) {
161             throw new ClassNotFoundException(
162                 jar + " does not specify a main class");
163         }
164         try {
165             getClassLoader(jar, attr).loadClass(name.replace('/', '.')).
166                 getMethod("main", new Class[] { String[].class }).
167                 invoke(null, new Object[] { arguments });
168         } catch (IllegalAccessException e) {
169             throw new RuntimeException("impossible " + e);
170         }
171     }
172 
173     /**
174      * Obtains the main section of the manifest of a JAR.
175      *
176      * @param jar the URL of a JAR; must not be <code>null</code>.
177      *
178      * @return the representation of the main section of the manifest of the
179      * given JAR <code>jar</code>, or <code>null</code> if the given JAR does
180      * not have a manifest.
181      *
182      * @throws IOException if there are any problems processing the given JAR
183      * <code>jar</code>.
184      */
getJarMainAttributes(URL jar)185     public static Attributes getJarMainAttributes(URL jar) throws IOException {
186         JarInputStream s = new JarInputStream(jar.openStream());
187         Manifest mf;
188         try {
189             mf = s.getManifest();
190         } finally {
191             s.close();
192         }
193         return mf == null ? null : mf.getMainAttributes();
194     }
195 
createUrls(URL base, URL[] classPath)196     private static URL[] createUrls(URL base, URL[] classPath)
197         throws MalformedURLException
198     {
199         final int JARS = 4;
200         URL[] urls = new URL[JARS + (classPath == null ? 0 : classPath.length)];
201         urls[0] = new URL(base, "java_uno.jar"); //TODO get rid of it here
202         urls[1] = new URL(base, "juh.jar");
203         urls[2] = new URL(base, "jurt.jar");
204         urls[3] = new URL(base, "ridl.jar");
205         if (classPath != null) {
206             System.arraycopy(classPath, 0, urls, JARS, classPath.length);
207         }
208         return urls;
209     }
210 }
211