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 }