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