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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 String codebase = delegate.getClassAnnotation(type); 096 if (codebase != null) { 097 try { 098 codebase = getNormalizedCodebase( codebase ); 099 } 100 catch (MalformedURLException ignore) { 101 } 102 } 103 return codebase; 104 } 105 106 /** 107 * Uses a ConcurrentReaderHashmap to save the contents of previous parses. 108 * 109 * @param codebase 110 * @return 111 * @throws MalformedURLException 112 */ 113 private String getNormalizedCodebase(String codebase) 114 throws MalformedURLException { 115 String cachedCodebase = (String)cachedCodebases.get(codebase); 116 if (cachedCodebase != null) 117 return cachedCodebase; 118 119 String normalizedCodebase = normalizeCodebase(codebase); 120 String oldValue = (String)cachedCodebases.put(codebase, normalizedCodebase); 121 122 // If there was a previous value remove the one we just added to make sure the 123 // cache doesn't grow. 124 if (oldValue != null) { 125 cachedCodebases.remove(codebase); 126 } 127 return normalizedCodebase; // We can use the oldValue 128 } 129 130 131 static String normalizeCodebase(String input) 132 throws MalformedURLException 133 { 134 assert input != null; 135 136 StringBuffer codebase = new StringBuffer(); 137 StringBuffer working = new StringBuffer(); 138 StringTokenizer stok = new StringTokenizer(input, " \t\n\r\f", true); 139 140 while (stok.hasMoreTokens()) { 141 String item = stok.nextToken(); 142 // Optimisation: This optimisation to prevent unnecessary MalformedURLExceptions 143 // being generated is most helpful on windows where directory names in the path 144 // often contain spaces. E.G: 145 // file:/C:/Program Files/Apache Software Foundation/Maven 1.0.2/lib/ant-1.5.3-1.jar 146 // 147 // Therefore we won't attempt URL("Files/Apache) or URL(" ") for the path delimiter. 148 if ( item.indexOf(':') != -1 ) 149 { 150 try { 151 URL url = new URL(item); 152 // If we got this far then item is a valid url, so commit the current 153 // buffer and start collecting any trailing bits from where we are now 154 updateCodebase(working, codebase); 155 } catch (MalformedURLException ignore) { 156 // just keep going & append to the working buffer 157 } 158 } 159 160 // Append the URL or delimiter to the working buffer 161 working.append(item); 162 } 163 164 // Handle trailing elements 165 updateCodebase(working, codebase); 166 167 // System.out.println("Normalized codebase: " + codebase); 168 return codebase.toString(); 169 } 170 171 private static void updateCodebase(final StringBuffer working, final StringBuffer codebase) 172 throws MalformedURLException 173 { 174 if (working.length() != 0) { 175 // Normalize the URL 176 URL url = normalizeURL(new URL(working.toString())); 177 // System.out.println("Created normalized URL: " + url); 178 179 // Put spaces back in for URL delims 180 if (codebase.length() != 0) { 181 codebase.append(" "); 182 } 183 codebase.append(url); 184 185 // Reset the working buffer 186 working.setLength(0); 187 } 188 } 189 190 static URL normalizeURL(URL url) 191 { 192 assert url != null; 193 194 if (url.getProtocol().equals("file")) { 195 String filename = url.getFile().replace('/', File.separatorChar); 196 File file = new File(filename); 197 try { 198 url = file.toURI().toURL(); 199 } 200 catch (MalformedURLException ignore) {} 201 } 202 203 return url; 204 } 205 206 public interface ClassLoaderServerAware { 207 public URL[] getClassLoaderServerURLs(); 208 } 209 }