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