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