package railo.commons.io.res.util; import java.io.Closeable; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.commons.collections.map.ReferenceMap; import railo.commons.digest.MD5; import railo.commons.io.res.Resource; import railo.commons.io.res.type.file.FileResource; import railo.runtime.exp.PageException; import railo.runtime.type.util.ArrayUtil; /** * Classloader that load classes from resources */ public final class ResourceClassLoader extends URLClassLoader implements Closeable { private List<Resource> resources=new ArrayList<Resource>(); private Map<String,ResourceClassLoader> customCLs; /* * * Constructor of the class * @param resources * @throws PageException ResourceClassLoader(Resource[] resources) throws IOException { super(doURLs(resources)); }*/ /** * Constructor of the class * @param reses * @param parent * @throws PageException */ public ResourceClassLoader(Resource[] resources, ClassLoader parent) throws IOException { super(doURLs(resources), parent); for(int i=0;i<resources.length;i++){ this.resources.add(resources[i]); } } public ResourceClassLoader(ClassLoader parent) { super(new URL[0], parent); } /** * @return the resources */ public Resource[] getResources() { return resources.toArray(new Resource[resources.size()]); } /** * translate resources to url Objects * @param reses * @return * @throws PageException */ public static URL[] doURLs(Resource[] reses) throws IOException { List<URL> list=new ArrayList<URL>(); for(int i=0;i<reses.length;i++) { if(reses[i].isDirectory() || "jar".equalsIgnoreCase(ResourceUtil.getExtension(reses[i],null))) list.add(doURL(reses[i])); } return list.toArray(new URL[list.size()]); } private static URL doURL(Resource res) throws IOException { if(!(res instanceof FileResource)) throw new IOException("resource ["+res.getPath()+"] must be a local file"); return ((FileResource)res).toURL(); } @Override public void close(){} public synchronized void addResourcesX(Resource[] reses) throws IOException { for(int i=0;i<reses.length;i++){ if(!this.resources.contains(reses[i])){ this.resources.add(reses[i]); addURL(doURL(reses[i])); } } } public ResourceClassLoader getCustomResourceClassLoader(Resource[] resources) throws IOException{ if(ArrayUtil.isEmpty(resources)) return this; String key = hash(resources); ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); if(rcl!=null) return rcl; resources=ResourceUtil.merge(this.getResources(), resources); rcl=new ResourceClassLoader(resources,getParent()); if(customCLs==null)customCLs=new ReferenceMap(); customCLs.put(key, rcl); return rcl; } public ResourceClassLoader getCustomResourceClassLoader2(Resource[] resources) throws IOException{ if(ArrayUtil.isEmpty(resources)) return this; String key = hash(resources); ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); if(rcl!=null) return rcl; rcl=new ResourceClassLoader(resources,this); if(customCLs==null)customCLs=new ReferenceMap(); customCLs.put(key, rcl); return rcl; } private String hash(Resource[] resources) { Arrays.sort(resources); StringBuilder sb=new StringBuilder(); for(int i=0;i<resources.length;i++){ sb.append(ResourceUtil.getCanonicalPathEL(resources[i])); sb.append(';'); } return MD5.getDigestAsString(sb.toString(),null); } }