Java tutorial
/* * Copyright (C) NetStruxr, Inc. All rights reserved. * * This software is published under the terms of the NetStruxr * Public Software License version 0.5, a copy of which has been * included with this distribution in the LICENSE.NPL file. */ package er.extensions.foundation; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.math.BigDecimal; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Stack; import java.util.TreeMap; import java.util.TreeSet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import com.webobjects.appserver.WOApplication; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSBundle; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSForwardException; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSNotificationCenter; import com.webobjects.foundation.NSProperties; import com.webobjects.foundation.NSPropertyListSerialization; import er.extensions.appserver.ERXApplication; import er.extensions.crypting.ERXCrypto; import er.extensions.net.ERXTcpIp; /** * <div class="en"> * Collection of simple utility methods used to get and set properties * in the system properties. The only reason this class is needed is * because all of the methods in NSProperties have been deprecated. * This is a wee bit annoying. The usual method is to have a method * like <code>getBoolean</code> off of Boolean which would resolve * the System property as a Boolean object. * * Properties can be set in all the following places: * <ul> * <li>Properties in a bundle Resources directory</li> * <li>Properties.dev in a bundle Resources directory</li> * <li>Properties.username in a bundle Resources directory </li> * <li>~/Library/WebObjects.properties file</li> * <li>in the eclipse launcher or on the command-line</li> * </ul> * </div> * * <div class="ja"> * ????? * ??????????NSProperties????????? * ?? Boolean ?? <code>getBoolean</code> ?????????? * </div> * * @property er.extensions.ERXProperties.RetainDefaultsEnabled * @property NSProperties.useLoadtimeAppSpecifics Default is true. * * TODO - Neither of these property names are standard. Should be camel-case and proper prefix. * * TODO - What character sets can you use in property names? Only ISO-8859-1? UTF-8? * * TODO - If this would fallback to calling the System getProperty, we could ask that Project Wonder frameworks only use this class. * */ public class ERXProperties extends Properties implements NSKeyValueCoding { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; /** default string */ public static final String DefaultString = "Default"; private static Boolean RetainDefaultsEnabled; private static String UndefinedMarker = "-undefined-"; private static final Logger log = LoggerFactory.getLogger(ERXProperties.class); private static final Logger configLog = LoggerFactory.getLogger(ERXConfigurationManager.class); private static final Map AppSpecificPropertyNames = new HashMap(128); /** WebObjects version number as string */ private static String _webObjectsVersion; /** WebObjects version number as double */ private static double _webObjectsVersionDouble; /** * <div class="en">Internal cache of type converted values to avoid reconverting attributes that are asked for frequently</div> * * <div class="ja">??????????????????</div> */ private static Map _cache = Collections.synchronizedMap(new HashMap()); /** * This boolean controls the behavior of application specific properties. Setting this to * false makes the old behavior active, true activates the new behavior. The value of this * boolean is controlled by the property "NSProperties.useLoadtimeAppSpecifics" and defaults * to true. Please note this property MUST be defined with a -D argument on the application * launch command. * <p> * The old behavior will retain the original property names (including the application name), * and will search for an application specific version of each property every time someone * reads a property. * <p> * The new behavior will analyze all properties after being loaded from their source, and create * (or update) generic properties for each application specific one. So, if we are MyApp, * foo.bar.MyApp=4 will originate a new property foo.bar=4 (or, if foo.bar already exists, * update its value to 4). foo.bar.MyApp is also kept, because we cannot be sure foo.bar.MyApp * is an app specific property or a regular property with an ambiguous name. * <p> * The advantage of the new method is getting rid of the performance hit each time an app * accesses a property, and making application specific properties work for code that uses * the native Java System.getProperty call. */ public static final boolean _useLoadtimeAppSpecifics; /** * Set in flattenPropertyNames(). * * The flattenPropertyNames() method is called from ERXSystem.updateProperties(), * which is called from ERXConfigurationManager.loadConfiguration(), * which is called from ERXExtensions.finishInitialization(), * which is registered to be called at ApplicationDidFinishLaunching-time by ERXApplication. */ private String _appNameSuffix; static { _useLoadtimeAppSpecifics = ERXValueUtilities .booleanValueWithDefault(System.getProperty("NSProperties.useLoadtimeAppSpecifics"), true); } /** * <div class="ja"> * ????????? * * @return boolean - true ??????? * </div> */ private static boolean retainDefaultsEnabled() { if (RetainDefaultsEnabled == null) { final String propertyValue = ERXSystem.getProperty("er.extensions.ERXProperties.RetainDefaultsEnabled", "false"); final boolean isEnabled = "true".equals(propertyValue); RetainDefaultsEnabled = Boolean.valueOf(isEnabled); } return RetainDefaultsEnabled.booleanValue(); } /** * <div class="en"> * Puts handy properties such as <code>com.webobjects.version</code> * into the system properties. This method is called when * the framework is initialized * (when WOApplication.ApplicationWillFinishLaunchingNotification * is posted.) * </div> * * <div class="ja"> * <code>com.webobjects.version</code> ???? * ??????? * * (WOApplication.ApplicationWillFinishLaunchingNotification ) * </div> */ public static void populateSystemProperties() { System.setProperty("com.webobjects.version", "5.4"); } /** * <div class="en"> * Returns the version string of the application. * It checks <code>CFBundleShortVersionString</code> property * in the <code>info.plist</code> resource and returns * a trimmed version of the value. * </div> * * <div class="ja"> * ?????? * * CustomInfo.plist ? CFBundleShortVersionString ??Trim?????? * </div> * * @return <div class="en">version number as string; can be a null-string when the application doesn't have the value of <code>CFBundleShortVersionString</code> in its <code>info.plist</code> resource.</div> * <div class="ja">?????????????? null-string ???</div> * @see #versionStringForFrameworkNamed * @see #webObjectsVersion */ public static String versionStringForApplication() { return valueFromPlistBundleWithKey(NSBundle.mainBundle(), "../Info.plist", "CFBundleShortVersionString"); } /** * <div class="en"> * Returns the version string of the given framework. * It checks <code>CFBundleShortVersionString</code> property * in the <code>info.plist</code> resource and returns * a trimmed version of the value. * </div> * * <div class="ja"> * ???????? * * CustomInfo.plist ? CFBundleShortVersionString ??Trim?????? * </div> * * @param frameworkName <div class="en">name</div> * <div class="ja">??</div> * * @return <div class="en">version number as string; can be null-string when the framework is not found or the framework doesn't have the value of <code>CFBundleShortVersionString</code> in its <code>info.plist</code> resource.</div> * <div class="ja">??????????????? null-string ???</div> * @see #versionStringForApplication() * @see #webObjectsVersion() * @see ERXStringUtilities#removeExtraDotsFromVersionString(String) */ public static String versionStringForFrameworkNamed(String frameworkName) { return valueFromPlistBundleWithKey(NSBundle.bundleForName(frameworkName), "Info.plist", "CFBundleShortVersionString"); } /** * <div class="en"> * Returns the version string of the given framework. * It checks <code>SourceVersion</code> property * in the <code>version.plist</code> resource and returns * a trimmed version of the value. * </div> * * <div class="ja"> * WebObjects?????? * * version.plist? <code>SourceVersion</code> ??Trim?????? * </div> * * @return <div class="en">version number as string; can be null-string when the framework is not found or the framework doesn't have the value of <code>SourceVersion</code> in its <code>info.plist</code> resource.</div> * <div class="ja">??String?????????????null-string???</div> * @see #versionStringForApplication * @see #webObjectsVersion */ public static String sourceVersionString() { return valueFromPlistBundleWithKey(NSBundle.bundleForName("JavaWebObjects"), "version.plist", "SourceVersion"); } /** * <div class="en"> * Returns the key in an plist of the given framework. * </div> * <div class="ja"> * ??plist???????? * </div> * * @param bundle <div class="en">bundle name</div> * <div class="ja">????</div> * @param plist <div class="en">plist Filename</div> * <div class="ja">plist ??</div> * @param key <div class="en">key</div> * <div class="ja">plist ??</div> * * @return <div class="en">Result</div> * <div class="ja">?</div> */ public static String valueFromPlistBundleWithKey(NSBundle bundle, String plist, String key) { if (bundle == null) return ""; String dictString = new String(bundle.bytesForResourcePath(plist)); NSDictionary versionDictionary = NSPropertyListSerialization.dictionaryForString(dictString); String versionString = (String) versionDictionary.objectForKey(key); return versionString == null ? "" : versionString.trim(); // trim() removes the line ending char } /** constant string used if Wonder version could not be determined */ public static final String UNKNOWN_WONDER_VERSION = "Not Available"; /** * Returns the version string of the Wonder frameworks. * * @return version string */ public static String wonderVersion() { String wonderVersion = null; NSBundle bundle = NSBundle.bundleForName("ERExtensions"); if (bundle != null) { String note = valueFromPlistBundleWithKey(bundle, "Info.plist", "NOTE"); if (note != null && note.equals("autogenerated")) { // Info.plist was generated by WOLips incremental builder wonderVersion = valueFromPlistBundleWithKey(bundle, "Info.plist", "CFBundleVersion"); } else { // Info.plist was generated by ant wonderVersion = valueFromPlistBundleWithKey(bundle, "Info.plist", "CFBundleShortVersionString"); } if (wonderVersion == "" && ERXApplication.isDevelopmentModeSafe()) { // running within Eclipse and no Info.plist has been generated so look at maven config FileInputStream is = null; try { URL path = bundle.pathURLForResourcePath("../pom.xml"); File pomFile = new File(path.toURI()); if (pomFile.exists()) { is = new FileInputStream(pomFile); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document xml = builder.parse(is); XPath xPath = XPathFactory.newInstance().newXPath(); wonderVersion = xPath.compile("/project/parent/version").evaluate(xml); } } catch (Exception e) { // ignore } finally { if (is != null) { try { is.close(); } catch (IOException e) { // ignore } } } } } if (wonderVersion == null || wonderVersion == "") { wonderVersion = UNKNOWN_WONDER_VERSION; } return wonderVersion; } /** * <div class="en"> * Cover method for returning an NSArray for a * given system property. * </div> * * <div class="ja"> * ?? NSArray ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">array de-serialized from the string in the system properties</div> * <div class="ja">???? String NSArray ?????</div> */ public static NSArray arrayForKey(String s) { return arrayForKeyWithDefault(s, null); } /** * <div class="en"> * Converts the standard propertyName into one with a .<AppName> on the end, if the property is defined with * that suffix. If not, then this caches the standard propertyName. A cache is maintained to avoid concatenating * strings frequently, but may be overkill since most usage of this system doesn't involve frequent access. * </div> * <div class="ja"> * ???????????????????? * ????????? * </div> * * @param propertyName <div class="en"></div> * <div class="ja">??</div> * * @return <div class="en"></div> * <div class="ja">???</div> */ private static String getApplicationSpecificPropertyName(final String propertyName) { if (_useLoadtimeAppSpecifics) { return propertyName; } synchronized (AppSpecificPropertyNames) { // only keep 128 of these around if (AppSpecificPropertyNames.size() > 128) { AppSpecificPropertyNames.clear(); } String appSpecificPropertyName = (String) AppSpecificPropertyNames.get(propertyName); if (appSpecificPropertyName == null) { final WOApplication application = WOApplication.application(); if (application != null) { final String appName = application.name(); appSpecificPropertyName = propertyName + "." + appName; } else { appSpecificPropertyName = propertyName; } final String propertyValue = ERXSystem.getProperty(appSpecificPropertyName); if (propertyValue == null) { appSpecificPropertyName = propertyName; } AppSpecificPropertyNames.put(propertyName, appSpecificPropertyName); } return appSpecificPropertyName; } } /** * <div class="en"> * Cover method for returning an NSArray for a * given system property and set a default value if not given. * </div> * * <div class="ja"> * ?? NSArray ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">array de-serialized from the string in the system properties or default value</div> * <div class="ja">???? String NSArray ?????</div> */ public static NSArray arrayForKeyWithDefault(final String s, final NSArray defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); NSArray value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof NSArray) { value = (NSArray) cachedValue; } else { value = ERXValueUtilities.arrayValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, value == null ? (Object) UndefinedMarker : value); if (value == null) { value = defaultValue; } if (retainDefaultsEnabled() && value == null && defaultValue != null) { setArrayForKey(defaultValue, propertyName); } } return value; } /** * <div class="en"> * Cover method for returning a boolean for a * given system property. This method uses the * method <code>booleanValue</code> from * {@link ERXUtilities}. * </div> * * <div class="ja"> * ?? boolean ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">boolean value of the string in the system properties.</div> * <div class="ja">boolean ??false</div> */ public static boolean booleanForKey(String s) { return booleanForKeyWithDefault(s, false); } /** * <div class="en"> * Cover method for returning a boolean for a * given system property or a default value. This method uses the * method <code>booleanValue</code> from * {@link ERXUtilities}. * </div> * * <div class="ja"> * ?? boolean ????<br> * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">boolean value of the string in the system properties.</div> * <div class="ja">boolean </div> */ public static boolean booleanForKeyWithDefault(final String s, final boolean defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); boolean value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof Boolean) { value = ((Boolean) cachedValue).booleanValue(); } else { Boolean objValue = ERXValueUtilities.BooleanValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(propertyName, objValue == null ? (Object) UndefinedMarker : objValue); if (objValue == null) { value = defaultValue; } else { value = objValue.booleanValue(); } if (retainDefaultsEnabled() && objValue == null) { System.setProperty(propertyName, Boolean.toString(defaultValue)); } } return value; } /** * <div class="en"> * Cover method for returning an NSDictionary for a * given system property. * </div> * * <div class="ja"> * ?? NSDictionary ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">dictionary de-serialized from the string in the system properties</div> * <div class="ja">? NSDictionary ??</div> */ public static NSDictionary dictionaryForKey(String s) { return dictionaryForKeyWithDefault(s, null); } /** * <div class="en"> * dictionaryForKeyWithDefault * Cover method for returning an NSDictionary for a * given system property or the default value. * </div> * * <div class="ja"> * ?? NSDictionary ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">dictionary de-serialized from the string in the system properties</div> * <div class="ja">?? NSDictionary</div> */ public static NSDictionary dictionaryForKeyWithDefault(final String s, final NSDictionary defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); NSDictionary value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof NSDictionary) { value = (NSDictionary) cachedValue; } else { value = ERXValueUtilities.dictionaryValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, value == null ? (Object) UndefinedMarker : value); if (value == null) { value = defaultValue; } if (retainDefaultsEnabled() && value == null && defaultValue != null) { setDictionaryForKey(defaultValue, propertyName); } } return value; } /** * Checks if a property for the given key exists. * @param key name of the property * @return <code>true</code> if a property for key exists */ public static boolean hasKey(String key) { return hasKey(key, false); } /** * Checks if a property for the given key exists. If you want to * ignore properties that have an empty value pass <code>true</code> * as parameter (i.e. '<code>my.property=</code>'). * @param key name of the property * @param ignoreEmptyValue <code>true</code> if you want to ignore * properties with empty values * @return <code>true</code> if a property exists */ public static boolean hasKey(String key, boolean ignoreEmptyValue) { final String propertyName = getApplicationSpecificPropertyName(key); Object cachedValue = _cache.get(propertyName); if (cachedValue == null || UndefinedMarker.equals(cachedValue)) { String value = ERXSystem.getProperty(key); return value != null && !(ignoreEmptyValue && value.length() == 0); } return true; } /** * <div class="en"> * Cover method for returning an int for a * given system property. * </div> * * <div class="ja"> * ?? int ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">int value of the system property or 0</div> * <div class="ja">int ?? 0</div> */ public static int intForKey(String s) { return intForKeyWithDefault(s, 0); } /** * <div class="en"> * Cover method for returning a long for a * given system property. * </div> * * <div class="ja"> * ?? long ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">long value of the system property or 0</div> * <div class="ja">long ?? 0</div> */ public static long longForKey(String s) { return longForKeyWithDefault(s, 0); } /** * <div class="en"> * Cover method for returning a float for a * given system property. * </div> * * <div class="ja"> * ?? float ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">float value of the system property or 0</div> * <div class="ja">float ?? 0</div> */ public static float floatForKey(String s) { return floatForKeyWithDefault(s, 0); } /** * <div class="en"> * Cover method for returning a double for a * given system property. * </div> * * <div class="ja"> * ?? double ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">double value of the system property or 0</div> * <div class="ja">double ?? 0</div> */ public static double doubleForKey(String s) { return doubleForKeyWithDefault(s, 0); } /** * <div class="en"> * Cover method for returning a BigDecimal for a * given system property. This method uses the * method <code>bigDecimalValueWithDefault</code> from * {@link ERXValueUtilities}. * </div> * * <div class="ja"> * ?? BigDecimal ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">BigDecimal value of the string in the system properties. Scale is controlled by the string, ie "4.400" will have a scale of 3.</div> * <div class="ja">BigDecimal ?? null</div> */ public static BigDecimal bigDecimalForKey(String s) { return bigDecimalForKeyWithDefault(s, null); } /** * <div class="en"> * Cover method for returning a BigDecimal for a * given system property or a default value. This method uses the * method <code>bigDecimalValueWithDefault</code> from * {@link ERXValueUtilities}. * </div> * * <div class="ja"> * ?? BigDecimal ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">BigDecimal value of the string in the system properties. Scale is controlled by the string, ie "4.400" will have a scale of 3.</div> * <div class="ja">BigDecimal ???</div> */ public static BigDecimal bigDecimalForKeyWithDefault(String s, BigDecimal defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); Object value = _cache.get(propertyName); if (UndefinedMarker.equals(value)) { return defaultValue; } if (value instanceof BigDecimal) { return (BigDecimal) value; } String propertyValue = ERXSystem.getProperty(propertyName); final BigDecimal bigDecimal = ERXValueUtilities.bigDecimalValueWithDefault(propertyValue, defaultValue); if (retainDefaultsEnabled() && propertyValue == null && bigDecimal != null) { propertyValue = bigDecimal.toString(); System.setProperty(propertyName, propertyValue); } _cache.put(propertyName, propertyValue == null ? (Object) UndefinedMarker : bigDecimal); return bigDecimal; } /** * <div class="en"> * Cover method for returning an int for a * given system property with a default value. * </div> * * <div class="ja"> * ?? int ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">int value of the system property or the default value</div> * <div class="ja">int ???</div> */ public static int intForKeyWithDefault(final String s, final int defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); int value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof Integer) { value = ((Integer) cachedValue).intValue(); } else { Integer objValue = ERXValueUtilities.IntegerValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, objValue == null ? (Object) UndefinedMarker : objValue); if (objValue == null) { value = defaultValue; } else { value = objValue.intValue(); } if (retainDefaultsEnabled() && objValue == null) { System.setProperty(propertyName, Integer.toString(defaultValue)); } } return value; } /** * <div class="en"> * Cover method for returning a long for a * given system property with a default value. * </div> * * <div class="ja"> * ?? long ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">long value of the system property or the default value</div> * <div class="ja">long ???</div> */ public static long longForKeyWithDefault(final String s, final long defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); long value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof Long) { value = ((Long) cachedValue).longValue(); } else { Long objValue = ERXValueUtilities.LongValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, objValue == null ? (Object) UndefinedMarker : objValue); if (objValue == null) { value = defaultValue; } else { value = objValue.longValue(); } if (retainDefaultsEnabled() && objValue == null) { System.setProperty(propertyName, Long.toString(defaultValue)); } } return value; } /** * <div class="en"> * Cover method for returning a float for a * given system property with a default value. * </div> * * <div class="ja"> * ?? float ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">float value of the system property or the default value</div> * <div class="ja">float </div> */ public static float floatForKeyWithDefault(final String s, final float defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); float value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof Float) { value = ((Float) cachedValue).floatValue(); } else { Float objValue = ERXValueUtilities.FloatValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, objValue == null ? (Object) UndefinedMarker : objValue); if (objValue == null) { value = defaultValue; } else { value = objValue.floatValue(); } if (retainDefaultsEnabled() && objValue == null) { System.setProperty(propertyName, Float.toString(defaultValue)); } } return value; } /** * <div class="en"> * Cover method for returning a double for a * given system property with a default value. * </div> * * <div class="ja"> * ?? double ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">double value of the system property or the default value</div> * <div class="ja">double </div> */ public static double doubleForKeyWithDefault(final String s, final double defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); double value; Object cachedValue = _cache.get(propertyName); if (UndefinedMarker.equals(cachedValue)) { value = defaultValue; } else if (cachedValue instanceof Double) { value = ((Double) cachedValue).doubleValue(); } else { Double objValue = ERXValueUtilities.DoubleValueWithDefault(ERXSystem.getProperty(propertyName), null); _cache.put(s, objValue == null ? (Object) UndefinedMarker : objValue); if (objValue == null) { value = defaultValue; } else { value = objValue.doubleValue(); } if (retainDefaultsEnabled() && objValue == null) { System.setProperty(propertyName, Double.toString(defaultValue)); } } return value; } /** * <div class="en"> * Returning an string for a given system * property. This is a cover method of * {@link java.lang.System#getProperty} * </div> * * <div class="ja"> * ?? String ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * * @return <div class="en">string value of the system property or null</div> * <div class="ja">String ??null</div> */ public static String stringForKey(String s) { return stringForKeyWithDefault(s, null); } /** * <div class="en"> * Returning an string for a given system * property. This is a cover method of * {@link java.lang.System#getProperty} * </div> * * <div class="ja"> * ?? String ???? * </div> * * @param s <div class="en">system property</div> * <div class="ja"></div> * @param defaultValue <div class="en">default value</div> * <div class="ja"></div> * * @return <div class="en">string value of the system property or null</div> * <div class="ja">String ???</div> */ public static String stringForKeyWithDefault(final String s, final String defaultValue) { final String propertyName = getApplicationSpecificPropertyName(s); final String propertyValue = ERXSystem.getProperty(propertyName); final String stringValue = propertyValue == null ? defaultValue : propertyValue; if (retainDefaultsEnabled() && propertyValue == null) { System.setProperty(propertyName, stringValue == null ? UndefinedMarker : stringValue); } return stringValue == UndefinedMarker ? null : stringValue; } /** * <div class="en"> * Returns the decrypted value for the given property name using * the default crypter if the property propertyName.encrypted=true. For * instance, if you are requesting my.password, if my.password.encrypted=true * the value of my.password will be passed to the default crypter's decrypt * method. * </div> * * <div class="ja"> * ???? (propertyName.encrypted=true) ???????? * ???my.password ????my.password.encrypted=true ????? * my.password ???? {@link er.extensions.crypting.ERXCrypto#defaultCrypter()} ??? * </div> * * @param propertyName <div class="en">the property name to retrieve and optionally decrypt</div> * <div class="ja">??</div> * * @return <div class="en">the decrypted property value</div> * <div class="ja">???</div> */ public static String decryptedStringForKey(String propertyName) { return ERXProperties.decryptedStringForKeyWithDefault(propertyName, null); } /** * <div class="en"> * If the <code>propertyName.encrypted</code> property is set to true, returns * the plain text value of the given property name, after decrypting it with the * {@link er.extensions.crypting.ERXCrypto#defaultCrypter()}. For instance, if you are requesting * my.password and <code>my.password.encrypted</code> is set to true, * the value of <code>my.password</code> will be sent to the default crypter's * decrypt() method. * </div> * * <div class="ja"> * ???? (propertyName.encrypted=true) ???????? * ???my.password ????my.password.encrypted=true ????? * my.password ???? {@link er.extensions.crypting.ERXCrypto#defaultCrypter()} ??? * </div> * * @param propertyName <div class="en">the property name to retrieve and optionally decrypt</div> * <div class="ja">??</div> * @param defaultValue <div class="en">the default value to return if there is no password</div> * <div class="ja">????</div> * * @return <div class="en">the decrypted property value</div> * <div class="ja">???</div> */ public static String decryptedStringForKeyWithDefault(String propertyName, String defaultValue) { boolean propertyNameEncrypted = ERXProperties.booleanForKeyWithDefault(propertyName + ".encrypted", false); String decryptedPassword; if (propertyNameEncrypted) { String encryptedPassword = ERXProperties.stringForKey(propertyName); decryptedPassword = ERXCrypto.defaultCrypter().decrypt(encryptedPassword); } else { decryptedPassword = ERXProperties.stringForKey(propertyName); } if (decryptedPassword == null) { decryptedPassword = defaultValue; } return decryptedPassword; } /** * <div class="en"> * Returns the decrypted value for the given property name using the * {@link er.extensions.crypting.ERXCrypto#defaultCrypter()}. This is slightly different than * decryptedStringWithKeyWithDefault in that it does not require the encrypted * property to be set. * </div> * * <div class="ja"> * ???? (propertyName.encrypted=true) ???????? * ???my.password ????my.password.encrypted=true ????? * my.password ???? {@link er.extensions.crypting.ERXCrypto#defaultCrypter()} ??? * </div> * * @param propertyName <div class="en">the name of the property to decrypt</div> * <div class="ja">??</div> * @param defaultValue <div class="en">the default encrypted value</div> * <div class="ja">????????</div> * * @return <div class="en">the decrypted value</div> * <div class="ja">???</div> */ public static String decryptedStringForKeyWithEncryptedDefault(String propertyName, String defaultValue) { String encryptedPassword = ERXProperties.stringForKeyWithDefault(propertyName, defaultValue); return ERXCrypto.defaultCrypter().decrypt(encryptedPassword); } /** * <div class="en"> * Returns an array of strings separated with the given separator string. * </div> * <div class="ja"> * ?? key ? separator ???? * ????????? * </div> * * @param key <div class="en">the key to lookup</div> * <div class="ja"></div> * @param separator <div class="en">the separator (",")</div> * <div class="ja">? (",")</div> * * @return <div class="en">the array of strings or NSArray.EmptyArray if not found</div> * <div class="ja">????? NSArray.EmptyArray ???</div> */ @SuppressWarnings({ "unchecked" }) public static NSArray<String> componentsSeparatedByString(String key, String separator) { return ERXProperties.componentsSeparatedByStringWithDefault(key, separator, NSArray.EmptyArray); } /** * <div class="en"> * Returns an array of strings separated with the given separator string. * </div> * * <div class="ja"> * ?? key ? separator ???? * ????????? * </div> * * @param key <div class="en">the key to lookup</div> * <div class="ja"></div> * @param separator <div class="en">the separator (",")</div> * <div class="ja">? (",")</div> * @param defaultValue <div class="en">the default array to return if there is no value</div> * <div class="ja">????</div> * * @return <div class="en">the array of strings</div> * <div class="ja">????? defaultValue ???</div> */ @SuppressWarnings({ "unchecked" }) public static NSArray<String> componentsSeparatedByStringWithDefault(String key, String separator, NSArray<String> defaultValue) { NSArray<String> array; String str = stringForKeyWithDefault(key, null); if (str == null) { array = defaultValue; } else { array = NSArray.componentsSeparatedByString(str, separator); } return array; } /** * <div class="en"> * Sets an array in the System properties for * a particular key. * </div> * * <div class="ja"> * ? NSArray ??? * </div> * * @param array <div class="en">to be set in the System properties</div> * <div class="ja">???</div> * @param key <div class="en">to be used to get the value</div> * <div class="ja">??</div> */ public static void setArrayForKey(NSArray array, String key) { setStringForKey(NSPropertyListSerialization.stringFromPropertyList(array), key); } /** * <div class="en"> * Sets a dictionary in the System properties for * a particular key. * </div> * * <div class="ja"> * ????? * </div> * * @param dictionary <div class="en">to be set in the System properties</div> * <div class="ja">?</div> * @param key <div class="en">to be used to get the value</div> * <div class="ja">?</div> */ public static void setDictionaryForKey(NSDictionary dictionary, String key) { setStringForKey(NSPropertyListSerialization.stringFromPropertyList(dictionary), key); } /** * <div class="en"> * Sets a string in the System properties for * another string. * </div> * * <div class="ja"> * ? String ??? * </div> * * @param string <div class="en">to be set in the System properties</div> * <div class="ja">??String</div> * @param key <div class="en">to be used to get the value</div> * <div class="ja">??</div> */ // DELETEME: Really not needed anymore -- MS: Why? We need the cache clearing. public static void setStringForKey(String string, String key) { System.setProperty(key, string); _cache.remove(key); } /** * <div class="ja"> * ??? * * @param key - * </div> */ public static void removeKey(String key) { System.getProperties().remove(key); _cache.remove(key); } /** * <div class="en"> * Copies all properties from source to dest. * </div> * * <div class="ja"> * ??????? * </div> * * @param source <div class="en">properties copied from</div> * <div class="ja">?</div> * @param dest <div class="en">properties copied to</div> * <div class="ja">?</div> */ public static void transferPropertiesFromSourceToDest(Properties source, Properties dest) { if (source != null) { dest.putAll(source); if (dest == System.getProperties()) { systemPropertiesChanged(); } } } /** * <div class="en"> * Reads a Java properties file at the given path * and returns a {@link java.util.Properties Properties} object * as the result. If the file does not exist, returns * an empty properties object. * </div> * * <div class="ja"> * ??? Java ?? * {@link java.util.Properties Properties} ?????? * ????????empty ??? * </div> * * @param path <div class="en">file path to the properties file</div> * <div class="ja">??</div> * * @return <div class="en">properties object with the values from the file specified.</div> * <div class="ja"???</div> */ // FIXME: This shouldn't eat the exception public static Properties propertiesFromPath(String path) { ERXProperties._Properties prop = new ERXProperties._Properties(); if (path == null || path.length() == 0) { log.warn("Attempting to read property file for null file path"); return prop; } File file = new File(path); if (!file.exists() || !file.isFile() || !file.canRead()) { log.warn("File '{}' doesn't exist or can't be read.", path); return prop; } try { prop.load(file); log.debug("Loaded configuration file at path: {}", path); } catch (IOException e) { log.error("Unable to initialize properties from file '{}'", path, e); } return prop; } /** * <div class="en"> * Gets the properties for a given file. * </div> * * <div class="ja"> * ????? * </div> * * @param file <div class="en">the properties file</div> * <div class="ja"></div> * * @return <div class="en">properties from the given file</div> * <div class="ja">???</div> * @throws java.io.IOException if the file is not found or cannot be read */ public static Properties propertiesFromFile(File file) throws java.io.IOException { if (file == null) throw new IllegalStateException("Attempting to get properties for a null file!"); ERXProperties._Properties prop = new ERXProperties._Properties(); prop.load(file); return prop; } /** * <div class="en"> * Sets and returns properties object with the values from * the given command line arguments string array. * </div> * <div class="ja"> * ????????? * </div> * * @param argv <div class="en">string array typically provided by the command line arguments</div> * <div class="ja">?????</div> * * @return <div class="en">properties object with the values from the argv</div> * <div class="ja">argv ???</div> */ public static Properties propertiesFromArgv(String[] argv) { ERXProperties._Properties properties = new ERXProperties._Properties(); NSDictionary argvDict = NSProperties.valuesFromArgv(argv); Enumeration e = argvDict.allKeys().objectEnumerator(); while (e.hasMoreElements()) { Object key = e.nextElement(); properties.put(key, argvDict.objectForKey(key)); } return properties; } /** * <div class="en"> * Returns an array of paths to the <code>Properties</code> and * <code>WebObjects.properties</code> files contained in the * application/framework bundles and home directory. * <p> * If ProjectBuilder (for Mac OS X) has the project opened, * it will attempt to get the path to the one in the project * directory instead of the one in the bundle. * <p> * This opened project detection feature is pretty fragile and * will change between versions of the dev-tools. * </div> * <div class="ja"> * application/framework ???? <code>Properties</code> ? * <code>WebObjects.properties</code> ?????? * <p> * ??????????????????? * <p> * ?????????????? * </div> * * @return <div class="en">paths to Properties files</div> * <div class="ja">?</div> */ public static NSArray pathsForUserAndBundleProperties() { return pathsForUserAndBundleProperties(false); } private static void addIfPresent(String info, String path, NSMutableArray<String> propertiesPaths, NSMutableArray<String> projectsInfo) { if (path != null && path.length() > 0) { path = getActualPath(path); if (propertiesPaths.containsObject(path)) { log.error("Path was already included: {}", path); } projectsInfo.addObject(" " + info + " -> " + path); propertiesPaths.addObject(path); } } public static NSArray<String> pathsForUserAndBundleProperties(boolean reportLoggingEnabled) { NSMutableArray<String> propertiesPaths = new NSMutableArray(); NSMutableArray<String> projectsInfo = new NSMutableArray(); /* Properties for frameworks */ NSArray frameworkNames = (NSArray) NSBundle.frameworkBundles().valueForKey("name"); Enumeration e = frameworkNames.reverseObjectEnumerator(); while (e.hasMoreElements()) { String frameworkName = (String) e.nextElement(); String propertyPath = ERXFileUtilities.pathForResourceNamed("Properties", frameworkName, null); addIfPresent(frameworkName + ".framework", propertyPath, propertiesPaths, projectsInfo); /** Properties.dev -- per-Framework-dev properties * This adds support for Properties.dev in your Frameworks new load order will be */ String devPropertiesPath = ERXApplication.isDevelopmentModeSafe() ? ERXProperties.variantPropertiesInBundle("dev", frameworkName) : null; addIfPresent(frameworkName + ".framework.dev", devPropertiesPath, propertiesPaths, projectsInfo); /** Properties.<userName> -- per-Framework-per-User properties */ String userPropertiesPath = ERXProperties.variantPropertiesInBundle(ERXSystem.getProperty("user.name"), frameworkName); addIfPresent(frameworkName + ".framework.user", userPropertiesPath, propertiesPaths, projectsInfo); } NSBundle mainBundle = NSBundle.mainBundle(); if (mainBundle != null) { String mainBundleName = mainBundle.name(); String appPath = ERXFileUtilities.pathForResourceNamed("Properties", "app", null); addIfPresent(mainBundleName + ".app", appPath, propertiesPaths, projectsInfo); } /* WebObjects.properties in the user home directory */ String userHome = ERXSystem.getProperty("user.home"); if (userHome != null && userHome.length() > 0) { File file = new File(userHome, "WebObjects.properties"); if (file.exists() && file.isFile() && file.canRead()) { try { String userHomePath = file.getCanonicalPath(); addIfPresent("{$user.home}/WebObjects.properties", userHomePath, propertiesPaths, projectsInfo); } catch (java.io.IOException ex) { log.error("Failed to load the configuration file '{}'.", file, ex); } } } /* Optional properties files */ if (optionalConfigurationFiles() != null && optionalConfigurationFiles().count() > 0) { for (Enumeration configEnumerator = optionalConfigurationFiles().objectEnumerator(); configEnumerator .hasMoreElements();) { String configFile = (String) configEnumerator.nextElement(); File file = new File(configFile); if (file.exists() && file.isFile() && file.canRead()) { try { String optionalPath = file.getCanonicalPath(); addIfPresent("Optional Configuration", optionalPath, propertiesPaths, projectsInfo); } catch (java.io.IOException ex) { log.error("Failed to load configuration file '{}'.", file, ex); } } else { log.error("The optional configuration file '{}' either does not exist or could not be read.", file); } } } optionalPropertiesLoader(ERXSystem.getProperty("user.name"), propertiesPaths, projectsInfo); /** /etc/WebObjects/AppName/Properties -- per-Application-per-Machine properties */ String applicationMachinePropertiesPath = ERXProperties.applicationMachinePropertiesPath("Properties"); addIfPresent("Application-Machine Properties", applicationMachinePropertiesPath, propertiesPaths, projectsInfo); /** Properties.dev -- per-Application-dev properties */ String applicationDeveloperPropertiesPath = ERXProperties.applicationDeveloperProperties(); addIfPresent("Application-Developer Properties", applicationDeveloperPropertiesPath, propertiesPaths, projectsInfo); /** Properties.<userName> -- per-Application-per-User properties */ String applicationUserPropertiesPath = ERXProperties.applicationUserProperties(); addIfPresent("Application-User Properties", applicationUserPropertiesPath, propertiesPaths, projectsInfo); /* Report the result */ if (reportLoggingEnabled && projectsInfo.count() > 0 && log.isInfoEnabled()) { StringBuilder message = new StringBuilder(); message.append("\n\n").append("ERXProperties has found the following Properties files: \n"); message.append(projectsInfo.componentsJoinedByString("\n")); message.append('\n'); message.append("ERXProperties currently has the following properties:\n"); message.append(ERXProperties.logString(ERXSystem.getProperties())); // ERXLogger.configureLoggingWithSystemProperties(); log.info(message.toString()); } return propertiesPaths.immutableClone(); } /** * <div class="en"> * Making it possible to use Properties File in the Application more * powerful, specially for newcomers. * For every Framework it will try to call also following * Properties.[Framework] and Properties.[Framework].[Username] * Also there is a Propertie for * Properties.log4j, Properties.log4j.[Username] for logging * Properties.database, Properties.database.[Username] for database infos * Properties.multilanguage, Properties.multilanguage.[Username] for Encoding * Properties.migration, Properties.migration.[Username] for Migration * </div> * * <div class="ja"> * ????????????? * ????????????? * ????????? * Properties.[Framework] ? Properties.[Framework].[Username] * ?????? * Properties.log4j, Properties.log4j.[Username] * Properties.database, Properties.database.[Username] * Properties.multilanguage, Properties.multilanguage.[Username] * Properties.migration, Properties.migration.[Username] * </div> * * @param userName <div class="en">Username</div> * <div class="ja">??</div> * @param propertiesPaths <div class="en">Properites Path {@link ERXProperties#pathsForUserAndBundleProperties}</div> * <div class="ja"> {@link ERXProperties#pathsForUserAndBundleProperties}</div> * @param projectsInfo <div class="en">Project Info {@link ERXProperties#pathsForUserAndBundleProperties}</div> * <div class="ja"> {@link ERXProperties#pathsForUserAndBundleProperties}</div> */ private static void optionalPropertiesLoader(String userName, NSMutableArray<String> propertiesPaths, NSMutableArray<String> projectsInfo) { /** Properties.log4j.<userName> -- per-Application-per-User properties */ String logPropertiesPath; logPropertiesPath = ERXProperties.variantPropertiesInBundle("log4j", "app"); if (logPropertiesPath != null) { addIfPresent("Application-User Log4j Properties", logPropertiesPath, propertiesPaths, projectsInfo); } logPropertiesPath = ERXProperties.variantPropertiesInBundle("log4j." + userName, "app"); if (logPropertiesPath != null) { addIfPresent("Application-User Log4j Properties", logPropertiesPath, propertiesPaths, projectsInfo); } /** Properties.database.<userName> -- per-Application-per-User properties */ String databasePropertiesPath; databasePropertiesPath = ERXProperties.variantPropertiesInBundle("database", "app"); if (databasePropertiesPath != null) { addIfPresent("Application-User Database Properties", databasePropertiesPath, propertiesPaths, projectsInfo); } databasePropertiesPath = ERXProperties.variantPropertiesInBundle("database." + userName, "app"); if (databasePropertiesPath != null) { addIfPresent("Application-User Database Properties", databasePropertiesPath, propertiesPaths, projectsInfo); } /** Properties.multilanguage.<userName> -- per-Application-per-User properties */ String multilanguagePath; multilanguagePath = ERXProperties.variantPropertiesInBundle("multilanguage", "app"); if (multilanguagePath != null) { addIfPresent("Application-User Multilanguage Properties", multilanguagePath, propertiesPaths, projectsInfo); } multilanguagePath = ERXProperties.variantPropertiesInBundle("multilanguage." + userName, "app"); if (multilanguagePath != null) { addIfPresent("Application-User Multilanguage Properties", multilanguagePath, propertiesPaths, projectsInfo); } /** Properties.migration -- per-Application properties */ String migrationPath; migrationPath = ERXProperties.variantPropertiesInBundle("migration", "app"); if (migrationPath != null) { addIfPresent("Application-User Migration Properties", migrationPath, propertiesPaths, projectsInfo); } migrationPath = ERXProperties.variantPropertiesInBundle("migration." + userName, "app"); if (migrationPath != null) { addIfPresent("Application-User Migration Properties", migrationPath, propertiesPaths, projectsInfo); } /** Properties.<frameworkName>.<userName> -- per-Application-per-User properties */ @SuppressWarnings("unchecked") NSArray<String> frameworkNames = (NSArray<String>) NSBundle.frameworkBundles().valueForKey("name"); Enumeration<String> e = frameworkNames.reverseObjectEnumerator(); while (e.hasMoreElements()) { String frameworkName = e.nextElement(); String userPropertiesPath; userPropertiesPath = ERXProperties.variantPropertiesInBundle(frameworkName, "app"); if (userPropertiesPath != null) { addIfPresent(frameworkName + ".framework.common", userPropertiesPath, propertiesPaths, projectsInfo); } userPropertiesPath = ERXProperties.variantPropertiesInBundle(frameworkName + "." + userName, "app"); if (userPropertiesPath != null) { addIfPresent(frameworkName + ".framework.user", userPropertiesPath, propertiesPaths, projectsInfo); } } } /** * Apply the current configuration to the supplied properties. * @param source * @param commandLine * @return the applied properties */ public static Properties applyConfiguration(Properties source, Properties commandLine) { Properties dest = source != null ? (Properties) source.clone() : new Properties(); NSArray additionalConfigurationFiles = ERXProperties.pathsForUserAndBundleProperties(false); if (additionalConfigurationFiles.count() > 0) { for (Enumeration configEnumerator = additionalConfigurationFiles.objectEnumerator(); configEnumerator .hasMoreElements();) { String configFile = (String) configEnumerator.nextElement(); File file = new File(configFile); if (file.exists() && file.isFile() && file.canRead()) { try { Properties props = ERXProperties.propertiesFromFile(file); if (log.isDebugEnabled()) { log.debug("Loaded: {}\n{}", file, ERXProperties.logString(props)); } ERXProperties.transferPropertiesFromSourceToDest(props, dest); } catch (java.io.IOException ex) { log.error("Unable to load optional configuration file: {}", configFile, ex); } } else { configLog.error("The optional configuration file '{}' either does not exist or cannot be read.", file); } } } if (commandLine != null) { ERXProperties.transferPropertiesFromSourceToDest(commandLine, dest); } return dest; } /** * <div class="en"> * Returns all of the properties in the system mapped to their evaluated values, sorted by key. * </div> * * <div class="ja"> * ????????????? * </div> * * @param protectValues <div class="en">if true, keys with the word "password" in them will have their values removed</div> * <div class="ja">true ?????????</div> * * @return <div class="en">all of the properties in the system mapped to their evaluated values, sorted by key</div> * <div class="ja">???????</div> */ public static Map<String, String> allPropertiesMap(boolean protectValues) { return propertiesMap(ERXSystem.getProperties(), protectValues); } /** * Returns all of the properties in the system mapped to their evaluated values, sorted by key. * * @param properties * @param protectValues if <code>true</code>, keys with the word "password" in them will have their values removed * @return all of the properties in the system mapped to their evaluated values, sorted by key */ public static Map<String, String> propertiesMap(Properties properties, boolean protectValues) { Map<String, String> props = new TreeMap<String, String>(); for (Enumeration e = properties.keys(); e.hasMoreElements();) { String key = (String) e.nextElement(); if (protectValues && key.toLowerCase().contains("password")) { props.put(key, "<deleted for log>"); } else { props.put(key, String.valueOf(properties.getProperty(key))); } } return props; } /** * Returns a string suitable for logging. * @param properties * @return string for logging */ public static String logString(Properties properties) { StringBuilder message = new StringBuilder(); for (Map.Entry<String, String> entry : propertiesMap(properties, true).entrySet()) { message.append(" " + entry.getKey() + "=" + entry.getValue() + "\n"); } return message.toString(); } public static class Property { public String key, value; public Property(String key, String value) { this.key = key; this.value = value; } @Override public String toString() { return key + " = " + value; } } /** * <div class="ja"> * ????? * * @return ?? * </div> */ public static NSArray<Property> allProperties() { NSMutableArray props = new NSMutableArray(); for (Enumeration e = ERXSystem.getProperties().keys(); e.hasMoreElements();) { String key = (String) e.nextElement(); String object = "" + ERXSystem.getProperty(key); props.addObject(new Property(key, object)); } return (NSArray) props.valueForKey("@sortAsc.key"); } /** * <div class="en"> * Returns the full path to the Properties file under the * given project path. At the current implementation, * it looks for /Properties and /Resources/Properties. * If the Properties file doesn't exist, returns null. * </div> * * <div class="ja"> * ????? * ???? /Properties ? /Resources/Properties ???? * ???????? null ??? * </div> * * @param projectPath <div class="en">string to the project root directory</div> * <div class="ja">???</div> * * @return <div class="en">the path to the Properties file if it exists</div> * <div class="ja">??</div> */ public static String pathForPropertiesUnderProjectPath(String projectPath) { String path = null; final NSArray supportedPropertiesPaths = new NSArray( new Object[] { "/Properties", "/Resources/Properties" }); Enumeration e = supportedPropertiesPaths.objectEnumerator(); while (e.hasMoreElements()) { File file = new File(projectPath + (String) e.nextElement()); if (file.exists() && file.isFile() && file.canRead()) { try { path = file.getCanonicalPath(); } catch (IOException ex) { log.error("Could not get canonical path from {}", file, ex); } break; } } return path; } /** * <div class="en"> * Returns the application-specific user properties. * </div> * * <div class="ja"> * ????? * </div> * * @return <div class="en">application-specific user properties</div> * <div class="ja">??</div> */ public static String applicationDeveloperProperties() { String applicationDeveloperPropertiesPath = null; if (ERXApplication.isDevelopmentModeSafe()) { String devName = ERXSystem.getProperty("er.extensions.ERXProperties.devPropertiesName", "dev"); applicationDeveloperPropertiesPath = variantPropertiesInBundle(devName, "app"); } return applicationDeveloperPropertiesPath; } /** * Returns the application-specific variant properties for the given bundle. * @param userName * @param bundleName * @return the application-specific variant properties for the given bundle. */ public static String variantPropertiesInBundle(String userName, String bundleName) { String applicationUserPropertiesPath = null; if (userName != null && userName.length() > 0) { String resourceApplicationUserPropertiesPath = ERXFileUtilities .pathForResourceNamed("Properties." + userName, bundleName, null); if (resourceApplicationUserPropertiesPath != null) { applicationUserPropertiesPath = ERXProperties.getActualPath(resourceApplicationUserPropertiesPath); } } return applicationUserPropertiesPath; } /** * <div class="en"> * Returns the application-specific user properties. * </div> * * <div class="ja"> * ?????? * </div> * * @return <div class="en">the application-specific user properties</div> * <div class="ja">??</div> */ public static String applicationUserProperties() { return variantPropertiesInBundle(ERXSystem.getProperty("user.name"), "app"); } /** * <div class="en"> * Returns the path to the application-specific system-wide file "fileName". By default this path is /etc/WebObjects, * and the application name will be appended. For instance, if you are asking for the MyApp Properties file for the * system, it would go in /etc/WebObjects/MyApp/Properties. * </div> * * <div class="ja"> * ? "fileName" ????? * ?? /etc/WebObjects ????? * ???MyApp ??????????/etc/WebObjects/MyApp/Properties ??? * </div> * * @param fileName <div class="en">the Filename</div> * <div class="ja">??</div> * * @return <div class="en">the path, or null if the path does not exist</div> * <div class="ja">????? null</div> */ public static String applicationMachinePropertiesPath(String fileName) { String applicationMachinePropertiesPath = null; String machinePropertiesPath = ERXSystem.getProperty("er.extensions.ERXProperties.machinePropertiesPath", "/etc/WebObjects"); WOApplication application = WOApplication.application(); String applicationName; if (application != null) { applicationName = application.name(); } else { applicationName = ERXSystem.getProperty("WOApplicationName"); if (applicationName == null) { NSBundle mainBundle = NSBundle.mainBundle(); if (mainBundle != null) { applicationName = mainBundle.name(); } if (applicationName == null) { applicationName = "Unknown"; } } } File applicationPropertiesFile = new File(machinePropertiesPath + File.separator + fileName); if (!applicationPropertiesFile.exists()) { applicationPropertiesFile = new File( machinePropertiesPath + File.separator + applicationName + File.separator + fileName); } if (applicationPropertiesFile.exists()) { try { applicationMachinePropertiesPath = applicationPropertiesFile.getCanonicalPath(); } catch (IOException e) { log.error("Failed to load machine Properties file '{}'.", fileName, e); } } return applicationMachinePropertiesPath; } /** * <div class="en"> * Gets an array of optionally defined configuration files. For each file, if it does not * exist as an absolute path, ERXProperties will attempt to resolve it as an application resource * and use that instead. * </div> * * <div class="ja"> * ??????????????????? * ERXProperties ???????? * </div> * * @return <div class="en">array of configuration file names</div> * <div class="ja">???</div> */ public static NSArray optionalConfigurationFiles() { NSArray immutableOptionalConfigurationFiles = arrayForKey( "er.extensions.ERXProperties.OptionalConfigurationFiles"); NSMutableArray optionalConfigurationFiles = null; if (immutableOptionalConfigurationFiles != null) { optionalConfigurationFiles = immutableOptionalConfigurationFiles.mutableClone(); for (int i = 0; i < optionalConfigurationFiles.count(); i++) { String optionalConfigurationFile = (String) optionalConfigurationFiles.objectAtIndex(i); if (!new File(optionalConfigurationFile).exists()) { String resourcePropertiesPath = ERXFileUtilities.pathForResourceNamed(optionalConfigurationFile, "app", null); if (resourcePropertiesPath != null) { optionalConfigurationFiles .replaceObjectAtIndex(ERXProperties.getActualPath(resourcePropertiesPath), i); } } } } return optionalConfigurationFiles; } /** * <div class="en"> * Returns actual full path to the given file system path * that could contain symbolic links. For example: * /Resources will be converted to /Versions/A/Resources * when /Resources is a symbolic link. * </div> * * <div class="ja"> * ?????? * * ???/Resources ????? /Resources ? /Versions/A/Resources ??? * </div> * * @param path <div class="en">path string to a resource that could contain symbolic links</div> * <div class="ja">?????????</div> * * @return <div class="en">actual path to the resource</div> * <div class="ja">???</div> */ public static String getActualPath(String path) { String actualPath = null; File file = new File(path); try { actualPath = file.getCanonicalPath(); } catch (Exception ex) { log.warn("The file at {} does not seem to exist.", path, ex); } return actualPath; } /** * <div class="ja">???????</div> */ public static void systemPropertiesChanged() { synchronized (AppSpecificPropertyNames) { AppSpecificPropertyNames.clear(); } _cache.clear(); // MS: Leave for future WO support ... NSNotificationCenter.defaultCenter().postNotification("PropertiesDidChange", null, null); } // =========================================================================== // Instance Variable(s) // --------------------------------------------------------------------------- /** caches the application name that is appended to the key for lookup */ protected String applicationNameForAppending; // =========================================================================== // Instance Method(s) // --------------------------------------------------------------------------- /** * <div class="en"> * Caches the application name for appending to the key. * Note that for a period when the application is starting up * application() will be null and name() will be null. * <p> * Note: this is redundant with the scheme checked in on March 21, 2005 by clloyd (ben holt did checkin). * This scheme requires the user to swizzle the existing properties file with a new one of this type. * </div> * * <div class="ja"> * ???????? * ????? application() ? name() ??? null ?????? * </div> * * @return <div class="en">application name used for appending, for example ".ERMailer"</div> * <div class="ja">???????? ".ERMailer"</div> */ protected String applicationNameForAppending() { if (applicationNameForAppending == null) { applicationNameForAppending = WOApplication.application() != null ? WOApplication.application().name() : null; if (applicationNameForAppending != null) { applicationNameForAppending = "." + applicationNameForAppending; } } return applicationNameForAppending; } /** * <div class="en"> * Overriding the default getProperty method to first check: * key.<ApplicationName> before checking for key. If nothing * is found then key.Default is checked. * </div> * * <div class="ja"> * ? getProperty ???? * * <ApplicationName> ????? * ????????? * </div> * * @param key <div class="en">to check</div> * <div class="ja">??</div> * * @return <div class="en">property value</div> * <div class="ja"></div> */ @Override public String getProperty(String key) { String property = null; String application = applicationNameForAppending(); if (application != null) { property = super.getProperty(key + application); } if (property == null) { property = super.getProperty(key); if (property == null) { property = super.getProperty(key + DefaultString); } // We go ahead and set the value to increase the lookup the next time the // property is accessed. if (property != null && application != null) { setProperty(key + application, property); } } return property; } /** * <div class="en"> * Returns the properties as a String in Property file format. Useful when you use them * as custom value types, you would set this as the conversion method name. * </div> * * <div class="ja"> * ??????? * ??????? * ?????????? * </div> * * @return <div class="en">Returns the properties as a String in Property file format</div> * <div class="ja">?</div> * * @throws IOException */ // TODO The result isn't a Object it is a String public Object toExternalForm() throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); store(os, null); return new String(os.toByteArray()); } /** * <div class="en"> * Load the properties from a String in Property file format. Useful when you use them * as custom value types, you would set this as the factory method name. * </div> * <div class="ja"> * ????? * ??????? * ????????? * </div> * * @param string <div class="en"></div> * <div class="ja">?</div> * * @return <div class="en"></div> * <div class="ja"></div> */ public static ERXProperties fromExternalForm(String string) { ERXProperties result = new ERXProperties(); try { result.load(new ByteArrayInputStream(string.getBytes())); } catch (IOException e) { // AK: shouldn't ever happen... throw NSForwardException._runtimeExceptionForThrowable(e); } return result; } @Override public void takeValueForKey(Object anObject, String aKey) { setProperty(aKey, (anObject != null ? anObject.toString() : null)); } @Override public Object valueForKey(String aKey) { return getProperty(aKey); } /** * <div class="en">Stores the mapping between operator keys and operators</div> * * <div class="ja">???????</div> */ private static final NSMutableDictionary<String, ERXProperties.Operator> operators = new NSMutableDictionary<String, ERXProperties.Operator>(); /** * <div class="en"> * Registers a property operator for a particular key. * </div> * * <div class="ja"> * ?????? * </div> * * @param operator <div class="en">the operator to register</div> * <div class="ja">?</div> * @param key <div class="en">the key name of the operator</div> * <div class="ja">???</div> */ public static void setOperatorForKey(ERXProperties.Operator operator, String key) { ERXProperties.operators.setObjectForKey(operator, key); } /** * <div class="en"> * Property operators work like array operators. In your properties, you can * define keys like: * <pre><code>er.extensions.akey.@someOperatorKey.aparameter=somevalue</code></pre> * Which will be processed by the someOperatorKey operator. Because * properties get handled very early in the startup process, you should * register operators somewhere like a static block in your Application * class. For instance, if you wanted to register the forInstance operator, * you might put the following your Application class: * <pre><code> * static { * ERXProperties.setOperatorForKey(new ERXProperties.InRangeOperator(100), ERXProperties.InRangeOperator.ForInstanceKey); * } * </code></pre> * It's important to note that property operators evaluate at load time, not * access time, so the compute function should not depend on any runtime * state to execute. Additionally, access to other properties inside the * compute method should be very carefully considered because it's possible * that the operators are evaluated before all of the properties in the * system are loaded. * </div> * * <div class="ja"> * ????????? * ?????????????? * <pre><code>er.extensions.akey.@someOperatorKey.aparameter=somevalue</code></pre> * someOperatorKey ?????? * ????????????????? * <pre><code> * static { * ERXProperties.setOperatorForKey(new ERXProperties.InRangeOperator(100), ERXProperties.InRangeOperator.ForInstanceKey); * } * </code></pre> * ??????????????? * ???????????????? * * ????????? * ??????????????????? * </div> * * @author mschrag */ public static interface Operator { /** * <div class="en"> * Performs some computation on the key, value, and parameters and * returns a dictionary of new properties. If this method returns null, * the original key and value will be used. If any other dictionary is * returned, the properties in the dictionary will be copied into the * destination properties. * </div> * <div class="ja"> * ????????? * ????? null ???????? * * ?????? * ???? * </div> * * @param key <div class="en">the key ("er.extensions.akey" in "er.extensions.akey.@someOperatorKey.aparameter=somevalue")</div> * <div class="ja"> ("er.extensions.akey.@someOperatorKey.aparameter=somevalue" ? "er.extensions.akey")</div> * @param value <div class="en">("somevalue" in "er.extensions.akey.@someOperatorKey.aparameter=somevalue")</div> * <div class="ja"> ("er.extensions.akey.@someOperatorKey.aparameter=somevalue" ? "somevalue")</div> * @param parameters <div class="en">("aparameter" in "er.extensions.akey.@someOperatorKey.aparameter=somevalue")</div> * <div class="ja"> ("er.extensions.akey.@someOperatorKey.aparameter=somevalue" ? "aparameter")</div> * * @return <div class="en">a dictionary of properties (or null to use the original key and value)</div> * <div class="ja">? (null ???????)</div> */ public NSDictionary<String, String> compute(String key, String value, String parameters); } /** * <div class="en"> * InRangeOperator provides support for defining properties that only * get set if a value falls within a specific range of values. * <p> * An example of this is instance-number-based properties, where you want to * only set a specific value if the instance number of the application falls * within a certain value. In this example, because instance number is * something that is associated with a request rather than the application * itself, it is up to the class registering this operator to specify which * instance number this application is (via, for instance, a custom system property). * <p> * InRangeOperator supports specifying keys like: * <pre><code>er.extensions.akey.@forInstance.50=avalue</code></pre> * which would set the value of "er.extensions.akey" to "avalue" if this * instance is 50. * <pre><code>er.extensions.akey.@forInstance.60,70=avalue</code></pre> * which would set the value of "er.extensions.akey" to "avalue" if this * instance is 60 or 70. * <pre><code>er.extensions.akey.@forInstance.100-300=avalue</code></pre> * which would set the value of "er.extensions.akey" to "avalue" if this * instance is between 100 and 300 (inclusive). * <pre><code>er.extensions.akey.@forInstance.20-30,500=avalue</code></pre> * which would set the value of "er.extensions.akey" to "avalue" if this * instance is between 20 and 30 (inclusive), or if the instance is 50. * <p> * If there are multiple inRange operators that match for the same key, * the last property (when sorted alphabetically by key name) will win. As a * result, it's important to not define overlapping ranges, or you * may get unexpected results. * </div> * * <div class="ja"> * InRangeOperator ????????? * <p> * ?????????? * ?????????????? * ??? * ????????? * ?????????????? * <p> * InRangeOperator ?????????? * <pre><code>er.extensions.akey.@forInstance.50=avalue</code></pre> * ?50??? "er.extensions.akey" ? "avalue" ???? * <pre><code>er.extensions.akey.@forInstance.60,70=avalue</code></pre> * ?60??70?? "er.extensions.akey" ? "avalue" ???? * <pre><code>er.extensions.akey.@forInstance.100-300=avalue</code></pre> * ?100?300???? "er.extensions.akey" ? "avalue" ???? * <pre><code>er.extensions.akey.@forInstance.20-30,50=avalue</code></pre> * ?20?30????50?? "er.extensions.akey" ? "avalue" ???? * <p> * ??????????????????? * ???????????????????????????? * </div> * * @author mschrag */ public static class InRangeOperator implements ERXProperties.Operator { /** * <div class="en">The default key name of the ForInstance variant of the InRange operator.</div> * <div class="ja">InRange???</div> */ public static final String ForInstanceKey = "forInstance"; private int _instanceNumber; /** * <div class="en"> * Constructs a new InRangeOperator. * </div> * * <div class="ja"> * ? InRangeOperator ????? * </div> * * @param value <div class="en">the instance number of this application</div> * <div class="ja">????</div> */ public InRangeOperator(int value) { _instanceNumber = value; } public NSDictionary<String, String> compute(String key, String value, String parameters) { NSDictionary computedProperties = null; if (parameters != null && parameters.length() > 0) { if (ERXStringUtilities.isValueInRange(_instanceNumber, parameters)) { computedProperties = new NSDictionary(value, key); } else { computedProperties = NSDictionary.EmptyDictionary; } } return computedProperties; } } /** * <div class="en"> * Encrypted operator supports decrypting values using the default crypter. To register this * operator, add the following static block to your Application class: * <pre><code> * static { * ERXProperties.setOperatorForKey(new ERXProperties.EncryptedOperator(), ERXProperties.EncryptedOperator.Key); * } * </code></pre> * * Call er.extensions.ERXProperties.EncryptedOperator.register() in an Application static * block to register this operator. * </div> * * <div class="ja"> * ???????? * <pre><code> * static { * ERXProperties.setOperatorForKey(new ERXProperties.EncryptedOperator(), ERXProperties.EncryptedOperator.Key); * } * </code></pre> * * er.extensions.ERXProperties.EncryptedOperator.register() ?????????? * </div> * * @author mschrag */ public static class EncryptedOperator implements ERXProperties.Operator { public static final String Key = "encrypted"; public static void register() { ERXProperties.setOperatorForKey(new ERXProperties.EncryptedOperator(), ERXProperties.EncryptedOperator.Key); } public NSDictionary<String, String> compute(String key, String value, String parameters) { String decryptedValue = ERXCrypto.defaultCrypter().decrypt(value); return new NSDictionary<String, String>(decryptedValue, key); } } /** * <div class="ja"> * InIpRangeOperator ?IP???????? * <p> * ? IP ??????? * <code>er.erxtensions.ERXTcpIp.IpPriority.[[ip address]]</code> * ? 0 - 9999 * er.erxtensions.ERXTcpIp.IpPriority.10.0.1.97 = 5 * * ?IP * <code>er.erxtensions.ERXTcpIp.UseThisIp</code> * IP???IP???????IP??????? * ?????????? * er.erxtensions.ERXTcpIp.UseThisIp = 192.168.1.68 * * er.erxtensions.ERXTcpIp.UseThisIp ???? IP ?????? * <code>er.erxtensions.ERXTcpIp.NoIpAndNoNetwork</code> * ??????????IPMachineIp?????? * ???????????????127.0.0.1 * er.erxtensions.ERXTcpIp.NoIpAndNoNetwork = 192.168.1.220 * * * InIpRangeOperator ?????????? * * <pre><code>sampleip1.@forIP.192.168.1.68 = avalue</code></pre> * IP? 192.168.1.68 ??? "sampleip1" ? "avalue" ???? * * <pre><code>test.sampleip2.@forIP.192.168.1.67,192.168.1.68 = avalue</code></pre> * IP? 192.168.1.67 ?? 192.168.1.68 ?? "test.sampleip2" ? "avalue" ???? * * <pre><code>test.sampleip3.@forIP.192.168.1.50-192.168.1.90 = avalue</code></pre> * IP? 192.168.1.50 ? 192.168.1.90 ???? "test.sampleip3" ? "avalue" ???? * * <pre><code>test.sampleip4.@forIP.192.168.1.50-192.168.1.90,127.0.0.1 = avalue</code></pre> * IP? 192.168.1.50 ? 192.168.1.90 ???? 127.0.0.1 ?? "test.sampleip4" ? "avalue" ???? * <p> * ??????????????????? * ???????????????????????????? * </div> * * @property er.erxtensions.ERXTcpIp.UseThisIp * @property er.erxtensions.ERXTcpIp.NoIpAndNoNetwork * @property er.erxtensions.ERXTcpIp.IpPriority.{IP Address} * * @author tani & fukui & ishimoto */ public static class InIpRangeOperator implements ERXProperties.Operator { /** * <div class="ja">InIpRange???</div> */ public static final String ForInstanceKey = "forIP"; /** * <div class="ja">IP ?</div> */ private NSArray<String> _ipAddress; /** * <div class="ja"> * ? InIpRangeOperator ????? * * @param ipList - ??? IP ? * </div> */ public InIpRangeOperator(NSArray<String> ipList) { _ipAddress = ipList; } /** * ?? InIpRangeOperator ????A10Application ??? */ public static void register() { ERXProperties.setOperatorForKey(new ERXProperties.InIpRangeOperator(ERXTcpIp.machineIpList()), ERXProperties.InIpRangeOperator.ForInstanceKey); } public NSDictionary<String, String> compute(String key, String value, String parameters) { NSDictionary<String, String> computedProperties = null; if (parameters != null && parameters.length() > 0) { boolean ipNumberMatches = false; String[] ranges = parameters.split(","); for (String range : ranges) { range = range.trim(); int dashIndex = range.indexOf('-'); if (dashIndex == -1) { String singleIP = range; if (_ipAddress.contains(singleIP)) { ipNumberMatches = true; break; } } else { String lowValue = range.substring(0, dashIndex).trim(); String highValue = range.substring(dashIndex + 1).trim(); for (String obj : _ipAddress) { if (ERXTcpIp.isInet4IPAddressWithinRange(lowValue, obj, highValue)) { ipNumberMatches = true; break; } } } } if (ipNumberMatches) { computedProperties = new NSDictionary<String, String>(value, key); } else { computedProperties = new NSDictionary<String, String>(); } } return computedProperties; } } /** * <div class="en"> * For each property in originalProperties, process the keys and values with * the registered property operators and stores the converted value into * destinationProperties. * </div> * * <div class="ja"> * ??????????? * ????????? * ERXSystem? * </div> * * @param originalProperties <div class="en">the properties to convert</div> * <div class="ja"></div> * @param destinationProperties <div class="en">the properties to copy into</div> * <div class="ja">???</div> */ public static void evaluatePropertyOperators(Properties originalProperties, Properties destinationProperties) { NSArray<String> operatorKeys = ERXProperties.operators.allKeys(); for (Object keyObj : new TreeSet<Object>(originalProperties.keySet())) { String key = (String) keyObj; if (key != null && key.length() > 0) { String value = originalProperties.getProperty(key); if (operatorKeys.count() > 0 && key.indexOf(".@") != -1) { ERXProperties.Operator operator = null; NSDictionary<String, String> computedProperties = null; for (String operatorKey : operatorKeys) { String operatorKeyWithAt = ".@" + operatorKey; if (key.endsWith(operatorKeyWithAt)) { operator = ERXProperties.operators.objectForKey(operatorKey); computedProperties = operator.compute( key.substring(0, key.length() - operatorKeyWithAt.length()), value, null); break; } int keyIndex = key.indexOf(operatorKeyWithAt + "."); if (keyIndex != -1) { operator = ERXProperties.operators.objectForKey(operatorKey); computedProperties = operator.compute(key.substring(0, keyIndex), value, key.substring(keyIndex + operatorKeyWithAt.length() + 1)); break; } } if (computedProperties == null) { destinationProperties.put(key, value); } else { originalProperties.remove(key); // If the key exists in the System properties' defaults with a different value, we must reinsert // the property so it doesn't get overwritten with the default value when we evaluate again. // This happens because ERXConfigurationManager processes the properties after a configuration // change in multiple passes and each calls this method. if (System.getProperty(key) != null && !System.getProperty(key).equals(value)) { originalProperties.put(key, value); } for (String computedKey : computedProperties.allKeys()) { destinationProperties.put(computedKey, computedProperties.objectForKey(computedKey)); } } } else { destinationProperties.put(key, value); } } } } /** * For every application specific property, this method generates a similar property without * the application name in the property key. The original property is not removed. * <p> * Ex: if current application is MyApp, for a property foo.bar.MyApp=true a new property * foo.bar=true is generated. * * @param properties Properties to update */ // xxxxxxxxxxxxxxxxxxx // This is more complex than it needs to be. Can just use endsWith.... // public static void flattenPropertyNames(Properties properties) { if (_useLoadtimeAppSpecifics == false) { return; } WOApplication application = WOApplication.application(); if (application == null) { return; } String applicationName = application.name(); for (Object keyObj : new TreeSet<Object>(properties.keySet())) { String key = (String) keyObj; if (key != null && key.length() > 0) { String value = properties.getProperty(key); int lastDotPosition = key.lastIndexOf("."); if (lastDotPosition != -1) { String lastElement = key.substring(lastDotPosition + 1); if (lastElement.equals(applicationName)) { properties.put(key.substring(0, lastDotPosition), value); } } } } } /** * <div class="en"> * _Properties is a subclass of Properties that provides support for including other * Properties files on the fly. If you create a property named .includeProps, the value * will be interpreted as a file to load. If the path is absolute, it will just load it * directly. If it's relative, the path will be loaded relative to the current user's * home directory. Multiple .includeProps can be included in a Properties file and they * will be loaded in the order they appear within the file. * </div> * * <div class="ja"> * _Properties ? Properties ?????????? * .includeProps ?????????????????? * ?????????????????? * ??? .includeProps ????????????? * </div> * * @author mschrag */ public static class _Properties extends Properties { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; public static final String IncludePropsKey = ".includeProps"; private Stack<File> _files = new Stack<File>(); @Override public synchronized Object put(Object key, Object value) { if (_Properties.IncludePropsKey.equals(key)) { String propsFileName = (String) value; File propsFile = new File(propsFileName); if (!propsFile.isAbsolute()) { // if we don't have any context for a relative (non-absolute) props file, // we presume that it's relative to the user's home directory File cwd = null; if (_files.size() > 0) { cwd = _files.peek(); } else { cwd = new File(System.getProperty("user.home")); } propsFile = new File(cwd, propsFileName); } // Detect mutually recursing props files by tracking what we've already loaded: String existingIncludeProps = getProperty(_Properties.IncludePropsKey); if (existingIncludeProps == null) { existingIncludeProps = ""; } if (existingIncludeProps.indexOf(propsFile.getPath()) > -1) { log.error("_Properties.load(): recursive includeProps detected! {} in {}", propsFile, existingIncludeProps); log.error("_Properties.load() cannot proceed - QUITTING!"); System.exit(1); } if (existingIncludeProps.length() > 0) { existingIncludeProps += ", "; } existingIncludeProps += propsFile; super.put(_Properties.IncludePropsKey, existingIncludeProps); try { log.info("_Properties.load(): Including props file: {}", propsFile); load(propsFile); } catch (IOException e) { throw new RuntimeException("Failed to load the property file '" + value + "'.", e); } return null; } return super.put(key, value); } public synchronized void load(File propsFile) throws IOException { _files.push(propsFile.getParentFile()); try { BufferedInputStream is = new BufferedInputStream(new FileInputStream(propsFile)); try { load(is); } finally { is.close(); } } finally { _files.pop(); } } } /** * <div class="ja"> * ?? String ???? * 2?????????????? * * @param s1 - 1 * @param s2 - 2 * * @return String ??null * </div> * * @author A10 nettani */ public static String stringFor2Keys(String s1, String s2) { return stringForManyKeys(ERXValueUtilities.stringsToStringArray(s1, s2)); } /** * <div class="ja"> * ?? String ???? * 2?????????????? * * @param s1 - 1 * @param s2 - 2 * @param defaultValue - * * @return String ??defaultValue * </div> * * @author A10 nettani */ public static String stringFor2KeysWithDefault(String s1, String s2, final String defaultValue) { return stringForManyKeysWithDefault(ERXValueUtilities.stringsToStringArray(s1, s2), defaultValue); } /** * <div class="ja"> * ?? String ???? * 3?????????????? * * @param s1 - 1 * @param s2 - 2 * @param s3 - 3 * * @return String ?? null * </div> * * @author A10 nettani */ public static String stringFor3Keys(String s1, String s2, String s3) { return stringForManyKeys(ERXValueUtilities.stringsToStringArray(s1, s2, s3)); } /** * <div class="ja"> * ?? String ???? * 3?????????????? * * @param s1 - 1 * @param s2 - 2 * @param s3 - 3 * @param defaultValue - * * @return String ??defaultValue * </div> * * @author A10 nettani */ public static String stringFor3KeysWithDefault(String s1, String s2, String s3, final String defaultValue) { return stringForManyKeysWithDefault(ERXValueUtilities.stringsToStringArray(s1, s2, s3), defaultValue); } /** * <div class="ja"> * ?? String ???? * ???????0??????????? * * @param ss - s * * @return String ??null * </div> * * @author A10 nettani */ public static String stringForManyKeys(String[] ss) { return stringForManyKeysWithDefault(ss, null); } /** * <div class="ja"> * ?? String ???? * ???????0??????????? * * @param ss - s * @param defaultValue - * * @return String * </div> * * @author A10 nettani */ public static String stringForManyKeysWithDefault(final String[] ss, final String defaultValue) { if (ArrayUtils.isEmpty(ss)) return defaultValue; // ????defaultValue int count = ss.length; for (int loop = 0; loop < count; loop++) { if (!ERXStringUtilities.stringIsNullOrEmpty(ss[loop])) { String value = stringForKey(ss[loop]); if (!ERXStringUtilities.stringIsNullOrEmpty(value)) return value; } } return defaultValue; } }