001 /** 002 * Copyright 2005-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.uif.util; 017 018 import java.lang.ref.Reference; 019 import java.lang.ref.WeakReference; 020 import java.lang.reflect.Field; 021 import java.lang.reflect.Modifier; 022 import java.util.ArrayList; 023 import java.util.Collections; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Queue; 027 import java.util.WeakHashMap; 028 import java.util.concurrent.ConcurrentLinkedQueue; 029 030 /** 031 * Simple utility class for implementing an object recycling factory pattern. 032 * 033 * <p> 034 * Weak references to objects are held by a thread-local queue. When a process has finished working 035 * with an object, the {@link #recycle} method may be offer the recycled object to the queue for 036 * consideration as reusable on the same thread. 037 * </p> 038 * 039 * @author Kuali Rice Team (rice.collab@kuali.org) 040 */ 041 public final class RecycleUtils { 042 043 /** 044 * Thread local reference to recycled objects. 045 */ 046 private final static Map<Class<?>, Reference<Queue<Object>>> RECYCLE = 047 Collections.synchronizedMap(new WeakHashMap<Class<?>, Reference<Queue<Object>>>()); 048 049 /** 050 * Field cache to reduce reflection overhead during clean operations. 051 */ 052 private final static Map<Class<?>, List<Field>> FIELD_CACHE = 053 Collections.synchronizedMap(new WeakHashMap<Class<?>, List<Field>>()); 054 055 /** 056 * Get an instance of the given class that has previously been recycled on the same thread, if 057 * an instance of available. 058 * 059 * @param <T> recycled instance type 060 * @param c The class. 061 * @return An instance of the given class previously recycled on the same thread, if one is 062 * available. If no instance is available, then null is returned. 063 */ 064 public static <T> T getRecycledInstance(Class<T> c) { 065 return c.cast(getRecycleQueue(c).poll()); 066 } 067 068 /** 069 * Get an instance of the given class that has previously been recycled on the same thread, or a 070 * new instance using a default constructor if a recycled instance is not available. 071 * 072 * @param <T> recycled instance type 073 * @param c The class. 074 * @return An instance of the given class previously recycled on the same thread, if one is 075 * available. If no instance is available, then null is returned. 076 */ 077 public static <T> T getInstance(Class<T> c) { 078 T rv = getRecycledInstance(c); 079 080 if (rv == null) { 081 try { 082 rv = c.newInstance(); 083 } catch (InstantiationException e) { 084 throw new IllegalStateException("Unabled to instantiate " + c); 085 } catch (IllegalAccessException e) { 086 throw new IllegalStateException("Unabled to instantiate " + c); 087 } 088 } 089 090 return rv; 091 } 092 093 /** 094 * Recycle a instance, for later retrieval in the same thread. 095 * 096 * <p> 097 * Note that this method does not clean the instance, it only queues it for later retrieval by 098 * {@link #getRecycledInstance(Class)}. The state of the instance should be cleared before 099 * 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 }