001 package railo.commons.lang; 002 003 import java.io.ByteArrayOutputStream; 004 import java.io.IOException; 005 import java.io.InputStream; 006 import java.net.URL; 007 import java.util.Arrays; 008 import java.util.Map; 009 010 import org.apache.commons.collections.map.ReferenceMap; 011 012 import railo.commons.digest.MD5; 013 import railo.commons.io.IOUtil; 014 import railo.commons.io.SystemUtil; 015 import railo.commons.io.res.Resource; 016 import railo.commons.io.res.util.ResourceClassLoader; 017 import railo.commons.io.res.util.ResourceUtil; 018 import railo.runtime.config.Config; 019 import railo.runtime.config.ConfigImpl; 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.type.Sizeable; 022 import railo.runtime.type.util.ArrayUtil; 023 024 /** 025 * Directory ClassLoader 026 */ 027 public final class PhysicalClassLoader extends ExtendableClassLoader implements Sizeable { 028 029 private Resource directory; 030 private ClassLoader parent; 031 private int size=0; 032 private int count; 033 private Map<String,PhysicalClassLoader> customCLs; 034 035 /** 036 * Constructor of the class 037 * @param directory 038 * @throws IOException 039 */ 040 public PhysicalClassLoader(Resource directory) throws IOException { 041 this(directory,getParentCL()); 042 } 043 private static ClassLoader getParentCL() { 044 Config config = ThreadLocalPageContext.getConfig(); 045 if(config!=null) return config.getClassLoader(); 046 return new ClassLoaderHelper().getClass().getClassLoader(); 047 } 048 049 /** 050 * Constructor of the class 051 * @param directory 052 * @param parent 053 * @throws IOException 054 */ 055 public PhysicalClassLoader(Resource directory, ClassLoader parent) throws IOException { 056 super(parent); 057 this.parent=parent; 058 if(!directory.isDirectory()) 059 throw new IOException("resource "+directory+" is not a directory"); 060 if(!directory.canRead()) 061 throw new IOException("no access to "+directory+" directory"); 062 this.directory=directory; 063 } 064 065 /** 066 * Loads the class with the specified name. This method searches for 067 * classes in the same manner as the {@link #loadClass(String, boolean)} 068 * method. It is called by the Java virtual machine to resolve class 069 * references. Calling this method is equivalent to calling 070 * <code>loadClass(name, false)</code>. 071 * 072 * @param name the name of the class 073 * @return the resulting <code>Class</code> object 074 * @exception ClassNotFoundException if the class was not found 075 */ 076 public Class<?> loadClass(String name) throws ClassNotFoundException { 077 return loadClass(name, false); 078 }//15075171 079 080 /** 081 * Loads the class with the specified name. The default implementation of 082 * this method searches for classes in the following order:<p> 083 * 084 * <ol> 085 * <li> Call {@link #findLoadedClass(String)} to check if the class has 086 * already been loaded. <p> 087 * <li> Call the <code>loadClass</code> method on the parent class 088 * loader. If the parent is <code>null</code> the class loader 089 * built-in to the virtual machine is used, instead. <p> 090 * <li> Call the {@link #findClass(String)} method to find the class. <p> 091 * </ol> 092 * 093 * If the class was found using the above steps, and the 094 * <code>resolve</code> flag is true, this method will then call the 095 * {@link #resolveClass(Class)} method on the resulting class object. 096 * <p> 097 * From the Java 2 SDK, v1.2, subclasses of ClassLoader are 098 * encouraged to override 099 * {@link #findClass(String)}, rather than this method.<p> 100 * 101 * @param name the name of the class 102 * @param resolve if <code>true</code> then resolve the class 103 * @return the resulting <code>Class</code> object 104 * @exception ClassNotFoundException if the class could not be found 105 */ 106 protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 107 //if(!name.endsWith("$cf")) return super.loadClass(name, resolve); this break Webervices 108 // First, check if the class has already been loaded 109 Class<?> c = findLoadedClass(name); 110 //print.o("load:"+name+" -> "+c); 111 if (c == null) { 112 try { 113 c =parent.loadClass(name); 114 } 115 catch (Throwable t) { 116 c = findClass(name); 117 } 118 } 119 if (resolve) { 120 resolveClass(c); 121 } 122 return c; 123 } 124 125 126 127 128 public static long lastModified(Resource res, long defaultValue) { 129 InputStream in = null; 130 try{ 131 in=res.getInputStream(); 132 byte[] buffer = new byte[10]; 133 in.read(buffer); 134 if(!ClassUtil.hasCF33Prefix(buffer)) return defaultValue; 135 136 byte[] _buffer = new byte[]{ 137 buffer[2], 138 buffer[3], 139 buffer[4], 140 buffer[5], 141 buffer[6], 142 buffer[7], 143 buffer[8], 144 buffer[9], 145 }; 146 147 148 return NumberUtil.byteArrayToLong(_buffer); 149 } 150 catch(IOException ioe){ 151 return defaultValue; 152 } 153 finally { 154 IOUtil.closeEL(in); 155 } 156 157 } 158 159 @Override 160 protected Class<?> findClass(String name) throws ClassNotFoundException { 161 Resource res=directory.getRealResource(name.replace('.','/').concat(".class")); 162 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 163 try { 164 IOUtil.copy(res,baos,false); 165 } 166 catch (IOException e) {//e.printStackTrace(); 167 throw new ClassNotFoundException("class "+name+" is invalid or doesn't exist"); 168 } 169 170 byte[] barr=baos.toByteArray(); 171 size+=barr.length; 172 count++; 173 //print.o(name+":"+count+" -> "+(size/1024)); 174 IOUtil.closeEL(baos); 175 return loadClass(name, barr); 176 //return defineClass(name,barr,0,barr.length); 177 } 178 179 180 public Class<?> loadClass(String name, byte[] barr) { 181 int start=0; 182 if(ClassUtil.hasCF33Prefix(barr)) start=10; 183 size+=barr.length-start; 184 count++; 185 try { 186 return defineClass(name,barr,start,barr.length-start); 187 } 188 catch (Throwable t) { 189 SystemUtil.sleep(1); 190 return defineClass(name,barr,start,barr.length-start); 191 } 192 //return loadClass(name,false); 193 } 194 195 @Override 196 public URL getResource(String name) { 197 /*URL url=super.getResource(name); 198 if(url!=null) return url; 199 200 Resource f =_getResource(name); 201 if(f!=null) { 202 try { 203 return f.toURL(); 204 } 205 catch (MalformedURLException e) {} 206 }*/ 207 return null; 208 } 209 210 @Override 211 public InputStream getResourceAsStream(String name) { 212 InputStream is = super.getResourceAsStream(name); 213 if(is!=null) return is; 214 215 Resource f = _getResource(name); 216 if(f!=null) { 217 try { 218 return IOUtil.toBufferedInputStream(f.getInputStream()); 219 } 220 catch (IOException e) {} 221 } 222 return null; 223 } 224 225 /** 226 * returns matching File Object or null if file not exust 227 * @param name 228 * @return matching file 229 */ 230 public Resource _getResource(String name) { 231 Resource f = directory.getRealResource(name); 232 if(f!=null && f.exists() && f.isFile()) return f; 233 return null; 234 } 235 236 public boolean hasClass(String className) { 237 return hasResource(className.replace('.','/').concat(".class")); 238 } 239 240 public boolean isClassLoaded(String className) { 241 //print.o("isClassLoaded:"+className+"-"+(findLoadedClass(className)!=null)); 242 return findLoadedClass(className)!=null; 243 } 244 245 public boolean hasResource(String name) { 246 return _getResource(name)!=null; 247 } 248 249 /** 250 * @return the directory 251 */ 252 public Resource getDirectory() { 253 return directory; 254 } 255 256 @Override 257 public long sizeOf() { 258 return 0; 259 } 260 261 public int count() { 262 return count; 263 } 264 265 // FUTURE add to interface 266 public PhysicalClassLoader getCustomClassLoader(Resource[] resources, boolean reload) throws IOException{ 267 if(ArrayUtil.isEmpty(resources)) return this; 268 String key = hash(resources); 269 270 if(reload && customCLs!=null) customCLs.remove(key); 271 272 273 PhysicalClassLoader pcl=customCLs==null?null:customCLs.get(key); 274 if(pcl!=null) return pcl; 275 pcl=new PhysicalClassLoader(this.getDirectory(),new ResourceClassLoader(resources,getParent())); 276 if(customCLs==null)customCLs=new ReferenceMap(); 277 customCLs.put(key, pcl); 278 return pcl; 279 } 280 281 private String hash(Resource[] resources) { 282 Arrays.sort(resources); 283 StringBuilder sb=new StringBuilder(); 284 for(int i=0;i<resources.length;i++){ 285 sb.append(ResourceUtil.getCanonicalPathEL(resources[i])); 286 sb.append(';'); 287 } 288 return MD5.getDigestAsString(sb.toString(),null); 289 } 290 291 }