Skip to content

Commit c002328

Browse files
authored
GH-3298: Support unified file based configurations for CLI (#3304)
1 parent 44cb94b commit c002328

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

parquet-cli/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Usage: parquet [options] [command] [command options]
137137
### Configuration Options
138138

139139
- `--conf` or `--property`: Set any configuration property in format `key=value`. Can be specified multiple times.
140+
- `--config-file`: Path to a configuration file (`.properties` or `.xml` format).
140141

141142
Examples:
142143
```bash
@@ -147,4 +148,7 @@ parquet convert input.avro -o output.parquet --conf parquet.avro.write-old-list-
147148
# Multiple options
148149
parquet convert-csv input.csv -o output.parquet --schema schema.avsc --conf parquet.avro.write-parquet-uuid=true --conf parquet.avro.write-old-list-structure=false
149150

151+
# Using config file
152+
parquet convert input.avro -o output.parquet --config-file config.properties
153+
150154
```

parquet-cli/src/main/java/org/apache/parquet/cli/Main.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@
2525
import com.beust.jcommander.Parameters;
2626
import com.google.common.annotations.VisibleForTesting;
2727
import com.google.common.collect.ImmutableSet;
28+
import java.io.FileInputStream;
29+
import java.io.InputStream;
2830
import java.util.List;
31+
import java.util.Properties;
2932
import java.util.Set;
3033
import org.apache.commons.logging.LogFactory;
3134
import org.apache.hadoop.conf.Configurable;
3235
import org.apache.hadoop.conf.Configuration;
3336
import org.apache.hadoop.conf.Configured;
37+
import org.apache.hadoop.fs.Path;
3438
import org.apache.hadoop.util.Tool;
3539
import org.apache.hadoop.util.ToolRunner;
3640
import org.apache.log4j.Level;
@@ -73,6 +77,11 @@ public class Main extends Configured implements Tool {
7377
description = "Set a configuration property (format: key=value). Can be specified multiple times.")
7478
private List<String> confProperties;
7579

80+
@Parameter(
81+
names = {"--config-file"},
82+
description = "Path to a configuration file (properties or Hadoop XML format).")
83+
private String configFilePath;
84+
7685
@VisibleForTesting
7786
@Parameter(names = "--dollar-zero", description = "A way for the runtime path to be passed in", hidden = true)
7887
String programName = DEFAULT_PROGRAM_NAME;
@@ -172,6 +181,24 @@ public int run(String[] args) throws Exception {
172181
// If the command does not support the configs, it would simply be ignored.
173182
if (command instanceof Configurable) {
174183
Configuration merged = new Configuration(getConf());
184+
185+
if (configFilePath != null) {
186+
try {
187+
if (isXmlConfigFile(configFilePath)) {
188+
loadXmlConfiguration(merged, configFilePath);
189+
} else if (isPropertiesConfigFile(configFilePath)) {
190+
loadPropertiesConfiguration(merged, configFilePath);
191+
} else {
192+
throw new IllegalArgumentException(
193+
"Unsupported config file format. Only .xml and .properties files are supported: "
194+
+ configFilePath);
195+
}
196+
} catch (Exception e) {
197+
throw new IllegalArgumentException(
198+
"Failed to load config file '" + configFilePath + "': " + e.getMessage(), e);
199+
}
200+
}
201+
175202
if (confProperties != null) {
176203
for (String prop : confProperties) {
177204
String[] parts = prop.split("=", 2);
@@ -218,4 +245,27 @@ public static void main(String[] args) throws Exception {
218245
int rc = ToolRunner.run(new Configuration(), new Main(console), args);
219246
System.exit(rc);
220247
}
248+
249+
private boolean isXmlConfigFile(String filePath) {
250+
return filePath.toLowerCase().endsWith(".xml");
251+
}
252+
253+
private boolean isPropertiesConfigFile(String filePath) {
254+
String lowerPath = filePath.toLowerCase();
255+
return lowerPath.endsWith(".properties");
256+
}
257+
258+
private void loadXmlConfiguration(Configuration config, String filePath) {
259+
config.addResource(new Path(filePath));
260+
console.debug("Loaded XML configuration from file: {}", filePath);
261+
}
262+
263+
private void loadPropertiesConfiguration(Configuration config, String filePath) throws Exception {
264+
try (InputStream in = new FileInputStream(filePath)) {
265+
Properties props = new Properties();
266+
props.load(in);
267+
props.forEach((key, value) -> config.set(key.toString(), value.toString()));
268+
console.debug("Loaded properties configuration from file: {}", filePath);
269+
}
270+
}
221271
}

parquet-cli/src/test/java/org/apache/parquet/cli/MainTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.parquet.cli;
2020

21+
import java.io.File;
22+
import java.io.FileWriter;
2123
import org.apache.hadoop.conf.Configuration;
2224
import org.apache.hadoop.util.ToolRunner;
2325
import org.junit.Assert;
@@ -31,4 +33,38 @@ public void mainTest() throws Exception {
3133
ToolRunner.run(new Configuration(), new Main(LoggerFactory.getLogger(MainTest.class)), new String[] {});
3234
Assert.assertTrue("we simply verify there are no errors here", true);
3335
}
36+
37+
@Test
38+
public void testConfigFileLoading() throws Exception {
39+
File configFile = File.createTempFile("test-config", ".properties");
40+
configFile.deleteOnExit();
41+
42+
try (FileWriter writer = new FileWriter(configFile)) {
43+
writer.write("test.key=test.value\n");
44+
}
45+
46+
try {
47+
new Main(LoggerFactory.getLogger(MainTest.class))
48+
.run(new String[] {"--config-file", configFile.getAbsolutePath(), "help"});
49+
Assert.assertTrue("Config file loading should not throw exception", true);
50+
} catch (IllegalArgumentException e) {
51+
Assert.fail("Config file loading failed: " + e.getMessage());
52+
}
53+
}
54+
55+
@Test
56+
public void testLocalPropertiesFile() throws Exception {
57+
String configFile = getClass().getResource("/test-config.properties").getPath();
58+
ToolRunner.run(new Configuration(), new Main(LoggerFactory.getLogger(MainTest.class)), new String[] {
59+
"--config-file", configFile, "version"
60+
});
61+
}
62+
63+
@Test
64+
public void testLocalXmlFile() throws Exception {
65+
String configFile = getClass().getResource("/test-config.xml").getPath();
66+
ToolRunner.run(new Configuration(), new Main(LoggerFactory.getLogger(MainTest.class)), new String[] {
67+
"--config-file", configFile, "version"
68+
});
69+
}
3470
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
test.key=test.value
19+
parquet.avro.write-old-list-structure=false
20+
parquet.compression=SNAPPY
21+
parquet.block.size=134217728
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<configuration>
21+
<property>
22+
<name>test.key</name>
23+
<value>test.value</value>
24+
</property>
25+
26+
<property>
27+
<name>parquet.avro.write-old-list-structure</name>
28+
<value>false</value>
29+
</property>
30+
31+
<property>
32+
<name>parquet.compression</name>
33+
<value>SNAPPY</value>
34+
</property>
35+
36+
</configuration>

0 commit comments

Comments
 (0)