001 package railo.commons.io.res.util; 002 003 import java.io.Closeable; 004 import java.io.IOException; 005 import java.net.URL; 006 import java.net.URLClassLoader; 007 import java.util.ArrayList; 008 import java.util.Arrays; 009 import java.util.List; 010 import java.util.Map; 011 012 import org.apache.commons.collections.map.ReferenceMap; 013 014 import railo.commons.digest.MD5; 015 import railo.commons.io.res.Resource; 016 import railo.commons.io.res.type.file.FileResource; 017 import railo.runtime.exp.PageException; 018 import railo.runtime.type.util.ArrayUtil; 019 020 /** 021 * Classloader that load classes from resources 022 */ 023 public final class ResourceClassLoader extends URLClassLoader implements Closeable { 024 025 private List<Resource> resources=new ArrayList<Resource>(); 026 private Map<String,ResourceClassLoader> customCLs; 027 028 /* * 029 * Constructor of the class 030 * @param resources 031 * @throws PageException 032 033 ResourceClassLoader(Resource[] resources) throws IOException { 034 super(doURLs(resources)); 035 }*/ 036 037 /** 038 * Constructor of the class 039 * @param reses 040 * @param parent 041 * @throws PageException 042 */ 043 public ResourceClassLoader(Resource[] resources, ClassLoader parent) throws IOException { 044 super(doURLs(resources), parent); 045 for(int i=0;i<resources.length;i++){ 046 this.resources.add(resources[i]); 047 } 048 } 049 050 public ResourceClassLoader(ClassLoader parent) { 051 super(new URL[0], parent); 052 } 053 054 /** 055 * @return the resources 056 */ 057 public Resource[] getResources() { 058 return resources.toArray(new Resource[resources.size()]); 059 } 060 061 /** 062 * translate resources to url Objects 063 * @param reses 064 * @return 065 * @throws PageException 066 */ 067 public static URL[] doURLs(Resource[] reses) throws IOException { 068 List<URL> list=new ArrayList<URL>(); 069 for(int i=0;i<reses.length;i++) { 070 if(reses[i].isDirectory() || "jar".equalsIgnoreCase(ResourceUtil.getExtension(reses[i],null))) 071 list.add(doURL(reses[i])); 072 } 073 return list.toArray(new URL[list.size()]); 074 075 } 076 private static URL doURL(Resource res) throws IOException { 077 if(!(res instanceof FileResource)) 078 throw new IOException("resource ["+res.getPath()+"] must be a local file"); 079 return ((FileResource)res).toURL(); 080 } 081 082 @Override 083 public void close(){} 084 085 public synchronized void addResourcesX(Resource[] reses) throws IOException { 086 for(int i=0;i<reses.length;i++){ 087 if(!this.resources.contains(reses[i])){ 088 this.resources.add(reses[i]); 089 addURL(doURL(reses[i])); 090 } 091 } 092 } 093 094 095 public ResourceClassLoader getCustomResourceClassLoader(Resource[] resources) throws IOException{ 096 if(ArrayUtil.isEmpty(resources)) return this; 097 String key = hash(resources); 098 ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); 099 if(rcl!=null) return rcl; 100 101 resources=ResourceUtil.merge(this.getResources(), resources); 102 rcl=new ResourceClassLoader(resources,getParent()); 103 if(customCLs==null)customCLs=new ReferenceMap(); 104 customCLs.put(key, rcl); 105 return rcl; 106 } 107 108 public ResourceClassLoader getCustomResourceClassLoader2(Resource[] resources) throws IOException{ 109 if(ArrayUtil.isEmpty(resources)) return this; 110 String key = hash(resources); 111 ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); 112 if(rcl!=null) return rcl; 113 114 rcl=new ResourceClassLoader(resources,this); 115 if(customCLs==null)customCLs=new ReferenceMap(); 116 customCLs.put(key, rcl); 117 return rcl; 118 } 119 120 private String hash(Resource[] resources) { 121 Arrays.sort(resources); 122 StringBuilder sb=new StringBuilder(); 123 for(int i=0;i<resources.length;i++){ 124 sb.append(ResourceUtil.getCanonicalPathEL(resources[i])); 125 sb.append(';'); 126 } 127 return MD5.getDigestAsString(sb.toString(),null); 128 } 129 130 }