001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017-2018 microBean.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.configuration.spi;
018
019import java.io.Serializable;
020
021import java.util.Collections;
022import java.util.Map;
023import java.util.HashSet;
024import java.util.Properties;
025import java.util.Set;
026
027import org.microbean.configuration.api.ConfigurationValue;
028
029/**
030 * An {@link AbstractConfiguration} that houses {@linkplain
031 * System#getProperties() System properties} and hence, by definition,
032 * minimally {@linkplain ConfigurationValue#specificity() specific}
033 * {@link ConfigurationValue}s representing them.
034 *
035 * @author <a href="https://about.me/lairdnelson"
036 * target="_parent">Laird Nelson</a>
037 */
038public final class SystemPropertiesConfiguration extends AbstractConfiguration implements Ranked, Serializable {
039
040
041  /*
042   * Static fields.
043   */
044
045
046  /**
047   * The version of this class for {@linkplain Serializable
048   * serialization} purposes.
049   */
050  private static final long serialVersionUID = 1L;
051
052  /**
053   * A {@link Set} of {@linkplain System#getProperties() System
054   * properties} that the Java Language Specification guarantees will
055   * exist on all platforms.
056   *
057   * <p>This field is never {@code null}.</p>
058   *
059   * @see System#getProperties()
060   */
061  private static final Set<String> systemPropertiesGuaranteedToExist = new HashSet<>(29, 1F);
062
063  static {
064    // See https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getProperties--
065    systemPropertiesGuaranteedToExist.add("java.version");
066    systemPropertiesGuaranteedToExist.add("java.vendor"); // Java Runtime Environment vendor
067    systemPropertiesGuaranteedToExist.add("java.vendor.url"); // Java vendor URL
068    systemPropertiesGuaranteedToExist.add("java.home"); // Java installation directory
069    systemPropertiesGuaranteedToExist.add("java.vm.specification.version"); // Java Virtual Machine specification version
070    systemPropertiesGuaranteedToExist.add("java.vm.specification.vendor"); // Java Virtual Machine specification vendor
071    systemPropertiesGuaranteedToExist.add("java.vm.specification.name"); // Java Virtual Machine specification name
072    systemPropertiesGuaranteedToExist.add("java.vm.version"); // Java Virtual Machine implementation version
073    systemPropertiesGuaranteedToExist.add("java.vm.vendor"); // Java Virtual Machine implementation vendor
074    systemPropertiesGuaranteedToExist.add("java.vm.name"); // Java Virtual Machine implementation name
075    systemPropertiesGuaranteedToExist.add("java.specification.version"); // Java Runtime Environment specification version
076    systemPropertiesGuaranteedToExist.add("java.specification.vendor"); // Java Runtime Environment specification vendor
077    systemPropertiesGuaranteedToExist.add("java.specification.name"); // Java Runtime Environment specification name
078    systemPropertiesGuaranteedToExist.add("java.class.version"); // Java class format version number
079    systemPropertiesGuaranteedToExist.add("java.class.path"); // Java class path
080    systemPropertiesGuaranteedToExist.add("java.library.path"); // List of paths to search when loading libraries
081    systemPropertiesGuaranteedToExist.add("java.io.tmpdir"); // Default temp file path
082    systemPropertiesGuaranteedToExist.add("java.compiler"); // Name of JIT compiler to use
083    systemPropertiesGuaranteedToExist.add("java.ext.dirs"); // Path of extension directory or directories
084    systemPropertiesGuaranteedToExist.add("os.name"); // Operating system name
085    systemPropertiesGuaranteedToExist.add("os.arch"); // Operating system architecture
086    systemPropertiesGuaranteedToExist.add("os.version"); // Operating system version
087    systemPropertiesGuaranteedToExist.add("file.separator"); // File separator ("/" on UNIX)
088    systemPropertiesGuaranteedToExist.add("path.separator"); // Path separator (":" on UNIX)
089    systemPropertiesGuaranteedToExist.add("line.separator"); // Line separator ("\n" on UNIX)
090    systemPropertiesGuaranteedToExist.add("user.name"); // User's account name
091    systemPropertiesGuaranteedToExist.add("user.home"); // User's home directory
092    systemPropertiesGuaranteedToExist.add("user.dir"); // User's current working directory
093  }
094
095
096  /*
097   * Constructors.
098   */
099  
100
101  /**
102   * Creates a new {@link SystemPropertiesConfiguration}.
103   */
104  public SystemPropertiesConfiguration() {
105    super();
106  }
107
108
109  /*
110   * Instance methods.
111   */
112
113  
114  @Override
115  public final int getRank() {
116    return 400;
117  }
118
119  /**
120   * Returns a {@link ConfigurationValue} representing the {@linkplain
121   * System#getProperty(String, String) System property} identified by
122   * the supplied {@code name}, or {@code null}.
123   *
124   * <p>The {@link ConfigurationValue} returned will be marked as
125   * {@linkplain ConfigurationValue#isAuthoritative() authoritative}
126   * if it is one of the {@linkplain System#getProperties() System
127   * properties guaranteed by the Java Language Specification to exist
128   * on every available Java platform}.</p>
129   *
130   * @param coordinates the configuration coordinates in effect for
131   * the current request; may be {@code null}
132   *
133   * @param name the name of the configuration property for which to
134   * return a {@link ConfigurationValue}; may be {@code null}
135   *
136   * @return a {@link ConfigurationValue}, or {@code null}
137   */
138  @Override
139  public final ConfigurationValue getValue(final Map<String, String> coordinates, final String name) {
140    ConfigurationValue returnValue = null;
141    if (name != null) {
142      final String propertyValue = System.getProperty(name);
143      if (propertyValue != null) {
144        returnValue = new ConfigurationValue(this, null /* deliberately null coordinates */, name, propertyValue, this.isAuthoritative(name));
145      }
146    }
147    return returnValue;
148  }
149
150  /**
151   * Returns a {@link Set} of the names of all {@link
152   * ConfigurationValue}s that might be returned by this {@link
153   * Configuration}.
154   *
155   * <p>This method never returns {@code null}.</p>
156   *
157   * <p>Overrides of this method must not return {@code null}.</p>
158   *
159   * <p>This implementation returns an immutable representation of the
160   * equivalent of {@link System#getProperties()
161   * System.getProperties().stringPropertyNames()}.</p>
162   *
163   * @return a non-{@code null} {@link Set} of names
164   */
165  @Override
166  public final Set<String> getNames() {
167    final Properties properties = System.getProperties();
168    assert properties != null; // by contract
169    assert !properties.isEmpty(); // by contract
170    final Set<String> returnValue = Collections.unmodifiableSet(properties.stringPropertyNames());
171    return returnValue;
172  }
173  
174  /**
175   * Returns {@code true} if the supplied {@code name} is not {@code
176   * null} and is one of the {@linkplain System#getProperties() System
177   * properties guaranteed by the Java Language Specification to exist
178   * on every available Java platform}.
179   *
180   * @param name the name of a {@linkplain System#getProperty(String,
181   * String) System property}; may be {@code null} in which case
182   * {@code false} will be returned
183   *
184   * @return {@code true} if any value that this {@link
185   * SystemPropertiesConfiguration} produces for the supplied {@code
186   * name} is to be regarded as the authoritative value; {@code false}
187   * otherwise
188   */
189  protected boolean isAuthoritative(final String name) {
190    return name != null && systemPropertiesGuaranteedToExist.contains(name);
191  }
192
193  @Override
194  public final int hashCode() {
195    return System.getProperties().hashCode();
196  }
197
198  @Override
199  public final boolean equals(final Object other) {
200    return other instanceof SystemPropertiesConfiguration;
201  }
202
203  @Override
204  public String toString() {
205    return System.getProperties().toString();
206  }
207  
208}