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 }