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