4 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, Santa
5 * Clara, California 95054, U.S.A. All rights reserved.
7 * Sun Microsystems, Inc. has intellectual property rights relating to
8 * technology embodied in the product that is described in this
9 * document. In particular, and without limitation, these
10 * intellectual property rights may include one or more of the
11 * U.S. patents listed at http://www.sun.com/patents and one or more
12 * additional patents or pending patent applications in the U.S. and
15 * U.S. Government Rights - Commercial software.
16 * Government users are subject to the Sun Microsystems, Inc. standard
17 * license agreement and applicable provisions of the FAR and its
18 * supplements. Use is subject to license terms. Sun, Sun
19 * Microsystems, the Sun logo and Java are trademarks or registered
20 * trademarks of Sun Microsystems, Inc. in the U.S. and other
23 * This product is covered and controlled by U.S. Export Control laws
24 * and may be subject to the export or import laws in other countries.
25 * Nuclear, missile, chemical biological weapons or nuclear maritime
26 * end uses or end users, whether direct or indirect, are strictly
27 * prohibited. Export or reexport to countries subject to
28 * U.S. embargo or to entities identified on U.S. export exclusion
29 * lists, including, but not limited to, the denied persons and
30 * specially designated nationals lists is strictly prohibited.
33 package dstm2.factory;
37 import java.util.Map.Entry;
38 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.io.IOException;
42 import java.io.ByteArrayOutputStream;
44 import dstm2.Transaction;
45 import TransactionalIO.exceptions.PanicException;
47 import org.apache.bcel.classfile.*;
48 import org.apache.bcel.generic.*;
49 import org.apache.bcel.util.*;
50 import org.apache.bcel.Constants;
51 import org.apache.bcel.Repository;
52 import org.apache.bcel.classfile.Method;
55 * Provides utility methods for factories.
56 * @author Maurice Herlihy
58 public abstract class BaseFactory<T> implements Factory<T> {
60 * BCEL is not thread-safe
62 protected static Object lock = new Object();
64 * BCEL instruction factory.
66 protected InstructionFactory _factory;
68 * Constant pool for created class.
70 protected ConstantPoolGen _cp;
72 * Used to generate the class.
74 protected ClassGen _cg;
76 * The actual class we are building.
78 protected Class<T> theClass;
80 * The name of the class we are building.
82 protected String className;
84 * Interface exported by created class.
86 protected String interfaceName;
91 protected Set<Property> properties = new HashSet<Property>();
93 * Set of other methods.
95 protected Set<Method> methods = new HashSet<Method>();
98 * set of interfaces satisfied by this class
100 protected Set<Class> interfaces = new HashSet<Class>();
103 * This constructor separates methods into properties and miscellaneous
104 * methods. For properties, it deduces type and name of properties, and
105 * stores them in the <CODE>properties</CODE> set. Other methods go into
106 * the <CODE>methods</CODE> field.
107 * @param interfaces additional interfaces satisfied by this class and understood by this factory.
108 * @param _class interface class
110 public BaseFactory(Class<T> _class) {
111 if (!_class.isInterface()) {
112 throw new PanicException("%s is not an interface", _class);
114 parseProperties(_class);
118 * Once methods and fields have been defined, this method turns the
119 * byte code into a Java class.
121 protected void seal() {
122 ByteArrayOutputStream out = new ByteArrayOutputStream();
124 _cg.getJavaClass().dump(out);
125 } catch (IOException ex) {
126 throw new PanicException(ex);
128 byte[] bytes = out.toByteArray();
129 ClassLoader<T> classLoader = new ClassLoader<T>(bytes);
130 theClass = classLoader.make();
134 * Create a new field.
135 * @param type BCEL type of field.
136 * @param name field name
138 protected void createField(Type type, String name) {
139 FieldGen field = new FieldGen(0, type, name, _cp);
140 _cg.addField(field.getField());
144 * Extracts properties from interface definition. Does rudimentary type
145 * and sanity checking
146 * @param _class class to inspect.
148 private void parseProperties(Class _class) {
149 // Make sure implied field types are scalar or atomic.
150 // Enough to check return type of getField() methods.
151 for (java.lang.reflect.Method method : _class.getMethods()) {
152 if (method.getName().substring(0,3).equals("get")) {
153 if (!isAtomicOrScalar(method.getReturnType())) {
154 throw new PanicException("Method %s return type %s not scalar or atomic.",
155 method.getName(), method.getReturnType().getName());
161 jc = Repository.lookupClass(_class);
162 } catch (java.lang.ClassNotFoundException ex) {
163 throw new PanicException("Class not found: " + _class);
165 properties = new HashSet<Property>();
166 methods = new HashSet<Method>();
167 Map<String,Property> fieldMap = new HashMap<String,Property>();
168 for (Method m : jc.getMethods()) {
169 String methodName = m.getName();
170 Class returnClass = null; // need both class & type
172 returnClass = _class.getMethod(methodName).getReturnType();
173 } catch (NoSuchMethodException e) {};
174 Type[] parameterTypes = m.getArgumentTypes();
175 // for get and set methods
176 String fieldName = unCapitalize(methodName.substring(3));
177 // for boolean is method
178 String fieldNameBool = unCapitalize(methodName.substring(2));
180 if (methodName.substring(0,3).equals("get")) {
181 Property record = fieldMap.get(fieldName);
182 if (parameterTypes.length != 0) {
183 throw new PanicException("Class %s: method %s\n has bad signature\n",
184 jc.getClassName(), methodName);
186 Type returnType = m.getReturnType();
188 if (record == null) {
189 Property newRecord = new Property(returnClass, returnType, fieldName);
190 properties.add(newRecord);
191 newRecord.getMethod = m;
192 fieldMap.put(fieldName, newRecord);
193 } else { // not first time
194 if (!record.type.equals(returnType)) {
195 throw new PanicException("Class %s: inconsistent definitions for field type %s: %s vs %s\n",
196 jc.getClassName(), fieldName, record.type, returnType);
197 } else if (record.getMethod != null) {
198 throw new PanicException("Class %s: multiple definitions for %s\n",
199 jc.getClassName(), methodName);
201 record.getMethod = m;
204 } else if (methodName.substring(0,2).equals("is")) {
205 Property record = fieldMap.get(fieldNameBool);
206 if (parameterTypes.length != 0) {
207 throw new PanicException("Class %s: method %s\n has bad signature\n",
208 jc.getClassName(), methodName);
210 Type returnType = m.getReturnType();
211 if (!returnType.equals(Type.BOOLEAN)) {
212 throw new PanicException("Class %s: method %s\n must have Boolean argument\n",
213 jc.getClassName(), methodName);
216 if (record == null) {
217 Property newRecord = new Property(returnClass, returnType, fieldNameBool);
218 properties.add(newRecord);
219 newRecord.getMethod = m;
220 fieldMap.put(fieldNameBool, newRecord);
221 } else { // not first time
222 if (!record.type.equals(returnType)) {
223 throw new PanicException("Class %s: inconsistent definitions for field type %s: %s vs %s\n",
224 jc.getClassName(), fieldNameBool, record.type, returnType);
225 } else if (record.getMethod != null) {
226 throw new PanicException("Class %s: multiple definitions for %s\n",
227 jc.getClassName(), methodName);
229 record.getMethod = m;
231 } else if (methodName.substring(0,3).equals("set")) {
232 Property record = fieldMap.get(fieldName);
233 if (parameterTypes.length != 1) {
234 throw new PanicException("Class %s: method %s has bad signature\n",
235 jc.getClassName(), methodName);
237 Type argType = parameterTypes[0];
239 if (record == null) {
240 Property newRecord = new Property(returnClass, argType, fieldName);
241 properties.add(newRecord);
242 newRecord.setMethod = m;
243 fieldMap.put(fieldName, newRecord);
244 } else { // not first time
245 if (!record.type.equals(argType)) {
246 throw new PanicException("Class %s: inconsistent definitions for field arg type %s: %s vs %s\n",
247 jc.getClassName(), fieldName, record.type, argType);
248 } else if (record.setMethod != null) {
249 throw new PanicException("Class %s: multiple definitions for %s\n",
250 jc.getClassName(), methodName);
252 record.setMethod = m;
255 throw new PanicException("%s: not a get/set method\n", m.getName());
258 for (Entry<String, Property> entry : fieldMap.entrySet()) {
259 if (entry.getValue().getMethod == null) {
260 throw new PanicException("Class %s: method get%s not defined\n",
261 jc.getClassName(), entry.getKey());
262 } else if (entry.getValue().setMethod == null) {
263 throw new PanicException("Class %s: method set%s not defined\n",
264 jc.getClassName(), entry.getKey());
270 * Did caller declare a <CODE>void release()</CODE> method?
271 * @return whether to provide a release() implementation.
273 protected boolean releaseDefined() {
274 for (Method m : methods) {
275 if (m.getName().equals("release") &&
276 m.getReturnType() == Type.NULL &&
277 m.getArgumentTypes().length == 0) {
285 * Checks whether a field type is scalar or declared atomic.
286 * @param _class class to check
287 * @return whether field is legit type for an atomic object
289 public boolean isAtomicOrScalar(Class _class) {
290 return _class.isPrimitive() ||
292 _class.isAnnotationPresent(atomic.class) ||
293 _class.equals(Boolean.class) ||
294 _class.equals(Character.class) ||
295 _class.equals(Byte.class) ||
296 _class.equals(Short.class) ||
297 _class.equals(Integer.class) ||
298 _class.equals(Long.class) ||
299 _class.equals(Float.class) ||
300 _class.equals(Double.class) ||
301 _class.equals(String.class);
304 String unCapitalize(String s) {
305 StringBuffer sb = new StringBuffer(s);
306 sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
307 return sb.toString();