/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.util.fudgemsg; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.opengamma.OpenGammaRuntimeException; /** * Helper methods for using the {@code writeReplace} method defined by Java serialization to allow an object to nominate another for encoding or serialization. For example, inner classes may implement * a {@code writeReplace} that allows them to be represented using an alternative strategy. */ public final class WriteReplaceHelper { private static final Object NO_WRITE_REPLACE = new Object(); private static final Object BAD_ANONYMOUS_CLASS = new Object(); /** * The cache of previously resolved (and forced accessible) {@code writeReplace} methods. */ private static final ConcurrentMap<Class<?>, Object> s_writeReplace = new ConcurrentHashMap<Class<?>, Object>(); private WriteReplaceHelper() { } private static Method getWriteReplace(final Class<?> clazz) { Object method = s_writeReplace.get(clazz); if (method == null) { method = AccessController.doPrivileged(new PrivilegedAction<Method>() { @Override public Method run() { try { final Method mtd = clazz.getMethod("writeReplace"); mtd.setAccessible(true); return mtd; } catch (final NoSuchMethodException e) { // Ignore } return null; } }); if (method == null) { if (clazz.isAnonymousClass()) { s_writeReplace.putIfAbsent(clazz, BAD_ANONYMOUS_CLASS); throw new OpenGammaRuntimeException("No serialization substitution available for anonymous inner class object " + clazz); } else { s_writeReplace.putIfAbsent(clazz, NO_WRITE_REPLACE); return null; } } else { s_writeReplace.putIfAbsent(clazz, method); return (Method) method; } } else { if (method == NO_WRITE_REPLACE) { return null; } else if (method == BAD_ANONYMOUS_CLASS) { throw new OpenGammaRuntimeException("No serialization substitution available for anonymous inner class object " + clazz); } else { return (Method) method; } } } /** * Replaces an class with a serializable substitution based on its {@code writeReplace} method. * * @param object the object to substitute, not null * @return the substitution object as returned by its {@code writeReplace} method or the original object if there is no write replace * @throws OpenGammaRuntimeException if the class is an inner class and does not defined a {@code writeReplace} */ public static Object writeReplace(final Object object) { final Class<?> clazz = object.getClass(); final Method method = getWriteReplace(clazz); if (method == null) { return object; } try { return method.invoke(object); } catch (final Exception e) { throw new OpenGammaRuntimeException("Couldn't call writeReplace on object", e); } } }