Skip to content

Commit c5a8e9a

Browse files
committed
Add RESTOperationsBuilder for injectable table and view operations
1 parent b211c8b commit c5a8e9a

File tree

5 files changed

+190
-17
lines changed

5 files changed

+190
-17
lines changed

core/src/main/java/org/apache/iceberg/rest/RESTCatalog.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.Map;
2525
import java.util.Set;
26+
import java.util.function.BiFunction;
2627
import java.util.function.Function;
2728
import org.apache.iceberg.CatalogProperties;
2829
import org.apache.iceberg.PartitionSpec;
@@ -39,6 +40,7 @@
3940
import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
4041
import org.apache.iceberg.exceptions.NoSuchNamespaceException;
4142
import org.apache.iceberg.hadoop.Configurable;
43+
import org.apache.iceberg.io.FileIO;
4244
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
4345
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
4446
import org.apache.iceberg.view.View;
@@ -63,13 +65,28 @@ public RESTCatalog() {
6365
}
6466

6567
public RESTCatalog(Function<Map<String, String>, RESTClient> clientBuilder) {
66-
this(SessionCatalog.SessionContext.createEmpty(), clientBuilder);
68+
this(SessionCatalog.SessionContext.createEmpty(), clientBuilder, null, null);
6769
}
6870

6971
public RESTCatalog(
7072
SessionCatalog.SessionContext context,
7173
Function<Map<String, String>, RESTClient> clientBuilder) {
72-
this.sessionCatalog = new RESTSessionCatalog(clientBuilder, null);
74+
this(context, clientBuilder, null, null);
75+
}
76+
77+
public RESTCatalog(
78+
Function<Map<String, String>, RESTClient> clientBuilder,
79+
BiFunction<SessionCatalog.SessionContext, Map<String, String>, FileIO> ioBuilder,
80+
RESTOperationsBuilder operationsBuilder) {
81+
this(SessionCatalog.SessionContext.createEmpty(), clientBuilder, ioBuilder, operationsBuilder);
82+
}
83+
84+
public RESTCatalog(
85+
SessionCatalog.SessionContext context,
86+
Function<Map<String, String>, RESTClient> clientBuilder,
87+
BiFunction<SessionCatalog.SessionContext, Map<String, String>, FileIO> ioBuilder,
88+
RESTOperationsBuilder operationsBuilder) {
89+
this.sessionCatalog = new RESTSessionCatalog(clientBuilder, ioBuilder, operationsBuilder);
7390
this.delegate = sessionCatalog.asCatalog(context);
7491
this.nsDelegate = (SupportsNamespaces) delegate;
7592
this.context = context;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.rest;
20+
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.function.Supplier;
25+
import org.apache.iceberg.MetadataUpdate;
26+
import org.apache.iceberg.TableMetadata;
27+
import org.apache.iceberg.io.FileIO;
28+
import org.apache.iceberg.view.ViewMetadata;
29+
30+
/**
31+
* A builder interface for creating {@link RESTTableOperations} and {@link RESTViewOperations}
32+
* instances for REST catalogs.
33+
*
34+
* <p>This interface allows custom implementations of table and view operations to be injected into
35+
* {@link RESTSessionCatalog} and {@link RESTCatalog}, enabling extensibility for specialized use
36+
* cases.
37+
*
38+
* <p>Example usage:
39+
*
40+
* <pre>
41+
* RESTOperationsBuilder customBuilder = new RESTOperationsBuilder() {
42+
* {@literal @}Override
43+
* public RESTTableOperations createTableOperations(
44+
* RESTClient client,
45+
* String path,
46+
* Supplier&lt;Map&lt;String, String&gt;&gt; headers,
47+
* FileIO io,
48+
* TableMetadata current,
49+
* Set&lt;Endpoint&gt; endpoints) {
50+
* return new CustomRESTTableOperations(client, path, headers, io, current, endpoints);
51+
* }
52+
*
53+
* {@literal @}Override
54+
* public RESTViewOperations createViewOperations(
55+
* RESTClient client,
56+
* String path,
57+
* Supplier&lt;Map&lt;String, String&gt;&gt; headers,
58+
* ViewMetadata current,
59+
* Set&lt;Endpoint&gt; endpoints) {
60+
* return new CustomRESTViewOperations(client, path, headers, current, endpoints);
61+
* }
62+
* };
63+
*
64+
* RESTSessionCatalog catalog = new RESTSessionCatalog(clientBuilder, ioBuilder, customBuilder);
65+
* </pre>
66+
*/
67+
public interface RESTOperationsBuilder {
68+
69+
/**
70+
* Create a new {@link RESTTableOperations} instance for simple table operations.
71+
*
72+
* <p>The default implementation creates a standard {@link RESTTableOperations} instance.
73+
*
74+
* @param client the REST client to use for communicating with the catalog server
75+
* @param path the REST path for the table
76+
* @param headers a supplier for additional HTTP headers to include in requests
77+
* @param io the FileIO implementation for reading and writing table metadata and data files
78+
* @param current the current table metadata
79+
* @param endpoints the set of supported REST endpoints
80+
* @return a new RESTTableOperations instance
81+
*/
82+
default RESTTableOperations createTableOperations(
83+
RESTClient client,
84+
String path,
85+
Supplier<Map<String, String>> headers,
86+
FileIO io,
87+
TableMetadata current,
88+
Set<Endpoint> endpoints) {
89+
return new RESTTableOperations(client, path, headers, io, current, endpoints);
90+
}
91+
92+
/**
93+
* Create a new {@link RESTTableOperations} instance for transaction-based operations (create or
94+
* replace).
95+
*
96+
* <p>This method is used when creating tables or replacing table metadata within a transaction.
97+
* The default implementation creates a standard {@link RESTTableOperations} instance.
98+
*
99+
* @param client the REST client to use for communicating with the catalog server
100+
* @param path the REST path for the table
101+
* @param headers a supplier for additional HTTP headers to include in requests
102+
* @param io the FileIO implementation for reading and writing table metadata and data files
103+
* @param updateType the type of update being performed (CREATE, REPLACE, or SIMPLE)
104+
* @param createChanges the list of metadata updates to apply during table creation or replacement
105+
* @param current the current table metadata (may be null for CREATE operations)
106+
* @param endpoints the set of supported REST endpoints
107+
* @return a new RESTTableOperations instance
108+
*/
109+
default RESTTableOperations createTableOperationsForTransaction(
110+
RESTClient client,
111+
String path,
112+
Supplier<Map<String, String>> headers,
113+
FileIO io,
114+
RESTTableOperations.UpdateType updateType,
115+
List<MetadataUpdate> createChanges,
116+
TableMetadata current,
117+
Set<Endpoint> endpoints) {
118+
return new RESTTableOperations(
119+
client, path, headers, io, updateType, createChanges, current, endpoints);
120+
}
121+
122+
/**
123+
* Create a new {@link RESTViewOperations} instance.
124+
*
125+
* <p>The default implementation creates a standard {@link RESTViewOperations} instance.
126+
*
127+
* @param client the REST client to use for communicating with the catalog server
128+
* @param path the REST path for the view
129+
* @param headers a supplier for additional HTTP headers to include in requests
130+
* @param current the current view metadata
131+
* @param endpoints the set of supported REST endpoints
132+
* @return a new RESTViewOperations instance
133+
*/
134+
default RESTViewOperations createViewOperations(
135+
RESTClient client,
136+
String path,
137+
Supplier<Map<String, String>> headers,
138+
ViewMetadata current,
139+
Set<Endpoint> endpoints) {
140+
return new RESTViewOperations(client, path, headers, current, endpoints);
141+
}
142+
143+
/** Default {@link RESTOperationsBuilder} instance. */
144+
RESTOperationsBuilder DEFAULT = new RESTOperationsBuilder() {};
145+
}

core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public class RESTSessionCatalog extends BaseViewSessionCatalog
146146

147147
private final Function<Map<String, String>, RESTClient> clientBuilder;
148148
private final BiFunction<SessionContext, Map<String, String>, FileIO> ioBuilder;
149+
private final RESTOperationsBuilder operationsBuilder;
149150
private FileIOTracker fileIOTracker = null;
150151
private AuthSession catalogAuth = null;
151152
private AuthManager authManager;
@@ -167,15 +168,25 @@ public RESTSessionCatalog() {
167168
.uri(config.get(CatalogProperties.URI))
168169
.withHeaders(RESTUtil.configHeaders(config))
169170
.build(),
171+
null,
170172
null);
171173
}
172174

173175
public RESTSessionCatalog(
174176
Function<Map<String, String>, RESTClient> clientBuilder,
175177
BiFunction<SessionContext, Map<String, String>, FileIO> ioBuilder) {
178+
this(clientBuilder, ioBuilder, null);
179+
}
180+
181+
public RESTSessionCatalog(
182+
Function<Map<String, String>, RESTClient> clientBuilder,
183+
BiFunction<SessionContext, Map<String, String>, FileIO> ioBuilder,
184+
RESTOperationsBuilder operationsBuilder) {
176185
Preconditions.checkNotNull(clientBuilder, "Invalid client builder: null");
177186
this.clientBuilder = clientBuilder;
178187
this.ioBuilder = ioBuilder;
188+
this.operationsBuilder =
189+
operationsBuilder != null ? operationsBuilder : RESTOperationsBuilder.DEFAULT;
179190
}
180191

181192
@Override
@@ -450,7 +461,7 @@ public Table loadTable(SessionContext context, TableIdentifier identifier) {
450461

451462
RESTClient tableClient = client.withAuthSession(tableSession);
452463
RESTTableOperations ops =
453-
new RESTTableOperations(
464+
operationsBuilder.createTableOperations(
454465
tableClient,
455466
paths.table(finalIdentifier),
456467
Map::of,
@@ -529,7 +540,7 @@ public Table registerTable(
529540
AuthSession tableSession = authManager.tableSession(ident, tableConf, contextualSession);
530541
RESTClient tableClient = client.withAuthSession(tableSession);
531542
RESTTableOperations ops =
532-
new RESTTableOperations(
543+
operationsBuilder.createTableOperations(
533544
tableClient,
534545
paths.table(ident),
535546
Map::of,
@@ -788,7 +799,7 @@ public Table create() {
788799
AuthSession tableSession = authManager.tableSession(ident, tableConf, contextualSession);
789800
RESTClient tableClient = client.withAuthSession(tableSession);
790801
RESTTableOperations ops =
791-
new RESTTableOperations(
802+
operationsBuilder.createTableOperations(
792803
tableClient,
793804
paths.table(ident),
794805
Map::of,
@@ -815,7 +826,7 @@ public Transaction createTransaction() {
815826

816827
RESTClient tableClient = client.withAuthSession(tableSession);
817828
RESTTableOperations ops =
818-
new RESTTableOperations(
829+
operationsBuilder.createTableOperationsForTransaction(
819830
tableClient,
820831
paths.table(ident),
821832
Map::of,
@@ -878,7 +889,7 @@ public Transaction replaceTransaction() {
878889

879890
RESTClient tableClient = client.withAuthSession(tableSession);
880891
RESTTableOperations ops =
881-
new RESTTableOperations(
892+
operationsBuilder.createTableOperationsForTransaction(
882893
tableClient,
883894
paths.table(ident),
884895
Map::of,
@@ -1154,7 +1165,7 @@ public View loadView(SessionContext context, TableIdentifier identifier) {
11541165
ViewMetadata metadata = response.metadata();
11551166

11561167
RESTViewOperations ops =
1157-
new RESTViewOperations(
1168+
operationsBuilder.createViewOperations(
11581169
client.withAuthSession(tableSession),
11591170
paths.view(identifier),
11601171
Map::of,
@@ -1333,7 +1344,7 @@ public View create() {
13331344
Map<String, String> tableConf = response.config();
13341345
AuthSession tableSession = authManager.tableSession(identifier, tableConf, contextualSession);
13351346
RESTViewOperations ops =
1336-
new RESTViewOperations(
1347+
operationsBuilder.createViewOperations(
13371348
client.withAuthSession(tableSession),
13381349
paths.view(identifier),
13391350
Map::of,
@@ -1424,7 +1435,7 @@ private View replace(LoadViewResponse response) {
14241435
AuthSession contextualSession = authManager.contextualSession(context, catalogAuth);
14251436
AuthSession tableSession = authManager.tableSession(identifier, tableConf, contextualSession);
14261437
RESTViewOperations ops =
1427-
new RESTViewOperations(
1438+
operationsBuilder.createViewOperations(
14281439
client.withAuthSession(tableSession),
14291440
paths.view(identifier),
14301441
Map::of,

core/src/main/java/org/apache/iceberg/rest/RESTTableOperations.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
import org.apache.iceberg.rest.responses.LoadTableResponse;
4545
import org.apache.iceberg.util.LocationUtil;
4646

47-
class RESTTableOperations implements TableOperations {
47+
public class RESTTableOperations implements TableOperations {
4848
private static final String METADATA_FOLDER_NAME = "metadata";
4949

50-
enum UpdateType {
50+
public enum UpdateType {
5151
CREATE,
5252
REPLACE,
5353
SIMPLE
@@ -63,7 +63,7 @@ enum UpdateType {
6363
private UpdateType updateType;
6464
private TableMetadata current;
6565

66-
RESTTableOperations(
66+
public RESTTableOperations(
6767
RESTClient client,
6868
String path,
6969
Supplier<Map<String, String>> headers,
@@ -73,7 +73,7 @@ enum UpdateType {
7373
this(client, path, headers, io, UpdateType.SIMPLE, Lists.newArrayList(), current, endpoints);
7474
}
7575

76-
RESTTableOperations(
76+
public RESTTableOperations(
7777
RESTClient client,
7878
String path,
7979
Supplier<Map<String, String>> headers,

core/src/main/java/org/apache/iceberg/rest/RESTViewOperations.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
import org.apache.iceberg.view.ViewMetadata;
3030
import org.apache.iceberg.view.ViewOperations;
3131

32-
class RESTViewOperations implements ViewOperations {
32+
public class RESTViewOperations implements ViewOperations {
3333
private final RESTClient client;
3434
private final String path;
3535
private final Supplier<Map<String, String>> headers;
3636
private final Set<Endpoint> endpoints;
3737
private ViewMetadata current;
3838

39-
RESTViewOperations(
39+
public RESTViewOperations(
4040
RESTClient client,
4141
String path,
4242
Supplier<Map<String, String>> headers,
@@ -79,7 +79,7 @@ public void commit(ViewMetadata base, ViewMetadata metadata) {
7979
updateCurrentMetadata(response);
8080
}
8181

82-
private ViewMetadata updateCurrentMetadata(LoadViewResponse response) {
82+
protected ViewMetadata updateCurrentMetadata(LoadViewResponse response) {
8383
if (!Objects.equals(current.metadataFileLocation(), response.metadataLocation())) {
8484
this.current = response.metadata();
8585
}

0 commit comments

Comments
 (0)