/** * @param pkgName Package name. * @return Class names. */ @SuppressWarnings("ConstantConditions") private static Iterable<String> classesInPackage(String pkgName) { assert pkgName != null; Collection<String> clsNames = new ArrayList<>(); ClassLoader ldr = U.gridClassLoader(); if (ldr instanceof URLClassLoader) { String pkgPath = pkgName.replaceAll("\\.", "/"); URL[] urls = ((URLClassLoader) ldr).getURLs(); for (URL url : urls) { String proto = url.getProtocol().toLowerCase(); if ("file".equals(proto)) { try { File cpElement = new File(url.toURI()); if (cpElement.isDirectory()) { File pkgDir = new File(cpElement, pkgPath); if (pkgDir.isDirectory()) { for (File file : pkgDir.listFiles()) { String fileName = file.getName(); if (file.isFile() && fileName.toLowerCase().endsWith(".class")) clsNames.add(pkgName + '.' + fileName.substring(0, fileName.length() - 6)); } } } else if (cpElement.isFile()) { try { JarFile jar = new JarFile(cpElement); Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { String entry = entries.nextElement().getName(); if (entry.startsWith(pkgPath) && entry.endsWith(".class")) { String clsName = entry.substring(pkgPath.length() + 1, entry.length() - 6); if (!clsName.contains("/") && !clsName.contains("\\")) clsNames.add(pkgName + '.' + clsName); } } } catch (IOException ignored) { // No-op. } } } catch (URISyntaxException ignored) { // No-op. } } } } return clsNames; }
/** Binary context. */ public class BinaryContext { /** */ private static final ClassLoader dfltLdr = U.gridClassLoader(); /** */ private final ConcurrentMap<Class<?>, BinaryClassDescriptor> descByCls = new ConcurrentHashMap8<>(); /** Holds classes loaded by default class loader only. */ private final ConcurrentMap<Integer, BinaryClassDescriptor> userTypes = new ConcurrentHashMap8<>(); /** */ private final Map<Integer, BinaryClassDescriptor> predefinedTypes = new HashMap<>(); /** */ private final Map<String, Integer> predefinedTypeNames = new HashMap<>(); /** */ private final Map<Class<? extends Collection>, Byte> colTypes = new HashMap<>(); /** */ private final Map<Class<? extends Map>, Byte> mapTypes = new HashMap<>(); /** */ private final ConcurrentMap<Integer, BinaryIdMapper> mappers = new ConcurrentHashMap8<>(0); /** Affinity key field names. */ private final ConcurrentMap<Integer, String> affKeyFieldNames = new ConcurrentHashMap8<>(0); /** */ private final Map<String, BinaryIdMapper> typeMappers = new ConcurrentHashMap8<>(0); /** */ private BinaryMetadataHandler metaHnd; /** Actual marshaller. */ private BinaryMarshaller marsh; /** */ private MarshallerContext marshCtx; /** */ private IgniteConfiguration igniteCfg; /** Logger. */ private IgniteLogger log; /** */ private final OptimizedMarshaller optmMarsh = new OptimizedMarshaller(false); /** Compact footer flag. */ private boolean compactFooter; /** Object schemas. */ private volatile Map<Integer, BinarySchemaRegistry> schemas; /** For {@link Externalizable}. */ public BinaryContext() { // No-op. } /** * @param metaHnd Meta data handler. * @param igniteCfg Ignite configuration. * @param log Logger. */ public BinaryContext( BinaryMetadataHandler metaHnd, IgniteConfiguration igniteCfg, IgniteLogger log) { assert metaHnd != null; assert igniteCfg != null; this.metaHnd = metaHnd; this.igniteCfg = igniteCfg; this.log = log; colTypes.put(ArrayList.class, GridBinaryMarshaller.ARR_LIST); colTypes.put(LinkedList.class, GridBinaryMarshaller.LINKED_LIST); colTypes.put(HashSet.class, GridBinaryMarshaller.HASH_SET); colTypes.put(LinkedHashSet.class, GridBinaryMarshaller.LINKED_HASH_SET); mapTypes.put(HashMap.class, GridBinaryMarshaller.HASH_MAP); mapTypes.put(LinkedHashMap.class, GridBinaryMarshaller.LINKED_HASH_MAP); // IDs range from [0..200] is used by Java SDK API and GridGain legacy API registerPredefinedType(Byte.class, GridBinaryMarshaller.BYTE); registerPredefinedType(Boolean.class, GridBinaryMarshaller.BOOLEAN); registerPredefinedType(Short.class, GridBinaryMarshaller.SHORT); registerPredefinedType(Character.class, GridBinaryMarshaller.CHAR); registerPredefinedType(Integer.class, GridBinaryMarshaller.INT); registerPredefinedType(Long.class, GridBinaryMarshaller.LONG); registerPredefinedType(Float.class, GridBinaryMarshaller.FLOAT); registerPredefinedType(Double.class, GridBinaryMarshaller.DOUBLE); registerPredefinedType(String.class, GridBinaryMarshaller.STRING); registerPredefinedType(BigDecimal.class, GridBinaryMarshaller.DECIMAL); registerPredefinedType(Date.class, GridBinaryMarshaller.DATE); registerPredefinedType(Timestamp.class, GridBinaryMarshaller.TIMESTAMP); registerPredefinedType(UUID.class, GridBinaryMarshaller.UUID); registerPredefinedType(byte[].class, GridBinaryMarshaller.BYTE_ARR); registerPredefinedType(short[].class, GridBinaryMarshaller.SHORT_ARR); registerPredefinedType(int[].class, GridBinaryMarshaller.INT_ARR); registerPredefinedType(long[].class, GridBinaryMarshaller.LONG_ARR); registerPredefinedType(float[].class, GridBinaryMarshaller.FLOAT_ARR); registerPredefinedType(double[].class, GridBinaryMarshaller.DOUBLE_ARR); registerPredefinedType(char[].class, GridBinaryMarshaller.CHAR_ARR); registerPredefinedType(boolean[].class, GridBinaryMarshaller.BOOLEAN_ARR); registerPredefinedType(BigDecimal[].class, GridBinaryMarshaller.DECIMAL_ARR); registerPredefinedType(String[].class, GridBinaryMarshaller.STRING_ARR); registerPredefinedType(UUID[].class, GridBinaryMarshaller.UUID_ARR); registerPredefinedType(Date[].class, GridBinaryMarshaller.DATE_ARR); registerPredefinedType(Timestamp[].class, GridBinaryMarshaller.TIMESTAMP_ARR); registerPredefinedType(Object[].class, GridBinaryMarshaller.OBJ_ARR); // Special collections. registerPredefinedType(ArrayList.class, 0); registerPredefinedType(LinkedList.class, 0); registerPredefinedType(HashSet.class, 0); registerPredefinedType(LinkedHashSet.class, 0); registerPredefinedType(HashMap.class, 0); registerPredefinedType(LinkedHashMap.class, 0); // Classes with overriden default serialization flag. registerPredefinedType(AffinityKey.class, 0, affinityFieldName(AffinityKey.class)); registerPredefinedType(GridMapEntry.class, 60); registerPredefinedType(IgniteBiTuple.class, 61); registerPredefinedType(T2.class, 62); registerPredefinedType(BinaryObjectImpl.class, 0); registerPredefinedType(BinaryObjectOffheapImpl.class, 0); registerPredefinedType(BinaryMetadataKey.class, 0); registerPredefinedType(BinaryMetadata.class, 0); // IDs range [200..1000] is used by Ignite internal APIs. } /** @return Logger. */ public IgniteLogger log() { return log; } /** @return Marshaller. */ public BinaryMarshaller marshaller() { return marsh; } /** * Check whether class must be deserialized anyway. * * @param cls Class. * @return {@code True} if must be deserialized. */ public boolean mustDeserialize(Class cls) { BinaryClassDescriptor desc = descByCls.get(cls); if (desc == null) return marshCtx.isSystemType(cls.getName()) || serializerForClass(cls) == null; else return desc.useOptimizedMarshaller(); } /** @return Ignite configuration. */ public IgniteConfiguration configuration() { return igniteCfg; } /** * @param marsh Binary marshaller. * @param cfg Configuration. * @throws BinaryObjectException In case of error. */ public void configure(BinaryMarshaller marsh, IgniteConfiguration cfg) throws BinaryObjectException { if (marsh == null) return; this.marsh = marsh; marshCtx = marsh.getContext(); BinaryConfiguration binaryCfg = cfg.getBinaryConfiguration(); if (binaryCfg == null) binaryCfg = new BinaryConfiguration(); assert marshCtx != null; optmMarsh.setContext(marshCtx); configure( binaryCfg.getIdMapper(), binaryCfg.getSerializer(), binaryCfg.getTypeConfigurations()); compactFooter = binaryCfg.isCompactFooter(); } /** * @param globalIdMapper ID mapper. * @param globalSerializer Serializer. * @param typeCfgs Type configurations. * @throws BinaryObjectException In case of error. */ private void configure( BinaryIdMapper globalIdMapper, BinarySerializer globalSerializer, Collection<BinaryTypeConfiguration> typeCfgs) throws BinaryObjectException { TypeDescriptors descs = new TypeDescriptors(); Map<String, String> affFields = new HashMap<>(); if (!F.isEmpty(igniteCfg.getCacheKeyConfiguration())) { for (CacheKeyConfiguration keyCfg : igniteCfg.getCacheKeyConfiguration()) affFields.put(keyCfg.getTypeName(), keyCfg.getAffinityKeyFieldName()); } if (typeCfgs != null) { for (BinaryTypeConfiguration typeCfg : typeCfgs) { String clsName = typeCfg.getTypeName(); if (clsName == null) throw new BinaryObjectException("Class name is required for binary type configuration."); BinaryIdMapper idMapper = globalIdMapper; if (typeCfg.getIdMapper() != null) idMapper = typeCfg.getIdMapper(); idMapper = BinaryInternalIdMapper.create(idMapper); BinarySerializer serializer = globalSerializer; if (typeCfg.getSerializer() != null) serializer = typeCfg.getSerializer(); if (clsName.endsWith(".*")) { String pkgName = clsName.substring(0, clsName.length() - 2); for (String clsName0 : classesInPackage(pkgName)) descs.add( clsName0, idMapper, serializer, affFields.get(clsName0), typeCfg.isEnum(), true); } else descs.add(clsName, idMapper, serializer, affFields.get(clsName), typeCfg.isEnum(), false); } } for (TypeDescriptor desc : descs.descriptors()) registerUserType( desc.clsName, desc.idMapper, desc.serializer, desc.affKeyFieldName, desc.isEnum); BinaryInternalIdMapper dfltMapper = BinaryInternalIdMapper.create(globalIdMapper); // Put affinity field names for unconfigured types. for (Map.Entry<String, String> entry : affFields.entrySet()) { String typeName = entry.getKey(); int typeId = dfltMapper.typeId(typeName); affKeyFieldNames.putIfAbsent(typeId, entry.getValue()); } addSystemClassAffinityKey(CollocatedSetItemKey.class); addSystemClassAffinityKey(CollocatedQueueItemKey.class); } /** @param cls Class. */ private void addSystemClassAffinityKey(Class<?> cls) { String fieldName = affinityFieldName(cls); assert fieldName != null : cls; affKeyFieldNames.putIfAbsent(cls.getName().hashCode(), affinityFieldName(cls)); } /** * @param pkgName Package name. * @return Class names. */ @SuppressWarnings("ConstantConditions") private static Iterable<String> classesInPackage(String pkgName) { assert pkgName != null; Collection<String> clsNames = new ArrayList<>(); ClassLoader ldr = U.gridClassLoader(); if (ldr instanceof URLClassLoader) { String pkgPath = pkgName.replaceAll("\\.", "/"); URL[] urls = ((URLClassLoader) ldr).getURLs(); for (URL url : urls) { String proto = url.getProtocol().toLowerCase(); if ("file".equals(proto)) { try { File cpElement = new File(url.toURI()); if (cpElement.isDirectory()) { File pkgDir = new File(cpElement, pkgPath); if (pkgDir.isDirectory()) { for (File file : pkgDir.listFiles()) { String fileName = file.getName(); if (file.isFile() && fileName.toLowerCase().endsWith(".class")) clsNames.add(pkgName + '.' + fileName.substring(0, fileName.length() - 6)); } } } else if (cpElement.isFile()) { try { JarFile jar = new JarFile(cpElement); Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { String entry = entries.nextElement().getName(); if (entry.startsWith(pkgPath) && entry.endsWith(".class")) { String clsName = entry.substring(pkgPath.length() + 1, entry.length() - 6); if (!clsName.contains("/") && !clsName.contains("\\")) clsNames.add(pkgName + '.' + clsName); } } } catch (IOException ignored) { // No-op. } } } catch (URISyntaxException ignored) { // No-op. } } } } return clsNames; } /** * @param cls Class. * @return Class descriptor. * @throws BinaryObjectException In case of error. */ public BinaryClassDescriptor descriptorForClass(Class<?> cls, boolean deserialize) throws BinaryObjectException { assert cls != null; BinaryClassDescriptor desc = descByCls.get(cls); if (desc == null || !desc.registered()) desc = registerClassDescriptor(cls, deserialize); return desc; } /** * @param userType User type or not. * @param typeId Type ID. * @param ldr Class loader. * @return Class descriptor. */ public BinaryClassDescriptor descriptorForTypeId( boolean userType, int typeId, ClassLoader ldr, boolean deserialize) { assert typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID; // TODO: As a workaround for IGNITE-1358 we always check the predefined map before without // checking 'userType' BinaryClassDescriptor desc = predefinedTypes.get(typeId); if (desc != null) return desc; if (ldr == null) ldr = dfltLdr; // If the type hasn't been loaded by default class loader then we mustn't return the descriptor // from here // giving a chance to a custom class loader to reload type's class. if (userType && ldr.equals(dfltLdr)) { desc = userTypes.get(typeId); if (desc != null) return desc; } Class cls; try { cls = marshCtx.getClass(typeId, ldr); desc = descByCls.get(cls); } catch (ClassNotFoundException e) { // Class might have been loaded by default class loader. if (userType && !ldr.equals(dfltLdr) && (desc = descriptorForTypeId(true, typeId, dfltLdr, deserialize)) != null) return desc; throw new BinaryInvalidTypeException(e); } catch (IgniteCheckedException e) { // Class might have been loaded by default class loader. if (userType && !ldr.equals(dfltLdr) && (desc = descriptorForTypeId(true, typeId, dfltLdr, deserialize)) != null) return desc; throw new BinaryObjectException("Failed resolve class for ID: " + typeId, e); } if (desc == null) { desc = registerClassDescriptor(cls, deserialize); assert desc.typeId() == typeId; } return desc; } /** * Creates and registers {@link BinaryClassDescriptor} for the given {@code class}. * * @param cls Class. * @return Class descriptor. */ private BinaryClassDescriptor registerClassDescriptor(Class<?> cls, boolean deserialize) { BinaryClassDescriptor desc; String clsName = cls.getName(); if (marshCtx.isSystemType(clsName)) { desc = new BinaryClassDescriptor( this, cls, false, clsName.hashCode(), clsName, null, BinaryInternalIdMapper.defaultInstance(), null, false, true /* registered */); BinaryClassDescriptor old = descByCls.putIfAbsent(cls, desc); if (old != null) desc = old; } else desc = registerUserClassDescriptor(cls, deserialize); return desc; } /** * Creates and registers {@link BinaryClassDescriptor} for the given user {@code class}. * * @param cls Class. * @return Class descriptor. */ private BinaryClassDescriptor registerUserClassDescriptor(Class<?> cls, boolean deserialize) { boolean registered; String typeName = typeName(cls.getName()); BinaryIdMapper idMapper = userTypeIdMapper(typeName); int typeId = idMapper.typeId(typeName); try { registered = marshCtx.registerClass(typeId, cls); } catch (IgniteCheckedException e) { throw new BinaryObjectException("Failed to register class.", e); } BinarySerializer serializer = serializerForClass(cls); String affFieldName = affinityFieldName(cls); BinaryClassDescriptor desc = new BinaryClassDescriptor( this, cls, true, typeId, typeName, affFieldName, idMapper, serializer, true, registered); if (!deserialize) { Collection<BinarySchema> schemas = desc.schema() != null ? Collections.singleton(desc.schema()) : null; metaHnd.addMeta( typeId, new BinaryMetadata( typeId, typeName, desc.fieldsMeta(), affFieldName, schemas, desc.isEnum()) .wrap(this)); } // perform put() instead of putIfAbsent() because "registered" flag might have been changed or // class loader // might have reloaded described class. if (IgniteUtils.detectClassLoader(cls).equals(dfltLdr)) userTypes.put(typeId, desc); descByCls.put(cls, desc); mappers.putIfAbsent(typeId, idMapper); return desc; } /** * Get serializer for class taking in count default one. * * @param cls Class. * @return Serializer for class or {@code null} if none exists. */ private @Nullable BinarySerializer serializerForClass(Class cls) { BinarySerializer serializer = defaultSerializer(); if (serializer == null && canUseReflectiveSerializer(cls)) serializer = new BinaryReflectiveSerializer(); return serializer; } /** @return Default serializer. */ private BinarySerializer defaultSerializer() { BinaryConfiguration binCfg = igniteCfg.getBinaryConfiguration(); return binCfg != null ? binCfg.getSerializer() : null; } /** * @param cls Collection class. * @return Collection type ID. */ public byte collectionType(Class<? extends Collection> cls) { assert cls != null; Byte type = colTypes.get(cls); if (type != null) return type; return Set.class.isAssignableFrom(cls) ? GridBinaryMarshaller.USER_SET : GridBinaryMarshaller.USER_COL; } /** * @param cls Map class. * @return Map type ID. */ public byte mapType(Class<? extends Map> cls) { assert cls != null; Byte type = mapTypes.get(cls); return type != null ? type : GridBinaryMarshaller.USER_COL; } /** * @param typeName Type name. * @return Type ID. */ public int typeId(String typeName) { String typeName0 = typeName(typeName); Integer id = predefinedTypeNames.get(typeName0); if (id != null) return id; if (marshCtx.isSystemType(typeName)) return typeName.hashCode(); return userTypeIdMapper(typeName0).typeId(typeName0); } /** * @param typeId Type ID. * @param fieldName Field name. * @return Field ID. */ public int fieldId(int typeId, String fieldName) { return userTypeIdMapper(typeId).fieldId(typeId, fieldName); } /** * @param typeId Type ID. * @return Instance of ID mapper. */ public BinaryIdMapper userTypeIdMapper(int typeId) { BinaryIdMapper idMapper = mappers.get(typeId); return idMapper != null ? idMapper : BinaryInternalIdMapper.defaultInstance(); } /** * @param typeName Type name. * @return Instance of ID mapper. */ private BinaryIdMapper userTypeIdMapper(String typeName) { BinaryIdMapper idMapper = typeMappers.get(typeName); return idMapper != null ? idMapper : BinaryInternalIdMapper.defaultInstance(); } /** * @param cls Class to get affinity field for. * @return Affinity field name or {@code null} if field name was not found. */ private String affinityFieldName(Class cls) { for (; cls != Object.class && cls != null; cls = cls.getSuperclass()) { for (Field f : cls.getDeclaredFields()) { if (f.getAnnotation(AffinityKeyMapped.class) != null) return f.getName(); } } return null; } /** * @param cls Class. * @param id Type ID. * @return GridBinaryClassDescriptor. */ public BinaryClassDescriptor registerPredefinedType(Class<?> cls, int id) { return registerPredefinedType(cls, id, null); } /** * @param cls Class. * @param id Type ID. * @return GridBinaryClassDescriptor. */ public BinaryClassDescriptor registerPredefinedType(Class<?> cls, int id, String affFieldName) { String typeName = typeName(cls.getName()); if (id == 0) id = BinaryInternalIdMapper.defaultInstance().typeId(typeName); BinaryClassDescriptor desc = new BinaryClassDescriptor( this, cls, false, id, typeName, affFieldName, BinaryInternalIdMapper.defaultInstance(), new BinaryReflectiveSerializer(), false, true /* registered */); predefinedTypeNames.put(typeName, id); predefinedTypes.put(id, desc); descByCls.put(cls, desc); if (affFieldName != null) affKeyFieldNames.putIfAbsent(id, affFieldName); return desc; } /** * @param clsName Class name. * @param idMapper ID mapper. * @param serializer Serializer. * @param affKeyFieldName Affinity key field name. * @param isEnum If enum. * @throws BinaryObjectException In case of error. */ @SuppressWarnings("ErrorNotRethrown") public void registerUserType( String clsName, BinaryIdMapper idMapper, @Nullable BinarySerializer serializer, @Nullable String affKeyFieldName, boolean isEnum) throws BinaryObjectException { assert idMapper != null; Class<?> cls = null; try { cls = Class.forName(clsName); } catch (ClassNotFoundException | NoClassDefFoundError ignored) { // No-op. } String typeName = typeName(clsName); int id = idMapper.typeId(typeName); // Workaround for IGNITE-1358 if (predefinedTypes.get(id) != null) throw new BinaryObjectException("Duplicate type ID [clsName=" + clsName + ", id=" + id + ']'); if (mappers.put(id, idMapper) != null) throw new BinaryObjectException("Duplicate type ID [clsName=" + clsName + ", id=" + id + ']'); if (affKeyFieldName != null) { if (affKeyFieldNames.put(id, affKeyFieldName) != null) throw new BinaryObjectException( "Duplicate type ID [clsName=" + clsName + ", id=" + id + ']'); } typeMappers.put(typeName, idMapper); Map<String, Integer> fieldsMeta = null; Collection<BinarySchema> schemas = null; if (cls != null) { if (serializer == null) { // At this point we must decide whether to rely on Java serialization mechanics or not. // If no serializer is provided, we examine the class and if it doesn't contain non-trivial // serialization logic we are safe to fallback to reflective binary serialization. if (canUseReflectiveSerializer(cls)) serializer = new BinaryReflectiveSerializer(); } BinaryClassDescriptor desc = new BinaryClassDescriptor( this, cls, true, id, typeName, affKeyFieldName, idMapper, serializer, true, true /* registered */); fieldsMeta = desc.fieldsMeta(); schemas = desc.schema() != null ? Collections.singleton(desc.schema()) : null; if (IgniteUtils.detectClassLoader(cls).equals(dfltLdr)) userTypes.put(id, desc); descByCls.put(cls, desc); } metaHnd.addMeta( id, new BinaryMetadata(id, typeName, fieldsMeta, affKeyFieldName, schemas, isEnum).wrap(this)); } /** * Check whether reflective serializer can be used for class. * * @param cls Class. * @return {@code True} if reflective serializer can be used. */ private static boolean canUseReflectiveSerializer(Class cls) { return BinaryUtils.isBinarylizable(cls) || !BinaryUtils.isCustomJavaSerialization(cls); } /** * Create binary field. * * @param typeId Type ID. * @param fieldName Field name. * @return Binary field. */ public BinaryFieldImpl createField(int typeId, String fieldName) { BinarySchemaRegistry schemaReg = schemaRegistry(typeId); int fieldId = userTypeIdMapper(typeId).fieldId(typeId, fieldName); return new BinaryFieldImpl(typeId, schemaReg, fieldName, fieldId); } /** * @param typeId Type ID. * @return Meta data. * @throws BinaryObjectException In case of error. */ @Nullable public BinaryType metadata(int typeId) throws BinaryObjectException { return metaHnd != null ? metaHnd.metadata(typeId) : null; } /** * @param typeId Type ID. * @return Affinity key field name. */ public String affinityKeyFieldName(int typeId) { return affKeyFieldNames.get(typeId); } /** * @param typeId Type ID. * @param meta Meta data. * @throws BinaryObjectException In case of error. */ public void updateMetadata(int typeId, BinaryMetadata meta) throws BinaryObjectException { metaHnd.addMeta(typeId, meta.wrap(this)); } /** @return Whether field IDs should be skipped in footer or not. */ public boolean isCompactFooter() { return compactFooter; } /** * Get schema registry for type ID. * * @param typeId Type ID. * @return Schema registry for type ID. */ public BinarySchemaRegistry schemaRegistry(int typeId) { Map<Integer, BinarySchemaRegistry> schemas0 = schemas; if (schemas0 == null) { synchronized (this) { schemas0 = schemas; if (schemas0 == null) { schemas0 = new HashMap<>(); BinarySchemaRegistry reg = new BinarySchemaRegistry(); schemas0.put(typeId, reg); schemas = schemas0; return reg; } } } BinarySchemaRegistry reg = schemas0.get(typeId); if (reg == null) { synchronized (this) { reg = schemas.get(typeId); if (reg == null) { reg = new BinarySchemaRegistry(); schemas0 = new HashMap<>(schemas); schemas0.put(typeId, reg); schemas = schemas0; } } } return reg; } /** * Returns instance of {@link OptimizedMarshaller}. * * @return Optimized marshaller. */ OptimizedMarshaller optimizedMarsh() { return optmMarsh; } /** * @param clsName Class name. * @return Type name. */ @SuppressWarnings("ResultOfMethodCallIgnored") public static String typeName(String clsName) { assert clsName != null; int idx = clsName.lastIndexOf('$'); if (idx == clsName.length() - 1) // This is a regular (not inner) class name that ends with '$'. Common use case for Scala // classes. idx = -1; else if (idx >= 0) { String typeName = clsName.substring(idx + 1); try { Integer.parseInt(typeName); // This is an anonymous class. Don't cut off enclosing class name for it. idx = -1; } catch (NumberFormatException ignore) { // This is a lambda class. if (clsName.indexOf("$$Lambda$") > 0) idx = -1; else return typeName; } } if (idx < 0) idx = clsName.lastIndexOf('.'); return idx >= 0 ? clsName.substring(idx + 1) : clsName; } /** * Undeployment callback invoked when class loader is being undeployed. * * <p>Some marshallers may want to clean their internal state that uses the undeployed class * loader somehow. * * @param ldr Class loader being undeployed. */ public void onUndeploy(ClassLoader ldr) { for (Class<?> cls : descByCls.keySet()) { if (ldr.equals(cls.getClassLoader())) descByCls.remove(cls); } U.clearClassCache(ldr); } /** Type descriptors. */ private static class TypeDescriptors { /** Descriptors map. */ private final Map<String, TypeDescriptor> descs = new LinkedHashMap<>(); /** * Add type descriptor. * * @param clsName Class name. * @param idMapper ID mapper. * @param serializer Serializer. * @param affKeyFieldName Affinity key field name. * @param isEnum Enum flag. * @param canOverride Whether this descriptor can be override. * @throws BinaryObjectException If failed. */ private void add( String clsName, BinaryIdMapper idMapper, BinarySerializer serializer, String affKeyFieldName, boolean isEnum, boolean canOverride) throws BinaryObjectException { TypeDescriptor desc = new TypeDescriptor(clsName, idMapper, serializer, affKeyFieldName, isEnum, canOverride); TypeDescriptor oldDesc = descs.get(clsName); if (oldDesc == null) descs.put(clsName, desc); else oldDesc.override(desc); } /** * Get all collected descriptors. * * @return Descriptors. */ private Iterable<TypeDescriptor> descriptors() { return descs.values(); } } /** Type descriptor. */ private static class TypeDescriptor { /** Class name. */ private final String clsName; /** ID mapper. */ private BinaryIdMapper idMapper; /** Serializer. */ private BinarySerializer serializer; /** Affinity key field name. */ private String affKeyFieldName; /** Enum flag. */ private boolean isEnum; /** Whether this descriptor can be override. */ private boolean canOverride; /** * Constructor. * * @param clsName Class name. * @param idMapper ID mapper. * @param serializer Serializer. * @param affKeyFieldName Affinity key field name. * @param isEnum Enum type. * @param canOverride Whether this descriptor can be override. */ private TypeDescriptor( String clsName, BinaryIdMapper idMapper, BinarySerializer serializer, String affKeyFieldName, boolean isEnum, boolean canOverride) { this.clsName = clsName; this.idMapper = idMapper; this.serializer = serializer; this.affKeyFieldName = affKeyFieldName; this.isEnum = isEnum; this.canOverride = canOverride; } /** * Override binary class descriptor. * * @param other Other descriptor. * @throws BinaryObjectException If failed. */ private void override(TypeDescriptor other) throws BinaryObjectException { assert clsName.equals(other.clsName); if (canOverride) { idMapper = other.idMapper; serializer = other.serializer; affKeyFieldName = other.affKeyFieldName; isEnum = other.isEnum; canOverride = other.canOverride; } else if (!other.canOverride) throw new BinaryObjectException( "Duplicate explicit class definition in configuration: " + clsName); } } /** Type id wrapper. */ static class Type { /** Type id */ private final int id; /** Whether the following type is registered in a cache or not */ private final boolean registered; /** * @param id Id. * @param registered Registered. */ public Type(int id, boolean registered) { this.id = id; this.registered = registered; } /** @return Type ID. */ public int id() { return id; } /** @return Registered flag value. */ public boolean registered() { return registered; } } }