xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigMerge.java (revision d6df19e1b22784c78f567cf74c42f18e3901b900)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20  /*
21  * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
22  */
23 package org.opengrok.indexer.configuration;
24 
25 import java.beans.IntrospectionException;
26 import java.beans.PropertyDescriptor;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.PrintStream;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Modifier;
34 import java.text.ParseException;
35 import org.opengrok.indexer.util.Getopt;
36 
37 /**
38  * Merge 2 config files together. More precisely, take the 1st as a base and
39  * set all properties from the 2nd in it.
40  *
41  * @author Vladimir Kotal
42  */
43 public class ConfigMerge {
44 
45     private static final String NAME = "ConfigMerge";
46 
ConfigMerge()47     private ConfigMerge() {
48     }
49 
50     /**
51      * Merge base and new configuration.
52      * @param cfgBase base configuration
53      * @param cfgNew new configuration, will receive properties from the base configuration
54      * @throws Exception exception
55      */
merge(Configuration cfgBase, Configuration cfgNew)56     public static void merge(Configuration cfgBase, Configuration cfgNew) throws Exception {
57         Configuration cfgDefault = new Configuration();
58 
59         // Basic strategy: take all non-static/transient fields that have a setter
60         // from cfgBase that are not of default value and set them to cfgNew.
61         for (Field field : cfgBase.getClass().getDeclaredFields()) {
62             String fieldName = field.getName();
63             int modifiers = field.getModifiers();
64             if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers) ||
65                 Modifier.isFinal(modifiers)) {
66                 continue;
67             }
68             PropertyDescriptor desc;
69             try {
70                 desc = new PropertyDescriptor(fieldName, Configuration.class);
71             } catch (IntrospectionException ex) {
72                 throw new Exception("cannot get property descriptor for '" + fieldName + "'");
73             }
74 
75             Method setter = desc.getWriteMethod();
76             if (setter == null) {
77                 throw new Exception("no setter for '" + fieldName + "'");
78             }
79 
80             Method getter = desc.getReadMethod();
81             if (getter == null) {
82                 throw new Exception("no getter for '" + fieldName + "'");
83             }
84 
85             try {
86                 Object obj = getter.invoke(cfgBase);
87                 if ((obj == null && getter.invoke(cfgDefault) == null) ||
88                     obj.equals(getter.invoke(cfgDefault))) {
89                         continue;
90                 }
91             } catch (Exception ex) {
92                 throw new Exception("failed to invoke getter for " + fieldName + ": " + ex);
93             }
94 
95             try {
96                 setter.invoke(cfgNew, getter.invoke(cfgBase));
97             } catch (Exception ex) {
98                 throw new Exception("failed to invoke setter for '" + fieldName + "'");
99             }
100         }
101     }
102 
main(String[] argv)103     public static void main(String[] argv) {
104 
105         Getopt getopt = new Getopt(argv, "h?");
106 
107         try {
108             getopt.parse();
109         } catch (ParseException ex) {
110             System.err.println(NAME + ": " + ex.getMessage());
111             bUsage(System.err);
112             System.exit(1);
113         }
114 
115         int cmd;
116         getopt.reset();
117         while ((cmd = getopt.getOpt()) != -1) {
118             switch (cmd) {
119                 case '?':
120                 case 'h':
121                     aUsage(System.out);
122                     System.exit(0);
123                     break;
124                 default:
125                     System.err.println("Internal Error - Not implemented option: " + (char) cmd);
126                     bUsage(System.err);
127                     System.exit(1);
128                     break;
129             }
130         }
131 
132         int optind = getopt.getOptind();
133         if (optind < 0 || argv.length - optind != 2) {
134             aUsage(System.err);
135             System.exit(1);
136         }
137 
138         Configuration cfgBase = null;
139         try {
140             cfgBase = Configuration.read(new File(argv[optind]));
141         } catch (IOException ex) {
142             System.err.println("cannot read base file " + argv[optind] + ":" + ex);
143             System.exit(1);
144         }
145 
146         Configuration cfgNew = null;
147         try {
148             cfgNew = Configuration.read(new File(argv[optind + 1]));
149         } catch (IOException ex) {
150             System.err.println("cannot read file " + argv[optind + 1] + ":" + ex);
151             System.exit(1);
152         }
153 
154         try {
155             merge(cfgBase, cfgNew);
156         } catch (Exception ex) {
157             System.err.print(ex);
158             System.exit(1);
159         }
160 
161         // Write the resulting XML representation to standard output.
162         OutputStream os = System.out;
163         cfgNew.encodeObject(os);
164     }
165 
aUsage(PrintStream out)166     private static void aUsage(PrintStream out) {
167         out.println("Usage:");
168         out.println(NAME + " [-h] <config_file_base> <config_file_new>");
169         out.println();
170         out.println("OPTIONS:");
171         out.println("Help");
172         out.println("-?                   print this help message");
173         out.println("-h                   print this help message");
174         out.println();
175     }
176 
bUsage(PrintStream out)177     private static void bUsage(PrintStream out) {
178         out.println("Maybe try to run " + NAME + " -h");
179     }
180 }
181