001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.reflect; 023 024import java.io.Externalizable; 025import java.io.IOException; 026import java.io.ObjectInputStream; 027import java.io.ObjectOutputStream; 028import java.lang.ref.SoftReference; 029import java.lang.reflect.Field; 030import java.lang.reflect.InvocationTargetException; 031import java.lang.reflect.Method; 032import java.lang.reflect.Modifier; 033import java.util.ArrayList; 034import java.util.Collections; 035import java.util.List; 036 037import org.granite.messaging.annotations.Exclude; 038import org.granite.messaging.annotations.Include; 039import org.granite.messaging.annotations.Serialized; 040 041/** 042 * @author Franck WOLFF 043 */ 044public class ClassDescriptor { 045 046 private static final Class<?>[] WRITE_OBJECT_PARAMS = new Class<?>[]{ ObjectOutputStream.class }; 047 private static final Class<?>[] READ_OBJECT_PARAMS = new Class<?>[]{ ObjectInputStream.class }; 048 049 private final Class<?> cls; 050 private final Instantiator instantiator; 051 private final List<Property> properties; 052 private final Method writeObjectMethod; 053 private final Method readObjectMethod; 054 private final Method writeReplaceMethod; 055 private final Method readResolveMethod; 056 057 private final ClassDescriptor parent; 058 059 private volatile SoftReference<List<Property>> inheritedProperties = new SoftReference<List<Property>>(null); 060 061 public ClassDescriptor(Reflection reflection, Class<?> cls) { 062 this.cls = cls; 063 064 this.instantiator = getInstantiator(reflection, cls); 065 066 if (!Externalizable.class.isAssignableFrom(cls)) { 067 this.writeObjectMethod = getPrivateMethod(cls, "writeObject", WRITE_OBJECT_PARAMS, Void.TYPE); 068 this.readObjectMethod = getPrivateMethod(cls, "readObject", READ_OBJECT_PARAMS, Void.TYPE); 069 } 070 else { 071 this.writeObjectMethod = null; 072 this.readObjectMethod = null; 073 } 074 075 this.writeReplaceMethod = getInheritedMethod(cls, "writeReplace", null, Object.class); 076 this.readResolveMethod = getInheritedMethod(cls, "readResolve", null, Object.class); 077 078 this.properties = getSerializableProperties(reflection, cls); 079 080 this.parent = reflection.getDescriptor(cls.getSuperclass()); 081 } 082 083 private static Instantiator getInstantiator(Reflection reflection, Class<?> cls) { 084 try { 085 return new ConstructorInstantiator(cls.getConstructor()); 086 } 087 catch (NoSuchMethodException e) { 088 // Fallback... 089 } 090 091 try { 092 return reflection.instanceFactory.newInstantiator(cls); 093 } 094 catch (IllegalAccessException e) { 095 throw new RuntimeException(e); 096 } 097 catch (InvocationTargetException e) { 098 throw new RuntimeException(e); 099 } 100 } 101 102 public Class<?> getCls() { 103 return cls; 104 } 105 106 public Object newInstance() throws InstantiationException, IllegalAccessException, InvocationTargetException { 107 return instantiator.newInstance(); 108 } 109 110 public List<Property> getSerializableProperties() { 111 return properties; 112 } 113 114 public List<Property> getInheritedSerializableProperties() { 115 List<Property> inheritedSerializableProperties = inheritedProperties.get(); 116 if (inheritedSerializableProperties == null) { 117 if (parent == null) 118 inheritedSerializableProperties = properties; 119 else { 120 List<Property> parentProperties = parent.getInheritedSerializableProperties(); 121 if (!parentProperties.isEmpty()) { 122 if (!properties.isEmpty()) { 123 inheritedSerializableProperties = new ArrayList<Property>(parentProperties.size() + properties.size()); 124 inheritedSerializableProperties.addAll(parentProperties); 125 inheritedSerializableProperties.addAll(properties); 126 inheritedSerializableProperties = Collections.unmodifiableList(inheritedSerializableProperties); 127 } 128 else 129 inheritedSerializableProperties = parentProperties; 130 } 131 else 132 inheritedSerializableProperties = properties; 133 } 134 inheritedProperties = new SoftReference<List<Property>>(inheritedSerializableProperties); 135 } 136 return inheritedSerializableProperties; 137 } 138 139 public boolean hasWriteObjectMethod() { 140 return writeObjectMethod != null; 141 } 142 143 public void invokeWriteObjectMethod(ObjectOutputStream oos, Object v) throws IOException { 144 if (writeObjectMethod == null) 145 throw new UnsupportedOperationException("No writeObject(...) method in " + cls); 146 147 try { 148 writeObjectMethod.invoke(v, oos); 149 } 150 catch (InvocationTargetException e) { 151 Throwable t = e.getTargetException(); 152 if (t instanceof IOException) 153 throw (IOException)t; 154 throw new IOException(t); 155 } 156 catch (IllegalAccessException e) { 157 throw new InternalError(); 158 } 159 } 160 161 public boolean hasReadObjectMethod() { 162 return readObjectMethod != null; 163 } 164 165 public void invokeReadObjectMethod(ObjectInputStream ois, Object v) throws ClassNotFoundException, IOException { 166 if (readObjectMethod == null) 167 throw new UnsupportedOperationException("No readObject(...) method in " + cls); 168 169 try { 170 readObjectMethod.invoke(v, ois); 171 } 172 catch (InvocationTargetException e) { 173 Throwable t = e.getTargetException(); 174 if (t instanceof ClassNotFoundException) 175 throw (ClassNotFoundException)t; 176 if (t instanceof IOException) 177 throw (IOException)t; 178 throw new IOException(t); 179 } 180 catch (IllegalAccessException e) { 181 throw new InternalError(); 182 } 183 } 184 185 public boolean hasWriteReplaceMethod() { 186 return writeReplaceMethod != null; 187 } 188 189 public Object invokeWriteReplaceMethod(Object v) throws IOException { 190 if (writeReplaceMethod == null) 191 throw new UnsupportedOperationException("No writeReplace() method in " + cls); 192 193 try { 194 return writeReplaceMethod.invoke(v); 195 } 196 catch (InvocationTargetException e) { 197 Throwable t = e.getTargetException(); 198 if (t instanceof IOException) 199 throw (IOException)t; 200 throw new IOException(t); 201 } 202 catch (IllegalAccessException e) { 203 throw new InternalError(); 204 } 205 } 206 207 public boolean hasReadResolveMethod() { 208 return readResolveMethod != null; 209 } 210 211 public Object invokeReadResolveMethod(Object v) throws IOException { 212 if (readResolveMethod == null) 213 throw new UnsupportedOperationException("No readResolve() method in " + cls); 214 215 try { 216 return readResolveMethod.invoke(v); 217 } 218 catch (InvocationTargetException e) { 219 Throwable t = e.getTargetException(); 220 if (t instanceof IOException) 221 throw (IOException)t; 222 throw new IOException(t); 223 } 224 catch (IllegalAccessException e) { 225 throw new InternalError(); 226 } 227 } 228 229 public ClassDescriptor getParent() { 230 return parent; 231 } 232 233 protected static Method getInheritedMethod(Class<?> cls, String name, Class<?>[] params, Class<?> ret) { 234 try { 235 Method method = cls.getDeclaredMethod(name, params); 236 method.setAccessible(true); 237 return ( 238 method.getReturnType() == ret && ((Modifier.STATIC | Modifier.ABSTRACT) & method.getModifiers()) == 0 ? 239 method : 240 null 241 ); 242 } 243 catch (NoSuchMethodException e) { 244 } 245 246 final Class<?> root = cls; 247 while ((cls = cls.getSuperclass()) != null) { 248 try { 249 Method method = cls.getDeclaredMethod(name, params); 250 method.setAccessible(true); 251 252 if (method.getReturnType() != ret) 253 return null; 254 255 final int modifiers = method.getModifiers(); 256 257 if (((Modifier.STATIC | Modifier.ABSTRACT | Modifier.PRIVATE) & modifiers) != 0) 258 return null; 259 260 if (((Modifier.PUBLIC | Modifier.PROTECTED) & modifiers) != 0) 261 return method; 262 263 return root.getPackage() == cls.getPackage() ? method : null; 264 } 265 catch (NoSuchMethodException e) { 266 } 267 } 268 269 return null; 270 } 271 272 protected static Method getPrivateMethod(Class<?> cls, String name, Class<?>[] params, Class<?> ret) { 273 try { 274 Method method = cls.getDeclaredMethod(name, params); 275 method.setAccessible(true); 276 if (method.getReturnType() == ret && (method.getModifiers() & (Modifier.STATIC | Modifier.PRIVATE)) == Modifier.PRIVATE) 277 return method; 278 } 279 catch (NoSuchMethodException e) { 280 } 281 return null; 282 } 283 284 protected static List<Property> getSerializableProperties(Reflection reflection, Class<?> cls) { 285 Field[] declaredFields = cls.getDeclaredFields(); 286 287 List<Property> serializableProperties = new ArrayList<Property>(declaredFields.length); 288 for (Field field : declaredFields) { 289 if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) == 0 && 290 !field.isAnnotationPresent(Exclude.class)) { 291 field.setAccessible(true); 292 serializableProperties.add(reflection.newFieldProperty(field)); 293 } 294 } 295 296 Method[] declaredMethods = cls.getDeclaredMethods(); 297 for (Method method : declaredMethods) { 298 if ((method.getModifiers() & (Modifier.STATIC | Modifier.PRIVATE | Modifier.PROTECTED)) == 0 && 299 method.getReturnType() != Void.TYPE && 300 method.isAnnotationPresent(Include.class) && 301 method.getParameterTypes().length == 0) { 302 303 String name = method.getName(); 304 if (name.startsWith("get")) { 305 if (name.length() <= 3) 306 continue; 307 name = name.substring(3, 4).toLowerCase() + name.substring(4); 308 } 309 else if (name.startsWith("is") && 310 (method.getReturnType() == Boolean.class || method.getReturnType() == Boolean.TYPE)) { 311 if (name.length() <= 2) 312 continue; 313 name = name.substring(2, 3).toLowerCase() + name.substring(3); 314 } 315 else 316 continue; 317 318 serializableProperties.add(reflection.newMethodProperty(method, null, name)); 319 } 320 } 321 322 Serialized serialized = cls.getAnnotation(Serialized.class); 323 if (serialized != null && serialized.propertiesOrder().length > 0) { 324 String[] value = serialized.propertiesOrder(); 325 326 if (value.length != serializableProperties.size()) 327 throw new ReflectionException("Illegal @Serialized(propertiesOrder) value: " + serialized + " on: " + cls.getName() + " (bad length)"); 328 329 for (int i = 0; i < value.length; i++) { 330 String propertyName = value[i]; 331 332 boolean found = false; 333 for (int j = i; j < value.length; j++) { 334 Property property = serializableProperties.get(j); 335 if (property.getName().equals(propertyName)) { 336 found = true; 337 if (i != j) { 338 serializableProperties.set(j, serializableProperties.get(i)); 339 serializableProperties.set(i, property); 340 } 341 break; 342 } 343 } 344 if (!found) 345 throw new ReflectionException("Illegal @Serialized(propertiesOrder) value: " + serialized + " on: " + cls.getName() + " (\"" + propertyName + "\" isn't a property name)"); 346 } 347 } 348 else 349 Collections.sort(serializableProperties, reflection.getLexicalPropertyComparator()); 350 351 return Collections.unmodifiableList(serializableProperties); 352 } 353}