You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/02/17 13:52:11 UTC
[ignite] branch master updated: IGNITE-13389 Thin client: optionally append server exception stack trace to error message (#9824)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 2311878 IGNITE-13389 Thin client: optionally append server exception stack trace to error message (#9824)
2311878 is described below
commit 2311878802b57a50a07b842447fa86f9dbc059a7
Author: Sergei Ryzhov <s....@gmail.com>
AuthorDate: Thu Feb 17 16:50:23 2022 +0300
IGNITE-13389 Thin client: optionally append server exception stack trace to error message (#9824)
* Add `ThinClientConfiguration.sendServerExceptionStackTraceToClient`, default false. When true, include exception stack trace with error message in thin client response.
* Add `DistributedThinClientConfiguration` and `ClientProcessorMXBean.showFullStackOnClientSide` to control the behavior for the entire cluster at runtime.
Co-authored-by: zstan <st...@gmail.com>
---
.../configuration/ThinClientConfiguration.java | 18 +++++
.../processors/cache/GridCacheMapEntry.java | 9 ++-
.../DistributedThinClientConfiguration.java | 81 ++++++++++++++++++++
.../processors/odbc/ClientListenerProcessor.java | 27 +++++++
.../platform/client/ClientRequestHandler.java | 6 ++
.../ignite/mxbean/ClientProcessorMXBean.java | 10 +++
.../org/apache/ignite/client/IgniteBinaryTest.java | 89 ++++++++++++++++++++--
7 files changed, 231 insertions(+), 9 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/ThinClientConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/ThinClientConfiguration.java
index 5ff3410..dbafffd 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/ThinClientConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/ThinClientConfiguration.java
@@ -37,6 +37,9 @@ public class ThinClientConfiguration {
/** Active compute tasks per connection limit. */
private int maxActiveComputeTasksPerConn = DFLT_MAX_ACTIVE_COMPUTE_TASKS_PER_CONNECTION;
+ /** If {@code true} sends a server exception stack trace to the client side. */
+ private boolean sendServerExcStackTraceToClient;
+
/**
* Creates thin-client configuration with all default values.
*/
@@ -54,6 +57,7 @@ public class ThinClientConfiguration {
maxActiveTxPerConn = cfg.maxActiveTxPerConn;
maxActiveComputeTasksPerConn = cfg.maxActiveComputeTasksPerConn;
+ sendServerExcStackTraceToClient = cfg.sendServerExcStackTraceToClient;
}
/**
@@ -97,6 +101,20 @@ public class ThinClientConfiguration {
return this;
}
+ /**
+ * @return If {@code true} sends a server exception stack to the client side.
+ */
+ public boolean sendServerExceptionStackTraceToClient() {
+ return sendServerExcStackTraceToClient;
+ }
+
+ /**
+ * @param sendServerExcStackTraceToClient If {@code true} sends a server exception stack to the client side.
+ */
+ public void sendServerExceptionStackTraceToClient(boolean sendServerExcStackTraceToClient) {
+ this.sendServerExcStackTraceToClient = sendServerExcStackTraceToClient;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(ThinClientConfiguration.class, this);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index f2edcd4..456f652 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -6438,7 +6438,14 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
CacheLazyEntry<Object, Object> interceptEntry =
new CacheLazyEntry<>(cctx, entry.key, null, oldVal, null, keepBinary);
- Object interceptorVal = cctx.config().getInterceptor().onBeforePut(interceptEntry, updated0);
+ Object interceptorVal = null;
+
+ try {
+ interceptorVal = cctx.config().getInterceptor().onBeforePut(interceptEntry, updated0);
+ }
+ catch (Throwable e) {
+ throw new IgniteCheckedException(e);
+ }
wasIntercepted = true;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedThinClientConfiguration.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedThinClientConfiguration.java
new file mode 100644
index 0000000..ff41dca
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/configuration/distributed/DistributedThinClientConfiguration.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.configuration.distributed;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.subscription.GridInternalSubscriptionProcessor;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.cluster.DistributedConfigurationUtils.makeUpdateListener;
+import static org.apache.ignite.internal.processors.configuration.distributed.DistributedBooleanProperty.detachedBooleanProperty;
+
+/**
+ * Thin client distributed configuration.
+ */
+public class DistributedThinClientConfiguration {
+ /** */
+ private final IgniteLogger log;
+
+ /** . */
+ private final DistributedChangeableProperty<Boolean> showStackTrace =
+ detachedBooleanProperty("thinClientProperty.showStackTrace");
+
+ /** Message of baseline auto-adjust parameter was changed. */
+ private static final String PROPERTY_UPDATE_MESSAGE =
+ "ThinClientProperty parameter '%s' was changed from '%s' to '%s'";
+
+ /**
+ * @param ctx Kernal context.
+ */
+ public DistributedThinClientConfiguration(
+ GridKernalContext ctx
+ ) {
+ log = ctx.log(DistributedThinClientConfiguration.class);
+
+ GridInternalSubscriptionProcessor isp = ctx.internalSubscriptionProcessor();
+
+ isp.registerDistributedConfigurationListener(
+ new DistributedConfigurationLifecycleListener() {
+ @Override public void onReadyToRegister(DistributedPropertyDispatcher dispatcher) {
+ showStackTrace.addListener(makeUpdateListener(PROPERTY_UPDATE_MESSAGE, log));
+
+ dispatcher.registerProperties(showStackTrace);
+ }
+ }
+ );
+ }
+
+ /**
+ * @param showStack If {@code true} shows full stack trace on the client side.
+ * @return Future for update operation.
+ */
+ public GridFutureAdapter<?> updateThinClientSendServerStackTraceAsync(boolean showStack) throws IgniteCheckedException {
+ return showStackTrace.propagateAsync(showStack);
+ }
+
+ /**
+ * @return If {@code true}, thin client response will include full stack trace when exception occurs.
+ * When {@code false}, only top level exception message is included.
+ */
+ @Nullable public Boolean sendServerExceptionStackTraceToClient() {
+ return showStackTrace.get();
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
index b0a260f..877644b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
@@ -30,6 +30,7 @@ import javax.management.JMException;
import javax.management.ObjectName;
import javax.net.ssl.SSLContext;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.OdbcConfiguration;
@@ -37,6 +38,7 @@ import org.apache.ignite.configuration.SqlConnectorConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.managers.systemview.walker.ClientConnectionViewWalker;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import org.apache.ignite.internal.processors.configuration.distributed.DistributedThinClientConfiguration;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcConnectionContext;
import org.apache.ignite.internal.processors.odbc.odbc.OdbcConnectionContext;
import org.apache.ignite.internal.util.GridSpinBusyLock;
@@ -90,6 +92,9 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
/** Executor service. */
private ExecutorService execSvc;
+ /** Thin client distributed configuration. */
+ private DistributedThinClientConfiguration distrThinCfg;
+
/**
* @param ctx Kernal context.
*/
@@ -198,6 +203,8 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
new ClientConnectionViewWalker(),
srv.sessions(),
ClientConnectionView::new);
+
+ distrThinCfg = new DistributedThinClientConfiguration(ctx);
}
catch (Exception e) {
throw new IgniteCheckedException("Failed to start client connector processor.", e);
@@ -559,6 +566,16 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
}
/**
+ * @return If {@code true} sends a server exception stack to the client side.
+ */
+ public boolean sendServerExceptionStackTraceToClient() {
+ Boolean send = distrThinCfg.sendServerExceptionStackTraceToClient();
+
+ return send == null ?
+ ctx.config().getClientConnectorConfiguration().getThinClientConfiguration().sendServerExceptionStackTraceToClient() : send;
+ }
+
+ /**
* ClientProcessorMXBean interface.
*/
private class ClientProcessorMXBeanImpl implements ClientProcessorMXBean {
@@ -620,5 +637,15 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
return false;
}
+
+ /** {@inheritDoc} */
+ @Override public void showFullStackOnClientSide(boolean show) {
+ try {
+ distrThinCfg.updateThinClientSendServerStackTraceAsync(show).get();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
index 7510dfc..d086a57 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
@@ -32,6 +32,8 @@ import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheSq
import org.apache.ignite.internal.processors.platform.client.cache.ClientCacheSqlQueryRequest;
import org.apache.ignite.internal.processors.platform.client.tx.ClientTxAwareRequest;
import org.apache.ignite.internal.processors.platform.client.tx.ClientTxContext;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.plugin.security.SecurityException;
import static org.apache.ignite.internal.processors.platform.client.ClientProtocolVersionFeature.BITMAP_FEATURES;
@@ -123,6 +125,10 @@ public class ClientRequestHandler implements ClientListenerRequestHandler {
msg = sqlState + ": " + msg;
}
+ if ((X.getCause(e) != null || !X.getSuppressedList(e).isEmpty())
+ && ctx.kernalContext().sqlListener().sendServerExceptionStackTraceToClient())
+ msg = msg + U.nl() + X.getFullStackTrace(e);
+
return new ClientResponse(req.requestId(), status, msg);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
index 5da7eee..93375af 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
@@ -48,4 +48,14 @@ public interface ClientProcessorMXBean {
public boolean dropConnection(
@MXBeanParameter(name = "id", description = "Client connection ID.") long id
);
+
+ /**
+ * If sets to {@code true} shows full stack trace otherwise highlevel short error message.
+ *
+ * @param show Show flag.
+ */
+ @MXBeanDescription("Show error full stack.")
+ void showFullStackOnClientSide(
+ @MXBeanParameter(name = "show", description = "Show error full stack.") boolean show
+ );
}
diff --git a/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java b/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
index f7681fa..6e019c8 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
+import javax.cache.Cache;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteBinary;
import org.apache.ignite.IgniteCache;
@@ -35,14 +36,20 @@ import org.apache.ignite.binary.BinarySerializer;
import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.binary.BinaryTypeConfiguration;
import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.cache.CacheInterceptorAdapter;
import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.binary.BinaryObjectImpl;
import org.apache.ignite.internal.binary.GridBinaryMarshaller;
-import org.apache.ignite.testframework.GridTestUtils;
-import org.junit.Rule;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.mxbean.ClientProcessorMXBean;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.LogListener;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
-import org.junit.rules.Timeout;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -51,11 +58,7 @@ import static org.junit.Assert.assertNull;
/**
* Ignite {@link BinaryObject} API system tests.
*/
-public class IgniteBinaryTest {
- /** Per test timeout */
- @Rule
- public Timeout globalTimeout = new Timeout((int)GridTestUtils.DFLT_TEST_TIMEOUT);
-
+public class IgniteBinaryTest extends GridCommonAbstractTest {
/**
* Unmarshalling schema-less Ignite binary objects into Java static types.
*/
@@ -134,6 +137,76 @@ public class IgniteBinaryTest {
}
/**
+ * Tests that {@code org.apache.ignite.cache.CacheInterceptor#onBeforePut(javax.cache.Cache.Entry, java.lang.Object)}
+ * throws correct exception in case while cache operations are called from thin client. Only BinaryObject`s are
+ * acceptable in this case.
+ */
+ @Test
+ public void testBinaryWithNotGenericInterceptor() throws Exception {
+ IgniteConfiguration ccfg = Config.getServerConfiguration()
+ .setCacheConfiguration(new CacheConfiguration("test").setInterceptor(new ThinBinaryValueInterceptor()));
+
+ String castErr = "cannot be cast to";
+ String treeErr = "B+Tree is corrupted";
+
+ ListeningTestLogger srvLog = new ListeningTestLogger(log);
+
+ LogListener lsnrCast = LogListener.matches(castErr).
+ andMatches(str -> !str.contains(treeErr)).build();
+
+ srvLog.registerListener(lsnrCast);
+
+ ccfg.setGridLogger(srvLog);
+
+ try (Ignite ign = Ignition.start(ccfg)) {
+ try (IgniteClient client =
+ Ignition.startClient(new ClientConfiguration().setAddresses(Config.SERVER))
+ ) {
+ ClientCache<Integer, ThinBinaryValue> cache = client.cache("test");
+
+ try {
+ cache.put(1, new ThinBinaryValue());
+
+ fail();
+ }
+ catch (Exception e) {
+ assertFalse(X.getFullStackTrace(e).contains(castErr));
+ }
+
+ ClientProcessorMXBean serverMxBean =
+ getMxBean(ign.name(), "Clients", ClientListenerProcessor.class, ClientProcessorMXBean.class);
+
+ serverMxBean.showFullStackOnClientSide(true);
+
+ try {
+ cache.put(1, new ThinBinaryValue());
+ }
+ catch (Exception e) {
+ assertTrue(X.getFullStackTrace(e).contains(castErr));
+ }
+ }
+ }
+
+ assertTrue(lsnrCast.check());
+ }
+
+ /**
+ * Test interceptor implementation.
+ */
+ private static class ThinBinaryValueInterceptor extends CacheInterceptorAdapter<String, ThinBinaryValue> {
+ /** {@inheritDoc} */
+ @Override public ThinBinaryValue onBeforePut(Cache.Entry<String, ThinBinaryValue> entry, ThinBinaryValue newVal) {
+ return super.onBeforePut(entry, newVal);
+ }
+ }
+
+ /**
+ * Test value class.
+ */
+ private static class ThinBinaryValue {
+ }
+
+ /**
* Check that binary types are registered for nested types too.
* With enabled "CompactFooter" binary type schema also should be passed to server.
*/