| /* |
| * 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. |
| */ |
| package org.apache.geode.internal; |
| |
| import static org.apache.geode.distributed.ConfigurationProperties.MEMBERSHIP_PORT_RANGE; |
| import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_SHIRO_INIT; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.lang.reflect.Array; |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| |
| import org.apache.geode.InternalGemFireException; |
| import org.apache.geode.distributed.internal.FlowControlParams; |
| import org.apache.geode.internal.net.SocketCreator; |
| import org.apache.geode.internal.security.SecurableCommunicationChannel; |
| import org.apache.geode.internal.util.ArgumentRedactor; |
| |
| /** |
| * Provides an implementation of the {@link Config} interface that implements functionality that all |
| * {@link Config} implementations can share. |
| */ |
| public abstract class AbstractConfig implements Config { |
| |
| private static final String GEM_FIRE_PROPERTIES_USING_DEFAULT_VALUES = |
| "### GemFire Properties using default values ###"; |
| private static final String GEM_FIRE_PROPERTIES_DEFINED_WITH_PREFIX = |
| "### GemFire Properties defined with "; |
| private static final String GEM_FIRE_PROPERTIES_DEFINED_WITH_SUFFIX = " ###"; |
| |
| /** |
| * Use {@link #toLoggerString()} instead. If you need to override this in a subclass, be careful |
| * not to expose any private data or security related values. Fixing bug #48155 by not exposing |
| * all values. |
| */ |
| @Override |
| public String toString() { |
| return super.toString(); |
| } |
| |
| @Override |
| public String toLoggerString() { |
| StringWriter stringWriter = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(stringWriter); |
| printSourceSection(ConfigSource.runtime(), printWriter); |
| printSourceSection(ConfigSource.sysprop(), printWriter); |
| printSourceSection(ConfigSource.api(), printWriter); |
| for (ConfigSource fileSource : getFileSources()) { |
| printSourceSection(fileSource, printWriter); |
| } |
| printSourceSection(ConfigSource.xml(), printWriter); |
| printSourceSection(ConfigSource.launcher(), printWriter); // fix for bug 46653 |
| printSourceSection(null, printWriter); |
| printWriter.close(); |
| return stringWriter.toString(); |
| } |
| |
| /** |
| * Gets the Map of GemFire properties and values from a given ConfigSource |
| * |
| * @return map of GemFire properties and values |
| */ |
| public Map<String, String> getConfigPropsFromSource(ConfigSource source) { |
| Map<String, String> configProps = new HashMap<>(); |
| String[] validAttributeNames = getAttributeNames(); |
| Map<String, ConfigSource> sm = getAttSourceMap(); |
| |
| for (String name : validAttributeNames) { |
| if (source == null && sm.get(name) != null |
| || source != null && !source.equals(sm.get(name))) { |
| continue; |
| } |
| configProps.put(name, getAttribute(name)); |
| } |
| return configProps; |
| } |
| |
| /** |
| * Gets all the GemFire properties defined using file(s) |
| * |
| * @return Map of GemFire properties and values set using property files |
| */ |
| public Map<String, String> getConfigPropsDefinedUsingFiles() { |
| Map<String, String> configProps = new HashMap<>(); |
| for (ConfigSource fileSource : getFileSources()) { |
| configProps.putAll(getConfigPropsFromSource(fileSource)); |
| } |
| return configProps; |
| } |
| |
| @Override |
| public Properties toProperties() { |
| Properties result = new SortedProperties(); |
| String[] attNames = getAttributeNames(); |
| for (String name : attNames) { |
| result.setProperty(name, getAttribute(name)); |
| } |
| return result; |
| } |
| |
| @Override |
| public void toFile(File file) throws IOException { |
| try (FileOutputStream out = new FileOutputStream(file)) { |
| toProperties().store(out, null); |
| } |
| } |
| |
| @Override |
| public boolean sameAs(Config other) { |
| if (this == other) { |
| return true; |
| } |
| if (other == null) { |
| return false; |
| } |
| if (!getClass().equals(other.getClass())) { |
| return false; |
| } |
| String[] validAttributeNames = getAttributeNames(); |
| for (String name : validAttributeNames) { |
| Object thisAtt = getAttributeObject(name); |
| Object otherAtt = other.getAttributeObject(name); |
| if (thisAtt != otherAtt) { |
| if (thisAtt == null) { |
| return false; |
| } else if (thisAtt.getClass().isArray()) { |
| int thisLength = Array.getLength(thisAtt); |
| int otherLength = Array.getLength(otherAtt); |
| if (thisLength != otherLength) { |
| return false; |
| } |
| for (int j = 0; j < thisLength; j++) { |
| Object thisArrObj = Array.get(thisAtt, j); |
| Object otherArrObj = Array.get(otherAtt, j); |
| if (thisArrObj != otherArrObj) { |
| if (thisArrObj == null) { |
| return false; |
| } else if (!thisArrObj.equals(otherArrObj)) { |
| return false; |
| } |
| } |
| } |
| } else if (!thisAtt.equals(otherAtt)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public String getAttribute(String name) { |
| Object result = getAttributeObject(name); |
| if (result instanceof String) { |
| return (String) result; |
| } |
| |
| if (name.equalsIgnoreCase(MEMBERSHIP_PORT_RANGE)) { |
| int[] value = (int[]) result; |
| return "" + value[0] + "-" + value[1]; |
| } |
| |
| if (result.getClass().isArray() && name.startsWith("ssl-")) { |
| return SystemAdmin.join((Object[]) result, ","); |
| } |
| |
| if (result.getClass().isArray()) { |
| return SystemAdmin.join((Object[]) result); |
| } |
| |
| if (result instanceof InetAddress) { |
| InetAddress addr = (InetAddress) result; |
| String addrName; |
| if (addr.isMulticastAddress() || !SocketCreator.resolve_dns) { |
| // on Windows getHostName on mcast addrs takes ~5 seconds |
| addrName = addr.getHostAddress(); |
| } else { |
| addrName = SocketCreator.getHostName(addr); |
| } |
| return addrName; |
| } |
| |
| return result.toString(); |
| } |
| |
| @Override |
| public ConfigSource getAttributeSource(String name) { |
| return getAttSourceMap().get(name); |
| } |
| |
| @Override |
| public void setAttribute(String name, String value, ConfigSource source) { |
| Object attObjectValue; |
| Class valueType = getAttributeType(name); |
| try { |
| if (valueType.equals(String.class)) { |
| attObjectValue = value; |
| } else if (valueType.equals(String[].class)) { |
| attObjectValue = value.split(","); |
| } else if (valueType.equals(Integer.class)) { |
| attObjectValue = Integer.valueOf(value); |
| } else if (valueType.equals(Long.class)) { |
| attObjectValue = Long.valueOf(value); |
| } else if (valueType.equals(Boolean.class)) { |
| attObjectValue = Boolean.valueOf(value); |
| } else if (valueType.equals(File.class)) { |
| attObjectValue = new File(value); |
| } else if (valueType.equals(int[].class)) { |
| int minus = value.indexOf('-'); |
| if (minus <= 0) { |
| throw new IllegalArgumentException( |
| "expected a setting in the form X-Y but found no dash for attribute " + name); |
| } |
| int[] tempValue = new int[2]; |
| tempValue[0] = Integer.valueOf(value.substring(0, minus)); |
| tempValue[1] = Integer.valueOf(value.substring(minus + 1)); |
| attObjectValue = tempValue; |
| } else if (valueType.equals(InetAddress.class)) { |
| try { |
| attObjectValue = InetAddress.getByName(value); |
| } catch (UnknownHostException ex) { |
| throw new IllegalArgumentException( |
| String.format("%s value %s must be a valid host name. %s", |
| name, value, ex.toString())); |
| } |
| } else if (valueType.equals(FlowControlParams.class)) { |
| String[] values = value.split(","); |
| if (values.length != 3) { |
| throw new IllegalArgumentException( |
| String.format("%s value %s must have three elements separated by commas", |
| name, value)); |
| } |
| int allowance; |
| float threshold; |
| int waitTime; |
| try { |
| allowance = Integer.parseInt(values[0].trim()); |
| threshold = Float.valueOf(values[1].trim()); |
| waitTime = Integer.parseInt(values[2].trim()); |
| } catch (NumberFormatException e) { |
| throw new IllegalArgumentException( |
| String.format("%s value %s must be composed of an integer, a float, and an integer", |
| name, value)); |
| } |
| attObjectValue = new FlowControlParams(allowance, threshold, waitTime); |
| } else if (valueType.isArray() |
| && SecurableCommunicationChannel.class.equals(valueType.getComponentType())) { |
| attObjectValue = commaDelimitedStringToSecurableCommunicationChannels(value); |
| } else { |
| throw new InternalGemFireException( |
| String.format("unhandled attribute type %s for %s.", |
| valueType, name)); |
| } |
| } catch (NumberFormatException ex) { |
| throw new IllegalArgumentException(String.format("%s value %s must be a number.", |
| name, value)); |
| } |
| |
| setAttributeObject(name, attObjectValue, source); |
| } |
| |
| @Override |
| public String getAttributeDescription(String name) { |
| checkAttributeName(name); |
| if (!getAttDescMap().containsKey(name)) { |
| throw new InternalGemFireException( |
| String.format("unhandled attribute name %s.", name)); |
| } |
| return (String) getAttDescMap().get(name); |
| } |
| |
| /** |
| * Returns the string to use as the exception message when an attempt is made to set an |
| * unmodifiable attribute. |
| */ |
| protected String _getUnmodifiableMsg(String name) { |
| return String.format("The %s configuration attribute can not be modified.", |
| name); |
| } |
| |
| /** |
| * Returns a map that contains attribute descriptions |
| */ |
| protected abstract Map getAttDescMap(); |
| |
| protected abstract Map<String, ConfigSource> getAttSourceMap(); |
| |
| /** |
| * Set to true if most of the attributes can be modified. Set to false if most of the attributes |
| * are read only. |
| */ |
| protected boolean _modifiableDefault() { |
| return false; |
| } |
| |
| protected void checkAttributeName(String name) { |
| String[] validAttNames = getAttributeNames(); |
| if (!Arrays.asList(validAttNames).contains(name.toLowerCase())) { |
| throw new IllegalArgumentException( |
| String.format("Unknown configuration attribute name %s. Valid attribute names are: %s .", |
| name, SystemAdmin.join(validAttNames))); |
| } |
| } |
| |
| private List<ConfigSource> getFileSources() { |
| ArrayList<ConfigSource> result = new ArrayList<>(); |
| for (ConfigSource cs : getAttSourceMap().values()) { |
| if (cs.getType() == ConfigSource.Type.FILE || cs.getType() == ConfigSource.Type.SECURE_FILE) { |
| if (!result.contains(cs)) { |
| result.add(cs); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private void printSourceSection(ConfigSource source, PrintWriter printWriter) { |
| String[] validAttributeNames = getAttributeNames(); |
| boolean sourceFound = false; |
| Map<String, ConfigSource> sourceMap = getAttSourceMap(); |
| boolean sourceIsSecured = false; |
| if (source != null && source.getType() == ConfigSource.Type.SECURE_FILE) { |
| sourceIsSecured = true; |
| } |
| for (String name : validAttributeNames) { |
| if (source == null) { |
| if (sourceMap.get(name) != null) { |
| continue; |
| } |
| } else if (!source.equals(sourceMap.get(name))) { |
| continue; |
| } |
| if (!sourceFound) { |
| sourceFound = true; |
| if (source == null) { |
| printWriter.println(GEM_FIRE_PROPERTIES_USING_DEFAULT_VALUES); |
| } else { |
| printWriter.println(GEM_FIRE_PROPERTIES_DEFINED_WITH_PREFIX + source.getDescription() |
| + GEM_FIRE_PROPERTIES_DEFINED_WITH_SUFFIX); |
| } |
| } |
| // hide the shiro-init configuration for now. Remove after we can allow customer to specify |
| // shiro.ini file |
| if (name.equals(SECURITY_SHIRO_INIT)) { |
| continue; |
| } |
| |
| String attributeValueToPrint; |
| if (source == null) { |
| // always show defaults values |
| attributeValueToPrint = getAttribute(name); |
| } else if (sourceIsSecured) { |
| // Never show secure sources |
| attributeValueToPrint = ArgumentRedactor.redacted; |
| } else { |
| // Otherwise, redact based on the key string |
| attributeValueToPrint = |
| ArgumentRedactor.redactArgumentIfNecessary(name, getAttribute(name)); |
| } |
| printWriter.print(name); |
| printWriter.print('='); |
| printWriter.println(attributeValueToPrint); |
| } |
| } |
| |
| private SecurableCommunicationChannel[] commaDelimitedStringToSecurableCommunicationChannels( |
| final String tokenizeString) { |
| StringTokenizer stringTokenizer = new StringTokenizer(tokenizeString, ","); |
| SecurableCommunicationChannel[] returnArray = |
| new SecurableCommunicationChannel[stringTokenizer.countTokens()]; |
| for (int i = 0; i < returnArray.length; i++) { |
| String name = stringTokenizer.nextToken(); |
| try { |
| returnArray[i] = SecurableCommunicationChannel.getEnum(name); |
| } catch (Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| return returnArray; |
| } |
| |
| /** |
| * This class was added to fix bug 39382. It does this be overriding "keys" which is used by the |
| * store0 implementation of Properties. |
| */ |
| protected static class SortedProperties extends Properties { |
| |
| private static final long serialVersionUID = 7156507110684631135L; |
| |
| @Override |
| public Enumeration keys() { |
| // the TreeSet gets the sorting we desire but is only safe |
| // because the keys in this context are always String which is Comparable |
| return Collections.enumeration(new TreeSet(keySet())); |
| } |
| } |
| } |