blob: 1725dcb2b98b3cb6170a1cd35d0eb74d57155d17 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Core.Impl
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Text.RegularExpressions;
using System.Threading;
using Apache.Ignite.Core.Binary;
using Apache.Ignite.Core.Cache;
using Apache.Ignite.Core.Cache.Store;
using Apache.Ignite.Core.Cluster;
using Apache.Ignite.Core.Common;
using Apache.Ignite.Core.Compute;
using Apache.Ignite.Core.Impl.Binary;
using Apache.Ignite.Core.Services;
using Apache.Ignite.Core.Transactions;
/// <summary>
/// Managed environment. Acts as a gateway for native code.
/// </summary>
internal static class ExceptionUtils
{
/** NoClassDefFoundError fully-qualified class name which is important during startup phase. */
private const string ClsNoClsDefFoundErr = "java.lang.NoClassDefFoundError";
/** NoSuchMethodError fully-qualified class name which is important during startup phase. */
private const string ClsNoSuchMthdErr = "java.lang.NoSuchMethodError";
/** InteropCachePartialUpdateException. */
private const string ClsCachePartialUpdateErr = "org.apache.ignite.internal.processors.platform.cache.PlatformCachePartialUpdateException";
/** Map with predefined exceptions. */
private static readonly IDictionary<string, ExceptionFactory> Exs = new Dictionary<string, ExceptionFactory>();
/** Inner class regex. */
private static readonly Regex InnerClassRegex = new Regex(@"class ([^\s]+): (.*)", RegexOptions.Compiled);
/// <summary>
/// Static initializer.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline",
Justification = "Readability")]
static ExceptionUtils()
{
// Common Java exceptions mapped to common .NET exceptions.
Exs["java.lang.IllegalArgumentException"] = (c, m, e, i) => new ArgumentException(m, e);
Exs["java.lang.IllegalStateException"] = (c, m, e, i) => new InvalidOperationException(m, e);
Exs["java.lang.UnsupportedOperationException"] = (c, m, e, i) => new NotSupportedException(m, e);
Exs["java.lang.InterruptedException"] = (c, m, e, i) => new ThreadInterruptedException(m, e);
Exs["java.lang.IllegalMonitorStateException"] = (c, m, e, i) => new SynchronizationLockException(m, e);
// Generic Ignite exceptions.
Exs["org.apache.ignite.IgniteException"] = (c, m, e, i) => new IgniteException(m, e);
Exs["org.apache.ignite.IgniteCheckedException"] = (c, m, e, i) => new IgniteException(m, e);
Exs["org.apache.ignite.IgniteIllegalStateException"] = (c, m, e, i) => new IgniteIllegalStateException(m, e);
Exs["org.apache.ignite.IgniteClientDisconnectedException"] = (c, m, e, i) => new ClientDisconnectedException(m, e, i.GetCluster().ClientReconnectTask);
Exs["org.apache.ignite.internal.IgniteClientDisconnectedCheckedException"] = (c, m, e, i) => new ClientDisconnectedException(m, e, i.GetCluster().ClientReconnectTask);
Exs["org.apache.ignite.binary.BinaryObjectException"] = (c, m, e, i) => new BinaryObjectException(m, e);
// Cluster exceptions.
Exs["org.apache.ignite.cluster.ClusterGroupEmptyException"] = (c, m, e, i) => new ClusterGroupEmptyException(m, e);
Exs["org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException"] = (c, m, e, i) => new ClusterGroupEmptyException(m, e);
Exs["org.apache.ignite.cluster.ClusterTopologyException"] = (c, m, e, i) => new ClusterTopologyException(m, e);
// Compute exceptions.
Exs["org.apache.ignite.compute.ComputeExecutionRejectedException"] = (c, m, e, i) => new ComputeExecutionRejectedException(m, e);
Exs["org.apache.ignite.compute.ComputeJobFailoverException"] = (c, m, e, i) => new ComputeJobFailoverException(m, e);
Exs["org.apache.ignite.compute.ComputeTaskCancelledException"] = (c, m, e, i) => new ComputeTaskCancelledException(m, e);
Exs["org.apache.ignite.compute.ComputeTaskTimeoutException"] = (c, m, e, i) => new ComputeTaskTimeoutException(m, e);
Exs["org.apache.ignite.compute.ComputeUserUndeclaredException"] = (c, m, e, i) => new ComputeUserUndeclaredException(m, e);
// Cache exceptions.
Exs["javax.cache.CacheException"] = (c, m, e, i) => new CacheException(m, e);
Exs["javax.cache.integration.CacheLoaderException"] = (c, m, e, i) => new CacheStoreException(m, e);
Exs["javax.cache.integration.CacheWriterException"] = (c, m, e, i) => new CacheStoreException(m, e);
Exs["javax.cache.processor.EntryProcessorException"] = (c, m, e, i) => new CacheEntryProcessorException(m, e);
// Transaction exceptions.
Exs["org.apache.ignite.transactions.TransactionOptimisticException"] = (c, m, e, i) => new TransactionOptimisticException(m, e);
Exs["org.apache.ignite.internal.transactions.IgniteTxOptimisticCheckedException"] = (c, m, e, i) => new TransactionOptimisticException(m, e);
Exs["org.apache.ignite.transactions.TransactionTimeoutException"] = (c, m, e, i) => new TransactionTimeoutException(m, e);
Exs["org.apache.ignite.transactions.TransactionRollbackException"] = (c, m, e, i) => new TransactionRollbackException(m, e);
Exs["org.apache.ignite.transactions.TransactionHeuristicException"] = (c, m, e, i) => new TransactionHeuristicException(m, e);
Exs["org.apache.ignite.transactions.TransactionDeadlockException"] = (c, m, e, i) => new TransactionDeadlockException(m, e);
// Security exceptions.
Exs["org.apache.ignite.IgniteAuthenticationException"] = (c, m, e, i) => new SecurityException(m, e);
Exs["org.apache.ignite.plugin.security.GridSecurityException"] = (c, m, e, i) => new SecurityException(m, e);
// Future exceptions.
Exs["org.apache.ignite.lang.IgniteFutureCancelledException"] = (c, m, e, i) => new IgniteFutureCancelledException(m, e);
Exs["org.apache.ignite.internal.IgniteFutureCancelledCheckedException"] = (c, m, e, i) => new IgniteFutureCancelledException(m, e);
// Service exceptions.
Exs["org.apache.ignite.services.ServiceDeploymentException"] = (c, m, e, i) => new ServiceDeploymentException(m, e);
}
/// <summary>
/// Creates exception according to native code class and message.
/// </summary>
/// <param name="igniteInt">The ignite.</param>
/// <param name="clsName">Exception class name.</param>
/// <param name="msg">Exception message.</param>
/// <param name="stackTrace">Native stack trace.</param>
/// <param name="reader">Error data reader.</param>
/// <param name="innerException">Inner exception.</param>
/// <returns>Exception.</returns>
public static Exception GetException(IIgniteInternal igniteInt, string clsName, string msg, string stackTrace,
BinaryReader reader = null, Exception innerException = null)
{
// Set JavaException as immediate inner.
var jex = new JavaException(clsName, msg, stackTrace, innerException);
return GetException(igniteInt, jex, reader);
}
/// <summary>
/// Creates exception according to native code class and message.
/// </summary>
/// <param name="igniteInt">The ignite.</param>
/// <param name="innerException">Java exception.</param>
/// <param name="reader">Error data reader.</param>
/// <returns>Exception.</returns>
public static Exception GetException(IIgniteInternal igniteInt, JavaException innerException,
BinaryReader reader = null)
{
var ignite = igniteInt == null ? null : igniteInt.GetIgnite();
var msg = innerException.JavaMessage;
var clsName = innerException.JavaClassName;
ExceptionFactory ctor;
if (Exs.TryGetValue(clsName, out ctor))
{
var match = InnerClassRegex.Match(msg ?? string.Empty);
ExceptionFactory innerCtor;
if (match.Success && Exs.TryGetValue(match.Groups[1].Value, out innerCtor))
{
return ctor(clsName, msg,
innerCtor(match.Groups[1].Value, match.Groups[2].Value, innerException, ignite),
ignite);
}
return ctor(clsName, msg, innerException, ignite);
}
if (ClsNoClsDefFoundErr.Equals(clsName, StringComparison.OrdinalIgnoreCase))
return new IgniteException("Java class is not found (did you set IGNITE_HOME environment " +
"variable?): " + msg, innerException);
if (ClsNoSuchMthdErr.Equals(clsName, StringComparison.OrdinalIgnoreCase))
return new IgniteException("Java class method is not found (did you set IGNITE_HOME environment " +
"variable?): " + msg, innerException);
if (ClsCachePartialUpdateErr.Equals(clsName, StringComparison.OrdinalIgnoreCase))
return ProcessCachePartialUpdateException(igniteInt, msg, innerException.Message, reader);
// Predefined mapping not found - check plugins.
if (igniteInt != null && igniteInt.PluginProcessor != null)
{
ctor = igniteInt.PluginProcessor.GetExceptionMapping(clsName);
if (ctor != null)
{
return ctor(clsName, msg, innerException, ignite);
}
}
// Return default exception.
return new IgniteException(string.Format("Java exception occurred [class={0}, message={1}]", clsName, msg),
innerException);
}
/// <summary>
/// Process cache partial update exception.
/// </summary>
/// <param name="ignite">The ignite.</param>
/// <param name="msg">Message.</param>
/// <param name="stackTrace">Stack trace.</param>
/// <param name="reader">Reader.</param>
/// <returns>CachePartialUpdateException.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private static Exception ProcessCachePartialUpdateException(IIgniteInternal ignite, string msg,
string stackTrace, BinaryReader reader)
{
if (reader == null)
return new CachePartialUpdateException(msg, new IgniteException("Failed keys are not available."));
bool dataExists = reader.ReadBoolean();
Debug.Assert(dataExists);
if (reader.ReadBoolean())
{
bool keepBinary = reader.ReadBoolean();
BinaryReader keysReader = reader.Marshaller.StartUnmarshal(reader.Stream, keepBinary);
try
{
return new CachePartialUpdateException(msg, ReadNullableList(keysReader));
}
catch (Exception e)
{
// Failed to deserialize data.
return new CachePartialUpdateException(msg, e);
}
}
// Was not able to write keys.
string innerErrCls = reader.ReadString();
string innerErrMsg = reader.ReadString();
Exception innerErr = GetException(ignite, innerErrCls, innerErrMsg, stackTrace);
return new CachePartialUpdateException(msg, innerErr);
}
/// <summary>
/// Create JVM initialization exception.
/// </summary>
/// <param name="clsName">Class name.</param>
/// <param name="msg">Message.</param>
/// <param name="stackTrace">Stack trace.</param>
/// <returns>Exception.</returns>
[ExcludeFromCodeCoverage] // Covered by a test in a separate process.
public static Exception GetJvmInitializeException(string clsName, string msg, string stackTrace)
{
if (clsName != null)
return new IgniteException("Failed to initialize JVM.", GetException(null, clsName, msg, stackTrace));
if (msg != null)
return new IgniteException("Failed to initialize JVM: " + msg + "\n" + stackTrace);
return new IgniteException("Failed to initialize JVM.");
}
/// <summary>
/// Reads nullable list.
/// </summary>
/// <param name="reader">Reader.</param>
/// <returns>List.</returns>
private static List<object> ReadNullableList(BinaryReader reader)
{
if (!reader.ReadBoolean())
return null;
var size = reader.ReadInt();
var list = new List<object>(size);
for (int i = 0; i < size; i++)
list.Add(reader.ReadObject<object>());
return list;
}
}
}