1*bc1502c1SDamjan Jovanovic /**************************************************************
2*bc1502c1SDamjan Jovanovic  *
3*bc1502c1SDamjan Jovanovic  * Licensed to the Apache Software Foundation (ASF) under one
4*bc1502c1SDamjan Jovanovic  * or more contributor license agreements.  See the NOTICE file
5*bc1502c1SDamjan Jovanovic  * distributed with this work for additional information
6*bc1502c1SDamjan Jovanovic  * regarding copyright ownership.  The ASF licenses this file
7*bc1502c1SDamjan Jovanovic  * to you under the Apache License, Version 2.0 (the
8*bc1502c1SDamjan Jovanovic  * "License"); you may not use this file except in compliance
9*bc1502c1SDamjan Jovanovic  * with the License.  You may obtain a copy of the License at
10*bc1502c1SDamjan Jovanovic  *
11*bc1502c1SDamjan Jovanovic  *   http://www.apache.org/licenses/LICENSE-2.0
12*bc1502c1SDamjan Jovanovic  *
13*bc1502c1SDamjan Jovanovic  * Unless required by applicable law or agreed to in writing,
14*bc1502c1SDamjan Jovanovic  * software distributed under the License is distributed on an
15*bc1502c1SDamjan Jovanovic  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*bc1502c1SDamjan Jovanovic  * KIND, either express or implied.  See the License for the
17*bc1502c1SDamjan Jovanovic  * specific language governing permissions and limitations
18*bc1502c1SDamjan Jovanovic  * under the License.
19*bc1502c1SDamjan Jovanovic  *
20*bc1502c1SDamjan Jovanovic  *************************************************************/
21*bc1502c1SDamjan Jovanovic package com.sun.star.comp.sdbc.classloading;
22*bc1502c1SDamjan Jovanovic 
23*bc1502c1SDamjan Jovanovic import java.lang.ref.WeakReference;
24*bc1502c1SDamjan Jovanovic import java.net.MalformedURLException;
25*bc1502c1SDamjan Jovanovic import java.net.URL;
26*bc1502c1SDamjan Jovanovic import java.net.URLClassLoader;
27*bc1502c1SDamjan Jovanovic import java.util.ArrayList;
28*bc1502c1SDamjan Jovanovic import java.util.Iterator;
29*bc1502c1SDamjan Jovanovic import java.util.LinkedList;
30*bc1502c1SDamjan Jovanovic import java.util.List;
31*bc1502c1SDamjan Jovanovic import java.util.StringTokenizer;
32*bc1502c1SDamjan Jovanovic 
33*bc1502c1SDamjan Jovanovic import org.apache.openoffice.comp.sdbc.dbtools.comphelper.CompHelper;
34*bc1502c1SDamjan Jovanovic 
35*bc1502c1SDamjan Jovanovic import com.sun.star.lang.IllegalArgumentException;
36*bc1502c1SDamjan Jovanovic import com.sun.star.uno.RuntimeException;
37*bc1502c1SDamjan Jovanovic import com.sun.star.uno.UnoRuntime;
38*bc1502c1SDamjan Jovanovic import com.sun.star.uno.XComponentContext;
39*bc1502c1SDamjan Jovanovic import com.sun.star.uri.UriReferenceFactory;
40*bc1502c1SDamjan Jovanovic import com.sun.star.uri.XUriReferenceFactory;
41*bc1502c1SDamjan Jovanovic import com.sun.star.uri.XVndSunStarExpandUrlReference;
42*bc1502c1SDamjan Jovanovic import com.sun.star.util.XMacroExpander;
43*bc1502c1SDamjan Jovanovic 
44*bc1502c1SDamjan Jovanovic /**
45*bc1502c1SDamjan Jovanovic  * A map from pairs of (classPath, name) to pairs of weak Java
46*bc1502c1SDamjan Jovanovic  * references to (ClassLoader, Class) is maintained, so that a class is only
47*bc1502c1SDamjan Jovanovic  * loaded once.
48*bc1502c1SDamjan Jovanovic  *
49*bc1502c1SDamjan Jovanovic  * It may happen that the weak reference to the ClassLoader becomes null while
50*bc1502c1SDamjan Jovanovic  * the reference to the Class remains non-null (in case the Class was actually
51*bc1502c1SDamjan Jovanovic  * loaded by some parent of the ClassLoader), in which case the ClassLoader is
52*bc1502c1SDamjan Jovanovic  * resurrected (which cannot cause any classes to be loaded multiple times, as
53*bc1502c1SDamjan Jovanovic  * the ClassLoader is no longer reachable, so no classes it has ever loaded are
54*bc1502c1SDamjan Jovanovic  * still reachable).
55*bc1502c1SDamjan Jovanovic  *
56*bc1502c1SDamjan Jovanovic  * Similarly, it may happen that the weak reference to the Class becomes null
57*bc1502c1SDamjan Jovanovic  * while the reference to the ClassLoader remains non-null, in which case the
58*bc1502c1SDamjan Jovanovic  * Class is simply re-loaded.
59*bc1502c1SDamjan Jovanovic  */
60*bc1502c1SDamjan Jovanovic public class ClassMap {
61*bc1502c1SDamjan Jovanovic 
62*bc1502c1SDamjan Jovanovic     private static class ClassMapEntry {
63*bc1502c1SDamjan Jovanovic         String classPath;
64*bc1502c1SDamjan Jovanovic         String className;
65*bc1502c1SDamjan Jovanovic         WeakReference<ClassLoader> classLoader;
66*bc1502c1SDamjan Jovanovic         WeakReference<Class<?>> classObject;
67*bc1502c1SDamjan Jovanovic     }
68*bc1502c1SDamjan Jovanovic 
69*bc1502c1SDamjan Jovanovic     private final LinkedList<ClassMapEntry> map = new LinkedList<>();
70*bc1502c1SDamjan Jovanovic 
loadClass(XComponentContext context, String classPath, String className)71*bc1502c1SDamjan Jovanovic     public synchronized ClassLoaderAndClass loadClass(XComponentContext context, String classPath, String className)
72*bc1502c1SDamjan Jovanovic             throws MalformedURLException, ClassNotFoundException {
73*bc1502c1SDamjan Jovanovic 
74*bc1502c1SDamjan Jovanovic         ClassLoader classLoader = null;
75*bc1502c1SDamjan Jovanovic         Class<?> classObject = null;
76*bc1502c1SDamjan Jovanovic         // Prune dangling weak references from the list while searching for a match,
77*bc1502c1SDamjan Jovanovic         // so that the list cannot grow unbounded:
78*bc1502c1SDamjan Jovanovic         ClassMapEntry matchingEntry = null;
79*bc1502c1SDamjan Jovanovic         for (Iterator<ClassMapEntry> it = map.iterator(); it.hasNext();) {
80*bc1502c1SDamjan Jovanovic             ClassMapEntry entry = it.next();
81*bc1502c1SDamjan Jovanovic             classLoader = entry.classLoader.get();
82*bc1502c1SDamjan Jovanovic             classObject = entry.classObject.get();
83*bc1502c1SDamjan Jovanovic             if (classLoader == null && classObject == null) {
84*bc1502c1SDamjan Jovanovic                 it.remove();
85*bc1502c1SDamjan Jovanovic             } else if (entry.classPath.equals(classPath) && entry.className.equals(className)) {
86*bc1502c1SDamjan Jovanovic                 matchingEntry = entry;
87*bc1502c1SDamjan Jovanovic                 break;
88*bc1502c1SDamjan Jovanovic             }
89*bc1502c1SDamjan Jovanovic         }
90*bc1502c1SDamjan Jovanovic         if (classLoader == null || classObject == null) {
91*bc1502c1SDamjan Jovanovic             if (matchingEntry == null) {
92*bc1502c1SDamjan Jovanovic                 // Push a new ClassMapEntry (which can potentially fail) before
93*bc1502c1SDamjan Jovanovic                 // loading the class, so that it never happens that a class is
94*bc1502c1SDamjan Jovanovic                 // loaded but not added to the map (which could have effects on the
95*bc1502c1SDamjan Jovanovic                 // JVM that are not easily undone).  If the pushed ClassMapEntry is
96*bc1502c1SDamjan Jovanovic                 // not used after all (return false, etc.) it will be pruned on next
97*bc1502c1SDamjan Jovanovic                 // call because its classLoader/classObject are null:
98*bc1502c1SDamjan Jovanovic                 matchingEntry = new ClassMapEntry();
99*bc1502c1SDamjan Jovanovic                 matchingEntry.classPath = classPath;
100*bc1502c1SDamjan Jovanovic                 matchingEntry.className = className;
101*bc1502c1SDamjan Jovanovic                 map.addFirst(matchingEntry);
102*bc1502c1SDamjan Jovanovic             }
103*bc1502c1SDamjan Jovanovic             if (classLoader == null) {
104*bc1502c1SDamjan Jovanovic                 List<URL> urls = translateToUrls(context, classPath);
105*bc1502c1SDamjan Jovanovic                 classLoader = new URLClassLoader(urls.toArray(new URL[0]));
106*bc1502c1SDamjan Jovanovic             }
107*bc1502c1SDamjan Jovanovic             if (classObject == null) {
108*bc1502c1SDamjan Jovanovic                 classObject = classLoader.loadClass(className);
109*bc1502c1SDamjan Jovanovic             }
110*bc1502c1SDamjan Jovanovic             matchingEntry.classLoader = new WeakReference<ClassLoader>(classLoader);
111*bc1502c1SDamjan Jovanovic             matchingEntry.classObject = new WeakReference<Class<?>>(classObject);
112*bc1502c1SDamjan Jovanovic         }
113*bc1502c1SDamjan Jovanovic         return new ClassLoaderAndClass(classLoader, classObject);
114*bc1502c1SDamjan Jovanovic     }
115*bc1502c1SDamjan Jovanovic 
translateToUrls(XComponentContext context, String classPath)116*bc1502c1SDamjan Jovanovic     private static List<URL> translateToUrls(XComponentContext context, String classPath) throws MalformedURLException {
117*bc1502c1SDamjan Jovanovic         StringTokenizer tokenizer = new StringTokenizer(classPath, " ", false);
118*bc1502c1SDamjan Jovanovic         ArrayList<URL> urls = new ArrayList<>();
119*bc1502c1SDamjan Jovanovic         while (tokenizer.hasMoreTokens()) {
120*bc1502c1SDamjan Jovanovic             String url = tokenizer.nextToken();
121*bc1502c1SDamjan Jovanovic             XUriReferenceFactory uriReferenceFactory = null;
122*bc1502c1SDamjan Jovanovic             XVndSunStarExpandUrlReference expUrl = null;
123*bc1502c1SDamjan Jovanovic             XMacroExpander macroExpander = null;
124*bc1502c1SDamjan Jovanovic             try {
125*bc1502c1SDamjan Jovanovic                 uriReferenceFactory = UriReferenceFactory.create(context);
126*bc1502c1SDamjan Jovanovic                 expUrl = UnoRuntime.queryInterface(
127*bc1502c1SDamjan Jovanovic                         XVndSunStarExpandUrlReference.class, uriReferenceFactory.parse(url));
128*bc1502c1SDamjan Jovanovic                 if (expUrl != null) {
129*bc1502c1SDamjan Jovanovic                     macroExpander = UnoRuntime.queryInterface(
130*bc1502c1SDamjan Jovanovic                             XMacroExpander.class,
131*bc1502c1SDamjan Jovanovic                             context.getValueByName("/singletons/com.sun.star.util.theMacroExpander"));
132*bc1502c1SDamjan Jovanovic                     try {
133*bc1502c1SDamjan Jovanovic                         url = expUrl.expand(macroExpander);
134*bc1502c1SDamjan Jovanovic                     } catch (IllegalArgumentException illegalArgumentException) {
135*bc1502c1SDamjan Jovanovic                         throw new RuntimeException(
136*bc1502c1SDamjan Jovanovic                                 "com.sun.star.lang.IllegalArgumentException: " + illegalArgumentException.getMessage(),
137*bc1502c1SDamjan Jovanovic                                 illegalArgumentException);
138*bc1502c1SDamjan Jovanovic                     }
139*bc1502c1SDamjan Jovanovic                 }
140*bc1502c1SDamjan Jovanovic             } finally {
141*bc1502c1SDamjan Jovanovic                 CompHelper.disposeComponent(uriReferenceFactory);
142*bc1502c1SDamjan Jovanovic                 CompHelper.disposeComponent(expUrl);
143*bc1502c1SDamjan Jovanovic                 CompHelper.disposeComponent(macroExpander);
144*bc1502c1SDamjan Jovanovic             }
145*bc1502c1SDamjan Jovanovic             URL javaURL = new URL(url);
146*bc1502c1SDamjan Jovanovic             urls.add(javaURL);
147*bc1502c1SDamjan Jovanovic         }
148*bc1502c1SDamjan Jovanovic         return urls;
149*bc1502c1SDamjan Jovanovic     }
150*bc1502c1SDamjan Jovanovic }
151