View Javadoc

1   package org.openrdf.elmo.dynacode;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.File;
5   import java.io.FileOutputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.net.MalformedURLException;
9   import java.net.URL;
10  import java.util.ArrayList;
11  import java.util.Enumeration;
12  import java.util.List;
13  import java.util.concurrent.ConcurrentHashMap;
14  import java.util.concurrent.ConcurrentMap;
15  
16  import javassist.CannotCompileException;
17  import javassist.ClassPool;
18  import javassist.CtClass;
19  import javassist.LoaderClassPath;
20  import javassist.NotFoundException;
21  import javassist.bytecode.Descriptor;
22  
23  import org.openrdf.elmo.exceptions.ElmoCompositionException;
24  
25  public class ClassFactory extends ClassLoader {
26  	private static final URL exists;
27  
28  	static {
29  		try {
30  			exists = new URL("http://java/"
31  					+ ClassFactory.class.getName().replace('.', '/')
32  					+ "#exists");
33  		} catch (MalformedURLException e) {
34  			throw new AssertionError(e);
35  		}
36  	}
37  
38  	private static CtClass getPrimitive(Class<?> type, ClassFactory cp) {
39  		if (type.equals(Boolean.TYPE))
40  			return CtClass.booleanType;
41  		if (type.equals(Byte.TYPE))
42  			return CtClass.byteType;
43  		if (type.equals(Character.TYPE))
44  			return CtClass.charType;
45  		if (type.equals(Double.TYPE))
46  			return CtClass.doubleType;
47  		if (type.equals(Float.TYPE))
48  			return CtClass.floatType;
49  		if (type.equals(Integer.TYPE))
50  			return CtClass.intType;
51  		if (type.equals(Long.TYPE))
52  			return CtClass.longType;
53  		if (type.equals(Short.TYPE))
54  			return CtClass.shortType;
55  		if (type.equals(Void.TYPE))
56  			return CtClass.voidType;
57  		throw new ElmoCompositionException("Unknown primative type: "
58  				+ type.getName());
59  	}
60  
61  	private ClassPool cp;
62  	private File target;
63  	private ConcurrentMap<String, byte[]> bytecodes;
64  	private List<ClassLoader> alternatives = new ArrayList<ClassLoader>();
65  
66  	/**
67  	 * Creates a new Class Factory using the current context class loader.
68  	 */
69  	public ClassFactory() {
70  		this(Thread.currentThread().getContextClassLoader());
71  	}
72  
73  	/**
74  	 * Create a given Class Factory with the given class loader.
75  	 * 
76  	 * @param parent
77  	 */
78  	public ClassFactory(ClassLoader parent) {
79  		super(parent);
80  		cp = new ClassPool();
81  		cp.appendClassPath(new LoaderClassPath(this));
82  		bytecodes = new ConcurrentHashMap<String, byte[]>();
83  		String property = System.getProperty("elmobeans.target");
84  		if (property != null) {
85  			target = new File(property);
86  		}
87  	}
88  
89  	/**
90  	 * Create the new Java Class from this template.
91  	 * 
92  	 * @param template
93  	 * @return new Java Class Object
94  	 * @throws ElmoCompositionException
95  	 */
96  	public Class<?> createClass(ClassTemplate template)
97  			throws ElmoCompositionException {
98  		CtClass cc = template.getCtClass();
99  		String name = cc.getName();
100 		try {
101 			byte[] bytecode = cc.toBytecode();
102 			cc.detach();
103 			return defineClass(name, bytecode);
104 		} catch (IOException e) {
105 			throw new ElmoCompositionException(e);
106 		} catch (CannotCompileException e) {
107 			throw new ElmoCompositionException(e);
108 		}
109 	}
110 
111 	/**
112 	 * Create a new Class template, which can later be used to create a Java
113 	 * class.
114 	 * 
115 	 * @param className
116 	 * @return temporary Class template
117 	 */
118 	public ClassTemplate createClassTemplate(String className) {
119 		return new ClassTemplate(cp.makeClass(className), this);
120 	}
121 
122 	/**
123 	 * Create a new Class template, which can later be used to create a Java
124 	 * class.
125 	 * 
126 	 * @param name
127 	 * @param class1 super class
128 	 * @return temporary Class template
129 	 */
130 	public ClassTemplate createClassTemplate(String name, Class<?> class1) {
131 		try {
132 			CtClass cc = cp.makeClass(name, cp.get(class1.getName()));
133 			return new ClassTemplate(cc, this);
134 		} catch (NotFoundException e) {
135 			throw new ElmoCompositionException(e);
136 		}
137 	}
138 
139 	@Override
140 	public URL getResource(String name) {
141 		if (bytecodes.containsKey(name))
142 			return exists;
143 		URL url = super.getResource(name);
144 		if (url != null)
145 			return url;
146 		synchronized (alternatives) {
147 			for (ClassLoader cl : alternatives) {
148 				url = cl.getResource(name);
149 				if (url != null)
150 					return url;
151 			}
152 		}
153 		return null;
154 	}
155 
156 	@Override
157 	public InputStream getResourceAsStream(String name) {
158 		if (bytecodes.containsKey(name)) {
159 			byte[] b = bytecodes.get(name);
160 			return new ByteArrayInputStream(b);
161 		}
162 		InputStream stream = super.getResourceAsStream(name);
163 		if (stream != null)
164 			return stream;
165 		synchronized (alternatives) {
166 			for (ClassLoader cl : alternatives) {
167 				stream = cl.getResourceAsStream(name);
168 				if (stream != null)
169 					return stream;
170 			}
171 		}
172 		return null;
173 	}
174 
175 	@Override
176 	public Enumeration<URL> getResources(String name) throws IOException {
177 		Enumeration<URL> resources = super.getResources(name);
178 		if (resources.hasMoreElements())
179 			return resources;
180 		synchronized (alternatives) {
181 			for (ClassLoader cl : alternatives) {
182 				resources = cl.getResources(name);
183 				if (resources.hasMoreElements())
184 					return resources;
185 			}
186 		}
187 		return resources;
188 	}
189 
190 	@Override
191 	protected Class<?> findClass(String name) throws ClassNotFoundException {
192 		try {
193 			return super.findClass(name);
194 		} catch (ClassNotFoundException e) {
195 			synchronized (alternatives) {
196 				for (ClassLoader cl : alternatives) {
197 					try {
198 						return cl.loadClass(name);
199 					} catch (ClassNotFoundException e1) {
200 						continue;
201 					}
202 				}
203 			}
204 			throw e;
205 		}
206 	}
207 
208 	/**
209 	 * Causes all created Java Classes to be saved into this directory.
210 	 * @param folder
211 	 */
212 	public void setTraget(File folder) {
213 		target = folder;
214 	}
215 
216 	CtClass get(Class<?> type) throws ElmoCompositionException {
217 		if (type.isPrimitive()) {
218 			return getPrimitive(type, this);
219 		}
220 		try {
221 			if (type.isArray())
222 				return Descriptor.toCtClass(type.getName(), cp);
223 			return cp.get(type.getName());
224 		} catch (NotFoundException e) {
225 			try {
226 				appendClassLoader(type.getClassLoader());
227 				if (type.isArray())
228 					return Descriptor.toCtClass(type.getName(), cp);
229 				return cp.get(type.getName());
230 			} catch (NotFoundException e1) {
231 				throw new ElmoCompositionException(e);
232 			}
233 		}
234 	}
235 
236 	private void appendClassLoader(ClassLoader cl) {
237 		cp.appendClassPath(new LoaderClassPath(cl));
238 		synchronized (alternatives) {
239 			alternatives.add(cl);
240 		}
241 	}
242 
243 	private Class defineClass(String name, byte[] bytecode) {
244 		String resource = name.replace('.', '/') + ".class";
245 		if (target != null) {
246 			saveResource(resource, bytecode);
247 		}
248 		bytecodes.putIfAbsent(resource, bytecode);
249 		return defineClass(name, bytecode, 0, bytecode.length);
250 	}
251 
252 	private void saveResource(String fileName, byte[] bytecode) {
253 		try {
254 			File file = new File(target, fileName);
255 			file.getParentFile().mkdirs();
256 			FileOutputStream out = new FileOutputStream(file);
257 			try {
258 				out.write(bytecode);
259 			} finally {
260 				out.close();
261 			}
262 		} catch (Exception e) {
263 		}
264 	}
265 
266 	Class<?> getJavaClass(CtClass cc) throws ClassNotFoundException {
267 		if (cc.isPrimitive()) {
268 			if (cc.equals(CtClass.booleanType))
269 				return Boolean.TYPE;
270 			if (cc.equals(CtClass.byteType))
271 				return Byte.TYPE;
272 			if (cc.equals(CtClass.charType))
273 				return Character.TYPE;
274 			if (cc.equals(CtClass.doubleType))
275 				return Double.TYPE;
276 			if (cc.equals(CtClass.floatType))
277 				return Float.TYPE;
278 			if (cc.equals(CtClass.intType))
279 				return Integer.TYPE;
280 			if (cc.equals(CtClass.longType))
281 				return Long.TYPE;
282 			if (cc.equals(CtClass.shortType))
283 				return Short.TYPE;
284 			throw new AssertionError();
285 		}
286 		String name = Descriptor.toJavaName(Descriptor.toJvmName(cc));
287 		return Class.forName(name, true, this);
288 	}
289 }