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
68
69 public ClassFactory() {
70 this(Thread.currentThread().getContextClassLoader());
71 }
72
73
74
75
76
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
91
92
93
94
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
113
114
115
116
117
118 public ClassTemplate createClassTemplate(String className) {
119 return new ClassTemplate(cp.makeClass(className), this);
120 }
121
122
123
124
125
126
127
128
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
210
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 }