001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.kernel.rmi; 019 020 import java.net.MalformedURLException; 021 import java.net.URL; 022 023 import java.io.File; 024 025 import java.util.StringTokenizer; 026 027 import java.rmi.server.RMIClassLoader; 028 import java.rmi.server.RMIClassLoaderSpi; 029 030 import java.util.concurrent.ConcurrentHashMap; 031 032 /** 033 * An implementation of {@link RMIClassLoaderSpi} which provides normilzation 034 * of codebase URLs and delegates to the default {@link RMIClassLoaderSpi}. 035 * 036 * @version $Rev: 520573 $ $Date: 2007-03-20 16:48:26 -0400 (Tue, 20 Mar 2007) $ 037 */ 038 public class RMIClassLoaderSpiImpl 039 extends RMIClassLoaderSpi 040 { 041 private RMIClassLoaderSpi delegate = RMIClassLoader.getDefaultProviderInstance(); 042 043 //TODO: Not sure of the best initial size. Starting with 100 which should be reasonable. 044 private ConcurrentHashMap cachedCodebases = new ConcurrentHashMap(100); 045 046 public Class loadClass(String codebase, String name, ClassLoader defaultLoader) 047 throws MalformedURLException, ClassNotFoundException 048 { 049 if (codebase != null) { 050 codebase = getNormalizedCodebase(codebase); 051 } 052 053 return delegate.loadClass(codebase, name, defaultLoader); 054 } 055 056 public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader) 057 throws MalformedURLException, ClassNotFoundException 058 { 059 if (codebase != null) { 060 codebase = getNormalizedCodebase(codebase); 061 } 062 063 return delegate.loadProxyClass(codebase, interfaces, defaultLoader); 064 } 065 066 public ClassLoader getClassLoader(String codebase) 067 throws MalformedURLException 068 { 069 if (codebase != null) { 070 codebase = getNormalizedCodebase(codebase); 071 } 072 073 return delegate.getClassLoader(codebase); 074 } 075 076 public String getClassAnnotation(Class type) { 077 Object obj = type.getClassLoader(); 078 if (obj instanceof ClassLoaderServerAware) { 079 ClassLoaderServerAware classLoader = (ClassLoaderServerAware) obj; 080 URL urls[] = classLoader.getClassLoaderServerURLs(); 081 if (null == urls) { 082 return delegate.getClassAnnotation(type); 083 } 084 StringBuffer codebase = new StringBuffer(); 085 for (int i = 0; i < urls.length; i++) { 086 URL url = normalizeURL(urls[i]); 087 if (codebase.length() != 0) { 088 codebase.append(' '); 089 } 090 codebase.append(url); 091 } 092 return codebase.toString(); 093 } 094 095 return delegate.getClassAnnotation(type); 096 } 097 098 /** 099 * Uses a ConcurrentReaderHashmap to save the contents of previous parses. 100 * 101 * @param codebase 102 * @return 103 * @throws MalformedURLException 104 */ 105 private String getNormalizedCodebase(String codebase) 106 throws MalformedURLException { 107 String cachedCodebase = (String)cachedCodebases.get(codebase); 108 if (cachedCodebase != null) 109 return cachedCodebase; 110 111 String normalizedCodebase = normalizeCodebase(codebase); 112 String oldValue = (String)cachedCodebases.put(codebase, normalizedCodebase); 113 114 // If there was a previous value remove the one we just added to make sure the 115 // cache doesn't grow. 116 if (oldValue != null) { 117 cachedCodebases.remove(codebase); 118 } 119 return normalizedCodebase; // We can use the oldValue 120 } 121 122 123 static String normalizeCodebase(String input) 124 throws MalformedURLException 125 { 126 assert input != null; 127 128 StringBuffer codebase = new StringBuffer(); 129 StringBuffer working = new StringBuffer(); 130 StringTokenizer stok = new StringTokenizer(input, " \t\n\r\f", true); 131 132 while (stok.hasMoreTokens()) { 133 String item = stok.nextToken(); 134 // Optimisation: This optimisation to prevent unnecessary MalformedURLExceptions 135 // being generated is most helpful on windows where directory names in the path 136 // often contain spaces. E.G: 137 // file:/C:/Program Files/Apache Software Foundation/Maven 1.0.2/lib/ant-1.5.3-1.jar 138 // 139 // Therefore we won't attempt URL("Files/Apache) or URL(" ") for the path delimiter. 140 if ( item.indexOf(':') != -1 ) 141 { 142 try { 143 URL url = new URL(item); 144 // If we got this far then item is a valid url, so commit the current 145 // buffer and start collecting any trailing bits from where we are now 146 updateCodebase(working, codebase); 147 } catch (MalformedURLException ignore) { 148 // just keep going & append to the working buffer 149 } 150 } 151 152 // Append the URL or delimiter to the working buffer 153 working.append(item); 154 } 155 156 // Handle trailing elements 157 updateCodebase(working, codebase); 158 159 // System.out.println("Normalized codebase: " + codebase); 160 return codebase.toString(); 161 } 162 163 private static void updateCodebase(final StringBuffer working, final StringBuffer codebase) 164 throws MalformedURLException 165 { 166 if (working.length() != 0) { 167 // Normalize the URL 168 URL url = normalizeURL(new URL(working.toString())); 169 // System.out.println("Created normalized URL: " + url); 170 171 // Put spaces back in for URL delims 172 if (codebase.length() != 0) { 173 codebase.append(" "); 174 } 175 codebase.append(url); 176 177 // Reset the working buffer 178 working.setLength(0); 179 } 180 } 181 182 static URL normalizeURL(URL url) 183 { 184 assert url != null; 185 186 if (url.getProtocol().equals("file")) { 187 String filename = url.getFile().replace('/', File.separatorChar); 188 File file = new File(filename); 189 try { 190 url = file.toURI().toURL(); 191 } 192 catch (MalformedURLException ignore) {} 193 } 194 195 return url; 196 } 197 198 public interface ClassLoaderServerAware { 199 public URL[] getClassLoaderServerURLs(); 200 } 201 }