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    }