1 /**
2 * Copyright 2005-2014 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.rice.krad.uif.util;
17
18 import java.lang.ref.Reference;
19 import java.lang.ref.WeakReference;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Modifier;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Queue;
27 import java.util.WeakHashMap;
28 import java.util.concurrent.ConcurrentLinkedQueue;
29
30 /**
31 * Simple utility class for implementing an object recycling factory pattern.
32 *
33 * <p>
34 * Weak references to objects are held by a thread-local queue. When a process has finished working
35 * with an object, the {@link #recycle} method may be offer the recycled object to the queue for
36 * consideration as reusable on the same thread.
37 * </p>
38 *
39 * @author Kuali Rice Team (rice.collab@kuali.org)
40 */
41 public final class RecycleUtils {
42
43 /**
44 * Thread local reference to recycled objects.
45 */
46 private final static Map<Class<?>, Reference<Queue<Object>>> RECYCLE =
47 Collections.synchronizedMap(new WeakHashMap<Class<?>, Reference<Queue<Object>>>());
48
49 /**
50 * Field cache to reduce reflection overhead during clean operations.
51 */
52 private final static Map<Class<?>, List<Field>> FIELD_CACHE =
53 Collections.synchronizedMap(new WeakHashMap<Class<?>, List<Field>>());
54
55 /**
56 * Get an instance of the given class that has previously been recycled on the same thread, if
57 * an instance of available.
58 *
59 * @param <T> recycled instance type
60 * @param c The class.
61 * @return An instance of the given class previously recycled on the same thread, if one is
62 * available. If no instance is available, then null is returned.
63 */
64 public static <T> T getRecycledInstance(Class<T> c) {
65 return c.cast(getRecycleQueue(c).poll());
66 }
67
68 /**
69 * Get an instance of the given class that has previously been recycled on the same thread, or a
70 * new instance using a default constructor if a recycled instance is not available.
71 *
72 * @param <T> recycled instance type
73 * @param c The class.
74 * @return An instance of the given class previously recycled on the same thread, if one is
75 * available. If no instance is available, then null is returned.
76 */
77 public static <T> T getInstance(Class<T> c) {
78 T rv = getRecycledInstance(c);
79
80 if (rv == null) {
81 try {
82 rv = c.newInstance();
83 } catch (InstantiationException e) {
84 throw new IllegalStateException("Unabled to instantiate " + c);
85 } catch (IllegalAccessException e) {
86 throw new IllegalStateException("Unabled to instantiate " + c);
87 }
88 }
89
90 return rv;
91 }
92
93 /**
94 * Recycle a instance, for later retrieval in the same thread.
95 *
96 * <p>
97 * Note that this method does not clean the instance, it only queues it for later retrieval by
98 * {@link #getRecycledInstance(Class)}. The state of the instance should be cleared before
99 * passing to this method. For a flexible means to clean instances using reflection
100 * {@link #clean(Object, Class)} may be considered, however note that a manually implemented
101 * clean operation will generally perform faster.
102 * </p>
103 *
104 * @param instance The instance to recycle.
105 */
106 public static void recycle(Object instance) {
107 if (instance != null) {
108 getRecycleQueue(instance.getClass()).offer(instance);
109 }
110 }
111
112 /**
113 * Clean all instance fields.
114 *
115 * @param <T> recycled instance type
116 * @param instance The instance to clean.
117 */
118 public static <T> void clean(T instance) {
119 clean(instance, Object.class);
120 }
121
122 /**
123 * Clean all instance fields, walking up the class hierarchy to the indicated super class.
124 *
125 * @param <T> recycled instance type
126 * @param instance The instance to clean.
127 * @param top The point in the class hierarchy at which to stop cleaning fields.
128 */
129 public static <T> void clean(T instance, Class<? super T> top) {
130 Class<?> c = instance.getClass();
131 while (c != null && c != top && top.isAssignableFrom(c)) {
132
133 List<Field> fields;
134 synchronized (FIELD_CACHE) {
135 // Get within synchronized, because FIELD_CACHE is a WeakHashMap
136 fields = FIELD_CACHE.get(c);
137 if (fields == null) {
138 Field[] declared = c.getDeclaredFields();
139 fields = new ArrayList<Field>(declared.length);
140
141 // Don't clean static fields.
142 for (Field field : fields) {
143 if ((field.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
144 field.setAccessible(true);
145 fields.add(field);
146 }
147 }
148
149 fields = Collections.unmodifiableList(fields);
150 FIELD_CACHE.put(c, fields);
151 }
152 }
153
154 for (Field field : fields) {
155 try {
156 Class<?> type = field.getType();
157
158 if (type.isPrimitive()) {
159
160 if (type == Integer.TYPE) {
161 field.set(instance, 0);
162 } else if (type == Boolean.TYPE) {
163 field.set(instance, false);
164 } else if (type == Long.TYPE) {
165 field.set(instance, 0L);
166 } else if (type == Character.TYPE) {
167 field.set(instance, '\0');
168 } else if (type == Double.TYPE) {
169 field.set(instance, 0.0);
170 } else if (type == Float.TYPE) {
171 field.set(instance, 0.0f);
172 } else if (type == Short.TYPE) {
173 field.set(instance, (short) 0);
174 } else if (type == Byte.TYPE) {
175 field.set(instance, (byte) 0);
176 }
177
178 } else {
179 field.set(instance, null);
180 }
181
182 } catch (IllegalAccessException e) {
183 throw new IllegalStateException("Unexpected error setting " + field, e);
184 }
185 }
186
187 c = c.getSuperclass();
188 }
189 }
190
191 /**
192 * Get a recycle queue by class.
193 *
194 * @param c The class to get a recycle queue for.
195 */
196 private static Queue<Object> getRecycleQueue(Class<?> c) {
197 synchronized (RECYCLE) {
198 Reference<Queue<Object>> recycleQueueRef = RECYCLE.get(c);
199 Queue<Object> recycleQueue = recycleQueueRef == null ? null : recycleQueueRef.get();
200 if (recycleQueue == null) {
201 recycleQueue = new ConcurrentLinkedQueue<Object>();
202 RECYCLE.put(c, new WeakReference<Queue<Object>>(recycleQueue));
203 }
204
205 return recycleQueue;
206 }
207 }
208
209 /**
210 * Private constructor - utility class only.
211 */
212 private RecycleUtils() {}
213
214 }