This is an automated email from the ASF dual-hosted git repository.
ayushtkn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new c28607ae27d HIVE-29713: Maintain properties package, update JEXL
dependency to JEXL 3.7.0 (#6592)
c28607ae27d is described below
commit c28607ae27db096adc024cca37e18444e0831db6
Author: Henrib <[email protected]>
AuthorDate: Tue Jul 28 21:11:50 2026 +0200
HIVE-29713: Maintain properties package, update JEXL dependency to JEXL
3.7.0 (#6592)
---
.../metastore/properties/HMSPropertyManager.java | 38 ++++-----
.../hive/metastore/properties/PropertyManager.java | 8 +-
.../hive/metastore/properties/PropertyMap.java | 2 +-
.../hive/metastore/properties/PropertyStore.java | 2 +-
.../hive/metastore/properties/PropertyType.java | 24 +++---
.../metastore/properties/SerializationProxy.java | 2 +
.../hive/metastore/properties/package-info.java | 83 +++++++++++++++++++
.../hadoop/hive/metastore/PropertyServlet.java | 95 +++++++++++++++++++++-
standalone-metastore/pom.xml | 2 +-
9 files changed, 217 insertions(+), 39 deletions(-)
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/HMSPropertyManager.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/HMSPropertyManager.java
index c1166bd28ed..3f2e2e63475 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/HMSPropertyManager.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/HMSPropertyManager.java
@@ -25,7 +25,7 @@
import java.util.TreeMap;
/**
- * A property manager tailored for the HiveMetaStore.
+ * A property manager tailored for the HiveMetaStore operating on Iceberg
tables.
* It describes properties for cluster, database and table based on declared
schemas.
* A property is of the form:
* <ul>
@@ -101,14 +101,14 @@ public static MaintenanceOpStatus findOpStatus(int
ordinal) {
*/
public static final PropertyType<MaintenanceOpType> MAINTENANCE_OPERATION =
new PropertyType<MaintenanceOpType>("MaintenanceOperation"){
@Override public MaintenanceOpType cast(Object value) {
- if (value instanceof MaintenanceOpType) {
- return (MaintenanceOpType) value;
+ if (value instanceof MaintenanceOpType op) {
+ return op;
}
if (value == null) {
return null;
}
- if (value instanceof Number) {
- return findOpType(((Number) value).intValue());
+ if (value instanceof Number n) {
+ return findOpType(n.intValue());
}
return parse(value.toString());
}
@@ -121,8 +121,8 @@ public static MaintenanceOpStatus findOpStatus(int ordinal)
{
}
@Override public String format(Object value) {
- if (value instanceof MaintenanceOpType) {
- return value.toString();
+ if (value instanceof MaintenanceOpType op) {
+ return op.toString();
}
return null;
}
@@ -133,14 +133,14 @@ public static MaintenanceOpStatus findOpStatus(int
ordinal) {
*/
public static final PropertyType<MaintenanceOpStatus> MAINTENANCE_STATUS =
new PropertyType<MaintenanceOpStatus>("MaintenanceStatus"){
@Override public MaintenanceOpStatus cast(Object value) {
- if (value instanceof MaintenanceOpStatus) {
- return (MaintenanceOpStatus) value;
+ if (value instanceof MaintenanceOpStatus op) {
+ return op;
}
if (value == null) {
return null;
}
- if (value instanceof Number) {
- return findOpStatus(((Number) value).intValue());
+ if (value instanceof Number n) {
+ return findOpStatus(n.intValue());
}
return parse(value.toString());
}
@@ -153,8 +153,8 @@ public static MaintenanceOpStatus findOpStatus(int ordinal)
{
}
@Override public String format(Object value) {
- if (value instanceof MaintenanceOpStatus) {
- return value.toString();
+ if (value instanceof MaintenanceOpStatus op) {
+ return op.toString();
}
return null;
}
@@ -247,12 +247,12 @@ public static void declareTableProperty(String name,
PropertyType<?> type, Objec
*/
@Override
public PropertySchema getSchema(String schemaName) {
- switch(schemaName) {
- case CLUSTER_PREFIX: return CLUSTER_SCHEMA;
- case DATABASE_PREFIX: return DATABASE_SCHEMA;
- case TABLE_PREFIX: return TABLE_SCHEMA;
- default: return null;
- }
+ return switch (schemaName) {
+ case CLUSTER_PREFIX -> CLUSTER_SCHEMA;
+ case DATABASE_PREFIX -> DATABASE_SCHEMA;
+ case TABLE_PREFIX -> TABLE_SCHEMA;
+ default -> null;
+ };
}
/**
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyManager.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyManager.java
index c1244533945..869eca9b07a 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyManager.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyManager.java
@@ -75,14 +75,14 @@ public abstract class PropertyManager {
/** A Jexl engine for convenience. */
static final JexlEngine JEXL;
static {
- JexlFeatures features = new JexlFeatures()
+ JexlFeatures features = JexlFeatures.createDefault()
.sideEffect(false)
.sideEffectGlobal(false);
- JexlPermissions p = JexlPermissions.RESTRICTED
- .compose("org.apache.hadoop.hive.metastore.properties.*");
+ JexlPermissions permissions = JexlPermissions.RESTRICTED
+ .compose("org.apache.hadoop.hive.metastore.properties.*",
"com.google.gson.internal.*");
JEXL = new JexlBuilder()
.features(features)
- .permissions(p)
+ .permissions(permissions)
.create();
}
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyMap.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyMap.java
index 06a7f7aa31b..5918bc1c582 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyMap.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyMap.java
@@ -135,7 +135,7 @@ public PropertyMap(PropertyMap src, Map<String, Object>
input) {
/**
* Deserialization ctor.
- * <p>Used through deserializtion through reflection by the serialization
proxy.</p>
+ * <p>Used through deserialization through reflection by the serialization
proxy.</p>
* @param input the input stream
* @throws IOException if IO fail
*/
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyStore.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyStore.java
index 0bc6beabf6c..911575d1793 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyStore.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyStore.java
@@ -25,7 +25,7 @@
/**
* The PropertyStore is the persistent container of property maps.
- * Maps are addressed in the store by their key - their name prepended by
their manager"s namespace.
+ * Maps are addressed in the store by their key - their name prepended by
their manager"s namespace.
*/
public abstract class PropertyStore {
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyType.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyType.java
index 930b625f593..89a1b0c0baa 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyType.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/PropertyType.java
@@ -182,8 +182,8 @@ public String parse(String str) {
@Nullable
@Override public String format(Object value) {
- if (value instanceof Boolean) {
- return ((Boolean) value) ? "true" : "false";
+ if (value instanceof Boolean b) {
+ return b.toString();
}
return null;
}
@@ -201,8 +201,8 @@ public Boolean read(DataInput in) throws IOException {
public static final PropertyType<Integer> INTEGER = new
PropertyType<Integer>("integer") {
@Override public Integer cast(Object value) {
- if (value instanceof Number) {
- return ((Number) value).intValue();
+ if (value instanceof Number n) {
+ return n.intValue();
}
if (value == null) {
return null;
@@ -241,8 +241,8 @@ public Integer read(DataInput in) throws IOException {
public static final PropertyType<Long> LONG = new PropertyType<Long>("long"){
@Override public Long cast(Object value) {
- if (value instanceof Number) {
- return ((Number) value).longValue();
+ if (value instanceof Number n) {
+ return n.longValue();
}
if (value == null) {
return null;
@@ -281,11 +281,11 @@ public Long read(DataInput in) throws IOException {
public static final PropertyType<Date> DATETIME = new
PropertyType<Date>("date"){
@Override public Date cast(Object value) {
- if (value instanceof Number) {
- return new Date(((Number) value).longValue());
+ if (value instanceof Number n) {
+ return new Date(n.longValue());
}
- if (value instanceof Date) {
- return (Date) value;
+ if (value instanceof Date d) {
+ return d;
}
if (value == null) {
return null;
@@ -322,8 +322,8 @@ public Long read(DataInput in) throws IOException {
public static final PropertyType<Double> DOUBLE = new
PropertyType<Double>("double"){
@Override public Double cast(Object value) {
- if (value instanceof Number) {
- return ((Number) value).doubleValue();
+ if (value instanceof Number n) {
+ return n.doubleValue();
}
if (value == null) {
return null;
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/SerializationProxy.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/SerializationProxy.java
index 8321d0ee276..9a90f223cb5 100644
---
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/SerializationProxy.java
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/SerializationProxy.java
@@ -33,6 +33,7 @@
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
+import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
@@ -75,6 +76,7 @@
*/
public class SerializationProxy<T extends Serializable> implements
Externalizable {
/** Serial version. */
+ @Serial
private static final long serialVersionUID = 202212281757L;
/** The logger. */
public static final Logger LOGGER =
LoggerFactory.getLogger(SerializationProxy.class);
diff --git
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/package-info.java
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/package-info.java
new file mode 100644
index 00000000000..4f237331a11
--- /dev/null
+++
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/properties/package-info.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+/**
+ * Type-safe, schema-driven property management for Hive metastore objects
(clusters, databases, tables).
+ *
+ * <p>This package allows declaring, validating, and persisting custom
properties with runtime type
+ * checking and serialization support.</p>
+ *
+ * <h2>Core Components</h2>
+ *
+ * <h3>{@link org.apache.hadoop.hive.metastore.properties.PropertySchema}</h3>
+ * <p>Defines the blueprint for what properties can be set on a metastore
object.</p>
+ * <ul>
+ * <li>Declares allowed properties and their types (STRING, INTEGER,
BOOLEAN, DATE, JSON, etc.)</li>
+ * <li>Manages default values for properties</li>
+ * <li>Tracks schema version — incremented when properties are added or
removed</li>
+ * <li>Generates a digest (UUID) for schema identity and caching</li>
+ * </ul>
+ *
+ * <h3>{@link org.apache.hadoop.hive.metastore.properties.PropertyType}</h3>
+ * <p>Abstract type system for property values with parsing, formatting, and
binary serialization.
+ * Built-in types: {@code STRING}, {@code BOOLEAN}, {@code INTEGER}, {@code
LONG}, {@code DOUBLE},
+ * {@code DATETIME} (ISO8601/UTC), {@code JSON} (Gson-backed). Custom types
can be registered via
+ * {@link
org.apache.hadoop.hive.metastore.properties.PropertyType#register(PropertyType)}.</p>
+ *
+ * <h3>{@link org.apache.hadoop.hive.metastore.properties.PropertyMap}</h3>
+ * <p>Holds the actual property values for a specific metastore object
instance, validated against
+ * a {@code PropertySchema}. Uses copy-on-write semantics gated by a dirty
flag for thread-safe,
+ * efficient sharing.</p>
+ *
+ * <h3>{@link org.apache.hadoop.hive.metastore.properties.PropertyManager}</h3>
+ * <p>High-level per-session manager that ties schemas into one namespace and
drives transactional
+ * reads and writes. Tracks dirty maps to batch persistence store updates.
Integrates with JEXL
+ * for dynamic property expressions.</p>
+ *
+ * <h3>{@link org.apache.hadoop.hive.metastore.properties.PropertyStore}</h3>
+ * <p>Persistence layer for loading and saving property maps to the metastore
database.
+ * Can be wrapped by {@link
org.apache.hadoop.hive.metastore.properties.CachingPropertyStore}
+ * for performance.</p>
+ *
+ * <h2>Workflow Example</h2>
+ * <pre>{@code
+ * // Define a schema (typically done at startup)
+ * PropertySchema tableSchema = new PropertySchema("table-schema", 1, new
TreeMap<>());
+ * tableSchema.declareProperty("owner", PropertyType.STRING, "admin");
+ * tableSchema.declareProperty("is_external", PropertyType.BOOLEAN, false);
+ * tableSchema.declareProperty("created_time", PropertyType.DATETIME);
+ *
+ * // Create a property map for an object, set values, and persist
+ * PropertyMap tableProps = manager.newPropertyMap(tableSchema);
+ * tableProps.put("owner", "alice");
+ * tableProps.put("is_external", true);
+ * manager.persistProperties(tableProps);
+ * }</pre>
+ *
+ * <h2>Design Patterns</h2>
+ * <ul>
+ * <li><b>Thread safety</b>: schemas use {@code AtomicInteger} version
counters; maps use
+ * copy-on-write with dirty flags; digest computation is double-checked
locked.</li>
+ * <li><b>Serialization</b>: a custom {@link
org.apache.hadoop.hive.metastore.properties.SerializationProxy}
+ * handles transient fields safely; both {@code DataInput}/{@code
DataOutput} and Java object
+ * serialization are supported.</li>
+ * <li><b>Schema versioning</b>: version and digest UUID allow detecting
schema changes and
+ * support schema evolution without breaking existing data.</li>
+ * </ul>
+ */
+package org.apache.hadoop.hive.metastore.properties;
diff --git
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PropertyServlet.java
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PropertyServlet.java
index adea578f239..fd3caea34cf 100644
---
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PropertyServlet.java
+++
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PropertyServlet.java
@@ -52,7 +52,100 @@
import java.util.TreeMap;
/**
- * The property-maps servlet.
+ * HTTP servlet exposing the {@link
org.apache.hadoop.hive.metastore.properties.PropertyManager}
+ * API over JSON.
+ *
+ * <p>The servlet is mounted at a configurable path and port (see
+ * {@code MetastoreConf.ConfVars.PROPERTIES_SERVLET_PORT} and
+ * {@code MetastoreConf.ConfVars.PROPERTIES_SERVLET_PATH}). The last segment
of the request URI is
+ * used as the property manager <em>namespace</em>, so the same servlet
instance can serve multiple
+ * independent namespaces.</p>
+ *
+ * <h2>Namespace and Schema resolution</h2>
+ * <p>The last URI segment is the <em>namespace</em>. Each namespace must be
registered in advance
+ * by calling {@link
org.apache.hadoop.hive.metastore.properties.PropertyManager#declare(String,
Class)}
+ * with a concrete {@link
org.apache.hadoop.hive.metastore.properties.PropertyManager} subclass.
+ * That subclass is instantiated per request via its {@code (String,
PropertyStore)} constructor.</p>
+ *
+ * <p>Within a namespace, the {@link
org.apache.hadoop.hive.metastore.properties.PropertySchema} is
+ * <em>not</em> fixed — it is resolved dynamically by the manager from the
number of dot-separated
+ * key fragments. For example, {@link
org.apache.hadoop.hive.metastore.properties.HMSPropertyManager}
+ * (registered under {@code "hms"}) uses:</p>
+ * <table border="1">
+ * <caption>HMSPropertyManager key-to-schema mapping</caption>
+ * <tr><th>Fragments</th><th>Example key</th><th>Schema</th></tr>
+ * <tr><td>1</td><td>{@code name}</td><td>CLUSTER_SCHEMA</td></tr>
+ * <tr><td>2</td><td>{@code db.name}</td><td>DATABASE_SCHEMA</td></tr>
+ * <tr><td>3+</td><td>{@code db.table.name}</td><td>TABLE_SCHEMA</td></tr>
+ * </table>
+ * <p>A namespace with no registered manager throws {@code
NoSuchObjectException} (HTTP 400).</p>
+ *
+ * <h2>HTTP Methods</h2>
+ *
+ * <h3>GET — fetch properties by key</h3>
+ * <p>One or more {@code key} query parameters select individual property
values.
+ * Returns a JSON object mapping each found key to its string value.</p>
+ * <pre>{@code
+ * GET /properties/mynamespace?key=db1.table1.owner&key=db1.table1.created_time
+ * → { "db1.table1.owner": "alice", "db1.table1.created_time":
"2024-01-15T08:00:00.00Z" }
+ * }</pre>
+ *
+ * <h3>PUT — set properties</h3>
+ * <p>Request body must be a JSON object mapping qualified keys to values. All
updates are
+ * committed atomically; any error triggers a rollback.</p>
+ * <pre>{@code
+ * PUT /properties/mynamespace
+ * { "db1.table1.owner": "bob", "db1.table1.is_external": "true" }
+ * }</pre>
+ *
+ * <h3>POST — query or script</h3>
+ * <p>The request body is a single JSON action object or a JSON array of
action objects.
+ * Each action must have a {@code "method"} field (defaults to {@code
"selectProperties"} if absent).
+ * Returns a single result object, or an array when multiple actions are
submitted.
+ * All actions in one request share a transaction; any failure rolls back the
entire batch.</p>
+ *
+ * <p>Supported methods:</p>
+ * <dl>
+ * <dt>{@code fetchProperties}</dt>
+ * <dd>Fetches exact values for a list of fully-qualified keys.
+ * <pre>{@code
+ * { "method": "fetchProperties", "keys": ["db1.table1.owner",
"db1.table1.tags"] }
+ * → { "db1.table1.owner": "alice" }
+ * }</pre></dd>
+ *
+ * <dt>{@code selectProperties}</dt>
+ * <dd>Selects property maps whose key starts with {@code prefix},
optionally filtered by a
+ * JEXL {@code predicate} expression and projected to a subset of property
names via
+ * {@code selection}.
+ * <pre>{@code
+ * { "method": "selectProperties",
+ * "prefix": "db1.",
+ * "predicate": "owner == 'alice'",
+ * "selection": ["owner", "is_external"] }
+ * → { "db1.table1": { "owner": "alice", "is_external": "true" } }
+ * }</pre></dd>
+ *
+ * <dt>{@code script}</dt>
+ * <dd>Executes a JEXL script via {@link
org.apache.hadoop.hive.metastore.properties.PropertyManager#runScript}.
+ * <pre>{@code
+ * { "method": "script", "source": "select('db1.', null, null)" }
+ * }</pre></dd>
+ *
+ * <dt>{@code echo}</dt>
+ * <dd>Returns the action object unchanged. Useful for health checks and
round-trip testing.</dd>
+ * </dl>
+ *
+ * <h2>Error handling</h2>
+ * <p>{@link org.apache.hadoop.hive.metastore.properties.PropertyException} and
+ * {@code NoSuchObjectException} map to HTTP 400 (Bad Request);
+ * all other exceptions map to HTTP 500 (Internal Server Error).</p>
+ *
+ * <h2>Configuration</h2>
+ * <ul>
+ * <li>{@code MetastoreConf.ConfVars.PROPERTIES_SERVLET_PORT} — listening
port (negative disables the servlet)</li>
+ * <li>{@code MetastoreConf.ConfVars.PROPERTIES_SERVLET_PATH} — URL path
prefix</li>
+ * <li>{@code MetastoreConf.ConfVars.PROPERTIES_SERVLET_AUTH} —
authentication type</li>
+ * </ul>
*/
public class PropertyServlet extends HttpServlet {
/** The common prefix for errors. */
diff --git a/standalone-metastore/pom.xml b/standalone-metastore/pom.xml
index 5e52e03e1d3..806a00bb300 100644
--- a/standalone-metastore/pom.xml
+++ b/standalone-metastore/pom.xml
@@ -89,7 +89,7 @@
<hikaricp.version>4.0.3</hikaricp.version>
<iceberg.version>1.11.0</iceberg.version>
<jackson.version>2.18.6</jackson.version>
- <jexl.version>3.3</jexl.version>
+ <jexl.version>3.7.0</jexl.version>
<javolution.version>5.5.1</javolution.version>
<junit.version>4.13.2</junit.version>
<junit.jupiter.version>5.13.3</junit.jupiter.version>