001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.io.res.util; 020 021import java.io.Closeable; 022import java.io.IOException; 023import java.net.URL; 024import java.net.URLClassLoader; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028import java.util.Map; 029 030import lucee.commons.digest.MD5; 031import lucee.commons.io.res.Resource; 032import lucee.commons.io.res.type.file.FileResource; 033import lucee.runtime.exp.PageException; 034import lucee.runtime.type.util.ArrayUtil; 035 036import org.apache.commons.collections.map.ReferenceMap; 037 038/** 039 * Classloader that load classes from resources 040 */ 041public final class ResourceClassLoader extends URLClassLoader implements Closeable { 042 043 private List<Resource> resources=new ArrayList<Resource>(); 044 private Map<String,ResourceClassLoader> customCLs; 045 046 /* * 047 * Constructor of the class 048 * @param resources 049 * @throws PageException 050 051 ResourceClassLoader(Resource[] resources) throws IOException { 052 super(doURLs(resources)); 053 }*/ 054 055 /** 056 * Constructor of the class 057 * @param reses 058 * @param parent 059 * @throws PageException 060 */ 061 public ResourceClassLoader(Resource[] resources, ClassLoader parent) throws IOException { 062 super(doURLs(resources), parent); 063 for(int i=0;i<resources.length;i++){ 064 this.resources.add(resources[i]); 065 } 066 } 067 068 public ResourceClassLoader(ClassLoader parent) { 069 super(new URL[0], parent); 070 } 071 072 /** 073 * @return the resources 074 */ 075 public Resource[] getResources() { 076 return resources.toArray(new Resource[resources.size()]); 077 } 078 079 /** 080 * translate resources to url Objects 081 * @param reses 082 * @return 083 * @throws PageException 084 */ 085 public static URL[] doURLs(Resource[] reses) throws IOException { 086 List<URL> list=new ArrayList<URL>(); 087 for(int i=0;i<reses.length;i++) { 088 if(reses[i].isDirectory() || "jar".equalsIgnoreCase(ResourceUtil.getExtension(reses[i],null))) 089 list.add(doURL(reses[i])); 090 } 091 return list.toArray(new URL[list.size()]); 092 093 } 094 private static URL doURL(Resource res) throws IOException { 095 if(!(res instanceof FileResource)) 096 throw new IOException("resource ["+res.getPath()+"] must be a local file"); 097 return ((FileResource)res).toURL(); 098 } 099 100 @Override 101 public void close(){} 102 103 public synchronized void addResourcesX(Resource[] reses) throws IOException { 104 for(int i=0;i<reses.length;i++){ 105 if(!this.resources.contains(reses[i])){ 106 this.resources.add(reses[i]); 107 addURL(doURL(reses[i])); 108 } 109 } 110 } 111 112 113 public ResourceClassLoader getCustomResourceClassLoader(Resource[] resources) throws IOException{ 114 if(ArrayUtil.isEmpty(resources)) return this; 115 String key = hash(resources); 116 ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); 117 if(rcl!=null) return rcl; 118 119 resources=ResourceUtil.merge(this.getResources(), resources); 120 rcl=new ResourceClassLoader(resources,getParent()); 121 if(customCLs==null)customCLs=new ReferenceMap(); 122 customCLs.put(key, rcl); 123 return rcl; 124 } 125 126 public ResourceClassLoader getCustomResourceClassLoader2(Resource[] resources) throws IOException{ 127 if(ArrayUtil.isEmpty(resources)) return this; 128 String key = hash(resources); 129 ResourceClassLoader rcl=customCLs==null?null:customCLs.get(key); 130 if(rcl!=null) return rcl; 131 132 rcl=new ResourceClassLoader(resources,this); 133 if(customCLs==null)customCLs=new ReferenceMap(); 134 customCLs.put(key, rcl); 135 return rcl; 136 } 137 138 private String hash(Resource[] resources) { 139 Arrays.sort(resources); 140 StringBuilder sb=new StringBuilder(); 141 for(int i=0;i<resources.length;i++){ 142 sb.append(ResourceUtil.getCanonicalPathEL(resources[i])); 143 sb.append(';'); 144 } 145 return MD5.getDigestAsString(sb.toString(),null); 146 } 147 148}