View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  
19  package org.apache.geronimo.system.rmi;
20  
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  
24  import java.io.File;
25  
26  import java.util.StringTokenizer;
27  
28  import java.rmi.server.RMIClassLoader;
29  import java.rmi.server.RMIClassLoaderSpi;
30  
31  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
32  
33  /**
34   * An implementation of {@link RMIClassLoaderSpi} which provides normilzation
35   * of codebase URLs and delegates to the default {@link RMIClassLoaderSpi}.
36   *
37   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
38   */
39  public class RMIClassLoaderSpiImpl
40      extends RMIClassLoaderSpi
41  {
42      private RMIClassLoaderSpi delegate = RMIClassLoader.getDefaultProviderInstance();
43  
44      //TODO: Not sure of the best initial size.  Starting with 100 which should be reasonable.
45      private ConcurrentHashMap cachedCodebases = new ConcurrentHashMap(100, 0.75F);
46  
47  
48      public Class loadClass(String codebase, String name, ClassLoader defaultLoader)
49          throws MalformedURLException, ClassNotFoundException
50      {
51          if (codebase != null) {
52              codebase = getNormalizedCodebase(codebase);
53          }
54          
55          return delegate.loadClass(codebase, name, defaultLoader);
56      }
57      
58      public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader)
59          throws MalformedURLException, ClassNotFoundException
60      {
61          if (codebase != null) {
62              codebase = getNormalizedCodebase(codebase);
63          }
64          
65          return delegate.loadProxyClass(codebase, interfaces, defaultLoader);
66      }
67      
68      public ClassLoader getClassLoader(String codebase)
69          throws MalformedURLException
70      {
71          if (codebase != null) {
72              codebase = getNormalizedCodebase(codebase);
73          }
74          
75          return delegate.getClassLoader(codebase);
76      }
77      
78      public String getClassAnnotation(Class type) {
79          Object obj = type.getClassLoader();
80          if (obj instanceof ClassLoaderServerAware) {
81              ClassLoaderServerAware classLoader = (ClassLoaderServerAware) obj;
82              URL urls[] = classLoader.getClassLoaderServerURLs();
83              if (null == urls) {
84                  return delegate.getClassAnnotation(type);
85              }
86              StringBuffer codebase = new StringBuffer();
87              for (int i = 0; i < urls.length; i++) {
88                  URL url = normalizeURL(urls[i]);
89                  if (codebase.length() != 0) {
90                      codebase.append(' ');
91                  }
92                  codebase.append(url);
93              }
94              return codebase.toString();
95          }
96          
97          return delegate.getClassAnnotation(type);
98      }
99  
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 }