diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentLocalStore.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentLocalStore.java index 420eea8..d095efb 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentLocalStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentLocalStore.java @@ -39,7 +39,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteUuid; -import org.apache.ignite.marshaller.optimized.OptimizedMarshaller; +import org.apache.ignite.marshaller.AbstractMarshaller; import org.apache.ignite.spi.IgniteSpiException; import org.apache.ignite.spi.deployment.DeploymentListener; import org.apache.ignite.spi.deployment.DeploymentResource; @@ -541,8 +541,8 @@ class GridDeploymentLocalStore extends GridDeploymentStoreAdapter { ctx.resource().onUndeployed(dep); // Clear optimized marshaller's cache. - if (ctx.config().getMarshaller() instanceof OptimizedMarshaller) - ((OptimizedMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); + if (ctx.config().getMarshaller() instanceof AbstractMarshaller) + ((AbstractMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); clearSerializationCaches(); @@ -572,4 +572,4 @@ class GridDeploymentLocalStore extends GridDeploymentStoreAdapter { undeploy(ldr); } } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerLoaderStore.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerLoaderStore.java index 6ca74eb..4ba308c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerLoaderStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerLoaderStore.java @@ -39,7 +39,7 @@ import org.apache.ignite.internal.util.GridClassLoaderCache; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteUuid; -import org.apache.ignite.marshaller.optimized.OptimizedMarshaller; +import org.apache.ignite.marshaller.AbstractMarshaller; import org.apache.ignite.spi.deployment.DeploymentSpi; import static org.apache.ignite.events.EventType.EVT_CLASS_DEPLOYED; @@ -511,8 +511,8 @@ public class GridDeploymentPerLoaderStore extends GridDeploymentStoreAdapter { ctx.cache().onUndeployed(ldr); // Clear optimized marshaller's cache. - if (ctx.config().getMarshaller() instanceof OptimizedMarshaller) - ((OptimizedMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); + if (ctx.config().getMarshaller() instanceof AbstractMarshaller) + ((AbstractMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); clearSerializationCaches(); @@ -527,4 +527,4 @@ public class GridDeploymentPerLoaderStore extends GridDeploymentStoreAdapter { return S.toString(IsolatedDeployment.class, this, super.toString()); } } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java index 6f9e968..fabbcb2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentPerVersionStore.java @@ -46,7 +46,7 @@ import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteUuid; -import org.apache.ignite.marshaller.optimized.OptimizedMarshaller; +import org.apache.ignite.marshaller.AbstractMarshaller; import org.apache.ignite.spi.deployment.DeploymentSpi; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; @@ -1281,8 +1281,8 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter { ctx.cache().onUndeployed(ldr); // Clear optimized marshaller's cache. - if (ctx.config().getMarshaller() instanceof OptimizedMarshaller) - ((OptimizedMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); + if (ctx.config().getMarshaller() instanceof AbstractMarshaller) + ((AbstractMarshaller)ctx.config().getMarshaller()).onUndeploy(ldr); clearSerializationCaches(); @@ -1297,4 +1297,4 @@ public class GridDeploymentPerVersionStore extends GridDeploymentStoreAdapter { return S.toString(SharedDeployment.class, this, "super", super.toString()); } } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java index 2ee96b7..d5a3c5d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java @@ -52,24 +52,25 @@ import java.util.jar.JarFile; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.IgnitionEx; +import org.apache.ignite.internal.portable.api.PortableException; +import org.apache.ignite.internal.portable.api.PortableIdMapper; +import org.apache.ignite.internal.portable.api.PortableInvalidClassException; +import org.apache.ignite.internal.portable.api.PortableMarshaller; +import org.apache.ignite.internal.portable.api.PortableMetadata; +import org.apache.ignite.internal.portable.api.PortableSerializer; +import org.apache.ignite.internal.portable.api.PortableTypeConfiguration; import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessorImpl; +import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetConfiguration; +import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetPortableConfiguration; +import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetPortableTypeConfiguration; import org.apache.ignite.internal.util.GridConcurrentHashSet; +import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.lang.GridMapEntry; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiTuple; import org.apache.ignite.marshaller.MarshallerContext; import org.apache.ignite.marshaller.optimized.OptimizedMarshaller; -import org.apache.ignite.internal.portable.api.PortableMarshaller; -import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetConfiguration; -import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetPortableConfiguration; -import org.apache.ignite.internal.processors.platform.dotnet.PlatformDotNetPortableTypeConfiguration; -import org.apache.ignite.internal.portable.api.PortableException; -import org.apache.ignite.internal.portable.api.PortableIdMapper; -import org.apache.ignite.internal.portable.api.PortableInvalidClassException; -import org.apache.ignite.internal.portable.api.PortableMetadata; -import org.apache.ignite.internal.portable.api.PortableSerializer; -import org.apache.ignite.internal.portable.api.PortableTypeConfiguration; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; @@ -81,6 +82,9 @@ public class PortableContext implements Externalizable { private static final long serialVersionUID = 0L; /** */ + private static final ClassLoader dfltLdr = U.gridClassLoader(); + + /** */ static final PortableIdMapper DFLT_ID_MAPPER = new IdMapperWrapper(null); /** */ @@ -109,7 +113,8 @@ public class PortableContext implements Externalizable { private final ConcurrentMap, PortableClassDescriptor> descByCls = new ConcurrentHashMap8<>(); /** */ - private final ConcurrentMap userTypes = new ConcurrentHashMap8<>(0); + private final ConcurrentMap> userTypes + = new ConcurrentHashMap8<>(0); /** */ private final Map predefinedTypes = new HashMap<>(); @@ -454,19 +459,21 @@ public class PortableContext implements Externalizable { public PortableClassDescriptor descriptorForTypeId(boolean userType, int typeId, ClassLoader ldr) { assert typeId != GridPortableMarshaller.UNREGISTERED_TYPE_ID; - //TODO: IGNITE-1358 (uncomment when fixed) - //PortableClassDescriptor desc = userType ? userTypes.get(typeId) : predefinedTypes.get(typeId); - - // As a workaround for IGNITE-1358 we always check the predefined map before. + //TODO: As a workaround for IGNITE-1358 we always check the predefined map before without checking 'userType' PortableClassDescriptor desc = predefinedTypes.get(typeId); if (desc != null) return desc; + if (ldr == null) + ldr = dfltLdr; + if (userType) { - desc = userTypes.get(typeId); + desc = userTypesMap(ldr).get(typeId); - if (desc != null) + // 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 (desc != null && ldr.equals(dfltLdr)) return desc; } @@ -478,9 +485,15 @@ public class PortableContext implements Externalizable { desc = descByCls.get(cls); } catch (ClassNotFoundException e) { + if (userType && !ldr.equals(dfltLdr) && (desc = descriptorLoadingFailover(typeId, ldr)) != null) + return desc; + throw new PortableInvalidClassException(e); } catch (IgniteCheckedException e) { + if (userType && !ldr.equals(dfltLdr) && (desc = descriptorLoadingFailover(typeId, ldr)) != null) + return desc; + throw new PortableException("Failed resolve class for ID: " + typeId, e); } @@ -494,6 +507,29 @@ public class PortableContext implements Externalizable { } /** + * The method must be used in case when it wasn't possible to load user type's class using custom class loader. + * + * There are several reasons why this may happen. First, type's name can be not registered in a system cache. + * Second, class might have been predefined explicitly and loaded by default class loader. + * + * @param typeId Type ID. + * @param ldr Class loader that failed to load type's class. + * @return Type descriptor on success, {@code null} on failure. + */ + private PortableClassDescriptor descriptorLoadingFailover(int typeId, ClassLoader ldr) { + assert !ldr.equals(dfltLdr); + + // Type name can be not registered in a system cache. Try to get from local map. + PortableClassDescriptor desc = userTypesMap(ldr).get(typeId); + + if (desc == null) + // Class might have been loaded by default class loader. + desc = descriptorForTypeId(true, typeId, dfltLdr); + + return desc; + } + + /** * Creates and registers {@link PortableClassDescriptor} for the given {@code class}. * * @param cls Class. @@ -567,8 +603,10 @@ public class PortableContext implements Externalizable { false /* predefined */ ); - // perform put() instead of putIfAbsent() because "registered" flag may have been changed. - userTypes.put(typeId, desc); + // perform put() instead of putIfAbsent() because "registered" flag might have been changed or class loader + // might have reloaded described class. + userTypesMap(IgniteUtils.detectClassLoader(cls)).put(typeId, desc); + descByCls.put(cls, desc); // TODO uncomment for https://issues.apache.org/jira/browse/IGNITE-1377 @@ -642,9 +680,14 @@ public class PortableContext implements Externalizable { if (idMapper != null) return idMapper; - if (userTypes.containsKey(typeId) || predefinedTypes.containsKey(typeId)) + if (predefinedTypes.containsKey(typeId)) return DFLT_ID_MAPPER; + for (ConcurrentMap types : userTypes.values()) { + if (types.containsKey(typeId)) + return DFLT_ID_MAPPER; + } + return BASIC_CLS_ID_MAPPER; } @@ -785,7 +828,8 @@ public class PortableContext implements Externalizable { fieldsMeta = desc.fieldsMeta(); - userTypes.put(id, desc); + userTypesMap(IgniteUtils.detectClassLoader(cls)).put(id, desc); + descByCls.put(cls, desc); } @@ -934,6 +978,45 @@ public class PortableContext implements Externalizable { } /** + * Undeployment callback invoked when class loader is being undeployed. + * + * 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) { + userTypes.remove(ldr); + + for (Class cls : descByCls.keySet()) { + if (ldr.equals(cls.getClassLoader())) + descByCls.remove(cls); + } + + U.clearClassCache(ldr); + } + + /** + * Returns user type map for specific class loader. + * + * @param ldr Class loader that loaded user type's class. + * @return User type map. + */ + private ConcurrentMap userTypesMap(ClassLoader ldr) { + ConcurrentMap ldrMap = userTypes.get(ldr); + + if (ldrMap == null) { + ConcurrentMap old = userTypes.putIfAbsent(ldr, + ldrMap = new ConcurrentHashMap8<>()); + + if (old != null) + ldrMap = old; + } + + return ldrMap; + } + + + /** */ private static class IdMapperWrapper implements PortableIdMapper { /** */ @@ -1140,4 +1223,4 @@ public class PortableContext implements Externalizable { return registered; } } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/api/PortableMarshaller.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/api/PortableMarshaller.java index de0df8d..2da4af1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/portable/api/PortableMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/api/PortableMarshaller.java @@ -355,4 +355,9 @@ public class PortableMarshaller extends AbstractMarshaller { throw new PortableException("Failed to unmarshal the object from InputStream", e); } } + + /** {@inheritDoc} */ + @Override public void onUndeploy(ClassLoader ldr) { + impl.context().onUndeploy(ldr); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/AbstractMarshaller.java b/modules/core/src/main/java/org/apache/ignite/marshaller/AbstractMarshaller.java index 8c79a93..dd5bad0 100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/AbstractMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/AbstractMarshaller.java @@ -37,6 +37,16 @@ public abstract class AbstractMarshaller implements Marshaller { /** Context. */ protected MarshallerContext ctx; + + /** + * Undeployment callback invoked when class loader is being undeployed. + * + * Some marshallers may want to clean their internal state that uses the undeployed class loader somehow. + * + * @param ldr Class loader being undeployed. + */ + public abstract void onUndeploy(ClassLoader ldr); + /** {@inheritDoc} */ @Override public void setContext(MarshallerContext ctx) { this.ctx = ctx; @@ -71,4 +81,4 @@ public abstract class AbstractMarshaller implements Marshaller { U.close(in, null); } } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java b/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java index 0f4cf1f..9e0e823 100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java @@ -115,7 +115,12 @@ public class JdkMarshaller extends AbstractMarshaller { } /** {@inheritDoc} */ + @Override public void onUndeploy(ClassLoader ldr) { + + } + + /** {@inheritDoc} */ @Override public String toString() { return S.toString(JdkMarshaller.class, this); } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java index b9b782a..caccd99 100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java @@ -288,7 +288,7 @@ public class OptimizedMarshaller extends AbstractMarshaller { * * @param ldr Class loader being undeployed. */ - public void onUndeploy(ClassLoader ldr) { + @Override public void onUndeploy(ClassLoader ldr) { for (Class cls : clsMap.keySet()) { if (ldr.equals(cls.getClassLoader())) clsMap.remove(cls); @@ -296,4 +296,4 @@ public class OptimizedMarshaller extends AbstractMarshaller { U.clearClassCache(ldr); } -} \ No newline at end of file +}