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.concurrent.ConcurrentHashMap;
11  import java.util.concurrent.ConcurrentMap;
12  
13  import javassist.CannotCompileException;
14  import javassist.ClassPool;
15  import javassist.CtClass;
16  import javassist.LoaderClassPath;
17  import javassist.NotFoundException;
18  import javassist.bytecode.Descriptor;
19  
20  import org.openrdf.elmo.exceptions.ElmoCompositionException;
21  
22  public class ClassFactory extends ClassLoader {
23  	private static final URL exists;
24  
25  	static {
26  		try {
27  			exists = new URL("http://java/"
28  					+ ClassFactory.class.getName().replace('.', '/')
29  					+ "#exists");
30  		} catch (MalformedURLException e) {
31  			throw new AssertionError(e);
32  		}
33  	}
34  
35  	private static CtClass getPrimitive(Class<?> type, ClassFactory cp) {
36  		if (type.equals(Boolean.TYPE))
37  			return CtClass.booleanType;
38  		if (type.equals(Byte.TYPE))
39  			return CtClass.byteType;
40  		if (type.equals(Character.TYPE))
41  			return CtClass.charType;
42  		if (type.equals(Double.TYPE))
43  			return CtClass.doubleType;
44  		if (type.equals(Float.TYPE))
45  			return CtClass.floatType;
46  		if (type.equals(Integer.TYPE))
47  			return CtClass.intType;
48  		if (type.equals(Long.TYPE))
49  			return CtClass.longType;
50  		if (type.equals(Short.TYPE))
51  			return CtClass.shortType;
52  		if (type.equals(Void.TYPE))
53  			return CtClass.voidType;
54  		throw new ElmoCompositionException("Unknown primative type: "
55  				+ type.getName());
56  	}
57  
58  	private ConcurrentMap<String, byte[]> bytecodes;
59  
60  	private ClassPool cp;
61  
62  	private File target;
63  
64  	/**
65  	 * Creates a new Class Factory using the current context class loader.
66  	 */
67  	public ClassFactory() {
68  		this(Thread.currentThread().getContextClassLoader());
69  	}
70  
71  	/**
72  	 * Create a given Class Factory with the given class loader.
73  	 * 
74  	 * @param parent
75  	 */
76  	public ClassFactory(ClassLoader parent) {
77  		super(parent);
78  		cp = new ClassPool();
79  		cp.appendClassPath(new LoaderClassPath(this));
80  		bytecodes = new ConcurrentHashMap<String, byte[]>();
81  		String property = System.getProperty("elmobeans.target");
82  		if (property != null) {
83  			target = new File(property);
84  		}
85  	}
86  
87  	/**
88  	 * Create the new Java Class from this template.
89  	 * 
90  	 * @param template
91  	 * @return new Java Class Object
92  	 * @throws ElmoCompositionException
93  	 */
94  	public Class<?> createClass(ClassTemplate template)
95  			throws ElmoCompositionException {
96  		CtClass cc = template.getCtClass();
97  		String name = cc.getName();
98  		try {
99  			byte[] bytecode = cc.toBytecode();
100 			cc.detach();
101 			return defineClass(name, bytecode);
102 		} catch (IOException e) {
103 			throw new ElmoCompositionException(e);
104 		} catch (CannotCompileException e) {
105 			throw new ElmoCompositionException(e);
106 		}
107 	}
108 
109 	/**
110 	 * Create a new Class template, which can later be used to create a Java
111 	 * class.
112 	 * 
113 	 * @param className
114 	 * @return temporary Class template
115 	 */
116 	public ClassTemplate createClassTemplate(String className) {
117 		return new ClassTemplate(cp.makeClass(className), this);
118 	}
119 
120 	/**
121 	 * Create a new Class template, which can later be used to create a Java
122 	 * class.
123 	 * 
124 	 * @param name
125 	 * @param class1 super class
126 	 * @return temporary Class template
127 	 */
128 	public ClassTemplate createClassTemplate(String name, Class<?> class1) {
129 		try {
130 			CtClass cc = cp.makeClass(name, cp.get(class1.getName()));
131 			return new ClassTemplate(cc, this);
132 		} catch (NotFoundException e) {
133 			throw new ElmoCompositionException(e);
134 		}
135 	}
136 
137 	@Override
138 	public URL getResource(String name) {
139 		if (bytecodes.containsKey(name))
140 			return exists;
141 		return super.getResource(name);
142 	}
143 
144 	@Override
145 	public InputStream getResourceAsStream(String name) {
146 		if (bytecodes.containsKey(name)) {
147 			byte[] b = bytecodes.get(name);
148 			return new ByteArrayInputStream(b);
149 		}
150 		return super.getResourceAsStream(name);
151 	}
152 
153 	/**
154 	 * Causes all created Java Classes to be saved into this directory.
155 	 * @param folder
156 	 */
157 	public void setTraget(File folder) {
158 		target = folder;
159 	}
160 
161 	CtClass get(Class<?> type) throws ElmoCompositionException {
162 		if (type.isPrimitive()) {
163 			return getPrimitive(type, this);
164 		}
165 		try {
166 			if (type.isArray())
167 				return Descriptor.toCtClass(type.getName(), cp);
168 			return cp.get(type.getName());
169 		} catch (NotFoundException e) {
170 			throw new ElmoCompositionException(e);
171 		}
172 	}
173 
174 	private Class defineClass(String name, byte[] bytecode) {
175 		String resource = name.replace('.', '/') + ".class";
176 		if (target != null) {
177 			saveResource(resource, bytecode);
178 		}
179 		bytecodes.putIfAbsent(resource, bytecode);
180 		return defineClass(name, bytecode, 0, bytecode.length);
181 	}
182 
183 	private void saveResource(String fileName, byte[] bytecode) {
184 		try {
185 			File file = new File(target, fileName);
186 			file.getParentFile().mkdirs();
187 			FileOutputStream out = new FileOutputStream(file);
188 			try {
189 				out.write(bytecode);
190 			} finally {
191 				out.close();
192 			}
193 		} catch (Exception e) {
194 		}
195 	}
196 
197 	Class<?> getJavaClass(CtClass cc) throws ClassNotFoundException {
198 		if (cc.isPrimitive()) {
199 			if (cc.equals(CtClass.booleanType))
200 				return Boolean.TYPE;
201 			if (cc.equals(CtClass.byteType))
202 				return Byte.TYPE;
203 			if (cc.equals(CtClass.charType))
204 				return Character.TYPE;
205 			if (cc.equals(CtClass.doubleType))
206 				return Double.TYPE;
207 			if (cc.equals(CtClass.floatType))
208 				return Float.TYPE;
209 			if (cc.equals(CtClass.intType))
210 				return Integer.TYPE;
211 			if (cc.equals(CtClass.longType))
212 				return Long.TYPE;
213 			if (cc.equals(CtClass.shortType))
214 				return Short.TYPE;
215 			throw new AssertionError();
216 		}
217 		String name = Descriptor.toJavaName(Descriptor.toJvmName(cc));
218 		return Class.forName(name, true, this);
219 	}
220 }