View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.reggie.proxy;
19  
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Modifier;
22  import java.util.Arrays;
23  import java.util.Comparator;
24  import java.util.WeakHashMap;
25  import java.lang.ref.SoftReference;
26  import java.rmi.MarshalException;
27  
28  /**
29   * Maps Class to ServiceType/Base, Class to EntryClass/Base, and Class to
30   * Field[], with caching for efficiency.
31   *
32   * @author Sun Microsystems, Inc.
33   *
34   */
35  class ClassMapper {
36  
37      /** Weak Map from Class to SoftReference(ServiceTypeBase) */
38      private static final WeakHashMap serviceMap = new WeakHashMap(23);
39      /** Weak Map from Class to SoftReference(EntryClassBase) */
40      private static final WeakHashMap entryMap = new WeakHashMap(17);
41      /** Weak Map from Class to SoftReference(sorted Field[]) */
42      private static final WeakHashMap fieldMap = new WeakHashMap(17);
43      /** Comparator for sorting fields */
44      private static final FieldComparator comparator = new FieldComparator();
45      private static final ServiceType[] empty = {};
46      private static final Class[] noArg = new Class[0];
47  
48      private ClassMapper() {}
49  
50      /** Returns a ServiceTypeBase descriptor for a class. */
51      public static ServiceTypeBase toServiceTypeBase(Class cls) 
52          throws MarshalException 
53      {
54  	synchronized (serviceMap) {
55  	    return toServiceTypeBase(cls, true);
56  	}
57      }
58  
59      /**
60       * Returns a ServiceTypeBase descriptor for a class.  If needCodebase
61       * is false, the returned descriptor's codebase may be null.
62       */
63      private static ServiceTypeBase toServiceTypeBase(Class cls,
64  						     boolean needCodebase)
65  	throws MarshalException 
66      {
67  	if (cls == null)
68  	    return null;
69  	SoftReference cref = (SoftReference)serviceMap.get(cls);
70  	ServiceTypeBase stype = null;
71  	if (cref != null)
72  	    stype = (ServiceTypeBase)cref.get();
73  	if (stype == null) {
74  	    stype = new ServiceTypeBase(
75  			   new ServiceType(cls,
76  					   toServiceType(cls.getSuperclass()),
77  					   toServiceType(cls.getInterfaces())),
78  			   null);
79  	    serviceMap.put(cls, new SoftReference(stype));
80  	}
81  	if (needCodebase && stype.codebase == null)
82  	    stype.setCodebase(cls);
83  	return stype;
84      }
85  
86      /** Returns a ServiceType descriptor for a class. */
87      private static ServiceType toServiceType(Class cls) 
88  	throws MarshalException 
89      {
90  	if (cls != null)
91  	    return toServiceTypeBase(cls, false).type;
92  	return null;
93      }
94  
95      /** Converts an array of Class to an array of ServiceType. */
96      public static ServiceType[] toServiceType(Class[] classes) 
97  	throws MarshalException 
98      {
99  	if (classes == null)
100 	    return null;
101 	if (classes.length == 0)
102 	    return empty;
103 	ServiceType[] stypes = new ServiceType[classes.length];
104 	synchronized (serviceMap) {
105 	    for (int i = classes.length; --i >= 0; ) {
106 		stypes[i] = toServiceType(classes[i]);
107 	    }
108 	}
109 	return stypes;
110     }
111 
112     /** Returns a EntryClassBase descriptor for a class. */
113     public static EntryClassBase toEntryClassBase(Class cls) 
114 	throws MarshalException 
115     {
116 	synchronized (entryMap) {
117 	    return toEntryClassBase(cls, true);
118 	}
119     }
120 
121     /**
122      * Returns a EntryClassBase descriptor for a class.  If base is false,
123      * the returned descriptor's codebase may be null, and the class need
124      * not be public and need not have a no-arg constructor.
125      */
126     private static EntryClassBase toEntryClassBase(Class cls, boolean base) 
127         throws MarshalException 
128     {
129 	if (cls == null)
130 	    return null;
131 	SoftReference cref = (SoftReference)entryMap.get(cls);
132 	EntryClassBase eclass = null;
133 	if (cref != null)
134 	    eclass = (EntryClassBase)cref.get();
135 	if (eclass == null) {
136 	    if (base) {
137 		if (!Modifier.isPublic(cls.getModifiers()))
138 		    throw new IllegalArgumentException("entry class " +
139 						       cls.getName() +
140 						       " is not public");
141 		try {
142 		    cls.getConstructor(noArg);
143 		} catch (NoSuchMethodException e) {
144 		    throw new IllegalArgumentException("entry class " +
145 						       cls.getName() +
146 			        " does not have a public no-arg constructor");
147 		}
148 	    }
149 	    eclass = new EntryClassBase(
150 			     new EntryClass(cls,
151 					    toEntryClass(cls.getSuperclass())),
152 			     null);
153 	    entryMap.put(cls, new SoftReference(eclass));
154 	}
155 	if (base && eclass.codebase == null)
156 	    eclass.setCodebase(cls);
157 	return eclass;
158     }
159 
160     /** Returns an EntryClass descriptor for a class. */
161     private static EntryClass toEntryClass(Class cls) throws MarshalException {
162 	if (cls != null)
163 	    return toEntryClassBase(cls, false).eclass;
164 	return null;
165     }
166 
167     /** Field of an Entry class, with marshalling information */
168     static class EntryField {
169 	/** Field for the field */
170 	public final Field field;
171 	/**
172 	 * True if instances of the field need to be converted
173 	 * to MarshalledWrapper.  False if the type of the field
174 	 * is String, Integer, Boolean, Character, Long, Float,
175 	 * Double, Byte, or Short.
176 	 */
177 	public final boolean marshal;
178 
179 	/**
180 	 * Basic constructor.
181 	 */
182 	public EntryField(Field field) {
183 	    this.field = field;
184 	    Class c = field.getType();
185 	    marshal = !(c == String.class ||
186 			c == Integer.class ||
187 			c == Boolean.class ||
188 			c == Character.class ||
189 			c == Long.class ||
190 			c == Float.class ||
191 			c == Double.class ||
192 			c == Byte.class ||
193 			c == Short.class);
194 	}
195     }
196 
197     /**
198      * Returns public fields, in super to subclass order, sorted
199      * alphabetically within a given class.
200      */
201     public static EntryField[] getFields(Class cls) {
202 	synchronized (fieldMap) {
203 	    SoftReference cref = (SoftReference)fieldMap.get(cls);
204 	    EntryField[] efields = null;
205 	    if (cref != null)
206 		efields = (EntryField[])cref.get();
207 	    if (efields == null) {
208 		Field[] fields = cls.getFields();
209 		Arrays.sort(fields, comparator);
210 		int len = 0;
211 		for (int i = 0; i < fields.length; i++) {
212 		    if ((fields[i].getModifiers() &
213 			 (Modifier.STATIC|Modifier.FINAL|Modifier.TRANSIENT))
214 			== 0)
215 		    {
216 			if (fields[i].getType().isPrimitive())
217 			    throw new IllegalArgumentException("entry class " +
218 							       cls.getName() +
219 						  " has a primitive field");
220 			fields[len++] = fields[i];
221 		    }
222 		}
223 		efields = new EntryField[len];
224 		while (--len >= 0) {
225 		    efields[len] = new EntryField(fields[len]);
226 		}
227 		fieldMap.put(cls, new SoftReference(efields));
228 	    }
229 	    return efields;
230 	}
231     }
232 
233     /** Comparator for sorting fields. */
234     private static class FieldComparator implements Comparator {
235 	public FieldComparator() {}
236 
237 	/** Super before subclass, alphabetical within a given class */
238 	public int compare(Object o1, Object o2) {
239 	    Field f1 = (Field)o1;
240 	    Field f2 = (Field)o2;
241 	    if (f1 == f2)
242 		return 0;
243 	    if (f1.getDeclaringClass() == f2.getDeclaringClass())
244 		return f1.getName().compareTo(f2.getName());
245 	    if (f1.getDeclaringClass().isAssignableFrom(
246 						     f2.getDeclaringClass()))
247 		return -1;
248 	    return 1;
249 	}
250     }
251 }