package org.infinispan.commons.marshall; import java.util.concurrent.ConcurrentMap; import org.infinispan.commons.logging.Log; import org.infinispan.commons.logging.LogFactory; import org.infinispan.commons.util.CollectionFactory; /** * Class providing hints about marshallable types, such as whether a particular * type is marshallable or not, or an accurate approach to the serialized * size of a particular type. * * @author Galder ZamarreƱo * @since 5.1 */ public final class MarshallableTypeHints { private static final Log log = LogFactory.getLog(MarshallableTypeHints.class); private static final boolean trace = log.isTraceEnabled(); /** * Cache of classes that are considered to be marshallable alongside their * buffer size predictor. Since checking whether a type is marshallable * requires attempting to marshalling them, a cache for the types that are * known to be marshallable or not is advantageous. */ private final ConcurrentMap<Class<?>, MarshallingType> typeHints = CollectionFactory.makeConcurrentMap(); /** * Get the serialized form size predictor for a particular type. * * @param type Marshallable type for which serialized form size will be predicted * @return an instance of {@link BufferSizePredictor} */ public BufferSizePredictor getBufferSizePredictor(Class<?> type) { MarshallingType marshallingType = typeHints.get(type); if (marshallingType == null) { // Initialise with isMarshallable to null, meaning it's unknown marshallingType = new MarshallingType(null, new AdaptiveBufferSizePredictor()); MarshallingType prev = typeHints.putIfAbsent(type, marshallingType); if (prev != null) { marshallingType = prev; } else { if (trace) { log.tracef("Cache a buffer size predictor for '%s' assuming " + "its serializability is unknown", type.getName()); } } } return marshallingType.sizePredictor; } public BufferSizePredictor getBufferSizePredictor(Object obj) { return obj == null ? NullBufferSizePredictor.INSTANCE : getBufferSizePredictor(obj.getClass()); } /** * Returns whether the hint on whether a particular type is marshallable or * not is available. This method can be used to avoid attempting to marshall * a type, if the hints for the type have already been calculated. * * @param type Marshallable type to check whether an attempt to mark it as * marshallable has been made. * @return true if the type has been marked as marshallable at all, false * if no attempt has been made to mark the type as marshallable. */ public boolean isKnownMarshallable(Class<?> type) { MarshallingType marshallingType = typeHints.get(type); return marshallingType != null && marshallingType.isMarshallable != null; } /** * Returns whether a type can be serialized. In order for a type to be * considered marshallable, the type must have been marked as marshallable * using the {@link #markMarshallable(Class, boolean)} method earlier, * passing true as parameter. If a type has not yet been marked as * marshallable, this method will return false. */ public boolean isMarshallable(Class<?> type) { MarshallingType marshallingType = typeHints.get(type); if (marshallingType != null) { Boolean marshallable = marshallingType.isMarshallable; return marshallable != null ? marshallable.booleanValue() : false; } return false; } /** * Marks a particular type as being marshallable or not being not marshallable. * * @param type Class to mark as serializable or non-serializable * @param isMarshallable Whether the type can be marshalled or not. */ public void markMarshallable(Class<?> type, boolean isMarshallable) { MarshallingType marshallType = typeHints.get(type); if (marshallableUpdateRequired(isMarshallable, marshallType)) { boolean replaced = typeHints.replace(type, marshallType, new MarshallingType( Boolean.valueOf(isMarshallable), marshallType.sizePredictor)); if (replaced && trace) { log.tracef("Replacing '%s' type to be marshallable=%b", type.getName(), isMarshallable); } } else if (marshallType == null) { if (trace) { log.tracef("Cache '%s' type to be marshallable=%b", type.getName(), isMarshallable); } typeHints.put(type, new MarshallingType( Boolean.valueOf(isMarshallable), new AdaptiveBufferSizePredictor())); } } /** * Clear the cached marshallable type hints. */ public void clear() { typeHints.clear(); } private boolean marshallableUpdateRequired(boolean isMarshallable, MarshallingType marshallType) { return marshallType != null && (marshallType.isMarshallable == null || marshallType.isMarshallable.booleanValue() != isMarshallable); } private static class MarshallingType { final Boolean isMarshallable; final BufferSizePredictor sizePredictor; private MarshallingType(Boolean marshallable, BufferSizePredictor sizePredictor) { isMarshallable = marshallable; this.sizePredictor = sizePredictor; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MarshallingType that = (MarshallingType) o; if (isMarshallable != null ? !isMarshallable.equals(that.isMarshallable) : that.isMarshallable != null) return false; if (sizePredictor != null ? !sizePredictor.equals(that.sizePredictor) : that.sizePredictor != null) return false; return true; } @Override public int hashCode() { int result = isMarshallable != null ? isMarshallable.hashCode() : 0; result = 31 * result + (sizePredictor != null ? sizePredictor.hashCode() : 0); return result; } } final static class NullBufferSizePredictor implements BufferSizePredictor { static final BufferSizePredictor INSTANCE = new NullBufferSizePredictor(); @Override public int nextSize(Object obj) { return 1; } @Override public void recordSize(int previousSize) { // No-op } } }