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 package com.sun.star.comp.sdbc; 22 23 import java.lang.ref.WeakReference; 24 import java.net.MalformedURLException; 25 import java.net.URL; 26 import java.net.URLClassLoader; 27 import java.util.ArrayList; 28 import java.util.Iterator; 29 import java.util.LinkedList; 30 import java.util.List; 31 import java.util.StringTokenizer; 32 33 import org.apache.openoffice.comp.sdbc.dbtools.comphelper.CompHelper; 34 35 import com.sun.star.lang.IllegalArgumentException; 36 import com.sun.star.uno.RuntimeException; 37 import com.sun.star.uno.UnoRuntime; 38 import com.sun.star.uno.XComponentContext; 39 import com.sun.star.uri.UriReferenceFactory; 40 import com.sun.star.uri.XUriReferenceFactory; 41 import com.sun.star.uri.XVndSunStarExpandUrlReference; 42 import com.sun.star.util.XMacroExpander; 43 44 /** 45 * A map from pairs of (classPath, name) to pairs of weak Java 46 * references to (ClassLoader, Class) is maintained, so that a class is only 47 * loaded once. 48 * 49 * It may happen that the weak reference to the ClassLoader becomes null while 50 * the reference to the Class remains non-null (in case the Class was actually 51 * loaded by some parent of the ClassLoader), in which case the ClassLoader is 52 * resurrected (which cannot cause any classes to be loaded multiple times, as 53 * the ClassLoader is no longer reachable, so no classes it has ever loaded are 54 * still reachable). 55 * 56 * Similarly, it may happen that the weak reference to the Class becomes null 57 * while the reference to the ClassLoader remains non-null, in which case the 58 * Class is simply re-loaded. 59 */ 60 public class ClassMap { 61 public static class ClassLoaderAndClass { 62 private final ClassLoader classLoader; 63 private final Class<?> classObject; 64 ClassLoaderAndClass(ClassLoader classLoader, Class<?> classObject)65 public ClassLoaderAndClass(ClassLoader classLoader, Class<?> classObject) { 66 this.classLoader = classLoader; 67 this.classObject = classObject; 68 } 69 getClassLoader()70 public ClassLoader getClassLoader() { 71 return classLoader; 72 } 73 getClassObject()74 public Class<?> getClassObject() { 75 return classObject; 76 } 77 } 78 79 private static class ClassMapEntry { 80 String classPath; 81 String className; 82 WeakReference<ClassLoader> classLoader; 83 WeakReference<Class<?>> classObject; 84 } 85 86 private final LinkedList<ClassMapEntry> map = new LinkedList<>(); 87 loadClass(XComponentContext context, String classPath, String className)88 public synchronized ClassLoaderAndClass loadClass(XComponentContext context, String classPath, String className) 89 throws MalformedURLException, ClassNotFoundException { 90 91 ClassLoader classLoader = null; 92 Class<?> classObject = null; 93 // Prune dangling weak references from the list while searching for a match, 94 // so that the list cannot grow unbounded: 95 ClassMapEntry matchingEntry = null; 96 for (Iterator<ClassMapEntry> it = map.iterator(); it.hasNext();) { 97 ClassMapEntry entry = it.next(); 98 classLoader = entry.classLoader.get(); 99 classObject = entry.classObject.get(); 100 if (classLoader == null && classObject == null) { 101 it.remove(); 102 } else if (entry.classPath.equals(classPath) && entry.className.equals(className)) { 103 matchingEntry = entry; 104 break; 105 } 106 } 107 if (classLoader == null || classObject == null) { 108 if (matchingEntry == null) { 109 // Push a new ClassMapEntry (which can potentially fail) before 110 // loading the class, so that it never happens that a class is 111 // loaded but not added to the map (which could have effects on the 112 // JVM that are not easily undone). If the pushed ClassMapEntry is 113 // not used after all (return false, etc.) it will be pruned on next 114 // call because its classLoader/classObject are null: 115 matchingEntry = new ClassMapEntry(); 116 matchingEntry.classPath = classPath; 117 matchingEntry.className = className; 118 map.addFirst(matchingEntry); 119 } 120 if (classLoader == null) { 121 List<URL> urls = translateToUrls(context, classPath); 122 classLoader = new URLClassLoader(urls.toArray(new URL[0])); 123 } 124 if (classObject == null) { 125 classObject = classLoader.loadClass(className); 126 } 127 matchingEntry.classLoader = new WeakReference<ClassLoader>(classLoader); 128 matchingEntry.classObject = new WeakReference<Class<?>>(classObject); 129 } 130 return new ClassLoaderAndClass(classLoader, classObject); 131 } 132 translateToUrls(XComponentContext context, String classPath)133 private static List<URL> translateToUrls(XComponentContext context, String classPath) throws MalformedURLException { 134 StringTokenizer tokenizer = new StringTokenizer(classPath, " ", false); 135 ArrayList<URL> urls = new ArrayList<>(); 136 while (tokenizer.hasMoreTokens()) { 137 String url = tokenizer.nextToken(); 138 XUriReferenceFactory uriReferenceFactory = null; 139 XVndSunStarExpandUrlReference expUrl = null; 140 XMacroExpander macroExpander = null; 141 try { 142 uriReferenceFactory = UriReferenceFactory.create(context); 143 expUrl = UnoRuntime.queryInterface( 144 XVndSunStarExpandUrlReference.class, uriReferenceFactory.parse(url)); 145 if (expUrl != null) { 146 macroExpander = UnoRuntime.queryInterface( 147 XMacroExpander.class, 148 context.getValueByName("/singletons/com.sun.star.util.theMacroExpander")); 149 try { 150 url = expUrl.expand(macroExpander); 151 } catch (IllegalArgumentException illegalArgumentException) { 152 throw new RuntimeException( 153 "com.sun.star.lang.IllegalArgumentException: " + illegalArgumentException.getMessage(), 154 illegalArgumentException); 155 } 156 } 157 } finally { 158 CompHelper.disposeComponent(uriReferenceFactory); 159 CompHelper.disposeComponent(expUrl); 160 CompHelper.disposeComponent(macroExpander); 161 } 162 URL javaURL = new URL(url); 163 urls.add(javaURL); 164 } 165 return urls; 166 } 167 } 168