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.system.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 edu.emory.mathcs.backport.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: 487175 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
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, 0.75F);
045
046
047 public Class loadClass(String codebase, String name, ClassLoader defaultLoader)
048 throws MalformedURLException, ClassNotFoundException
049 {
050 if (codebase != null) {
051 codebase = getNormalizedCodebase(codebase);
052 }
053
054 return delegate.loadClass(codebase, name, defaultLoader);
055 }
056
057 public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader)
058 throws MalformedURLException, ClassNotFoundException
059 {
060 if (codebase != null) {
061 codebase = getNormalizedCodebase(codebase);
062 }
063
064 return delegate.loadProxyClass(codebase, interfaces, defaultLoader);
065 }
066
067 public ClassLoader getClassLoader(String codebase)
068 throws MalformedURLException
069 {
070 if (codebase != null) {
071 codebase = getNormalizedCodebase(codebase);
072 }
073
074 return delegate.getClassLoader(codebase);
075 }
076
077 public String getClassAnnotation(Class type) {
078 Object obj = type.getClassLoader();
079 if (obj instanceof ClassLoaderServerAware) {
080 ClassLoaderServerAware classLoader = (ClassLoaderServerAware) obj;
081 URL urls[] = classLoader.getClassLoaderServerURLs();
082 if (null == urls) {
083 return delegate.getClassAnnotation(type);
084 }
085 StringBuffer codebase = new StringBuffer();
086 for (int i = 0; i < urls.length; i++) {
087 URL url = normalizeURL(urls[i]);
088 if (codebase.length() != 0) {
089 codebase.append(' ');
090 }
091 codebase.append(url);
092 }
093 return codebase.toString();
094 }
095
096 return delegate.getClassAnnotation(type);
097 }
098
099 /**
100 * Uses a ConcurrentReaderHashmap to save the contents of previous parses.
101 *
102 * @param codebase
103 * @return
104 * @throws MalformedURLException
105 */
106 private String getNormalizedCodebase(String codebase)
107 throws MalformedURLException {
108 String cachedCodebase = (String)cachedCodebases.get(codebase);
109 if (cachedCodebase != null)
110 return cachedCodebase;
111
112 String normalizedCodebase = normalizeCodebase(codebase);
113 String oldValue = (String)cachedCodebases.put(codebase, normalizedCodebase);
114
115 // If there was a previous value remove the one we just added to make sure the
116 // cache doesn't grow.
117 if (oldValue != null) {
118 cachedCodebases.remove(codebase);
119 }
120 return normalizedCodebase; // We can use the oldValue
121 }
122
123
124 static String normalizeCodebase(String input)
125 throws MalformedURLException
126 {
127 assert input != null;
128
129 StringBuffer codebase = new StringBuffer();
130 StringBuffer working = new StringBuffer();
131 StringTokenizer stok = new StringTokenizer(input, " \t\n\r\f", true);
132
133 while (stok.hasMoreTokens()) {
134 String item = stok.nextToken();
135 // Optimisation: This optimisation to prevent unnecessary MalformedURLExceptions
136 // being generated is most helpful on windows where directory names in the path
137 // often contain spaces. E.G:
138 // file:/C:/Program Files/Apache Software Foundation/Maven 1.0.2/lib/ant-1.5.3-1.jar
139 //
140 // Therefore we won't attempt URL("Files/Apache) or URL(" ") for the path delimiter.
141 if ( item.indexOf(':') != -1 )
142 {
143 try {
144 URL url = new URL(item);
145 // If we got this far then item is a valid url, so commit the current
146 // buffer and start collecting any trailing bits from where we are now
147 updateCodebase(working, codebase);
148 } catch (MalformedURLException ignore) {
149 // just keep going & append to the working buffer
150 }
151 }
152
153 // Append the URL or delimiter to the working buffer
154 working.append(item);
155 }
156
157 // Handle trailing elements
158 updateCodebase(working, codebase);
159
160 // System.out.println("Normalized codebase: " + codebase);
161 return codebase.toString();
162 }
163
164 private static void updateCodebase(final StringBuffer working, final StringBuffer codebase)
165 throws MalformedURLException
166 {
167 if (working.length() != 0) {
168 // Normalize the URL
169 URL url = normalizeURL(new URL(working.toString()));
170 // System.out.println("Created normalized URL: " + url);
171
172 // Put spaces back in for URL delims
173 if (codebase.length() != 0) {
174 codebase.append(" ");
175 }
176 codebase.append(url);
177
178 // Reset the working buffer
179 working.setLength(0);
180 }
181 }
182
183 static URL normalizeURL(URL url)
184 {
185 assert url != null;
186
187 if (url.getProtocol().equals("file")) {
188 String filename = url.getFile().replace('/', File.separatorChar);
189 File file = new File(filename);
190 try {
191 url = file.toURI().toURL();
192 }
193 catch (MalformedURLException ignore) {}
194 }
195
196 return url;
197 }
198
199 public interface ClassLoaderServerAware {
200 public URL[] getClassLoaderServerURLs();
201 }
202 }