1956476d7SDamjan Jovanovic /**************************************************************
2956476d7SDamjan Jovanovic  *
3956476d7SDamjan Jovanovic  * Licensed to the Apache Software Foundation (ASF) under one
4956476d7SDamjan Jovanovic  * or more contributor license agreements.  See the NOTICE file
5956476d7SDamjan Jovanovic  * distributed with this work for additional information
6956476d7SDamjan Jovanovic  * regarding copyright ownership.  The ASF licenses this file
7956476d7SDamjan Jovanovic  * to you under the Apache License, Version 2.0 (the
8956476d7SDamjan Jovanovic  * "License"); you may not use this file except in compliance
9956476d7SDamjan Jovanovic  * with the License.  You may obtain a copy of the License at
10956476d7SDamjan Jovanovic  *
11956476d7SDamjan Jovanovic  *   http://www.apache.org/licenses/LICENSE-2.0
12956476d7SDamjan Jovanovic  *
13956476d7SDamjan Jovanovic  * Unless required by applicable law or agreed to in writing,
14956476d7SDamjan Jovanovic  * software distributed under the License is distributed on an
15956476d7SDamjan Jovanovic  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16956476d7SDamjan Jovanovic  * KIND, either express or implied.  See the License for the
17956476d7SDamjan Jovanovic  * specific language governing permissions and limitations
18956476d7SDamjan Jovanovic  * under the License.
19956476d7SDamjan Jovanovic  *
20956476d7SDamjan Jovanovic  *************************************************************/
21956476d7SDamjan Jovanovic package com.sun.star.comp.sdbc;
22956476d7SDamjan Jovanovic 
23956476d7SDamjan Jovanovic import java.lang.ref.WeakReference;
24956476d7SDamjan Jovanovic import java.net.MalformedURLException;
25956476d7SDamjan Jovanovic import java.net.URL;
26956476d7SDamjan Jovanovic import java.net.URLClassLoader;
27956476d7SDamjan Jovanovic import java.util.ArrayList;
28956476d7SDamjan Jovanovic import java.util.Iterator;
29956476d7SDamjan Jovanovic import java.util.LinkedList;
30956476d7SDamjan Jovanovic import java.util.List;
31956476d7SDamjan Jovanovic import java.util.StringTokenizer;
32956476d7SDamjan Jovanovic 
33956476d7SDamjan Jovanovic import org.apache.openoffice.comp.sdbc.dbtools.comphelper.CompHelper;
34956476d7SDamjan Jovanovic 
35956476d7SDamjan Jovanovic import com.sun.star.lang.IllegalArgumentException;
36956476d7SDamjan Jovanovic import com.sun.star.uno.RuntimeException;
37956476d7SDamjan Jovanovic import com.sun.star.uno.UnoRuntime;
38956476d7SDamjan Jovanovic import com.sun.star.uno.XComponentContext;
39956476d7SDamjan Jovanovic import com.sun.star.uri.UriReferenceFactory;
40956476d7SDamjan Jovanovic import com.sun.star.uri.XUriReferenceFactory;
41956476d7SDamjan Jovanovic import com.sun.star.uri.XVndSunStarExpandUrlReference;
42956476d7SDamjan Jovanovic import com.sun.star.util.XMacroExpander;
43956476d7SDamjan Jovanovic 
44956476d7SDamjan Jovanovic /**
45956476d7SDamjan Jovanovic  * A map from pairs of (classPath, name) to pairs of weak Java
46956476d7SDamjan Jovanovic  * references to (ClassLoader, Class) is maintained, so that a class is only
47956476d7SDamjan Jovanovic  * loaded once.
48956476d7SDamjan Jovanovic  *
49956476d7SDamjan Jovanovic  * It may happen that the weak reference to the ClassLoader becomes null while
50956476d7SDamjan Jovanovic  * the reference to the Class remains non-null (in case the Class was actually
51956476d7SDamjan Jovanovic  * loaded by some parent of the ClassLoader), in which case the ClassLoader is
52956476d7SDamjan Jovanovic  * resurrected (which cannot cause any classes to be loaded multiple times, as
53956476d7SDamjan Jovanovic  * the ClassLoader is no longer reachable, so no classes it has ever loaded are
54956476d7SDamjan Jovanovic  * still reachable).
55956476d7SDamjan Jovanovic  *
56956476d7SDamjan Jovanovic  * Similarly, it may happen that the weak reference to the Class becomes null
57956476d7SDamjan Jovanovic  * while the reference to the ClassLoader remains non-null, in which case the
58956476d7SDamjan Jovanovic  * Class is simply re-loaded.
59956476d7SDamjan Jovanovic  */
60956476d7SDamjan Jovanovic public class ClassMap {
61956476d7SDamjan Jovanovic     public static class ClassLoaderAndClass {
62*8ccb802dSDamjan Jovanovic         private final ClassLoader classLoader;
63*8ccb802dSDamjan Jovanovic         private final Class<?> classObject;
64956476d7SDamjan Jovanovic 
ClassLoaderAndClass(ClassLoader classLoader, Class<?> classObject)65956476d7SDamjan Jovanovic         public ClassLoaderAndClass(ClassLoader classLoader, Class<?> classObject) {
66956476d7SDamjan Jovanovic             this.classLoader = classLoader;
67956476d7SDamjan Jovanovic             this.classObject = classObject;
68956476d7SDamjan Jovanovic         }
69956476d7SDamjan Jovanovic 
getClassLoader()70956476d7SDamjan Jovanovic         public ClassLoader getClassLoader() {
71956476d7SDamjan Jovanovic             return classLoader;
72956476d7SDamjan Jovanovic         }
73956476d7SDamjan Jovanovic 
getClassObject()74956476d7SDamjan Jovanovic         public Class<?> getClassObject() {
75956476d7SDamjan Jovanovic             return classObject;
76956476d7SDamjan Jovanovic         }
77956476d7SDamjan Jovanovic     }
78956476d7SDamjan Jovanovic 
79956476d7SDamjan Jovanovic     private static class ClassMapEntry {
80956476d7SDamjan Jovanovic         String classPath;
81956476d7SDamjan Jovanovic         String className;
82956476d7SDamjan Jovanovic         WeakReference<ClassLoader> classLoader;
83956476d7SDamjan Jovanovic         WeakReference<Class<?>> classObject;
84956476d7SDamjan Jovanovic     }
85956476d7SDamjan Jovanovic 
86956476d7SDamjan Jovanovic     private final LinkedList<ClassMapEntry> map = new LinkedList<>();
87956476d7SDamjan Jovanovic 
loadClass(XComponentContext context, String classPath, String className)88956476d7SDamjan Jovanovic     public synchronized ClassLoaderAndClass loadClass(XComponentContext context, String classPath, String className)
89956476d7SDamjan Jovanovic             throws MalformedURLException, ClassNotFoundException {
90956476d7SDamjan Jovanovic 
91956476d7SDamjan Jovanovic         ClassLoader classLoader = null;
92956476d7SDamjan Jovanovic         Class<?> classObject = null;
93956476d7SDamjan Jovanovic         // Prune dangling weak references from the list while searching for a match,
94956476d7SDamjan Jovanovic         // so that the list cannot grow unbounded:
95956476d7SDamjan Jovanovic         ClassMapEntry matchingEntry = null;
96956476d7SDamjan Jovanovic         for (Iterator<ClassMapEntry> it = map.iterator(); it.hasNext();) {
97956476d7SDamjan Jovanovic             ClassMapEntry entry = it.next();
98956476d7SDamjan Jovanovic             classLoader = entry.classLoader.get();
99956476d7SDamjan Jovanovic             classObject = entry.classObject.get();
100956476d7SDamjan Jovanovic             if (classLoader == null && classObject == null) {
101956476d7SDamjan Jovanovic                 it.remove();
102956476d7SDamjan Jovanovic             } else if (entry.classPath.equals(classPath) && entry.className.equals(className)) {
103956476d7SDamjan Jovanovic                 matchingEntry = entry;
104956476d7SDamjan Jovanovic                 break;
105956476d7SDamjan Jovanovic             }
106956476d7SDamjan Jovanovic         }
107956476d7SDamjan Jovanovic         if (classLoader == null || classObject == null) {
108956476d7SDamjan Jovanovic             if (matchingEntry == null) {
109956476d7SDamjan Jovanovic                 // Push a new ClassMapEntry (which can potentially fail) before
110956476d7SDamjan Jovanovic                 // loading the class, so that it never happens that a class is
111956476d7SDamjan Jovanovic                 // loaded but not added to the map (which could have effects on the
112956476d7SDamjan Jovanovic                 // JVM that are not easily undone).  If the pushed ClassMapEntry is
113956476d7SDamjan Jovanovic                 // not used after all (return false, etc.) it will be pruned on next
114956476d7SDamjan Jovanovic                 // call because its classLoader/classObject are null:
115956476d7SDamjan Jovanovic                 matchingEntry = new ClassMapEntry();
116956476d7SDamjan Jovanovic                 matchingEntry.classPath = classPath;
117956476d7SDamjan Jovanovic                 matchingEntry.className = className;
118956476d7SDamjan Jovanovic                 map.addFirst(matchingEntry);
119956476d7SDamjan Jovanovic             }
120956476d7SDamjan Jovanovic             if (classLoader == null) {
121956476d7SDamjan Jovanovic                 List<URL> urls = translateToUrls(context, classPath);
122956476d7SDamjan Jovanovic                 classLoader = new URLClassLoader(urls.toArray(new URL[0]));
123956476d7SDamjan Jovanovic             }
124956476d7SDamjan Jovanovic             if (classObject == null) {
125956476d7SDamjan Jovanovic                 classObject = classLoader.loadClass(className);
126956476d7SDamjan Jovanovic             }
127956476d7SDamjan Jovanovic             matchingEntry.classLoader = new WeakReference<ClassLoader>(classLoader);
128956476d7SDamjan Jovanovic             matchingEntry.classObject = new WeakReference<Class<?>>(classObject);
129956476d7SDamjan Jovanovic         }
130956476d7SDamjan Jovanovic         return new ClassLoaderAndClass(classLoader, classObject);
131956476d7SDamjan Jovanovic     }
132956476d7SDamjan Jovanovic 
translateToUrls(XComponentContext context, String classPath)133956476d7SDamjan Jovanovic     private static List<URL> translateToUrls(XComponentContext context, String classPath) throws MalformedURLException {
134956476d7SDamjan Jovanovic         StringTokenizer tokenizer = new StringTokenizer(classPath, " ", false);
135956476d7SDamjan Jovanovic         ArrayList<URL> urls = new ArrayList<>();
136956476d7SDamjan Jovanovic         while (tokenizer.hasMoreTokens()) {
137956476d7SDamjan Jovanovic             String url = tokenizer.nextToken();
138956476d7SDamjan Jovanovic             XUriReferenceFactory uriReferenceFactory = null;
139956476d7SDamjan Jovanovic             XVndSunStarExpandUrlReference expUrl = null;
140956476d7SDamjan Jovanovic             XMacroExpander macroExpander = null;
141956476d7SDamjan Jovanovic             try {
142956476d7SDamjan Jovanovic                 uriReferenceFactory = UriReferenceFactory.create(context);
143956476d7SDamjan Jovanovic                 expUrl = UnoRuntime.queryInterface(
144956476d7SDamjan Jovanovic                         XVndSunStarExpandUrlReference.class, uriReferenceFactory.parse(url));
145956476d7SDamjan Jovanovic                 if (expUrl != null) {
146956476d7SDamjan Jovanovic                     macroExpander = UnoRuntime.queryInterface(
147956476d7SDamjan Jovanovic                             XMacroExpander.class,
148956476d7SDamjan Jovanovic                             context.getValueByName("/singletons/com.sun.star.util.theMacroExpander"));
149956476d7SDamjan Jovanovic                     try {
150956476d7SDamjan Jovanovic                         url = expUrl.expand(macroExpander);
151956476d7SDamjan Jovanovic                     } catch (IllegalArgumentException illegalArgumentException) {
152956476d7SDamjan Jovanovic                         throw new RuntimeException(
153956476d7SDamjan Jovanovic                                 "com.sun.star.lang.IllegalArgumentException: " + illegalArgumentException.getMessage(),
154956476d7SDamjan Jovanovic                                 illegalArgumentException);
155956476d7SDamjan Jovanovic                     }
156956476d7SDamjan Jovanovic                 }
157956476d7SDamjan Jovanovic             } finally {
158956476d7SDamjan Jovanovic                 CompHelper.disposeComponent(uriReferenceFactory);
159956476d7SDamjan Jovanovic                 CompHelper.disposeComponent(expUrl);
160956476d7SDamjan Jovanovic                 CompHelper.disposeComponent(macroExpander);
161956476d7SDamjan Jovanovic             }
162956476d7SDamjan Jovanovic             URL javaURL = new URL(url);
163956476d7SDamjan Jovanovic             urls.add(javaURL);
164956476d7SDamjan Jovanovic         }
165956476d7SDamjan Jovanovic         return urls;
166956476d7SDamjan Jovanovic     }
167956476d7SDamjan Jovanovic }
168