This is an automated email from the ASF dual-hosted git repository.

adutra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new 2f0c7a43d Generate Request IDs (if not specified); Return Request ID 
as a Header (#2602)
2f0c7a43d is described below

commit 2f0c7a43d446452004ea51196b618de9bdf0e25b
Author: Adnan Hemani <[email protected]>
AuthorDate: Tue Sep 30 03:24:15 2025 -0700

    Generate Request IDs (if not specified); Return Request ID as a Header 
(#2602)
---
 .../polaris/service/config/FilterPriorities.java   |   3 +-
 .../inmemory/InMemoryBufferEventListener.java      |   2 +-
 .../polaris/service/logging/LoggingMDCFilter.java  |   8 +-
 .../RequestIdFilter.java}                          |  26 ++---
 .../service/tracing/RequestIdGenerator.java        |  56 ++++++++++
 .../service/tracing/RequestIdResponseFilter.java   |  46 ++++++++
 .../polaris/service/tracing/TracingFilter.java     |   3 +-
 .../service/tracing/RequestIdGeneratorTest.java    | 122 +++++++++++++++++++++
 .../service/tracing/RequestIdHeaderTest.java       | 122 +++++++++++++++++++++
 9 files changed, 360 insertions(+), 28 deletions(-)

diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/config/FilterPriorities.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/config/FilterPriorities.java
index eed2d4353..5ce166409 100644
--- 
a/runtime/service/src/main/java/org/apache/polaris/service/config/FilterPriorities.java
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/config/FilterPriorities.java
@@ -21,7 +21,8 @@ package org.apache.polaris.service.config;
 import jakarta.ws.rs.Priorities;
 
 public final class FilterPriorities {
-  public static final int REALM_CONTEXT_FILTER = Priorities.AUTHENTICATION - 
100;
+  public static final int REQUEST_ID_FILTER = Priorities.AUTHENTICATION - 101;
+  public static final int REALM_CONTEXT_FILTER = REQUEST_ID_FILTER + 1;
   public static final int RATE_LIMITER_FILTER = Priorities.USER;
   public static final int MDC_FILTER = REALM_CONTEXT_FILTER + 1;
   public static final int TRACING_FILTER = REALM_CONTEXT_FILTER + 2;
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/events/listeners/inmemory/InMemoryBufferEventListener.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/events/listeners/inmemory/InMemoryBufferEventListener.java
index 6fd88d03f..533e983ae 100644
--- 
a/runtime/service/src/main/java/org/apache/polaris/service/events/listeners/inmemory/InMemoryBufferEventListener.java
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/events/listeners/inmemory/InMemoryBufferEventListener.java
@@ -19,7 +19,7 @@
 
 package org.apache.polaris.service.events.listeners.inmemory;
 
-import static 
org.apache.polaris.service.logging.LoggingMDCFilter.REQUEST_ID_KEY;
+import static 
org.apache.polaris.service.tracing.RequestIdFilter.REQUEST_ID_KEY;
 
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.github.benmanes.caffeine.cache.LoadingCache;
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
index c3b078ea1..fb0112cbb 100644
--- 
a/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
@@ -19,6 +19,7 @@
 package org.apache.polaris.service.logging;
 
 import static 
org.apache.polaris.service.context.RealmContextFilter.REALM_CONTEXT_KEY;
+import static 
org.apache.polaris.service.tracing.RequestIdFilter.REQUEST_ID_KEY;
 
 import jakarta.annotation.Priority;
 import jakarta.enterprise.context.ApplicationScoped;
@@ -38,7 +39,6 @@ import org.slf4j.MDC;
 public class LoggingMDCFilter implements ContainerRequestFilter {
 
   public static final String REALM_ID_KEY = "realmId";
-  public static final String REQUEST_ID_KEY = "requestId";
 
   @Inject LoggingConfiguration loggingConfiguration;
 
@@ -49,11 +49,7 @@ public class LoggingMDCFilter implements 
ContainerRequestFilter {
     // Also put the MDC values in the request context for use by other filters 
and handlers
     loggingConfiguration.mdc().forEach(MDC::put);
     loggingConfiguration.mdc().forEach(rc::setProperty);
-    var requestId = 
rc.getHeaderString(loggingConfiguration.requestIdHeaderName());
-    if (requestId != null) {
-      MDC.put(REQUEST_ID_KEY, requestId);
-      rc.setProperty(REQUEST_ID_KEY, requestId);
-    }
+    MDC.put(REQUEST_ID_KEY, (String) rc.getProperty(REQUEST_ID_KEY));
     RealmContext realmContext = (RealmContext) 
rc.getProperty(REALM_CONTEXT_KEY);
     MDC.put(REALM_ID_KEY, realmContext.getRealmIdentifier());
   }
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdFilter.java
similarity index 60%
copy from 
runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
copy to 
runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdFilter.java
index c3b078ea1..973313b9d 100644
--- 
a/runtime/service/src/main/java/org/apache/polaris/service/logging/LoggingMDCFilter.java
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdFilter.java
@@ -16,9 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.service.logging;
-
-import static 
org.apache.polaris.service.context.RealmContextFilter.REALM_CONTEXT_KEY;
+package org.apache.polaris.service.tracing;
 
 import jakarta.annotation.Priority;
 import jakarta.enterprise.context.ApplicationScoped;
@@ -27,34 +25,26 @@ import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.container.ContainerRequestFilter;
 import jakarta.ws.rs.container.PreMatching;
 import jakarta.ws.rs.ext.Provider;
-import org.apache.polaris.core.context.RealmContext;
 import org.apache.polaris.service.config.FilterPriorities;
-import org.slf4j.MDC;
+import org.apache.polaris.service.logging.LoggingConfiguration;
 
 @PreMatching
 @ApplicationScoped
-@Priority(FilterPriorities.MDC_FILTER)
+@Priority(FilterPriorities.REQUEST_ID_FILTER)
 @Provider
-public class LoggingMDCFilter implements ContainerRequestFilter {
+public class RequestIdFilter implements ContainerRequestFilter {
 
-  public static final String REALM_ID_KEY = "realmId";
   public static final String REQUEST_ID_KEY = "requestId";
 
   @Inject LoggingConfiguration loggingConfiguration;
+  @Inject RequestIdGenerator requestIdGenerator;
 
   @Override
   public void filter(ContainerRequestContext rc) {
-    // The request scope is active here, so any MDC values set here will be 
propagated to
-    // threads handling the request.
-    // Also put the MDC values in the request context for use by other filters 
and handlers
-    loggingConfiguration.mdc().forEach(MDC::put);
-    loggingConfiguration.mdc().forEach(rc::setProperty);
     var requestId = 
rc.getHeaderString(loggingConfiguration.requestIdHeaderName());
-    if (requestId != null) {
-      MDC.put(REQUEST_ID_KEY, requestId);
-      rc.setProperty(REQUEST_ID_KEY, requestId);
+    if (requestId == null) {
+      requestId = requestIdGenerator.generateRequestId();
     }
-    RealmContext realmContext = (RealmContext) 
rc.getProperty(REALM_CONTEXT_KEY);
-    MDC.put(REALM_ID_KEY, realmContext.getRealmIdentifier());
+    rc.setProperty(REQUEST_ID_KEY, requestId);
   }
 }
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdGenerator.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdGenerator.java
new file mode 100644
index 000000000..9ddd0b04c
--- /dev/null
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdGenerator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.polaris.service.tracing;
+
+import com.google.common.annotations.VisibleForTesting;
+import jakarta.enterprise.context.ApplicationScoped;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
+@ApplicationScoped
+public class RequestIdGenerator {
+  static final Long COUNTER_SOFT_MAX = Long.MAX_VALUE / 2;
+
+  record State(String uuid, long counter) {
+
+    State() {
+      this(UUID.randomUUID().toString(), 1);
+    }
+
+    String requestId() {
+      return String.format("%s_%019d", uuid, counter);
+    }
+
+    State increment() {
+      return counter >= COUNTER_SOFT_MAX ? new State() : new State(uuid, 
counter + 1);
+    }
+  }
+
+  final AtomicReference<State> state = new AtomicReference<>(new State());
+
+  public String generateRequestId() {
+    return state.getAndUpdate(State::increment).requestId();
+  }
+
+  @VisibleForTesting
+  public void setCounter(long counter) {
+    state.set(new State(state.get().uuid, counter));
+  }
+}
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdResponseFilter.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdResponseFilter.java
new file mode 100644
index 000000000..728960182
--- /dev/null
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/RequestIdResponseFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.polaris.service.tracing;
+
+import static 
org.apache.polaris.service.tracing.RequestIdFilter.REQUEST_ID_KEY;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+import jakarta.ws.rs.ext.Provider;
+import org.apache.polaris.service.logging.LoggingConfiguration;
+
+@ApplicationScoped
+@Provider
+public class RequestIdResponseFilter implements ContainerResponseFilter {
+
+  @Inject LoggingConfiguration loggingConfiguration;
+
+  @Override
+  public void filter(
+      ContainerRequestContext requestContext, ContainerResponseContext 
responseContext) {
+    responseContext
+        .getHeaders()
+        .add(
+            loggingConfiguration.requestIdHeaderName(), 
requestContext.getProperty(REQUEST_ID_KEY));
+  }
+}
diff --git 
a/runtime/service/src/main/java/org/apache/polaris/service/tracing/TracingFilter.java
 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/TracingFilter.java
index b4c056235..8b6859ca3 100644
--- 
a/runtime/service/src/main/java/org/apache/polaris/service/tracing/TracingFilter.java
+++ 
b/runtime/service/src/main/java/org/apache/polaris/service/tracing/TracingFilter.java
@@ -28,7 +28,6 @@ import jakarta.ws.rs.ext.Provider;
 import org.apache.polaris.core.context.RealmContext;
 import org.apache.polaris.service.config.FilterPriorities;
 import org.apache.polaris.service.context.RealmContextFilter;
-import org.apache.polaris.service.logging.LoggingMDCFilter;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 @PreMatching
@@ -47,7 +46,7 @@ public class TracingFilter implements ContainerRequestFilter {
   public void filter(ContainerRequestContext rc) {
     if (!sdkDisabled) {
       Span span = Span.current();
-      String requestId = (String) 
rc.getProperty(LoggingMDCFilter.REQUEST_ID_KEY);
+      String requestId = (String) 
rc.getProperty(RequestIdFilter.REQUEST_ID_KEY);
       if (requestId != null) {
         span.setAttribute(REQUEST_ID_ATTRIBUTE, requestId);
       }
diff --git 
a/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdGeneratorTest.java
 
b/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdGeneratorTest.java
new file mode 100644
index 000000000..08edfe481
--- /dev/null
+++ 
b/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdGeneratorTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.polaris.service.tracing;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class RequestIdGeneratorTest {
+
+  private RequestIdGenerator requestIdGenerator;
+
+  @BeforeEach
+  void setUp() {
+    requestIdGenerator = new RequestIdGenerator();
+  }
+
+  @Test
+  void testGenerateRequestId_ReturnsValidFormat() {
+    String requestId = requestIdGenerator.generateRequestId();
+
+    assertThat(requestId).isNotNull();
+    assertThat(requestId).matches(this::isValidRequestIdFormat);
+    // First call should increment counter to 1
+    assertThat(extractCounterFromRequestId(requestId)).isEqualTo(1);
+  }
+
+  @Test
+  void testGenerateRequestId_ReturnsUniqueIds() {
+    Set<String> generatedIds = new HashSet<>();
+
+    // Generate multiple request IDs and verify they're all unique
+    for (int i = 0; i < 1000; i++) {
+      String requestId = requestIdGenerator.generateRequestId();
+      assertThat(generatedIds).doesNotContain(requestId);
+      generatedIds.add(requestId);
+    }
+
+    assertThat(generatedIds).hasSize(1000);
+  }
+
+  @Test
+  void testCounterIncrementsSequentially() {
+    //    requestIdGenerator.setCounter(0);
+
+    String firstId = requestIdGenerator.generateRequestId();
+    String secondId = requestIdGenerator.generateRequestId();
+    String thirdId = requestIdGenerator.generateRequestId();
+
+    assertThat(extractCounterFromRequestId(firstId)).isEqualTo(1);
+    assertThat(extractCounterFromRequestId(secondId)).isEqualTo(2);
+    assertThat(extractCounterFromRequestId(thirdId)).isEqualTo(3);
+  }
+
+  @Test
+  void testCounterRotationAtSoftMax() {
+    // Set counter close to soft max
+    long softMax = RequestIdGenerator.COUNTER_SOFT_MAX;
+    requestIdGenerator.setCounter(softMax);
+
+    String beforeRotation = requestIdGenerator.generateRequestId();
+    String afterRotation = requestIdGenerator.generateRequestId();
+
+    // The UUID part should be different after rotation
+    String beforeUuidPart = beforeRotation.substring(0, 
beforeRotation.lastIndexOf('_'));
+    String afterUuidPart = afterRotation.substring(0, 
afterRotation.lastIndexOf('_'));
+    assertNotEquals(beforeUuidPart, afterUuidPart);
+
+    assertThat(extractCounterFromRequestId(beforeRotation)).isEqualTo(softMax);
+    // Counter reset to 1 (after increment from 0)
+    assertThat(extractCounterFromRequestId(afterRotation)).isEqualTo(1);
+  }
+
+  @Test
+  void testSetCounterChangesNextGeneratedId() {
+    requestIdGenerator.setCounter(100);
+
+    String requestId = requestIdGenerator.generateRequestId();
+
+    // Should increment from set value
+    assertThat(extractCounterFromRequestId(requestId)).isEqualTo(100);
+  }
+
+  private boolean isValidRequestIdFormat(String str) {
+    try {
+      String[] requestIdParts = str.split("_");
+      String uuid = requestIdParts[0];
+      String counter = requestIdParts[1];
+      UUID.fromString(uuid);
+      Long.parseLong(counter);
+      return true;
+    } catch (IllegalArgumentException e) {
+      return false;
+    }
+  }
+
+  private long extractCounterFromRequestId(String requestId) {
+    return Long.parseLong(requestId.split("_")[1]);
+  }
+}
diff --git 
a/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdHeaderTest.java
 
b/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdHeaderTest.java
new file mode 100644
index 000000000..ba64fddcc
--- /dev/null
+++ 
b/runtime/service/src/test/java/org/apache/polaris/service/tracing/RequestIdHeaderTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.polaris.service.tracing;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.Response;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.polaris.service.it.env.PolarisApiEndpoints;
+import org.apache.polaris.service.it.env.PolarisClient;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+@TestProfile(RequestIdHeaderTest.Profile.class)
+public class RequestIdHeaderTest {
+  public static class Profile implements QuarkusTestProfile {
+    @Override
+    public Map<String, String> getConfigOverrides() {
+      return Map.of(
+          "polaris.log.request-id-header-name",
+          REQUEST_ID_HEADER,
+          "polaris.realm-context.header-name",
+          REALM_HEADER,
+          "polaris.realm-context.realms",
+          REALM);
+    }
+  }
+
+  private static final String REQUEST_ID_HEADER = "x-test-request-id-random";
+  private static final String REALM_HEADER = "realm";
+  private static final String REALM = "realm1";
+  private static final String CLIENT_ID = "client1";
+  private static final String CLIENT_SECRET = "secret1";
+
+  private static final URI baseUri =
+      URI.create(
+          "http://localhost:";
+              + Objects.requireNonNull(
+                  Integer.getInteger("quarkus.http.test-port"),
+                  "System property not set correctly: 
quarkus.http.test-port"));
+
+  private Response request(Map<String, String> headers) {
+    try (PolarisClient client =
+        PolarisClient.polarisClient(new PolarisApiEndpoints(baseUri, REALM, 
headers))) {
+      return client
+          .catalogApiPlain()
+          .request("v1/oauth/tokens")
+          .post(
+              Entity.form(
+                  new MultivaluedHashMap<>(
+                      Map.of(
+                          "grant_type",
+                          "client_credentials",
+                          "scope",
+                          "PRINCIPAL_ROLE:ALL",
+                          "client_id",
+                          CLIENT_ID,
+                          "client_secret",
+                          CLIENT_SECRET))));
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Test
+  public void testRequestIdHeaderSpecified() {
+    String requestId = "pre-requested-request-id";
+    HashMap<String, String> headers =
+        new HashMap<>(Map.of(REALM_HEADER, REALM, REQUEST_ID_HEADER, 
requestId));
+    assertThat(sendRequest(headers)).matches(s -> s.equals(requestId));
+    assertThat(sendRequest(headers)).matches(s -> s.equals(requestId));
+
+    String newRequestId = "new-pre-requested-request-id";
+    headers.put(REQUEST_ID_HEADER, newRequestId);
+    assertThat(sendRequest(headers)).matches(s -> s.equals(newRequestId));
+  }
+
+  @Test
+  public void testRequestIdHeaderNotSpecified() {
+    Map<String, String> headers = Map.of(REALM_HEADER, REALM);
+    Set<String> requestIds = new HashSet<>();
+    for (int i = 0; i < 10; i++) {
+      String requestId = sendRequest(headers);
+      assertThat(requestIds).doesNotContain(requestId);
+      requestIds.add(requestId);
+    }
+  }
+
+  private String sendRequest(Map<String, String> headers) {
+    try (Response response = request(headers)) {
+      assertThat(response.getHeaders()).containsKey(REQUEST_ID_HEADER);
+      assertThat(response.getHeaders().get(REQUEST_ID_HEADER)).hasSize(1);
+      return 
response.getHeaders().get(REQUEST_ID_HEADER).getFirst().toString();
+    }
+  }
+}

Reply via email to